From 32d0425ff0594d93d1ef744003556f9355114c1d Mon Sep 17 00:00:00 2001 From: Simon Garrelou Date: Fri, 1 Jan 2021 18:56:53 +0100 Subject: First commit --- src/KINDLE/i_joy.c | 68 + src/KINDLE/i_main.c | 409 ++++ src/KINDLE/i_network.c | 292 +++ src/KINDLE/i_sound.c | 534 +++++ src/KINDLE/i_sshot.c | 60 + src/KINDLE/i_system.c | 460 ++++ src/KINDLE/i_video.c | 529 +++++ src/Makefile.am | 72 + src/Makefile.in | 736 ++++++ src/am_map.c | 1585 +++++++++++++ src/am_map.h | 111 + src/d_client.c | 539 +++++ src/d_deh.c | 3090 +++++++++++++++++++++++++ src/d_deh.h | 1118 +++++++++ src/d_englsh.h | 707 ++++++ src/d_event.h | 125 + src/d_items.c | 140 ++ src/d_items.h | 59 + src/d_main.c | 1725 ++++++++++++++ src/d_main.h | 82 + src/d_net.h | 214 ++ src/d_player.h | 234 ++ src/d_think.h | 94 + src/d_ticcmd.h | 59 + src/doomdata.h | 204 ++ src/doomdef.c | 48 + src/doomdef.h | 330 +++ src/doomstat.c | 108 + src/doomstat.h | 332 +++ src/doomtype.h | 128 ++ src/dstrings.c | 85 + src/dstrings.h | 80 + src/f_finale.c | 668 ++++++ src/f_finale.h | 56 + src/f_wipe.c | 202 ++ src/f_wipe.h | 45 + src/g_game.c | 2970 ++++++++++++++++++++++++ src/g_game.h | 178 ++ src/hu_lib.c | 767 +++++++ src/hu_lib.h | 247 ++ src/hu_stuff.c | 1593 +++++++++++++ src/hu_stuff.h | 90 + src/i_joy.h | 47 + src/i_main.h | 44 + src/i_network.h | 74 + src/i_sound.h | 120 + src/i_system.h | 77 + src/i_video.h | 82 + src/info.c | 4899 +++++++++++++++++++++++++++++++++++++++ src/info.h | 1498 ++++++++++++ src/lprintf.c | 382 ++++ src/lprintf.h | 68 + src/m_argv.c | 58 + src/m_argv.h | 47 + src/m_bbox.c | 58 + src/m_bbox.h | 56 + src/m_cheat.c | 744 ++++++ src/m_cheat.h | 58 + src/m_fixed.h | 101 + src/m_menu.c | 5559 +++++++++++++++++++++++++++++++++++++++++++++ src/m_menu.h | 182 ++ src/m_misc.c | 1081 +++++++++ src/m_misc.h | 111 + src/m_random.c | 147 ++ src/m_random.h | 154 ++ src/m_swap.h | 134 ++ src/md5.c | 240 ++ src/md5.h | 47 + src/mmus2mid.c | 866 +++++++ src/mmus2mid.h | 76 + src/p_ceilng.c | 467 ++++ src/p_checksum.c | 100 + src/p_checksum.h | 4 + src/p_doors.c | 711 ++++++ src/p_enemy.c | 2601 +++++++++++++++++++++ src/p_enemy.h | 118 + src/p_floor.c | 1042 +++++++++ src/p_genlin.c | 1164 ++++++++++ src/p_inter.c | 913 ++++++++ src/p_inter.h | 75 + src/p_lights.c | 443 ++++ src/p_map.c | 2335 +++++++++++++++++++ src/p_map.h | 92 + src/p_maputl.c | 683 ++++++ src/p_maputl.h | 89 + src/p_mobj.c | 1526 +++++++++++++ src/p_mobj.h | 403 ++++ src/p_plats.c | 437 ++++ src/p_pspr.c | 829 +++++++ src/p_pspr.h | 119 + src/p_saveg.c | 1029 +++++++++ src/p_saveg.h | 66 + src/p_setup.c | 1688 ++++++++++++++ src/p_setup.h | 57 + src/p_sight.c | 338 +++ src/p_spec.c | 3353 +++++++++++++++++++++++++++ src/p_spec.h | 1141 ++++++++++ src/p_switch.c | 1150 ++++++++++ src/p_telept.c | 345 +++ src/p_tick.c | 291 +++ src/p_tick.h | 75 + src/p_user.c | 452 ++++ src/p_user.h | 47 + src/protocol.h | 96 + src/r_bsp.c | 664 ++++++ src/r_bsp.h | 64 + src/r_data.c | 745 ++++++ src/r_data.h | 109 + src/r_defs.h | 428 ++++ src/r_demo.c | 88 + src/r_demo.h | 45 + src/r_draw.c | 1128 +++++++++ src/r_draw.h | 163 ++ src/r_drawcolpipeline.inl | 51 + src/r_drawcolumn.inl | 378 +++ src/r_drawflush.inl | 300 +++ src/r_drawspan.inl | 160 ++ src/r_filter.c | 119 + src/r_filter.h | 174 ++ src/r_fps.c | 450 ++++ src/r_fps.h | 76 + src/r_main.c | 649 ++++++ src/r_main.h | 120 + src/r_patch.c | 786 +++++++ src/r_patch.h | 111 + src/r_plane.c | 468 ++++ src/r_plane.h | 67 + src/r_segs.c | 854 +++++++ src/r_segs.h | 44 + src/r_sky.c | 56 + src/r_sky.h | 55 + src/r_state.h | 116 + src/r_things.c | 1077 +++++++++ src/r_things.h | 72 + src/s_sound.c | 688 ++++++ src/s_sound.h | 100 + src/sounds.c | 245 ++ src/sounds.h | 305 +++ src/st_lib.c | 374 +++ src/st_lib.h | 209 ++ src/st_stuff.c | 1160 ++++++++++ src/st_stuff.h | 102 + src/tables.c | 128 ++ src/tables.h | 93 + src/v_video.c | 1037 +++++++++ src/v_video.h | 207 ++ src/version.c | 38 + src/version.h | 40 + src/w_mmap.c | 333 +++ src/w_wad.c | 476 ++++ src/w_wad.h | 146 ++ src/wi_stuff.c | 1968 ++++++++++++++++ src/wi_stuff.h | 64 + src/z_bmalloc.c | 123 + src/z_bmalloc.h | 52 + src/z_zone.c | 705 ++++++ src/z_zone.h | 129 ++ 157 files changed, 80801 insertions(+) create mode 100644 src/KINDLE/i_joy.c create mode 100644 src/KINDLE/i_main.c create mode 100644 src/KINDLE/i_network.c create mode 100644 src/KINDLE/i_sound.c create mode 100644 src/KINDLE/i_sshot.c create mode 100644 src/KINDLE/i_system.c create mode 100644 src/KINDLE/i_video.c create mode 100644 src/Makefile.am create mode 100644 src/Makefile.in create mode 100644 src/am_map.c create mode 100644 src/am_map.h create mode 100644 src/d_client.c create mode 100644 src/d_deh.c create mode 100644 src/d_deh.h create mode 100644 src/d_englsh.h create mode 100644 src/d_event.h create mode 100644 src/d_items.c create mode 100644 src/d_items.h create mode 100644 src/d_main.c create mode 100644 src/d_main.h create mode 100644 src/d_net.h create mode 100644 src/d_player.h create mode 100644 src/d_think.h create mode 100644 src/d_ticcmd.h create mode 100644 src/doomdata.h create mode 100644 src/doomdef.c create mode 100644 src/doomdef.h create mode 100644 src/doomstat.c create mode 100644 src/doomstat.h create mode 100644 src/doomtype.h create mode 100644 src/dstrings.c create mode 100644 src/dstrings.h create mode 100644 src/f_finale.c create mode 100644 src/f_finale.h create mode 100644 src/f_wipe.c create mode 100644 src/f_wipe.h create mode 100644 src/g_game.c create mode 100644 src/g_game.h create mode 100644 src/hu_lib.c create mode 100644 src/hu_lib.h create mode 100644 src/hu_stuff.c create mode 100644 src/hu_stuff.h create mode 100644 src/i_joy.h create mode 100644 src/i_main.h create mode 100644 src/i_network.h create mode 100644 src/i_sound.h create mode 100644 src/i_system.h create mode 100644 src/i_video.h create mode 100644 src/info.c create mode 100644 src/info.h create mode 100644 src/lprintf.c create mode 100644 src/lprintf.h create mode 100644 src/m_argv.c create mode 100644 src/m_argv.h create mode 100644 src/m_bbox.c create mode 100644 src/m_bbox.h create mode 100644 src/m_cheat.c create mode 100644 src/m_cheat.h create mode 100644 src/m_fixed.h create mode 100644 src/m_menu.c create mode 100644 src/m_menu.h create mode 100644 src/m_misc.c create mode 100644 src/m_misc.h create mode 100644 src/m_random.c create mode 100644 src/m_random.h create mode 100644 src/m_swap.h create mode 100644 src/md5.c create mode 100644 src/md5.h create mode 100644 src/mmus2mid.c create mode 100644 src/mmus2mid.h create mode 100644 src/p_ceilng.c create mode 100644 src/p_checksum.c create mode 100644 src/p_checksum.h create mode 100644 src/p_doors.c create mode 100644 src/p_enemy.c create mode 100644 src/p_enemy.h create mode 100644 src/p_floor.c create mode 100644 src/p_genlin.c create mode 100644 src/p_inter.c create mode 100644 src/p_inter.h create mode 100644 src/p_lights.c create mode 100644 src/p_map.c create mode 100644 src/p_map.h create mode 100644 src/p_maputl.c create mode 100644 src/p_maputl.h create mode 100644 src/p_mobj.c create mode 100644 src/p_mobj.h create mode 100644 src/p_plats.c create mode 100644 src/p_pspr.c create mode 100644 src/p_pspr.h create mode 100644 src/p_saveg.c create mode 100644 src/p_saveg.h create mode 100644 src/p_setup.c create mode 100644 src/p_setup.h create mode 100644 src/p_sight.c create mode 100644 src/p_spec.c create mode 100644 src/p_spec.h create mode 100644 src/p_switch.c create mode 100644 src/p_telept.c create mode 100644 src/p_tick.c create mode 100644 src/p_tick.h create mode 100644 src/p_user.c create mode 100644 src/p_user.h create mode 100644 src/protocol.h create mode 100644 src/r_bsp.c create mode 100644 src/r_bsp.h create mode 100644 src/r_data.c create mode 100644 src/r_data.h create mode 100644 src/r_defs.h create mode 100644 src/r_demo.c create mode 100644 src/r_demo.h create mode 100644 src/r_draw.c create mode 100644 src/r_draw.h create mode 100644 src/r_drawcolpipeline.inl create mode 100644 src/r_drawcolumn.inl create mode 100644 src/r_drawflush.inl create mode 100644 src/r_drawspan.inl create mode 100644 src/r_filter.c create mode 100644 src/r_filter.h create mode 100644 src/r_fps.c create mode 100644 src/r_fps.h create mode 100644 src/r_main.c create mode 100644 src/r_main.h create mode 100644 src/r_patch.c create mode 100644 src/r_patch.h create mode 100644 src/r_plane.c create mode 100644 src/r_plane.h create mode 100644 src/r_segs.c create mode 100644 src/r_segs.h create mode 100644 src/r_sky.c create mode 100644 src/r_sky.h create mode 100644 src/r_state.h create mode 100644 src/r_things.c create mode 100644 src/r_things.h create mode 100644 src/s_sound.c create mode 100644 src/s_sound.h create mode 100644 src/sounds.c create mode 100644 src/sounds.h create mode 100644 src/st_lib.c create mode 100644 src/st_lib.h create mode 100644 src/st_stuff.c create mode 100644 src/st_stuff.h create mode 100644 src/tables.c create mode 100644 src/tables.h create mode 100644 src/v_video.c create mode 100644 src/v_video.h create mode 100644 src/version.c create mode 100644 src/version.h create mode 100644 src/w_mmap.c create mode 100644 src/w_wad.c create mode 100644 src/w_wad.h create mode 100644 src/wi_stuff.c create mode 100644 src/wi_stuff.h create mode 100644 src/z_bmalloc.c create mode 100644 src/z_bmalloc.h create mode 100644 src/z_zone.c create mode 100644 src/z_zone.h (limited to 'src') diff --git a/src/KINDLE/i_joy.c b/src/KINDLE/i_joy.c new file mode 100644 index 0000000..e436b33 --- /dev/null +++ b/src/KINDLE/i_joy.c @@ -0,0 +1,68 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Joystick handling for Linux + * + *----------------------------------------------------------------------------- + */ + +#ifndef lint +#endif /* lint */ + +#include + +#include "doomdef.h" +#include "doomtype.h" +#include "m_argv.h" +#include "d_event.h" +#include "d_main.h" +#include "i_joy.h" +#include "lprintf.h" + +int joyleft; +int joyright; +int joyup; +int joydown; + +int usejoystick; + +static void I_EndJoystick(void) +{ + lprintf(LO_DEBUG, "I_EndJoystick : closing joystick\n"); +} + +void I_PollJoystick(void) +{ + +} + +void I_InitJoystick(void) +{ + +} diff --git a/src/KINDLE/i_main.c b/src/KINDLE/i_main.c new file mode 100644 index 0000000..8c929b6 --- /dev/null +++ b/src/KINDLE/i_main.c @@ -0,0 +1,409 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Startup and quit functions. Handles signals, inits the + * memory management, then calls D_DoomMain. Also contains + * I_Init which does other system-related startup stuff. + * + *----------------------------------------------------------------------------- + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#ifdef HAVE_UNISTD_H +#include +#endif +#ifdef USE_SDL +#include "SDL.h" +#endif +#include "doomdef.h" +#include "m_argv.h" +#include "d_main.h" +#include "m_fixed.h" +#include "i_system.h" +#include "i_video.h" +#include "z_zone.h" +#include "lprintf.h" +#include "m_random.h" +#include "doomstat.h" +#include "g_game.h" +#include "m_misc.h" +#include "i_sound.h" +#include "i_main.h" +#include "r_fps.h" +#include "lprintf.h" + +#include +#include +#include + +/* Most of the following has been rewritten by Lee Killough + * + * I_GetTime + * killough 4/13/98: Make clock rate adjustable by scale factor + * cphipps - much made static + */ + +int realtic_clock_rate = 100; +static int_64_t I_GetTime_Scale = 1<<24; + +static int I_GetTime_Scaled(void) +{ + return (int)( (int_64_t) I_GetTime_RealTime() * I_GetTime_Scale >> 24); +} + + + +static int I_GetTime_FastDemo(void) +{ + static int fasttic; + return fasttic++; +} + + + +static int I_GetTime_Error(void) +{ + I_Error("I_GetTime_Error: GetTime() used before initialization"); + return 0; +} + + + +int (*I_GetTime)(void) = I_GetTime_Error; + +void I_Init(void) +{ + /* killough 4/14/98: Adjustable speedup based on realtic_clock_rate */ + if (fastdemo) + I_GetTime = I_GetTime_FastDemo; + else + if (realtic_clock_rate != 100) + { + I_GetTime_Scale = ((int_64_t) realtic_clock_rate << 24) / 100; + I_GetTime = I_GetTime_Scaled; + } + else + I_GetTime = I_GetTime_RealTime; + + { + /* killough 2/21/98: avoid sound initialization if no sound & no music */ + if (!(nomusicparm && nosfxparm)) + I_InitSound(); + } + + R_InitInterpolation(); +} + +/* cleanup handling -- killough: + */ +static void I_SignalHandler(int s) +{ + char buf[2048]; + + signal(s,SIG_IGN); /* Ignore future instances of this signal.*/ + + strcpy(buf,"Exiting on signal: "); + I_SigString(buf+strlen(buf),2000-strlen(buf),s); + + /* If corrupted memory could cause crash, dump memory + * allocation history, which points out probable causes + */ + if (s==SIGSEGV || s==SIGILL || s==SIGFPE) + Z_DumpHistory(buf); + + I_Error("I_SignalHandler: %s", buf); +} + + + +/* killough 2/22/98: Add support for ENDBOOM, which is PC-specific + * + * this converts BIOS color codes to ANSI codes. + * Its not pretty, but it does the job - rain + * CPhipps - made static + */ + +inline static int convert(int color, int *bold) +{ + if (color > 7) { + color -= 8; + *bold = 1; + } + switch (color) { + case 0: + return 0; + case 1: + return 4; + case 2: + return 2; + case 3: + return 6; + case 4: + return 1; + case 5: + return 5; + case 6: + return 3; + case 7: + return 7; + } + return 0; +} + +/* CPhipps - flags controlling ENDOOM behaviour */ +enum { + endoom_colours = 1, + endoom_nonasciichars = 2, + endoom_droplastline = 4 +}; + +unsigned int endoom_mode; + +static void PrintVer(void) +{ + char vbuf[200]; + lprintf(LO_INFO,"%s\n",I_GetVersionString(vbuf,200)); +} + +/* I_EndDoom + * Prints out ENDOOM or ENDBOOM, using some common sense to decide which. + * cphipps - moved to l_main.c, made static + */ +static void I_EndDoom(void) +{ + int lump_eb, lump_ed, lump = -1; + + /* CPhipps - ENDOOM/ENDBOOM selection */ + lump_eb = W_CheckNumForName("ENDBOOM");/* jff 4/1/98 sign our work */ + lump_ed = W_CheckNumForName("ENDOOM"); /* CPhipps - also maybe ENDOOM */ + + if (lump_eb == -1) + lump = lump_ed; + else if (lump_ed == -1) + lump = lump_eb; + else + { /* Both ENDOOM and ENDBOOM are present */ +#define LUMP_IS_NEW(num) (!((lumpinfo[num].source == source_iwad) || (lumpinfo[num].source == source_auto_load))) + switch ((LUMP_IS_NEW(lump_ed) ? 1 : 0 ) | + (LUMP_IS_NEW(lump_eb) ? 2 : 0)) { + case 1: + lump = lump_ed; + break; + case 2: + lump = lump_eb; + break; + default: + /* Both lumps have equal priority, both present */ + lump = (P_Random(pr_misc) & 1) ? lump_ed : lump_eb; + break; + } + } + + if (lump != -1) + { + const char (*endoom)[2] = (const void*)W_CacheLumpNum(lump); + int i, l = W_LumpLength(lump) / 2; + + /* cph - colour ENDOOM by rain */ + int oldbg = -1, oldcolor = -1, bold = 0, oldbold = -1, color = 0; +#ifndef _WIN32 + if (endoom_mode & endoom_nonasciichars) + /* switch to secondary charset, and set to cp437 (IBM charset) */ + printf("\e)K\016"); +#endif /* _WIN32 */ + + /* cph - optionally drop the last line, so everything fits on one screen */ + if (endoom_mode & endoom_droplastline) + l -= 80; + lprintf(LO_INFO,"\n"); + for (i=0; i +#endif +#include +#include +#ifdef HAVE_UNISTD_H +#include +#endif +#include +#include +#include + +#ifdef HAVE_NET + +#include "SDL.h" +#include "SDL_net.h" + +#include "protocol.h" +#include "i_network.h" +#include "lprintf.h" +//#include "doomstat.h" + +/* cph - + * Each client will either use the IPv4 socket or the IPv6 socket + * Each server will use whichever or both that are available + */ +UDP_CHANNEL sentfrom; +IPaddress sentfrom_addr; +UDP_SOCKET udp_socket; + +/* Statistics */ +size_t sentbytes, recvdbytes; + +UDP_PACKET *udp_packet; + +/* I_ShutdownNetwork + * + * Shutdown the network code + */ +void I_ShutdownNetwork(void) +{ + SDLNet_FreePacket(udp_packet); + SDLNet_Quit(); +} + +/* I_InitNetwork + * + * Sets up the network code + */ +void I_InitNetwork(void) +{ + SDLNet_Init(); + atexit(I_ShutdownNetwork); + udp_packet = SDLNet_AllocPacket(10000); +} + +UDP_PACKET *I_AllocPacket(int size) +{ + return(SDLNet_AllocPacket(size)); +} + +void I_FreePacket(UDP_PACKET *packet) +{ + SDLNet_FreePacket(packet); +} + + +/* cph - I_WaitForPacket - use select(2) via SDL_net's interface + * No more I_uSleep loop kludge */ + +void I_WaitForPacket(int ms) +{ + SDLNet_SocketSet ss = SDLNet_AllocSocketSet(1); + SDLNet_UDP_AddSocket(ss, udp_socket); + SDLNet_CheckSockets(ss,ms); + SDLNet_FreeSocketSet(ss); +#if (defined _WIN32 && !defined PRBOOM_SERVER) + I_UpdateConsole(); +#endif +} + +/* I_ConnectToServer + * + * Connect to a server + */ +IPaddress serverIP; + +int I_ConnectToServer(const char *serv) +{ + char server[500], *p; + Uint16 port; + + /* Split serv into address and port */ + if (strlen(serv)>500) return 0; + strcpy(server,serv); + p = strchr(server, ':'); + if(p) + { + *p++ = '\0'; + port = atoi(p); + } + else + port = 5030; /* Default server port */ + + SDLNet_ResolveHost(&serverIP, server, port); + if ( serverIP.host == INADDR_NONE ) + return -1; + + if (SDLNet_UDP_Bind(udp_socket, 0, &serverIP) == -1) + return -1; + + return 0; +} + +/* I_Disconnect + * + * Disconnect from server + */ +void I_Disconnect(void) +{ +/* int i; + UDP_PACKET *packet; + packet_header_t *pdata = (packet_header_t *)packet->data; + packet = I_AllocPacket(sizeof(packet_header_t) + 1); + + packet->data[sizeof(packet_header_t)] = consoleplayer; + pdata->type = PKT_QUIT; pdata->tic = gametic; + + for (i=0; i<4; i++) { + I_SendPacket(packet); + I_uSleep(10000); + } + I_FreePacket(packet);*/ + SDLNet_UDP_Unbind(udp_socket, 0); +} + +/* + * I_Socket + * + * Sets the given socket non-blocking, binds to the given port, or first + * available if none is given + */ +UDP_SOCKET I_Socket(Uint16 port) +{ + if(port) + return (SDLNet_UDP_Open(port)); + else { + UDP_SOCKET sock; + port = IPPORT_RESERVED; + while( (sock = SDLNet_UDP_Open(port)) == NULL ) + port++; + return sock; + } +} + +void I_CloseSocket(UDP_SOCKET sock) +{ + SDLNet_UDP_Close(sock); +} + +UDP_CHANNEL I_RegisterPlayer(IPaddress *ipaddr) +{ + static int freechannel; + return(SDLNet_UDP_Bind(udp_socket, freechannel++, ipaddr)); +} + +void I_UnRegisterPlayer(UDP_CHANNEL channel) +{ + SDLNet_UDP_Unbind(udp_socket, channel); +} + +/* + * ChecksumPacket + * + * Returns the checksum of a given network packet + */ +static byte ChecksumPacket(const packet_header_t* buffer, size_t len) +{ + const byte* p = (const void*)buffer; + byte sum = 0; + + if (len==0) + return 0; + + while (p++, --len) + sum += *p; + + return sum; +} + +size_t I_GetPacket(packet_header_t* buffer, size_t buflen) +{ + int checksum; + size_t len; + int status; + + status = SDLNet_UDP_Recv(udp_socket, udp_packet); + len = udp_packet->len; + if (buflen0) ) + memcpy(buffer, udp_packet->data, len); + sentfrom=udp_packet->channel; +#ifndef SDL_NET_UDP_PACKET_SRC + sentfrom_addr=udp_packet->address; +#else + sentfrom_addr=udp_packet->src; /* cph - allow for old SDL_net library */ +#endif + checksum=buffer->checksum; + buffer->checksum=0; + if ( (status!=0) && (len>0)) { + byte psum = ChecksumPacket(buffer, udp_packet->len); +/* fprintf(stderr, "recvlen = %u, stolen = %u, csum = %u, psum = %u\n", + udp_packet->len, len, checksum, psum); */ + if (psum == checksum) return len; + } + return 0; +} + +void I_SendPacket(packet_header_t* packet, size_t len) +{ + packet->checksum = ChecksumPacket(packet, len); + memcpy(udp_packet->data, packet, udp_packet->len = len); + SDLNet_UDP_Send(udp_socket, 0, udp_packet); +} + +void I_SendPacketTo(packet_header_t* packet, size_t len, UDP_CHANNEL *to) +{ + packet->checksum = ChecksumPacket(packet, len); + memcpy(udp_packet->data, packet, udp_packet->len = len); + SDLNet_UDP_Send(udp_socket, *to, udp_packet); +} + +void I_PrintAddress(FILE* fp, UDP_CHANNEL *addr) +{ +/* + char *addy; + Uint16 port; + IPaddress *address; + + address = SDLNet_UDP_GetPeerAddress(udp_socket, player); + +//FIXME: if it cant resolv it may freeze up + addy = SDLNet_ResolveIP(address); + port = address->port; + + if(addy != NULL) + fprintf(fp, "%s:%d", addy, port); + else + fprintf(fp, "Error"); +*/ +} + +#endif /* HAVE_NET */ diff --git a/src/KINDLE/i_sound.c b/src/KINDLE/i_sound.c new file mode 100644 index 0000000..d020313 --- /dev/null +++ b/src/KINDLE/i_sound.c @@ -0,0 +1,534 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * System interface for sound. + * + *----------------------------------------------------------------------------- + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#ifdef HAVE_LIBSDL_MIXER +#define HAVE_MIXER +#endif +#include +#ifdef HAVE_UNISTD_H +#include +#endif + +#ifdef HAVE_MIXER +#include "SDL_mixer.h" +#endif + +#include "z_zone.h" + +#include "m_swap.h" +#include "i_sound.h" +#include "m_argv.h" +#include "m_misc.h" +#include "w_wad.h" +#include "lprintf.h" +#include "s_sound.h" + +#include "doomdef.h" +#include "doomstat.h" +#include "doomtype.h" + +#include "d_main.h" + +// The number of internal mixing channels, +// the samples calculated for each mixing step, +// the size of the 16bit, 2 hardware channel (stereo) +// mixing buffer, and the samplerate of the raw data. + +// Variables used by Boom from Allegro +// created here to avoid changes to core Boom files +int snd_card = 1; +int mus_card = 1; +int detect_voices = 0; // God knows + +static boolean sound_inited = false; +static boolean first_sound_init = true; + +// Needed for calling the actual sound output. +static int SAMPLECOUNT= 512; +#define MAX_CHANNELS 32 + +// MWM 2000-01-08: Sample rate in samples/second +int snd_samplerate=11025; + +// The actual output device. +int audio_fd; + +typedef struct { + // SFX id of the playing sound effect. + // Used to catch duplicates (like chainsaw). + int id; +// The channel step amount... + unsigned int step; +// ... and a 0.16 bit remainder of last step. + unsigned int stepremainder; + unsigned int samplerate; +// The channel data pointers, start and end. + const unsigned char* data; + const unsigned char* enddata; +// Time/gametic that the channel started playing, +// used to determine oldest, which automatically +// has lowest priority. +// In case number of active sounds exceeds +// available channels. + int starttime; + // Hardware left and right channel volume lookup. + int *leftvol_lookup; + int *rightvol_lookup; +} channel_info_t; + +channel_info_t channelinfo[MAX_CHANNELS]; + +// Pitch to stepping lookup, unused. +int steptable[256]; + +// Volume lookups. +int vol_lookup[128*256]; + +/* cph + * stopchan + * Stops a sound, unlocks the data + */ + +static void stopchan(int i) +{ + if (channelinfo[i].data) /* cph - prevent excess unlocks */ + { + channelinfo[i].data=NULL; + W_UnlockLumpNum(S_sfx[channelinfo[i].id].lumpnum); + } +} + +// +// This function adds a sound to the +// list of currently active sounds, +// which is maintained as a given number +// (eight, usually) of internal channels. +// Returns a handle. +// +static int addsfx(int sfxid, int channel, const unsigned char* data, size_t len) +{ + stopchan(channel); + + channelinfo[channel].data = data; + /* Set pointer to end of raw data. */ + channelinfo[channel].enddata = channelinfo[channel].data + len - 1; + channelinfo[channel].samplerate = (channelinfo[channel].data[3]<<8)+channelinfo[channel].data[2]; + channelinfo[channel].data += 8; /* Skip header */ + + channelinfo[channel].stepremainder = 0; + // Should be gametic, I presume. + channelinfo[channel].starttime = gametic; + + // Preserve sound SFX id, + // e.g. for avoiding duplicates of chainsaw. + channelinfo[channel].id = sfxid; + + return channel; +} + +static void updateSoundParams(int handle, int volume, int seperation, int pitch) +{ + int slot = handle; + int rightvol; + int leftvol; + int step = steptable[pitch]; + +#ifdef RANGECHECK + if ((handle < 0) || (handle >= MAX_CHANNELS)) + I_Error("I_UpdateSoundParams: handle out of range"); +#endif + // Set stepping + // MWM 2000-12-24: Calculates proportion of channel samplerate + // to global samplerate for mixing purposes. + // Patched to shift left *then* divide, to minimize roundoff errors + // as well as to use SAMPLERATE as defined above, not to assume 11025 Hz + if (pitched_sounds) + channelinfo[slot].step = step + (((channelinfo[slot].samplerate<<16)/snd_samplerate)-65536); + else + channelinfo[slot].step = ((channelinfo[slot].samplerate<<16)/snd_samplerate); + + // Separation, that is, orientation/stereo. + // range is: 1 - 256 + seperation += 1; + + // Per left/right channel. + // x^2 seperation, + // adjust volume properly. + leftvol = volume - ((volume*seperation*seperation) >> 16); + seperation = seperation - 257; + rightvol= volume - ((volume*seperation*seperation) >> 16); + + // Sanity check, clamp volume. + if (rightvol < 0 || rightvol > 127) + I_Error("rightvol out of bounds"); + + if (leftvol < 0 || leftvol > 127) + I_Error("leftvol out of bounds"); + + // Get the proper lookup table piece + // for this volume level??? + channelinfo[slot].leftvol_lookup = &vol_lookup[leftvol*256]; + channelinfo[slot].rightvol_lookup = &vol_lookup[rightvol*256]; +} + +void I_UpdateSoundParams(int handle, int volume, int seperation, int pitch) +{ + +} + +// +// SFX API +// Note: this was called by S_Init. +// However, whatever they did in the +// old DPMS based DOS version, this +// were simply dummies in the Linux +// version. +// See soundserver initdata(). +// +void I_SetChannels(void) +{ + // Init internal lookups (raw data, mixing buffer, channels). + // This function sets up internal lookups used during + // the mixing process. + int i; + int j; + + int* steptablemid = steptable + 128; + + // Okay, reset internal mixing channels to zero. + for (i=0; iname); + return W_GetNumForName(namebuf); +} + +// +// Starting a sound means adding it +// to the current list of active sounds +// in the internal channels. +// As the SFX info struct contains +// e.g. a pointer to the raw data, +// it is ignored. +// As our sound handling does not handle +// priority, it is ignored. +// Pitching (that is, increased speed of playback) +// is set, but currently not used by mixing. +// +int I_StartSound(int id, int channel, int vol, int sep, int pitch, int priority) +{ + return -1; +} + + + +void I_StopSound (int handle) +{ +#ifdef RANGECHECK + if ((handle < 0) || (handle >= MAX_CHANNELS)) + I_Error("I_StopSound: handle out of range"); +#endif +} + + +boolean I_SoundIsPlaying(int handle) +{ +#ifdef RANGECHECK + if ((handle < 0) || (handle >= MAX_CHANNELS)) + I_Error("I_SoundIsPlaying: handle out of range"); +#endif + return channelinfo[handle].data != NULL; +} + + +boolean I_AnySoundStillPlaying(void) +{ + boolean result = false; + int i; + + for (i=0; idata = 0; + song->handle = 0; + song->lumpnum = 0; + return 0; + } +#else + return 1; +#endif +} + +void I_SetMusicVolume(int volume) +{ +#ifdef HAVE_MIXER + Mix_VolumeMusic(volume*8); +#endif +} + +#endif /* HAVE_OWN_MUSIC */ + diff --git a/src/KINDLE/i_sshot.c b/src/KINDLE/i_sshot.c new file mode 100644 index 0000000..32a8b1f --- /dev/null +++ b/src/KINDLE/i_sshot.c @@ -0,0 +1,60 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2006 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Screenshot functions, moved out of i_video.c + * + *----------------------------------------------------------------------------- + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include + + +#ifdef HAVE_LIBPNG +#include +#endif + +#include "doomstat.h" +#include "doomdef.h" +#include "doomtype.h" +#include "v_video.h" +#include "i_video.h" +#include "z_zone.h" +#include "lprintf.h" + + + +int I_ScreenShot (const char *fname) +{ + +} + diff --git a/src/KINDLE/i_system.c b/src/KINDLE/i_system.c new file mode 100644 index 0000000..077e708 --- /dev/null +++ b/src/KINDLE/i_system.c @@ -0,0 +1,460 @@ + + +#include + +#include +#include +#include +#include +#ifdef _MSC_VER +#define F_OK 0 /* Check for file existence */ +#define W_OK 2 /* Check for write permission */ +#define R_OK 4 /* Check for read permission */ +#include +#include +#else +#include +#include +#include +#include +#include +#include +#endif +#include + +#ifdef _WIN32 +#define WIN32_LEAN_AND_MEAN +#include +#endif + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#ifdef HAVE_UNISTD_H +#include +#endif +#ifdef HAVE_SCHED_H +#include +#endif +#ifdef _MSC_VER +#include +#endif +#include +#include +#include + +#ifndef PRBOOM_SERVER +#include "m_argv.h" +#endif +#include "lprintf.h" +#include "doomtype.h" +#include "doomdef.h" +#include "lprintf.h" +#ifndef PRBOOM_SERVER +#include "m_fixed.h" +#include "r_fps.h" +#endif +#include "i_system.h" + +#ifdef __GNUG__ +#pragma implementation "i_system.h" +#endif +#include "i_system.h" + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +static unsigned int start_displaytime; +static unsigned int displaytime; +static boolean InDisplay = false; + +unsigned int SDL_GetTicks() { + struct timespec ts; + clock_gettime(CLOCK_REALTIME, &ts); + + return ((unsigned int)ts.tv_sec*1000) + (ts.tv_nsec/1000); +} + +boolean I_StartDisplay(void) +{ + if (InDisplay) + return false; + + start_displaytime = SDL_GetTicks(); + InDisplay = true; + return true; +} + +void I_EndDisplay(void) +{ + displaytime = SDL_GetTicks() - start_displaytime; + InDisplay = false; +} + +void I_uSleep(unsigned long usecs) +{ + sleep(usecs/1000); +} + +int ms_to_next_tick; + +int I_GetTime_RealTime (void) +{ + /* int t = SDL_GetTicks(); + int i = t*(TICRATE/5)/200; + ms_to_next_tick = (i+1)*200/(TICRATE/5) - t; + if (ms_to_next_tick > 1000/TICRATE || ms_to_next_tick<1) ms_to_next_tick = 1; + return i;*/ + struct timeval tv; +struct timezone tz; +unsigned long thistimereply; + +gettimeofday(&tv, &tz); + +//tv_sec is seconds tv_usec is microseconds +thistimereply = (tv.tv_sec * TICRATE + (tv.tv_usec * TICRATE) / +1000000); +return thistimereply; +} + +#ifndef PRBOOM_SERVER +fixed_t I_GetTimeFrac (void) +{ + unsigned long now; + fixed_t frac; + + now = SDL_GetTicks(); + + if (tic_vars.step == 0) + return FRACUNIT; + else + { + frac = (fixed_t)((now - tic_vars.start + displaytime) * FRACUNIT / tic_vars.step); + if (frac < 0) + frac = 0; + if (frac > FRACUNIT) + frac = FRACUNIT; + return frac; + } +} + +void I_GetTime_SaveMS(void) +{ + if (!movement_smooth) + return; + + tic_vars.start = SDL_GetTicks(); + tic_vars.next = (unsigned int) ((tic_vars.start * tic_vars.msec + 1.0f) / tic_vars.msec); + tic_vars.step = tic_vars.next - tic_vars.start; +} +#endif + +/* + * I_GetRandomTimeSeed + * + * CPhipps - extracted from G_ReloadDefaults because it is O/S based + */ +unsigned long I_GetRandomTimeSeed(void) +{ +/* This isnt very random */ + return(SDL_GetTicks()); +} + +/* cphipps - I_GetVersionString + * Returns a version string in the given buffer + */ +const char* I_GetVersionString(char* buf, size_t sz) +{ +#ifdef HAVE_SNPRINTF + snprintf(buf,sz,"%s v%s (http://prboom.sourceforge.net/)",PACKAGE,VERSION); +#else + sprintf(buf,"%s v%s (http://prboom.sourceforge.net/)",PACKAGE,VERSION); +#endif + return buf; +} + +/* cphipps - I_SigString + * Returns a string describing a signal number + */ +const char* I_SigString(char* buf, size_t sz, int signum) +{ +#ifdef SYS_SIGLIST_DECLARED + if (strlen(sys_siglist[signum]) < sz) + strcpy(buf,sys_siglist[signum]); + else +#endif +#ifdef HAVE_SNPRINTF + snprintf(buf,sz,"signal %d",signum); +#else + sprintf(buf,"signal %d",signum); +#endif + return buf; +} + + +/* + * I_Read + * + * cph 2001/11/18 - wrapper for read(2) which handles partial reads and aborts + * on error. + */ +void I_Read(int fd, void* vbuf, size_t sz) +{ + unsigned char* buf = vbuf; + + while (sz) { + int rc = read(fd,buf,sz); + if (rc <= 0) { + I_Error("I_Read: read failed: %s", rc ? strerror(errno) : "EOF"); + } + sz -= rc; buf += rc; + } +} + +/* + * I_Filelength + * + * Return length of an open file. + */ + +int I_Filelength(int handle) +{ + struct stat fileinfo; + if (fstat(handle,&fileinfo) == -1) + I_Error("I_Filelength: %s",strerror(errno)); + return fileinfo.st_size; +} + +#ifndef PRBOOM_SERVER + +// Return the path where the executable lies -- Lee Killough +// proff_fs 2002-07-04 - moved to i_system +#ifdef _WIN32 +const char *I_DoomExeDir(void) +{ + static const char current_dir_dummy[] = {"."}; // proff - rem extra slash 8/21/03 + static char *base; + if (!base) // cache multiple requests + { + size_t len = strlen(*myargv); + char *p = (base = malloc(len+1)) + len - 1; + strcpy(base,*myargv); + while (p > base && *p!='/' && *p!='\\') + *p--=0; + if (*p=='/' || *p=='\\') + *p--=0; + if (strlen(base)<2) + { + free(base); + base = malloc(1024); + if (!getcwd(base,1024)) + strcpy(base, current_dir_dummy); + } + } + return base; +} + +#elif defined(AMIGA) + +const char *I_DoomExeDir(void) +{ + return "PROGDIR:"; +} + +#elif defined(MACOSX) + +/* Defined elsewhere */ + +#else +// cph - V.Aguilar (5/30/99) suggested return ~/.lxdoom/, creating +// if non-existant +static const char prboom_dir[] = {"/.prboom"}; // Mead rem extra slash 8/21/03 + +const char *I_DoomExeDir(void) +{ + static char *base; + if (!base) // cache multiple requests + { + char *home = getenv("HOME"); + size_t len = strlen(home); + + base = malloc(len + strlen(prboom_dir) + 1); + strcpy(base, home); + // I've had trouble with trailing slashes before... + if (base[len-1] == '/') base[len-1] = 0; + strcat(base, prboom_dir); + mkdir(base, S_IRUSR | S_IWUSR | S_IXUSR); // Make sure it exists + } + return base; +} +#endif + +/* + * HasTrailingSlash + * + * cphipps - simple test for trailing slash on dir names + */ + +boolean HasTrailingSlash(const char* dn) +{ + return ( (dn[strlen(dn)-1] == '/') +#if defined(AMIGA) + || (dn[strlen(dn)-1] == ':') +#endif + ); +} + +/* + * I_FindFile + * + * proff_fs 2002-07-04 - moved to i_system + * + * cphipps 19/1999 - writen to unify the logic in FindIWADFile and the WAD + * autoloading code. + * Searches the standard dirs for a named WAD file + * The dirs are listed at the start of the function + */ + +#ifndef MACOSX /* OSX defines its search paths elsewhere. */ +char* I_FindFile(const char* wfname, const char* ext) +{ + // lookup table of directories to search + static const struct { + const char *dir; // directory + const char *sub; // subdirectory + const char *env; // environment variable + const char *(*func)(void); // for I_DoomExeDir + } search[] = { + {NULL}, // current working directory + {NULL, NULL, "DOOMWADDIR"}, // run-time $DOOMWADDIR + {DOOMWADDIR}, // build-time configured DOOMWADDIR + {NULL, "doom", "HOME"}, // ~/doom + {NULL, NULL, "HOME"}, // ~ + {NULL, NULL, NULL, I_DoomExeDir}, // config directory + {"/usr/local/share/games/doom"}, + {"/usr/share/games/doom"}, + {"/usr/local/share/doom"}, + {"/usr/share/doom"}, + }; + + int i; + /* Precalculate a length we will need in the loop */ + size_t pl = strlen(wfname) + strlen(ext) + 4; + + for (i = 0; i < sizeof(search)/sizeof(*search); i++) { + char * p; + const char * d = NULL; + const char * s = NULL; + /* Each entry in the switch sets d to the directory to look in, + * and optionally s to a subdirectory of d */ + // switch replaced with lookup table + if (search[i].env) { + if (!(d = getenv(search[i].env))) + continue; + } else if (search[i].func) + d = search[i].func(); + else + d = search[i].dir; + s = search[i].sub; + + p = malloc((d ? strlen(d) : 0) + (s ? strlen(s) : 0) + pl); + sprintf(p, "%s%s%s%s%s", d ? d : "", (d && !HasTrailingSlash(d)) ? "/" : "", + s ? s : "", (s && !HasTrailingSlash(s)) ? "/" : "", + wfname); + + if (access(p,F_OK)) + strcat(p, ext); + if (!access(p,F_OK)) { + lprintf(LO_INFO, " found %s\n", p); + return p; + } + free(p); + } + return NULL; +} +#endif + +#ifdef _WIN32 +static char* WINError(void) +{ + static char *WinEBuff = NULL; + DWORD err = GetLastError(); + char *ch; + + if (WinEBuff) + { + LocalFree(WinEBuff); + } + + if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, + NULL, err, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPTSTR) &WinEBuff, 0, NULL) == 0) + { + return "Unknown error"; + } + + if ((ch = strchr(WinEBuff, '\n')) != 0) + *ch = 0; + if ((ch = strchr(WinEBuff, '\r')) != 0) + *ch = 0; + + return WinEBuff; +} +#endif + +#define DEFAULT_AFFINITY_MASK 1 + +void I_SetAffinityMask(void) +{ + unsigned int process_affinity_mask = DEFAULT_AFFINITY_MASK; + int p = M_CheckParm("-affinity"); + + if (p && p < myargc-1) + process_affinity_mask = atoi(myargv[p+1]); + + // Set the process affinity mask so that all threads + // run on the same processor. This is a workaround for a bug in + // SDL_mixer that causes occasional crashes. + if (process_affinity_mask) + { + const char *errbuf = NULL; +#ifdef _WIN32 + if (!SetProcessAffinityMask(GetCurrentProcess(), process_affinity_mask)) + { + errbuf = WINError(); + } +#elif defined(HAVE_SCHED_SETAFFINITY) + // POSIX version: + int i; + { + cpu_set_t set; + + CPU_ZERO(&set); + + for(i = 0; i < 16; i++) + { + CPU_SET((process_affinity_mask>>i)&1, &set); + } + + if (sched_setaffinity(getpid(), sizeof(set), &set) == -1) + { + errbuf = strerror(errno); + } + } +#else + return; +#endif + + if (errbuf == NULL) + { + lprintf(LO_INFO, "I_SetAffinityMask: manual affinity mask is %d\n", process_affinity_mask); + } + else + { + lprintf(LO_ERROR, "I_SetAffinityMask: failed to set process affinity mask (%s)\n", errbuf); + } + } +} + +#endif // PRBOOM_SERVER diff --git a/src/KINDLE/i_video.c b/src/KINDLE/i_video.c new file mode 100644 index 0000000..6e926d2 --- /dev/null +++ b/src/KINDLE/i_video.c @@ -0,0 +1,529 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2006 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * DOOM graphics stuff for SDL + * + *----------------------------------------------------------------------------- + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +#ifdef HAVE_UNISTD_H +#include +#include +#include +#include +#include +#include +#include +#endif + + +#include "m_argv.h" +#include "doomstat.h" +#include "doomdef.h" +#include "doomtype.h" +#include "v_video.h" +#include "r_draw.h" +#include "d_main.h" +#include "d_event.h" +#include "i_joy.h" +#include "i_video.h" +#include "z_zone.h" +#include "s_sound.h" +#include "sounds.h" +#include "w_wad.h" +#include "st_stuff.h" +#include "lprintf.h" + +int gl_colorbuffer_bits=16; +int gl_depthbuffer_bits=16; + +extern void M_QuitDOOM(int choice); +#ifdef DISABLE_DOUBLEBUFFER +int use_doublebuffer = 0; +#else +int use_doublebuffer = 1; // Included not to break m_misc, but not relevant to SDL +#endif +int use_fullscreen; +int desired_fullscreen; +static byte *screen; +static int fbFd; +static int kindleKeysFd; +static int kindleKeysFd2; + +typedef struct { + int truc1; + int truc2; + unsigned short truc3; + unsigned short keyCode; + int status; +} kindle_key_t; + +static kindle_key_t kkey; + +#define SCREEN_WIDTH 600 +#define SCREEN_HEIGHT 800 + +/////////// FRAMEBUFFER STUFF +static void init_framebuffer() { + fbFd = open("/dev/fb0", O_RDWR); + if (fbFd == -1) { + perror("open(/dev/fb0)"); + exit(1); + } +} + +static void update_framebuffer(int full) { + if (full) { + ioctl(fbFd, 0x46db, 1); + } else { + ioctl(fbFd, 0x46db, 0); + } +} + +static void clear_framebuffer() { + ioctl(fbFd, 0x46e1, 0); +} + + +////// KINDLE EVENTS + +static void init_kindle_keys() { + kindleKeysFd = open("/dev/input/event1", O_RDONLY|O_NONBLOCK); + if (kindleKeysFd == -1) { + perror("open(/dev/input/event1)"); + exit(1); + } + + kindleKeysFd2 = open("/dev/input/event0", O_RDONLY|O_NONBLOCK); + if (kindleKeysFd2 == -1) { + perror("open(/dev/input/event0)"); + exit(1); + } +} + +static int kindle_poll_keys(kindle_key_t *k) { + int retval = read(kindleKeysFd, k, sizeof(kindle_key_t)); + if (retval == -1) { + if (errno != EAGAIN) { + perror("read()"); + exit(1); + } + } else if (retval > 0) { + return 1; + } + + // This SHOULD NOT WORK: /dev/input/event0 doesn't have the same structure as event1 + // For some reason it matches for the HOME key, but it's a kludge and doesn't allow us to detect the other buttons + retval = read(kindleKeysFd2, k, sizeof(kindle_key_t)); + if (retval == -1) { + if (errno != EAGAIN) { + perror("read()"); + exit(1); + } + } else if (retval > 0) { + return 1; + } + + return 0; +} + +//////////////////////////////////////////////////////////////////////////// +// Input code +int leds_always_off = 0; // Expected by m_misc, not relevant + +// Mouse handling +extern int usemouse; // config file var +static boolean mouse_enabled; // usemouse, but can be overriden by -nomouse +static boolean mouse_currently_grabbed; + +///////////////////////////////////////////////////////////////////////////////// +// Keyboard handling + +// +// Translates the key currently in key +// + +#define KINDLE_LEFT 105 +#define KINDLE_RIGHT 106 +#define KINDLE_UP 103 +#define KINDLE_DOWN 108 +#define KINDE_OK 194 +#define KINDLE_HOME 102 +#define KINDLE_MENU 139 +#define KINDLE_BACK 158 + +static int I_TranslateKey(unsigned short code) +{ + int rc = 0; + + switch (code) { + case KINDLE_LEFT: rc = KEYD_LEFTARROW; break; + case KINDLE_RIGHT: rc = KEYD_RIGHTARROW; break; + case KINDLE_UP: rc = KEYD_UPARROW; break; + case KINDLE_DOWN: rc = KEYD_DOWNARROW; break; + case KINDE_OK: rc = KEYD_RCTRL; break; + default: break; + } + + + return rc; + +} + +///////////////////////////////////////////////////////////////////////////////// +// Main input code + +/* cph - pulled out common button code logic */ +static int I_SDLtoDoomMouseState(int buttonstate) +{ + return 0; +} + +static void I_GetEvent(kindle_key_t k) +{ + event_t event; + + if (k.keyCode == 102 && k.status == 1) { + // Press HOME + exit(0); + } + + switch (k.status) { + case 1: // SDL_KEYDOWN + event.type = ev_keydown; + event.data1 = I_TranslateKey(k.keyCode); + D_PostEvent(&event); + break; + + case 0: // SDL_KEYUP + { + event.type = ev_keyup; + event.data1 = I_TranslateKey(k.keyCode); + D_PostEvent(&event); + } + break; + + default: + break; + } +} + + +// +// I_StartTic +// + +void I_StartTic (void) +{ + while ( kindle_poll_keys(&kkey)) { + //printf("Keycode: %d // Status: %d\n", kkey.keyCode, kkey.status); + I_GetEvent(kkey); + } +} + +// +// I_StartFrame +// +void I_StartFrame (void) +{ +} + +// +// I_InitInputs +// + +static void I_InitInputs(void) +{ + init_kindle_keys(); +} +///////////////////////////////////////////////////////////////////////////// + +// I_SkipFrame +// +// Returns true if it thinks we can afford to skip this frame + +inline static boolean I_SkipFrame(void) +{ + static int frameno; + + frameno++; + switch (gamestate) { + case GS_LEVEL: + if (!paused) + return false; + default: + // Skip odd frames + return (frameno & 1) ? true : false; + } +} + +/////////////////////////////////////////////////////////// +// Palette stuff. +// +static void I_UploadNewPalette(int pal) +{ +} + +////////////////////////////////////////////////////////////////////////////// +// Graphics API + +void I_ShutdownGraphics(void) +{ +} + +// +// I_UpdateNoBlit +// +void I_UpdateNoBlit (void) +{ +} + +// +// I_FinishUpdate +// +static int newpal = 0; +#define NO_PALETTE_CHANGE 1000 + +void I_FinishUpdate (void) +{ + if (I_SkipFrame()) return; + + + int h; + byte *src; + byte *dest; + + + dest=screen; + src=screens[0].data; + h=SCREEN_HEIGHT; + for (; h>0; h--) + { + memcpy(dest,src,SCREENWIDTH); //*V_GetPixelDepth() + for (byte* off=dest; off < dest+SCREENWIDTH; off++) { + *off = ~*off; // Invert pixel color, 0xFF == black on the e-ink display + } + dest+=SCREEN_WIDTH; + src+=screens[0].byte_pitch; + } + + + update_framebuffer(0); +} + +// +// I_ScreenShot - moved to i_sshot.c +// + +// +// I_SetPalette +// +void I_SetPalette (int pal) +{ + newpal = pal; +} + +// I_PreInitGraphics + +static void I_ShutdownSDL(void) +{ + clear_framebuffer(); + close(fbFd); + return; +} + +void I_PreInitGraphics(void) +{ + init_framebuffer(); + + atexit(I_ShutdownSDL); +} + +// e6y +// GLBoom use this function for trying to set the closest supported resolution if the requested mode can't be set correctly. +// For example glboom.exe -geom 1025x768 -nowindow will set 1024x768. +// It should be used only for fullscreen modes. +static void I_ClosestResolution (int *width, int *height, int flags) +{ + *width=600; + *height=800; +} + +// CPhipps - +// I_CalculateRes +// Calculates the screen resolution, possibly using the supplied guide +void I_CalculateRes(unsigned int width, unsigned int height) +{ + // e6y: how about 1680x1050? + /* + SCREENWIDTH = (width+3) & ~3; + SCREENHEIGHT = (height+3) & ~3; + */ + +// e6y +// GLBoom will try to set the closest supported resolution +// if the requested mode can't be set correctly. +// For example glboom.exe -geom 1025x768 -nowindow will set 1024x768. +// It affects only fullscreen modes. + if (V_GetMode() == VID_MODEGL) { + if ( desired_fullscreen ) + { + I_ClosestResolution(&width, &height, 0); + } + SCREENWIDTH = width; + SCREENHEIGHT = height; + SCREENPITCH = SCREENWIDTH; + } else { + SCREENWIDTH = 600;//(width+15) & ~15; + SCREENHEIGHT = height; + if (!(SCREENWIDTH % 1024)) { + SCREENPITCH = SCREENWIDTH*V_GetPixelDepth()+32; + } else { + SCREENPITCH = SCREENWIDTH*V_GetPixelDepth(); + } + } +} + +// CPhipps - +// I_SetRes +// Sets the screen resolution +void I_SetRes(void) +{ + int i; + + I_CalculateRes(SCREENWIDTH, SCREENHEIGHT); + + // set first three to standard values + for (i=0; i<3; i++) { + screens[i].width = SCREENWIDTH; + screens[i].height = SCREENHEIGHT; + screens[i].byte_pitch = SCREENPITCH; + screens[i].short_pitch = SCREENPITCH / V_GetModePixelDepth(VID_MODE16); + screens[i].int_pitch = SCREENPITCH / V_GetModePixelDepth(VID_MODE32); + } + + // statusbar + screens[4].width = SCREENWIDTH; + screens[4].height = (ST_SCALED_HEIGHT+1); + screens[4].byte_pitch = SCREENPITCH; + screens[4].short_pitch = SCREENPITCH / V_GetModePixelDepth(VID_MODE16); + screens[4].int_pitch = SCREENPITCH / V_GetModePixelDepth(VID_MODE32); + + lprintf(LO_INFO,"I_SetRes: Using resolution %dx%d\n", SCREENWIDTH, SCREENHEIGHT); +} + +void I_InitGraphics(void) +{ + char titlebuffer[2048]; + static int firsttime=1; + + if (firsttime) + { + firsttime = 0; + + atexit(I_ShutdownGraphics); + lprintf(LO_INFO, "I_InitGraphics: %dx%d\n", SCREENWIDTH, SCREENHEIGHT); + + /* Set the video mode */ + I_UpdateVideoMode(); + + /* Setup the window title */ + strcpy(titlebuffer,PACKAGE); + strcat(titlebuffer," "); + strcat(titlebuffer,VERSION); + printf("Title: %s\n", titlebuffer); + + /* Initialize the input system */ + I_InitInputs(); + } +} + +int I_GetModeFromString(const char *modestr) +{ + video_mode_t mode; + + + mode = VID_MODE8; + + return mode; +} + +void I_UpdateVideoMode(void) +{ + int init_flags; + int i; + video_mode_t mode; + + lprintf(LO_INFO, "I_UpdateVideoMode: %dx%d (%s)\n", SCREENWIDTH, SCREENHEIGHT, desired_fullscreen ? "fullscreen" : "nofullscreen"); + + mode = I_GetModeFromString(default_videomode); + if ((i=M_CheckParm("-vidmode")) && ipitch / V_GetModePixelDepth(VID_MODE16); + screens[0].int_pitch = SCREEN_WIDTH; // screen->pitch / V_GetModePixelDepth(VID_MODE32); + + + V_AllocScreens(); + + // Hide pointer while over this window + //SDL_ShowCursor(0); + + R_InitBuffer(SCREENWIDTH, SCREENHEIGHT); + +} diff --git a/src/Makefile.am b/src/Makefile.am new file mode 100644 index 0000000..a972643 --- /dev/null +++ b/src/Makefile.am @@ -0,0 +1,72 @@ +# +# automake Makefile.am for the PrBoom source directory +# +# +# Process this file with automake to produce Makefile.in +# +# + +SUBDIRS = SDL POSIX MAC + +gamesdir=$(prefix)/games +games_PROGRAMS = prboom prboom-game-server + +CFLAGS = @CFLAGS@ @SDL_CFLAGS@ + +prboom_game_server_SOURCES = d_server.c protocol.h +prboom_game_server_LDADD = POSIX/libposixdoom.a SDL/i_network.o @NET_LIBS@ @SDL_LIBS@ + +COMMON_SRC = \ + am_map.c g_game.c p_maputl.h r_plane.h \ + am_map.h g_game.h p_mobj.c r_demo.c r_segs.c \ + hu_lib.c lprintf.c p_mobj.h r_demo.h r_segs.h \ + hu_lib.h lprintf.h p_plats.c r_sky.c \ + d_deh.c hu_stuff.c m_argv.c p_pspr.c r_sky.h \ + d_deh.h hu_stuff.h m_argv.h p_pspr.h r_state.h \ + d_englsh.h i_joy.h m_bbox.c p_saveg.c r_things.c \ + d_event.h m_bbox.h p_saveg.h r_things.h \ + d_items.c i_network.h m_cheat.c p_setup.c s_sound.c \ + d_items.h i_sound.h m_cheat.h p_setup.h s_sound.h \ + d_main.c i_system.h m_fixed.h p_sight.c sounds.c \ + d_main.h i_video.h m_menu.c p_spec.c sounds.h \ + info.c m_menu.h p_spec.h st_lib.c \ + d_net.h info.h m_misc.c p_switch.c st_lib.h \ + d_player.h m_misc.h p_telept.c st_stuff.c \ + m_random.c p_tick.c st_stuff.h i_main.h \ + d_think.h m_random.h p_tick.h tables.c \ + d_ticcmd.h m_swap.h p_user.c tables.h \ + doomdata.h p_ceilng.c p_user.h v_video.c \ + doomdef.c p_doors.c protocol.h v_video.h \ + doomdef.h p_enemy.c r_bsp.c version.c \ + doomstat.c p_enemy.h r_bsp.h version.h \ + doomstat.h p_floor.c r_data.c w_wad.c \ + doomtype.h p_genlin.c r_data.h w_wad.h \ + dstrings.c p_inter.c r_defs.h wi_stuff.c \ + dstrings.h p_inter.h r_draw.c wi_stuff.h \ + f_finale.c p_lights.c r_draw.h z_bmalloc.c \ + f_finale.h p_map.c r_main.c z_bmalloc.h \ + f_wipe.c p_map.h r_main.h z_zone.c \ + f_wipe.h p_maputl.c r_plane.c z_zone.h \ + md5.c md5.h p_checksum.h p_checksum.c \ + r_patch.c r_patch.h r_fps.c r_fps.h \ + r_filter.c r_filter.h + +NET_CLIENT_SRC = d_client.c + +if BUILD_GL +USE_GL_SRC = gl_intern.h gl_main.c gl_struct.h gl_texture.c +else +USE_GL_SRC = +endif + +if WAD_MMAP +WAD_SRC = w_mmap.c +else +WAD_SRC = w_memcache.c +endif + +prboom_SOURCES = mmus2mid.c mmus2mid.h $(COMMON_SRC) $(NET_CLIENT_SRC) $(USE_GL_SRC) $(WAD_SRC) +prboom_LDADD = SDL/libsdldoom.a @MIXER_LIBS@ @NET_LIBS@ @SDL_LIBS@ @GL_LIBS@ @MATH_LIB@ + +EXTRA_DIST = \ + r_drawcolumn.inl r_drawflush.inl r_drawspan.inl r_drawcolpipeline.inl diff --git a/src/Makefile.in b/src/Makefile.in new file mode 100644 index 0000000..511760b --- /dev/null +++ b/src/Makefile.in @@ -0,0 +1,736 @@ +# Makefile.in generated by automake 1.10 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, +# 2003, 2004, 2005, 2006 Free Software Foundation, Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +# +# automake Makefile.am for the PrBoom source directory +# +# +# Process this file with automake to produce Makefile.in +# +# + +VPATH = @srcdir@ +pkgdatadir = $(datadir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +target_triplet = @target@ +games_PROGRAMS = prboom$(EXEEXT) prboom-game-server$(EXEEXT) +subdir = src +DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/autotools/ac_c_compile_flags.m4 \ + $(top_srcdir)/autotools/ac_cpu_optimisations.m4 \ + $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +am__installdirs = "$(DESTDIR)$(gamesdir)" +gamesPROGRAMS_INSTALL = $(INSTALL_PROGRAM) +PROGRAMS = $(games_PROGRAMS) +am__prboom_SOURCES_DIST = mmus2mid.c mmus2mid.h am_map.c g_game.c \ + p_maputl.h r_plane.h am_map.h g_game.h p_mobj.c r_demo.c \ + r_segs.c hu_lib.c lprintf.c p_mobj.h r_demo.h r_segs.h \ + hu_lib.h lprintf.h p_plats.c r_sky.c d_deh.c hu_stuff.c \ + m_argv.c p_pspr.c r_sky.h d_deh.h hu_stuff.h m_argv.h p_pspr.h \ + r_state.h d_englsh.h i_joy.h m_bbox.c p_saveg.c r_things.c \ + d_event.h m_bbox.h p_saveg.h r_things.h d_items.c i_network.h \ + m_cheat.c p_setup.c s_sound.c d_items.h i_sound.h m_cheat.h \ + p_setup.h s_sound.h d_main.c i_system.h m_fixed.h p_sight.c \ + sounds.c d_main.h i_video.h m_menu.c p_spec.c sounds.h info.c \ + m_menu.h p_spec.h st_lib.c d_net.h info.h m_misc.c p_switch.c \ + st_lib.h d_player.h m_misc.h p_telept.c st_stuff.c m_random.c \ + p_tick.c st_stuff.h i_main.h d_think.h m_random.h p_tick.h \ + tables.c d_ticcmd.h m_swap.h p_user.c tables.h doomdata.h \ + p_ceilng.c p_user.h v_video.c doomdef.c p_doors.c protocol.h \ + v_video.h doomdef.h p_enemy.c r_bsp.c version.c doomstat.c \ + p_enemy.h r_bsp.h version.h doomstat.h p_floor.c r_data.c \ + w_wad.c doomtype.h p_genlin.c r_data.h w_wad.h dstrings.c \ + p_inter.c r_defs.h wi_stuff.c dstrings.h p_inter.h r_draw.c \ + wi_stuff.h f_finale.c p_lights.c r_draw.h z_bmalloc.c \ + f_finale.h p_map.c r_main.c z_bmalloc.h f_wipe.c p_map.h \ + r_main.h z_zone.c f_wipe.h p_maputl.c r_plane.c z_zone.h md5.c \ + md5.h p_checksum.h p_checksum.c r_patch.c r_patch.h r_fps.c \ + r_fps.h r_filter.c r_filter.h d_client.c gl_intern.h gl_main.c \ + gl_struct.h gl_texture.c w_memcache.c w_mmap.c +am__objects_1 = am_map.$(OBJEXT) g_game.$(OBJEXT) p_mobj.$(OBJEXT) \ + r_demo.$(OBJEXT) r_segs.$(OBJEXT) hu_lib.$(OBJEXT) \ + lprintf.$(OBJEXT) p_plats.$(OBJEXT) r_sky.$(OBJEXT) \ + d_deh.$(OBJEXT) hu_stuff.$(OBJEXT) m_argv.$(OBJEXT) \ + p_pspr.$(OBJEXT) m_bbox.$(OBJEXT) p_saveg.$(OBJEXT) \ + r_things.$(OBJEXT) d_items.$(OBJEXT) m_cheat.$(OBJEXT) \ + p_setup.$(OBJEXT) s_sound.$(OBJEXT) d_main.$(OBJEXT) \ + p_sight.$(OBJEXT) sounds.$(OBJEXT) m_menu.$(OBJEXT) \ + p_spec.$(OBJEXT) info.$(OBJEXT) st_lib.$(OBJEXT) \ + m_misc.$(OBJEXT) p_switch.$(OBJEXT) p_telept.$(OBJEXT) \ + st_stuff.$(OBJEXT) m_random.$(OBJEXT) p_tick.$(OBJEXT) \ + tables.$(OBJEXT) p_user.$(OBJEXT) p_ceilng.$(OBJEXT) \ + v_video.$(OBJEXT) doomdef.$(OBJEXT) p_doors.$(OBJEXT) \ + p_enemy.$(OBJEXT) r_bsp.$(OBJEXT) version.$(OBJEXT) \ + doomstat.$(OBJEXT) p_floor.$(OBJEXT) r_data.$(OBJEXT) \ + w_wad.$(OBJEXT) p_genlin.$(OBJEXT) dstrings.$(OBJEXT) \ + p_inter.$(OBJEXT) wi_stuff.$(OBJEXT) r_draw.$(OBJEXT) \ + f_finale.$(OBJEXT) p_lights.$(OBJEXT) z_bmalloc.$(OBJEXT) \ + p_map.$(OBJEXT) r_main.$(OBJEXT) f_wipe.$(OBJEXT) \ + z_zone.$(OBJEXT) p_maputl.$(OBJEXT) r_plane.$(OBJEXT) \ + md5.$(OBJEXT) p_checksum.$(OBJEXT) r_patch.$(OBJEXT) \ + r_fps.$(OBJEXT) r_filter.$(OBJEXT) +am__objects_2 = d_client.$(OBJEXT) +@BUILD_GL_TRUE@am__objects_3 = gl_main.$(OBJEXT) gl_texture.$(OBJEXT) +@WAD_MMAP_FALSE@am__objects_4 = w_memcache.$(OBJEXT) +@WAD_MMAP_TRUE@am__objects_4 = w_mmap.$(OBJEXT) +am_prboom_OBJECTS = mmus2mid.$(OBJEXT) $(am__objects_1) \ + $(am__objects_2) $(am__objects_3) $(am__objects_4) +prboom_OBJECTS = $(am_prboom_OBJECTS) +prboom_DEPENDENCIES = SDL/libsdldoom.a +am_prboom_game_server_OBJECTS = d_server.$(OBJEXT) +prboom_game_server_OBJECTS = $(am_prboom_game_server_OBJECTS) +prboom_game_server_DEPENDENCIES = POSIX/libposixdoom.a SDL/i_network.o +DEFAULT_INCLUDES = -I. -I$(top_builddir)@am__isrc@ +depcomp = $(SHELL) $(top_srcdir)/autotools/depcomp +am__depfiles_maybe = depfiles +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +CCLD = $(CC) +LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ +SOURCES = $(prboom_SOURCES) $(prboom_game_server_SOURCES) +DIST_SOURCES = $(am__prboom_SOURCES_DIST) \ + $(prboom_game_server_SOURCES) +RECURSIVE_TARGETS = all-recursive check-recursive dvi-recursive \ + html-recursive info-recursive install-data-recursive \ + install-dvi-recursive install-exec-recursive \ + install-html-recursive install-info-recursive \ + install-pdf-recursive install-ps-recursive install-recursive \ + installcheck-recursive installdirs-recursive pdf-recursive \ + ps-recursive uninstall-recursive +RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive \ + distclean-recursive maintainer-clean-recursive +ETAGS = etags +CTAGS = ctags +DIST_SUBDIRS = $(SUBDIRS) +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +AMTAR = @AMTAR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ @SDL_CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DOOMWADDIR = @DOOMWADDIR@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +GL_LIBS = @GL_LIBS@ +GREP = @GREP@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +LDFLAGS = @LDFLAGS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LN_S = @LN_S@ +LTLIBOBJS = @LTLIBOBJS@ +MAKEINFO = @MAKEINFO@ +MATH_LIB = @MATH_LIB@ +MIXER_CFLAGS = @MIXER_CFLAGS@ +MIXER_LIBS = @MIXER_LIBS@ +MKDIR_P = @MKDIR_P@ +NET_CFLAGS = @NET_CFLAGS@ +NET_LIBS = @NET_LIBS@ +OBJEXT = @OBJEXT@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +RANLIB = @RANLIB@ +SDL_CFLAGS = @SDL_CFLAGS@ +SDL_CONFIG = @SDL_CONFIG@ +SDL_LIBS = @SDL_LIBS@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +VERSION = @VERSION@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_CC = @ac_ct_CC@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target = @target@ +target_alias = @target_alias@ +target_cpu = @target_cpu@ +target_os = @target_os@ +target_vendor = @target_vendor@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +SUBDIRS = SDL POSIX MAC +gamesdir = $(prefix)/games +prboom_game_server_SOURCES = d_server.c protocol.h +prboom_game_server_LDADD = POSIX/libposixdoom.a SDL/i_network.o @NET_LIBS@ @SDL_LIBS@ +COMMON_SRC = \ + am_map.c g_game.c p_maputl.h r_plane.h \ + am_map.h g_game.h p_mobj.c r_demo.c r_segs.c \ + hu_lib.c lprintf.c p_mobj.h r_demo.h r_segs.h \ + hu_lib.h lprintf.h p_plats.c r_sky.c \ + d_deh.c hu_stuff.c m_argv.c p_pspr.c r_sky.h \ + d_deh.h hu_stuff.h m_argv.h p_pspr.h r_state.h \ + d_englsh.h i_joy.h m_bbox.c p_saveg.c r_things.c \ + d_event.h m_bbox.h p_saveg.h r_things.h \ + d_items.c i_network.h m_cheat.c p_setup.c s_sound.c \ + d_items.h i_sound.h m_cheat.h p_setup.h s_sound.h \ + d_main.c i_system.h m_fixed.h p_sight.c sounds.c \ + d_main.h i_video.h m_menu.c p_spec.c sounds.h \ + info.c m_menu.h p_spec.h st_lib.c \ + d_net.h info.h m_misc.c p_switch.c st_lib.h \ + d_player.h m_misc.h p_telept.c st_stuff.c \ + m_random.c p_tick.c st_stuff.h i_main.h \ + d_think.h m_random.h p_tick.h tables.c \ + d_ticcmd.h m_swap.h p_user.c tables.h \ + doomdata.h p_ceilng.c p_user.h v_video.c \ + doomdef.c p_doors.c protocol.h v_video.h \ + doomdef.h p_enemy.c r_bsp.c version.c \ + doomstat.c p_enemy.h r_bsp.h version.h \ + doomstat.h p_floor.c r_data.c w_wad.c \ + doomtype.h p_genlin.c r_data.h w_wad.h \ + dstrings.c p_inter.c r_defs.h wi_stuff.c \ + dstrings.h p_inter.h r_draw.c wi_stuff.h \ + f_finale.c p_lights.c r_draw.h z_bmalloc.c \ + f_finale.h p_map.c r_main.c z_bmalloc.h \ + f_wipe.c p_map.h r_main.h z_zone.c \ + f_wipe.h p_maputl.c r_plane.c z_zone.h \ + md5.c md5.h p_checksum.h p_checksum.c \ + r_patch.c r_patch.h r_fps.c r_fps.h \ + r_filter.c r_filter.h + +NET_CLIENT_SRC = d_client.c +@BUILD_GL_FALSE@USE_GL_SRC = +@BUILD_GL_TRUE@USE_GL_SRC = gl_intern.h gl_main.c gl_struct.h gl_texture.c +@WAD_MMAP_FALSE@WAD_SRC = w_memcache.c +@WAD_MMAP_TRUE@WAD_SRC = w_mmap.c +prboom_SOURCES = mmus2mid.c mmus2mid.h $(COMMON_SRC) $(NET_CLIENT_SRC) $(USE_GL_SRC) $(WAD_SRC) +prboom_LDADD = SDL/libsdldoom.a @MIXER_LIBS@ @NET_LIBS@ @SDL_LIBS@ @GL_LIBS@ @MATH_LIB@ +EXTRA_DIST = \ + r_drawcolumn.inl r_drawflush.inl r_drawspan.inl r_drawcolpipeline.inl + +all: all-recursive + +.SUFFIXES: +.SUFFIXES: .c .o .obj +$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh \ + && exit 0; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/Makefile'; \ + cd $(top_srcdir) && \ + $(AUTOMAKE) --foreign src/Makefile +.PRECIOUS: Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +install-gamesPROGRAMS: $(games_PROGRAMS) + @$(NORMAL_INSTALL) + test -z "$(gamesdir)" || $(MKDIR_P) "$(DESTDIR)$(gamesdir)" + @list='$(games_PROGRAMS)'; for p in $$list; do \ + p1=`echo $$p|sed 's/$(EXEEXT)$$//'`; \ + if test -f $$p \ + ; then \ + f=`echo "$$p1" | sed 's,^.*/,,;$(transform);s/$$/$(EXEEXT)/'`; \ + echo " $(INSTALL_PROGRAM_ENV) $(gamesPROGRAMS_INSTALL) '$$p' '$(DESTDIR)$(gamesdir)/$$f'"; \ + $(INSTALL_PROGRAM_ENV) $(gamesPROGRAMS_INSTALL) "$$p" "$(DESTDIR)$(gamesdir)/$$f" || exit 1; \ + else :; fi; \ + done + +uninstall-gamesPROGRAMS: + @$(NORMAL_UNINSTALL) + @list='$(games_PROGRAMS)'; for p in $$list; do \ + f=`echo "$$p" | sed 's,^.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/'`; \ + echo " rm -f '$(DESTDIR)$(gamesdir)/$$f'"; \ + rm -f "$(DESTDIR)$(gamesdir)/$$f"; \ + done + +clean-gamesPROGRAMS: + -test -z "$(games_PROGRAMS)" || rm -f $(games_PROGRAMS) +prboom$(EXEEXT): $(prboom_OBJECTS) $(prboom_DEPENDENCIES) + @rm -f prboom$(EXEEXT) + $(LINK) $(prboom_OBJECTS) $(prboom_LDADD) $(LIBS) +prboom-game-server$(EXEEXT): $(prboom_game_server_OBJECTS) $(prboom_game_server_DEPENDENCIES) + @rm -f prboom-game-server$(EXEEXT) + $(LINK) $(prboom_game_server_OBJECTS) $(prboom_game_server_LDADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/am_map.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/d_client.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/d_deh.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/d_items.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/d_main.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/d_server.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/doomdef.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/doomstat.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dstrings.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/f_finale.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/f_wipe.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/g_game.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gl_main.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gl_texture.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hu_lib.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hu_stuff.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/info.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lprintf.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/m_argv.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/m_bbox.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/m_cheat.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/m_menu.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/m_misc.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/m_random.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/md5.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mmus2mid.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/p_ceilng.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/p_checksum.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/p_doors.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/p_enemy.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/p_floor.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/p_genlin.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/p_inter.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/p_lights.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/p_map.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/p_maputl.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/p_mobj.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/p_plats.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/p_pspr.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/p_saveg.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/p_setup.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/p_sight.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/p_spec.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/p_switch.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/p_telept.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/p_tick.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/p_user.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/r_bsp.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/r_data.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/r_demo.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/r_draw.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/r_filter.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/r_fps.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/r_main.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/r_patch.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/r_plane.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/r_segs.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/r_sky.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/r_things.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/s_sound.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sounds.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/st_lib.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/st_stuff.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tables.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/v_video.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/version.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/w_memcache.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/w_mmap.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/w_wad.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/wi_stuff.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/z_bmalloc.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/z_zone.Po@am__quote@ + +.c.o: +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCC_TRUE@ mv -f $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(COMPILE) -c $< + +.c.obj: +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` +@am__fastdepCC_TRUE@ mv -f $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(COMPILE) -c `$(CYGPATH_W) '$<'` + +# This directory's subdirectories are mostly independent; you can cd +# into them and run `make' without going through this Makefile. +# To change the values of `make' variables: instead of editing Makefiles, +# (1) if the variable is set in `config.status', edit `config.status' +# (which will cause the Makefiles to be regenerated when you run `make'); +# (2) otherwise, pass the desired values on the `make' command line. +$(RECURSIVE_TARGETS): + @failcom='exit 1'; \ + for f in x $$MAKEFLAGS; do \ + case $$f in \ + *=* | --[!k]*);; \ + *k*) failcom='fail=yes';; \ + esac; \ + done; \ + dot_seen=no; \ + target=`echo $@ | sed s/-recursive//`; \ + list='$(SUBDIRS)'; for subdir in $$list; do \ + echo "Making $$target in $$subdir"; \ + if test "$$subdir" = "."; then \ + dot_seen=yes; \ + local_target="$$target-am"; \ + else \ + local_target="$$target"; \ + fi; \ + (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ + || eval $$failcom; \ + done; \ + if test "$$dot_seen" = "no"; then \ + $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \ + fi; test -z "$$fail" + +$(RECURSIVE_CLEAN_TARGETS): + @failcom='exit 1'; \ + for f in x $$MAKEFLAGS; do \ + case $$f in \ + *=* | --[!k]*);; \ + *k*) failcom='fail=yes';; \ + esac; \ + done; \ + dot_seen=no; \ + case "$@" in \ + distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \ + *) list='$(SUBDIRS)' ;; \ + esac; \ + rev=''; for subdir in $$list; do \ + if test "$$subdir" = "."; then :; else \ + rev="$$subdir $$rev"; \ + fi; \ + done; \ + rev="$$rev ."; \ + target=`echo $@ | sed s/-recursive//`; \ + for subdir in $$rev; do \ + echo "Making $$target in $$subdir"; \ + if test "$$subdir" = "."; then \ + local_target="$$target-am"; \ + else \ + local_target="$$target"; \ + fi; \ + (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ + || eval $$failcom; \ + done && test -z "$$fail" +tags-recursive: + list='$(SUBDIRS)'; for subdir in $$list; do \ + test "$$subdir" = . || (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) tags); \ + done +ctags-recursive: + list='$(SUBDIRS)'; for subdir in $$list; do \ + test "$$subdir" = . || (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) ctags); \ + done + +ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + mkid -fID $$unique +tags: TAGS + +TAGS: tags-recursive $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + tags=; \ + here=`pwd`; \ + if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \ + include_option=--etags-include; \ + empty_fix=.; \ + else \ + include_option=--include; \ + empty_fix=; \ + fi; \ + list='$(SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + test ! -f $$subdir/TAGS || \ + tags="$$tags $$include_option=$$here/$$subdir/TAGS"; \ + fi; \ + done; \ + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + if test -z "$(ETAGS_ARGS)$$tags$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$tags $$unique; \ + fi +ctags: CTAGS +CTAGS: ctags-recursive $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + tags=; \ + here=`pwd`; \ + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + test -z "$(CTAGS_ARGS)$$tags$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$tags $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && cd $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) $$here + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \ + fi; \ + cp -pR $$d/$$file $(distdir)$$dir || exit 1; \ + else \ + test -f $(distdir)/$$file \ + || cp -p $$d/$$file $(distdir)/$$file \ + || exit 1; \ + fi; \ + done + list='$(DIST_SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + test -d "$(distdir)/$$subdir" \ + || $(MKDIR_P) "$(distdir)/$$subdir" \ + || exit 1; \ + distdir=`$(am__cd) $(distdir) && pwd`; \ + top_distdir=`$(am__cd) $(top_distdir) && pwd`; \ + (cd $$subdir && \ + $(MAKE) $(AM_MAKEFLAGS) \ + top_distdir="$$top_distdir" \ + distdir="$$distdir/$$subdir" \ + am__remove_distdir=: \ + am__skip_length_check=: \ + distdir) \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-recursive +all-am: Makefile $(PROGRAMS) +installdirs: installdirs-recursive +installdirs-am: + for dir in "$(DESTDIR)$(gamesdir)"; do \ + test -z "$$dir" || $(MKDIR_P) "$$dir"; \ + done +install: install-recursive +install-exec: install-exec-recursive +install-data: install-data-recursive +uninstall: uninstall-recursive + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-recursive +install-strip: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + `test -z '$(STRIP)' || \ + echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-recursive + +clean-am: clean-gamesPROGRAMS clean-generic mostlyclean-am + +distclean: distclean-recursive + -rm -rf ./$(DEPDIR) + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +dvi: dvi-recursive + +dvi-am: + +html: html-recursive + +info: info-recursive + +info-am: + +install-data-am: install-gamesPROGRAMS + +install-dvi: install-dvi-recursive + +install-exec-am: + +install-html: install-html-recursive + +install-info: install-info-recursive + +install-man: + +install-pdf: install-pdf-recursive + +install-ps: install-ps-recursive + +installcheck-am: + +maintainer-clean: maintainer-clean-recursive + -rm -rf ./$(DEPDIR) + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-recursive + +mostlyclean-am: mostlyclean-compile mostlyclean-generic + +pdf: pdf-recursive + +pdf-am: + +ps: ps-recursive + +ps-am: + +uninstall-am: uninstall-gamesPROGRAMS + +.MAKE: $(RECURSIVE_CLEAN_TARGETS) $(RECURSIVE_TARGETS) install-am \ + install-strip + +.PHONY: $(RECURSIVE_CLEAN_TARGETS) $(RECURSIVE_TARGETS) CTAGS GTAGS \ + all all-am check check-am clean clean-gamesPROGRAMS \ + clean-generic ctags ctags-recursive distclean \ + distclean-compile distclean-generic distclean-tags distdir dvi \ + dvi-am html html-am info info-am install install-am \ + install-data install-data-am install-dvi install-dvi-am \ + install-exec install-exec-am install-gamesPROGRAMS \ + install-html install-html-am install-info install-info-am \ + install-man install-pdf install-pdf-am install-ps \ + install-ps-am install-strip installcheck installcheck-am \ + installdirs installdirs-am maintainer-clean \ + maintainer-clean-generic mostlyclean mostlyclean-compile \ + mostlyclean-generic pdf pdf-am ps ps-am tags tags-recursive \ + uninstall uninstall-am uninstall-gamesPROGRAMS + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/src/am_map.c b/src/am_map.c new file mode 100644 index 0000000..040c40b --- /dev/null +++ b/src/am_map.c @@ -0,0 +1,1585 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * the automap code + * + *----------------------------------------------------------------------------- + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "doomstat.h" +#include "st_stuff.h" +#include "r_main.h" +#include "p_setup.h" +#include "p_maputl.h" +#include "w_wad.h" +#include "v_video.h" +#include "p_spec.h" +#include "am_map.h" +#include "dstrings.h" +#include "d_deh.h" // Ty 03/27/98 - externalizations +#include "lprintf.h" // jff 08/03/98 - declaration of lprintf +#include "g_game.h" + +//jff 1/7/98 default automap colors added +int mapcolor_back; // map background +int mapcolor_grid; // grid lines color +int mapcolor_wall; // normal 1s wall color +int mapcolor_fchg; // line at floor height change color +int mapcolor_cchg; // line at ceiling height change color +int mapcolor_clsd; // line at sector with floor=ceiling color +int mapcolor_rkey; // red key color +int mapcolor_bkey; // blue key color +int mapcolor_ykey; // yellow key color +int mapcolor_rdor; // red door color (diff from keys to allow option) +int mapcolor_bdor; // blue door color (of enabling one but not other ) +int mapcolor_ydor; // yellow door color +int mapcolor_tele; // teleporter line color +int mapcolor_secr; // secret sector boundary color +int mapcolor_exit; // jff 4/23/98 add exit line color +int mapcolor_unsn; // computer map unseen line color +int mapcolor_flat; // line with no floor/ceiling changes +int mapcolor_sprt; // general sprite color +int mapcolor_item; // item sprite color +int mapcolor_frnd; // friendly sprite color +int mapcolor_enemy; // enemy sprite color +int mapcolor_hair; // crosshair color +int mapcolor_sngl; // single player arrow color +int mapcolor_plyr[4] = { 112, 88, 64, 32 }; // colors for player arrows in multiplayer + +//jff 3/9/98 add option to not show secret sectors until entered +int map_secret_after; +//jff 4/3/98 add symbols for "no-color" for disable and "black color" for black +#define NC 0 +#define BC 247 + +// drawing stuff +#define FB 0 + +// scale on entry +#define INITSCALEMTOF (.2*FRACUNIT) +// how much the automap moves window per tic in frame-buffer coordinates +// moves 140 pixels in 1 second +#define F_PANINC 4 +// how much zoom-in per tic +// goes to 2x in 1 second +#define M_ZOOMIN ((int) (1.02*FRACUNIT)) +// how much zoom-out per tic +// pulls out to 0.5x in 1 second +#define M_ZOOMOUT ((int) (FRACUNIT/1.02)) + +#define PLAYERRADIUS (16*(1<>16) +// translates between frame-buffer and map coordinates +#define CXMTOF(x) (f_x + MTOF((x)-m_x)) +#define CYMTOF(y) (f_y + (f_h - MTOF((y)-m_y))) + +typedef struct +{ + mpoint_t a, b; +} mline_t; + +// +// The vector graphics for the automap. +// A line drawing of the player pointing right, +// starting from the middle. +// +#define R ((8*PLAYERRADIUS)/7) +mline_t player_arrow[] = +{ + { { -R+R/8, 0 }, { R, 0 } }, // ----- + { { R, 0 }, { R-R/2, R/4 } }, // -----> + { { R, 0 }, { R-R/2, -R/4 } }, + { { -R+R/8, 0 }, { -R-R/8, R/4 } }, // >----> + { { -R+R/8, 0 }, { -R-R/8, -R/4 } }, + { { -R+3*R/8, 0 }, { -R+R/8, R/4 } }, // >>---> + { { -R+3*R/8, 0 }, { -R+R/8, -R/4 } } +}; +#undef R +#define NUMPLYRLINES (sizeof(player_arrow)/sizeof(mline_t)) + +#define R ((8*PLAYERRADIUS)/7) +mline_t cheat_player_arrow[] = +{ // killough 3/22/98: He's alive, Jim :) + { { -R+R/8, 0 }, { R, 0 } }, // ----- + { { R, 0 }, { R-R/2, R/4 } }, // -----> + { { R, 0 }, { R-R/2, -R/4 } }, + { { -R+R/8, 0 }, { -R-R/8, R/4 } }, // >----> + { { -R+R/8, 0 }, { -R-R/8, -R/4 } }, + { { -R+3*R/8, 0 }, { -R+R/8, R/4 } }, // >>---> + { { -R+3*R/8, 0 }, { -R+R/8, -R/4 } }, + { { -R/10-R/6, R/4}, {-R/10-R/6, -R/4} }, // J + { { -R/10-R/6, -R/4}, {-R/10-R/6-R/8, -R/4} }, + { { -R/10-R/6-R/8, -R/4}, {-R/10-R/6-R/8, -R/8} }, + { { -R/10, R/4}, {-R/10, -R/4}}, // F + { { -R/10, R/4}, {-R/10+R/8, R/4}}, + { { -R/10+R/4, R/4}, {-R/10+R/4, -R/4}}, // F + { { -R/10+R/4, R/4}, {-R/10+R/4+R/8, R/4}}, +}; +#undef R +#define NUMCHEATPLYRLINES (sizeof(cheat_player_arrow)/sizeof(mline_t)) + +#define R (FRACUNIT) +mline_t triangle_guy[] = +{ +{ { (fixed_t)(-.867*R), (fixed_t)(-.5*R) }, { (fixed_t)( .867*R), (fixed_t)(-.5*R) } }, +{ { (fixed_t)( .867*R), (fixed_t)(-.5*R) }, { (fixed_t)(0 ), (fixed_t)( R) } }, +{ { (fixed_t)(0 ), (fixed_t)( R) }, { (fixed_t)(-.867*R), (fixed_t)(-.5*R) } } +}; +#undef R +#define NUMTRIANGLEGUYLINES (sizeof(triangle_guy)/sizeof(mline_t)) + +//jff 1/5/98 new symbol for keys on automap +#define R (FRACUNIT) +mline_t cross_mark[] = +{ + { { -R, 0 }, { R, 0} }, + { { 0, -R }, { 0, R } }, +}; +#undef R +#define NUMCROSSMARKLINES (sizeof(cross_mark)/sizeof(mline_t)) +//jff 1/5/98 end of new symbol + +#define R (FRACUNIT) +mline_t thintriangle_guy[] = +{ +{ { (fixed_t)(-.5*R), (fixed_t)(-.7*R) }, { (fixed_t)( R), (fixed_t)( 0) } }, +{ { (fixed_t)( R), (fixed_t)( 0) }, { (fixed_t)(-.5*R), (fixed_t)( .7*R) } }, +{ { (fixed_t)(-.5*R), (fixed_t)( .7*R) }, { (fixed_t)(-.5*R), (fixed_t)(-.7*R) } } +}; +#undef R +#define NUMTHINTRIANGLEGUYLINES (sizeof(thintriangle_guy)/sizeof(mline_t)) + +int ddt_cheating = 0; // killough 2/7/98: make global, rename to ddt_* + +static int leveljuststarted = 1; // kluge until AM_LevelInit() is called + +enum automapmode_e automapmode; // Mode that the automap is in + +// location of window on screen +static int f_x; +static int f_y; + +// size of window on screen +static int f_w; +static int f_h; + +static mpoint_t m_paninc; // how far the window pans each tic (map coords) +static fixed_t mtof_zoommul; // how far the window zooms each tic (map coords) +static fixed_t ftom_zoommul; // how far the window zooms each tic (fb coords) + +static fixed_t m_x, m_y; // LL x,y window location on the map (map coords) +static fixed_t m_x2, m_y2; // UR x,y window location on the map (map coords) + +// +// width/height of window on map (map coords) +// +static fixed_t m_w; +static fixed_t m_h; + +// based on level size +static fixed_t min_x; +static fixed_t min_y; +static fixed_t max_x; +static fixed_t max_y; + +static fixed_t max_w; // max_x-min_x, +static fixed_t max_h; // max_y-min_y + +// based on player size +static fixed_t min_w; +static fixed_t min_h; + + +static fixed_t min_scale_mtof; // used to tell when to stop zooming out +static fixed_t max_scale_mtof; // used to tell when to stop zooming in + +// old stuff for recovery later +static fixed_t old_m_w, old_m_h; +static fixed_t old_m_x, old_m_y; + +// old location used by the Follower routine +static mpoint_t f_oldloc; + +// used by MTOF to scale from map-to-frame-buffer coords +static fixed_t scale_mtof = (fixed_t)INITSCALEMTOF; +// used by FTOM to scale from frame-buffer-to-map coords (=1/scale_mtof) +static fixed_t scale_ftom; + +static player_t *plr; // the player represented by an arrow + +// killough 2/22/98: Remove limit on automap marks, +// and make variables external for use in savegames. + +mpoint_t *markpoints = NULL; // where the points are +int markpointnum = 0; // next point to be assigned (also number of points now) +int markpointnum_max = 0; // killough 2/22/98 + +static boolean stopped = true; + +// +// AM_activateNewScale() +// +// Changes the map scale after zooming or translating +// +// Passed nothing, returns nothing +// +static void AM_activateNewScale(void) +{ + m_x += m_w/2; + m_y += m_h/2; + m_w = FTOM(f_w); + m_h = FTOM(f_h); + m_x -= m_w/2; + m_y -= m_h/2; + m_x2 = m_x + m_w; + m_y2 = m_y + m_h; +} + +// +// AM_saveScaleAndLoc() +// +// Saves the current center and zoom +// Affects the variables that remember old scale and loc +// +// Passed nothing, returns nothing +// +static void AM_saveScaleAndLoc(void) +{ + old_m_x = m_x; + old_m_y = m_y; + old_m_w = m_w; + old_m_h = m_h; +} + +// +// AM_restoreScaleAndLoc() +// +// restores the center and zoom from locally saved values +// Affects global variables for location and scale +// +// Passed nothing, returns nothing +// +static void AM_restoreScaleAndLoc(void) +{ + m_w = old_m_w; + m_h = old_m_h; + if (!(automapmode & am_follow)) + { + m_x = old_m_x; + m_y = old_m_y; + } + else + { + m_x = (plr->mo->x >> FRACTOMAPBITS) - m_w/2;//e6y + m_y = (plr->mo->y >> FRACTOMAPBITS) - m_h/2;//e6y + } + m_x2 = m_x + m_w; + m_y2 = m_y + m_h; + + // Change the scaling multipliers + scale_mtof = FixedDiv(f_w<= markpointnum_max) + markpoints = realloc(markpoints, + (markpointnum_max = markpointnum_max ? + markpointnum_max*2 : 16) * sizeof(*markpoints)); + + markpoints[markpointnum].x = m_x + m_w/2; + markpoints[markpointnum].y = m_y + m_h/2; + markpointnum++; +} + +// +// AM_findMinMaxBoundaries() +// +// Determines bounding box of all vertices, +// sets global variables controlling zoom range. +// +// Passed nothing, returns nothing +// +static void AM_findMinMaxBoundaries(void) +{ + int i; + fixed_t a; + fixed_t b; + + min_x = min_y = INT_MAX; + max_x = max_y = -INT_MAX; + + for (i=0;i max_x) + max_x = vertexes[i].x; + + if (vertexes[i].y < min_y) + min_y = vertexes[i].y; + else if (vertexes[i].y > max_y) + max_y = vertexes[i].y; + } + + max_w = (max_x >>= FRACTOMAPBITS) - (min_x >>= FRACTOMAPBITS);//e6y + max_h = (max_y >>= FRACTOMAPBITS) - (min_y >>= FRACTOMAPBITS);//e6y + + min_w = 2*PLAYERRADIUS; // const? never changed? + min_h = 2*PLAYERRADIUS; + + a = FixedDiv(f_w< max_x) + m_x = max_x - m_w/2; + else if (m_x + m_w/2 < min_x) + m_x = min_x - m_w/2; + + if (m_y + m_h/2 > max_y) + m_y = max_y - m_h/2; + else if (m_y + m_h/2 < min_y) + m_y = min_y - m_h/2; + + m_x2 = m_x + m_w; + m_y2 = m_y + m_h; +} + + +// +// AM_initVariables() +// +// Initialize the variables for the automap +// +// Affects the automap global variables +// Status bar is notified that the automap has been entered +// Passed nothing, returns nothing +// +static void AM_initVariables(void) +{ + int pnum; + static event_t st_notify = { ev_keyup, AM_MSGENTERED, 0, 0 }; + + automapmode |= am_active; + + f_oldloc.x = INT_MAX; + + m_paninc.x = m_paninc.y = 0; + ftom_zoommul = FRACUNIT; + mtof_zoommul = FRACUNIT; + + m_w = FTOM(f_w); + m_h = FTOM(f_h); + + // find player to center on initially + if (!playeringame[pnum = consoleplayer]) + for (pnum=0;pnummo->x >> FRACTOMAPBITS) - m_w/2;//e6y + m_y = (plr->mo->y >> FRACTOMAPBITS) - m_h/2;//e6y + AM_changeWindowLoc(); + + // for saving & restoring + old_m_x = m_x; + old_m_y = m_y; + old_m_w = m_w; + old_m_h = m_h; + + // inform the status bar of the change + ST_Responder(&st_notify); +} + +// +// AM_loadPics() +// +static void AM_loadPics(void) +{ + // cph - mark numbers no longer needed cached +} + +// +// AM_unloadPics() +// +static void AM_unloadPics(void) +{ + // cph - mark numbers no longer needed cached +} + +// +// AM_clearMarks() +// +// Sets the number of marks to 0, thereby clearing them from the display +// +// Affects the global variable markpointnum +// Passed nothing, returns nothing +// +void AM_clearMarks(void) +{ + markpointnum = 0; +} + +// +// AM_LevelInit() +// +// Initialize the automap at the start of a new level +// should be called at the start of every level +// +// Passed nothing, returns nothing +// Affects automap's global variables +// +// CPhipps - get status bar height from status bar code +static void AM_LevelInit(void) +{ + leveljuststarted = 0; + + f_x = f_y = 0; + f_w = SCREENWIDTH; // killough 2/7/98: get rid of finit_ vars + f_h = SCREENHEIGHT-ST_SCALED_HEIGHT;// to allow runtime setting of width/height + + AM_findMinMaxBoundaries(); + scale_mtof = FixedDiv(min_scale_mtof, (int) (0.7*FRACUNIT)); + if (scale_mtof > max_scale_mtof) + scale_mtof = min_scale_mtof; + scale_ftom = FixedDiv(FRACUNIT, scale_mtof); +} + +// +// AM_Stop() +// +// Cease automap operations, unload patches, notify status bar +// +// Passed nothing, returns nothing +// +void AM_Stop (void) +{ + static event_t st_notify = { 0, ev_keyup, AM_MSGEXITED, 0 }; + + AM_unloadPics(); + automapmode &= ~am_active; + ST_Responder(&st_notify); + stopped = true; +} + +// +// AM_Start() +// +// Start up automap operations, +// if a new level, or game start, (re)initialize level variables +// init map variables +// load mark patches +// +// Passed nothing, returns nothing +// +void AM_Start(void) +{ + static int lastlevel = -1, lastepisode = -1; + + if (!stopped) + AM_Stop(); + stopped = false; + if (lastlevel != gamemap || lastepisode != gameepisode) + { + AM_LevelInit(); + lastlevel = gamemap; + lastepisode = gameepisode; + } + AM_initVariables(); + AM_loadPics(); +} + +// +// AM_minOutWindowScale() +// +// Set the window scale to the maximum size +// +// Passed nothing, returns nothing +// +static void AM_minOutWindowScale(void) +{ + scale_mtof = min_scale_mtof; + scale_ftom = FixedDiv(FRACUNIT, scale_mtof); + AM_activateNewScale(); +} + +// +// AM_maxOutWindowScale(void) +// +// Set the window scale to the minimum size +// +// Passed nothing, returns nothing +// +static void AM_maxOutWindowScale(void) +{ + scale_mtof = max_scale_mtof; + scale_ftom = FixedDiv(FRACUNIT, scale_mtof); + AM_activateNewScale(); +} + +// +// AM_Responder() +// +// Handle events (user inputs) in automap mode +// +// Passed an input event, returns true if its handled +// +boolean AM_Responder +( event_t* ev ) +{ + int rc; + static int cheatstate=0; + static int bigstate=0; + int ch; // phares + + rc = false; + + if (!(automapmode & am_active)) + { + if (ev->type == ev_keydown && ev->data1 == key_map) // phares + { + AM_Start (); + rc = true; + } + } + else if (ev->type == ev_keydown) + { + rc = true; + ch = ev->data1; // phares + if (ch == key_map_right) // | + if (!(automapmode & am_follow)) // V + m_paninc.x = FTOM(F_PANINC); + else + rc = false; + else if (ch == key_map_left) + if (!(automapmode & am_follow)) + m_paninc.x = -FTOM(F_PANINC); + else + rc = false; + else if (ch == key_map_up) + if (!(automapmode & am_follow)) + m_paninc.y = FTOM(F_PANINC); + else + rc = false; + else if (ch == key_map_down) + if (!(automapmode & am_follow)) + m_paninc.y = -FTOM(F_PANINC); + else + rc = false; + else if (ch == key_map_zoomout) + { + mtof_zoommul = M_ZOOMOUT; + ftom_zoommul = M_ZOOMIN; + } + else if (ch == key_map_zoomin) + { + mtof_zoommul = M_ZOOMIN; + ftom_zoommul = M_ZOOMOUT; + } + else if (ch == key_map) + { + bigstate = 0; + AM_Stop (); + } + else if (ch == key_map_gobig) + { + bigstate = !bigstate; + if (bigstate) + { + AM_saveScaleAndLoc(); + AM_minOutWindowScale(); + } + else + AM_restoreScaleAndLoc(); + } + else if (ch == key_map_follow) + { + automapmode ^= am_follow; // CPhipps - put all automap mode stuff into one enum + f_oldloc.x = INT_MAX; + // Ty 03/27/98 - externalized + plr->message = (automapmode & am_follow) ? s_AMSTR_FOLLOWON : s_AMSTR_FOLLOWOFF; + } + else if (ch == key_map_grid) + { + automapmode ^= am_grid; // CPhipps + // Ty 03/27/98 - *not* externalized + plr->message = (automapmode & am_grid) ? s_AMSTR_GRIDON : s_AMSTR_GRIDOFF; + } + else if (ch == key_map_mark) + { + /* Ty 03/27/98 - *not* externalized + * cph 2001/11/20 - use doom_printf so we don't have our own buffer */ + doom_printf("%s %d", s_AMSTR_MARKEDSPOT, markpointnum); + AM_addMark(); + } + else if (ch == key_map_clear) + { + AM_clearMarks(); // Ty 03/27/98 - *not* externalized + plr->message = s_AMSTR_MARKSCLEARED; // ^ + } // | + else if (ch == key_map_rotate) { + automapmode ^= am_rotate; + plr->message = (automapmode & am_rotate) ? s_AMSTR_ROTATEON : s_AMSTR_ROTATEOFF; + } + else if (ch == key_map_overlay) { + automapmode ^= am_overlay; + plr->message = (automapmode & am_overlay) ? s_AMSTR_OVERLAYON : s_AMSTR_OVERLAYOFF; + } + else // phares + { + cheatstate=0; + rc = false; + } + } + else if (ev->type == ev_keyup) + { + rc = false; + ch = ev->data1; + if (ch == key_map_right) + { + if (!(automapmode & am_follow)) + m_paninc.x = 0; + } + else if (ch == key_map_left) + { + if (!(automapmode & am_follow)) + m_paninc.x = 0; + } + else if (ch == key_map_up) + { + if (!(automapmode & am_follow)) + m_paninc.y = 0; + } + else if (ch == key_map_down) + { + if (!(automapmode & am_follow)) + m_paninc.y = 0; + } + else if ((ch == key_map_zoomout) || (ch == key_map_zoomin)) + { + mtof_zoommul = FRACUNIT; + ftom_zoommul = FRACUNIT; + } + } + return rc; +} + +// +// AM_rotate() +// +// Rotation in 2D. +// Used to rotate player arrow line character. +// +// Passed the coordinates of a point, and an angle +// Returns the coordinates rotated by the angle +// +// CPhipps - made static & enhanced for automap rotation + +static void AM_rotate(fixed_t* x, fixed_t* y, angle_t a, fixed_t xorig, fixed_t yorig) +{ + fixed_t tmpx; + + //e6y + xorig>>=FRACTOMAPBITS; + yorig>>=FRACTOMAPBITS; + + tmpx = + FixedMul(*x - xorig,finecosine[a>>ANGLETOFINESHIFT]) + - FixedMul(*y - yorig,finesine[a>>ANGLETOFINESHIFT]); + + *y = yorig + + FixedMul(*x - xorig,finesine[a>>ANGLETOFINESHIFT]) + + FixedMul(*y - yorig,finecosine[a>>ANGLETOFINESHIFT]); + + *x = tmpx + xorig; +} + +// +// AM_changeWindowScale() +// +// Automap zooming +// +// Passed nothing, returns nothing +// +static void AM_changeWindowScale(void) +{ + // Change the scaling multipliers + scale_mtof = FixedMul(scale_mtof, mtof_zoommul); + scale_ftom = FixedDiv(FRACUNIT, scale_mtof); + + if (scale_mtof < min_scale_mtof) + AM_minOutWindowScale(); + else if (scale_mtof > max_scale_mtof) + AM_maxOutWindowScale(); + else + AM_activateNewScale(); +} + +// +// AM_doFollowPlayer() +// +// Turn on follow mode - the map scrolls opposite to player motion +// +// Passed nothing, returns nothing +// +static void AM_doFollowPlayer(void) +{ + if (f_oldloc.x != plr->mo->x || f_oldloc.y != plr->mo->y) + { + m_x = FTOM(MTOF(plr->mo->x >> FRACTOMAPBITS)) - m_w/2;//e6y + m_y = FTOM(MTOF(plr->mo->y >> FRACTOMAPBITS)) - m_h/2;//e6y + m_x2 = m_x + m_w; + m_y2 = m_y + m_h; + f_oldloc.x = plr->mo->x; + f_oldloc.y = plr->mo->y; + } +} + +// +// AM_Ticker() +// +// Updates on gametic - enter follow mode, zoom, or change map location +// +// Passed nothing, returns nothing +// +void AM_Ticker (void) +{ + if (!(automapmode & am_active)) + return; + + if (automapmode & am_follow) + AM_doFollowPlayer(); + + // Change the zoom if necessary + if (ftom_zoommul != FRACUNIT) + AM_changeWindowScale(); + + // Change x,y location + if (m_paninc.x || m_paninc.y) + AM_changeWindowLoc(); +} + +// +// AM_clipMline() +// +// Automap clipping of lines. +// +// Based on Cohen-Sutherland clipping algorithm but with a slightly +// faster reject and precalculated slopes. If the speed is needed, +// use a hash algorithm to handle the common cases. +// +// Passed the line's coordinates on map and in the frame buffer performs +// clipping on them in the lines frame coordinates. +// Returns true if any part of line was not clipped +// +static boolean AM_clipMline +( mline_t* ml, + fline_t* fl ) +{ + enum + { + LEFT =1, + RIGHT =2, + BOTTOM =4, + TOP =8 + }; + + register int outcode1 = 0; + register int outcode2 = 0; + register int outside; + + fpoint_t tmp; + int dx; + int dy; + + +#define DOOUTCODE(oc, mx, my) \ + (oc) = 0; \ + if ((my) < 0) (oc) |= TOP; \ + else if ((my) >= f_h) (oc) |= BOTTOM; \ + if ((mx) < 0) (oc) |= LEFT; \ + else if ((mx) >= f_w) (oc) |= RIGHT; + + + // do trivial rejects and outcodes + if (ml->a.y > m_y2) + outcode1 = TOP; + else if (ml->a.y < m_y) + outcode1 = BOTTOM; + + if (ml->b.y > m_y2) + outcode2 = TOP; + else if (ml->b.y < m_y) + outcode2 = BOTTOM; + + if (outcode1 & outcode2) + return false; // trivially outside + + if (ml->a.x < m_x) + outcode1 |= LEFT; + else if (ml->a.x > m_x2) + outcode1 |= RIGHT; + + if (ml->b.x < m_x) + outcode2 |= LEFT; + else if (ml->b.x > m_x2) + outcode2 |= RIGHT; + + if (outcode1 & outcode2) + return false; // trivially outside + + // transform to frame-buffer coordinates. + fl->a.x = CXMTOF(ml->a.x); + fl->a.y = CYMTOF(ml->a.y); + fl->b.x = CXMTOF(ml->b.x); + fl->b.y = CYMTOF(ml->b.y); + + DOOUTCODE(outcode1, fl->a.x, fl->a.y); + DOOUTCODE(outcode2, fl->b.x, fl->b.y); + + if (outcode1 & outcode2) + return false; + + while (outcode1 | outcode2) + { + // may be partially inside box + // find an outside point + if (outcode1) + outside = outcode1; + else + outside = outcode2; + + // clip to each side + if (outside & TOP) + { + dy = fl->a.y - fl->b.y; + dx = fl->b.x - fl->a.x; + tmp.x = fl->a.x + (dx*(fl->a.y))/dy; + tmp.y = 0; + } + else if (outside & BOTTOM) + { + dy = fl->a.y - fl->b.y; + dx = fl->b.x - fl->a.x; + tmp.x = fl->a.x + (dx*(fl->a.y-f_h))/dy; + tmp.y = f_h-1; + } + else if (outside & RIGHT) + { + dy = fl->b.y - fl->a.y; + dx = fl->b.x - fl->a.x; + tmp.y = fl->a.y + (dy*(f_w-1 - fl->a.x))/dx; + tmp.x = f_w-1; + } + else if (outside & LEFT) + { + dy = fl->b.y - fl->a.y; + dx = fl->b.x - fl->a.x; + tmp.y = fl->a.y + (dy*(-fl->a.x))/dx; + tmp.x = 0; + } + + if (outside == outcode1) + { + fl->a = tmp; + DOOUTCODE(outcode1, fl->a.x, fl->a.y); + } + else + { + fl->b = tmp; + DOOUTCODE(outcode2, fl->b.x, fl->b.y); + } + + if (outcode1 & outcode2) + return false; // trivially outside + } + + return true; +} +#undef DOOUTCODE + +// +// AM_drawMline() +// +// Clip lines, draw visible parts of lines. +// +// Passed the map coordinates of the line, and the color to draw it +// Color -1 is special and prevents drawing. Color 247 is special and +// is translated to black, allowing Color 0 to represent feature disable +// in the defaults file. +// Returns nothing. +// +static void AM_drawMline +( mline_t* ml, + int color ) +{ + static fline_t fl; + + if (color==-1) // jff 4/3/98 allow not drawing any sort of line + return; // by setting its color to -1 + if (color==247) // jff 4/3/98 if color is 247 (xparent), use black + color=0; + + if (AM_clipMline(ml, &fl)) + V_DrawLine(&fl, color); // draws it on frame buffer using fb coords +} + +// +// AM_drawGrid() +// +// Draws blockmap aligned grid lines. +// +// Passed the color to draw the grid lines +// Returns nothing +// +static void AM_drawGrid(int color) +{ + fixed_t x, y; + fixed_t start, end; + mline_t ml; + + // Figure out start of vertical gridlines + start = m_x; + if ((start-bmaporgx)%(MAPBLOCKUNITS<> LockedKeyShift; + if (!type || type==7) + return 3; //any or all keys + else return (type-1)%3; + } + switch (type) // closed keyed door + { + case 26: case 32: case 99: case 133: + /*bluekey*/ + return 1; + case 27: case 34: case 136: case 137: + /*yellowkey*/ + return 2; + case 28: case 33: case 134: case 135: + /*redkey*/ + return 0; + default: + return -1; //not a keyed door + } +} + +// +// Determines visible lines, draws them. +// This is LineDef based, not LineSeg based. +// +// jff 1/5/98 many changes in this routine +// backward compatibility not needed, so just changes, no ifs +// addition of clauses for: +// doors opening, keyed door id, secret sectors, +// teleports, exit lines, key things +// ability to suppress any of added features or lines with no height changes +// +// support for gamma correction in automap abandoned +// +// jff 4/3/98 changed mapcolor_xxxx=0 as control to disable feature +// jff 4/3/98 changed mapcolor_xxxx=-1 to disable drawing line completely +// +static void AM_drawWalls(void) +{ + int i; + static mline_t l; + + // draw the unclipped visible portions of all lines + for (i=0;ix >> FRACTOMAPBITS;//e6y + l.a.y = lines[i].v1->y >> FRACTOMAPBITS;//e6y + l.b.x = lines[i].v2->x >> FRACTOMAPBITS;//e6y + l.b.y = lines[i].v2->y >> FRACTOMAPBITS;//e6y + + if (automapmode & am_rotate) { + AM_rotate(&l.a.x, &l.a.y, ANG90-plr->mo->angle, plr->mo->x, plr->mo->y); + AM_rotate(&l.b.x, &l.b.y, ANG90-plr->mo->angle, plr->mo->x, plr->mo->y); + } + + // if line has been seen or IDDT has been used + if (ddt_cheating || (lines[i].flags & ML_MAPPED)) + { + if ((lines[i].flags & ML_DONTDRAW) && !ddt_cheating) + continue; + { + /* cph - show keyed doors and lines */ + int amd; + if ((mapcolor_bdor || mapcolor_ydor || mapcolor_rdor) && + !(lines[i].flags & ML_SECRET) && /* non-secret */ + (amd = AM_DoorColor(lines[i].special)) != -1 + ) + { + { + switch (amd) /* closed keyed door */ + { + case 1: + /*bluekey*/ + AM_drawMline(&l, + mapcolor_bdor? mapcolor_bdor : mapcolor_cchg); + continue; + case 2: + /*yellowkey*/ + AM_drawMline(&l, + mapcolor_ydor? mapcolor_ydor : mapcolor_cchg); + continue; + case 0: + /*redkey*/ + AM_drawMline(&l, + mapcolor_rdor? mapcolor_rdor : mapcolor_cchg); + continue; + case 3: + /*any or all*/ + AM_drawMline(&l, + mapcolor_clsd? mapcolor_clsd : mapcolor_cchg); + continue; + } + } + } + } + if /* jff 4/23/98 add exit lines to automap */ + ( + mapcolor_exit && + ( + lines[i].special==11 || + lines[i].special==52 || + lines[i].special==197 || + lines[i].special==51 || + lines[i].special==124 || + lines[i].special==198 + ) + ) { + AM_drawMline(&l, mapcolor_exit); /* exit line */ + continue; + } + + if (!lines[i].backsector) + { + // jff 1/10/98 add new color for 1S secret sector boundary + if (mapcolor_secr && //jff 4/3/98 0 is disable + ( + ( + map_secret_after && + P_WasSecret(lines[i].frontsector) && + !P_IsSecret(lines[i].frontsector) + ) + || + ( + !map_secret_after && + P_WasSecret(lines[i].frontsector) + ) + ) + ) + AM_drawMline(&l, mapcolor_secr); // line bounding secret sector + else //jff 2/16/98 fixed bug + AM_drawMline(&l, mapcolor_wall); // special was cleared + } + else /* now for 2S lines */ + { + // jff 1/10/98 add color change for all teleporter types + if + ( + mapcolor_tele && !(lines[i].flags & ML_SECRET) && + (lines[i].special == 39 || lines[i].special == 97 || + lines[i].special == 125 || lines[i].special == 126) + ) + { // teleporters + AM_drawMline(&l, mapcolor_tele); + } + else if (lines[i].flags & ML_SECRET) // secret door + { + AM_drawMline(&l, mapcolor_wall); // wall color + } + else if + ( + mapcolor_clsd && + !(lines[i].flags & ML_SECRET) && // non-secret closed door + ((lines[i].backsector->floorheight==lines[i].backsector->ceilingheight) || + (lines[i].frontsector->floorheight==lines[i].frontsector->ceilingheight)) + ) + { + AM_drawMline(&l, mapcolor_clsd); // non-secret closed door + } //jff 1/6/98 show secret sector 2S lines + else if + ( + mapcolor_secr && //jff 2/16/98 fixed bug + ( // special was cleared after getting it + (map_secret_after && + ( + (P_WasSecret(lines[i].frontsector) + && !P_IsSecret(lines[i].frontsector)) || + (P_WasSecret(lines[i].backsector) + && !P_IsSecret(lines[i].backsector)) + ) + ) + || //jff 3/9/98 add logic to not show secret til after entered + ( // if map_secret_after is true + !map_secret_after && + (P_WasSecret(lines[i].frontsector) || + P_WasSecret(lines[i].backsector)) + ) + ) + ) + { + AM_drawMline(&l, mapcolor_secr); // line bounding secret sector + } //jff 1/6/98 end secret sector line change + else if (lines[i].backsector->floorheight != + lines[i].frontsector->floorheight) + { + AM_drawMline(&l, mapcolor_fchg); // floor level change + } + else if (lines[i].backsector->ceilingheight != + lines[i].frontsector->ceilingheight) + { + AM_drawMline(&l, mapcolor_cchg); // ceiling level change + } + else if (mapcolor_flat && ddt_cheating) + { + AM_drawMline(&l, mapcolor_flat); //2S lines that appear only in IDDT + } + } + } // now draw the lines only visible because the player has computermap + else if (plr->powers[pw_allmap]) // computermap visible lines + { + if (!(lines[i].flags & ML_DONTDRAW)) // invisible flag lines do not show + { + if + ( + mapcolor_flat + || + !lines[i].backsector + || + lines[i].backsector->floorheight + != lines[i].frontsector->floorheight + || + lines[i].backsector->ceilingheight + != lines[i].frontsector->ceilingheight + ) + AM_drawMline(&l, mapcolor_unsn); + } + } + } +} + +// +// AM_drawLineCharacter() +// +// Draws a vector graphic according to numerous parameters +// +// Passed the structure defining the vector graphic shape, the number +// of vectors in it, the scale to draw it at, the angle to draw it at, +// the color to draw it with, and the map coordinates to draw it at. +// Returns nothing +// +static void AM_drawLineCharacter +( mline_t* lineguy, + int lineguylines, + fixed_t scale, + angle_t angle, + int color, + fixed_t x, + fixed_t y ) +{ + int i; + mline_t l; + + if (automapmode & am_rotate) angle -= plr->mo->angle - ANG90; // cph + + for (i=0;imo->angle, + mapcolor_sngl, //jff color + plr->mo->x >> FRACTOMAPBITS,//e6y + plr->mo->y >> FRACTOMAPBITS//e6y + ); + else + AM_drawLineCharacter + ( + player_arrow, + NUMPLYRLINES, + 0, + plr->mo->angle, + mapcolor_sngl, //jff color + plr->mo->x >> FRACTOMAPBITS,//e6y + plr->mo->y >> FRACTOMAPBITS);//e6y + return; + } + + for (i=0;imo->x >> FRACTOMAPBITS, y = p->mo->y >> FRACTOMAPBITS;//e6y + if (automapmode & am_rotate) + AM_rotate(&x, &y, ANG90-plr->mo->angle, plr->mo->x, plr->mo->y); + + AM_drawLineCharacter (player_arrow, NUMPLYRLINES, 0, p->mo->angle, + p->powers[pw_invisibility] ? 246 /* *close* to black */ + : mapcolor_plyr[i], //jff 1/6/98 use default color + x, y); + } + } +} + +// +// AM_drawThings() +// +// Draws the things on the automap in double IDDT cheat mode +// +// Passed colors and colorrange, no longer used +// Returns nothing +// +static void AM_drawThings(void) +{ + int i; + mobj_t* t; + + // for all sectors + for (i=0;ix >> FRACTOMAPBITS, y = t->y >> FRACTOMAPBITS;//e6y + + if (automapmode & am_rotate) + AM_rotate(&x, &y, ANG90-plr->mo->angle, plr->mo->x, plr->mo->y); + + //jff 1/5/98 case over doomednum of thing being drawn + if (mapcolor_rkey || mapcolor_ykey || mapcolor_bkey) + { + switch(t->info->doomednum) + { + //jff 1/5/98 treat keys special + case 38: case 13: //jff red key + AM_drawLineCharacter + ( + cross_mark, + NUMCROSSMARKLINES, + 16<angle, + mapcolor_rkey!=-1? mapcolor_rkey : mapcolor_sprt, + x, y + ); + t = t->snext; + continue; + case 39: case 6: //jff yellow key + AM_drawLineCharacter + ( + cross_mark, + NUMCROSSMARKLINES, + 16<angle, + mapcolor_ykey!=-1? mapcolor_ykey : mapcolor_sprt, + x, y + ); + t = t->snext; + continue; + case 40: case 5: //jff blue key + AM_drawLineCharacter + ( + cross_mark, + NUMCROSSMARKLINES, + 16<angle, + mapcolor_bkey!=-1? mapcolor_bkey : mapcolor_sprt, + x, y + ); + t = t->snext; + continue; + default: + break; + } + } + //jff 1/5/98 end added code for keys + //jff previously entire code + AM_drawLineCharacter + ( + thintriangle_guy, + NUMTHINTRIANGLEGUYLINES, + 16<angle, + t->flags & MF_FRIEND && !t->player ? mapcolor_frnd : + /* cph 2006/07/30 - Show count-as-kills in red. */ + ((t->flags & (MF_COUNTKILL | MF_CORPSE)) == MF_COUNTKILL) ? mapcolor_enemy : + /* bbm 2/28/03 Show countable items in yellow. */ + t->flags & MF_COUNTITEM ? mapcolor_item : mapcolor_sprt, + x, y + ); + t = t->snext; + } + } +} + +// +// AM_drawMarks() +// +// Draw the marked locations on the automap +// +// Passed nothing, returns nothing +// +// killough 2/22/98: +// Rewrote AM_drawMarks(). Removed limit on marks. +// +static void AM_drawMarks(void) +{ + int i; + for (i=0;imo->angle, plr->mo->x, plr->mo->y); + + fx = CXMTOF(fx); fy = CYMTOF(fy); + + do + { + int d = j % 10; + if (d==1) // killough 2/22/98: less spacing for '1' + fx++; + + if (fx >= f_x && fx < f_w - w && fy >= f_y && fy < f_h - h) { + // cph - construct patch name and draw marker + char namebuf[] = { 'A', 'M', 'M', 'N', 'U', 'M', '0'+d, 0 }; + + V_DrawNamePatch(fx, fy, FB, namebuf, CR_DEFAULT, VPT_NONE); + } + fx -= w-1; // killough 2/22/98: 1 space backwards + j /= 10; + } + while (j>0); + } +} + +// +// AM_drawCrosshair() +// +// Draw the single point crosshair representing map center +// +// Passed the color to draw the pixel with +// Returns nothing +// +// CPhipps - made static inline, and use the general pixel plotter function + +inline static void AM_drawCrosshair(int color) +{ + fline_t line; + + line.a.x = (f_w/2)-1; + line.a.y = (f_h/2); + line.b.x = (f_w/2)+1; + line.b.y = (f_h/2); + V_DrawLine(&line, color); + + line.a.x = (f_w/2); + line.a.y = (f_h/2)-1; + line.b.x = (f_w/2); + line.b.y = (f_h/2)+1; + V_DrawLine(&line, color); +} + +// +// AM_Drawer() +// +// Draws the entire automap +// +// Passed nothing, returns nothing +// +void AM_Drawer (void) +{ + // CPhipps - all automap modes put into one enum + if (!(automapmode & am_active)) return; + + if (!(automapmode & am_overlay)) // cph - If not overlay mode, clear background for the automap + V_FillRect(FB, f_x, f_y, f_w, f_h, (byte)mapcolor_back); //jff 1/5/98 background default color + if (automapmode & am_grid) + AM_drawGrid(mapcolor_grid); //jff 1/7/98 grid default color + AM_drawWalls(); + AM_drawPlayers(); + if (ddt_cheating==2) + AM_drawThings(); //jff 1/5/98 default double IDDT sprite + AM_drawCrosshair(mapcolor_hair); //jff 1/7/98 default crosshair color + + AM_drawMarks(); +} diff --git a/src/am_map.h b/src/am_map.h new file mode 100644 index 0000000..850c9ce --- /dev/null +++ b/src/am_map.h @@ -0,0 +1,111 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * AutoMap module. + * + *-----------------------------------------------------------------------------*/ + +#ifndef __AMMAP_H__ +#define __AMMAP_H__ + +#include "d_event.h" + +#define MAPBITS 12 +#define FRACTOMAPBITS (FRACBITS-MAPBITS) + +// Used by ST StatusBar stuff. +#define AM_MSGHEADER (('a'<<24)+('m'<<16)) +#define AM_MSGENTERED (AM_MSGHEADER | ('e'<<8)) +#define AM_MSGEXITED (AM_MSGHEADER | ('x'<<8)) + +// Called by main loop. +boolean AM_Responder (event_t* ev); + +// Called by main loop. +void AM_Ticker (void); + +// Called by main loop, +// called instead of view drawer if automap active. +void AM_Drawer (void); + +// Called to force the automap to quit +// if the level is completed while it is up. +void AM_Stop (void); + +// killough 2/22/98: for saving automap information in savegame: + +extern void AM_Start(void); + +//jff 4/16/98 make externally available + +extern void AM_clearMarks(void); + +typedef struct +{ + fixed_t x,y; +} mpoint_t; + +extern mpoint_t *markpoints; +extern int markpointnum, markpointnum_max; + +// end changes -- killough 2/22/98 + +// killough 5/2/98: moved from m_misc.c + +//jff 1/7/98 automap colors added +extern int mapcolor_back; // map background +extern int mapcolor_grid; // grid lines color +extern int mapcolor_wall; // normal 1s wall color +extern int mapcolor_fchg; // line at floor height change color +extern int mapcolor_cchg; // line at ceiling height change color +extern int mapcolor_clsd; // line at sector with floor=ceiling color +extern int mapcolor_rkey; // red key color +extern int mapcolor_bkey; // blue key color +extern int mapcolor_ykey; // yellow key color +extern int mapcolor_rdor; // red door color (diff from keys to allow option) +extern int mapcolor_bdor; // blue door color (of enabling one not other) +extern int mapcolor_ydor; // yellow door color +extern int mapcolor_tele; // teleporter line color +extern int mapcolor_secr; // secret sector boundary color +//jff 4/23/98 +extern int mapcolor_exit; // exit line +extern int mapcolor_unsn; // computer map unseen line color +extern int mapcolor_flat; // line with no floor/ceiling changes +extern int mapcolor_sprt; // general sprite color +extern int mapcolor_item; // item sprite color +extern int mapcolor_enemy; // enemy sprite color +extern int mapcolor_frnd; // friendly sprite color +extern int mapcolor_hair; // crosshair color +extern int mapcolor_sngl; // single player arrow color +extern int mapcolor_plyr[4]; // colors for players in multiplayer +extern int mapcolor_me; // consoleplayer's chosen colour +//jff 3/9/98 +extern int map_secret_after; // secrets do not appear til after bagged + +#endif diff --git a/src/d_client.c b/src/d_client.c new file mode 100644 index 0000000..dd09720 --- /dev/null +++ b/src/d_client.c @@ -0,0 +1,539 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Network client. Passes information to/from server, staying + * synchronised. + * Contains the main wait loop, waiting for network input or + * time before doing the next tic. + * Rewritten for LxDoom, but based around bits of the old code. + * + *----------------------------------------------------------------------------- + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#include +#ifdef HAVE_UNISTD_H +#include +#endif +#ifdef HAVE_SYS_WAIT_H +#include +#endif + +#ifdef USE_SDL_NET + #include "SDL.h" +#endif + +#include "doomtype.h" +#include "doomstat.h" +#include "d_net.h" +#include "z_zone.h" + +#include "d_main.h" +#include "g_game.h" +#include "m_menu.h" +#include "p_checksum.h" + +#include "protocol.h" +#include "i_network.h" +#include "i_system.h" +#include "i_main.h" +#include "i_video.h" +#include "m_argv.h" +#include "r_fps.h" +#include "lprintf.h" + +static boolean server; +static int remotetic; // Tic expected from the remote +static int remotesend; // Tic expected by the remote +ticcmd_t netcmds[MAXPLAYERS][BACKUPTICS]; +static ticcmd_t* localcmds; +static unsigned numqueuedpackets; +static packet_header_t** queuedpacket; +int maketic; +int ticdup = 1; +static int xtratics = 0; +int wanted_player_number; + +static boolean isExtraDDisplay = false; + +static void D_QuitNetGame (void); + +#ifndef HAVE_NET +doomcom_t* doomcom; +#endif + +#ifdef HAVE_NET +void D_InitNetGame (void) +{ + int i; + int numplayers = 1; + + i = M_CheckParm("-net"); + if (i && i < myargc-1) i++; + + if (!(netgame = server = !!i)) { + playeringame[consoleplayer = 0] = true; + // e6y + // for play, recording or playback using "single-player coop" mode. + // Equivalent to using prboom_server with -N 1 + netgame = M_CheckParm("-solo-net"); + } else { + // Get game info from server + packet_header_t *packet = Z_Malloc(1000, PU_STATIC, NULL); + struct setup_packet_s *sinfo = (void*)(packet+1); + struct { packet_header_t head; short pn; } PACKEDATTR initpacket; + + I_InitNetwork(); + udp_socket = I_Socket(0); + I_ConnectToServer(myargv[i]); + + do + { + do { + // Send init packet + initpacket.pn = doom_htons(wanted_player_number); + packet_set(&initpacket.head, PKT_INIT, 0); + I_SendPacket(&initpacket.head, sizeof(initpacket)); + I_WaitForPacket(5000); + } while (!I_GetPacket(packet, 1000)); + if (packet->type == PKT_DOWN) I_Error("Server aborted the game"); + } while (packet->type != PKT_SETUP); + + // Once we have been accepted by the server, we should tell it when we leave + atexit(D_QuitNetGame); + + // Get info from the setup packet + consoleplayer = sinfo->yourplayer; + compatibility_level = sinfo->complevel; + G_Compatibility(); + startskill = sinfo->skill; + deathmatch = sinfo->deathmatch; + startmap = sinfo->level; + startepisode = sinfo->episode; + ticdup = sinfo->ticdup; + xtratics = sinfo->extratic; + G_ReadOptions(sinfo->game_options); + + lprintf(LO_INFO, "\tjoined game as player %d/%d; %d WADs specified\n", + consoleplayer+1, numplayers = sinfo->players, sinfo->numwads); + { + char *p = sinfo->wadnames; + int i = sinfo->numwads; + + while (i--) { + D_AddFile(p, source_net); + p += strlen(p) + 1; + } + } + Z_Free(packet); + } + localcmds = netcmds[displayplayer = consoleplayer]; + for (i=0; iconsoleplayer = 0; + doomcom->numnodes = 0; doomcom->numplayers = 1; + localcmds = netcmds[consoleplayer]; + netgame = (M_CheckParm("-solo-net") != 0); + + for (i=0; inumplayers; i++) + playeringame[i] = true; + for (; iconsoleplayer; +} +#endif // HAVE_NET + +#ifdef HAVE_NET +void D_CheckNetGame(void) +{ + packet_header_t *packet = Z_Malloc(sizeof(packet_header_t)+1, PU_STATIC, NULL); + + if (server) { + lprintf(LO_INFO, "D_CheckNetGame: waiting for server to signal game start\n"); + do { + while (!I_GetPacket(packet, sizeof(packet_header_t)+1)) { + packet_set(packet, PKT_GO, 0); + *(byte*)(packet+1) = consoleplayer; + I_SendPacket(packet, sizeof(packet_header_t)+1); + I_uSleep(100000); + } + } while (packet->type != PKT_GO); + } + Z_Free(packet); +} + +boolean D_NetGetWad(const char* name) +{ +#if defined(HAVE_WAIT_H) + size_t psize = sizeof(packet_header_t) + strlen(name) + 500; + packet_header_t *packet; + boolean done = false; + + if (!server || strchr(name, '/')) return false; // If it contains path info, reject + + do { + // Send WAD request to remote + packet = Z_Malloc(psize, PU_STATIC, NULL); + packet_set(packet, PKT_WAD, 0); + *(byte*)(packet+1) = consoleplayer; + strcpy(1+(byte*)(packet+1), name); + I_SendPacket(packet, sizeof(packet_header_t) + strlen(name) + 2); + + I_uSleep(10000); + } while (!I_GetPacket(packet, psize) || (packet->type != PKT_WAD)); + Z_Free(packet); + + if (!strcasecmp((void*)(packet+1), name)) { + pid_t pid; + int rv; + byte *p = (byte*)(packet+1) + strlen(name) + 1; + + /* Automatic wad file retrieval using wget (supports http and ftp, using URLs) + * Unix systems have all these commands handy, this kind of thing is easy + * Any windo$e port will have some awkward work replacing these. + */ + /* cph - caution here. This is data from an untrusted source. + * Don't pass it via a shell. */ + if ((pid = fork()) == -1) + perror("fork"); + else if (!pid) { + /* Child chains to wget, does the download */ + execlp("wget", "wget", p, NULL); + } + /* This is the parent, i.e. main LxDoom process */ + wait(&rv); + if (!(done = !access(name, R_OK))) { + if (!strcmp(p+strlen(p)-4, ".zip")) { + p = strrchr(p, '/')+1; + if ((pid = fork()) == -1) + perror("fork"); + else if (!pid) { + /* Child executes decompressor */ + execlp("unzip", "unzip", p, name, NULL); + } + /* Parent waits for the file */ + wait(&rv); + done = !!access(name, R_OK); + } + /* Add more decompression protocols here as desired */ + } + Z_Free(buffer); + } + return done; +#else /* HAVE_WAIT_H */ + return false; +#endif +} + +void NetUpdate(void) +{ + static int lastmadetic; + if (isExtraDDisplay) + return; + if (server) { // Receive network packets + size_t recvlen; + packet_header_t *packet = Z_Malloc(10000, PU_STATIC, NULL); + while ((recvlen = I_GetPacket(packet, 10000))) { + switch(packet->type) { + case PKT_TICS: + { + byte *p = (void*)(packet+1); + int tics = *p++; + unsigned long ptic = doom_ntohl(packet->tic); + if (ptic > (unsigned)remotetic) { // Missed some + packet_set(packet, PKT_RETRANS, remotetic); + *(byte*)(packet+1) = consoleplayer; + I_SendPacket(packet, sizeof(*packet)+1); + } else { + if (ptic + tics <= (unsigned)remotetic) break; // Will not improve things + remotetic = ptic; + while (tics--) { + int players = *p++; + while (players--) { + int n = *p++; + RawToTic(&netcmds[n][remotetic%BACKUPTICS], p); + p += sizeof(ticcmd_t); + } + remotetic++; + } + } + } + break; + case PKT_RETRANS: // Resend request + remotesend = doom_ntohl(packet->tic); + break; + case PKT_DOWN: // Server downed + { + int j; + for (j=0; j 0 ? newtics : 0); + lastmadetic += newtics; + if (ffmap) newtics++; + while (newtics--) { + I_StartTic(); + if (maketic - gametic > BACKUPTICS/2) break; + G_BuildTiccmd(&localcmds[maketic%BACKUPTICS]); + maketic++; + } + if (server && maketic > remotesend) { // Send the tics to the server + int sendtics; + remotesend -= xtratics; + if (remotesend < 0) remotesend = 0; + sendtics = maketic - remotesend; + { + size_t pkt_size = sizeof(packet_header_t) + 2 + sendtics * sizeof(ticcmd_t); + packet_header_t *packet = Z_Malloc(pkt_size, PU_STATIC, NULL); + + packet_set(packet, PKT_TICC, maketic - sendtics); + *(byte*)(packet+1) = sendtics; + *(((byte*)(packet+1))+1) = consoleplayer; + { + void *tic = ((byte*)(packet+1)) +2; + while (sendtics--) { + TicToRaw(tic, &localcmds[remotesend++%BACKUPTICS]); + tic = (byte *)tic + sizeof(ticcmd_t); + } + } + I_SendPacket(packet, pkt_size); + Z_Free(packet); + } + } + } +} +#else + +void D_BuildNewTiccmds(void) +{ + static int lastmadetic; + int newtics = I_GetTime() - lastmadetic; + lastmadetic += newtics; + while (newtics--) + { + I_StartTic(); + if (maketic - gametic > BACKUPTICS/2) break; + G_BuildTiccmd(&localcmds[maketic%BACKUPTICS]); + maketic++; + } +} +#endif + +#ifdef HAVE_NET +/* cph - data passed to this must be in the Doom (little-) endian */ +void D_NetSendMisc(netmisctype_t type, size_t len, void* data) +{ + if (server) { + size_t size = sizeof(packet_header_t) + 3*sizeof(int) + len; + packet_header_t *packet = Z_Malloc(size, PU_STATIC, NULL); + int *p = (void*)(packet+1); + + packet_set(packet, PKT_EXTRA, gametic); + *p++ = LONG(type); *p++ = LONG(consoleplayer); *p++ = LONG(len); + memcpy(p, data, len); + I_SendPacket(packet, size); + + Z_Free(packet); + } +} + +static void CheckQueuedPackets(void) +{ + int i; + for (i=0; (unsigned)itic) <= gametic) + switch (queuedpacket[i]->type) { + case PKT_QUIT: // Player quit the game + { + int pn = *(byte*)(queuedpacket[i]+1); + playeringame[pn] = false; + doom_printf("Player %d left the game\n", pn); + } + break; + case PKT_EXTRA: + { + int *p = (int*)(queuedpacket[i]+1); + size_t len = LONG(*(p+2)); + switch (LONG(*p)) { + case nm_plcolour: + G_ChangedPlayerColour(LONG(*(p+1)), LONG(*(p+3))); + break; + case nm_savegamename: + if (len < SAVEDESCLEN) { + memcpy(savedescription, p+3, len); + // Force terminating 0 in case + savedescription[len] = 0; + } + break; + } + } + break; + default: // Should not be queued + break; + } + + { // Requeue remaining packets + int newnum = 0; + packet_header_t **newqueue = NULL; + + for (i=0; (unsigned)itic) > gametic) { + newqueue = Z_Realloc(newqueue, ++newnum * sizeof *newqueue, + PU_STATIC, NULL); + newqueue[newnum-1] = queuedpacket[i]; + } else Z_Free(queuedpacket[i]); + + Z_Free(queuedpacket); + numqueuedpackets = newnum; queuedpacket = newqueue; + } +} +#endif // HAVE_NET + +void TryRunTics (void) +{ + int runtics; + int entertime = I_GetTime(); + + // Wait for tics to run + while (1) { +#ifdef HAVE_NET + NetUpdate(); +#else + D_BuildNewTiccmds(); +#endif + runtics = (server ? remotetic : maketic) - gametic; + if (!runtics) { + if (!movement_smooth) { +#ifdef HAVE_NET + if (server) + I_WaitForPacket(ms_to_next_tick); + else +#endif + I_uSleep(ms_to_next_tick*1000); + } + if (I_GetTime() - entertime > 10) { +#ifdef HAVE_NET + if (server) { + char buf[sizeof(packet_header_t)+1]; + remotesend--; + packet_set((packet_header_t *)buf, PKT_RETRANS, remotetic); + buf[sizeof(buf)-1] = consoleplayer; + I_SendPacket((packet_header_t *)buf, sizeof buf); + } +#endif + M_Ticker(); return; + } + //if ((displaytime) < (tic_vars.next-SDL_GetTicks())) + { + WasRenderedInTryRunTics = true; + if (V_GetMode() == VID_MODEGL ? + movement_smooth : + movement_smooth && gamestate==wipegamestate) + { + isExtraDDisplay = true; + D_Display(); + isExtraDDisplay = false; + } + } + } else break; + } + + while (runtics--) { +#ifdef HAVE_NET + if (server) CheckQueuedPackets(); +#endif + if (advancedemo) + D_DoAdvanceDemo (); + M_Ticker (); + I_GetTime_SaveMS(); + G_Ticker (); + P_Checksum(gametic); + gametic++; +#ifdef HAVE_NET + NetUpdate(); // Keep sending our tics to avoid stalling remote nodes +#endif + } +} + +#ifdef HAVE_NET +static void D_QuitNetGame (void) +{ + byte buf[1 + sizeof(packet_header_t)]; + packet_header_t *packet = (void*)buf; + int i; + + if (!server) return; + buf[sizeof(packet_header_t)] = consoleplayer; + packet_set(packet, PKT_QUIT, gametic); + + for (i=0; i<4; i++) { + I_SendPacket(packet, 1 + sizeof(packet_header_t)); + I_uSleep(10000); + } +} +#endif diff --git a/src/d_deh.c b/src/d_deh.c new file mode 100644 index 0000000..b3790f5 --- /dev/null +++ b/src/d_deh.c @@ -0,0 +1,3090 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2004 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Dehacked file support + * New for the TeamTNT "Boom" engine + * + * Author: Ty Halderman, TeamTNT + * + *--------------------------------------------------------------------*/ + +// killough 5/2/98: fixed headers, removed rendunant external declarations: +#include "doomdef.h" +#include "doomtype.h" +#include "doomstat.h" +#include "d_deh.h" +#include "sounds.h" +#include "info.h" +#include "m_cheat.h" +#include "p_inter.h" +#include "p_enemy.h" +#include "g_game.h" +#include "d_think.h" +#include "w_wad.h" + +// CPhipps - modify to use logical output routine +#include "lprintf.h" + +#define TRUE 1 +#define FALSE 0 + +#ifndef HAVE_STRLWR +#include + +static char* strlwr(char* str) +{ + char* p; + for (p=str; *p; p++) *p = tolower(*p); + return str; +} +#endif + +// killough 10/98: new functions, to allow processing DEH files in-memory +// (e.g. from wads) + +typedef struct { + /* cph 2006/08/06 - + * if lump != NULL, lump is the start of the lump, + * inp is the current read pos. */ + const byte *inp, *lump; + long size; + /* else, !lump, and f is the file being read */ + FILE* f; +} DEHFILE; + +// killough 10/98: emulate IO whether input really comes from a file or not + +static char *dehfgets(char *buf, size_t n, DEHFILE *fp) +{ + if (!fp->lump) // If this is a real file, + return (fgets)(buf, n, fp->f); // return regular fgets + if (!n || !*fp->inp || fp->size<=0) // If no more characters + return NULL; + if (n==1) + fp->size--, *buf = *fp->inp++; + else + { // copy buffer + char *p = buf; + while (n>1 && *fp->inp && fp->size && + (n--, fp->size--, *p++ = *fp->inp++) != '\n') + ; + *p = 0; + } + return buf; // Return buffer pointer +} + +static int dehfeof(DEHFILE *fp) +{ + return !fp->lump ? feof(fp->f) : !*fp->inp || fp->size<=0; +} + +static int dehfgetc(DEHFILE *fp) +{ + return !fp->lump ? fgetc(fp->f) : fp->size > 0 ? + fp->size--, *fp->inp++ : EOF; +} + +// haleyjd 9/22/99 +int HelperThing = -1; // in P_SpawnMapThing to substitute helper thing + +// variables used in other routines +boolean deh_pars = FALSE; // in wi_stuff to allow pars in modified games + +// #include "d_deh.h" -- we don't do that here but we declare the +// variables. This externalizes everything that there is a string +// set for in the language files. See d_deh.h for detailed comments, +// original English values etc. These are set to the macro values, +// which are set by D_ENGLSH.H or D_FRENCH.H(etc). BEX files are a +// better way of changing these strings globally by language. + +// ==================================================================== +// Any of these can be changed using the bex extensions +#include "dstrings.h" // to get the initial values +/* cph - const's + * - removed redundant "can't XXX in a netgame" strings. + */ +const char *s_D_DEVSTR = D_DEVSTR; +const char *s_D_CDROM = D_CDROM; +const char *s_PRESSKEY = PRESSKEY; +const char *s_PRESSYN = PRESSYN; +const char *s_QUITMSG = QUITMSG; +const char *s_QSAVESPOT = QSAVESPOT; // PRESSKEY; +const char *s_SAVEDEAD = SAVEDEAD; // PRESSKEY; // remove duplicate y/n +const char *s_QSPROMPT = QSPROMPT; // PRESSYN; +const char *s_QLPROMPT = QLPROMPT; // PRESSYN; +const char *s_NEWGAME = NEWGAME; // PRESSKEY; +const char *s_RESTARTLEVEL= RESTARTLEVEL; // PRESSYN; +const char *s_NIGHTMARE = NIGHTMARE; // PRESSYN; +const char *s_SWSTRING = SWSTRING; // PRESSKEY; +const char *s_MSGOFF = MSGOFF; +const char *s_MSGON = MSGON; +const char *s_NETEND = NETEND; // PRESSKEY; +const char *s_ENDGAME = ENDGAME; // PRESSYN; // killough 4/4/98: end +const char *s_DOSY = DOSY; +const char *s_DETAILHI = DETAILHI; +const char *s_DETAILLO = DETAILLO; +const char *s_GAMMALVL0 = GAMMALVL0; +const char *s_GAMMALVL1 = GAMMALVL1; +const char *s_GAMMALVL2 = GAMMALVL2; +const char *s_GAMMALVL3 = GAMMALVL3; +const char *s_GAMMALVL4 = GAMMALVL4; +const char *s_EMPTYSTRING = EMPTYSTRING; +const char *s_GOTARMOR = GOTARMOR; +const char *s_GOTMEGA = GOTMEGA; +const char *s_GOTHTHBONUS = GOTHTHBONUS; +const char *s_GOTARMBONUS = GOTARMBONUS; +const char *s_GOTSTIM = GOTSTIM; +const char *s_GOTMEDINEED = GOTMEDINEED; +const char *s_GOTMEDIKIT = GOTMEDIKIT; +const char *s_GOTSUPER = GOTSUPER; +const char *s_GOTBLUECARD = GOTBLUECARD; +const char *s_GOTYELWCARD = GOTYELWCARD; +const char *s_GOTREDCARD = GOTREDCARD; +const char *s_GOTBLUESKUL = GOTBLUESKUL; +const char *s_GOTYELWSKUL = GOTYELWSKUL; +const char *s_GOTREDSKULL = GOTREDSKULL; +const char *s_GOTINVUL = GOTINVUL; +const char *s_GOTBERSERK = GOTBERSERK; +const char *s_GOTINVIS = GOTINVIS; +const char *s_GOTSUIT = GOTSUIT; +const char *s_GOTMAP = GOTMAP; +const char *s_GOTVISOR = GOTVISOR; +const char *s_GOTMSPHERE = GOTMSPHERE; +const char *s_GOTCLIP = GOTCLIP; +const char *s_GOTCLIPBOX = GOTCLIPBOX; +const char *s_GOTROCKET = GOTROCKET; +const char *s_GOTROCKBOX = GOTROCKBOX; +const char *s_GOTCELL = GOTCELL; +const char *s_GOTCELLBOX = GOTCELLBOX; +const char *s_GOTSHELLS = GOTSHELLS; +const char *s_GOTSHELLBOX = GOTSHELLBOX; +const char *s_GOTBACKPACK = GOTBACKPACK; +const char *s_GOTBFG9000 = GOTBFG9000; +const char *s_GOTCHAINGUN = GOTCHAINGUN; +const char *s_GOTCHAINSAW = GOTCHAINSAW; +const char *s_GOTLAUNCHER = GOTLAUNCHER; +const char *s_GOTPLASMA = GOTPLASMA; +const char *s_GOTSHOTGUN = GOTSHOTGUN; +const char *s_GOTSHOTGUN2 = GOTSHOTGUN2; +const char *s_PD_BLUEO = PD_BLUEO; +const char *s_PD_REDO = PD_REDO; +const char *s_PD_YELLOWO = PD_YELLOWO; +const char *s_PD_BLUEK = PD_BLUEK; +const char *s_PD_REDK = PD_REDK; +const char *s_PD_YELLOWK = PD_YELLOWK; +const char *s_PD_BLUEC = PD_BLUEC; +const char *s_PD_REDC = PD_REDC; +const char *s_PD_YELLOWC = PD_YELLOWC; +const char *s_PD_BLUES = PD_BLUES; +const char *s_PD_REDS = PD_REDS; +const char *s_PD_YELLOWS = PD_YELLOWS; +const char *s_PD_ANY = PD_ANY; +const char *s_PD_ALL3 = PD_ALL3; +const char *s_PD_ALL6 = PD_ALL6; +const char *s_GGSAVED = GGSAVED; +const char *s_HUSTR_MSGU = HUSTR_MSGU; +const char *s_HUSTR_E1M1 = HUSTR_E1M1; +const char *s_HUSTR_E1M2 = HUSTR_E1M2; +const char *s_HUSTR_E1M3 = HUSTR_E1M3; +const char *s_HUSTR_E1M4 = HUSTR_E1M4; +const char *s_HUSTR_E1M5 = HUSTR_E1M5; +const char *s_HUSTR_E1M6 = HUSTR_E1M6; +const char *s_HUSTR_E1M7 = HUSTR_E1M7; +const char *s_HUSTR_E1M8 = HUSTR_E1M8; +const char *s_HUSTR_E1M9 = HUSTR_E1M9; +const char *s_HUSTR_E2M1 = HUSTR_E2M1; +const char *s_HUSTR_E2M2 = HUSTR_E2M2; +const char *s_HUSTR_E2M3 = HUSTR_E2M3; +const char *s_HUSTR_E2M4 = HUSTR_E2M4; +const char *s_HUSTR_E2M5 = HUSTR_E2M5; +const char *s_HUSTR_E2M6 = HUSTR_E2M6; +const char *s_HUSTR_E2M7 = HUSTR_E2M7; +const char *s_HUSTR_E2M8 = HUSTR_E2M8; +const char *s_HUSTR_E2M9 = HUSTR_E2M9; +const char *s_HUSTR_E3M1 = HUSTR_E3M1; +const char *s_HUSTR_E3M2 = HUSTR_E3M2; +const char *s_HUSTR_E3M3 = HUSTR_E3M3; +const char *s_HUSTR_E3M4 = HUSTR_E3M4; +const char *s_HUSTR_E3M5 = HUSTR_E3M5; +const char *s_HUSTR_E3M6 = HUSTR_E3M6; +const char *s_HUSTR_E3M7 = HUSTR_E3M7; +const char *s_HUSTR_E3M8 = HUSTR_E3M8; +const char *s_HUSTR_E3M9 = HUSTR_E3M9; +const char *s_HUSTR_E4M1 = HUSTR_E4M1; +const char *s_HUSTR_E4M2 = HUSTR_E4M2; +const char *s_HUSTR_E4M3 = HUSTR_E4M3; +const char *s_HUSTR_E4M4 = HUSTR_E4M4; +const char *s_HUSTR_E4M5 = HUSTR_E4M5; +const char *s_HUSTR_E4M6 = HUSTR_E4M6; +const char *s_HUSTR_E4M7 = HUSTR_E4M7; +const char *s_HUSTR_E4M8 = HUSTR_E4M8; +const char *s_HUSTR_E4M9 = HUSTR_E4M9; +const char *s_HUSTR_1 = HUSTR_1; +const char *s_HUSTR_2 = HUSTR_2; +const char *s_HUSTR_3 = HUSTR_3; +const char *s_HUSTR_4 = HUSTR_4; +const char *s_HUSTR_5 = HUSTR_5; +const char *s_HUSTR_6 = HUSTR_6; +const char *s_HUSTR_7 = HUSTR_7; +const char *s_HUSTR_8 = HUSTR_8; +const char *s_HUSTR_9 = HUSTR_9; +const char *s_HUSTR_10 = HUSTR_10; +const char *s_HUSTR_11 = HUSTR_11; +const char *s_HUSTR_12 = HUSTR_12; +const char *s_HUSTR_13 = HUSTR_13; +const char *s_HUSTR_14 = HUSTR_14; +const char *s_HUSTR_15 = HUSTR_15; +const char *s_HUSTR_16 = HUSTR_16; +const char *s_HUSTR_17 = HUSTR_17; +const char *s_HUSTR_18 = HUSTR_18; +const char *s_HUSTR_19 = HUSTR_19; +const char *s_HUSTR_20 = HUSTR_20; +const char *s_HUSTR_21 = HUSTR_21; +const char *s_HUSTR_22 = HUSTR_22; +const char *s_HUSTR_23 = HUSTR_23; +const char *s_HUSTR_24 = HUSTR_24; +const char *s_HUSTR_25 = HUSTR_25; +const char *s_HUSTR_26 = HUSTR_26; +const char *s_HUSTR_27 = HUSTR_27; +const char *s_HUSTR_28 = HUSTR_28; +const char *s_HUSTR_29 = HUSTR_29; +const char *s_HUSTR_30 = HUSTR_30; +const char *s_HUSTR_31 = HUSTR_31; +const char *s_HUSTR_32 = HUSTR_32; +const char *s_PHUSTR_1 = PHUSTR_1; +const char *s_PHUSTR_2 = PHUSTR_2; +const char *s_PHUSTR_3 = PHUSTR_3; +const char *s_PHUSTR_4 = PHUSTR_4; +const char *s_PHUSTR_5 = PHUSTR_5; +const char *s_PHUSTR_6 = PHUSTR_6; +const char *s_PHUSTR_7 = PHUSTR_7; +const char *s_PHUSTR_8 = PHUSTR_8; +const char *s_PHUSTR_9 = PHUSTR_9; +const char *s_PHUSTR_10 = PHUSTR_10; +const char *s_PHUSTR_11 = PHUSTR_11; +const char *s_PHUSTR_12 = PHUSTR_12; +const char *s_PHUSTR_13 = PHUSTR_13; +const char *s_PHUSTR_14 = PHUSTR_14; +const char *s_PHUSTR_15 = PHUSTR_15; +const char *s_PHUSTR_16 = PHUSTR_16; +const char *s_PHUSTR_17 = PHUSTR_17; +const char *s_PHUSTR_18 = PHUSTR_18; +const char *s_PHUSTR_19 = PHUSTR_19; +const char *s_PHUSTR_20 = PHUSTR_20; +const char *s_PHUSTR_21 = PHUSTR_21; +const char *s_PHUSTR_22 = PHUSTR_22; +const char *s_PHUSTR_23 = PHUSTR_23; +const char *s_PHUSTR_24 = PHUSTR_24; +const char *s_PHUSTR_25 = PHUSTR_25; +const char *s_PHUSTR_26 = PHUSTR_26; +const char *s_PHUSTR_27 = PHUSTR_27; +const char *s_PHUSTR_28 = PHUSTR_28; +const char *s_PHUSTR_29 = PHUSTR_29; +const char *s_PHUSTR_30 = PHUSTR_30; +const char *s_PHUSTR_31 = PHUSTR_31; +const char *s_PHUSTR_32 = PHUSTR_32; +const char *s_THUSTR_1 = THUSTR_1; +const char *s_THUSTR_2 = THUSTR_2; +const char *s_THUSTR_3 = THUSTR_3; +const char *s_THUSTR_4 = THUSTR_4; +const char *s_THUSTR_5 = THUSTR_5; +const char *s_THUSTR_6 = THUSTR_6; +const char *s_THUSTR_7 = THUSTR_7; +const char *s_THUSTR_8 = THUSTR_8; +const char *s_THUSTR_9 = THUSTR_9; +const char *s_THUSTR_10 = THUSTR_10; +const char *s_THUSTR_11 = THUSTR_11; +const char *s_THUSTR_12 = THUSTR_12; +const char *s_THUSTR_13 = THUSTR_13; +const char *s_THUSTR_14 = THUSTR_14; +const char *s_THUSTR_15 = THUSTR_15; +const char *s_THUSTR_16 = THUSTR_16; +const char *s_THUSTR_17 = THUSTR_17; +const char *s_THUSTR_18 = THUSTR_18; +const char *s_THUSTR_19 = THUSTR_19; +const char *s_THUSTR_20 = THUSTR_20; +const char *s_THUSTR_21 = THUSTR_21; +const char *s_THUSTR_22 = THUSTR_22; +const char *s_THUSTR_23 = THUSTR_23; +const char *s_THUSTR_24 = THUSTR_24; +const char *s_THUSTR_25 = THUSTR_25; +const char *s_THUSTR_26 = THUSTR_26; +const char *s_THUSTR_27 = THUSTR_27; +const char *s_THUSTR_28 = THUSTR_28; +const char *s_THUSTR_29 = THUSTR_29; +const char *s_THUSTR_30 = THUSTR_30; +const char *s_THUSTR_31 = THUSTR_31; +const char *s_THUSTR_32 = THUSTR_32; +const char *s_HUSTR_CHATMACRO1 = HUSTR_CHATMACRO1; +const char *s_HUSTR_CHATMACRO2 = HUSTR_CHATMACRO2; +const char *s_HUSTR_CHATMACRO3 = HUSTR_CHATMACRO3; +const char *s_HUSTR_CHATMACRO4 = HUSTR_CHATMACRO4; +const char *s_HUSTR_CHATMACRO5 = HUSTR_CHATMACRO5; +const char *s_HUSTR_CHATMACRO6 = HUSTR_CHATMACRO6; +const char *s_HUSTR_CHATMACRO7 = HUSTR_CHATMACRO7; +const char *s_HUSTR_CHATMACRO8 = HUSTR_CHATMACRO8; +const char *s_HUSTR_CHATMACRO9 = HUSTR_CHATMACRO9; +const char *s_HUSTR_CHATMACRO0 = HUSTR_CHATMACRO0; +const char *s_HUSTR_TALKTOSELF1 = HUSTR_TALKTOSELF1; +const char *s_HUSTR_TALKTOSELF2 = HUSTR_TALKTOSELF2; +const char *s_HUSTR_TALKTOSELF3 = HUSTR_TALKTOSELF3; +const char *s_HUSTR_TALKTOSELF4 = HUSTR_TALKTOSELF4; +const char *s_HUSTR_TALKTOSELF5 = HUSTR_TALKTOSELF5; +const char *s_HUSTR_MESSAGESENT = HUSTR_MESSAGESENT; +const char *s_HUSTR_PLRGREEN = HUSTR_PLRGREEN; +const char *s_HUSTR_PLRINDIGO = HUSTR_PLRINDIGO; +const char *s_HUSTR_PLRBROWN = HUSTR_PLRBROWN; +const char *s_HUSTR_PLRRED = HUSTR_PLRRED; +const char *s_AMSTR_FOLLOWON = AMSTR_FOLLOWON; +const char *s_AMSTR_FOLLOWOFF = AMSTR_FOLLOWOFF; +const char *s_AMSTR_GRIDON = AMSTR_GRIDON; +const char *s_AMSTR_GRIDOFF = AMSTR_GRIDOFF; +const char *s_AMSTR_MARKEDSPOT = AMSTR_MARKEDSPOT; +const char *s_AMSTR_MARKSCLEARED = AMSTR_MARKSCLEARED; +// CPhipps - automap rotate & overlay +const char* s_AMSTR_ROTATEON = AMSTR_ROTATEON; +const char* s_AMSTR_ROTATEOFF = AMSTR_ROTATEOFF; +const char* s_AMSTR_OVERLAYON = AMSTR_OVERLAYON; +const char* s_AMSTR_OVERLAYOFF = AMSTR_OVERLAYOFF; +const char *s_STSTR_MUS = STSTR_MUS; +const char *s_STSTR_NOMUS = STSTR_NOMUS; +const char *s_STSTR_DQDON = STSTR_DQDON; +const char *s_STSTR_DQDOFF = STSTR_DQDOFF; +const char *s_STSTR_KFAADDED = STSTR_KFAADDED; +const char *s_STSTR_FAADDED = STSTR_FAADDED; +const char *s_STSTR_NCON = STSTR_NCON; +const char *s_STSTR_NCOFF = STSTR_NCOFF; +const char *s_STSTR_BEHOLD = STSTR_BEHOLD; +const char *s_STSTR_BEHOLDX = STSTR_BEHOLDX; +const char *s_STSTR_CHOPPERS = STSTR_CHOPPERS; +const char *s_STSTR_CLEV = STSTR_CLEV; +const char *s_STSTR_COMPON = STSTR_COMPON; +const char *s_STSTR_COMPOFF = STSTR_COMPOFF; +const char *s_E1TEXT = E1TEXT; +const char *s_E2TEXT = E2TEXT; +const char *s_E3TEXT = E3TEXT; +const char *s_E4TEXT = E4TEXT; +const char *s_C1TEXT = C1TEXT; +const char *s_C2TEXT = C2TEXT; +const char *s_C3TEXT = C3TEXT; +const char *s_C4TEXT = C4TEXT; +const char *s_C5TEXT = C5TEXT; +const char *s_C6TEXT = C6TEXT; +const char *s_P1TEXT = P1TEXT; +const char *s_P2TEXT = P2TEXT; +const char *s_P3TEXT = P3TEXT; +const char *s_P4TEXT = P4TEXT; +const char *s_P5TEXT = P5TEXT; +const char *s_P6TEXT = P6TEXT; +const char *s_T1TEXT = T1TEXT; +const char *s_T2TEXT = T2TEXT; +const char *s_T3TEXT = T3TEXT; +const char *s_T4TEXT = T4TEXT; +const char *s_T5TEXT = T5TEXT; +const char *s_T6TEXT = T6TEXT; +const char *s_CC_ZOMBIE = CC_ZOMBIE; +const char *s_CC_SHOTGUN = CC_SHOTGUN; +const char *s_CC_HEAVY = CC_HEAVY; +const char *s_CC_IMP = CC_IMP; +const char *s_CC_DEMON = CC_DEMON; +const char *s_CC_LOST = CC_LOST; +const char *s_CC_CACO = CC_CACO; +const char *s_CC_HELL = CC_HELL; +const char *s_CC_BARON = CC_BARON; +const char *s_CC_ARACH = CC_ARACH; +const char *s_CC_PAIN = CC_PAIN; +const char *s_CC_REVEN = CC_REVEN; +const char *s_CC_MANCU = CC_MANCU; +const char *s_CC_ARCH = CC_ARCH; +const char *s_CC_SPIDER = CC_SPIDER; +const char *s_CC_CYBER = CC_CYBER; +const char *s_CC_HERO = CC_HERO; +// Ty 03/30/98 - new substitutions for background textures +// during int screens +const char *bgflatE1 = "FLOOR4_8"; // end of DOOM Episode 1 +const char *bgflatE2 = "SFLR6_1"; // end of DOOM Episode 2 +const char *bgflatE3 = "MFLR8_4"; // end of DOOM Episode 3 +const char *bgflatE4 = "MFLR8_3"; // end of DOOM Episode 4 +const char *bgflat06 = "SLIME16"; // DOOM2 after MAP06 +const char *bgflat11 = "RROCK14"; // DOOM2 after MAP11 +const char *bgflat20 = "RROCK07"; // DOOM2 after MAP20 +const char *bgflat30 = "RROCK17"; // DOOM2 after MAP30 +const char *bgflat15 = "RROCK13"; // DOOM2 going MAP15 to MAP31 +const char *bgflat31 = "RROCK19"; // DOOM2 going MAP31 to MAP32 +const char *bgcastcall = "BOSSBACK"; // Panel behind cast call + +const char *startup1 = ""; // blank lines are default and are not printed +const char *startup2 = ""; +const char *startup3 = ""; +const char *startup4 = ""; +const char *startup5 = ""; + +/* Ty 05/03/98 - externalized + * cph - updated for prboom */ +const char *savegamename = "prbmsav"; + +// end d_deh.h variable declarations +// ==================================================================== + +// Do this for a lookup--the pointer (loaded above) is cross-referenced +// to a string key that is the same as the define above. We will use +// strdups to set these new values that we read from the file, orphaning +// the original value set above. + +// CPhipps - make strings pointed to const +typedef struct { + const char **ppstr; // doubly indirect pointer to string + const char *lookup; // pointer to lookup string name +} deh_strs; + +/* CPhipps - const, static + * - removed redundant "Can't XXX in a netgame" strings + */ +static const deh_strs deh_strlookup[] = { + {&s_D_DEVSTR,"D_DEVSTR"}, + {&s_D_CDROM,"D_CDROM"}, + {&s_PRESSKEY,"PRESSKEY"}, + {&s_PRESSYN,"PRESSYN"}, + {&s_QUITMSG,"QUITMSG"}, + {&s_QSAVESPOT,"QSAVESPOT"}, + {&s_SAVEDEAD,"SAVEDEAD"}, + /* cph - disabled to prevent format string attacks in WAD files + {&s_QSPROMPT,"QSPROMPT"}, + {&s_QLPROMPT,"QLPROMPT"},*/ + {&s_NEWGAME,"NEWGAME"}, + {&s_RESTARTLEVEL,"RESTARTLEVEL"}, + {&s_NIGHTMARE,"NIGHTMARE"}, + {&s_SWSTRING,"SWSTRING"}, + {&s_MSGOFF,"MSGOFF"}, + {&s_MSGON,"MSGON"}, + {&s_NETEND,"NETEND"}, + {&s_ENDGAME,"ENDGAME"}, + {&s_DOSY,"DOSY"}, + {&s_DETAILHI,"DETAILHI"}, + {&s_DETAILLO,"DETAILLO"}, + {&s_GAMMALVL0,"GAMMALVL0"}, + {&s_GAMMALVL1,"GAMMALVL1"}, + {&s_GAMMALVL2,"GAMMALVL2"}, + {&s_GAMMALVL3,"GAMMALVL3"}, + {&s_GAMMALVL4,"GAMMALVL4"}, + {&s_EMPTYSTRING,"EMPTYSTRING"}, + {&s_GOTARMOR,"GOTARMOR"}, + {&s_GOTMEGA,"GOTMEGA"}, + {&s_GOTHTHBONUS,"GOTHTHBONUS"}, + {&s_GOTARMBONUS,"GOTARMBONUS"}, + {&s_GOTSTIM,"GOTSTIM"}, + {&s_GOTMEDINEED,"GOTMEDINEED"}, + {&s_GOTMEDIKIT,"GOTMEDIKIT"}, + {&s_GOTSUPER,"GOTSUPER"}, + {&s_GOTBLUECARD,"GOTBLUECARD"}, + {&s_GOTYELWCARD,"GOTYELWCARD"}, + {&s_GOTREDCARD,"GOTREDCARD"}, + {&s_GOTBLUESKUL,"GOTBLUESKUL"}, + {&s_GOTYELWSKUL,"GOTYELWSKUL"}, + {&s_GOTREDSKULL,"GOTREDSKULL"}, + {&s_GOTINVUL,"GOTINVUL"}, + {&s_GOTBERSERK,"GOTBERSERK"}, + {&s_GOTINVIS,"GOTINVIS"}, + {&s_GOTSUIT,"GOTSUIT"}, + {&s_GOTMAP,"GOTMAP"}, + {&s_GOTVISOR,"GOTVISOR"}, + {&s_GOTMSPHERE,"GOTMSPHERE"}, + {&s_GOTCLIP,"GOTCLIP"}, + {&s_GOTCLIPBOX,"GOTCLIPBOX"}, + {&s_GOTROCKET,"GOTROCKET"}, + {&s_GOTROCKBOX,"GOTROCKBOX"}, + {&s_GOTCELL,"GOTCELL"}, + {&s_GOTCELLBOX,"GOTCELLBOX"}, + {&s_GOTSHELLS,"GOTSHELLS"}, + {&s_GOTSHELLBOX,"GOTSHELLBOX"}, + {&s_GOTBACKPACK,"GOTBACKPACK"}, + {&s_GOTBFG9000,"GOTBFG9000"}, + {&s_GOTCHAINGUN,"GOTCHAINGUN"}, + {&s_GOTCHAINSAW,"GOTCHAINSAW"}, + {&s_GOTLAUNCHER,"GOTLAUNCHER"}, + {&s_GOTPLASMA,"GOTPLASMA"}, + {&s_GOTSHOTGUN,"GOTSHOTGUN"}, + {&s_GOTSHOTGUN2,"GOTSHOTGUN2"}, + {&s_PD_BLUEO,"PD_BLUEO"}, + {&s_PD_REDO,"PD_REDO"}, + {&s_PD_YELLOWO,"PD_YELLOWO"}, + {&s_PD_BLUEK,"PD_BLUEK"}, + {&s_PD_REDK,"PD_REDK"}, + {&s_PD_YELLOWK,"PD_YELLOWK"}, + {&s_PD_BLUEC,"PD_BLUEC"}, + {&s_PD_REDC,"PD_REDC"}, + {&s_PD_YELLOWC,"PD_YELLOWC"}, + {&s_PD_BLUES,"PD_BLUES"}, + {&s_PD_REDS,"PD_REDS"}, + {&s_PD_YELLOWS,"PD_YELLOWS"}, + {&s_PD_ANY,"PD_ANY"}, + {&s_PD_ALL3,"PD_ALL3"}, + {&s_PD_ALL6,"PD_ALL6"}, + {&s_GGSAVED,"GGSAVED"}, + {&s_HUSTR_MSGU,"HUSTR_MSGU"}, + {&s_HUSTR_E1M1,"HUSTR_E1M1"}, + {&s_HUSTR_E1M2,"HUSTR_E1M2"}, + {&s_HUSTR_E1M3,"HUSTR_E1M3"}, + {&s_HUSTR_E1M4,"HUSTR_E1M4"}, + {&s_HUSTR_E1M5,"HUSTR_E1M5"}, + {&s_HUSTR_E1M6,"HUSTR_E1M6"}, + {&s_HUSTR_E1M7,"HUSTR_E1M7"}, + {&s_HUSTR_E1M8,"HUSTR_E1M8"}, + {&s_HUSTR_E1M9,"HUSTR_E1M9"}, + {&s_HUSTR_E2M1,"HUSTR_E2M1"}, + {&s_HUSTR_E2M2,"HUSTR_E2M2"}, + {&s_HUSTR_E2M3,"HUSTR_E2M3"}, + {&s_HUSTR_E2M4,"HUSTR_E2M4"}, + {&s_HUSTR_E2M5,"HUSTR_E2M5"}, + {&s_HUSTR_E2M6,"HUSTR_E2M6"}, + {&s_HUSTR_E2M7,"HUSTR_E2M7"}, + {&s_HUSTR_E2M8,"HUSTR_E2M8"}, + {&s_HUSTR_E2M9,"HUSTR_E2M9"}, + {&s_HUSTR_E3M1,"HUSTR_E3M1"}, + {&s_HUSTR_E3M2,"HUSTR_E3M2"}, + {&s_HUSTR_E3M3,"HUSTR_E3M3"}, + {&s_HUSTR_E3M4,"HUSTR_E3M4"}, + {&s_HUSTR_E3M5,"HUSTR_E3M5"}, + {&s_HUSTR_E3M6,"HUSTR_E3M6"}, + {&s_HUSTR_E3M7,"HUSTR_E3M7"}, + {&s_HUSTR_E3M8,"HUSTR_E3M8"}, + {&s_HUSTR_E3M9,"HUSTR_E3M9"}, + {&s_HUSTR_E4M1,"HUSTR_E4M1"}, + {&s_HUSTR_E4M2,"HUSTR_E4M2"}, + {&s_HUSTR_E4M3,"HUSTR_E4M3"}, + {&s_HUSTR_E4M4,"HUSTR_E4M4"}, + {&s_HUSTR_E4M5,"HUSTR_E4M5"}, + {&s_HUSTR_E4M6,"HUSTR_E4M6"}, + {&s_HUSTR_E4M7,"HUSTR_E4M7"}, + {&s_HUSTR_E4M8,"HUSTR_E4M8"}, + {&s_HUSTR_E4M9,"HUSTR_E4M9"}, + {&s_HUSTR_1,"HUSTR_1"}, + {&s_HUSTR_2,"HUSTR_2"}, + {&s_HUSTR_3,"HUSTR_3"}, + {&s_HUSTR_4,"HUSTR_4"}, + {&s_HUSTR_5,"HUSTR_5"}, + {&s_HUSTR_6,"HUSTR_6"}, + {&s_HUSTR_7,"HUSTR_7"}, + {&s_HUSTR_8,"HUSTR_8"}, + {&s_HUSTR_9,"HUSTR_9"}, + {&s_HUSTR_10,"HUSTR_10"}, + {&s_HUSTR_11,"HUSTR_11"}, + {&s_HUSTR_12,"HUSTR_12"}, + {&s_HUSTR_13,"HUSTR_13"}, + {&s_HUSTR_14,"HUSTR_14"}, + {&s_HUSTR_15,"HUSTR_15"}, + {&s_HUSTR_16,"HUSTR_16"}, + {&s_HUSTR_17,"HUSTR_17"}, + {&s_HUSTR_18,"HUSTR_18"}, + {&s_HUSTR_19,"HUSTR_19"}, + {&s_HUSTR_20,"HUSTR_20"}, + {&s_HUSTR_21,"HUSTR_21"}, + {&s_HUSTR_22,"HUSTR_22"}, + {&s_HUSTR_23,"HUSTR_23"}, + {&s_HUSTR_24,"HUSTR_24"}, + {&s_HUSTR_25,"HUSTR_25"}, + {&s_HUSTR_26,"HUSTR_26"}, + {&s_HUSTR_27,"HUSTR_27"}, + {&s_HUSTR_28,"HUSTR_28"}, + {&s_HUSTR_29,"HUSTR_29"}, + {&s_HUSTR_30,"HUSTR_30"}, + {&s_HUSTR_31,"HUSTR_31"}, + {&s_HUSTR_32,"HUSTR_32"}, + {&s_PHUSTR_1,"PHUSTR_1"}, + {&s_PHUSTR_2,"PHUSTR_2"}, + {&s_PHUSTR_3,"PHUSTR_3"}, + {&s_PHUSTR_4,"PHUSTR_4"}, + {&s_PHUSTR_5,"PHUSTR_5"}, + {&s_PHUSTR_6,"PHUSTR_6"}, + {&s_PHUSTR_7,"PHUSTR_7"}, + {&s_PHUSTR_8,"PHUSTR_8"}, + {&s_PHUSTR_9,"PHUSTR_9"}, + {&s_PHUSTR_10,"PHUSTR_10"}, + {&s_PHUSTR_11,"PHUSTR_11"}, + {&s_PHUSTR_12,"PHUSTR_12"}, + {&s_PHUSTR_13,"PHUSTR_13"}, + {&s_PHUSTR_14,"PHUSTR_14"}, + {&s_PHUSTR_15,"PHUSTR_15"}, + {&s_PHUSTR_16,"PHUSTR_16"}, + {&s_PHUSTR_17,"PHUSTR_17"}, + {&s_PHUSTR_18,"PHUSTR_18"}, + {&s_PHUSTR_19,"PHUSTR_19"}, + {&s_PHUSTR_20,"PHUSTR_20"}, + {&s_PHUSTR_21,"PHUSTR_21"}, + {&s_PHUSTR_22,"PHUSTR_22"}, + {&s_PHUSTR_23,"PHUSTR_23"}, + {&s_PHUSTR_24,"PHUSTR_24"}, + {&s_PHUSTR_25,"PHUSTR_25"}, + {&s_PHUSTR_26,"PHUSTR_26"}, + {&s_PHUSTR_27,"PHUSTR_27"}, + {&s_PHUSTR_28,"PHUSTR_28"}, + {&s_PHUSTR_29,"PHUSTR_29"}, + {&s_PHUSTR_30,"PHUSTR_30"}, + {&s_PHUSTR_31,"PHUSTR_31"}, + {&s_PHUSTR_32,"PHUSTR_32"}, + {&s_THUSTR_1,"THUSTR_1"}, + {&s_THUSTR_2,"THUSTR_2"}, + {&s_THUSTR_3,"THUSTR_3"}, + {&s_THUSTR_4,"THUSTR_4"}, + {&s_THUSTR_5,"THUSTR_5"}, + {&s_THUSTR_6,"THUSTR_6"}, + {&s_THUSTR_7,"THUSTR_7"}, + {&s_THUSTR_8,"THUSTR_8"}, + {&s_THUSTR_9,"THUSTR_9"}, + {&s_THUSTR_10,"THUSTR_10"}, + {&s_THUSTR_11,"THUSTR_11"}, + {&s_THUSTR_12,"THUSTR_12"}, + {&s_THUSTR_13,"THUSTR_13"}, + {&s_THUSTR_14,"THUSTR_14"}, + {&s_THUSTR_15,"THUSTR_15"}, + {&s_THUSTR_16,"THUSTR_16"}, + {&s_THUSTR_17,"THUSTR_17"}, + {&s_THUSTR_18,"THUSTR_18"}, + {&s_THUSTR_19,"THUSTR_19"}, + {&s_THUSTR_20,"THUSTR_20"}, + {&s_THUSTR_21,"THUSTR_21"}, + {&s_THUSTR_22,"THUSTR_22"}, + {&s_THUSTR_23,"THUSTR_23"}, + {&s_THUSTR_24,"THUSTR_24"}, + {&s_THUSTR_25,"THUSTR_25"}, + {&s_THUSTR_26,"THUSTR_26"}, + {&s_THUSTR_27,"THUSTR_27"}, + {&s_THUSTR_28,"THUSTR_28"}, + {&s_THUSTR_29,"THUSTR_29"}, + {&s_THUSTR_30,"THUSTR_30"}, + {&s_THUSTR_31,"THUSTR_31"}, + {&s_THUSTR_32,"THUSTR_32"}, + {&s_HUSTR_CHATMACRO1,"HUSTR_CHATMACRO1"}, + {&s_HUSTR_CHATMACRO2,"HUSTR_CHATMACRO2"}, + {&s_HUSTR_CHATMACRO3,"HUSTR_CHATMACRO3"}, + {&s_HUSTR_CHATMACRO4,"HUSTR_CHATMACRO4"}, + {&s_HUSTR_CHATMACRO5,"HUSTR_CHATMACRO5"}, + {&s_HUSTR_CHATMACRO6,"HUSTR_CHATMACRO6"}, + {&s_HUSTR_CHATMACRO7,"HUSTR_CHATMACRO7"}, + {&s_HUSTR_CHATMACRO8,"HUSTR_CHATMACRO8"}, + {&s_HUSTR_CHATMACRO9,"HUSTR_CHATMACRO9"}, + {&s_HUSTR_CHATMACRO0,"HUSTR_CHATMACRO0"}, + {&s_HUSTR_TALKTOSELF1,"HUSTR_TALKTOSELF1"}, + {&s_HUSTR_TALKTOSELF2,"HUSTR_TALKTOSELF2"}, + {&s_HUSTR_TALKTOSELF3,"HUSTR_TALKTOSELF3"}, + {&s_HUSTR_TALKTOSELF4,"HUSTR_TALKTOSELF4"}, + {&s_HUSTR_TALKTOSELF5,"HUSTR_TALKTOSELF5"}, + {&s_HUSTR_MESSAGESENT,"HUSTR_MESSAGESENT"}, + {&s_HUSTR_PLRGREEN,"HUSTR_PLRGREEN"}, + {&s_HUSTR_PLRINDIGO,"HUSTR_PLRINDIGO"}, + {&s_HUSTR_PLRBROWN,"HUSTR_PLRBROWN"}, + {&s_HUSTR_PLRRED,"HUSTR_PLRRED"}, + //{c_HUSTR_KEYGREEN,"HUSTR_KEYGREEN"}, + //{c_HUSTR_KEYINDIGO,"HUSTR_KEYINDIGO"}, + //{c_HUSTR_KEYBROWN,"HUSTR_KEYBROWN"}, + //{c_HUSTR_KEYRED,"HUSTR_KEYRED"}, + {&s_AMSTR_FOLLOWON,"AMSTR_FOLLOWON"}, + {&s_AMSTR_FOLLOWOFF,"AMSTR_FOLLOWOFF"}, + {&s_AMSTR_GRIDON,"AMSTR_GRIDON"}, + {&s_AMSTR_GRIDOFF,"AMSTR_GRIDOFF"}, + {&s_AMSTR_MARKEDSPOT,"AMSTR_MARKEDSPOT"}, + {&s_AMSTR_MARKSCLEARED,"AMSTR_MARKSCLEARED"}, + {&s_STSTR_MUS,"STSTR_MUS"}, + {&s_STSTR_NOMUS,"STSTR_NOMUS"}, + {&s_STSTR_DQDON,"STSTR_DQDON"}, + {&s_STSTR_DQDOFF,"STSTR_DQDOFF"}, + {&s_STSTR_KFAADDED,"STSTR_KFAADDED"}, + {&s_STSTR_FAADDED,"STSTR_FAADDED"}, + {&s_STSTR_NCON,"STSTR_NCON"}, + {&s_STSTR_NCOFF,"STSTR_NCOFF"}, + {&s_STSTR_BEHOLD,"STSTR_BEHOLD"}, + {&s_STSTR_BEHOLDX,"STSTR_BEHOLDX"}, + {&s_STSTR_CHOPPERS,"STSTR_CHOPPERS"}, + {&s_STSTR_CLEV,"STSTR_CLEV"}, + {&s_STSTR_COMPON,"STSTR_COMPON"}, + {&s_STSTR_COMPOFF,"STSTR_COMPOFF"}, + {&s_E1TEXT,"E1TEXT"}, + {&s_E2TEXT,"E2TEXT"}, + {&s_E3TEXT,"E3TEXT"}, + {&s_E4TEXT,"E4TEXT"}, + {&s_C1TEXT,"C1TEXT"}, + {&s_C2TEXT,"C2TEXT"}, + {&s_C3TEXT,"C3TEXT"}, + {&s_C4TEXT,"C4TEXT"}, + {&s_C5TEXT,"C5TEXT"}, + {&s_C6TEXT,"C6TEXT"}, + {&s_P1TEXT,"P1TEXT"}, + {&s_P2TEXT,"P2TEXT"}, + {&s_P3TEXT,"P3TEXT"}, + {&s_P4TEXT,"P4TEXT"}, + {&s_P5TEXT,"P5TEXT"}, + {&s_P6TEXT,"P6TEXT"}, + {&s_T1TEXT,"T1TEXT"}, + {&s_T2TEXT,"T2TEXT"}, + {&s_T3TEXT,"T3TEXT"}, + {&s_T4TEXT,"T4TEXT"}, + {&s_T5TEXT,"T5TEXT"}, + {&s_T6TEXT,"T6TEXT"}, + {&s_CC_ZOMBIE,"CC_ZOMBIE"}, + {&s_CC_SHOTGUN,"CC_SHOTGUN"}, + {&s_CC_HEAVY,"CC_HEAVY"}, + {&s_CC_IMP,"CC_IMP"}, + {&s_CC_DEMON,"CC_DEMON"}, + {&s_CC_LOST,"CC_LOST"}, + {&s_CC_CACO,"CC_CACO"}, + {&s_CC_HELL,"CC_HELL"}, + {&s_CC_BARON,"CC_BARON"}, + {&s_CC_ARACH,"CC_ARACH"}, + {&s_CC_PAIN,"CC_PAIN"}, + {&s_CC_REVEN,"CC_REVEN"}, + {&s_CC_MANCU,"CC_MANCU"}, + {&s_CC_ARCH,"CC_ARCH"}, + {&s_CC_SPIDER,"CC_SPIDER"}, + {&s_CC_CYBER,"CC_CYBER"}, + {&s_CC_HERO,"CC_HERO"}, + {&bgflatE1,"BGFLATE1"}, + {&bgflatE2,"BGFLATE2"}, + {&bgflatE3,"BGFLATE3"}, + {&bgflatE4,"BGFLATE4"}, + {&bgflat06,"BGFLAT06"}, + {&bgflat11,"BGFLAT11"}, + {&bgflat20,"BGFLAT20"}, + {&bgflat30,"BGFLAT30"}, + {&bgflat15,"BGFLAT15"}, + {&bgflat31,"BGFLAT31"}, + {&bgcastcall,"BGCASTCALL"}, + // Ty 04/08/98 - added 5 general purpose startup announcement + // strings for hacker use. See m_menu.c + {&startup1,"STARTUP1"}, + {&startup2,"STARTUP2"}, + {&startup3,"STARTUP3"}, + {&startup4,"STARTUP4"}, + {&startup5,"STARTUP5"}, + {&savegamename,"SAVEGAMENAME"}, // Ty 05/03/98 +}; + +static int deh_numstrlookup = +sizeof(deh_strlookup)/sizeof(deh_strlookup[0]); + +const char *deh_newlevel = "NEWLEVEL"; // CPhipps - const + +// DOOM shareware/registered/retail (Ultimate) names. +// CPhipps - const**const +const char **const mapnames[] = +{ + &s_HUSTR_E1M1, + &s_HUSTR_E1M2, + &s_HUSTR_E1M3, + &s_HUSTR_E1M4, + &s_HUSTR_E1M5, + &s_HUSTR_E1M6, + &s_HUSTR_E1M7, + &s_HUSTR_E1M8, + &s_HUSTR_E1M9, + + &s_HUSTR_E2M1, + &s_HUSTR_E2M2, + &s_HUSTR_E2M3, + &s_HUSTR_E2M4, + &s_HUSTR_E2M5, + &s_HUSTR_E2M6, + &s_HUSTR_E2M7, + &s_HUSTR_E2M8, + &s_HUSTR_E2M9, + + &s_HUSTR_E3M1, + &s_HUSTR_E3M2, + &s_HUSTR_E3M3, + &s_HUSTR_E3M4, + &s_HUSTR_E3M5, + &s_HUSTR_E3M6, + &s_HUSTR_E3M7, + &s_HUSTR_E3M8, + &s_HUSTR_E3M9, + + &s_HUSTR_E4M1, + &s_HUSTR_E4M2, + &s_HUSTR_E4M3, + &s_HUSTR_E4M4, + &s_HUSTR_E4M5, + &s_HUSTR_E4M6, + &s_HUSTR_E4M7, + &s_HUSTR_E4M8, + &s_HUSTR_E4M9, + + &deh_newlevel, // spares? Unused. + &deh_newlevel, + &deh_newlevel, + &deh_newlevel, + &deh_newlevel, + &deh_newlevel, + &deh_newlevel, + &deh_newlevel, + &deh_newlevel +}; + +// CPhipps - const**const +const char **const mapnames2[] = // DOOM 2 map names. +{ + &s_HUSTR_1, + &s_HUSTR_2, + &s_HUSTR_3, + &s_HUSTR_4, + &s_HUSTR_5, + &s_HUSTR_6, + &s_HUSTR_7, + &s_HUSTR_8, + &s_HUSTR_9, + &s_HUSTR_10, + &s_HUSTR_11, + + &s_HUSTR_12, + &s_HUSTR_13, + &s_HUSTR_14, + &s_HUSTR_15, + &s_HUSTR_16, + &s_HUSTR_17, + &s_HUSTR_18, + &s_HUSTR_19, + &s_HUSTR_20, + + &s_HUSTR_21, + &s_HUSTR_22, + &s_HUSTR_23, + &s_HUSTR_24, + &s_HUSTR_25, + &s_HUSTR_26, + &s_HUSTR_27, + &s_HUSTR_28, + &s_HUSTR_29, + &s_HUSTR_30, + &s_HUSTR_31, + &s_HUSTR_32, +}; + +// CPhipps - const**const +const char **const mapnamesp[] = // Plutonia WAD map names. +{ + &s_PHUSTR_1, + &s_PHUSTR_2, + &s_PHUSTR_3, + &s_PHUSTR_4, + &s_PHUSTR_5, + &s_PHUSTR_6, + &s_PHUSTR_7, + &s_PHUSTR_8, + &s_PHUSTR_9, + &s_PHUSTR_10, + &s_PHUSTR_11, + + &s_PHUSTR_12, + &s_PHUSTR_13, + &s_PHUSTR_14, + &s_PHUSTR_15, + &s_PHUSTR_16, + &s_PHUSTR_17, + &s_PHUSTR_18, + &s_PHUSTR_19, + &s_PHUSTR_20, + + &s_PHUSTR_21, + &s_PHUSTR_22, + &s_PHUSTR_23, + &s_PHUSTR_24, + &s_PHUSTR_25, + &s_PHUSTR_26, + &s_PHUSTR_27, + &s_PHUSTR_28, + &s_PHUSTR_29, + &s_PHUSTR_30, + &s_PHUSTR_31, + &s_PHUSTR_32, +}; + +// CPhipps - const**const +const char **const mapnamest[] = // TNT WAD map names. +{ + &s_THUSTR_1, + &s_THUSTR_2, + &s_THUSTR_3, + &s_THUSTR_4, + &s_THUSTR_5, + &s_THUSTR_6, + &s_THUSTR_7, + &s_THUSTR_8, + &s_THUSTR_9, + &s_THUSTR_10, + &s_THUSTR_11, + + &s_THUSTR_12, + &s_THUSTR_13, + &s_THUSTR_14, + &s_THUSTR_15, + &s_THUSTR_16, + &s_THUSTR_17, + &s_THUSTR_18, + &s_THUSTR_19, + &s_THUSTR_20, + + &s_THUSTR_21, + &s_THUSTR_22, + &s_THUSTR_23, + &s_THUSTR_24, + &s_THUSTR_25, + &s_THUSTR_26, + &s_THUSTR_27, + &s_THUSTR_28, + &s_THUSTR_29, + &s_THUSTR_30, + &s_THUSTR_31, + &s_THUSTR_32, +}; + +// Function prototypes +void lfstrip(char *); // strip the \r and/or \n off of a line +void rstrip(char *); // strip trailing whitespace +char * ptr_lstrip(char *); // point past leading whitespace +boolean deh_GetData(char *, char *, uint_64_t *, char **, FILE *); +boolean deh_procStringSub(char *, char *, char *, FILE *); +char * dehReformatStr(char *); + +// Prototypes for block processing functions +// Pointers to these functions are used as the blocks are encountered. + +static void deh_procThing(DEHFILE *fpin, FILE* fpout, char *line); +static void deh_procFrame(DEHFILE *, FILE*, char *); +static void deh_procPointer(DEHFILE *, FILE*, char *); +static void deh_procSounds(DEHFILE *, FILE*, char *); +static void deh_procAmmo(DEHFILE *, FILE*, char *); +static void deh_procWeapon(DEHFILE *, FILE*, char *); +static void deh_procSprite(DEHFILE *, FILE*, char *); +static void deh_procCheat(DEHFILE *, FILE*, char *); +static void deh_procMisc(DEHFILE *, FILE*, char *); +static void deh_procText(DEHFILE *, FILE*, char *); +static void deh_procPars(DEHFILE *, FILE*, char *); +static void deh_procStrings(DEHFILE *, FILE*, char *); +static void deh_procError(DEHFILE *, FILE*, char *); +static void deh_procBexCodePointers(DEHFILE *, FILE*, char *); +static void deh_procHelperThing(DEHFILE *, FILE *, char *); // haleyjd 9/22/99 +// haleyjd: handlers to fully deprecate the DeHackEd text section +static void deh_procBexSounds(DEHFILE *, FILE *, char *); +static void deh_procBexMusic(DEHFILE *, FILE *, char *); +static void deh_procBexSprites(DEHFILE *, FILE *, char *); + +// Structure deh_block is used to hold the block names that can +// be encountered, and the routines to use to decipher them + +typedef struct +{ + const char *key; // a mnemonic block code name // CPhipps - const* + void (*const fptr)(DEHFILE *, FILE*, char *); // handler +} deh_block; + +#define DEH_BUFFERMAX 1024 // input buffer area size, hardcodedfor now +// killough 8/9/98: make DEH_BLOCKMAX self-adjusting +#define DEH_BLOCKMAX (sizeof deh_blocks/sizeof*deh_blocks) // size of array +#define DEH_MAXKEYLEN 32 // as much of any key as we'll look at +#define DEH_MOBJINFOMAX 24 // number of ints in the mobjinfo_t structure (!) + +// Put all the block header values, and the function to be called when that +// one is encountered, in this array: +static const deh_block deh_blocks[] = { // CPhipps - static const + /* 0 */ {"Thing",deh_procThing}, + /* 1 */ {"Frame",deh_procFrame}, + /* 2 */ {"Pointer",deh_procPointer}, + /* 3 */ {"Sound",deh_procSounds}, // Ty 03/16/98 corrected from "Sounds" + /* 4 */ {"Ammo",deh_procAmmo}, + /* 5 */ {"Weapon",deh_procWeapon}, + /* 6 */ {"Sprite",deh_procSprite}, + /* 7 */ {"Cheat",deh_procCheat}, + /* 8 */ {"Misc",deh_procMisc}, + /* 9 */ {"Text",deh_procText}, // -- end of standard "deh" entries, + + // begin BOOM Extensions (BEX) + + /* 10 */ {"[STRINGS]",deh_procStrings}, // new string changes + /* 11 */ {"[PARS]",deh_procPars}, // alternative block marker + /* 12 */ {"[CODEPTR]",deh_procBexCodePointers}, // bex codepointers by mnemonic + /* 13 */ {"[HELPER]", deh_procHelperThing}, // helper thing substitution haleyjd 9/22/99 + /* 14 */ {"[SPRITES]", deh_procBexSprites}, // bex style sprites + /* 15 */ {"[SOUNDS]", deh_procBexSounds}, // bex style sounds + /* 16 */ {"[MUSIC]", deh_procBexMusic}, // bex style music + /* 17 */ {"", deh_procError} // dummy to handle anything else +}; + +// flag to skip included deh-style text, used with INCLUDE NOTEXT directive +static boolean includenotext = false; + +// MOBJINFO - Dehacked block name = "Thing" +// Usage: Thing nn (name) +// These are for mobjinfo_t types. Each is an integer +// within the structure, so we can use index of the string in this +// array to offset by sizeof(int) into the mobjinfo_t array at [nn] +// * things are base zero but dehacked considers them to start at #1. *** +// CPhipps - static const + +static const char *deh_mobjinfo[DEH_MOBJINFOMAX] = +{ + "ID #", // .doomednum + "Initial frame", // .spawnstate + "Hit points", // .spawnhealth + "First moving frame", // .seestate + "Alert sound", // .seesound + "Reaction time", // .reactiontime + "Attack sound", // .attacksound + "Injury frame", // .painstate + "Pain chance", // .painchance + "Pain sound", // .painsound + "Close attack frame", // .meleestate + "Far attack frame", // .missilestate + "Death frame", // .deathstate + "Exploding frame", // .xdeathstate + "Death sound", // .deathsound + "Speed", // .speed + "Width", // .radius + "Height", // .height + "Mass", // .mass + "Missile damage", // .damage + "Action sound", // .activesound + "Bits", // .flags + "Bits2", // .flags + "Respawn frame" // .raisestate +}; + +// Strings that are used to indicate flags ("Bits" in mobjinfo) +// This is an array of bit masks that are related to p_mobj.h +// values, using the smae names without the MF_ in front. +// Ty 08/27/98 new code +// +// killough 10/98: +// +// Convert array to struct to allow multiple values, make array size variable + +#define DEH_MOBJFLAGMAX (sizeof deh_mobjflags/sizeof*deh_mobjflags) + +struct deh_mobjflags_s { + const char *name; // CPhipps - const* + uint_64_t value; +}; + +// CPhipps - static const +static const struct deh_mobjflags_s deh_mobjflags[] = { + {"SPECIAL", MF_SPECIAL}, // call P_Specialthing when touched + {"SOLID", MF_SOLID}, // block movement + {"SHOOTABLE", MF_SHOOTABLE}, // can be hit + {"NOSECTOR", MF_NOSECTOR}, // invisible but touchable + {"NOBLOCKMAP", MF_NOBLOCKMAP}, // inert but displayable + {"AMBUSH", MF_AMBUSH}, // deaf monster + {"JUSTHIT", MF_JUSTHIT}, // will try to attack right back + {"JUSTATTACKED", MF_JUSTATTACKED}, // take at least 1 step before attacking + {"SPAWNCEILING", MF_SPAWNCEILING}, // initially hang from ceiling + {"NOGRAVITY", MF_NOGRAVITY}, // don't apply gravity during play + {"DROPOFF", MF_DROPOFF}, // can jump from high places + {"PICKUP", MF_PICKUP}, // will pick up items + {"NOCLIP", MF_NOCLIP}, // goes through walls + {"SLIDE", MF_SLIDE}, // keep info about sliding along walls + {"FLOAT", MF_FLOAT}, // allow movement to any height + {"TELEPORT", MF_TELEPORT}, // don't cross lines or look at heights + {"MISSILE", MF_MISSILE}, // don't hit same species, explode on block + {"DROPPED", MF_DROPPED}, // dropped, not spawned (like ammo clip) + {"SHADOW", MF_SHADOW}, // use fuzzy draw like spectres + {"NOBLOOD", MF_NOBLOOD}, // puffs instead of blood when shot + {"CORPSE", MF_CORPSE}, // so it will slide down steps when dead + {"INFLOAT", MF_INFLOAT}, // float but not to target height + {"COUNTKILL", MF_COUNTKILL}, // count toward the kills total + {"COUNTITEM", MF_COUNTITEM}, // count toward the items total + {"SKULLFLY", MF_SKULLFLY}, // special handling for flying skulls + {"NOTDMATCH", MF_NOTDMATCH}, // do not spawn in deathmatch + + // killough 10/98: TRANSLATION consists of 2 bits, not 1: + + {"TRANSLATION", MF_TRANSLATION1}, // for Boom bug-compatibility + {"TRANSLATION1", MF_TRANSLATION1}, // use translation table for color (players) + {"TRANSLATION2", MF_TRANSLATION2}, // use translation table for color (players) + {"UNUSED1", MF_TRANSLATION2}, // unused bit # 1 -- For Boom bug-compatibility + {"UNUSED2", MF_UNUSED2}, // unused bit # 2 -- For Boom compatibility + {"UNUSED3", MF_UNUSED3}, // unused bit # 3 -- For Boom compatibility + {"UNUSED4", MF_TRANSLUCENT}, // unused bit # 4 -- For Boom compatibility + {"TRANSLUCENT", MF_TRANSLUCENT}, // apply translucency to sprite (BOOM) + {"TOUCHY", MF_TOUCHY}, // dies on contact with solid objects (MBF) + {"BOUNCES", MF_BOUNCES}, // bounces off floors, ceilings and maybe walls (MBF) + {"FRIEND", MF_FRIEND}, // a friend of the player(s) (MBF) +}; + +// STATE - Dehacked block name = "Frame" and "Pointer" +// Usage: Frame nn +// Usage: Pointer nn (Frame nn) +// These are indexed separately, for lookup to the actual +// function pointers. Here we'll take whatever Dehacked gives +// us and go from there. The (Frame nn) after the pointer is the +// real place to put this value. The "Pointer" value is an xref +// that Dehacked uses and is useless to us. +// * states are base zero and have a dummy #0 (TROO) + +static const char *deh_state[] = // CPhipps - static const* +{ + "Sprite number", // .sprite (spritenum_t) // an enum + "Sprite subnumber", // .frame (long) + "Duration", // .tics (long) + "Next frame", // .nextstate (statenum_t) + // This is set in a separate "Pointer" block from Dehacked + "Codep Frame", // pointer to first use of action (actionf_t) + "Unknown 1", // .misc1 (long) + "Unknown 2" // .misc2 (long) +}; + +// SFXINFO_STRUCT - Dehacked block name = "Sounds" +// Sound effects, typically not changed (redirected, and new sfx put +// into the pwad, but not changed here. Can you tell that Gregdidn't +// know what they were for, mostly? Can you tell that I don't either? +// Mostly I just put these into the same slots as they are in the struct. +// This may not be supported in our -deh option if it doesn't make sense by then. + +// * sounds are base zero but have a dummy #0 + +static const char *deh_sfxinfo[] = // CPhipps - static const* +{ + "Offset", // pointer to a name string, changed in text + "Zero/One", // .singularity (int, one at a time flag) + "Value", // .priority + "Zero 1", // .link (sfxinfo_t*) referenced sound if linked + "Zero 2", // .pitch + "Zero 3", // .volume + "Zero 4", // .data (SAMPLE*) sound data + "Neg. One 1", // .usefulness + "Neg. One 2" // .lumpnum +}; + +// MUSICINFO is not supported in Dehacked. Ignored here. +// * music entries are base zero but have a dummy #0 + +// SPRITE - Dehacked block name = "Sprite" +// Usage = Sprite nn +// Sprite redirection by offset into the text area - unsupported by BOOM +// * sprites are base zero and dehacked uses it that way. + +// static const char *deh_sprite[] = // CPhipps - static const* +// { +// "Offset" // supposed to be the offset into the text section +// }; + +// AMMO - Dehacked block name = "Ammo" +// usage = Ammo n (name) +// Ammo information for the few types of ammo + +static const char *deh_ammo[] = // CPhipps - static const* +{ + "Max ammo", // maxammo[] + "Per ammo" // clipammo[] +}; + +// WEAPONS - Dehacked block name = "Weapon" +// Usage: Weapon nn (name) +// Basically a list of frames and what kind of ammo (see above)it uses. + +static const char *deh_weapon[] = // CPhipps - static const* +{ + "Ammo type", // .ammo + "Deselect frame", // .upstate + "Select frame", // .downstate + "Bobbing frame", // .readystate + "Shooting frame", // .atkstate + "Firing frame" // .flashstate +}; + +// CHEATS - Dehacked block name = "Cheat" +// Usage: Cheat 0 +// Always uses a zero in the dehacked file, for consistency. No meaning. +// These are just plain funky terms compared with id's +// +// killough 4/18/98: integrated into main cheat table now (see st_stuff.c) + +// MISC - Dehacked block name = "Misc" +// Usage: Misc 0 +// Always uses a zero in the dehacked file, for consistency. No meaning. + +static const char *deh_misc[] = // CPhipps - static const* +{ + "Initial Health", // initial_health + "Initial Bullets", // initial_bullets + "Max Health", // maxhealth + "Max Armor", // max_armor + "Green Armor Class", // green_armor_class + "Blue Armor Class", // blue_armor_class + "Max Soulsphere", // max_soul + "Soulsphere Health", // soul_health + "Megasphere Health", // mega_health + "God Mode Health", // god_health + "IDFA Armor", // idfa_armor + "IDFA Armor Class", // idfa_armor_class + "IDKFA Armor", // idkfa_armor + "IDKFA Armor Class", // idkfa_armor_class + "BFG Cells/Shot", // BFGCELLS + "Monsters Infight" // Unknown--not a specific number it seems, but + // the logic has to be here somewhere or + // it'd happen always +}; + +// TEXT - Dehacked block name = "Text" +// Usage: Text fromlen tolen +// Dehacked allows a bit of adjustment to the length (why?) + +// BEX extension [CODEPTR] +// Usage: Start block, then each line is: +// FRAME nnn = PointerMnemonic + +typedef struct { + actionf_t cptr; // actual pointer to the subroutine + const char *lookup; // mnemonic lookup string to be specified in BEX + // CPhipps - const* +} deh_bexptr; + +static const deh_bexptr deh_bexptrs[] = // CPhipps - static const +{ + {A_Light0, "A_Light0"}, + {A_WeaponReady, "A_WeaponReady"}, + {A_Lower, "A_Lower"}, + {A_Raise, "A_Raise"}, + {A_Punch, "A_Punch"}, + {A_ReFire, "A_ReFire"}, + {A_FirePistol, "A_FirePistol"}, + {A_Light1, "A_Light1"}, + {A_FireShotgun, "A_FireShotgun"}, + {A_Light2, "A_Light2"}, + {A_FireShotgun2, "A_FireShotgun2"}, + {A_CheckReload, "A_CheckReload"}, + {A_OpenShotgun2, "A_OpenShotgun2"}, + {A_LoadShotgun2, "A_LoadShotgun2"}, + {A_CloseShotgun2, "A_CloseShotgun2"}, + {A_FireCGun, "A_FireCGun"}, + {A_GunFlash, "A_GunFlash"}, + {A_FireMissile, "A_FireMissile"}, + {A_Saw, "A_Saw"}, + {A_FirePlasma, "A_FirePlasma"}, + {A_BFGsound, "A_BFGsound"}, + {A_FireBFG, "A_FireBFG"}, + {A_BFGSpray, "A_BFGSpray"}, + {A_Explode, "A_Explode"}, + {A_Pain, "A_Pain"}, + {A_PlayerScream, "A_PlayerScream"}, + {A_Fall, "A_Fall"}, + {A_XScream, "A_XScream"}, + {A_Look, "A_Look"}, + {A_Chase, "A_Chase"}, + {A_FaceTarget, "A_FaceTarget"}, + {A_PosAttack, "A_PosAttack"}, + {A_Scream, "A_Scream"}, + {A_SPosAttack, "A_SPosAttack"}, + {A_VileChase, "A_VileChase"}, + {A_VileStart, "A_VileStart"}, + {A_VileTarget, "A_VileTarget"}, + {A_VileAttack, "A_VileAttack"}, + {A_StartFire, "A_StartFire"}, + {A_Fire, "A_Fire"}, + {A_FireCrackle, "A_FireCrackle"}, + {A_Tracer, "A_Tracer"}, + {A_SkelWhoosh, "A_SkelWhoosh"}, + {A_SkelFist, "A_SkelFist"}, + {A_SkelMissile, "A_SkelMissile"}, + {A_FatRaise, "A_FatRaise"}, + {A_FatAttack1, "A_FatAttack1"}, + {A_FatAttack2, "A_FatAttack2"}, + {A_FatAttack3, "A_FatAttack3"}, + {A_BossDeath, "A_BossDeath"}, + {A_CPosAttack, "A_CPosAttack"}, + {A_CPosRefire, "A_CPosRefire"}, + {A_TroopAttack, "A_TroopAttack"}, + {A_SargAttack, "A_SargAttack"}, + {A_HeadAttack, "A_HeadAttack"}, + {A_BruisAttack, "A_BruisAttack"}, + {A_SkullAttack, "A_SkullAttack"}, + {A_Metal, "A_Metal"}, + {A_SpidRefire, "A_SpidRefire"}, + {A_BabyMetal, "A_BabyMetal"}, + {A_BspiAttack, "A_BspiAttack"}, + {A_Hoof, "A_Hoof"}, + {A_CyberAttack, "A_CyberAttack"}, + {A_PainAttack, "A_PainAttack"}, + {A_PainDie, "A_PainDie"}, + {A_KeenDie, "A_KeenDie"}, + {A_BrainPain, "A_BrainPain"}, + {A_BrainScream, "A_BrainScream"}, + {A_BrainDie, "A_BrainDie"}, + {A_BrainAwake, "A_BrainAwake"}, + {A_BrainSpit, "A_BrainSpit"}, + {A_SpawnSound, "A_SpawnSound"}, + {A_SpawnFly, "A_SpawnFly"}, + {A_BrainExplode, "A_BrainExplode"}, + {A_Detonate, "A_Detonate"}, // killough 8/9/98 + {A_Mushroom, "A_Mushroom"}, // killough 10/98 + {A_Die, "A_Die"}, // killough 11/98 + {A_Spawn, "A_Spawn"}, // killough 11/98 + {A_Turn, "A_Turn"}, // killough 11/98 + {A_Face, "A_Face"}, // killough 11/98 + {A_Scratch, "A_Scratch"}, // killough 11/98 + {A_PlaySound, "A_PlaySound"}, // killough 11/98 + {A_RandomJump, "A_RandomJump"}, // killough 11/98 + {A_LineEffect, "A_LineEffect"}, // killough 11/98 + + // This NULL entry must be the last in the list + {NULL, "A_NULL"}, // Ty 05/16/98 +}; + +// to hold startup code pointers from INFO.C +// CPhipps - static +static actionf_t deh_codeptr[NUMSTATES]; + +// haleyjd: support for BEX SPRITES, SOUNDS, and MUSIC +char *deh_spritenames[NUMSPRITES + 1]; +char *deh_musicnames[NUMMUSIC + 1]; +char *deh_soundnames[NUMSFX + 1]; + +void D_BuildBEXTables(void) +{ + int i; + + // moved from ProcessDehFile, then we don't need the static int i + for (i = 0; i < NUMSTATES; i++) // remember what they start as for deh xref + deh_codeptr[i] = states[i].action; + + for(i = 0; i < NUMSPRITES; i++) + deh_spritenames[i] = strdup(sprnames[i]); + deh_spritenames[NUMSPRITES] = NULL; + + for(i = 1; i < NUMMUSIC; i++) + deh_musicnames[i] = strdup(S_music[i].name); + deh_musicnames[0] = deh_musicnames[NUMMUSIC] = NULL; + + for(i = 1; i < NUMSFX; i++) + deh_soundnames[i] = strdup(S_sfx[i].name); + deh_soundnames[0] = deh_soundnames[NUMSFX] = NULL; +} + +// ==================================================================== +// ProcessDehFile +// Purpose: Read and process a DEH or BEX file +// Args: filename -- name of the DEH/BEX file +// outfilename -- output file (DEHOUT.TXT), appended to here +// Returns: void +// +// killough 10/98: +// substantially modified to allow input from wad lumps instead of .deh files. + +void ProcessDehFile(const char *filename, const char *outfilename, int lumpnum) +{ + static FILE *fileout; // In case -dehout was used + DEHFILE infile, *filein = &infile; // killough 10/98 + char inbuffer[DEH_BUFFERMAX]; // Place to put the primary infostring + + // Open output file if we're writing output + if (outfilename && *outfilename && !fileout) + { + static boolean firstfile = true; // to allow append to output log + if (!strcmp(outfilename, "-")) + fileout = stdout; + else + if (!(fileout=fopen(outfilename, firstfile ? "wt" : "at"))) + { + lprintf(LO_WARN, "Could not open -dehout file %s\n... using stdout.\n", + outfilename); + fileout = stdout; + } + firstfile = false; + } + + // killough 10/98: allow DEH files to come from wad lumps + + if (filename) + { + if (!(infile.f = fopen(filename,"rt"))) + { + lprintf(LO_WARN, "-deh file %s not found\n",filename); + return; // should be checked up front anyway + } + infile.lump = NULL; + } + else // DEH file comes from lump indicated by third argument + { + infile.size = W_LumpLength(lumpnum); + infile.inp = infile.lump = W_CacheLumpNum(lumpnum); + filename = "(WAD)"; + } + + lprintf(LO_INFO, "Loading DEH file %s\n",filename); + if (fileout) fprintf(fileout,"\nLoading DEH file %s\n\n",filename); + + // move deh_codeptr initialisation to D_BuildBEXTables + + // loop until end of file + + while (dehfgets(inbuffer,sizeof(inbuffer),filein)) + { + unsigned i; + + lfstrip(inbuffer); + if (fileout) fprintf(fileout,"Line='%s'\n",inbuffer); + if (!*inbuffer || *inbuffer == '#' || *inbuffer == ' ') + continue; /* Blank line or comment line */ + + // -- If DEH_BLOCKMAX is set right, the processing is independently + // -- handled based on data in the deh_blocks[] structure array + + // killough 10/98: INCLUDE code rewritten to allow arbitrary nesting, + // and to greatly simplify code, fix memory leaks, other bugs + + if (!strnicmp(inbuffer,"INCLUDE",7)) // include a file + { + // preserve state while including a file + // killough 10/98: moved to here + + char *nextfile; + boolean oldnotext = includenotext; // killough 10/98 + + // killough 10/98: exclude if inside wads (only to discourage + // the practice, since the code could otherwise handle it) + + if (infile.lump) + { + if (fileout) + fprintf(fileout, + "No files may be included from wads: %s\n",inbuffer); + continue; + } + + // check for no-text directive, used when including a DEH + // file but using the BEX format to handle strings + + if (!strnicmp(nextfile = ptr_lstrip(inbuffer+7),"NOTEXT",6)) + includenotext = true, nextfile = ptr_lstrip(nextfile+6); + + if (fileout) + fprintf(fileout,"Branching to include file %s...\n", nextfile); + + // killough 10/98: + // Second argument must be NULL to prevent closing fileout too soon + + ProcessDehFile(nextfile,NULL,0); // do the included file + + includenotext = oldnotext; + if (fileout) fprintf(fileout,"...continuing with %s\n",filename); + continue; + } + + for (i=0; i= NUMSTATES) + { + if (fpout) fprintf(fpout,"Bad pointer number %d of %d\n", + indexnum, NUMSTATES); + return; // killough 10/98: fix SegViol + } + strcpy(key,"A_"); // reusing the key area to prefix the mnemonic + strcat(key,ptr_lstrip(mnemonic)); + + found = FALSE; + i= -1; // incremented to start at zero at the top of the loop + do // Ty 05/16/98 - fix loop logic to look for null ending entry + { + ++i; + if (!stricmp(key,deh_bexptrs[i].lookup)) + { // Ty 06/01/98 - add to states[].action for new djgcc version + states[indexnum].action = deh_bexptrs[i].cptr; // assign + if (fpout) fprintf(fpout, + " - applied %s from codeptr[%d] to states[%d]\n", + deh_bexptrs[i].lookup,i,indexnum); + found = TRUE; + } + } while (!found && (deh_bexptrs[i].cptr != NULL)); + + if (!found) + if (fpout) fprintf(fpout, + "Invalid frame pointer mnemonic '%s' at %d\n", + mnemonic, indexnum); + } + return; +} + +//--------------------------------------------------------------------------- +// To be on the safe, compatible side, we manually convert DEH bitflags +// to prboom types - POPE +//--------------------------------------------------------------------------- +static uint_64_t getConvertedDEHBits(uint_64_t bits) { + static const uint_64_t bitMap[32] = { + /* cf linuxdoom-1.10 p_mobj.h */ + MF_SPECIAL, // 0 Can be picked up - When touched the thing can be picked up. + MF_SOLID, // 1 Obstacle - The thing is solid and will not let you (or others) pass through it + MF_SHOOTABLE, // 2 Shootable - Can be shot. + MF_NOSECTOR, // 3 Total Invisibility - Invisible, but can be touched + MF_NOBLOCKMAP, // 4 Don't use the blocklinks (inert but displayable) + MF_AMBUSH, // 5 Semi deaf - The thing is a deaf monster + MF_JUSTHIT, // 6 In pain - Will try to attack right back after being hit + MF_JUSTATTACKED, // 7 Steps before attack - Will take at least one step before attacking + MF_SPAWNCEILING, // 8 Hangs from ceiling - When the level starts, this thing will be at ceiling height. + MF_NOGRAVITY, // 9 No gravity - Gravity does not affect this thing + MF_DROPOFF, // 10 Travels over cliffs - Monsters normally do not walk off ledges/steps they could not walk up. With this set they can walk off any height of cliff. Usually only used for flying monsters. + MF_PICKUP, // 11 Pick up items - The thing can pick up gettable items. + MF_NOCLIP, // 12 No clipping - Thing can walk through walls. + MF_SLIDE, // 13 Slides along walls - Keep info about sliding along walls (don't really know much about this one). + MF_FLOAT, // 14 Floating - Thing can move to any height + MF_TELEPORT, // 15 Semi no clipping - Don't cross lines or look at teleport heights. (don't really know much about this one either). + MF_MISSILE, // 16 Projectiles - Behaves like a projectile, explodes when hitting something that blocks movement + MF_DROPPED, // 17 Disappearing weapon - Dropped, not spawned (like an ammo clip) I have not had much success in using this one. + MF_SHADOW, // 18 Partial invisibility - Drawn like a spectre. + MF_NOBLOOD, // 19 Puffs (vs. bleeds) - If hit will spawn bullet puffs instead of blood splats. + MF_CORPSE, // 20 Sliding helpless - Will slide down steps when dead. + MF_INFLOAT, // 21 No auto levelling - float but not to target height (?) + MF_COUNTKILL, // 22 Affects kill % - counted as a killable enemy and affects percentage kills on level summary. + MF_COUNTITEM, // 23 Affects item % - affects percentage items gathered on level summary. + MF_SKULLFLY, // 24 Running - special handling for flying skulls. + MF_NOTDMATCH, // 25 Not in deathmatch - do not spawn in deathmatch (like keys) + MF_TRANSLATION1, // 26 Color 1 (grey / red) + MF_TRANSLATION2, // 27 Color 2 (brown / red) + // Convert bit 28 to MF_TOUCHY, not (MF_TRANSLATION1|MF_TRANSLATION2) + // fixes bug #1576151 (part 1) + MF_TOUCHY, // 28 - explodes on contact (MBF) + MF_BOUNCES, // 29 - bounces off walls and floors (MBF) + MF_FRIEND, // 30 - friendly monster helps players (MBF) + MF_TRANSLUCENT // e6y: Translucency via dehacked/bex doesn't work without it + }; + int i; + uint_64_t shiftBits = bits; + uint_64_t convertedBits = 0; + for (i=0; i<32; i++) { + if (shiftBits & 0x1) convertedBits |= bitMap[i]; + shiftBits >>= 1; + } + return convertedBits; +} + +//--------------------------------------------------------------------------- +// See usage below for an explanation of this function's existence - POPE +//--------------------------------------------------------------------------- +static void setMobjInfoValue(int mobjInfoIndex, int keyIndex, uint_64_t value) { + mobjinfo_t *mi; + if (mobjInfoIndex >= NUMMOBJTYPES || mobjInfoIndex < 0) return; + mi = &mobjinfo[mobjInfoIndex]; + switch (keyIndex) { + case 0: mi->doomednum = (int)value; return; + case 1: mi->spawnstate = (int)value; return; + case 2: mi->spawnhealth = (int)value; return; + case 3: mi->seestate = (int)value; return; + case 4: mi->seesound = (int)value; return; + case 5: mi->reactiontime = (int)value; return; + case 6: mi->attacksound = (int)value; return; + case 7: mi->painstate = (int)value; return; + case 8: mi->painchance = (int)value; return; + case 9: mi->painsound = (int)value; return; + case 10: mi->meleestate = (int)value; return; + case 11: mi->missilestate = (int)value; return; + case 12: mi->deathstate = (int)value; return; + case 13: mi->xdeathstate = (int)value; return; + case 14: mi->deathsound = (int)value; return; + case 15: mi->speed = (int)value; return; + case 16: mi->radius = (int)value; return; + case 17: mi->height = (int)value; return; + case 18: mi->mass = (int)value; return; + case 19: mi->damage = (int)value; return; + case 20: mi->activesound = (int)value; return; + case 21: mi->flags = value; return; + case 22: return; // "Bits2", unused + case 23: mi->raisestate = (int)value; return; + default: return; + } +} + +// ==================================================================== +// deh_procThing +// Purpose: Handle DEH Thing block +// Args: fpin -- input file stream +// fpout -- output file stream (DEHOUT.TXT) +// line -- current line in file to process +// Returns: void +// +// Ty 8/27/98 - revised to also allow mnemonics for +// bit masks for monster attributes +// + +static void deh_procThing(DEHFILE *fpin, FILE* fpout, char *line) +{ + char key[DEH_MAXKEYLEN]; + char inbuffer[DEH_BUFFERMAX]; + uint_64_t value; // All deh values are ints or longs + int indexnum; + int ix; + char *strval; + + strncpy(inbuffer,line,DEH_BUFFERMAX); + if (fpout) fprintf(fpout,"Thing line: '%s'\n",inbuffer); + + // killough 8/98: allow hex numbers in input: + ix = sscanf(inbuffer,"%s %i",key, &indexnum); + if (fpout) fprintf(fpout,"count=%d, Thing %d\n",ix, indexnum); + + // Note that the mobjinfo[] array is base zero, but object numbers + // in the dehacked file start with one. Grumble. + --indexnum; + + // now process the stuff + // Note that for Things we can look up the key and use its offset + // in the array of key strings as an int offset in the structure + + // get a line until a blank or end of file--it's not + // blank now because it has our incoming key in it + while (!dehfeof(fpin) && *inbuffer && (*inbuffer != ' ')) + { + // e6y: Correction of wrong processing of Bits parameter if its value is equal to zero + // No more desync on HACX demos. + int bGetData; + + if (!dehfgets(inbuffer, sizeof(inbuffer), fpin)) break; + lfstrip(inbuffer); // toss the end of line + + // killough 11/98: really bail out on blank lines (break != continue) + if (!*inbuffer) break; // bail out with blank line between sections + + // e6y: Correction of wrong processing of Bits parameter if its value is equal to zero + // No more desync on HACX demos. + bGetData = deh_GetData(inbuffer,key,&value,&strval,fpout); + if (!bGetData) + // Old code: if (!deh_GetData(inbuffer,key,&value,&strval,fpout)) // returns TRUE if ok + { + if (fpout) fprintf(fpout,"Bad data pair in '%s'\n",inbuffer); + continue; + } + for (ix=0; ix>32) & 0xffffffff, + (unsigned long)deh_mobjflags[iy].value & 0xffffffff, strval + ); + } + value |= deh_mobjflags[iy].value; + break; + } + if (iy >= DEH_MOBJFLAGMAX && fpout) { + fprintf(fpout, "Could not find bit mnemonic %s\n", strval); + } + } + + // Don't worry about conversion -- simply print values + if (fpout) { + fprintf(fpout, + "Bits = 0x%08lX%08lX\n", + (unsigned long)(value>>32) & 0xffffffff, + (unsigned long)value & 0xffffffff + ); + } + mobjinfo[indexnum].flags = value; // e6y + } + } + if (fpout) { + fprintf(fpout, + "Assigned 0x%08lx%08lx to %s(%d) at index %d\n", + (unsigned long)(value>>32) & 0xffffffff, + (unsigned long)value & 0xffffffff, key, indexnum, ix + ); + } + } + } + return; +} + +// ==================================================================== +// deh_procFrame +// Purpose: Handle DEH Frame block +// Args: fpin -- input file stream +// fpout -- output file stream (DEHOUT.TXT) +// line -- current line in file to process +// Returns: void +// +static void deh_procFrame(DEHFILE *fpin, FILE* fpout, char *line) +{ + char key[DEH_MAXKEYLEN]; + char inbuffer[DEH_BUFFERMAX]; + uint_64_t value; // All deh values are ints or longs + int indexnum; + + strncpy(inbuffer,line,DEH_BUFFERMAX); + + // killough 8/98: allow hex numbers in input: + sscanf(inbuffer,"%s %i",key, &indexnum); + if (fpout) fprintf(fpout,"Processing Frame at index %d: %s\n",indexnum,key); + if (indexnum < 0 || indexnum >= NUMSTATES) + if (fpout) fprintf(fpout,"Bad frame number %d of %d\n",indexnum, NUMSTATES); + + while (!dehfeof(fpin) && *inbuffer && (*inbuffer != ' ')) + { + if (!dehfgets(inbuffer, sizeof(inbuffer), fpin)) break; + lfstrip(inbuffer); + if (!*inbuffer) break; // killough 11/98 + if (!deh_GetData(inbuffer,key,&value,NULL,fpout)) // returns TRUE if ok + { + if (fpout) fprintf(fpout,"Bad data pair in '%s'\n",inbuffer); + continue; + } + if (!strcasecmp(key,deh_state[0])) // Sprite number + { + if (fpout) fprintf(fpout," - sprite = %lld\n",value); + states[indexnum].sprite = (spritenum_t)value; + } + else + if (!strcasecmp(key,deh_state[1])) // Sprite subnumber + { + if (fpout) fprintf(fpout," - frame = %lld\n",value); + states[indexnum].frame = (long)value; // long + } + else + if (!strcasecmp(key,deh_state[2])) // Duration + { + if (fpout) fprintf(fpout," - tics = %lld\n",value); + states[indexnum].tics = (long)value; // long + } + else + if (!strcasecmp(key,deh_state[3])) // Next frame + { + if (fpout) fprintf(fpout," - nextstate = %lld\n",value); + states[indexnum].nextstate = (statenum_t)value; + } + else + if (!strcasecmp(key,deh_state[4])) // Codep frame (not set in Frame deh block) + { + if (fpout) fprintf(fpout," - codep, should not be set in Frame section!\n"); + /* nop */ ; + } + else + if (!strcasecmp(key,deh_state[5])) // Unknown 1 + { + if (fpout) fprintf(fpout," - misc1 = %lld\n",value); + states[indexnum].misc1 = (long)value; // long + } + else + if (!strcasecmp(key,deh_state[6])) // Unknown 2 + { + if (fpout) fprintf(fpout," - misc2 = %lld\n",value); + states[indexnum].misc2 = (long)value; // long + } + else + if (fpout) fprintf(fpout,"Invalid frame string index for '%s'\n",key); + } + return; +} + +// ==================================================================== +// deh_procPointer +// Purpose: Handle DEH Code pointer block, can use BEX [CODEPTR] instead +// Args: fpin -- input file stream +// fpout -- output file stream (DEHOUT.TXT) +// line -- current line in file to process +// Returns: void +// +static void deh_procPointer(DEHFILE *fpin, FILE* fpout, char *line) // done +{ + char key[DEH_MAXKEYLEN]; + char inbuffer[DEH_BUFFERMAX]; + uint_64_t value; // All deh values are ints or longs + int indexnum; + int i; // looper + + strncpy(inbuffer,line,DEH_BUFFERMAX); + // NOTE: different format from normal + + // killough 8/98: allow hex numbers in input, fix error case: + if (sscanf(inbuffer,"%*s %*i (%s %i)",key, &indexnum) != 2) + { + if (fpout) fprintf(fpout,"Bad data pair in '%s'\n",inbuffer); + return; + } + + if (fpout) fprintf(fpout,"Processing Pointer at index %d: %s\n",indexnum, key); + if (indexnum < 0 || indexnum >= NUMSTATES) + { + if (fpout) + fprintf(fpout,"Bad pointer number %d of %d\n",indexnum, NUMSTATES); + return; + } + + while (!dehfeof(fpin) && *inbuffer && (*inbuffer != ' ')) + { + if (!dehfgets(inbuffer, sizeof(inbuffer), fpin)) break; + lfstrip(inbuffer); + if (!*inbuffer) break; // killough 11/98 + if (!deh_GetData(inbuffer,key,&value,NULL,fpout)) // returns TRUE if ok + { + if (fpout) fprintf(fpout,"Bad data pair in '%s'\n",inbuffer); + continue; + } + + if (value < 0 || value >= NUMSTATES) + { + if (fpout) + fprintf(fpout,"Bad pointer number %lld of %d\n",value, NUMSTATES); + return; + } + + if (!strcasecmp(key,deh_state[4])) // Codep frame (not set in Frame deh block) + { + states[indexnum].action = deh_codeptr[value]; + if (fpout) fprintf(fpout," - applied from codeptr[%lld] to states[%d]\n", + value,indexnum); + // Write BEX-oriented line to match: + // for (i=0;i FRAME %d = %s\n", + indexnum, &deh_bexptrs[i].lookup[2]); + break; + } + if (deh_bexptrs[i].cptr == NULL) // stop at null entry + break; + } + } + else + if (fpout) fprintf(fpout,"Invalid frame pointer index for '%s' at %lld\n", + key, value); + } + return; +} + +// ==================================================================== +// deh_procSounds +// Purpose: Handle DEH Sounds block +// Args: fpin -- input file stream +// fpout -- output file stream (DEHOUT.TXT) +// line -- current line in file to process +// Returns: void +// +static void deh_procSounds(DEHFILE *fpin, FILE* fpout, char *line) +{ + char key[DEH_MAXKEYLEN]; + char inbuffer[DEH_BUFFERMAX]; + uint_64_t value; // All deh values are ints or longs + int indexnum; + + strncpy(inbuffer,line,DEH_BUFFERMAX); + + // killough 8/98: allow hex numbers in input: + sscanf(inbuffer,"%s %i",key, &indexnum); + if (fpout) fprintf(fpout,"Processing Sounds at index %d: %s\n", + indexnum, key); + if (indexnum < 0 || indexnum >= NUMSFX) + if (fpout) fprintf(fpout,"Bad sound number %d of %d\n", + indexnum, NUMSFX); + + while (!dehfeof(fpin) && *inbuffer && (*inbuffer != ' ')) + { + if (!dehfgets(inbuffer, sizeof(inbuffer), fpin)) break; + lfstrip(inbuffer); + if (!*inbuffer) break; // killough 11/98 + if (!deh_GetData(inbuffer,key,&value,NULL,fpout)) // returns TRUE if ok + { + if (fpout) fprintf(fpout,"Bad data pair in '%s'\n",inbuffer); + continue; + } + if (!strcasecmp(key,deh_sfxinfo[0])) // Offset + /* nop */ ; // we don't know what this is, I don't think + else + if (!strcasecmp(key,deh_sfxinfo[1])) // Zero/One + S_sfx[indexnum].singularity = (int)value; + else + if (!strcasecmp(key,deh_sfxinfo[2])) // Value + S_sfx[indexnum].priority = (int)value; + else + if (!strcasecmp(key,deh_sfxinfo[3])) // Zero 1 + S_sfx[indexnum].link = (sfxinfo_t *)value; + else + if (!strcasecmp(key,deh_sfxinfo[4])) // Zero 2 + S_sfx[indexnum].pitch = (int)value; + else + if (!strcasecmp(key,deh_sfxinfo[5])) // Zero 3 + S_sfx[indexnum].volume = (int)value; + else + if (!strcasecmp(key,deh_sfxinfo[6])) // Zero 4 + S_sfx[indexnum].data = (void *) value; // killough 5/3/98: changed cast + else + if (!strcasecmp(key,deh_sfxinfo[7])) // Neg. One 1 + S_sfx[indexnum].usefulness = (int)value; + else + if (!strcasecmp(key,deh_sfxinfo[8])) // Neg. One 2 + S_sfx[indexnum].lumpnum = (int)value; + else + if (fpout) fprintf(fpout, + "Invalid sound string index for '%s'\n",key); + } + return; +} + +// ==================================================================== +// deh_procAmmo +// Purpose: Handle DEH Ammo block +// Args: fpin -- input file stream +// fpout -- output file stream (DEHOUT.TXT) +// line -- current line in file to process +// Returns: void +// +static void deh_procAmmo(DEHFILE *fpin, FILE* fpout, char *line) +{ + char key[DEH_MAXKEYLEN]; + char inbuffer[DEH_BUFFERMAX]; + uint_64_t value; // All deh values are ints or longs + int indexnum; + + strncpy(inbuffer,line,DEH_BUFFERMAX); + + // killough 8/98: allow hex numbers in input: + sscanf(inbuffer,"%s %i",key, &indexnum); + if (fpout) fprintf(fpout,"Processing Ammo at index %d: %s\n", + indexnum, key); + if (indexnum < 0 || indexnum >= NUMAMMO) + if (fpout) fprintf(fpout,"Bad ammo number %d of %d\n", + indexnum,NUMAMMO); + + while (!dehfeof(fpin) && *inbuffer && (*inbuffer != ' ')) + { + if (!dehfgets(inbuffer, sizeof(inbuffer), fpin)) break; + lfstrip(inbuffer); + if (!*inbuffer) break; // killough 11/98 + if (!deh_GetData(inbuffer,key,&value,NULL,fpout)) // returns TRUE if ok + { + if (fpout) fprintf(fpout,"Bad data pair in '%s'\n",inbuffer); + continue; + } + if (!strcasecmp(key,deh_ammo[0])) // Max ammo + maxammo[indexnum] = (int)value; + else + if (!strcasecmp(key,deh_ammo[1])) // Per ammo + clipammo[indexnum] = (int)value; + else + if (fpout) fprintf(fpout,"Invalid ammo string index for '%s'\n",key); + } + return; +} + +// ==================================================================== +// deh_procWeapon +// Purpose: Handle DEH Weapon block +// Args: fpin -- input file stream +// fpout -- output file stream (DEHOUT.TXT) +// line -- current line in file to process +// Returns: void +// +static void deh_procWeapon(DEHFILE *fpin, FILE* fpout, char *line) +{ + char key[DEH_MAXKEYLEN]; + char inbuffer[DEH_BUFFERMAX]; + uint_64_t value; // All deh values are ints or longs + int indexnum; + + strncpy(inbuffer,line,DEH_BUFFERMAX); + + // killough 8/98: allow hex numbers in input: + sscanf(inbuffer,"%s %i",key, &indexnum); + if (fpout) fprintf(fpout,"Processing Weapon at index %d: %s\n", + indexnum, key); + if (indexnum < 0 || indexnum >= NUMWEAPONS) + if (fpout) fprintf(fpout,"Bad weapon number %d of %d\n", + indexnum, NUMAMMO); + + while (!dehfeof(fpin) && *inbuffer && (*inbuffer != ' ')) + { + if (!dehfgets(inbuffer, sizeof(inbuffer), fpin)) break; + lfstrip(inbuffer); + if (!*inbuffer) break; // killough 11/98 + if (!deh_GetData(inbuffer,key,&value,NULL,fpout)) // returns TRUE if ok + { + if (fpout) fprintf(fpout,"Bad data pair in '%s'\n",inbuffer); + continue; + } + if (!strcasecmp(key,deh_weapon[0])) // Ammo type + weaponinfo[indexnum].ammo = (ammotype_t)value; + else + if (!strcasecmp(key,deh_weapon[1])) // Deselect frame + weaponinfo[indexnum].upstate = (int)value; + else + if (!strcasecmp(key,deh_weapon[2])) // Select frame + weaponinfo[indexnum].downstate = (int)value; + else + if (!strcasecmp(key,deh_weapon[3])) // Bobbing frame + weaponinfo[indexnum].readystate = (int)value; + else + if (!strcasecmp(key,deh_weapon[4])) // Shooting frame + weaponinfo[indexnum].atkstate = (int)value; + else + if (!strcasecmp(key,deh_weapon[5])) // Firing frame + weaponinfo[indexnum].flashstate = (int)value; + else + if (fpout) fprintf(fpout,"Invalid weapon string index for '%s'\n",key); + } + return; +} + +// ==================================================================== +// deh_procSprite +// Purpose: Dummy - we do not support the DEH Sprite block +// Args: fpin -- input file stream +// fpout -- output file stream (DEHOUT.TXT) +// line -- current line in file to process +// Returns: void +// +static void deh_procSprite(DEHFILE *fpin, FILE* fpout, char *line) // Not supported +{ + char key[DEH_MAXKEYLEN]; + char inbuffer[DEH_BUFFERMAX]; + int indexnum; + + // Too little is known about what this is supposed to do, and + // there are better ways of handling sprite renaming. Not supported. + + strncpy(inbuffer,line,DEH_BUFFERMAX); + + // killough 8/98: allow hex numbers in input: + sscanf(inbuffer,"%s %i",key, &indexnum); + if (fpout) fprintf(fpout, + "Ignoring Sprite offset change at index %d: %s\n",indexnum, key); + while (!dehfeof(fpin) && *inbuffer && (*inbuffer != ' ')) + { + if (!dehfgets(inbuffer, sizeof(inbuffer), fpin)) break; + lfstrip(inbuffer); + if (!*inbuffer) break; // killough 11/98 + // ignore line + if (fpout) fprintf(fpout,"- %s\n",inbuffer); + } + return; +} + +// ==================================================================== +// deh_procPars +// Purpose: Handle BEX extension for PAR times +// Args: fpin -- input file stream +// fpout -- output file stream (DEHOUT.TXT) +// line -- current line in file to process +// Returns: void +// +static void deh_procPars(DEHFILE *fpin, FILE* fpout, char *line) // extension +{ + char key[DEH_MAXKEYLEN]; + char inbuffer[DEH_BUFFERMAX]; + int indexnum; + int episode, level, partime, oldpar; + + // new item, par times + // usage: After [PARS] Par 0 section identifier, use one or more of these + // lines: + // par 3 5 120 + // par 14 230 + // The first would make the par for E3M5 be 120 seconds, and the + // second one makes the par for MAP14 be 230 seconds. The number + // of parameters on the line determines which group of par values + // is being changed. Error checking is done based on current fixed + // array sizes of[4][10] and [32] + + strncpy(inbuffer,line,DEH_BUFFERMAX); + + // killough 8/98: allow hex numbers in input: + sscanf(inbuffer,"%s %i",key, &indexnum); + if (fpout) fprintf(fpout, + "Processing Par value at index %d: %s\n",indexnum, key); + // indexnum is a dummy entry + while (!dehfeof(fpin) && *inbuffer && (*inbuffer != ' ')) + { + if (!dehfgets(inbuffer, sizeof(inbuffer), fpin)) break; + lfstrip(strlwr(inbuffer)); // lowercase it + if (!*inbuffer) break; // killough 11/98 + if (3 != sscanf(inbuffer,"par %i %i %i",&episode, &level, &partime)) + { // not 3 + if (2 != sscanf(inbuffer,"par %i %i",&level, &partime)) + { // not 2 + if (fpout) fprintf(fpout,"Invalid par time setting string: %s\n",inbuffer); + } + else + { // is 2 + // Ty 07/11/98 - wrong range check, not zero-based + if (level < 1 || level > 32) // base 0 array (but 1-based parm) + { + if (fpout) fprintf(fpout,"Invalid MAPnn value MAP%d\n",level); + } + else + { + oldpar = cpars[level-1]; + if (fpout) fprintf(fpout,"Changed par time for MAP%02d from %d to %d\n",level,oldpar,partime); + cpars[level-1] = partime; + deh_pars = TRUE; + } + } + } + else + { // is 3 + // note that though it's a [4][10] array, the "left" and "top" aren't used, + // effectively making it a base 1 array. + // Ty 07/11/98 - level was being checked against max 3 - dumb error + // Note that episode 4 does not have par times per original design + // in Ultimate DOOM so that is not supported here. + if (episode < 1 || episode > 3 || level < 1 || level > 9) + { + if (fpout) fprintf(fpout, + "Invalid ExMx values E%dM%d\n",episode, level); + } + else + { + oldpar = pars[episode][level]; + pars[episode][level] = partime; + if (fpout) fprintf(fpout, + "Changed par time for E%dM%d from %d to %d\n", + episode,level,oldpar,partime); + deh_pars = TRUE; + } + } + } + return; +} + +// ==================================================================== +// deh_procCheat +// Purpose: Handle DEH Cheat block +// Args: fpin -- input file stream +// fpout -- output file stream (DEHOUT.TXT) +// line -- current line in file to process +// Returns: void +// +static void deh_procCheat(DEHFILE *fpin, FILE* fpout, char *line) // done +{ + char key[DEH_MAXKEYLEN]; + char inbuffer[DEH_BUFFERMAX]; + uint_64_t value; // All deh values are ints or longs + char ch = 0; // CPhipps - `writable' null string to initialise... + char *strval = &ch; // pointer to the value area + int ix, iy; // array indices + char *p; // utility pointer + + if (fpout) fprintf(fpout,"Processing Cheat: %s\n",line); + + strncpy(inbuffer,line,DEH_BUFFERMAX); + while (!dehfeof(fpin) && *inbuffer && (*inbuffer != ' ')) + { + if (!dehfgets(inbuffer, sizeof(inbuffer), fpin)) break; + lfstrip(inbuffer); + if (!*inbuffer) break; // killough 11/98 + if (!deh_GetData(inbuffer,key,&value,&strval,fpout)) // returns TRUE if ok + { + if (fpout) fprintf(fpout,"Bad data pair in '%s'\n",inbuffer); + continue; + } + // Otherwise we got a (perhaps valid) cheat name, + // so look up the key in the array + + // killough 4/18/98: use main cheat code table in st_stuff.c now + for (ix=0; cheat[ix].cheat; ix++) + if (cheat[ix].deh_cheat) // killough 4/18/98: skip non-deh + { + if (!stricmp(key,cheat[ix].deh_cheat)) // found the cheat, ignored case + { + // replace it but don't overflow it. Use current length as limit. + // Ty 03/13/98 - add 0xff code + // Deal with the fact that the cheats in deh files are extended + // with character 0xFF to the original cheat length, which we don't do. + for (iy=0; strval[iy]; iy++) + strval[iy] = (strval[iy]==(char)0xff) ? '\0' : strval[iy]; + + iy = ix; // killough 4/18/98 + + // Ty 03/14/98 - skip leading spaces + p = strval; + while (*p == ' ') ++p; + // Ty 03/16/98 - change to use a strdup and orphan the original + // Also has the advantage of allowing length changes. + // strncpy(cheat[iy].cheat,p,strlen(cheat[iy].cheat)); +#if 0 + { // killough 9/12/98: disable cheats which are prefixes of this one + int i; + for (i=0; cheat[i].cheat; i++) + if (cheat[i].when & not_deh && + !strncasecmp(cheat[i].cheat, + cheat[iy].cheat, + strlen(cheat[i].cheat)) && i != iy) + cheat[i].deh_modified = true; + } +#endif + cheat[iy].cheat = strdup(p); + if (fpout) fprintf(fpout, + "Assigned new cheat '%s' to cheat '%s'at index %d\n", + p, cheat[ix].deh_cheat, iy); // killough 4/18/98 + } + } + if (fpout) fprintf(fpout,"- %s\n",inbuffer); + } + return; +} + +// ==================================================================== +// deh_procMisc +// Purpose: Handle DEH Misc block +// Args: fpin -- input file stream +// fpout -- output file stream (DEHOUT.TXT) +// line -- current line in file to process +// Returns: void +// +static void deh_procMisc(DEHFILE *fpin, FILE* fpout, char *line) // done +{ + char key[DEH_MAXKEYLEN]; + char inbuffer[DEH_BUFFERMAX]; + uint_64_t value; // All deh values are ints or longs + + strncpy(inbuffer,line,DEH_BUFFERMAX); + while (!dehfeof(fpin) && *inbuffer && (*inbuffer != ' ')) + { + if (!dehfgets(inbuffer, sizeof(inbuffer), fpin)) break; + lfstrip(inbuffer); + if (!*inbuffer) break; // killough 11/98 + if (!deh_GetData(inbuffer,key,&value,NULL,fpout)) // returns TRUE if ok + { + if (fpout) fprintf(fpout,"Bad data pair in '%s'\n",inbuffer); + continue; + } + // Otherwise it's ok + if (fpout) fprintf(fpout,"Processing Misc item '%s'\n", key); + + if (!strcasecmp(key,deh_misc[0])) // Initial Health + initial_health = (int)value; + else + if (!strcasecmp(key,deh_misc[1])) // Initial Bullets + initial_bullets = (int)value; + else + if (!strcasecmp(key,deh_misc[2])) // Max Health + maxhealth = (int)value; + else + if (!strcasecmp(key,deh_misc[3])) // Max Armor + max_armor = (int)value; + else + if (!strcasecmp(key,deh_misc[4])) // Green Armor Class + green_armor_class = (int)value; + else + if (!strcasecmp(key,deh_misc[5])) // Blue Armor Class + blue_armor_class = (int)value; + else + if (!strcasecmp(key,deh_misc[6])) // Max Soulsphere + max_soul = (int)value; + else + if (!strcasecmp(key,deh_misc[7])) // Soulsphere Health + soul_health = (int)value; + else + if (!strcasecmp(key,deh_misc[8])) // Megasphere Health + mega_health = (int)value; + else + if (!strcasecmp(key,deh_misc[9])) // God Mode Health + god_health = (int)value; + else + if (!strcasecmp(key,deh_misc[10])) // IDFA Armor + idfa_armor = (int)value; + else + if (!strcasecmp(key,deh_misc[11])) // IDFA Armor Class + idfa_armor_class = (int)value; + else + if (!strcasecmp(key,deh_misc[12])) // IDKFA Armor + idkfa_armor = (int)value; + else + if (!strcasecmp(key,deh_misc[13])) // IDKFA Armor Class + idkfa_armor_class = (int)value; + else + if (!strcasecmp(key,deh_misc[14])) // BFG Cells/Shot + bfgcells = (int)value; + else + if (!strcasecmp(key,deh_misc[15])) { // Monsters Infight + // e6y: Dehacked support - monsters infight + if (value == 202) monsters_infight = 0; + else if (value == 221) monsters_infight = 1; + else if (fpout) fprintf(fpout, + "Invalid value for 'Monsters Infight': %i", (int)value); + + /* No such switch in DOOM - nop */ //e6y ; + } else + if (fpout) fprintf(fpout, + "Invalid misc item string index for '%s'\n",key); + } + return; +} + +// ==================================================================== +// deh_procText +// Purpose: Handle DEH Text block +// Notes: We look things up in the current information and if found +// we replace it. At the same time we write the new and +// improved BEX syntax to the log file for future use. +// Args: fpin -- input file stream +// fpout -- output file stream (DEHOUT.TXT) +// line -- current line in file to process +// Returns: void +// +static void deh_procText(DEHFILE *fpin, FILE* fpout, char *line) +{ + char key[DEH_MAXKEYLEN]; + char inbuffer[DEH_BUFFERMAX*2]; // can't use line -- double size buffer too. + int i; // loop variable + int fromlen, tolen; // as specified on the text block line + int usedlen; // shorter of fromlen and tolen if not matched + boolean found = FALSE; // to allow early exit once found + char* line2 = NULL; // duplicate line for rerouting + + // Ty 04/11/98 - Included file may have NOTEXT skip flag set + if (includenotext) // flag to skip included deh-style text + { + if (fpout) fprintf(fpout, + "Skipped text block because of notext directive\n"); + strcpy(inbuffer,line); + while (!dehfeof(fpin) && *inbuffer && (*inbuffer != ' ')) + dehfgets(inbuffer, sizeof(inbuffer), fpin); // skip block + // Ty 05/17/98 - don't care if this fails + return; // ************** Early return + } + + // killough 8/98: allow hex numbers in input: + sscanf(line,"%s %i %i",key,&fromlen,&tolen); + if (fpout) fprintf(fpout, + "Processing Text (key=%s, from=%d, to=%d)\n", + key, fromlen, tolen); + + // killough 10/98: fix incorrect usage of feof + { + int c, totlen = 0; + while (totlen < fromlen + tolen && (c = dehfgetc(fpin)) != EOF) + if (c != '\r') + inbuffer[totlen++] = c; + inbuffer[totlen]='\0'; + } + + // if the from and to are 4, this may be a sprite rename. Check it + // against the array and process it as such if it matches. Remember + // that the original names are (and should remain) uppercase. + // Future: this will be from a separate [SPRITES] block. + if (fromlen==4 && tolen==4) + { + i=0; + while (sprnames[i]) // null terminated list in info.c //jff 3/19/98 + { //check pointer + if (!strnicmp(sprnames[i],inbuffer,fromlen)) //not first char + { + if (fpout) fprintf(fpout, + "Changing name of sprite at index %d from %s to %*s\n", + i,sprnames[i],tolen,&inbuffer[fromlen]); + // Ty 03/18/98 - not using strdup because length is fixed + + // killough 10/98: but it's an array of pointers, so we must + // use strdup unless we redeclare sprnames and change all else + { + // CPhipps - fix constness problem + char *s; + sprnames[i] = s = strdup(sprnames[i]); + + strncpy(s,&inbuffer[fromlen],tolen); + } + found = TRUE; + break; // only one will match--quit early + } + ++i; // next array element + } + } + else + if (fromlen < 7 && tolen < 7) // lengths of music and sfx are 6 or shorter + { + usedlen = (fromlen < tolen) ? fromlen : tolen; + if (fromlen != tolen) + if (fpout) fprintf(fpout, + "Warning: Mismatched lengths from=%d, to=%d, used %d\n", + fromlen, tolen, usedlen); + // Try sound effects entries - see sounds.c + for (i=1; i 12) ? "..." : "",fromlen,tolen); + if ((size_t)fromlen <= strlen(inbuffer)) + { + line2 = strdup(&inbuffer[fromlen]); + inbuffer[fromlen] = '\0'; + } + + deh_procStringSub(NULL, inbuffer, line2, fpout); + } + free(line2); // may be NULL, ignored by free() + return; +} + +static void deh_procError(DEHFILE *fpin, FILE* fpout, char *line) +{ + char inbuffer[DEH_BUFFERMAX]; + + strncpy(inbuffer,line,DEH_BUFFERMAX); + if (fpout) fprintf(fpout,"Unmatched Block: '%s'\n",inbuffer); + return; +} + +// ==================================================================== +// deh_procStrings +// Purpose: Handle BEX [STRINGS] extension +// Args: fpin -- input file stream +// fpout -- output file stream (DEHOUT.TXT) +// line -- current line in file to process +// Returns: void +// +static void deh_procStrings(DEHFILE *fpin, FILE* fpout, char *line) +{ + char key[DEH_MAXKEYLEN]; + char inbuffer[DEH_BUFFERMAX]; + uint_64_t value; // All deh values are ints or longs + char *strval; // holds the string value of the line + static int maxstrlen = 128; // maximum string length, bumped 128 at + // a time as needed + // holds the final result of the string after concatenation + static char *holdstring = NULL; + boolean found = false; // looking for string continuation + + if (fpout) fprintf(fpout,"Processing extended string substitution\n"); + + if (!holdstring) holdstring = malloc(maxstrlen*sizeof(*holdstring)); + + *holdstring = '\0'; // empty string to start with + strncpy(inbuffer,line,DEH_BUFFERMAX); + // Ty 04/24/98 - have to allow inbuffer to start with a blank for + // the continuations of C1TEXT etc. + while (!dehfeof(fpin) && *inbuffer) /* && (*inbuffer != ' ') */ + { + if (!dehfgets(inbuffer, sizeof(inbuffer), fpin)) break; + if (*inbuffer == '#') continue; // skip comment lines + lfstrip(inbuffer); + if (!*inbuffer) break; // killough 11/98 + if (!*holdstring) // first one--get the key + { + if (!deh_GetData(inbuffer,key,&value,&strval,fpout)) // returns TRUE if ok + { + if (fpout) fprintf(fpout,"Bad data pair in '%s'\n",inbuffer); + continue; + } + } + while (strlen(holdstring) + strlen(inbuffer) > (size_t)maxstrlen) // Ty03/29/98 - fix stupid error + { + // killough 11/98: allocate enough the first time + maxstrlen += strlen(holdstring) + strlen(inbuffer) - maxstrlen; + if (fpout) fprintf(fpout, + "* increased buffer from to %d for buffer size %d\n", + maxstrlen,(int)strlen(inbuffer)); + holdstring = realloc(holdstring,maxstrlen*sizeof(*holdstring)); + } + // concatenate the whole buffer if continuation or the value iffirst + strcat(holdstring,ptr_lstrip(((*holdstring) ? inbuffer : strval))); + rstrip(holdstring); + // delete any trailing blanks past the backslash + // note that blanks before the backslash will be concatenated + // but ones at the beginning of the next line will not, allowing + // indentation in the file to read well without affecting the + // string itself. + if (holdstring[strlen(holdstring)-1] == '\\') + { + holdstring[strlen(holdstring)-1] = '\0'; + continue; // ready to concatenate + } + if (*holdstring) // didn't have a backslash, trap above would catch that + { + // go process the current string + found = deh_procStringSub(key, NULL, holdstring, fpout); // supply keyand not search string + + if (!found) + if (fpout) fprintf(fpout, + "Invalid string key '%s', substitution skipped.\n",key); + + *holdstring = '\0'; // empty string for the next one + } + } + return; +} + +// ==================================================================== +// deh_procStringSub +// Purpose: Common string parsing and handling routine for DEH and BEX +// Args: key -- place to put the mnemonic for the string if found +// lookfor -- original value string to look for +// newstring -- string to put in its place if found +// fpout -- file stream pointer for log file (DEHOUT.TXT) +// Returns: boolean: True if string found, false if not +// +boolean deh_procStringSub(char *key, char *lookfor, char *newstring, FILE *fpout) +{ + boolean found; // loop exit flag + int i; // looper + + found = false; + for (i=0;i '%s'\n",key,newstring); + + if (!key) + if (fpout) fprintf(fpout, + "Assigned '%.12s%s' to'%.12s%s' at key %s\n", + lookfor, (strlen(lookfor) > 12) ? "..." : "", + newstring, (strlen(newstring) > 12) ? "..." :"", + deh_strlookup[i].lookup); + + if (!key) // must have passed an old style string so showBEX + if (fpout) fprintf(fpout, + "*BEX FORMAT:\n%s = %s\n*END BEX\n", + deh_strlookup[i].lookup, + dehReformatStr(newstring)); + + break; + } + } + if (!found) + if (fpout) fprintf(fpout, + "Could not find '%.12s'\n",key ? key: lookfor); + + return found; +} + +//======================================================================== +// haleyjd 9/22/99 +// +// deh_procHelperThing +// +// Allows handy substitution of any thing for helper dogs. DEH patches +// are being made frequently for this purpose and it requires a complete +// rewiring of the DOG thing. I feel this is a waste of effort, and so +// have added this new [HELPER] BEX block + +static void deh_procHelperThing(DEHFILE *fpin, FILE *fpout, char *line) +{ + char key[DEH_MAXKEYLEN]; + char inbuffer[DEH_BUFFERMAX]; + uint_64_t value; // All deh values are ints or longs + + strncpy(inbuffer,line,DEH_BUFFERMAX); + while (!dehfeof(fpin) && *inbuffer && (*inbuffer != ' ')) + { + if (!dehfgets(inbuffer, sizeof(inbuffer), fpin)) break; + lfstrip(inbuffer); + if (!*inbuffer) break; + if (!deh_GetData(inbuffer,key,&value,NULL,fpout)) // returns TRUE if ok + { + if (fpout) fprintf(fpout,"Bad data pair in '%s'\n",inbuffer); + continue; + } + // Otherwise it's ok + if (fpout) + { + fprintf(fpout,"Processing Helper Thing item '%s'\n", key); + fprintf(fpout,"value is %i", (int)value); + } + if (!strncasecmp(key, "type", 4)) + HelperThing = (int)value; + } + return; +} + +// +// deh_procBexSprites +// +// Supports sprite name substitutions without requiring use +// of the DeHackEd Text block +// +static void deh_procBexSprites(DEHFILE *fpin, FILE *fpout, char *line) +{ + char key[DEH_MAXKEYLEN]; + char inbuffer[DEH_BUFFERMAX]; + uint_64_t value; // All deh values are ints or longs + char *strval; // holds the string value of the line + char candidate[5]; + int rover; + + if(fpout) + fprintf(fpout,"Processing sprite name substitution\n"); + + strncpy(inbuffer,line,DEH_BUFFERMAX); + + while(!dehfeof(fpin) && *inbuffer && (*inbuffer != ' ')) + { + if(!dehfgets(inbuffer, sizeof(inbuffer), fpin)) + break; + if(*inbuffer == '#') + continue; // skip comment lines + lfstrip(inbuffer); + if(!*inbuffer) + break; // killough 11/98 + if(!deh_GetData(inbuffer,key,&value,&strval,fpout)) // returns TRUE if ok + { + if(fpout) + fprintf(fpout,"Bad data pair in '%s'\n",inbuffer); + continue; + } + // do it + memset(candidate, 0, sizeof(candidate)); + strncpy(candidate, ptr_lstrip(strval), 4); + if(strlen(candidate) != 4) + { + if(fpout) + fprintf(fpout, "Bad length for sprite name '%s'\n", + candidate); + continue; + } + + rover = 0; + while(deh_spritenames[rover]) + { + if(!strncasecmp(deh_spritenames[rover], key, 4)) + { + if(fpout) + fprintf(fpout, "Substituting '%s' for sprite '%s'\n", + candidate, deh_spritenames[rover]); + + sprnames[rover] = strdup(candidate); + break; + } + rover++; + } + } +} + +// ditto for sound names +static void deh_procBexSounds(DEHFILE *fpin, FILE *fpout, char *line) +{ + char key[DEH_MAXKEYLEN]; + char inbuffer[DEH_BUFFERMAX]; + uint_64_t value; // All deh values are ints or longs + char *strval; // holds the string value of the line + char candidate[7]; + int rover, len; + + if(fpout) + fprintf(fpout,"Processing sound name substitution\n"); + + strncpy(inbuffer,line,DEH_BUFFERMAX); + + while(!dehfeof(fpin) && *inbuffer && (*inbuffer != ' ')) + { + if(!dehfgets(inbuffer, sizeof(inbuffer), fpin)) + break; + if(*inbuffer == '#') + continue; // skip comment lines + lfstrip(inbuffer); + if(!*inbuffer) + break; // killough 11/98 + if(!deh_GetData(inbuffer,key,&value,&strval,fpout)) // returns TRUE if ok + { + if(fpout) + fprintf(fpout,"Bad data pair in '%s'\n",inbuffer); + continue; + } + // do it + memset(candidate, 0, 7); + strncpy(candidate, ptr_lstrip(strval), 6); + len = strlen(candidate); + if(len < 1 || len > 6) + { + if(fpout) + fprintf(fpout, "Bad length for sound name '%s'\n", + candidate); + continue; + } + + rover = 1; + while(deh_soundnames[rover]) + { + if(!strncasecmp(deh_soundnames[rover], key, 6)) + { + if(fpout) + fprintf(fpout, "Substituting '%s' for sound '%s'\n", + candidate, deh_soundnames[rover]); + + S_sfx[rover].name = strdup(candidate); + break; + } + rover++; + } + } +} + +// ditto for music names +static void deh_procBexMusic(DEHFILE *fpin, FILE *fpout, char *line) +{ + char key[DEH_MAXKEYLEN]; + char inbuffer[DEH_BUFFERMAX]; + uint_64_t value; // All deh values are ints or longs + char *strval; // holds the string value of the line + char candidate[7]; + int rover, len; + + if(fpout) + fprintf(fpout,"Processing music name substitution\n"); + + strncpy(inbuffer,line,DEH_BUFFERMAX); + + while(!dehfeof(fpin) && *inbuffer && (*inbuffer != ' ')) + { + if(!dehfgets(inbuffer, sizeof(inbuffer), fpin)) + break; + if(*inbuffer == '#') + continue; // skip comment lines + lfstrip(inbuffer); + if(!*inbuffer) + break; // killough 11/98 + if(!deh_GetData(inbuffer,key,&value,&strval,fpout)) // returns TRUE if ok + { + if(fpout) + fprintf(fpout,"Bad data pair in '%s'\n",inbuffer); + continue; + } + // do it + memset(candidate, 0, 7); + strncpy(candidate, ptr_lstrip(strval), 6); + len = strlen(candidate); + if(len < 1 || len > 6) + { + if(fpout) + fprintf(fpout, "Bad length for music name '%s'\n", + candidate); + continue; + } + + rover = 1; + while(deh_musicnames[rover]) + { + if(!strncasecmp(deh_musicnames[rover], key, 6)) + { + if(fpout) + fprintf(fpout, "Substituting '%s' for music '%s'\n", + candidate, deh_musicnames[rover]); + + S_music[rover].name = strdup(candidate); + break; + } + rover++; + } + } +} + +// ==================================================================== +// General utility function(s) +// ==================================================================== + +// ==================================================================== +// dehReformatStr +// Purpose: Convert a string into a continuous string with embedded +// linefeeds for "\n" sequences in the source string +// Args: string -- the string to convert +// Returns: the converted string (converted in a static buffer) +// +char *dehReformatStr(char *string) +{ + static char buff[DEH_BUFFERMAX]; // only processing the changed string, + // don't need double buffer + char *s, *t; + + s = string; // source + t = buff; // target + // let's play... + + while (*s) + { + if (*s == '\n') + ++s, *t++ = '\\', *t++ = 'n', *t++ = '\\', *t++='\n'; + else + *t++ = *s++; + } + *t = '\0'; + return buff; +} + +// ==================================================================== +// lfstrip +// Purpose: Strips CR/LF off the end of a string +// Args: s -- the string to work on +// Returns: void -- the string is modified in place +// +// killough 10/98: only strip at end of line, not entire string + +void lfstrip(char *s) // strip the \r and/or \n off of a line +{ + char *p = s+strlen(s); + while (p > s && (*--p=='\r' || *p=='\n')) + *p = 0; +} + +// ==================================================================== +// rstrip +// Purpose: Strips trailing blanks off a string +// Args: s -- the string to work on +// Returns: void -- the string is modified in place +// +void rstrip(char *s) // strip trailing whitespace +{ + char *p = s+strlen(s); // killough 4/4/98: same here + while (p > s && isspace(*--p)) // break on first non-whitespace + *p='\0'; +} + +// ==================================================================== +// ptr_lstrip +// Purpose: Points past leading whitespace in a string +// Args: s -- the string to work on +// Returns: char * pointing to the first nonblank character in the +// string. The original string is not changed. +// +char *ptr_lstrip(char *p) // point past leading whitespace +{ + while (isspace(*p)) + p++; + return p; +} + +// e6y: Correction of wrong processing of Bits parameter if its value is equal to zero +// No more desync on HACX demos. +// FIXME!!! (lame) +static boolean StrToInt(char *s, long *l) +{ + return ( + (sscanf(s, " 0x%lx", l) == 1) || + (sscanf(s, " 0X%lx", l) == 1) || + (sscanf(s, " 0%lo", l) == 1) || + (sscanf(s, " %ld", l) == 1) + ); +} + +// ==================================================================== +// deh_GetData +// Purpose: Get a key and data pair from a passed string +// Args: s -- the string to be examined +// k -- a place to put the key +// l -- pointer to a long integer to store the number +// strval -- a pointer to the place in s where the number +// value comes from. Pass NULL to not use this. +// fpout -- stream pointer to output log (DEHOUT.TXT) +// Notes: Expects a key phrase, optional space, equal sign, +// optional space and a value, mostly an int but treated +// as a long just in case. The passed pointer to hold +// the key must be DEH_MAXKEYLEN in size. + +boolean deh_GetData(char *s, char *k, uint_64_t *l, char **strval, FILE *fpout) +{ + char *t; // current char + long val; // to hold value of pair + char buffer[DEH_MAXKEYLEN]; // to hold key in progress + // e6y: Correction of wrong processing of Bits parameter if its value is equal to zero + // No more desync on HACX demos. + boolean okrc = 1; // assume good unless we have problems + int i; // iterator + + *buffer = '\0'; + val = 0; // defaults in case not otherwise set + for (i=0, t=s; *t && i < DEH_MAXKEYLEN; t++, i++) + { + if (*t == '=') break; + buffer[i] = *t; // copy it + } + buffer[--i] = '\0'; // terminate the key before the '=' + if (!*t) // end of string with no equal sign + { + okrc = FALSE; + } + else + { + if (!*++t) + { + val = 0; // in case "thiskey =" with no value + okrc = FALSE; + } + // we've incremented t + // e6y: Correction of wrong processing of Bits parameter if its value is equal to zero + // No more desync on HACX demos. + // Old code: e6y val = strtol(t,NULL,0); // killough 8/9/98: allow hex or octal input + if (!StrToInt(t,&val)) + { + val = 0; + okrc = 2; + } + } + + // go put the results in the passed pointers + *l = val; // may be a faked zero + + // if spaces between key and equal sign, strip them + strcpy(k,ptr_lstrip(buffer)); // could be a zero-length string + + if (strval != NULL) // pass NULL if you don't want this back + *strval = t; // pointer, has to be somewhere in s, + // even if pointing at the zero byte. + + return(okrc); +} diff --git a/src/d_deh.h b/src/d_deh.h new file mode 100644 index 0000000..02cdffa --- /dev/null +++ b/src/d_deh.h @@ -0,0 +1,1118 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2006 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Dehacked file support + * New for the TeamTNT "Boom" engine + * + * Author: Ty Halderman, TeamTNT + * + * Description: This file translates the #defined string constants + * to named variables to externalize them for deh/bex changes. + * Should be able to compile with D_FRENCH (for example) and still + * work (untested). + * + */ + +#ifndef __D_DEH__ +#define __D_DEH__ + +void ProcessDehFile(const char *filename, const char *outfilename, int lumpnum); + +// +// Ty 03/22/98 - note that we are keeping the english versions and +// comments in this file +// New string names all start with an extra s_ to avoid conflicts, +// but are otherwise identical to the original including uppercase. +// This is partly to keep the changes simple and partly for easier +// identification of the locations in which they're used. +// +// Printed strings for translation +// + +// +// D_Main.C +// +//#define D_DEVSTR "Development mode ON.\n" +extern const char *s_D_DEVSTR; // = D_DEVSTR; +//#define D_CDROM "CD-ROM Version: default.cfg from c:\\doomdata\n" +extern const char *s_D_CDROM; // = D_CDROM; + +// +// M_Menu.C +// +//#define PRESSKEY "press a key." +extern const char *s_PRESSKEY; // = PRESSKEY; +//#define PRESSYN "press y or n." +extern const char *s_PRESSYN; // = PRESSYN; +//#define QUITMSG "are you sure you want to\nquit this great game?" +extern const char *s_QUITMSG; // = QUITMSG; +//#define LOADNET "you can't do load while in a net game!\n\n"PRESSKEY +extern const char *s_LOADNET; // = LOADNET; +//#define QLOADNET "you can't quickload during a netgame!\n\n"PRESSKEY +extern const char *s_QLOADNET; // = QLOADNET; +//#define QSAVESPOT "you haven't picked a quicksave slot yet!\n\n"PRESSKEY +extern const char *s_QSAVESPOT; // = QSAVESPOT; +//#define SAVEDEAD "you can't save if you aren't playing!\n\n"PRESSKEY +extern const char *s_SAVEDEAD; // = SAVEDEAD; +//#define QSPROMPT "quicksave over your game named\n\n'%s'?\n\n"PRESSYN +extern const char *s_QSPROMPT; // = QSPROMPT; +//#define QLPROMPT "do you want to quickload the game named\n\n'%s'?\n\n"PRESSYN +extern const char *s_QLPROMPT; // = QLPROMPT; + +/* +#define NEWGAME \ +"you can't start a new game\n"\ +"while in a network game.\n\n"PRESSKEY +*/ +extern const char *s_NEWGAME; // = NEWGAME; + +// CPhipps - message given when asked if to restart the level +extern const char *s_RESTARTLEVEL; + +/* +#define NIGHTMARE \ +"are you sure? this skill level\n"\ +"isn't even remotely fair.\n\n"PRESSYN +*/ +extern const char *s_NIGHTMARE; // = NIGHTMARE; + +/* +#define SWSTRING \ +"this is the shareware version of doom.\n\n"\ +"you need to order the entire trilogy.\n\n"PRESSKEY +*/ +extern const char *s_SWSTRING; // = SWSTRING; + +//#define MSGOFF "Messages OFF" +extern const char *s_MSGOFF; // = MSGOFF; +//#define MSGON "Messages ON" +extern const char *s_MSGON; // = MSGON; +//#define NETEND "you can't end a netgame!\n\n"PRESSKEY +extern const char *s_NETEND; // = NETEND; +//#define ENDGAME "are you sure you want to end the game?\n\n"PRESSYN +extern const char *s_ENDGAME; // = ENDGAME; + +//#define DOSY "(press y to quit)" +extern const char *s_DOSY; // = DOSY; + +//#define DETAILHI "High detail" +extern const char *s_DETAILHI; // = DETAILHI; +//#define DETAILLO "Low detail" +extern const char *s_DETAILLO; // = DETAILLO; +//#define GAMMALVL0 "Gamma correction OFF" +extern const char *s_GAMMALVL0; // = GAMMALVL0; +//#define GAMMALVL1 "Gamma correction level 1" +extern const char *s_GAMMALVL1; // = GAMMALVL1; +//#define GAMMALVL2 "Gamma correction level 2" +extern const char *s_GAMMALVL2; // = GAMMALVL2; +//#define GAMMALVL3 "Gamma correction level 3" +extern const char *s_GAMMALVL3; // = GAMMALVL3; +//#define GAMMALVL4 "Gamma correction level 4" +extern const char *s_GAMMALVL4; // = GAMMALVL4; +//#define EMPTYSTRING "empty slot" +extern const char *s_EMPTYSTRING; // = EMPTYSTRING; + +// +// P_inter.C +// +//#define GOTARMOR "Picked up the armor." +extern const char *s_GOTARMOR; // = GOTARMOR; +//#define GOTMEGA "Picked up the MegaArmor!" +extern const char *s_GOTMEGA; // = GOTMEGA; +//#define GOTHTHBONUS "Picked up a health bonus." +extern const char *s_GOTHTHBONUS; // = GOTHTHBONUS; +//#define GOTARMBONUS "Picked up an armor bonus." +extern const char *s_GOTARMBONUS; // = GOTARMBONUS; +//#define GOTSTIM "Picked up a stimpack." +extern const char *s_GOTSTIM; // = GOTSTIM; +//#define GOTMEDINEED "Picked up a medikit that you REALLY need!" +extern const char *s_GOTMEDINEED; // = GOTMEDINEED; +//#define GOTMEDIKIT "Picked up a medikit." +extern const char *s_GOTMEDIKIT; // = GOTMEDIKIT; +//#define GOTSUPER "Supercharge!" +extern const char *s_GOTSUPER; // = GOTSUPER; + +//#define GOTBLUECARD "Picked up a blue keycard." +extern const char *s_GOTBLUECARD; // = GOTBLUECARD; +//#define GOTYELWCARD "Picked up a yellow keycard." +extern const char *s_GOTYELWCARD; // = GOTYELWCARD; +//#define GOTREDCARD "Picked up a red keycard." +extern const char *s_GOTREDCARD; // = GOTREDCARD; +//#define GOTBLUESKUL "Picked up a blue skull key." +extern const char *s_GOTBLUESKUL; // = GOTBLUESKUL; +//#define GOTYELWSKUL "Picked up a yellow skull key." +extern const char *s_GOTYELWSKUL; // = GOTYELWSKUL; +//#define GOTREDSKULL "Picked up a red skull key." +extern const char *s_GOTREDSKULL; // = GOTREDSKULL; + +//#define GOTINVUL "Invulnerability!" +extern const char *s_GOTINVUL; // = GOTINVUL; +//#define GOTBERSERK "Berserk!" +extern const char *s_GOTBERSERK; // = GOTBERSERK; +//#define GOTINVIS "Partial Invisibility" +extern const char *s_GOTINVIS; // = GOTINVIS; +//#define GOTSUIT "Radiation Shielding Suit" +extern const char *s_GOTSUIT; // = GOTSUIT; +//#define GOTMAP "Computer Area Map" +extern const char *s_GOTMAP; // = GOTMAP; +//#define GOTVISOR "Light Amplification Visor" +extern const char *s_GOTVISOR; // = GOTVISOR; +//#define GOTMSPHERE "MegaSphere!" +extern const char *s_GOTMSPHERE; // = GOTMSPHERE; + +//#define GOTCLIP "Picked up a clip." +extern const char *s_GOTCLIP; // = GOTCLIP; +//#define GOTCLIPBOX "Picked up a box of bullets." +extern const char *s_GOTCLIPBOX; // = GOTCLIPBOX; +//#define GOTROCKET "Picked up a rocket." +extern const char *s_GOTROCKET; // = GOTROCKET; +//#define GOTROCKBOX "Picked up a box of rockets." +extern const char *s_GOTROCKBOX; // = GOTROCKBOX; +//#define GOTCELL "Picked up an energy cell." +extern const char *s_GOTCELL; // = GOTCELL; +//#define GOTCELLBOX "Picked up an energy cell pack." +extern const char *s_GOTCELLBOX; // = GOTCELLBOX; +//#define GOTSHELLS "Picked up 4 shotgun shells." +extern const char *s_GOTSHELLS; // = GOTSHELLS; +//#define GOTSHELLBOX "Picked up a box of shotgun shells." +extern const char *s_GOTSHELLBOX; // = GOTSHELLBOX; +//#define GOTBACKPACK "Picked up a backpack full of ammo!" +extern const char *s_GOTBACKPACK; // = GOTBACKPACK; + +//#define GOTBFG9000 "You got the BFG9000! Oh, yes." +extern const char *s_GOTBFG9000; // = GOTBFG9000; +//#define GOTCHAINGUN "You got the chaingun!" +extern const char *s_GOTCHAINGUN; // = GOTCHAINGUN; +//#define GOTCHAINSAW "A chainsaw! Find some meat!" +extern const char *s_GOTCHAINSAW; // = GOTCHAINSAW; +//#define GOTLAUNCHER "You got the rocket launcher!" +extern const char *s_GOTLAUNCHER; // = GOTLAUNCHER; +//#define GOTPLASMA "You got the plasma gun!" +extern const char *s_GOTPLASMA; // = GOTPLASMA; +//#define GOTSHOTGUN "You got the shotgun!" +extern const char *s_GOTSHOTGUN; // = GOTSHOTGUN; +//#define GOTSHOTGUN2 "You got the super shotgun!" +extern const char *s_GOTSHOTGUN2; // = GOTSHOTGUN2; + +// +// P_Doors.C +// +//#define PD_BLUEO "You need a blue key to activate this object" +extern const char *s_PD_BLUEO; // = PD_BLUEO; +//#define PD_REDO "You need a red key to activate this object" +extern const char *s_PD_REDO; // = PD_REDO; +//#define PD_YELLOWO "You need a yellow key to activate this object" +extern const char *s_PD_YELLOWO; // = PD_YELLOWO; +//#define PD_BLUEK "You need a blue key to open this door" +extern const char *s_PD_BLUEK; // = PD_BLUEK; +//#define PD_REDK "You need a red key to open this door" +extern const char *s_PD_REDK; // = PD_REDK; +//#define PD_YELLOWK "You need a yellow key to open this door" +extern const char *s_PD_YELLOWK; // = PD_YELLOWK; +//jff 02/05/98 Create messages specific to card and skull keys +//#define PD_BLUEC "You need a blue card to open this door" +extern const char *s_PD_BLUEC; // = PD_BLUEC; +//#define PD_REDC "You need a red card to open this door" +extern const char *s_PD_REDC; // = PD_REDC; +//#define PD_YELLOWC "You need a yellow card to open this door" +extern const char *s_PD_YELLOWC; // = PD_YELLOWC; +//#define PD_BLUES "You need a blue skull to open this door" +extern const char *s_PD_BLUES; // = PD_BLUES; +//#define PD_REDS "You need a red skull to open this door" +extern const char *s_PD_REDS; // = PD_REDS; +//#define PD_YELLOWS "You need a yellow skull to open this door" +extern const char *s_PD_YELLOWS; // = PD_YELLOWS; +//#define PD_ANY "Any key will open this door" +extern const char *s_PD_ANY; // = PD_ANY; +//#define PD_ALL3 "You need all three keys to open this door" +extern const char *s_PD_ALL3; // = PD_ALL3; +//#define PD_ALL6 "You need all six keys to open this door" +extern const char *s_PD_ALL6; // = PD_ALL6; + +// +// G_game.C +// +//#define GGSAVED "game saved." +extern const char *s_GGSAVED; // = GGSAVED; + +// +// HU_stuff.C +// +//#define HUSTR_MSGU "[Message unsent]" +extern const char *s_HUSTR_MSGU; // = HUSTR_MSGU; + +//#define HUSTR_E1M1 "E1M1: Hangar" +extern const char *s_HUSTR_E1M1; // = HUSTR_E1M1; +//#define HUSTR_E1M2 "E1M2: Nuclear Plant" +extern const char *s_HUSTR_E1M2; // = HUSTR_E1M2; +//#define HUSTR_E1M3 "E1M3: Toxin Refinery" +extern const char *s_HUSTR_E1M3; // = HUSTR_E1M3; +//#define HUSTR_E1M4 "E1M4: Command Control" +extern const char *s_HUSTR_E1M4; // = HUSTR_E1M4; +//#define HUSTR_E1M5 "E1M5: Phobos Lab" +extern const char *s_HUSTR_E1M5; // = HUSTR_E1M5; +//#define HUSTR_E1M6 "E1M6: Central Processing" +extern const char *s_HUSTR_E1M6; // = HUSTR_E1M6; +//#define HUSTR_E1M7 "E1M7: Computer Station" +extern const char *s_HUSTR_E1M7; // = HUSTR_E1M7; +//#define HUSTR_E1M8 "E1M8: Phobos Anomaly" +extern const char *s_HUSTR_E1M8; // = HUSTR_E1M8; +//#define HUSTR_E1M9 "E1M9: Military Base" +extern const char *s_HUSTR_E1M9; // = HUSTR_E1M9; + +//#define HUSTR_E2M1 "E2M1: Deimos Anomaly" +extern const char *s_HUSTR_E2M1; // = HUSTR_E2M1; +//#define HUSTR_E2M2 "E2M2: Containment Area" +extern const char *s_HUSTR_E2M2; // = HUSTR_E2M2; +//#define HUSTR_E2M3 "E2M3: Refinery" +extern const char *s_HUSTR_E2M3; // = HUSTR_E2M3; +//#define HUSTR_E2M4 "E2M4: Deimos Lab" +extern const char *s_HUSTR_E2M4; // = HUSTR_E2M4; +//#define HUSTR_E2M5 "E2M5: Command Center" +extern const char *s_HUSTR_E2M5; // = HUSTR_E2M5; +//#define HUSTR_E2M6 "E2M6: Halls of the Damned" +extern const char *s_HUSTR_E2M6; // = HUSTR_E2M6; +//#define HUSTR_E2M7 "E2M7: Spawning Vats" +extern const char *s_HUSTR_E2M7; // = HUSTR_E2M7; +//#define HUSTR_E2M8 "E2M8: Tower of Babel" +extern const char *s_HUSTR_E2M8; // = HUSTR_E2M8; +//#define HUSTR_E2M9 "E2M9: Fortress of Mystery" +extern const char *s_HUSTR_E2M9; // = HUSTR_E2M9; + +//#define HUSTR_E3M1 "E3M1: Hell Keep" +extern const char *s_HUSTR_E3M1; // = HUSTR_E3M1; +//#define HUSTR_E3M2 "E3M2: Slough of Despair" +extern const char *s_HUSTR_E3M2; // = HUSTR_E3M2; +//#define HUSTR_E3M3 "E3M3: Pandemonium" +extern const char *s_HUSTR_E3M3; // = HUSTR_E3M3; +//#define HUSTR_E3M4 "E3M4: House of Pain" +extern const char *s_HUSTR_E3M4; // = HUSTR_E3M4; +//#define HUSTR_E3M5 "E3M5: Unholy Cathedral" +extern const char *s_HUSTR_E3M5; // = HUSTR_E3M5; +//#define HUSTR_E3M6 "E3M6: Mt. Erebus" +extern const char *s_HUSTR_E3M6; // = HUSTR_E3M6; +//#define HUSTR_E3M7 "E3M7: Limbo" +extern const char *s_HUSTR_E3M7; // = HUSTR_E3M7; +//#define HUSTR_E3M8 "E3M8: Dis" +extern const char *s_HUSTR_E3M8; // = HUSTR_E3M8; +//#define HUSTR_E3M9 "E3M9: Warrens" +extern const char *s_HUSTR_E3M9; // = HUSTR_E3M9; + +//#define HUSTR_E4M1 "E4M1: Hell Beneath" +extern const char *s_HUSTR_E4M1; // = HUSTR_E4M1; +//#define HUSTR_E4M2 "E4M2: Perfect Hatred" +extern const char *s_HUSTR_E4M2; // = HUSTR_E4M2; +//#define HUSTR_E4M3 "E4M3: Sever The Wicked" +extern const char *s_HUSTR_E4M3; // = HUSTR_E4M3; +//#define HUSTR_E4M4 "E4M4: Unruly Evil" +extern const char *s_HUSTR_E4M4; // = HUSTR_E4M4; +//#define HUSTR_E4M5 "E4M5: They Will Repent" +extern const char *s_HUSTR_E4M5; // = HUSTR_E4M5; +//#define HUSTR_E4M6 "E4M6: Against Thee Wickedly" +extern const char *s_HUSTR_E4M6; // = HUSTR_E4M6; +//#define HUSTR_E4M7 "E4M7: And Hell Followed" +extern const char *s_HUSTR_E4M7; // = HUSTR_E4M7; +//#define HUSTR_E4M8 "E4M8: Unto The Cruel" +extern const char *s_HUSTR_E4M8; // = HUSTR_E4M8; +//#define HUSTR_E4M9 "E4M9: Fear" +extern const char *s_HUSTR_E4M9; // = HUSTR_E4M9; + +//#define HUSTR_1 "level 1: entryway" +extern const char *s_HUSTR_1; // = HUSTR_1; +//#define HUSTR_2 "level 2: underhalls" +extern const char *s_HUSTR_2; // = HUSTR_2; +//#define HUSTR_3 "level 3: the gantlet" +extern const char *s_HUSTR_3; // = HUSTR_3; +//#define HUSTR_4 "level 4: the focus" +extern const char *s_HUSTR_4; // = HUSTR_4; +//#define HUSTR_5 "level 5: the waste tunnels" +extern const char *s_HUSTR_5; // = HUSTR_5; +//#define HUSTR_6 "level 6: the crusher" +extern const char *s_HUSTR_6; // = HUSTR_6; +//#define HUSTR_7 "level 7: dead simple" +extern const char *s_HUSTR_7; // = HUSTR_7; +//#define HUSTR_8 "level 8: tricks and traps" +extern const char *s_HUSTR_8; // = HUSTR_8; +//#define HUSTR_9 "level 9: the pit" +extern const char *s_HUSTR_9; // = HUSTR_9; +//#define HUSTR_10 "level 10: refueling base" +extern const char *s_HUSTR_10; // = HUSTR_10; +//#define HUSTR_11 "level 11: 'o' of destruction!" +extern const char *s_HUSTR_11; // = HUSTR_11; + +//#define HUSTR_12 "level 12: the factory" +extern const char *s_HUSTR_12; // = HUSTR_12; +//#define HUSTR_13 "level 13: downtown" +extern const char *s_HUSTR_13; // = HUSTR_13; +//#define HUSTR_14 "level 14: the inmost dens" +extern const char *s_HUSTR_14; // = HUSTR_14; +//#define HUSTR_15 "level 15: industrial zone" +extern const char *s_HUSTR_15; // = HUSTR_15; +//#define HUSTR_16 "level 16: suburbs" +extern const char *s_HUSTR_16; // = HUSTR_16; +//#define HUSTR_17 "level 17: tenements" +extern const char *s_HUSTR_17; // = HUSTR_17; +//#define HUSTR_18 "level 18: the courtyard" +extern const char *s_HUSTR_18; // = HUSTR_18; +//#define HUSTR_19 "level 19: the citadel" +extern const char *s_HUSTR_19; // = HUSTR_19; +//#define HUSTR_20 "level 20: gotcha!" +extern const char *s_HUSTR_20; // = HUSTR_20; + +//#define HUSTR_21 "level 21: nirvana" +extern const char *s_HUSTR_21; // = HUSTR_21; +//#define HUSTR_22 "level 22: the catacombs" +extern const char *s_HUSTR_22; // = HUSTR_22; +//#define HUSTR_23 "level 23: barrels o' fun" +extern const char *s_HUSTR_23; // = HUSTR_23; +//#define HUSTR_24 "level 24: the chasm" +extern const char *s_HUSTR_24; // = HUSTR_24; +//#define HUSTR_25 "level 25: bloodfalls" +extern const char *s_HUSTR_25; // = HUSTR_25; +//#define HUSTR_26 "level 26: the abandoned mines" +extern const char *s_HUSTR_26; // = HUSTR_26; +//#define HUSTR_27 "level 27: monster condo" +extern const char *s_HUSTR_27; // = HUSTR_27; +//#define HUSTR_28 "level 28: the spirit world" +extern const char *s_HUSTR_28; // = HUSTR_28; +//#define HUSTR_29 "level 29: the living end" +extern const char *s_HUSTR_29; // = HUSTR_29; +//#define HUSTR_30 "level 30: icon of sin" +extern const char *s_HUSTR_30; // = HUSTR_30; + +//#define HUSTR_31 "level 31: wolfenstein" +extern const char *s_HUSTR_31; // = HUSTR_31; +//#define HUSTR_32 "level 32: grosse" +extern const char *s_HUSTR_32; // = HUSTR_32; + +//#define PHUSTR_1 "level 1: congo" +extern const char *s_PHUSTR_1; // = PHUSTR_1; +//#define PHUSTR_2 "level 2: well of souls" +extern const char *s_PHUSTR_2; // = PHUSTR_2; +//#define PHUSTR_3 "level 3: aztec" +extern const char *s_PHUSTR_3; // = PHUSTR_3; +//#define PHUSTR_4 "level 4: caged" +extern const char *s_PHUSTR_4; // = PHUSTR_4; +//#define PHUSTR_5 "level 5: ghost town" +extern const char *s_PHUSTR_5; // = PHUSTR_5; +//#define PHUSTR_6 "level 6: baron's lair" +extern const char *s_PHUSTR_6; // = PHUSTR_6; +//#define PHUSTR_7 "level 7: caughtyard" +extern const char *s_PHUSTR_7; // = PHUSTR_7; +//#define PHUSTR_8 "level 8: realm" +extern const char *s_PHUSTR_8; // = PHUSTR_8; +//#define PHUSTR_9 "level 9: abattoire" +extern const char *s_PHUSTR_9; // = PHUSTR_9; +//#define PHUSTR_10 "level 10: onslaught" +extern const char *s_PHUSTR_10; // = PHUSTR_10; +//#define PHUSTR_11 "level 11: hunted" +extern const char *s_PHUSTR_11; // = PHUSTR_11; + +//#define PHUSTR_12 "level 12: speed" +extern const char *s_PHUSTR_12; // = PHUSTR_12; +//#define PHUSTR_13 "level 13: the crypt" +extern const char *s_PHUSTR_13; // = PHUSTR_13; +//#define PHUSTR_14 "level 14: genesis" +extern const char *s_PHUSTR_14; // = PHUSTR_14; +//#define PHUSTR_15 "level 15: the twilight" +extern const char *s_PHUSTR_15; // = PHUSTR_15; +//#define PHUSTR_16 "level 16: the omen" +extern const char *s_PHUSTR_16; // = PHUSTR_16; +//#define PHUSTR_17 "level 17: compound" +extern const char *s_PHUSTR_17; // = PHUSTR_17; +//#define PHUSTR_18 "level 18: neurosphere" +extern const char *s_PHUSTR_18; // = PHUSTR_18; +//#define PHUSTR_19 "level 19: nme" +extern const char *s_PHUSTR_19; // = PHUSTR_19; +//#define PHUSTR_20 "level 20: the death domain" +extern const char *s_PHUSTR_20; // = PHUSTR_20; + +//#define PHUSTR_21 "level 21: slayer" +extern const char *s_PHUSTR_21; // = PHUSTR_21; +//#define PHUSTR_22 "level 22: impossible mission" +extern const char *s_PHUSTR_22; // = PHUSTR_22; +//#define PHUSTR_23 "level 23: tombstone" +extern const char *s_PHUSTR_23; // = PHUSTR_23; +//#define PHUSTR_24 "level 24: the final frontier" +extern const char *s_PHUSTR_24; // = PHUSTR_24; +//#define PHUSTR_25 "level 25: the temple of darkness" +extern const char *s_PHUSTR_25; // = PHUSTR_25; +//#define PHUSTR_26 "level 26: bunker" +extern const char *s_PHUSTR_26; // = PHUSTR_26; +//#define PHUSTR_27 "level 27: anti-christ" +extern const char *s_PHUSTR_27; // = PHUSTR_27; +//#define PHUSTR_28 "level 28: the sewers" +extern const char *s_PHUSTR_28; // = PHUSTR_28; +//#define PHUSTR_29 "level 29: odyssey of noises" +extern const char *s_PHUSTR_29; // = PHUSTR_29; +//#define PHUSTR_30 "level 30: the gateway of hell" +extern const char *s_PHUSTR_30; // = PHUSTR_30; + +//#define PHUSTR_31 "level 31: cyberden" +extern const char *s_PHUSTR_31; // = PHUSTR_31; +//#define PHUSTR_32 "level 32: go 2 it" +extern const char *s_PHUSTR_32; // = PHUSTR_32; + +//#define THUSTR_1 "level 1: system control" +extern const char *s_THUSTR_1; // = THUSTR_1; +//#define THUSTR_2 "level 2: human bbq" +extern const char *s_THUSTR_2; // = THUSTR_2; +//#define THUSTR_3 "level 3: power control" +extern const char *s_THUSTR_3; // = THUSTR_3; +//#define THUSTR_4 "level 4: wormhole" +extern const char *s_THUSTR_4; // = THUSTR_4; +//#define THUSTR_5 "level 5: hanger" +extern const char *s_THUSTR_5; // = THUSTR_5; +//#define THUSTR_6 "level 6: open season" +extern const char *s_THUSTR_6; // = THUSTR_6; +//#define THUSTR_7 "level 7: prison" +extern const char *s_THUSTR_7; // = THUSTR_7; +//#define THUSTR_8 "level 8: metal" +extern const char *s_THUSTR_8; // = THUSTR_8; +//#define THUSTR_9 "level 9: stronghold" +extern const char *s_THUSTR_9; // = THUSTR_9; +//#define THUSTR_10 "level 10: redemption" +extern const char *s_THUSTR_10; // = THUSTR_10; +//#define THUSTR_11 "level 11: storage facility" +extern const char *s_THUSTR_11; // = THUSTR_11; + +//#define THUSTR_12 "level 12: crater" +extern const char *s_THUSTR_12; // = THUSTR_12; +//#define THUSTR_13 "level 13: nukage processing" +extern const char *s_THUSTR_13; // = THUSTR_13; +//#define THUSTR_14 "level 14: steel works" +extern const char *s_THUSTR_14; // = THUSTR_14; +//#define THUSTR_15 "level 15: dead zone" +extern const char *s_THUSTR_15; // = THUSTR_15; +//#define THUSTR_16 "level 16: deepest reaches" +extern const char *s_THUSTR_16; // = THUSTR_16; +//#define THUSTR_17 "level 17: processing area" +extern const char *s_THUSTR_17; // = THUSTR_17; +//#define THUSTR_18 "level 18: mill" +extern const char *s_THUSTR_18; // = THUSTR_18; +//#define THUSTR_19 "level 19: shipping/respawning" +extern const char *s_THUSTR_19; // = THUSTR_19; +//#define THUSTR_20 "level 20: central processing" +extern const char *s_THUSTR_20; // = THUSTR_20; + +//#define THUSTR_21 "level 21: administration center" +extern const char *s_THUSTR_21; // = THUSTR_21; +//#define THUSTR_22 "level 22: habitat" +extern const char *s_THUSTR_22; // = THUSTR_22; +//#define THUSTR_23 "level 23: lunar mining project" +extern const char *s_THUSTR_23; // = THUSTR_23; +//#define THUSTR_24 "level 24: quarry" +extern const char *s_THUSTR_24; // = THUSTR_24; +//#define THUSTR_25 "level 25: baron's den" +extern const char *s_THUSTR_25; // = THUSTR_25; +//#define THUSTR_26 "level 26: ballistyx" +extern const char *s_THUSTR_26; // = THUSTR_26; +//#define THUSTR_27 "level 27: mount pain" +extern const char *s_THUSTR_27; // = THUSTR_27; +//#define THUSTR_28 "level 28: heck" +extern const char *s_THUSTR_28; // = THUSTR_28; +//#define THUSTR_29 "level 29: river styx" +extern const char *s_THUSTR_29; // = THUSTR_29; +//#define THUSTR_30 "level 30: last call" +extern const char *s_THUSTR_30; // = THUSTR_30; + +//#define THUSTR_31 "level 31: pharaoh" +extern const char *s_THUSTR_31; // = THUSTR_31; +//#define THUSTR_32 "level 32: caribbean" +extern const char *s_THUSTR_32; // = THUSTR_32; + +//#define HUSTR_CHATMACRO1 "I'm ready to kick butt!" +extern const char *s_HUSTR_CHATMACRO1; // = HUSTR_CHATMACRO1; +//#define HUSTR_CHATMACRO2 "I'm OK." +extern const char *s_HUSTR_CHATMACRO2; // = HUSTR_CHATMACRO2; +//#define HUSTR_CHATMACRO3 "I'm not looking too good!" +extern const char *s_HUSTR_CHATMACRO3; // = HUSTR_CHATMACRO3; +//#define HUSTR_CHATMACRO4 "Help!" +extern const char *s_HUSTR_CHATMACRO4; // = HUSTR_CHATMACRO4; +//#define HUSTR_CHATMACRO5 "You suck!" +extern const char *s_HUSTR_CHATMACRO5; // = HUSTR_CHATMACRO5; +//#define HUSTR_CHATMACRO6 "Next time, scumbag..." +extern const char *s_HUSTR_CHATMACRO6; // = HUSTR_CHATMACRO6; +//#define HUSTR_CHATMACRO7 "Come here!" +extern const char *s_HUSTR_CHATMACRO7; // = HUSTR_CHATMACRO7; +//#define HUSTR_CHATMACRO8 "I'll take care of it." +extern const char *s_HUSTR_CHATMACRO8; // = HUSTR_CHATMACRO8; +//#define HUSTR_CHATMACRO9 "Yes" +extern const char *s_HUSTR_CHATMACRO9; // = HUSTR_CHATMACRO9; +//#define HUSTR_CHATMACRO0 "No" +extern const char *s_HUSTR_CHATMACRO0; // = HUSTR_CHATMACRO0; + +//#define HUSTR_TALKTOSELF1 "You mumble to yourself" +extern const char *s_HUSTR_TALKTOSELF1; // = HUSTR_TALKTOSELF1; +//#define HUSTR_TALKTOSELF2 "Who's there?" +extern const char *s_HUSTR_TALKTOSELF2; // = HUSTR_TALKTOSELF2; +//#define HUSTR_TALKTOSELF3 "You scare yourself" +extern const char *s_HUSTR_TALKTOSELF3; // = HUSTR_TALKTOSELF3; +//#define HUSTR_TALKTOSELF4 "You start to rave" +extern const char *s_HUSTR_TALKTOSELF4; // = HUSTR_TALKTOSELF4; +//#define HUSTR_TALKTOSELF5 "You've lost it..." +extern const char *s_HUSTR_TALKTOSELF5; // = HUSTR_TALKTOSELF5; + +//#define HUSTR_MESSAGESENT "[Message Sent]" +extern const char *s_HUSTR_MESSAGESENT; // = HUSTR_MESSAGESENT; + +// The following should NOT be changed unless it seems +// just AWFULLY necessary + +//#define HUSTR_PLRGREEN "Green: " +extern const char *s_HUSTR_PLRGREEN; // = HUSTR_PLRGREEN; +//#define HUSTR_PLRINDIGO "Indigo: " +extern const char *s_HUSTR_PLRINDIGO; // = HUSTR_PLRINDIGO; +//#define HUSTR_PLRBROWN "Brown: " +extern const char *s_HUSTR_PLRBROWN; // = HUSTR_PLRBROWN; +//#define HUSTR_PLRRED "Red: " +extern const char *s_HUSTR_PLRRED; // = HUSTR_PLRRED; + +// +// AM_map.C +// + +//#define AMSTR_FOLLOWON "Follow Mode ON" +extern const char* s_AMSTR_FOLLOWON; // = AMSTR_FOLLOWON; +//#define AMSTR_FOLLOWOFF "Follow Mode OFF" +extern const char* s_AMSTR_FOLLOWOFF; // = AMSTR_FOLLOWOFF; + +//#define AMSTR_GRIDON "Grid ON" +extern const char* s_AMSTR_GRIDON; // = AMSTR_GRIDON; +//#define AMSTR_GRIDOFF "Grid OFF" +extern const char* s_AMSTR_GRIDOFF; // = AMSTR_GRIDOFF; + +//#define AMSTR_MARKEDSPOT "Marked Spot" +extern const char* s_AMSTR_MARKEDSPOT; // = AMSTR_MARKEDSPOT; +//#define AMSTR_MARKSCLEARED "All Marks Cleared" +extern const char* s_AMSTR_MARKSCLEARED; // = AMSTR_MARKSCLEARED; + +// CPhipps - automap rotate & overlay +extern const char* s_AMSTR_ROTATEON; +extern const char* s_AMSTR_ROTATEOFF; +extern const char* s_AMSTR_OVERLAYON; +extern const char* s_AMSTR_OVERLAYOFF; + +// +// ST_stuff.C +// + +//#define STSTR_MUS "Music Change" +extern const char* s_STSTR_MUS; // = STSTR_MUS; +//#define STSTR_NOMUS "IMPOSSIBLE SELECTION" +extern const char* s_STSTR_NOMUS; // = STSTR_NOMUS; +//#define STSTR_DQDON "Degreelessness Mode On" +extern const char* s_STSTR_DQDON; // = STSTR_DQDON; +//#define STSTR_DQDOFF "Degreelessness Mode Off" +extern const char* s_STSTR_DQDOFF; // = STSTR_DQDOFF; + +//#define STSTR_KFAADDED "Very Happy Ammo Added" +extern const char* s_STSTR_KFAADDED; // = STSTR_KFAADDED; +//#define STSTR_FAADDED "Ammo (no keys) Added" +extern const char* s_STSTR_FAADDED; // = STSTR_FAADDED; + +//#define STSTR_NCON "No Clipping Mode ON" +extern const char* s_STSTR_NCON; // = STSTR_NCON; +//#define STSTR_NCOFF "No Clipping Mode OFF" +extern const char* s_STSTR_NCOFF; // = STSTR_NCOFF; + +//#define STSTR_BEHOLD "inVuln, Str, Inviso, Rad, Allmap, or Lite-amp" +extern const char* s_STSTR_BEHOLD; // = STSTR_BEHOLD; +//#define STSTR_BEHOLDX "Power-up Toggled" +extern const char* s_STSTR_BEHOLDX; // = STSTR_BEHOLDX; + +//#define STSTR_CHOPPERS "... doesn't suck - GM" +extern const char* s_STSTR_CHOPPERS; // = STSTR_CHOPPERS; +//#define STSTR_CLEV "Changing Level..." +extern const char* s_STSTR_CLEV; // = STSTR_CLEV; + +// +// F_Finale.C +// +/* +#define E1TEXT \ +"Once you beat the big badasses and\n"\ +"clean out the moon base you're supposed\n"\ +"to win, aren't you? Aren't you? Where's\n"\ +"your fat reward and ticket home? What\n"\ +"the hell is this? It's not supposed to\n"\ +"end this way!\n"\ +"\n" \ +"It stinks like rotten meat, but looks\n"\ +"like the lost Deimos base. Looks like\n"\ +"you're stuck on The Shores of Hell.\n"\ +"The only way out is through.\n"\ +"\n"\ +"To continue the DOOM experience, play\n"\ +"The Shores of Hell and its amazing\n"\ +"sequel, Inferno!\n" +*/ +extern const char* s_E1TEXT; // = E1TEXT; + + +/* +#define E2TEXT \ +"You've done it! The hideous cyber-\n"\ +"demon lord that ruled the lost Deimos\n"\ +"moon base has been slain and you\n"\ +"are triumphant! But ... where are\n"\ +"you? You clamber to the edge of the\n"\ +"moon and look down to see the awful\n"\ +"truth.\n" \ +"\n"\ +"Deimos floats above Hell itself!\n"\ +"You've never heard of anyone escaping\n"\ +"from Hell, but you'll make the bastards\n"\ +"sorry they ever heard of you! Quickly,\n"\ +"you rappel down to the surface of\n"\ +"Hell.\n"\ +"\n" \ +"Now, it's on to the final chapter of\n"\ +"DOOM! -- Inferno." +*/ +extern const char* s_E2TEXT; // = E2TEXT; + + +/* +#define E3TEXT \ +"The loathsome spiderdemon that\n"\ +"masterminded the invasion of the moon\n"\ +"bases and caused so much death has had\n"\ +"its ass kicked for all time.\n"\ +"\n"\ +"A hidden doorway opens and you enter.\n"\ +"You've proven too tough for Hell to\n"\ +"contain, and now Hell at last plays\n"\ +"fair -- for you emerge from the door\n"\ +"to see the green fields of Earth!\n"\ +"Home at last.\n" \ +"\n"\ +"You wonder what's been happening on\n"\ +"Earth while you were battling evil\n"\ +"unleashed. It's good that no Hell-\n"\ +"spawn could have come through that\n"\ +"door with you ..." +*/ +extern const char* s_E3TEXT; // = E3TEXT; + + +/* +#define E4TEXT \ +"the spider mastermind must have sent forth\n"\ +"its legions of hellspawn before your\n"\ +"final confrontation with that terrible\n"\ +"beast from hell. but you stepped forward\n"\ +"and brought forth eternal damnation and\n"\ +"suffering upon the horde as a true hero\n"\ +"would in the face of something so evil.\n"\ +"\n"\ +"besides, someone was gonna pay for what\n"\ +"happened to daisy, your pet rabbit.\n"\ +"\n"\ +"but now, you see spread before you more\n"\ +"potential pain and gibbitude as a nation\n"\ +"of demons run amok among our cities.\n"\ +"\n"\ +"next stop, hell on earth!" +*/ +extern const char* s_E4TEXT; // = E4TEXT; + + +// after level 6, put this: + +/* +#define C1TEXT \ +"YOU HAVE ENTERED DEEPLY INTO THE INFESTED\n" \ +"STARPORT. BUT SOMETHING IS WRONG. THE\n" \ +"MONSTERS HAVE BROUGHT THEIR OWN REALITY\n" \ +"WITH THEM, AND THE STARPORT'S TECHNOLOGY\n" \ +"IS BEING SUBVERTED BY THEIR PRESENCE.\n" \ +"\n"\ +"AHEAD, YOU SEE AN OUTPOST OF HELL, A\n" \ +"FORTIFIED ZONE. IF YOU CAN GET PAST IT,\n" \ +"YOU CAN PENETRATE INTO THE HAUNTED HEART\n" \ +"OF THE STARBASE AND FIND THE CONTROLLING\n" \ +"SWITCH WHICH HOLDS EARTH'S POPULATION\n" \ +"HOSTAGE." +*/ +extern const char* s_C1TEXT; // = C1TEXT; + +// After level 11, put this: + +/* +#define C2TEXT \ +"YOU HAVE WON! YOUR VICTORY HAS ENABLED\n" \ +"HUMANKIND TO EVACUATE EARTH AND ESCAPE\n"\ +"THE NIGHTMARE. NOW YOU ARE THE ONLY\n"\ +"HUMAN LEFT ON THE FACE OF THE PLANET.\n"\ +"CANNIBAL MUTATIONS, CARNIVOROUS ALIENS,\n"\ +"AND EVIL SPIRITS ARE YOUR ONLY NEIGHBORS.\n"\ +"YOU SIT BACK AND WAIT FOR DEATH, CONTENT\n"\ +"THAT YOU HAVE SAVED YOUR SPECIES.\n"\ +"\n"\ +"BUT THEN, EARTH CONTROL BEAMS DOWN A\n"\ +"MESSAGE FROM SPACE: \"SENSORS HAVE LOCATED\n"\ +"THE SOURCE OF THE ALIEN INVASION. IF YOU\n"\ +"GO THERE, YOU MAY BE ABLE TO BLOCK THEIR\n"\ +"ENTRY. THE ALIEN BASE IS IN THE HEART OF\n"\ +"YOUR OWN HOME CITY, NOT FAR FROM THE\n"\ +"STARPORT.\" SLOWLY AND PAINFULLY YOU GET\n"\ +"UP AND RETURN TO THE FRAY." +*/ +extern const char* s_C2TEXT; // = C2TEXT; + + +// After level 20, put this: + +/* +#define C3TEXT \ +"YOU ARE AT THE CORRUPT HEART OF THE CITY,\n"\ +"SURROUNDED BY THE CORPSES OF YOUR ENEMIES.\n"\ +"YOU SEE NO WAY TO DESTROY THE CREATURES'\n"\ +"ENTRYWAY ON THIS SIDE, SO YOU CLENCH YOUR\n"\ +"TEETH AND PLUNGE THROUGH IT.\n"\ +"\n"\ +"THERE MUST BE A WAY TO CLOSE IT ON THE\n"\ +"OTHER SIDE. WHAT DO YOU CARE IF YOU'VE\n"\ +"GOT TO GO THROUGH HELL TO GET TO IT?" +*/ +extern const char* s_C3TEXT; // = C3TEXT; + + +// After level 29, put this: + +/* +#define C4TEXT \ +"THE HORRENDOUS VISAGE OF THE BIGGEST\n"\ +"DEMON YOU'VE EVER SEEN CRUMBLES BEFORE\n"\ +"YOU, AFTER YOU PUMP YOUR ROCKETS INTO\n"\ +"HIS EXPOSED BRAIN. THE MONSTER SHRIVELS\n"\ +"UP AND DIES, ITS THRASHING LIMBS\n"\ +"DEVASTATING UNTOLD MILES OF HELL'S\n"\ +"SURFACE.\n"\ +"\n"\ +"YOU'VE DONE IT. THE INVASION IS OVER.\n"\ +"EARTH IS SAVED. HELL IS A WRECK. YOU\n"\ +"WONDER WHERE BAD FOLKS WILL GO WHEN THEY\n"\ +"DIE, NOW. WIPING THE SWEAT FROM YOUR\n"\ +"FOREHEAD YOU BEGIN THE LONG TREK BACK\n"\ +"HOME. REBUILDING EARTH OUGHT TO BE A\n"\ +"LOT MORE FUN THAN RUINING IT WAS.\n" +*/ +extern const char* s_C4TEXT; // = C4TEXT; + + + +// Before level 31, put this: + +/* +#define C5TEXT \ +"CONGRATULATIONS, YOU'VE FOUND THE SECRET\n"\ +"LEVEL! LOOKS LIKE IT'S BEEN BUILT BY\n"\ +"HUMANS, RATHER THAN DEMONS. YOU WONDER\n"\ +"WHO THE INMATES OF THIS CORNER OF HELL\n"\ +"WILL BE." +*/ +extern const char* s_C5TEXT; // = C5TEXT; + + +// Before level 32, put this: + +/* +#define C6TEXT \ +"CONGRATULATIONS, YOU'VE FOUND THE\n"\ +"SUPER SECRET LEVEL! YOU'D BETTER\n"\ +"BLAZE THROUGH THIS ONE!\n" +*/ +extern const char* s_C6TEXT; // = C6TEXT; + + +// after map 06 + +/* +#define P1TEXT \ +"You gloat over the steaming carcass of the\n"\ +"Guardian. With its death, you've wrested\n"\ +"the Accelerator from the stinking claws\n"\ +"of Hell. You relax and glance around the\n"\ +"room. Damn! There was supposed to be at\n"\ +"least one working prototype, but you can't\n"\ +"see it. The demons must have taken it.\n"\ +"\n"\ +"You must find the prototype, or all your\n"\ +"struggles will have been wasted. Keep\n"\ +"moving, keep fighting, keep killing.\n"\ +"Oh yes, keep living, too." +*/ +extern const char* s_P1TEXT; // = P1TEXT; + + +// after map 11 + +/* +#define P2TEXT \ +"Even the deadly Arch-Vile labyrinth could\n"\ +"not stop you, and you've gotten to the\n"\ +"prototype Accelerator which is soon\n"\ +"efficiently and permanently deactivated.\n"\ +"\n"\ +"You're good at that kind of thing." +*/ +extern const char* s_P2TEXT; // = P2TEXT; + + +// after map 20 + +/* +#define P3TEXT \ +"You've bashed and battered your way into\n"\ +"the heart of the devil-hive. Time for a\n"\ +"Search-and-Destroy mission, aimed at the\n"\ +"Gatekeeper, whose foul offspring is\n"\ +"cascading to Earth. Yeah, he's bad. But\n"\ +"you know who's worse!\n"\ +"\n"\ +"Grinning evilly, you check your gear, and\n"\ +"get ready to give the bastard a little Hell\n"\ +"of your own making!" +*/ +extern const char* s_P3TEXT; // = P3TEXT; + +// after map 30 + +/* +#define P4TEXT \ +"The Gatekeeper's evil face is splattered\n"\ +"all over the place. As its tattered corpse\n"\ +"collapses, an inverted Gate forms and\n"\ +"sucks down the shards of the last\n"\ +"prototype Accelerator, not to mention the\n"\ +"few remaining demons. You're done. Hell\n"\ +"has gone back to pounding bad dead folks \n"\ +"instead of good live ones. Remember to\n"\ +"tell your grandkids to put a rocket\n"\ +"launcher in your coffin. If you go to Hell\n"\ +"when you die, you'll need it for some\n"\ +"final cleaning-up ..." +*/ +extern const char* s_P4TEXT; // = P4TEXT; + +// before map 31 + +/* +#define P5TEXT \ +"You've found the second-hardest level we\n"\ +"got. Hope you have a saved game a level or\n"\ +"two previous. If not, be prepared to die\n"\ +"aplenty. For master marines only." +*/ +extern const char* s_P5TEXT; // = P5TEXT; + +// before map 32 + +/* +#define P6TEXT \ +"Betcha wondered just what WAS the hardest\n"\ +"level we had ready for ya? Now you know.\n"\ +"No one gets out alive." +*/ +extern const char* s_P6TEXT; // = P6TEXT; + + +/* +#define T1TEXT \ +"You've fought your way out of the infested\n"\ +"experimental labs. It seems that UAC has\n"\ +"once again gulped it down. With their\n"\ +"high turnover, it must be hard for poor\n"\ +"old UAC to buy corporate health insurance\n"\ +"nowadays..\n"\ +"\n"\ +"Ahead lies the military complex, now\n"\ +"swarming with diseased horrors hot to get\n"\ +"their teeth into you. With luck, the\n"\ +"complex still has some warlike ordnance\n"\ +"laying around." +*/ +extern const char* s_T1TEXT; // = T1TEXT; + + +/* +#define T2TEXT \ +"You hear the grinding of heavy machinery\n"\ +"ahead. You sure hope they're not stamping\n"\ +"out new hellspawn, but you're ready to\n"\ +"ream out a whole herd if you have to.\n"\ +"They might be planning a blood feast, but\n"\ +"you feel about as mean as two thousand\n"\ +"maniacs packed into one mad killer.\n"\ +"\n"\ +"You don't plan to go down easy." +*/ +extern const char* s_T2TEXT; // = T2TEXT; + + +/* +#define T3TEXT \ +"The vista opening ahead looks real damn\n"\ +"familiar. Smells familiar, too -- like\n"\ +"fried excrement. You didn't like this\n"\ +"place before, and you sure as hell ain't\n"\ +"planning to like it now. The more you\n"\ +"brood on it, the madder you get.\n"\ +"Hefting your gun, an evil grin trickles\n"\ +"onto your face. Time to take some names." +*/ +extern const char* s_T3TEXT; // = T3TEXT; + +/* +#define T4TEXT \ +"Suddenly, all is silent, from one horizon\n"\ +"to the other. The agonizing echo of Hell\n"\ +"fades away, the nightmare sky turns to\n"\ +"blue, the heaps of monster corpses start \n"\ +"to evaporate along with the evil stench \n"\ +"that filled the air. Jeeze, maybe you've\n"\ +"done it. Have you really won?\n"\ +"\n"\ +"Something rumbles in the distance.\n"\ +"A blue light begins to glow inside the\n"\ +"ruined skull of the demon-spitter." +*/ +extern const char* s_T4TEXT; // = T4TEXT; + + +/* +#define T5TEXT \ +"What now? Looks totally different. Kind\n"\ +"of like King Tut's condo. Well,\n"\ +"whatever's here can't be any worse\n"\ +"than usual. Can it? Or maybe it's best\n"\ +"to let sleeping gods lie.." +*/ +extern const char* s_T5TEXT; // = T5TEXT; + + +/* +#define T6TEXT \ +"Time for a vacation. You've burst the\n"\ +"bowels of hell and by golly you're ready\n"\ +"for a break. You mutter to yourself,\n"\ +"Maybe someone else can kick Hell's ass\n"\ +"next time around. Ahead lies a quiet town,\n"\ +"with peaceful flowing water, quaint\n"\ +"buildings, and presumably no Hellspawn.\n"\ +"\n"\ +"As you step off the transport, you hear\n"\ +"the stomp of a cyberdemon's iron shoe." +*/ +extern const char* s_T6TEXT; // = T6TEXT; + +// +// Character cast strings F_FINALE.C +// +//#define CC_ZOMBIE "ZOMBIEMAN" +extern const char* s_CC_ZOMBIE; // = CC_ZOMBIE; +//#define CC_SHOTGUN "SHOTGUN GUY" +extern const char* s_CC_SHOTGUN; // = CC_SHOTGUN; +//#define CC_HEAVY "HEAVY WEAPON DUDE" +extern const char* s_CC_HEAVY; // = CC_HEAVY; +//#define CC_IMP "IMP" +extern const char* s_CC_IMP; // = CC_IMP; +//#define CC_DEMON "DEMON" +extern const char* s_CC_DEMON; // = CC_DEMON; +//#define CC_LOST "LOST SOUL" +extern const char* s_CC_LOST; // = CC_LOST; +//#define CC_CACO "CACODEMON" +extern const char* s_CC_CACO; // = CC_CACO; +//#define CC_HELL "HELL KNIGHT" +extern const char* s_CC_HELL; // = CC_HELL; +//#define CC_BARON "BARON OF HELL" +extern const char* s_CC_BARON; // = CC_BARON; +//#define CC_ARACH "ARACHNOTRON" +extern const char* s_CC_ARACH; // = CC_ARACH; +//#define CC_PAIN "PAIN ELEMENTAL" +extern const char* s_CC_PAIN; // = CC_PAIN; +//#define CC_REVEN "REVENANT" +extern const char* s_CC_REVEN; // = CC_REVEN; +//#define CC_MANCU "MANCUBUS" +extern const char* s_CC_MANCU; // = CC_MANCU; +//#define CC_ARCH "ARCH-VILE" +extern const char* s_CC_ARCH; // = CC_ARCH; +//#define CC_SPIDER "THE SPIDER MASTERMIND" +extern const char* s_CC_SPIDER; // = CC_SPIDER; +//#define CC_CYBER "THE CYBERDEMON" +extern const char* s_CC_CYBER; // = CC_CYBER; +//#define CC_HERO "OUR HERO" +extern const char* s_CC_HERO; // = CC_HERO; + +// Ty 03/30/98 - new substitutions for background textures during int screens +// char* bgflatE1 = "FLOOR4_8"; +extern const char* bgflatE1; +// char* bgflatE2 = "SFLR6_1"; +extern const char* bgflatE2; +// char* bgflatE3 = "MFLR8_4"; +extern const char* bgflatE3; +// char* bgflatE4 = "MFLR8_3"; +extern const char* bgflatE4; + +// char* bgflat06 = "SLIME16"; +extern const char* bgflat06; +// char* bgflat11 = "RROCK14"; +extern const char* bgflat11; +// char* bgflat20 = "RROCK07"; +extern const char* bgflat20; +// char* bgflat30 = "RROCK17"; +extern const char* bgflat30; +// char* bgflat15 = "RROCK13"; +extern const char* bgflat15; +// char* bgflat31 = "RROCK19"; +extern const char* bgflat31; + +// char* bgcastcall = "BOSSBACK"; // panel behind cast call +extern const char* bgcastcall; + +// ignored if blank, general purpose startup announcements +// char* startup1 = ""; +extern const char* startup1; +// char* startup2 = ""; +extern const char* startup2; +// char* startup3 = ""; +extern const char* startup3; +// char* startup4 = ""; +extern const char* startup4; +// char* startup5 = ""; +extern const char* startup5; + +// from g_game.c, prefix for savegame name like "boomsav" +extern const char* savegamename; + +void D_BuildBEXTables(void); + +#endif diff --git a/src/d_englsh.h b/src/d_englsh.h new file mode 100644 index 0000000..86e7416 --- /dev/null +++ b/src/d_englsh.h @@ -0,0 +1,707 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Printed strings for translation. + * English language support (default). + * See dstrings.h for suggestions about foreign language BEX support + * + *-----------------------------------------------------------------------------*/ + +#ifndef __D_ENGLSH__ +#define __D_ENGLSH__ + +/* d_main.c */ +#define D_DEVSTR "Development mode ON.\n" +#define D_CDROM "CD-ROM Version: default.cfg from c:\\doomdata\n" + +/* m_menu.c */ +#define PRESSKEY "press a key." +#define PRESSYN "press y or n." +#define QUITMSG "are you sure you want to\nquit this great game?" +#define LOADNET "you can't do load while in a net game!\n\n"PRESSKEY +#define QLOADNET "you can't quickload during a netgame!\n\n"PRESSKEY +#define QSAVESPOT "you haven't picked a quicksave slot yet!\n\n"PRESSKEY +#define SAVEDEAD "you can't save if you aren't playing!\n\n"PRESSKEY +#define QSPROMPT "quicksave over your game named\n\n'%s'?\n\n"PRESSYN +#define QLPROMPT "do you want to quickload the game named\n\n'%s'?\n\n"PRESSYN + +#define NEWGAME \ + "you can't start a new game\n"\ + "while in a network game.\n\n"PRESSKEY + +#define NIGHTMARE \ + "are you sure? this skill level\n"\ + "isn't even remotely fair.\n\n"PRESSYN + +#define SWSTRING \ + "this is the shareware version of doom.\n\n"\ + "you need to order the entire trilogy.\n\n"PRESSKEY + +#define MSGOFF "Messages OFF" +#define MSGON "Messages ON" +#define NETEND "you can't end a netgame!\n\n"PRESSKEY +#define ENDGAME "are you sure you want to end the game?\n\n"PRESSYN +#define RESTARTLEVEL "restart the level?\n\n"PRESSYN + +#define DOSY "(press y to quit)" + +#define DETAILHI "High detail" +#define DETAILLO "Low detail" +#define GAMMALVL0 "Gamma correction OFF" +#define GAMMALVL1 "Gamma correction level 1" +#define GAMMALVL2 "Gamma correction level 2" +#define GAMMALVL3 "Gamma correction level 3" +#define GAMMALVL4 "Gamma correction level 4" +#define EMPTYSTRING "empty slot" + +/* p_inter.c */ +#define GOTARMOR "Picked up the armor." +#define GOTMEGA "Picked up the MegaArmor!" +#define GOTHTHBONUS "Picked up a health bonus." +#define GOTARMBONUS "Picked up an armor bonus." +#define GOTSTIM "Picked up a stimpack." +#define GOTMEDINEED "Picked up a medikit that you REALLY need!" +#define GOTMEDIKIT "Picked up a medikit." +#define GOTSUPER "Supercharge!" + +#define GOTBLUECARD "Picked up a blue keycard." +#define GOTYELWCARD "Picked up a yellow keycard." +#define GOTREDCARD "Picked up a red keycard." +#define GOTBLUESKUL "Picked up a blue skull key." +#define GOTYELWSKUL "Picked up a yellow skull key." +#define GOTREDSKULL "Picked up a red skull key." + +#define GOTINVUL "Invulnerability!" +#define GOTBERSERK "Berserk!" +#define GOTINVIS "Partial Invisibility" +#define GOTSUIT "Radiation Shielding Suit" +#define GOTMAP "Computer Area Map" +#define GOTVISOR "Light Amplification Visor" +#define GOTMSPHERE "MegaSphere!" + +#define GOTCLIP "Picked up a clip." +#define GOTCLIPBOX "Picked up a box of bullets." +#define GOTROCKET "Picked up a rocket." +#define GOTROCKBOX "Picked up a box of rockets." +#define GOTCELL "Picked up an energy cell." +#define GOTCELLBOX "Picked up an energy cell pack." +#define GOTSHELLS "Picked up 4 shotgun shells." +#define GOTSHELLBOX "Picked up a box of shotgun shells." +#define GOTBACKPACK "Picked up a backpack full of ammo!" + +#define GOTBFG9000 "You got the BFG9000! Oh, yes." +#define GOTCHAINGUN "You got the chaingun!" +#define GOTCHAINSAW "A chainsaw! Find some meat!" +#define GOTLAUNCHER "You got the rocket launcher!" +#define GOTPLASMA "You got the plasma gun!" +#define GOTSHOTGUN "You got the shotgun!" +#define GOTSHOTGUN2 "You got the super shotgun!" + +/* p_doors.c */ +#define PD_BLUEO "You need a blue key to activate this object" +#define PD_REDO "You need a red key to activate this object" +#define PD_YELLOWO "You need a yellow key to activate this object" +#define PD_BLUEK "You need a blue key to open this door" +#define PD_REDK "You need a red key to open this door" +#define PD_YELLOWK "You need a yellow key to open this door" +/* jff 02/05/98 Create messages specific to card and skull keys */ +#define PD_BLUEC "You need a blue card to open this door" +#define PD_REDC "You need a red card to open this door" +#define PD_YELLOWC "You need a yellow card to open this door" +#define PD_BLUES "You need a blue skull to open this door" +#define PD_REDS "You need a red skull to open this door" +#define PD_YELLOWS "You need a yellow skull to open this door" +#define PD_ANY "Any key will open this door" +#define PD_ALL3 "You need all three keys to open this door" +#define PD_ALL6 "You need all six keys to open this door" + +/* g_game.c */ +#define GGSAVED "game saved." + +/* hu_stuff.c */ +#define HUSTR_MSGU "[Message unsent]" + +#define HUSTR_E1M1 "E1M1: Hangar" +#define HUSTR_E1M2 "E1M2: Nuclear Plant" +#define HUSTR_E1M3 "E1M3: Toxin Refinery" +#define HUSTR_E1M4 "E1M4: Command Control" +#define HUSTR_E1M5 "E1M5: Phobos Lab" +#define HUSTR_E1M6 "E1M6: Central Processing" +#define HUSTR_E1M7 "E1M7: Computer Station" +#define HUSTR_E1M8 "E1M8: Phobos Anomaly" +#define HUSTR_E1M9 "E1M9: Military Base" + +#define HUSTR_E2M1 "E2M1: Deimos Anomaly" +#define HUSTR_E2M2 "E2M2: Containment Area" +#define HUSTR_E2M3 "E2M3: Refinery" +#define HUSTR_E2M4 "E2M4: Deimos Lab" +#define HUSTR_E2M5 "E2M5: Command Center" +#define HUSTR_E2M6 "E2M6: Halls of the Damned" +#define HUSTR_E2M7 "E2M7: Spawning Vats" +#define HUSTR_E2M8 "E2M8: Tower of Babel" +#define HUSTR_E2M9 "E2M9: Fortress of Mystery" + +#define HUSTR_E3M1 "E3M1: Hell Keep" +#define HUSTR_E3M2 "E3M2: Slough of Despair" +#define HUSTR_E3M3 "E3M3: Pandemonium" +#define HUSTR_E3M4 "E3M4: House of Pain" +#define HUSTR_E3M5 "E3M5: Unholy Cathedral" +#define HUSTR_E3M6 "E3M6: Mt. Erebus" +#define HUSTR_E3M7 "E3M7: Limbo" +#define HUSTR_E3M8 "E3M8: Dis" +#define HUSTR_E3M9 "E3M9: Warrens" + +#define HUSTR_E4M1 "E4M1: Hell Beneath" +#define HUSTR_E4M2 "E4M2: Perfect Hatred" +#define HUSTR_E4M3 "E4M3: Sever The Wicked" +#define HUSTR_E4M4 "E4M4: Unruly Evil" +#define HUSTR_E4M5 "E4M5: They Will Repent" +#define HUSTR_E4M6 "E4M6: Against Thee Wickedly" +#define HUSTR_E4M7 "E4M7: And Hell Followed" +#define HUSTR_E4M8 "E4M8: Unto The Cruel" +#define HUSTR_E4M9 "E4M9: Fear" + +#define HUSTR_1 "level 1: entryway" +#define HUSTR_2 "level 2: underhalls" +#define HUSTR_3 "level 3: the gantlet" +#define HUSTR_4 "level 4: the focus" +#define HUSTR_5 "level 5: the waste tunnels" +#define HUSTR_6 "level 6: the crusher" +#define HUSTR_7 "level 7: dead simple" +#define HUSTR_8 "level 8: tricks and traps" +#define HUSTR_9 "level 9: the pit" +#define HUSTR_10 "level 10: refueling base" +#define HUSTR_11 "level 11: 'o' of destruction!" + +#define HUSTR_12 "level 12: the factory" +#define HUSTR_13 "level 13: downtown" +#define HUSTR_14 "level 14: the inmost dens" +#define HUSTR_15 "level 15: industrial zone" +#define HUSTR_16 "level 16: suburbs" +#define HUSTR_17 "level 17: tenements" +#define HUSTR_18 "level 18: the courtyard" +#define HUSTR_19 "level 19: the citadel" +#define HUSTR_20 "level 20: gotcha!" + +#define HUSTR_21 "level 21: nirvana" +#define HUSTR_22 "level 22: the catacombs" +#define HUSTR_23 "level 23: barrels o' fun" +#define HUSTR_24 "level 24: the chasm" +#define HUSTR_25 "level 25: bloodfalls" +#define HUSTR_26 "level 26: the abandoned mines" +#define HUSTR_27 "level 27: monster condo" +#define HUSTR_28 "level 28: the spirit world" +#define HUSTR_29 "level 29: the living end" +#define HUSTR_30 "level 30: icon of sin" + +#define HUSTR_31 "level 31: wolfenstein" +#define HUSTR_32 "level 32: grosse" + +#define PHUSTR_1 "level 1: congo" +#define PHUSTR_2 "level 2: well of souls" +#define PHUSTR_3 "level 3: aztec" +#define PHUSTR_4 "level 4: caged" +#define PHUSTR_5 "level 5: ghost town" +#define PHUSTR_6 "level 6: baron's lair" +#define PHUSTR_7 "level 7: caughtyard" +#define PHUSTR_8 "level 8: realm" +#define PHUSTR_9 "level 9: abattoire" +#define PHUSTR_10 "level 10: onslaught" +#define PHUSTR_11 "level 11: hunted" + +#define PHUSTR_12 "level 12: speed" +#define PHUSTR_13 "level 13: the crypt" +#define PHUSTR_14 "level 14: genesis" +#define PHUSTR_15 "level 15: the twilight" +#define PHUSTR_16 "level 16: the omen" +#define PHUSTR_17 "level 17: compound" +#define PHUSTR_18 "level 18: neurosphere" +#define PHUSTR_19 "level 19: nme" +#define PHUSTR_20 "level 20: the death domain" + +#define PHUSTR_21 "level 21: slayer" +#define PHUSTR_22 "level 22: impossible mission" +#define PHUSTR_23 "level 23: tombstone" +#define PHUSTR_24 "level 24: the final frontier" +#define PHUSTR_25 "level 25: the temple of darkness" +#define PHUSTR_26 "level 26: bunker" +#define PHUSTR_27 "level 27: anti-christ" +#define PHUSTR_28 "level 28: the sewers" +#define PHUSTR_29 "level 29: odyssey of noises" +#define PHUSTR_30 "level 30: the gateway of hell" + +#define PHUSTR_31 "level 31: cyberden" +#define PHUSTR_32 "level 32: go 2 it" + +#define THUSTR_1 "level 1: system control" +#define THUSTR_2 "level 2: human bbq" +#define THUSTR_3 "level 3: power control" +#define THUSTR_4 "level 4: wormhole" +#define THUSTR_5 "level 5: hanger" +#define THUSTR_6 "level 6: open season" +#define THUSTR_7 "level 7: prison" +#define THUSTR_8 "level 8: metal" +#define THUSTR_9 "level 9: stronghold" +#define THUSTR_10 "level 10: redemption" +#define THUSTR_11 "level 11: storage facility" + +#define THUSTR_12 "level 12: crater" +#define THUSTR_13 "level 13: nukage processing" +#define THUSTR_14 "level 14: steel works" +#define THUSTR_15 "level 15: dead zone" +#define THUSTR_16 "level 16: deepest reaches" +#define THUSTR_17 "level 17: processing area" +#define THUSTR_18 "level 18: mill" +#define THUSTR_19 "level 19: shipping/respawning" +#define THUSTR_20 "level 20: central processing" + +#define THUSTR_21 "level 21: administration center" +#define THUSTR_22 "level 22: habitat" +#define THUSTR_23 "level 23: lunar mining project" +#define THUSTR_24 "level 24: quarry" +#define THUSTR_25 "level 25: baron's den" +#define THUSTR_26 "level 26: ballistyx" +#define THUSTR_27 "level 27: mount pain" +#define THUSTR_28 "level 28: heck" +#define THUSTR_29 "level 29: river styx" +#define THUSTR_30 "level 30: last call" + +#define THUSTR_31 "level 31: pharaoh" +#define THUSTR_32 "level 32: caribbean" + +#define HUSTR_CHATMACRO1 "I'm ready to kick butt!" +#define HUSTR_CHATMACRO2 "I'm OK." +#define HUSTR_CHATMACRO3 "I'm not looking too good!" +#define HUSTR_CHATMACRO4 "Help!" +#define HUSTR_CHATMACRO5 "You suck!" +#define HUSTR_CHATMACRO6 "Next time, scumbag..." +#define HUSTR_CHATMACRO7 "Come here!" +#define HUSTR_CHATMACRO8 "I'll take care of it." +#define HUSTR_CHATMACRO9 "Yes" +#define HUSTR_CHATMACRO0 "No" + +#define HUSTR_TALKTOSELF1 "You mumble to yourself" +#define HUSTR_TALKTOSELF2 "Who's there?" +#define HUSTR_TALKTOSELF3 "You scare yourself" +#define HUSTR_TALKTOSELF4 "You start to rave" +#define HUSTR_TALKTOSELF5 "You've lost it..." + +#define HUSTR_MESSAGESENT "[Message Sent]" + +/* The following should NOT be changed unless it seems + * just AWFULLY necessary */ + +#define HUSTR_PLRGREEN "Player 1: " +#define HUSTR_PLRINDIGO "Player 2: " +#define HUSTR_PLRBROWN "Player 3: " +#define HUSTR_PLRRED "Player 4: " + +#define HUSTR_KEYGREEN 'g' +#define HUSTR_KEYINDIGO 'i' +#define HUSTR_KEYBROWN 'b' +#define HUSTR_KEYRED 'r' + +/* am_map.c */ + +#define AMSTR_FOLLOWON "Follow Mode ON" +#define AMSTR_FOLLOWOFF "Follow Mode OFF" + +#define AMSTR_GRIDON "Grid ON" +#define AMSTR_GRIDOFF "Grid OFF" + +#define AMSTR_MARKEDSPOT "Marked Spot" +#define AMSTR_MARKSCLEARED "All Marks Cleared" + +#define AMSTR_ROTATEON "Rotate Mode ON" +#define AMSTR_ROTATEOFF "Rotate Mode OFF" + +#define AMSTR_OVERLAYON "Overlay Mode ON" +#define AMSTR_OVERLAYOFF "Overlay Mode OFF" + +/* st_stuff.c */ + +#define STSTR_MUS "Music Change" +#define STSTR_NOMUS "IMPOSSIBLE SELECTION" +#define STSTR_DQDON "Degreelessness Mode On" +#define STSTR_DQDOFF "Degreelessness Mode Off" + +#define STSTR_KFAADDED "Very Happy Ammo Added" +#define STSTR_FAADDED "Ammo (no keys) Added" + +#define STSTR_NCON "No Clipping Mode ON" +#define STSTR_NCOFF "No Clipping Mode OFF" + +#define STSTR_BEHOLD "inVuln, Str, Inviso, Rad, Allmap, or Lite-amp" +#define STSTR_BEHOLDX "Power-up Toggled" + +#define STSTR_CHOPPERS "... doesn't suck - GM" +#define STSTR_CLEV "Changing Level..." + +#define STSTR_COMPON "Compatibility Mode On" /* phares */ +#define STSTR_COMPOFF "Compatibility Mode Off" /* phares */ + +/* f_finale.c */ + +#define E1TEXT \ + "Once you beat the big badasses and\n"\ + "clean out the moon base you're supposed\n"\ + "to win, aren't you? Aren't you? Where's\n"\ + "your fat reward and ticket home? What\n"\ + "the hell is this? It's not supposed to\n"\ + "end this way!\n"\ + "\n" \ + "It stinks like rotten meat, but looks\n"\ + "like the lost Deimos base. Looks like\n"\ + "you're stuck on The Shores of Hell.\n"\ + "The only way out is through.\n"\ + "\n"\ + "To continue the DOOM experience, play\n"\ + "The Shores of Hell and its amazing\n"\ + "sequel, Inferno!\n" + + +#define E2TEXT \ + "You've done it! The hideous cyber-\n"\ + "demon lord that ruled the lost Deimos\n"\ + "moon base has been slain and you\n"\ + "are triumphant! But ... where are\n"\ + "you? You clamber to the edge of the\n"\ + "moon and look down to see the awful\n"\ + "truth.\n" \ + "\n"\ + "Deimos floats above Hell itself!\n"\ + "You've never heard of anyone escaping\n"\ + "from Hell, but you'll make the bastards\n"\ + "sorry they ever heard of you! Quickly,\n"\ + "you rappel down to the surface of\n"\ + "Hell.\n"\ + "\n" \ + "Now, it's on to the final chapter of\n"\ + "DOOM! -- Inferno." + + +#define E3TEXT \ + "The loathsome spiderdemon that\n"\ + "masterminded the invasion of the moon\n"\ + "bases and caused so much death has had\n"\ + "its ass kicked for all time.\n"\ + "\n"\ + "A hidden doorway opens and you enter.\n"\ + "You've proven too tough for Hell to\n"\ + "contain, and now Hell at last plays\n"\ + "fair -- for you emerge from the door\n"\ + "to see the green fields of Earth!\n"\ + "Home at last.\n" \ + "\n"\ + "You wonder what's been happening on\n"\ + "Earth while you were battling evil\n"\ + "unleashed. It's good that no Hell-\n"\ + "spawn could have come through that\n"\ + "door with you ..." + + +#define E4TEXT \ + "the spider mastermind must have sent forth\n"\ + "its legions of hellspawn before your\n"\ + "final confrontation with that terrible\n"\ + "beast from hell. but you stepped forward\n"\ + "and brought forth eternal damnation and\n"\ + "suffering upon the horde as a true hero\n"\ + "would in the face of something so evil.\n"\ + "\n"\ + "besides, someone was gonna pay for what\n"\ + "happened to daisy, your pet rabbit.\n"\ + "\n"\ + "but now, you see spread before you more\n"\ + "potential pain and gibbitude as a nation\n"\ + "of demons run amok among our cities.\n"\ + "\n"\ + "next stop, hell on earth!" + + +/* after level 6, put this: */ + +#define C1TEXT \ + "YOU HAVE ENTERED DEEPLY INTO THE INFESTED\n" \ + "STARPORT. BUT SOMETHING IS WRONG. THE\n" \ + "MONSTERS HAVE BROUGHT THEIR OWN REALITY\n" \ + "WITH THEM, AND THE STARPORT'S TECHNOLOGY\n" \ + "IS BEING SUBVERTED BY THEIR PRESENCE.\n" \ + "\n"\ + "AHEAD, YOU SEE AN OUTPOST OF HELL, A\n" \ + "FORTIFIED ZONE. IF YOU CAN GET PAST IT,\n" \ + "YOU CAN PENETRATE INTO THE HAUNTED HEART\n" \ + "OF THE STARBASE AND FIND THE CONTROLLING\n" \ + "SWITCH WHICH HOLDS EARTH'S POPULATION\n" \ + "HOSTAGE." + +/* After level 11, put this: */ + +#define C2TEXT \ + "YOU HAVE WON! YOUR VICTORY HAS ENABLED\n" \ + "HUMANKIND TO EVACUATE EARTH AND ESCAPE\n"\ + "THE NIGHTMARE. NOW YOU ARE THE ONLY\n"\ + "HUMAN LEFT ON THE FACE OF THE PLANET.\n"\ + "CANNIBAL MUTATIONS, CARNIVOROUS ALIENS,\n"\ + "AND EVIL SPIRITS ARE YOUR ONLY NEIGHBORS.\n"\ + "YOU SIT BACK AND WAIT FOR DEATH, CONTENT\n"\ + "THAT YOU HAVE SAVED YOUR SPECIES.\n"\ + "\n"\ + "BUT THEN, EARTH CONTROL BEAMS DOWN A\n"\ + "MESSAGE FROM SPACE: \"SENSORS HAVE LOCATED\n"\ + "THE SOURCE OF THE ALIEN INVASION. IF YOU\n"\ + "GO THERE, YOU MAY BE ABLE TO BLOCK THEIR\n"\ + "ENTRY. THE ALIEN BASE IS IN THE HEART OF\n"\ + "YOUR OWN HOME CITY, NOT FAR FROM THE\n"\ + "STARPORT.\" SLOWLY AND PAINFULLY YOU GET\n"\ + "UP AND RETURN TO THE FRAY." + + +/* After level 20, put this: */ + +#define C3TEXT \ + "YOU ARE AT THE CORRUPT HEART OF THE CITY,\n"\ + "SURROUNDED BY THE CORPSES OF YOUR ENEMIES.\n"\ + "YOU SEE NO WAY TO DESTROY THE CREATURES'\n"\ + "ENTRYWAY ON THIS SIDE, SO YOU CLENCH YOUR\n"\ + "TEETH AND PLUNGE THROUGH IT.\n"\ + "\n"\ + "THERE MUST BE A WAY TO CLOSE IT ON THE\n"\ + "OTHER SIDE. WHAT DO YOU CARE IF YOU'VE\n"\ + "GOT TO GO THROUGH HELL TO GET TO IT?" + + +/* After level 29, put this: */ + +#define C4TEXT \ + "THE HORRENDOUS VISAGE OF THE BIGGEST\n"\ + "DEMON YOU'VE EVER SEEN CRUMBLES BEFORE\n"\ + "YOU, AFTER YOU PUMP YOUR ROCKETS INTO\n"\ + "HIS EXPOSED BRAIN. THE MONSTER SHRIVELS\n"\ + "UP AND DIES, ITS THRASHING LIMBS\n"\ + "DEVASTATING UNTOLD MILES OF HELL'S\n"\ + "SURFACE.\n"\ + "\n"\ + "YOU'VE DONE IT. THE INVASION IS OVER.\n"\ + "EARTH IS SAVED. HELL IS A WRECK. YOU\n"\ + "WONDER WHERE BAD FOLKS WILL GO WHEN THEY\n"\ + "DIE, NOW. WIPING THE SWEAT FROM YOUR\n"\ + "FOREHEAD YOU BEGIN THE LONG TREK BACK\n"\ + "HOME. REBUILDING EARTH OUGHT TO BE A\n"\ + "LOT MORE FUN THAN RUINING IT WAS.\n" + +/* Before level 31, put this: */ + +#define C5TEXT \ + "CONGRATULATIONS, YOU'VE FOUND THE SECRET\n"\ + "LEVEL! LOOKS LIKE IT'S BEEN BUILT BY\n"\ + "HUMANS, RATHER THAN DEMONS. YOU WONDER\n"\ + "WHO THE INMATES OF THIS CORNER OF HELL\n"\ + "WILL BE." + + +/* Before level 32, put this: */ + +#define C6TEXT \ + "CONGRATULATIONS, YOU'VE FOUND THE\n"\ + "SUPER SECRET LEVEL! YOU'D BETTER\n"\ + "BLAZE THROUGH THIS ONE!\n" + +/*** Plutonia ***/ +/* after map 06 */ + +#define P1TEXT \ + "You gloat over the steaming carcass of the\n"\ + "Guardian. With its death, you've wrested\n"\ + "the Accelerator from the stinking claws\n"\ + "of Hell. You relax and glance around the\n"\ + "room. Damn! There was supposed to be at\n"\ + "least one working prototype, but you can't\n"\ + "see it. The demons must have taken it.\n"\ + "\n"\ + "You must find the prototype, or all your\n"\ + "struggles will have been wasted. Keep\n"\ + "moving, keep fighting, keep killing.\n"\ + "Oh yes, keep living, too." + + +/* after map 11 */ + +#define P2TEXT \ + "Even the deadly Arch-Vile labyrinth could\n"\ + "not stop you, and you've gotten to the\n"\ + "prototype Accelerator which is soon\n"\ + "efficiently and permanently deactivated.\n"\ + "\n"\ + "You're good at that kind of thing." + + +/* after map 20 */ + +#define P3TEXT \ + "You've bashed and battered your way into\n"\ + "the heart of the devil-hive. Time for a\n"\ + "Search-and-Destroy mission, aimed at the\n"\ + "Gatekeeper, whose foul offspring is\n"\ + "cascading to Earth. Yeah, he's bad. But\n"\ + "you know who's worse!\n"\ + "\n"\ + "Grinning evilly, you check your gear, and\n"\ + "get ready to give the bastard a little Hell\n"\ + "of your own making!" + +/* after map 30 */ + +#define P4TEXT \ + "The Gatekeeper's evil face is splattered\n"\ + "all over the place. As its tattered corpse\n"\ + "collapses, an inverted Gate forms and\n"\ + "sucks down the shards of the last\n"\ + "prototype Accelerator, not to mention the\n"\ + "few remaining demons. You're done. Hell\n"\ + "has gone back to pounding bad dead folks \n"\ + "instead of good live ones. Remember to\n"\ + "tell your grandkids to put a rocket\n"\ + "launcher in your coffin. If you go to Hell\n"\ + "when you die, you'll need it for some\n"\ + "final cleaning-up ..." + +/* before map 31 */ + +#define P5TEXT \ + "You've found the second-hardest level we\n"\ + "got. Hope you have a saved game a level or\n"\ + "two previous. If not, be prepared to die\n"\ + "aplenty. For master marines only." + +/* before map 32 */ + +#define P6TEXT \ + "Betcha wondered just what WAS the hardest\n"\ + "level we had ready for ya? Now you know.\n"\ + "No one gets out alive." + +/*** TNT: Evilution ***/ + +#define T1TEXT \ + "You've fought your way out of the infested\n"\ + "experimental labs. It seems that UAC has\n"\ + "once again gulped it down. With their\n"\ + "high turnover, it must be hard for poor\n"\ + "old UAC to buy corporate health insurance\n"\ + "nowadays..\n"\ + "\n"\ + "Ahead lies the military complex, now\n"\ + "swarming with diseased horrors hot to get\n"\ + "their teeth into you. With luck, the\n"\ + "complex still has some warlike ordnance\n"\ + "laying around." + + +#define T2TEXT \ + "You hear the grinding of heavy machinery\n"\ + "ahead. You sure hope they're not stamping\n"\ + "out new hellspawn, but you're ready to\n"\ + "ream out a whole herd if you have to.\n"\ + "They might be planning a blood feast, but\n"\ + "you feel about as mean as two thousand\n"\ + "maniacs packed into one mad killer.\n"\ + "\n"\ + "You don't plan to go down easy." + + +#define T3TEXT \ + "The vista opening ahead looks real damn\n"\ + "familiar. Smells familiar, too -- like\n"\ + "fried excrement. You didn't like this\n"\ + "place before, and you sure as hell ain't\n"\ + "planning to like it now. The more you\n"\ + "brood on it, the madder you get.\n"\ + "Hefting your gun, an evil grin trickles\n"\ + "onto your face. Time to take some names." + +#define T4TEXT \ + "Suddenly, all is silent, from one horizon\n"\ + "to the other. The agonizing echo of Hell\n"\ + "fades away, the nightmare sky turns to\n"\ + "blue, the heaps of monster corpses start \n"\ + "to evaporate along with the evil stench \n"\ + "that filled the air. Jeeze, maybe you've\n"\ + "done it. Have you really won?\n"\ + "\n"\ + "Something rumbles in the distance.\n"\ + "A blue light begins to glow inside the\n"\ + "ruined skull of the demon-spitter." + + +#define T5TEXT \ + "What now? Looks totally different. Kind\n"\ + "of like King Tut's condo. Well,\n"\ + "whatever's here can't be any worse\n"\ + "than usual. Can it? Or maybe it's best\n"\ + "to let sleeping gods lie.." + + +#define T6TEXT \ + "Time for a vacation. You've burst the\n"\ + "bowels of hell and by golly you're ready\n"\ + "for a break. You mutter to yourself,\n"\ + "Maybe someone else can kick Hell's ass\n"\ + "next time around. Ahead lies a quiet town,\n"\ + "with peaceful flowing water, quaint\n"\ + "buildings, and presumably no Hellspawn.\n"\ + "\n"\ + "As you step off the transport, you hear\n"\ + "the stomp of a cyberdemon's iron shoe." + + + +/* + * Character cast strings F_FINALE.C + */ +#define CC_ZOMBIE "ZOMBIEMAN" +#define CC_SHOTGUN "SHOTGUN GUY" +#define CC_HEAVY "HEAVY WEAPON DUDE" +#define CC_IMP "IMP" +#define CC_DEMON "DEMON" +#define CC_LOST "LOST SOUL" +#define CC_CACO "CACODEMON" +#define CC_HELL "HELL KNIGHT" +#define CC_BARON "BARON OF HELL" +#define CC_ARACH "ARACHNOTRON" +#define CC_PAIN "PAIN ELEMENTAL" +#define CC_REVEN "REVENANT" +#define CC_MANCU "MANCUBUS" +#define CC_ARCH "ARCH-VILE" +#define CC_SPIDER "THE SPIDER MASTERMIND" +#define CC_CYBER "THE CYBERDEMON" +#define CC_HERO "OUR HERO" + + +#endif diff --git a/src/d_event.h b/src/d_event.h new file mode 100644 index 0000000..da5e702 --- /dev/null +++ b/src/d_event.h @@ -0,0 +1,125 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Event information structures. + * + *-----------------------------------------------------------------------------*/ + + +#ifndef __D_EVENT__ +#define __D_EVENT__ + + +#include "doomtype.h" + + +// +// Event handling. +// + +// Input event types. +typedef enum +{ + ev_keydown, + ev_keyup, + ev_mouse, + ev_joystick +} evtype_t; + +// Event structure. +typedef struct +{ + evtype_t type; + int data1; // keys / mouse/joystick buttons + int data2; // mouse/joystick x move + int data3; // mouse/joystick y move +} event_t; + + +typedef enum +{ + ga_nothing, + ga_loadlevel, + ga_newgame, + ga_loadgame, + ga_savegame, + ga_playdemo, + ga_completed, + ga_victory, + ga_worlddone, +} gameaction_t; + + + +// +// Button/action code definitions. +// +typedef enum +{ + // Press "Fire". + BT_ATTACK = 1, + + // Use button, to open doors, activate switches. + BT_USE = 2, + + // Flag: game events, not really buttons. + BT_SPECIAL = 128, + BT_SPECIALMASK = 3, + + // Flag, weapon change pending. + // If true, the next 4 bits hold weapon num. + BT_CHANGE = 4, + + // The 4bit weapon mask and shift, convenience. +//BT_WEAPONMASK = (8+16+32), + BT_WEAPONMASK = (8+16+32+64), // extended to pick up SSG // phares + BT_WEAPONSHIFT = 3, + + // Special events + BTS_LOADGAME = 0, // Loads a game + // Pause the game. + BTS_PAUSE = 1, + // Save the game at each console. + BTS_SAVEGAME = 2, + BTS_RESTARTLEVEL= 3, // Restarts the current level + + // Savegame slot numbers occupy the second byte of buttons. + BTS_SAVEMASK = (4+8+16), + BTS_SAVESHIFT = 2, + +} buttoncode_t; + + +// +// GLOBAL VARIABLES +// + +extern gameaction_t gameaction; + +#endif diff --git a/src/d_items.c b/src/d_items.c new file mode 100644 index 0000000..5adc28d --- /dev/null +++ b/src/d_items.c @@ -0,0 +1,140 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Something to do with weapon sprite frames. Don't ask me. + * + *----------------------------------------------------------------------------- + */ + +// We are referring to sprite numbers. +#include "doomtype.h" +#include "info.h" + +#ifdef __GNUG__ +#pragma implementation "d_items.h" +#endif +#include "d_items.h" + + +// +// PSPRITE ACTIONS for waepons. +// This struct controls the weapon animations. +// +// Each entry is: +// ammo/amunition type +// upstate +// downstate +// readystate +// atkstate, i.e. attack/fire/hit frame +// flashstate, muzzle flash +// +weaponinfo_t weaponinfo[NUMWEAPONS] = +{ + { + // fist + am_noammo, + S_PUNCHUP, + S_PUNCHDOWN, + S_PUNCH, + S_PUNCH1, + S_NULL + }, + { + // pistol + am_clip, + S_PISTOLUP, + S_PISTOLDOWN, + S_PISTOL, + S_PISTOL1, + S_PISTOLFLASH + }, + { + // shotgun + am_shell, + S_SGUNUP, + S_SGUNDOWN, + S_SGUN, + S_SGUN1, + S_SGUNFLASH1 + }, + { + // chaingun + am_clip, + S_CHAINUP, + S_CHAINDOWN, + S_CHAIN, + S_CHAIN1, + S_CHAINFLASH1 + }, + { + // missile launcher + am_misl, + S_MISSILEUP, + S_MISSILEDOWN, + S_MISSILE, + S_MISSILE1, + S_MISSILEFLASH1 + }, + { + // plasma rifle + am_cell, + S_PLASMAUP, + S_PLASMADOWN, + S_PLASMA, + S_PLASMA1, + S_PLASMAFLASH1 + }, + { + // bfg 9000 + am_cell, + S_BFGUP, + S_BFGDOWN, + S_BFG, + S_BFG1, + S_BFGFLASH1 + }, + { + // chainsaw + am_noammo, + S_SAWUP, + S_SAWDOWN, + S_SAW, + S_SAW1, + S_NULL + }, + { + // super shotgun + am_shell, + S_DSGUNUP, + S_DSGUNDOWN, + S_DSGUN, + S_DSGUN1, + S_DSGUNFLASH1 + }, +}; diff --git a/src/d_items.h b/src/d_items.h new file mode 100644 index 0000000..8da4df2 --- /dev/null +++ b/src/d_items.h @@ -0,0 +1,59 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Items: key cards, artifacts, weapon, ammunition. + * + *-----------------------------------------------------------------------------*/ + + +#ifndef __D_ITEMS__ +#define __D_ITEMS__ + +#include "doomdef.h" + +#ifdef __GNUG__ +#pragma interface +#endif + + +/* Weapon info: sprite frames, ammunition use. */ +typedef struct +{ + ammotype_t ammo; + int upstate; + int downstate; + int readystate; + int atkstate; + int flashstate; + +} weaponinfo_t; + +extern weaponinfo_t weaponinfo[NUMWEAPONS]; + +#endif diff --git a/src/d_main.c b/src/d_main.c new file mode 100644 index 0000000..6d8493e --- /dev/null +++ b/src/d_main.c @@ -0,0 +1,1725 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2004 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * DOOM main program (D_DoomMain) and game loop (D_DoomLoop), + * plus functions to determine game mode (shareware, registered), + * parse command line parameters, configure game parameters (turbo), + * and call the startup functions. + * + *----------------------------------------------------------------------------- + */ + + +#ifdef _MSC_VER +#define F_OK 0 /* Check for file existence */ +#define W_OK 2 /* Check for write permission */ +#define R_OK 4 /* Check for read permission */ +#include +#include +#else +#include +#endif +#include +#include +#include + +#include "doomdef.h" +#include "doomtype.h" +#include "doomstat.h" +#include "d_net.h" +#include "dstrings.h" +#include "sounds.h" +#include "z_zone.h" +#include "w_wad.h" +#include "s_sound.h" +#include "v_video.h" +#include "f_finale.h" +#include "f_wipe.h" +#include "m_argv.h" +#include "m_misc.h" +#include "m_menu.h" +#include "p_checksum.h" +#include "i_main.h" +#include "i_system.h" +#include "i_sound.h" +#include "i_video.h" +#include "g_game.h" +#include "hu_stuff.h" +#include "wi_stuff.h" +#include "st_stuff.h" +#include "am_map.h" +#include "p_setup.h" +#include "r_draw.h" +#include "r_main.h" +#include "r_fps.h" +#include "d_main.h" +#include "d_deh.h" // Ty 04/08/98 - Externalizations +#include "lprintf.h" // jff 08/03/98 - declaration of lprintf +#include "am_map.h" + +void GetFirstMap(int *ep, int *map); // Ty 08/29/98 - add "-warp x" functionality +static void D_PageDrawer(void); + +// CPhipps - removed wadfiles[] stuff + +boolean devparm; // started game with -devparm + +// jff 1/24/98 add new versions of these variables to remember command line +boolean clnomonsters; // checkparm of -nomonsters +boolean clrespawnparm; // checkparm of -respawn +boolean clfastparm; // checkparm of -fast +// jff 1/24/98 end definition of command line version of play mode switches + +boolean nomonsters; // working -nomonsters +boolean respawnparm; // working -respawn +boolean fastparm; // working -fast + +boolean singletics = false; // debug flag to cancel adaptiveness + +//jff 1/22/98 parms for disabling music and sound +boolean nosfxparm; +boolean nomusicparm; + +//jff 4/18/98 +extern boolean inhelpscreens; + +skill_t startskill; +int startepisode; +int startmap; +boolean autostart; +FILE *debugfile; +int ffmap; + +boolean advancedemo; + +char wadfile[PATH_MAX+1]; // primary wad file +char mapdir[PATH_MAX+1]; // directory of development maps +char baseiwad[PATH_MAX+1]; // jff 3/23/98: iwad directory +char basesavegame[PATH_MAX+1]; // killough 2/16/98: savegame directory + +//jff 4/19/98 list of standard IWAD names +const char *const standard_iwads[]= +{ + "doom2f.wad", + "doom2.wad", + "plutonia.wad", + "tnt.wad", + "doom.wad", + "doom1.wad", + "doomu.wad", /* CPhipps - alow doomu.wad */ + "freedoom.wad", /* wart@kobold.org: added freedoom for Fedora Extras */ +}; +static const int nstandard_iwads = sizeof standard_iwads/sizeof*standard_iwads; + +/* + * D_PostEvent - Event handling + * + * Called by I/O functions when an event is received. + * Try event handlers for each code area in turn. + * cph - in the true spirit of the Boom source, let the + * short ciruit operator madness begin! + */ + +void D_PostEvent(event_t *ev) +{ + /* cph - suppress all input events at game start + * FIXME: This is a lousy kludge */ + if (gametic < 3) return; + M_Responder(ev) || + (gamestate == GS_LEVEL && ( + HU_Responder(ev) || + ST_Responder(ev) || + AM_Responder(ev) + ) + ) || + G_Responder(ev); +} + +// +// D_Wipe +// +// CPhipps - moved the screen wipe code from D_Display to here +// The screens to wipe between are already stored, this just does the timing +// and screen updating + +static void D_Wipe(void) +{ + boolean done; + int wipestart = I_GetTime () - 1; + + do + { + int nowtime, tics; + do + { + I_uSleep(5000); // CPhipps - don't thrash cpu in this loop + nowtime = I_GetTime(); + tics = nowtime - wipestart; + } + while (!tics); + wipestart = nowtime; + done = wipe_ScreenWipe(tics); + I_UpdateNoBlit(); + M_Drawer(); // menu is drawn even on top of wipes + I_FinishUpdate(); // page flip or blit buffer + } + while (!done); +} + +// +// D_Display +// draw current display, possibly wiping it from the previous +// + +// wipegamestate can be set to -1 to force a wipe on the next draw +gamestate_t wipegamestate = GS_DEMOSCREEN; +extern boolean setsizeneeded; +extern int showMessages; + +void D_Display (void) +{ + static boolean inhelpscreensstate = false; + static boolean isborderstate = false; + static boolean borderwillneedredraw = false; + static gamestate_t oldgamestate = -1; + boolean wipe; + boolean viewactive = false, isborder = false; + + if (nodrawers) // for comparative timing / profiling + return; + + if (!I_StartDisplay()) + return; + + // save the current screen if about to wipe + if ((wipe = gamestate != wipegamestate) && (V_GetMode() != VID_MODEGL)) + wipe_StartScreen(); + + if (gamestate != GS_LEVEL) { // Not a level + switch (oldgamestate) { + case -1: + case GS_LEVEL: + V_SetPalette(0); // cph - use default (basic) palette + default: + break; + } + + switch (gamestate) { + case GS_INTERMISSION: + WI_Drawer(); + break; + case GS_FINALE: + F_Drawer(); + break; + case GS_DEMOSCREEN: + D_PageDrawer(); + break; + default: + break; + } + } else if (gametic != basetic) { // In a level + boolean redrawborderstuff; + + HU_Erase(); + + if (setsizeneeded) { // change the view size if needed + R_ExecuteSetViewSize(); + oldgamestate = -1; // force background redraw + } + + // Work out if the player view is visible, and if there is a border + viewactive = (!(automapmode & am_active) || (automapmode & am_overlay)) && !inhelpscreens; + isborder = viewactive ? (viewheight != SCREENHEIGHT) : (!inhelpscreens && (automapmode & am_active)); + + if (oldgamestate != GS_LEVEL) { + R_FillBackScreen (); // draw the pattern into the back screen + redrawborderstuff = isborder; + } else { + // CPhipps - + // If there is a border, and either there was no border last time, + // or the border might need refreshing, then redraw it. + redrawborderstuff = isborder && (!isborderstate || borderwillneedredraw); + // The border may need redrawing next time if the border surrounds the screen, + // and there is a menu being displayed + borderwillneedredraw = menuactive && isborder && viewactive && (viewwidth != SCREENWIDTH); + } + if (redrawborderstuff || (V_GetMode() == VID_MODEGL)) + R_DrawViewBorder(); + + // Now do the drawing + if (viewactive) + R_RenderPlayerView (&players[displayplayer]); + if (automapmode & am_active) + AM_Drawer(); + ST_Drawer((viewheight != SCREENHEIGHT) || ((automapmode & am_active) && !(automapmode & am_overlay)), redrawborderstuff); + if (V_GetMode() != VID_MODEGL) + R_DrawViewBorder(); + HU_Drawer(); + } + + inhelpscreensstate = inhelpscreens; + isborderstate = isborder; + oldgamestate = wipegamestate = gamestate; + + // draw pause pic + if (paused) { + // Simplified the "logic" here and no need for x-coord caching - POPE + V_DrawNamePatch((320 - V_NamePatchWidth("M_PAUSE"))/2, 4, + 0, "M_PAUSE", CR_DEFAULT, VPT_STRETCH); + } + + // menus go directly to the screen + M_Drawer(); // menu is drawn even on top of everything +#ifdef HAVE_NET + NetUpdate(); // send out any new accumulation +#else + D_BuildNewTiccmds(); +#endif + + // normal update + if (!wipe || (V_GetMode() == VID_MODEGL)) + I_FinishUpdate (); // page flip or blit buffer + else { + // wipe update + wipe_EndScreen(); + D_Wipe(); + } + + I_EndDisplay(); + + //e6y: don't thrash cpu during pausing + if (paused) { + I_uSleep(1000); + } +} + +// CPhipps - Auto screenshot Variables + +static int auto_shot_count, auto_shot_time; +static const char *auto_shot_fname; + +// +// D_DoomLoop() +// +// Not a globally visible function, +// just included for source reference, +// called by D_DoomMain, never exits. +// Manages timing and IO, +// calls all ?_Responder, ?_Ticker, and ?_Drawer, +// calls I_GetTime, I_StartFrame, and I_StartTic +// + +static void D_DoomLoop(void) +{ + for (;;) + { + WasRenderedInTryRunTics = false; + // frame syncronous IO operations + I_StartFrame (); + + if (ffmap == gamemap) ffmap = 0; + + // process one or more tics + if (singletics) + { + I_StartTic (); + G_BuildTiccmd (&netcmds[consoleplayer][maketic%BACKUPTICS]); + if (advancedemo) + D_DoAdvanceDemo (); + M_Ticker (); + G_Ticker (); + P_Checksum(gametic); + gametic++; + maketic++; + } + else + TryRunTics (); // will run at least one tic + + // killough 3/16/98: change consoleplayer to displayplayer + if (players[displayplayer].mo) // cph 2002/08/10 + S_UpdateSounds(players[displayplayer].mo);// move positional sounds + + if (V_GetMode() == VID_MODEGL ? + !movement_smooth || !WasRenderedInTryRunTics : + !movement_smooth || !WasRenderedInTryRunTics || gamestate != wipegamestate + ) + { + // Update display, next frame, with current state. + D_Display(); + } + + // CPhipps - auto screenshot + if (auto_shot_fname && !--auto_shot_count) { + auto_shot_count = auto_shot_time; + M_DoScreenShot(auto_shot_fname); + } + } +} + +// +// DEMO LOOP +// + +static int demosequence; // killough 5/2/98: made static +static int pagetic; +static const char *pagename; // CPhipps - const + +// +// D_PageTicker +// Handles timing for warped projection +// +void D_PageTicker(void) +{ + if (--pagetic < 0) + D_AdvanceDemo(); +} + +// +// D_PageDrawer +// +static void D_PageDrawer(void) +{ + // proff/nicolas 09/14/98 -- now stretchs bitmaps to fullscreen! + // CPhipps - updated for new patch drawing + // proff - added M_DrawCredits + if (pagename) + { + V_DrawNamePatch(0, 0, 0, pagename, CR_DEFAULT, VPT_STRETCH); + } + else + M_DrawCredits(); +} + +// +// D_AdvanceDemo +// Called after each demo or intro demosequence finishes +// +void D_AdvanceDemo (void) +{ + advancedemo = true; +} + +/* killough 11/98: functions to perform demo sequences + * cphipps 10/99: constness fixes + */ + +static void D_SetPageName(const char *name) +{ + pagename = name; +} + +static void D_DrawTitle1(const char *name) +{ + S_StartMusic(mus_intro); + pagetic = (TICRATE*170)/35; + D_SetPageName(name); +} + +static void D_DrawTitle2(const char *name) +{ + S_StartMusic(mus_dm2ttl); + D_SetPageName(name); +} + +/* killough 11/98: tabulate demo sequences + */ + +static struct +{ + void (*func)(const char *); + const char *name; +} const demostates[][4] = + { + { + {D_DrawTitle1, "TITLEPIC"}, + {D_DrawTitle1, "TITLEPIC"}, + {D_DrawTitle2, "TITLEPIC"}, + {D_DrawTitle1, "TITLEPIC"}, + }, + + { + {G_DeferedPlayDemo, "demo1"}, + {G_DeferedPlayDemo, "demo1"}, + {G_DeferedPlayDemo, "demo1"}, + {G_DeferedPlayDemo, "demo1"}, + }, + { + {D_SetPageName, NULL}, + {D_SetPageName, NULL}, + {D_SetPageName, NULL}, + {D_SetPageName, NULL}, + }, + + { + {G_DeferedPlayDemo, "demo2"}, + {G_DeferedPlayDemo, "demo2"}, + {G_DeferedPlayDemo, "demo2"}, + {G_DeferedPlayDemo, "demo2"}, + }, + + { + {D_SetPageName, "HELP2"}, + {D_SetPageName, "HELP2"}, + {D_SetPageName, "CREDIT"}, + {D_DrawTitle1, "TITLEPIC"}, + }, + + { + {G_DeferedPlayDemo, "demo3"}, + {G_DeferedPlayDemo, "demo3"}, + {G_DeferedPlayDemo, "demo3"}, + {G_DeferedPlayDemo, "demo3"}, + }, + + { + {NULL}, + {NULL}, + {NULL}, + {D_SetPageName, "CREDIT"}, + }, + + { + {NULL}, + {NULL}, + {NULL}, + {G_DeferedPlayDemo, "demo4"}, + }, + + { + {NULL}, + {NULL}, + {NULL}, + {NULL}, + } + }; + +/* + * This cycles through the demo sequences. + * killough 11/98: made table-driven + */ + +void D_DoAdvanceDemo(void) +{ + players[consoleplayer].playerstate = PST_LIVE; /* not reborn */ + advancedemo = usergame = paused = false; + gameaction = ga_nothing; + + pagetic = TICRATE * 11; /* killough 11/98: default behavior */ + gamestate = GS_DEMOSCREEN; + + if (netgame && !demoplayback) { + demosequence = 0; + } else + if (!demostates[++demosequence][gamemode].func) + demosequence = 0; + demostates[demosequence][gamemode].func + (demostates[demosequence][gamemode].name); +} + +// +// D_StartTitle +// +void D_StartTitle (void) +{ + gameaction = ga_nothing; + demosequence = -1; + D_AdvanceDemo(); +} + +// +// D_AddFile +// +// Rewritten by Lee Killough +// +// Ty 08/29/98 - add source parm to indicate where this came from +// CPhipps - static, const char* parameter +// - source is an enum +// - modified to allocate & use new wadfiles array +void D_AddFile (const char *file, wad_source_t source) +{ + char *gwa_filename=NULL; + + wadfiles = realloc(wadfiles, sizeof(*wadfiles)*(numwadfiles+1)); + wadfiles[numwadfiles].name = + AddDefaultExtension(strcpy(malloc(strlen(file)+5), file), ".wad"); + wadfiles[numwadfiles].src = source; // Ty 08/29/98 + numwadfiles++; + // proff: automatically try to add the gwa files + // proff - moved from w_wad.c + gwa_filename=AddDefaultExtension(strcpy(malloc(strlen(file)+5), file), ".wad"); + if (strlen(gwa_filename)>4) + if (!strcasecmp(gwa_filename+(strlen(gwa_filename)-4),".wad")) + { + char *ext; + ext = &gwa_filename[strlen(gwa_filename)-4]; + ext[1] = 'g'; ext[2] = 'w'; ext[3] = 'a'; + wadfiles = realloc(wadfiles, sizeof(*wadfiles)*(numwadfiles+1)); + wadfiles[numwadfiles].name = gwa_filename; + wadfiles[numwadfiles].src = source; // Ty 08/29/98 + numwadfiles++; + } +} + +// killough 10/98: support -dehout filename +// cph - made const, don't cache results +static const char *D_dehout(void) +{ + int p = M_CheckParm("-dehout"); + if (!p) + p = M_CheckParm("-bexout"); + return (p && ++p < myargc ? myargv[p] : NULL); +} + +// +// CheckIWAD +// +// Verify a file is indeed tagged as an IWAD +// Scan its lumps for levelnames and return gamemode as indicated +// Detect missing wolf levels in DOOM II +// +// The filename to check is passed in iwadname, the gamemode detected is +// returned in gmode, hassec returns the presence of secret levels +// +// jff 4/19/98 Add routine to test IWAD for validity and determine +// the gamemode from it. Also note if DOOM II, whether secret levels exist +// CPhipps - const char* for iwadname, made static +static void CheckIWAD(const char *iwadname,GameMode_t *gmode,boolean *hassec) +{ + if ( !access (iwadname,R_OK) ) + { + int ud=0,rg=0,sw=0,cm=0,sc=0; + FILE* fp; + + // Identify IWAD correctly + if ((fp = fopen(iwadname, "rb"))) + { + wadinfo_t header; + + // read IWAD header + if (fread(&header, sizeof(header), 1, fp) == 1 && !strncmp(header.identification, "IWAD", 4)) + { + size_t length; + filelump_t *fileinfo; + + // read IWAD directory + header.numlumps = LONG(header.numlumps); + header.infotableofs = LONG(header.infotableofs); + length = header.numlumps; + fileinfo = malloc(length*sizeof(filelump_t)); + if (fseek (fp, header.infotableofs, SEEK_SET) || + fread (fileinfo, sizeof(filelump_t), length, fp) != length || + fclose(fp)) + I_Error("CheckIWAD: failed to read directory %s",iwadname); + + // scan directory for levelname lumps + while (length--) + if (fileinfo[length].name[0] == 'E' && + fileinfo[length].name[2] == 'M' && + fileinfo[length].name[4] == 0) + { + if (fileinfo[length].name[1] == '4') + ++ud; + else if (fileinfo[length].name[1] == '3') + ++rg; + else if (fileinfo[length].name[1] == '2') + ++rg; + else if (fileinfo[length].name[1] == '1') + ++sw; + } + else if (fileinfo[length].name[0] == 'M' && + fileinfo[length].name[1] == 'A' && + fileinfo[length].name[2] == 'P' && + fileinfo[length].name[5] == 0) + { + ++cm; + if (fileinfo[length].name[3] == '3') + if (fileinfo[length].name[4] == '1' || + fileinfo[length].name[4] == '2') + ++sc; + } + + free(fileinfo); + } + else // missing IWAD tag in header + I_Error("CheckIWAD: IWAD tag %s not present", iwadname); + } + else // error from open call + I_Error("CheckIWAD: Can't open IWAD %s", iwadname); + + // Determine game mode from levels present + // Must be a full set for whichever mode is present + // Lack of wolf-3d levels also detected here + + *gmode = indetermined; + *hassec = false; + if (cm>=30) + { + *gmode = commercial; + *hassec = sc>=2; + } + else if (ud>=9) + *gmode = retail; + else if (rg>=18) + *gmode = registered; + else if (sw>=9) + *gmode = shareware; + } + else // error from access call + I_Error("CheckIWAD: IWAD %s not readable", iwadname); +} + + + +// NormalizeSlashes +// +// Remove trailing slashes, translate backslashes to slashes +// The string to normalize is passed and returned in str +// +// jff 4/19/98 Make killoughs slash fixer a subroutine +// +static void NormalizeSlashes(char *str) +{ + int l; + + // killough 1/18/98: Neater / \ handling. + // Remove trailing / or \ to prevent // /\ \/ \\, and change \ to / + + if (!str || !(l = strlen(str))) + return; + if (str[--l]=='/' || str[l]=='\\') // killough 1/18/98 + str[l]=0; + while (l--) + if (str[l]=='\\') + str[l]='/'; +} + +/* + * FindIWADFIle + * + * Search for one of the standard IWADs + * CPhipps - static, proper prototype + * - 12/1999 - rewritten to use I_FindFile + */ +static char *FindIWADFile(void) +{ + int i; + char * iwad = NULL; + + i = M_CheckParm("-iwad"); + if (i && (++i < myargc)) { + iwad = I_FindFile(myargv[i], ".wad"); + } else { + for (i=0; !iwad && i PATH_MAX-12) p = NULL; + + strcpy(basesavegame,(p == NULL) ? I_DoomExeDir() : p); + } + if ((i=M_CheckParm("-save")) && i=10 && !strnicmp(iwad+i-10,"doom2f.wad",10)) + language=french; + else if (i>=7 && !strnicmp(iwad+i-7,"tnt.wad",7)) + gamemission = pack_tnt; + else if (i>=12 && !strnicmp(iwad+i-12,"plutonia.wad",12)) + gamemission = pack_plut; + break; + default: + gamemission = none; + break; + } + if (gamemode == indetermined) + //jff 9/3/98 use logical output routine + lprintf(LO_WARN,"Unknown Game Version, may not work\n"); + D_AddFile(iwad,source_iwad); + free(iwad); + } + else + I_Error("IdentifyVersion: IWAD not found\n"); +} + + + +// killough 5/3/98: old code removed +// +// Find a Response File +// + +#define MAXARGVS 100 + +static void FindResponseFile (void) +{ + int i; + + for (i = 1;i < myargc;i++) + if (myargv[i][0] == '@') + { + int size; + int index; + int indexinfile; + byte *file = NULL; + const char **moreargs = malloc(myargc * sizeof(const char*)); + const char **newargv; + // proff 04/05/2000: Added for searching responsefile + char fname[PATH_MAX+1]; + + strcpy(fname,&myargv[i][1]); + AddDefaultExtension(fname,".rsp"); + + // READ THE RESPONSE FILE INTO MEMORY + // proff 04/05/2000: changed for searching responsefile + // cph 2002/08/09 - use M_ReadFile for simplicity + size = M_ReadFile(fname, &file); + // proff 04/05/2000: Added for searching responsefile + if (size < 0) + { + strcat(strcpy(fname,I_DoomExeDir()),&myargv[i][1]); + AddDefaultExtension(fname,".rsp"); + size = M_ReadFile(fname, &file); + } + if (size < 0) + { + /* proff 04/05/2000: Changed from LO_FATAL + * proff 04/05/2000: Simply removed the exit(1); + * cph - made fatal, don't drop through and SEGV + */ + I_Error("No such response file: %s",fname); + } + //jff 9/3/98 use logical output routine + lprintf(LO_CONFIRM,"Found response file %s\n",fname); + // proff 04/05/2000: Added check for empty rsp file + if (size<=0) + { + int k; + lprintf(LO_ERROR,"\nResponse file empty!\n"); + + newargv = calloc(sizeof(char *),MAXARGVS); + newargv[0] = myargv[0]; + for (k = 1,index = 1;k < myargc;k++) + { + if (i!=k) + newargv[index++] = myargv[k]; + } + myargc = index; myargv = newargv; + return; + } + + // KEEP ALL CMDLINE ARGS FOLLOWING @RESPONSEFILE ARG + memcpy((void *)moreargs,&myargv[i+1],(index = myargc - i - 1) * sizeof(myargv[0])); + + { + const char *firstargv = myargv[0]; + newargv = calloc(sizeof(char *),MAXARGVS); + newargv[0] = firstargv; + } + + { + byte *infile = file; + indexinfile = 0; + indexinfile++; // SKIP PAST ARGV[0] (KEEP IT) + do { + while (size > 0 && isspace(*infile)) { infile++; size--; } + if (size > 0) { + char *s = malloc(size+1); + char *p = s; + int quoted = 0; + + while (size > 0) { + // Whitespace terminates the token unless quoted + if (!quoted && isspace(*infile)) break; + if (*infile == '\"') { + // Quotes are removed but remembered + infile++; size--; quoted ^= 1; + } else { + *p++ = *infile++; size--; + } + } + if (quoted) I_Error("Runaway quoted string in response file"); + + // Terminate string, realloc and add to argv + *p = 0; + newargv[indexinfile++] = realloc(s,strlen(s)+1); + } + } while(size > 0); + } + free(file); + + memcpy((void *)&newargv[indexinfile],moreargs,index*sizeof(moreargs[0])); + free((void *)moreargs); + + myargc = indexinfile+index; myargv = newargv; + + // DISPLAY ARGS + //jff 9/3/98 use logical output routine + lprintf(LO_CONFIRM,"%d command-line args:\n",myargc); + for (index=1;index 0) + { + tmyargv[tmyargc++] = strdup("-file"); // put the switch in + for (i=0;i 0) + { + tmyargv[tmyargc++] = strdup("-deh"); + for (i=0;i 0) + { + tmyargv[tmyargc++] = strdup("-playdemo"); + for (i=0;i 400) + scale = 400; + //jff 9/3/98 use logical output routine + lprintf (LO_CONFIRM,"turbo scale: %i%%\n",scale); + forwardmove[0] = forwardmove[0]*scale/100; + forwardmove[1] = forwardmove[1]*scale/100; + sidemove[0] = sidemove[0]*scale/100; + sidemove[1] = sidemove[1]*scale/100; + } + + modifiedgame = false; + + // get skill / episode / map from parms + + startskill = sk_none; // jff 3/24/98 was sk_medium, just note not picked + startepisode = 1; + startmap = 1; + autostart = false; + + if ((p = M_CheckParm ("-skill")) && p < myargc-1) + { + startskill = myargv[p+1][0]-'1'; + autostart = true; + } + + if ((p = M_CheckParm ("-episode")) && p < myargc-1) + { + startepisode = myargv[p+1][0]-'0'; + startmap = 1; + autostart = true; + } + + if ((p = M_CheckParm ("-timer")) && p < myargc-1 && deathmatch) + { + int time = atoi(myargv[p+1]); + //jff 9/3/98 use logical output routine + lprintf(LO_CONFIRM,"Levels will end after %d minute%s.\n", time, time>1 ? "s" : ""); + } + + if ((p = M_CheckParm ("-avg")) && p < myargc-1 && deathmatch) + //jff 9/3/98 use logical output routine + lprintf(LO_CONFIRM,"Austin Virtual Gaming: Levels will end after 20 minutes\n"); + + if ((p = M_CheckParm ("-warp")) || // killough 5/2/98 + (p = M_CheckParm ("-wart"))) + // Ty 08/29/98 - moved this check later so we can have -warp alone: && p < myargc-1) + { + startmap = 0; // Ty 08/29/98 - allow "-warp x" to go to first map in wad(s) + autostart = true; // Ty 08/29/98 - move outside the decision tree + if (gamemode == commercial) + { + if (p < myargc-1) + startmap = atoi(myargv[p+1]); // Ty 08/29/98 - add test if last parm + } + else // 1/25/98 killough: fix -warp xxx from crashing Doom 1 / UD + { + if (p < myargc-2) + { + startepisode = atoi(myargv[++p]); + startmap = atoi(myargv[p+1]); + } + } + } + // Ty 08/29/98 - later we'll check for startmap=0 and autostart=true + // as a special case that -warp * was used. Actually -warp with any + // non-numeric will do that but we'll only document "*" + + //jff 1/22/98 add command line parms to disable sound and music + { + int nosound = M_CheckParm("-nosound"); + nomusicparm = nosound || M_CheckParm("-nomusic"); + nosfxparm = nosound || M_CheckParm("-nosfx"); + } + //jff end of sound/music command line parms + + // killough 3/2/98: allow -nodraw -noblit generally + nodrawers = M_CheckParm ("-nodraw"); + noblit = M_CheckParm ("-noblit"); + + //proff 11/22/98: Added setting of viewangleoffset + p = M_CheckParm("-viewangle"); + if (p) + { + viewangleoffset = atoi(myargv[p+1]); + viewangleoffset = viewangleoffset<0 ? 0 : (viewangleoffset>7 ? 7 : viewangleoffset); + viewangleoffset = (8-viewangleoffset) * ANG45; + } + + // init subsystems + + G_ReloadDefaults(); // killough 3/4/98: set defaults just loaded. + // jff 3/24/98 this sets startskill if it was -1 + + // Video stuff + if ((p = M_CheckParm("-width"))) + if (myargv[p+1]) + desired_screenwidth = atoi(myargv[p+1]); + + if ((p = M_CheckParm("-height"))) + if (myargv[p+1]) + desired_screenheight = atoi(myargv[p+1]); + + if ((p = M_CheckParm("-fullscreen"))) + use_fullscreen = 1; + + if ((p = M_CheckParm("-nofullscreen"))) + use_fullscreen = 0; + + // e6y + // New command-line options for setting a window (-window) + // or fullscreen (-nowindow) mode temporarily which is not saved in cfg. + // It works like "-geom" switch + desired_fullscreen = use_fullscreen; + if ((p = M_CheckParm("-window"))) + desired_fullscreen = 0; + + if ((p = M_CheckParm("-nowindow"))) + desired_fullscreen = 1; + + { // -geometry handling, change screen size for this session only + // e6y: new code by me + int w, h; + + if (!(p = M_CheckParm("-geom"))) + p = M_CheckParm("-geometry"); + + if (!(p && (p+1= MAXLOADFILES) + ProcessDehFile(fpath, D_dehout(), 0); + else { + D_AddFile(fpath,source_auto_load); + } + modifiedgame = true; + free(fpath); + } + } + } + + // e6y: DEH files preloaded in wrong order + // http://sourceforge.net/tracker/index.php?func=detail&aid=1418158&group_id=148658&atid=772943 + // The dachaked stuff has been moved from above + + // ty 03/09/98 do dehacked stuff + // Note: do this before any other since it is expected by + // the deh patch author that this is actually part of the EXE itself + // Using -deh in BOOM, others use -dehacked. + // Ty 03/18/98 also allow .bex extension. .bex overrides if both exist. + + D_BuildBEXTables(); // haleyjd + + p = M_CheckParm ("-deh"); + if (p) + { + char file[PATH_MAX+1]; // cph - localised + // the parms after p are deh/bex file names, + // until end of parms or another - preceded parm + // Ty 04/11/98 - Allow multiple -deh files in a row + + while (++p != myargc && *myargv[p] != '-') + { + AddDefaultExtension(strcpy(file, myargv[p]), ".bex"); + if (access(file, F_OK)) // nope + { + AddDefaultExtension(strcpy(file, myargv[p]), ".deh"); + if (access(file, F_OK)) // still nope + I_Error("D_DoomMainSetup: Cannot find .deh or .bex file named %s",myargv[p]); + } + // during the beta we have debug output to dehout.txt + ProcessDehFile(file,D_dehout(),0); + } + } + // ty 03/09/98 end of do dehacked stuff + + // add any files specified on the command line with -file wadfile + // to the wad list + + // killough 1/31/98, 5/2/98: reload hack removed, -wart same as -warp now. + + if ((p = M_CheckParm ("-file"))) + { + // the parms after p are wadfile/lump names, + // until end of parms or another - preceded parm + modifiedgame = true; // homebrew levels + while (++p != myargc && *myargv[p] != '-') + D_AddFile(myargv[p],source_pwad); + } + + if (!(p = M_CheckParm("-playdemo")) || p >= myargc-1) { /* killough */ + if ((p = M_CheckParm ("-fastdemo")) && p < myargc-1) /* killough */ + fastdemo = true; // run at fastest speed possible + else + p = M_CheckParm ("-timedemo"); + } + + if (p && p < myargc-1) + { + char file[PATH_MAX+1]; // cph - localised + strcpy(file,myargv[p+1]); + AddDefaultExtension(file,".lmp"); // killough + D_AddFile (file,source_lmp); + //jff 9/3/98 use logical output routine + lprintf(LO_CONFIRM,"Playing demo %s\n",file); + if ((p = M_CheckParm ("-ffmap")) && p < myargc-1) { + ffmap = atoi(myargv[p+1]); + } + + } + + // internal translucency set to config file value // phares + general_translucency = default_translucency; // phares + + // 1/18/98 killough: Z_Init() call moved to i_main.c + + // CPhipps - move up netgame init + //jff 9/3/98 use logical output routine + lprintf(LO_INFO,"D_InitNetGame: Checking for network game.\n"); + D_InitNetGame(); + + //jff 9/3/98 use logical output routine + lprintf(LO_INFO,"W_Init: Init WADfiles.\n"); + W_Init(); // CPhipps - handling of wadfiles init changed + + lprintf(LO_INFO,"\n"); // killough 3/6/98: add a newline, by popular demand :) + + // e6y + // option to disable automatic loading of dehacked-in-wad lump + if (!M_CheckParm ("-nodeh")) + if ((p = W_CheckNumForName("DEHACKED")) != -1) // cph - add dehacked-in-a-wad support + ProcessDehFile(NULL, D_dehout(), p); + + V_InitColorTranslation(); //jff 4/24/98 load color translation lumps + + // killough 2/22/98: copyright / "modified game" / SPA banners removed + + // Ty 04/08/98 - Add 5 lines of misc. data, only if nonblank + // The expectation is that these will be set in a .bex file + //jff 9/3/98 use logical output routine + if (*startup1) lprintf(LO_INFO,"%s",startup1); + if (*startup2) lprintf(LO_INFO,"%s",startup2); + if (*startup3) lprintf(LO_INFO,"%s",startup3); + if (*startup4) lprintf(LO_INFO,"%s",startup4); + if (*startup5) lprintf(LO_INFO,"%s",startup5); + // End new startup strings + + //jff 9/3/98 use logical output routine + lprintf(LO_INFO,"M_Init: Init miscellaneous info.\n"); + M_Init(); + +#ifdef HAVE_NET + // CPhipps - now wait for netgame start + D_CheckNetGame(); +#endif + + //jff 9/3/98 use logical output routine + lprintf(LO_INFO,"R_Init: Init DOOM refresh daemon - "); + R_Init(); + + //jff 9/3/98 use logical output routine + lprintf(LO_INFO,"\nP_Init: Init Playloop state.\n"); + P_Init(); + + //jff 9/3/98 use logical output routine + lprintf(LO_INFO,"I_Init: Setting up machine state.\n"); + I_Init(); + + //jff 9/3/98 use logical output routine + lprintf(LO_INFO,"S_Init: Setting up sound.\n"); + S_Init(snd_SfxVolume /* *8 */, snd_MusicVolume /* *8*/ ); + + //jff 9/3/98 use logical output routine + lprintf(LO_INFO,"HU_Init: Setting up heads up display.\n"); + HU_Init(); + + if (!(M_CheckParm("-nodraw") && M_CheckParm("-nosound"))) + I_InitGraphics(); + + //jff 9/3/98 use logical output routine + lprintf(LO_INFO,"ST_Init: Init status bar.\n"); + ST_Init(); + + idmusnum = -1; //jff 3/17/98 insure idmus number is blank + + // CPhipps - auto screenshots + if ((p = M_CheckParm("-autoshot")) && (p < myargc-2)) + if ((auto_shot_count = auto_shot_time = atoi(myargv[p+1]))) + auto_shot_fname = myargv[p+2]; + + // start the apropriate game based on parms + + // killough 12/98: + // Support -loadgame with -record and reimplement -recordfrom. + + if ((slot = M_CheckParm("-recordfrom")) && (p = slot+2) < myargc) + G_RecordDemo(myargv[p]); + else + { + slot = M_CheckParm("-loadgame"); + if ((p = M_CheckParm("-record")) && ++p < myargc) + { + autostart = true; + G_RecordDemo(myargv[p]); + } + } + + if ((p = M_CheckParm ("-checksum")) && ++p < myargc) + { + P_RecordChecksum (myargv[p]); + } + + if ((p = M_CheckParm ("-fastdemo")) && ++p < myargc) + { // killough + fastdemo = true; // run at fastest speed possible + timingdemo = true; // show stats after quit + G_DeferedPlayDemo(myargv[p]); + singledemo = true; // quit after one demo + } + else + if ((p = M_CheckParm("-timedemo")) && ++p < myargc) + { + singletics = true; + timingdemo = true; // show stats after quit + G_DeferedPlayDemo(myargv[p]); + singledemo = true; // quit after one demo + } + else + if ((p = M_CheckParm("-playdemo")) && ++p < myargc) + { + G_DeferedPlayDemo(myargv[p]); + singledemo = true; // quit after one demo + } + + if (slot && ++slot < myargc) + { + slot = atoi(myargv[slot]); // killough 3/16/98: add slot info + G_LoadGame(slot, true); // killough 5/15/98: add command flag // cph - no filename + } + else + if (!singledemo) { /* killough 12/98 */ + if (autostart || netgame) + { + G_InitNew(startskill, startepisode, startmap); + if (demorecording) + G_BeginRecording(); + } + else + D_StartTitle(); // start up intro loop + } +} + +// +// D_DoomMain +// + +void D_DoomMain(void) +{ + D_DoomMainSetup(); // CPhipps - setup out of main execution stack + + D_DoomLoop (); // never returns +} + +// +// GetFirstMap +// +// Ty 08/29/98 - determine first available map from the loaded wads and run it +// + +void GetFirstMap(int *ep, int *map) +{ + int i,j; // used to generate map name + boolean done = false; // Ty 09/13/98 - to exit inner loops + char test[6]; // MAPxx or ExMx plus terminator for testing + char name[6]; // MAPxx or ExMx plus terminator for display + boolean newlevel = false; // Ty 10/04/98 - to test for new level + int ix; // index for lookup + + strcpy(name,""); // initialize + if (*map == 0) // unknown so go search for first changed one + { + *ep = 1; + *map = 1; // default E1M1 or MAP01 + if (gamemode == commercial) + { + for (i=1;!done && i<33;i++) // Ty 09/13/98 - add use of !done + { + sprintf(test,"MAP%02d",i); + ix = W_CheckNumForName(test); + if (ix != -1) // Ty 10/04/98 avoid -1 subscript + { + if (lumpinfo[ix].source == source_pwad) + { + *map = i; + strcpy(name,test); // Ty 10/04/98 + done = true; // Ty 09/13/98 + newlevel = true; // Ty 10/04/98 + } + else + { + if (!*name) // found one, not pwad. First default. + strcpy(name,test); + } + } + } + } + else // one of the others + { + strcpy(name,"E1M1"); // Ty 10/04/98 - default for display + for (i=1;!done && i<5;i++) // Ty 09/13/98 - add use of !done + { + for (j=1;!done && j<10;j++) // Ty 09/13/98 - add use of !done + { + sprintf(test,"E%dM%d",i,j); + ix = W_CheckNumForName(test); + if (ix != -1) // Ty 10/04/98 avoid -1 subscript + { + if (lumpinfo[ix].source == source_pwad) + { + *ep = i; + *map = j; + strcpy(name,test); // Ty 10/04/98 + done = true; // Ty 09/13/98 + newlevel = true; // Ty 10/04/98 + } + else + { + if (!*name) // found one, not pwad. First default. + strcpy(name,test); + } + } + } + } + } + //jff 9/3/98 use logical output routine + lprintf(LO_CONFIRM,"Auto-warping to first %slevel: %s\n", + newlevel ? "new " : "", name); // Ty 10/04/98 - new level test + } +} diff --git a/src/d_main.h b/src/d_main.h new file mode 100644 index 0000000..ccd1910 --- /dev/null +++ b/src/d_main.h @@ -0,0 +1,82 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Main startup and splash screenstuff. + * + *-----------------------------------------------------------------------------*/ + +#ifndef __D_MAIN__ +#define __D_MAIN__ + +#include "d_event.h" +#include "w_wad.h" + +#ifdef __GNUG__ +#pragma interface +#endif + +/* CPhipps - removed wadfiles[] stuff to w_wad.h */ + +extern char basesavegame[]; // killough 2/16/98: savegame path + +//jff 1/24/98 make command line copies of play modes available +extern boolean clnomonsters; // checkparm of -nomonsters +extern boolean clrespawnparm; // checkparm of -respawn +extern boolean clfastparm; // checkparm of -fast +//jff end of external declaration of command line playmode + +extern boolean nosfxparm; +extern boolean nomusicparm; +extern int ffmap; + +// Called by IO functions when input is detected. +void D_PostEvent(event_t* ev); + +// Demo stuff +extern boolean advancedemo; +void D_AdvanceDemo(void); +void D_DoAdvanceDemo (void); + +// +// BASE LEVEL +// + +void D_Display(void); +void D_PageTicker(void); +void D_StartTitle(void); +void D_DoomMain(void); +void D_AddFile (const char *file, wad_source_t source); + +/* cph - MBF-like wad/deh/bex autoload code */ +/* proff 2001/7/1 - added prboom.wad as last entry so it's always loaded and + doesn't overlap with the cfg settings */ +#define MAXLOADFILES 3 +extern const char *wad_files[MAXLOADFILES], *deh_files[MAXLOADFILES]; + +#endif diff --git a/src/d_net.h b/src/d_net.h new file mode 100644 index 0000000..fcdeb6c --- /dev/null +++ b/src/d_net.h @@ -0,0 +1,214 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Networking stuff. + * + *-----------------------------------------------------------------------------*/ + + +#ifndef __D_NET__ +#define __D_NET__ + +#include "d_player.h" + + +#ifdef __GNUG__ +#pragma interface +#endif + + +// +// Network play related stuff. +// There is a data struct that stores network +// communication related stuff, and another +// one that defines the actual packets to +// be transmitted. +// + +#define DOOMCOM_ID 0x12345678l + +// Max computers/players in a game. +#define MAXNETNODES 8 + + +typedef enum +{ + CMD_SEND = 1, + CMD_GET = 2 + +} command_t; + + +// +// Network packet data. +// +typedef struct +{ + // High bit is retransmit request. + unsigned checksum; + // Only valid if NCMD_RETRANSMIT. + byte retransmitfrom; + + byte starttic; + byte player; + byte numtics; + ticcmd_t cmds[BACKUPTICS]; + +} doomdata_t; + +// +// Startup packet difference +// SG: 4/12/98 +// Added so we can send more startup data to synch things like +// bobbing, recoil, etc. +// this is just mapped over the ticcmd_t array when setup packet is sent +// +// Note: the original code takes care of startskill, deathmatch, nomonsters +// respawn, startepisode, startmap +// Note: for phase 1 we need to add monsters_remember, variable_friction, +// weapon_recoil, allow_pushers, over_under, player_bobbing, +// fastparm, demo_insurance, and the rngseed +//Stick all options into bytes so we don't need to mess with bitfields +//WARNING: make sure this doesn't exceed the size of the ticcmds area! +//sizeof(ticcmd_t)*BACKUPTICS +//This is the current length of our extra stuff +// +//killough 5/2/98: this should all be replaced by calls to G_WriteOptions() +//and G_ReadOptions(), which were specifically designed to set up packets. +//By creating a separate struct and functions to read/write the options, +//you now have two functions and data to maintain instead of just one. +//If the array in g_game.c which G_WriteOptions()/G_ReadOptions() operates +//on, is too large (more than sizeof(ticcmd_t)*BACKUPTICS), it can +//either be shortened, or the net code needs to divide it up +//automatically into packets. The STARTUPLEN below is non-portable. +//There's a portable way to do it without having to know the sizes. + +#define STARTUPLEN 12 +typedef struct +{ + byte monsters_remember; + byte variable_friction; + byte weapon_recoil; + byte allow_pushers; + byte over_under; + byte player_bobbing; + byte fastparm; + byte demo_insurance; + unsigned long rngseed; + char filler[sizeof(ticcmd_t)*BACKUPTICS-STARTUPLEN]; +} startup_t; + +typedef enum { + // Leave space, so low values corresponding to normal netgame setup packets can be ignored + nm_plcolour = 3, + nm_savegamename = 4, +} netmisctype_t; + +typedef struct +{ + netmisctype_t type; + size_t len; + byte value[sizeof(ticcmd_t)*BACKUPTICS - sizeof(netmisctype_t) - sizeof(size_t)]; +} netmisc_t; + +typedef struct +{ + // Supposed to be DOOMCOM_ID? + long id; + + // DOOM executes an int to execute commands. + short intnum; + // Communication between DOOM and the driver. + // Is CMD_SEND or CMD_GET. + short command; + // Is dest for send, set by get (-1 = no packet). + short remotenode; + + // Number of bytes in doomdata to be sent + short datalength; + + // Info common to all nodes. + // Console is allways node 0. + short numnodes; + // Flag: 1 = no duplication, 2-5 = dup for slow nets. + short ticdup; + // Flag: 1 = send a backup tic in every packet. + short extratics; + // Flag: 1 = deathmatch. + short deathmatch; + // Flag: -1 = new game, 0-5 = load savegame + short savegame; + short episode; // 1-3 + short map; // 1-9 + short skill; // 1-5 + + // Info specific to this node. + short consoleplayer; + short numplayers; + + // These are related to the 3-display mode, + // in which two drones looking left and right + // were used to render two additional views + // on two additional computers. + // Probably not operational anymore. + // 1 = left, 0 = center, -1 = right + short angleoffset; + // 1 = drone + short drone; + + // The packet data to be sent. + doomdata_t data; + +} doomcom_t; + +// Create any new ticcmds and broadcast to other players. +#ifdef HAVE_NET +void NetUpdate (void); +#else +void D_BuildNewTiccmds(void); +#endif + +//? how many ticks to run? +void TryRunTics (void); + +// CPhipps - move to header file +void D_InitNetGame (void); // This does the setup +void D_CheckNetGame(void); // This waits for game start + +// CPhipps - misc info broadcast +void D_NetSendMisc(netmisctype_t type, size_t len, void* data); + +// CPhipps - ask server for a wad file we need +boolean D_NetGetWad(const char* name); + +// Netgame stuff (buffers and pointers, i.e. indices). +extern doomcom_t *doomcom; +extern doomdata_t *netbuffer; // This points inside doomcom. + +#endif diff --git a/src/d_player.h b/src/d_player.h new file mode 100644 index 0000000..a45abe8 --- /dev/null +++ b/src/d_player.h @@ -0,0 +1,234 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Player state structure. + * + *-----------------------------------------------------------------------------*/ + + +#ifndef __D_PLAYER__ +#define __D_PLAYER__ + + +// The player data structure depends on a number +// of other structs: items (internal inventory), +// animation states (closely tied to the sprites +// used to represent them, unfortunately). +#include "d_items.h" +#include "p_pspr.h" + +// In addition, the player is just a special +// case of the generic moving object/actor. +#include "p_mobj.h" + +// Finally, for odd reasons, the player input +// is buffered within the player data struct, +// as commands per game tick. +#include "d_ticcmd.h" + +#ifdef __GNUG__ +#pragma interface +#endif + + +// +// Player states. +// +typedef enum +{ + // Playing or camping. + PST_LIVE, + // Dead on the ground, view follows killer. + PST_DEAD, + // Ready to restart/respawn??? + PST_REBORN + +} playerstate_t; + + +// +// Player internal flags, for cheats and debug. +// +typedef enum +{ + // No clipping, walk through barriers. + CF_NOCLIP = 1, + // No damage, no health loss. + CF_GODMODE = 2, + // Not really a cheat, just a debug aid. + CF_NOMOMENTUM = 4 + +} cheat_t; + + +// +// Extended player object info: player_t +// +typedef struct player_s +{ + mobj_t* mo; + playerstate_t playerstate; + ticcmd_t cmd; + + // Determine POV, + // including viewpoint bobbing during movement. + // Focal origin above r.z + fixed_t viewz; + // Base height above floor for viewz. + fixed_t viewheight; + // Bob/squat speed. + fixed_t deltaviewheight; + // bounded/scaled total momentum. + fixed_t bob; + + /* killough 10/98: used for realistic bobbing (i.e. not simply overall speed) + * mo->momx and mo->momy represent true momenta experienced by player. + * This only represents the thrust that the player applies himself. + * This avoids anomolies with such things as Boom ice and conveyors. + */ + fixed_t momx, momy; // killough 10/98 + + // This is only used between levels, + // mo->health is used during levels. + int health; + int armorpoints; + // Armor type is 0-2. + int armortype; + + // Power ups. invinc and invis are tic counters. + int powers[NUMPOWERS]; + boolean cards[NUMCARDS]; + boolean backpack; + + // Frags, kills of other players. + int frags[MAXPLAYERS]; + weapontype_t readyweapon; + + // Is wp_nochange if not changing. + weapontype_t pendingweapon; + + boolean weaponowned[NUMWEAPONS]; + int ammo[NUMAMMO]; + int maxammo[NUMAMMO]; + + // True if button down last tic. + int attackdown; + int usedown; + + // Bit flags, for cheats and debug. + // See cheat_t, above. + int cheats; + + // Refired shots are less accurate. + int refire; + + // For intermission stats. + int killcount; + int itemcount; + int secretcount; + + // Hint messages. // CPhipps - const + const char* message; + + // For screen flashing (red or bright). + int damagecount; + int bonuscount; + + // Who did damage (NULL for floors/ceilings). + mobj_t* attacker; + + // So gun flashes light up areas. + int extralight; + + // Current PLAYPAL, ??? + // can be set to REDCOLORMAP for pain, etc. + int fixedcolormap; + + // Player skin colorshift, + // 0-3 for which color to draw player. + int colormap; + + // Overlay view sprites (gun, etc). + pspdef_t psprites[NUMPSPRITES]; + + // True if secret level has been done. + boolean didsecret; + +} player_t; + + +// +// INTERMISSION +// Structure passed e.g. to WI_Start(wb) +// +typedef struct +{ + boolean in; // whether the player is in game + + // Player stats, kills, collected items etc. + int skills; + int sitems; + int ssecret; + int stime; + int frags[4]; + int score; // current score on entry, modified on return + +} wbplayerstruct_t; + +typedef struct +{ + int epsd; // episode # (0-2) + + // if true, splash the secret level + boolean didsecret; + + // previous and next levels, origin 0 + int last; + int next; + + int maxkills; + int maxitems; + int maxsecret; + int maxfrags; + + // the par time + int partime; + + // index of this player in game + int pnum; + + wbplayerstruct_t plyr[MAXPLAYERS]; + + // CPhipps - total game time for completed levels so far + int totaltimes; + +} wbstartstruct_t; + + +#endif diff --git a/src/d_think.h b/src/d_think.h new file mode 100644 index 0000000..63e6626 --- /dev/null +++ b/src/d_think.h @@ -0,0 +1,94 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * MapObj data. Map Objects or mobjs are actors, entities, + * thinker, take-your-pick... anything that moves, acts, or + * suffers state changes of more or less violent nature. + * + *-----------------------------------------------------------------------------*/ + +#ifndef __D_THINK__ +#define __D_THINK__ + +#ifdef __GNUG__ +#pragma interface +#endif + +/* + * Experimental stuff. + * To compile this as "ANSI C with classes" + * we will need to handle the various + * action functions cleanly. + */ +// killough 11/98: convert back to C instead of C++ +typedef void (*actionf_t)(); +//typedef void (*actionf_v)(); +//typedef void (*actionf_p1)( void* ); +//typedef void (*actionf_p2)( void*, void* ); + +/* Note: In d_deh.c you will find references to these + * wherever code pointers and function handlers exist + */ +/* +typedef union +{ + actionf_p1 acp1; + actionf_v acv; + actionf_p2 acp2; + +} actionf_t; +*/ + +/* Historically, "think_t" is yet another + * function pointer to a routine to handle + * an actor. + */ +typedef actionf_t think_t; + + +/* Doubly linked list of actors. */ +typedef struct thinker_s +{ + struct thinker_s* prev; + struct thinker_s* next; + think_t function; + + /* killough 8/29/98: we maintain thinkers in several equivalence classes, + * according to various criteria, so as to allow quicker searches. + */ + + struct thinker_s *cnext, *cprev; /* Next, previous thinkers in same class */ + + /* killough 11/98: count of how many other objects reference + * this one using pointers. Used for garbage collection. + */ + unsigned references; +} thinker_t; + +#endif diff --git a/src/d_ticcmd.h b/src/d_ticcmd.h new file mode 100644 index 0000000..4469dfb --- /dev/null +++ b/src/d_ticcmd.h @@ -0,0 +1,59 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * System specific interface stuff. + * + *-----------------------------------------------------------------------------*/ + +#ifndef __D_TICCMD__ +#define __D_TICCMD__ + +#include "doomtype.h" + +#ifdef __GNUG__ +#pragma interface +#endif + +/* The data sampled per tick (single player) + * and transmitted to other peers (multiplayer). + * Mainly movements/button commands per game tick, + * plus a checksum for internal state consistency. + * CPhipps - explicitely signed the elements, since they have to be signed to work right + */ +typedef struct +{ + signed char forwardmove; /* *2048 for move */ + signed char sidemove; /* *2048 for move */ + signed short angleturn; /* <<16 for angle delta */ + short consistancy; /* checks for net game */ + byte chatchar; + byte buttons; +} ticcmd_t; + +#endif diff --git a/src/doomdata.h b/src/doomdata.h new file mode 100644 index 0000000..20eb99a --- /dev/null +++ b/src/doomdata.h @@ -0,0 +1,204 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * all external data is defined here + * most of the data is loaded into different structures at run time + * some internal structures shared by many modules are here + * + *-----------------------------------------------------------------------------*/ + +#ifndef __DOOMDATA__ +#define __DOOMDATA__ + +// The most basic types we use, portability. +#include "config.h" +#include "doomtype.h" + +// +// Map level types. +// The following data structures define the persistent format +// used in the lumps of the WAD files. +// + +// Lump order in a map WAD: each map needs a couple of lumps +// to provide a complete scene geometry description. +enum { + ML_LABEL, // A separator, name, ExMx or MAPxx + ML_THINGS, // Monsters, items.. + ML_LINEDEFS, // LineDefs, from editing + ML_SIDEDEFS, // SideDefs, from editing + ML_VERTEXES, // Vertices, edited and BSP splits generated + ML_SEGS, // LineSegs, from LineDefs split by BSP + ML_SSECTORS, // SubSectors, list of LineSegs + ML_NODES, // BSP nodes + ML_SECTORS, // Sectors, from editing + ML_REJECT, // LUT, sector-sector visibility + ML_BLOCKMAP // LUT, motion clipping, walls/grid element +}; + +#ifdef _MSC_VER // proff: This is the same as __attribute__ ((packed)) in GNUC +#pragma pack(push) +#pragma pack(1) +#endif //_MSC_VER + +// A single Vertex. +typedef struct { + short x,y; +} PACKEDATTR mapvertex_t; + +// A SideDef, defining the visual appearance of a wall, +// by setting textures and offsets. +typedef struct { + short textureoffset; + short rowoffset; + char toptexture[8]; + char bottomtexture[8]; + char midtexture[8]; + short sector; // Front sector, towards viewer. +} PACKEDATTR mapsidedef_t; + +// A LineDef, as used for editing, and as input to the BSP builder. + +typedef struct { + unsigned short v1; + unsigned short v2; + unsigned short flags; + short special; + short tag; + // proff 07/23/2006 - support more than 32768 sidedefs + // use the unsigned value and special case the -1 + // sidenum[1] will be -1 (NO_INDEX) if one sided + unsigned short sidenum[2]; +} PACKEDATTR maplinedef_t; + +#define NO_INDEX ((unsigned short)-1) + +// +// LineDef attributes. +// + +// Solid, is an obstacle. +#define ML_BLOCKING 1 + +// Blocks monsters only. +#define ML_BLOCKMONSTERS 2 + +// Backside will not be drawn if not two sided. +#define ML_TWOSIDED 4 + +// If a texture is pegged, the texture will have +// the end exposed to air held constant at the +// top or bottom of the texture (stairs or pulled +// down things) and will move with a height change +// of one of the neighbor sectors. +// Unpegged textures always have the first row of +// the texture at the top pixel of the line for both +// top and bottom textures (use next to windows). + +// upper texture unpegged +#define ML_DONTPEGTOP 8 + +// lower texture unpegged +#define ML_DONTPEGBOTTOM 16 + +// In AutoMap: don't map as two sided: IT'S A SECRET! +#define ML_SECRET 32 + +// Sound rendering: don't let sound cross two of these. +#define ML_SOUNDBLOCK 64 + +// Don't draw on the automap at all. +#define ML_DONTDRAW 128 + +// Set if already seen, thus drawn in automap. +#define ML_MAPPED 256 + +//jff 3/21/98 Set if line absorbs use by player +//allow multiple push/switch triggers to be used on one push +#define ML_PASSUSE 512 + +// Sector definition, from editing. +typedef struct { + short floorheight; + short ceilingheight; + char floorpic[8]; + char ceilingpic[8]; + short lightlevel; + short special; + short tag; +} PACKEDATTR mapsector_t; + +// SubSector, as generated by BSP. +typedef struct { + unsigned short numsegs; + unsigned short firstseg; // Index of first one; segs are stored sequentially. +} PACKEDATTR mapsubsector_t; + +// LineSeg, generated by splitting LineDefs +// using partition lines selected by BSP builder. +typedef struct { + unsigned short v1; + unsigned short v2; + short angle; + unsigned short linedef; + short side; + short offset; +} PACKEDATTR mapseg_t; + +// BSP node structure. + +// Indicate a leaf. +#define NF_SUBSECTOR 0x8000 + +typedef struct { + short x; // Partition line from (x,y) to x+dx,y+dy) + short y; + short dx; + short dy; + // Bounding box for each child, clip against view frustum. + short bbox[2][4]; + // If NF_SUBSECTOR its a subsector, else it's a node of another subtree. + unsigned short children[2]; +} PACKEDATTR mapnode_t; + +// Thing definition, position, orientation and type, +// plus skill/visibility flags and attributes. +typedef struct { + short x; + short y; + short angle; + short type; + short options; +} PACKEDATTR mapthing_t; + +#ifdef _MSC_VER +#pragma pack(pop) +#endif //_MSC_VER + +#endif // __DOOMDATA__ diff --git a/src/doomdef.c b/src/doomdef.c new file mode 100644 index 0000000..c99c113 --- /dev/null +++ b/src/doomdef.c @@ -0,0 +1,48 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * DoomDef - basic defines for DOOM, e.g. Version, game mode + * and skill level, and display parameters. + * + *----------------------------------------------------------------------------- + */ + +#ifdef __GNUG__ +#pragma implementation "doomdef.h" +#endif + +#include "doomdef.h" + +// Location for any defines turned variables. +// None. + +// proff 08/17/98: Changed for high-res +int SCREENWIDTH=320; +int SCREENHEIGHT=200; +int SCREENPITCH=320; diff --git a/src/doomdef.h b/src/doomdef.h new file mode 100644 index 0000000..140db82 --- /dev/null +++ b/src/doomdef.h @@ -0,0 +1,330 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Internally used data structures for virtually everything, + * key definitions, lots of other stuff. + * + *-----------------------------------------------------------------------------*/ + +#ifndef __DOOMDEF__ +#define __DOOMDEF__ + +/* use config.h if autoconf made one -- josh */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +// killough 4/25/98: Make gcc extensions mean nothing on other compilers +#ifndef __GNUC__ +#define __attribute__(x) +#endif + +// This must come first, since it redefines malloc(), free(), etc. -- killough: +#include "z_zone.h" + +#include +#include +#include +#include +#include + +// this should go here, not in makefile/configure.ac -- josh +#ifndef O_BINARY +#define O_BINARY 0 +#endif + +#include "m_swap.h" +#include "version.h" + +// Game mode handling - identify IWAD version +// to handle IWAD dependend animations etc. +typedef enum { + shareware, // DOOM 1 shareware, E1, M9 + registered, // DOOM 1 registered, E3, M27 + commercial, // DOOM 2 retail, E1 M34 (DOOM 2 german edition not handled) + retail, // DOOM 1 retail, E4, M36 + indetermined // Well, no IWAD found. +} GameMode_t; + +// Mission packs - might be useful for TC stuff? +typedef enum { + doom, // DOOM 1 + doom2, // DOOM 2 + pack_tnt, // TNT mission pack + pack_plut, // Plutonia pack + none +} GameMission_t; + +// Identify language to use, software localization. +typedef enum { + english, + french, + german, + unknown +} Language_t; + +// +// For resize of screen, at start of game. +// + +#define BASE_WIDTH 320 + +// It is educational but futile to change this +// scaling e.g. to 2. Drawing of status bar, +// menues etc. is tied to the scale implied +// by the graphics. + +#define INV_ASPECT_RATIO 0.625 /* 0.75, ideally */ + +// killough 2/8/98: MAX versions for maximum screen sizes +// allows us to avoid the overhead of dynamic allocation +// when multiple screen sizes are supported + +// proff 08/17/98: Changed for high-res +#define MAX_SCREENWIDTH 2048 +#define MAX_SCREENHEIGHT 1536 + +// SCREENWIDTH and SCREENHEIGHT define the visible size +extern int SCREENWIDTH; +extern int SCREENHEIGHT; +// SCREENPITCH is the size of one line in the buffer and +// can be bigger than the SCREENWIDTH depending on the size +// of one pixel (8, 16 or 32 bit) and the padding at the +// end of the line caused by hardware considerations +extern int SCREENPITCH; + +// The maximum number of players, multiplayer/networking. +#define MAXPLAYERS 4 + +// phares 5/14/98: +// DOOM Editor Numbers (aka doomednum in mobj_t) + +#define DEN_PLAYER5 4001 +#define DEN_PLAYER6 4002 +#define DEN_PLAYER7 4003 +#define DEN_PLAYER8 4004 + +// State updates, number of tics / second. +#define TICRATE 35 + +// The current state of the game: whether we are playing, gazing +// at the intermission screen, the game final animation, or a demo. + +typedef enum { + GS_LEVEL, + GS_INTERMISSION, + GS_FINALE, + GS_DEMOSCREEN +} gamestate_t; + +// +// Difficulty/skill settings/filters. +// +// These are Thing flags + +// Skill flags. +#define MTF_EASY 1 +#define MTF_NORMAL 2 +#define MTF_HARD 4 +// Deaf monsters/do not react to sound. +#define MTF_AMBUSH 8 + +/* killough 11/98 */ +#define MTF_NOTSINGLE 16 +#define MTF_NOTDM 32 +#define MTF_NOTCOOP 64 +#define MTF_FRIEND 128 +#define MTF_RESERVED 256 + +typedef enum { + sk_none=-1, //jff 3/24/98 create unpicked skill setting + sk_baby=0, + sk_easy, + sk_medium, + sk_hard, + sk_nightmare +} skill_t; + +// +// Key cards. +// + +typedef enum { + it_bluecard, + it_yellowcard, + it_redcard, + it_blueskull, + it_yellowskull, + it_redskull, + NUMCARDS +} card_t; + +// The defined weapons, including a marker +// indicating user has not changed weapon. +typedef enum { + wp_fist, + wp_pistol, + wp_shotgun, + wp_chaingun, + wp_missile, + wp_plasma, + wp_bfg, + wp_chainsaw, + wp_supershotgun, + + NUMWEAPONS, + wp_nochange // No pending weapon change. +} weapontype_t; + +// Ammunition types defined. +typedef enum { + am_clip, // Pistol / chaingun ammo. + am_shell, // Shotgun / double barreled shotgun. + am_cell, // Plasma rifle, BFG. + am_misl, // Missile launcher. + NUMAMMO, + am_noammo // Unlimited for chainsaw / fist. +} ammotype_t; + +// Power up artifacts. +typedef enum { + pw_invulnerability, + pw_strength, + pw_invisibility, + pw_ironfeet, + pw_allmap, + pw_infrared, + NUMPOWERS +} powertype_t; + +// Power up durations (how many seconds till expiration). +typedef enum { + INVULNTICS = (30*TICRATE), + INVISTICS = (60*TICRATE), + INFRATICS = (120*TICRATE), + IRONTICS = (60*TICRATE) +} powerduration_t; + +// DOOM keyboard definition. +// This is the stuff configured by Setup.Exe. +// Most key data are simple ascii (uppercased). + +#define KEYD_RIGHTARROW 0xae +#define KEYD_LEFTARROW 0xac +#define KEYD_UPARROW 0xad +#define KEYD_DOWNARROW 0xaf +#define KEYD_ESCAPE 27 +#define KEYD_ENTER 13 +#define KEYD_TAB 9 +#define KEYD_F1 (0x80+0x3b) +#define KEYD_F2 (0x80+0x3c) +#define KEYD_F3 (0x80+0x3d) +#define KEYD_F4 (0x80+0x3e) +#define KEYD_F5 (0x80+0x3f) +#define KEYD_F6 (0x80+0x40) +#define KEYD_F7 (0x80+0x41) +#define KEYD_F8 (0x80+0x42) +#define KEYD_F9 (0x80+0x43) +#define KEYD_F10 (0x80+0x44) +#define KEYD_F11 (0x80+0x57) +#define KEYD_F12 (0x80+0x58) +#define KEYD_BACKSPACE 127 +#define KEYD_PAUSE 0xff +#define KEYD_EQUALS 0x3d +#define KEYD_MINUS 0x2d +#define KEYD_RSHIFT (0x80+0x36) +#define KEYD_RCTRL (0x80+0x1d) +#define KEYD_RALT (0x80+0x38) +#define KEYD_LALT KEYD_RALT +#define KEYD_CAPSLOCK 0xba // phares + +// phares 3/2/98: +#define KEYD_INSERT 0xd2 +#define KEYD_HOME 0xc7 +#define KEYD_PAGEUP 0xc9 +#define KEYD_PAGEDOWN 0xd1 +#define KEYD_DEL 0xc8 +#define KEYD_END 0xcf +#define KEYD_SCROLLLOCK 0xc6 +#define KEYD_SPACEBAR 0x20 +// phares 3/2/98 + +#define KEYD_NUMLOCK 0xC5 // killough 3/6/98 + +// cph - Add the numeric keypad keys, as suggested by krose 4/22/99: +// The way numbers are assigned to keys is a mess, but it's too late to +// change that easily. At least these additions are don neatly. +// Codes 0x100-0x200 are reserved for number pad + +#define KEYD_KEYPAD0 (0x100 + '0') +#define KEYD_KEYPAD1 (0x100 + '1') +#define KEYD_KEYPAD2 (0x100 + '2') +#define KEYD_KEYPAD3 (0x100 + '3') +#define KEYD_KEYPAD4 (0x100 + '4') +#define KEYD_KEYPAD5 (0x100 + '5') +#define KEYD_KEYPAD6 (0x100 + '6') +#define KEYD_KEYPAD7 (0x100 + '7') +#define KEYD_KEYPAD8 (0x100 + '8') +#define KEYD_KEYPAD9 (0x100 + '9') +#define KEYD_KEYPADENTER (0x100 + KEYD_ENTER) +#define KEYD_KEYPADDIVIDE (0x100 + '/') +#define KEYD_KEYPADMULTIPLY (0x100 + '*') +#define KEYD_KEYPADMINUS (0x100 + '-') +#define KEYD_KEYPADPLUS (0x100 + '+') +#define KEYD_KEYPADPERIOD (0x100 + '.') + +// phares 4/19/98: +// Defines Setup Screen groups that config variables appear in. +// Used when resetting the defaults for every item in a Setup group. + +typedef enum { + ss_none, + ss_keys, + ss_weap, + ss_stat, + ss_auto, + ss_enem, + ss_mess, + ss_chat, + ss_gen, /* killough 10/98 */ + ss_comp, /* killough 10/98 */ + ss_max +} ss_types; + +// phares 3/20/98: +// +// Player friction is variable, based on controlling +// linedefs. More friction can create mud, sludge, +// magnetized floors, etc. Less friction can create ice. + +#define MORE_FRICTION_MOMENTUM 15000 // mud factor based on momentum +#define ORIG_FRICTION 0xE800 // original value +#define ORIG_FRICTION_FACTOR 2048 // original value + +#endif // __DOOMDEF__ diff --git a/src/doomstat.c b/src/doomstat.c new file mode 100644 index 0000000..56506ad --- /dev/null +++ b/src/doomstat.c @@ -0,0 +1,108 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Put all global state variables here. + * + *----------------------------------------------------------------------------- + */ + +#ifdef __GNUG__ +#pragma implementation "doomstat.h" +#endif +#include "doomstat.h" + +// Game Mode - identify IWAD as shareware, retail etc. +GameMode_t gamemode = indetermined; +GameMission_t gamemission = doom; + +// Language. +Language_t language = english; + +// Set if homebrew PWAD stuff has been added. +boolean modifiedgame; + +//----------------------------------------------------------------------------- + +// CPhipps - compatibility vars +complevel_t compatibility_level, default_compatibility_level; + +int comp[COMP_TOTAL], default_comp[COMP_TOTAL]; // killough 10/98 + +// v1.1-like pitched sounds +int pitched_sounds; // killough + +int default_translucency; // config file says // phares +boolean general_translucency; // true if translucency is ok // phares + +int demo_insurance, default_demo_insurance; // killough 1/16/98 + +int allow_pushers = 1; // MT_PUSH Things // phares 3/10/98 +int default_allow_pushers; // killough 3/1/98: make local to each game + +int variable_friction = 1; // ice & mud // phares 3/10/98 +int default_variable_friction; // killough 3/1/98: make local to each game + +int weapon_recoil; // weapon recoil // phares +int default_weapon_recoil; // killough 3/1/98: make local to each game + +int player_bobbing; // whether player bobs or not // phares 2/25/98 +int default_player_bobbing; // killough 3/1/98: make local to each game + +int monsters_remember; // killough 3/1/98 +int default_monsters_remember; + +int monster_infighting=1; // killough 7/19/98: monster<=>monster attacks +int default_monster_infighting=1; + +int monster_friction=1; // killough 10/98: monsters affected by friction +int default_monster_friction=1; + +#ifdef DOGS +int dogs, default_dogs; // killough 7/19/98: Marine's best friend :) +int dog_jumping, default_dog_jumping; // killough 10/98 +#endif + +// killough 8/8/98: distance friends tend to move towards players +int distfriend = 128, default_distfriend = 128; + +// killough 9/8/98: whether monsters are allowed to strafe or retreat +int monster_backing, default_monster_backing; + +// killough 9/9/98: whether monsters are able to avoid hazards (e.g. crushers) +int monster_avoid_hazards, default_monster_avoid_hazards; + +// killough 9/9/98: whether monsters help friends +int help_friends, default_help_friends; + +int flashing_hom; // killough 10/98 + +int doom_weapon_toggles; // killough 10/98 + +int monkeys, default_monkeys; + diff --git a/src/doomstat.h b/src/doomstat.h new file mode 100644 index 0000000..88f7b8d --- /dev/null +++ b/src/doomstat.h @@ -0,0 +1,332 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2006 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * All the global variables that store the internal state. + * Theoretically speaking, the internal state of the engine + * should be found by looking at the variables collected + * here, and every relevant module will have to include + * this header file. + * In practice, things are a bit messy. + * + *-----------------------------------------------------------------------------*/ + +#ifndef __D_STATE__ +#define __D_STATE__ + +// We need the playr data structure as well. +#include "d_player.h" + +#ifdef __GNUG__ +#pragma interface +#endif + +// ------------------------ +// Command line parameters. +// + +extern boolean nomonsters; // checkparm of -nomonsters +extern boolean respawnparm; // checkparm of -respawn +extern boolean fastparm; // checkparm of -fast +extern boolean devparm; // DEBUG: launched with -devparm + +// ----------------------------------------------------- +// Game Mode - identify IWAD as shareware, retail etc. +// + +extern GameMode_t gamemode; +extern GameMission_t gamemission; + +// Set if homebrew PWAD stuff has been added. +extern boolean modifiedgame; + +// CPhipps - new compatibility handling +extern complevel_t compatibility_level, default_compatibility_level; + +// CPhipps - old compatibility testing flags aliased to new handling +#define compatibility (compatibility_level<=boom_compatibility_compatibility) +#define demo_compatibility (compatibility_level < boom_compatibility_compatibility) +#define mbf_features (compatibility_level>=mbf_compatibility) + +// v1.1-like pitched sounds +extern int pitched_sounds; // killough + +extern int default_translucency; // config file says // phares +extern boolean general_translucency; // true if translucency is ok // phares + +extern int demo_insurance, default_demo_insurance; // killough 4/5/98 + +// ------------------------------------------- +// killough 10/98: compatibility vector + +enum { + comp_telefrag, + comp_dropoff, + comp_vile, + comp_pain, + comp_skull, + comp_blazing, + comp_doorlight, + comp_model, + comp_god, + comp_falloff, + comp_floors, + comp_skymap, + comp_pursuit, + comp_doorstuck, + comp_staylift, + comp_zombie, + comp_stairs, + comp_infcheat, + comp_zerotags, + comp_moveblock, + comp_respawn, /* cph - this is the inverse of comp_respawnfix from eternity */ + comp_sound, + comp_666, + comp_soul, + comp_maskedanim, + COMP_NUM, /* cph - should be last in sequence */ + COMP_TOTAL=32 // Some extra room for additional variables +}; + +extern int comp[COMP_TOTAL], default_comp[COMP_TOTAL]; + +// ------------------------------------------- +// Language. +extern Language_t language; + +// ------------------------------------------- +// Selected skill type, map etc. +// + +// Defaults for menu, methinks. +extern skill_t startskill; +extern int startepisode; +extern int startmap; + +extern boolean autostart; + +// Selected by user. +extern skill_t gameskill; +extern int gameepisode; +extern int gamemap; + +// Nightmare mode flag, single player. +extern boolean respawnmonsters; + +// Netgame? Only true if >1 player. +extern boolean netgame; + +// Flag: true only if started as net deathmatch. +// An enum might handle altdeath/cooperative better. +extern boolean deathmatch; + +// ------------------------------------------ +// Internal parameters for sound rendering. +// These have been taken from the DOS version, +// but are not (yet) supported with Linux +// (e.g. no sound volume adjustment with menu. + +// These are not used, but should be (menu). +// From m_menu.c: +// Sound FX volume has default, 0 - 15 +// Music volume has default, 0 - 15 +// These are multiplied by 8. +extern int snd_SfxVolume; // maximum volume for sound +extern int snd_MusicVolume; // maximum volume for music + +// CPhipps - screen parameters +extern unsigned int desired_screenwidth, desired_screenheight; + +// ------------------------- +// Status flags for refresh. +// + +enum automapmode_e { + am_active = 1, // currently shown + am_overlay= 2, // covers the screen, i.e. not overlay mode + am_rotate = 4, // rotates to the player facing direction + am_follow = 8, // keep the player centred + am_grid =16, // show grid +}; +extern enum automapmode_e automapmode; // Mode that the automap is in + +extern boolean menuactive; // Menu overlayed? +extern boolean paused; // Game Pause? +extern boolean nodrawers; +extern boolean noblit; + +// This one is related to the 3-screen display mode. +// ANG90 = left side, ANG270 = right +extern int viewangleoffset; + +// Player taking events, and displaying. +extern int consoleplayer; +extern int displayplayer; + +// ------------------------------------- +// Scores, rating. +// Statistics on a given map, for intermission. +// +extern int totalkills, totallive; +extern int totalitems; +extern int totalsecret; + +// Timer, for scores. +extern int basetic; /* killough 9/29/98: levelstarttic, adjusted */ +extern int leveltime; // tics in game play for par + +// -------------------------------------- +// DEMO playback/recording related stuff. + +extern boolean usergame; +extern boolean demoplayback; +extern boolean demorecording; +extern int demover; + +// Quit after playing a demo from cmdline. +extern boolean singledemo; +// Print timing information after quitting. killough +extern boolean timingdemo; +// Run tick clock at fastest speed possible while playing demo. killough +extern boolean fastdemo; + +extern gamestate_t gamestate; + +//----------------------------- +// Internal parameters, fixed. +// These are set by the engine, and not changed +// according to user inputs. Partly load from +// WAD, partly set at startup time. + +extern int gametic; + + +// Bookkeeping on players - state. +extern player_t players[MAXPLAYERS]; + +// Alive? Disconnected? +extern boolean playeringame[MAXPLAYERS]; +extern boolean realplayeringame[MAXPLAYERS]; + +extern mapthing_t *deathmatchstarts; // killough +extern size_t num_deathmatchstarts; // killough + +extern mapthing_t *deathmatch_p; + +// Player spawn spots. +extern mapthing_t playerstarts[]; + +// Intermission stats. +// Parameters for world map / intermission. +extern wbstartstruct_t wminfo; + +//----------------------------------------- +// Internal parameters, used for engine. +// + +// File handling stuff. +extern FILE *debugfile; + +// if true, load all graphics at level load +extern boolean precache; + +// wipegamestate can be set to -1 +// to force a wipe on the next draw +extern gamestate_t wipegamestate; + +extern int mouseSensitivity_horiz; // killough +extern int mouseSensitivity_vert; + +// debug flag to cancel adaptiveness +extern boolean singletics; + +extern int bodyqueslot; + +// Needed to store the number of the dummy sky flat. +// Used for rendering, as well as tracking projectiles etc. + +extern int skyflatnum; + +extern int maketic; + +// Networking and tick handling related. +#define BACKUPTICS 12 + +extern ticcmd_t netcmds[][BACKUPTICS]; +extern int ticdup; + +//----------------------------------------------------------------------------- + +extern int allow_pushers; // MT_PUSH Things // phares 3/10/98 +extern int default_allow_pushers; + +extern int variable_friction; // ice & mud // phares 3/10/98 +extern int default_variable_friction; + +extern int monsters_remember; // killough 3/1/98 +extern int default_monsters_remember; + +extern int weapon_recoil; // weapon recoil // phares +extern int default_weapon_recoil; + +extern int player_bobbing; // whether player bobs or not // phares 2/25/98 +extern int default_player_bobbing; // killough 3/1/98: make local to each game + +#ifdef DOGS +extern int dogs, default_dogs; // killough 7/19/98: Marine's best friend :) +extern int dog_jumping, default_dog_jumping; // killough 10/98 +#endif + +/* killough 8/8/98: distance friendly monsters tend to stay from player */ +extern int distfriend, default_distfriend; + +/* killough 9/8/98: whether monsters are allowed to strafe or retreat */ +extern int monster_backing, default_monster_backing; + +/* killough 9/9/98: whether monsters intelligently avoid hazards */ +extern int monster_avoid_hazards, default_monster_avoid_hazards; + +/* killough 10/98: whether monsters are affected by friction */ +extern int monster_friction, default_monster_friction; + +/* killough 9/9/98: whether monsters help friends */ +extern int help_friends, default_help_friends; + +extern int flashing_hom; // killough 10/98 + +extern int doom_weapon_toggles; // killough 10/98 + +/* killough 7/19/98: whether monsters should fight against each other */ +extern int monster_infighting, default_monster_infighting; + +extern int monkeys, default_monkeys; + +extern int HelperThing; // type of thing to use for helper + +#endif diff --git a/src/doomtype.h b/src/doomtype.h new file mode 100644 index 0000000..bfc2f0a --- /dev/null +++ b/src/doomtype.h @@ -0,0 +1,128 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2006 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Simple basic typedefs, isolated here to make it easier + * separating modules. + * + *-----------------------------------------------------------------------------*/ + +#ifndef __DOOMTYPE__ +#define __DOOMTYPE__ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifndef __BYTEBOOL__ +#define __BYTEBOOL__ +/* Fixed to use builtin bool type with C++. */ +#ifdef __cplusplus +typedef bool boolean; +#else +typedef enum {false, true} boolean; +#endif +typedef unsigned char byte; +#endif + +//e6y +#ifndef MAX +#define MAX(a,b) ((a)>(b)?(a):(b)) +#endif +#ifndef MIN +#define MIN(a,b) ((a)<(b)?(a):(b)) +#endif + +/* cph - Wrapper for the long long type, as Win32 used a different name. + * Except I don't know what to test as it's compiler specific + * Proff - I fixed it */ +#ifndef _MSC_VER +typedef signed long long int_64_t; +typedef unsigned long long uint_64_t; +// define compiled-specific long-long contstant notation here +#define LONGLONG(num) (uint_64_t)num ## ll +#else +typedef __int64 int_64_t; +typedef unsigned __int64 uint_64_t; +// define compiled-specific long-long contstant notation here +#define LONGLONG(num) (uint_64_t)num +#undef PATH_MAX +#define PATH_MAX 1024 +#define strcasecmp _stricmp +#define strncasecmp _strnicmp +#define S_ISDIR(x) (((sbuf.st_mode & S_IFDIR)==S_IFDIR)?1:0) +#endif + +#ifdef __GNUC__ +#define CONSTFUNC __attribute__((const)) +#define PUREFUNC __attribute__((pure)) +#define NORETURN __attribute__ ((noreturn)) +#else +#define CONSTFUNC +#define PUREFUNC +#define NORETURN +#endif + +/* CPhipps - use limits.h instead of depreciated values.h */ +#include + +/* cph - move compatibility levels here so we can use them in d_server.c */ +typedef enum { + doom_12_compatibility, /* Doom v1.2 */ + doom_1666_compatibility, /* Doom v1.666 */ + doom2_19_compatibility, /* Doom & Doom 2 v1.9 */ + ultdoom_compatibility, /* Doom 2 v1.9 */ + finaldoom_compatibility, /* Final & Ultimate Doom v1.9, and Doom95 */ + dosdoom_compatibility, /* Early dosdoom & tasdoom */ + tasdoom_compatibility, /* Early dosdoom & tasdoom */ + boom_compatibility_compatibility, /* Boom's compatibility mode */ + boom_201_compatibility, /* Compatible with Boom v2.01 */ + boom_202_compatibility, /* Compatible with Boom v2.01 */ + lxdoom_1_compatibility, /* LxDoom v1.3.2+ */ + mbf_compatibility, /* MBF */ + prboom_1_compatibility, /* PrBoom 2.03beta? */ + prboom_2_compatibility, /* PrBoom 2.1.0-2.1.1 */ + prboom_3_compatibility, /* PrBoom 2.2.x */ + prboom_4_compatibility, /* PrBoom 2.3.x */ + prboom_5_compatibility, /* PrBoom 2.4.0 */ + prboom_6_compatibility, /* Latest PrBoom */ + MAX_COMPATIBILITY_LEVEL, /* Must be last entry */ + /* Aliases follow */ + boom_compatibility = boom_201_compatibility, /* Alias used by G_Compatibility */ + best_compatibility = prboom_6_compatibility, +} complevel_t; + +/* cph - from v_video.h, needed by gl_struct.h */ +enum patch_translation_e { + VPT_NONE = 0, // Normal + VPT_FLIP = 1, // Flip image horizontally + VPT_TRANS = 2, // Translate image via a translation table + VPT_STRETCH = 4, // Stretch to compensate for high-res +}; + +#endif diff --git a/src/dstrings.c b/src/dstrings.c new file mode 100644 index 0000000..4e27ea5 --- /dev/null +++ b/src/dstrings.c @@ -0,0 +1,85 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Globally defined strings. + * + *----------------------------------------------------------------------------- + */ + +#ifdef __GNUG__ +#pragma implementation "dstrings.h" +#endif +#include "dstrings.h" + + +// killough 1/18/98: remove hardcoded limit, add const: +const char *const endmsg[]= +{ + // DOOM1 + QUITMSG, + "please don't leave, there's more\ndemons to toast!", + "let's beat it -- this is turning\ninto a bloodbath!", + "i wouldn't leave if i were you.\ndos is much worse.", + "you're trying to say you like dos\nbetter than me, right?", + "don't leave yet -- there's a\ndemon around that corner!", + "ya know, next time you come in here\ni'm gonna toast ya.", + "go ahead and leave. see if i care.", // 1/15/98 killough + + // QuitDOOM II messages + "you want to quit?\nthen, thou hast lost an eighth!", + "don't go now, there's a \ndimensional shambler waiting\nat the dos prompt!", + "get outta here and go back\nto your boring programs.", + "if i were your boss, i'd \n deathmatch ya in a minute!", + "look, bud. you leave now\nand you forfeit your body count!", + "just leave. when you come\nback, i'll be waiting with a bat.", + "you're lucky i don't smack\nyou for thinking about leaving.", // 1/15/98 killough + + // FinalDOOM? + +// Note that these ending "bad taste" strings were commented out +// in the original id code as the #else case of an #if 1 +// Obviously they were internal playthings before the release of +// DOOM2 and were not intended for public use. +// +// Following messages commented out for now. Bad taste. // phares + +// "fuck you, pussy!\nget the fuck out!", +// "you quit and i'll jizz\nin your cystholes!", +// "if you leave, i'll make\nthe lord drink my jizz.", +// "hey, ron! can we say\n'fuck' in the game?", +// "i'd leave: this is just\nmore monsters and levels.\nwhat a load.", +// "suck it down, asshole!\nyou're a fucking wimp!", +// "don't quit now! we're \nstill spending your money!", + + // Internal debug. Different style, too. + "THIS IS NO MESSAGE!\nPage intentionally left blank.", // 1/15/98 killough +}; + +// killough 1/18/98: remove hardcoded limit and replace with var (silly hack): +const size_t NUM_QUITMESSAGES = sizeof(endmsg)/sizeof(*endmsg) - 1; diff --git a/src/dstrings.h b/src/dstrings.h new file mode 100644 index 0000000..abb77f8 --- /dev/null +++ b/src/dstrings.h @@ -0,0 +1,80 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * DOOM strings, by language. + * Note: In BOOM, some new strings hav ebeen defined that are + * not found in the French version. A better approach is + * to create a BEX text-replacement file for other + * languages since any language can be supported that way + * without recompiling the program. + * + *-----------------------------------------------------------------------------*/ + +#ifndef __DSTRINGS__ +#define __DSTRINGS__ + +/* All important printed strings. + * Language selection (message strings). + * Use -DFRENCH etc. + */ + +#ifdef FRENCH +#include "d_french.h" +#else +#include "d_englsh.h" +#endif + +/* Note this is not externally modifiable through DEH/BEX + * Misc. other strings. + * #define SAVEGAMENAME "boomsav" * killough 3/22/98 * + * Ty 05/04/98 - replaced with a modifiable string, see d_deh.c + */ + +/* + * File locations, + * relative to current position. + * Path names are OS-sensitive. + */ +#define DEVMAPS "devmaps" +#define DEVDATA "devdata" + + +/* Not done in french? + * QuitDOOM messages * + * killough 1/18/98: + * replace hardcoded limit with extern var (silly hack, I know) + */ + +#include + +extern const size_t NUM_QUITMESSAGES; /* Calculated in dstrings.c */ + +extern const char* const endmsg[]; /* killough 1/18/98 const added */ + +#endif diff --git a/src/f_finale.c b/src/f_finale.c new file mode 100644 index 0000000..1a96c33 --- /dev/null +++ b/src/f_finale.c @@ -0,0 +1,668 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Game completion, final screen animation. + * + *----------------------------------------------------------------------------- + */ + +#include "doomstat.h" +#include "d_event.h" +#include "v_video.h" +#include "w_wad.h" +#include "s_sound.h" +#include "sounds.h" +#include "d_deh.h" // Ty 03/22/98 - externalizations +#include "f_finale.h" // CPhipps - hmm... + +// Stage of animation: +// 0 = text, 1 = art screen, 2 = character cast +static int finalestage; // cph - +static int finalecount; // made static +static const char* finaletext; // cph - +static const char* finaleflat; // made static const + +// defines for the end mission display text // phares + +#define TEXTSPEED 3 // original value // phares +#define TEXTWAIT 250 // original value // phares +#define NEWTEXTSPEED 0.01f // new value // phares +#define NEWTEXTWAIT 1000 // new value // phares + +// CPhipps - removed the old finale screen text message strings; +// they were commented out for ages already +// Ty 03/22/98 - ... the new s_WHATEVER extern variables are used +// in the code below instead. + +void F_StartCast (void); +void F_CastTicker (void); +boolean F_CastResponder (event_t *ev); +void F_CastDrawer (void); + +void WI_checkForAccelerate(void); // killough 3/28/98: used to +extern int acceleratestage; // accelerate intermission screens +static int midstage; // whether we're in "mid-stage" + +// +// F_StartFinale +// +void F_StartFinale (void) +{ + gameaction = ga_nothing; + gamestate = GS_FINALE; + automapmode &= ~am_active; + + // killough 3/28/98: clear accelerative text flags + acceleratestage = midstage = 0; + + // Okay - IWAD dependend stuff. + // This has been changed severly, and + // some stuff might have changed in the process. + switch ( gamemode ) + { + // DOOM 1 - E1, E3 or E4, but each nine missions + case shareware: + case registered: + case retail: + { + S_ChangeMusic(mus_victor, true); + + switch (gameepisode) + { + case 1: + finaleflat = bgflatE1; // Ty 03/30/98 - new externalized bg flats + finaletext = s_E1TEXT; // Ty 03/23/98 - Was e1text variable. + break; + case 2: + finaleflat = bgflatE2; + finaletext = s_E2TEXT; // Ty 03/23/98 - Same stuff for each + break; + case 3: + finaleflat = bgflatE3; + finaletext = s_E3TEXT; + break; + case 4: + finaleflat = bgflatE4; + finaletext = s_E4TEXT; + break; + default: + // Ouch. + break; + } + break; + } + + // DOOM II and missions packs with E1, M34 + case commercial: + { + S_ChangeMusic(mus_read_m, true); + + // Ty 08/27/98 - added the gamemission logic + switch (gamemap) + { + case 6: + finaleflat = bgflat06; + finaletext = (gamemission==pack_tnt) ? s_T1TEXT : + (gamemission==pack_plut) ? s_P1TEXT : s_C1TEXT; + break; + case 11: + finaleflat = bgflat11; + finaletext = (gamemission==pack_tnt) ? s_T2TEXT : + (gamemission==pack_plut) ? s_P2TEXT : s_C2TEXT; + break; + case 20: + finaleflat = bgflat20; + finaletext = (gamemission==pack_tnt) ? s_T3TEXT : + (gamemission==pack_plut) ? s_P3TEXT : s_C3TEXT; + break; + case 30: + finaleflat = bgflat30; + finaletext = (gamemission==pack_tnt) ? s_T4TEXT : + (gamemission==pack_plut) ? s_P4TEXT : s_C4TEXT; + break; + case 15: + finaleflat = bgflat15; + finaletext = (gamemission==pack_tnt) ? s_T5TEXT : + (gamemission==pack_plut) ? s_P5TEXT : s_C5TEXT; + break; + case 31: + finaleflat = bgflat31; + finaletext = (gamemission==pack_tnt) ? s_T6TEXT : + (gamemission==pack_plut) ? s_P6TEXT : s_C6TEXT; + break; + default: + // Ouch. + break; + } + break; + // Ty 08/27/98 - end gamemission logic + } + + // Indeterminate. + default: // Ty 03/30/98 - not externalized + S_ChangeMusic(mus_read_m, true); + finaleflat = "F_SKY1"; // Not used anywhere else. + finaletext = s_C1TEXT; // FIXME - other text, music? + break; + } + + finalestage = 0; + finalecount = 0; +} + + + +boolean F_Responder (event_t *event) +{ + if (finalestage == 2) + return F_CastResponder (event); + + return false; +} + +// Get_TextSpeed() returns the value of the text display speed // phares +// Rewritten to allow user-directed acceleration -- killough 3/28/98 + +static float Get_TextSpeed(void) +{ + return midstage ? NEWTEXTSPEED : (midstage=acceleratestage) ? + acceleratestage=0, NEWTEXTSPEED : TEXTSPEED; +} + + +// +// F_Ticker +// +// killough 3/28/98: almost totally rewritten, to use +// player-directed acceleration instead of constant delays. +// Now the player can accelerate the text display by using +// the fire/use keys while it is being printed. The delay +// automatically responds to the user, and gives enough +// time to read. +// +// killough 5/10/98: add back v1.9 demo compatibility +// + +void F_Ticker(void) +{ + int i; + if (!demo_compatibility) + WI_checkForAccelerate(); // killough 3/28/98: check for acceleration + else + if (gamemode == commercial && finalecount > 50) // check for skipping + for (i=0; i strlen(finaletext)*speed + + (midstage ? NEWTEXTWAIT : TEXTWAIT) || + (midstage && acceleratestage)) { + if (gamemode != commercial) // Doom 1 / Ultimate Doom episode end + { // with enough time, it's automatic + finalecount = 0; + finalestage = 1; + wipegamestate = -1; // force a wipe + if (gameepisode == 3) + S_StartMusic(mus_bunny); + } + else // you must press a button to continue in Doom 2 + if (!demo_compatibility && midstage) + { + next_level: + if (gamemap == 30) + F_StartCast(); // cast of Doom 2 characters + else + gameaction = ga_worlddone; // next level, e.g. MAP07 + } + } + } +} + +// +// F_TextWrite +// +// This program displays the background and text at end-mission // phares +// text time. It draws both repeatedly so that other displays, // | +// like the main menu, can be drawn over it dynamically and // V +// erased dynamically. The TEXTSPEED constant is changed into +// the Get_TextSpeed function so that the speed of writing the // ^ +// text can be increased, and there's still time to read what's // | +// written. // phares +// CPhipps - reformatted + +#include "hu_stuff.h" +extern patchnum_t hu_font[HU_FONTSIZE]; + + +static void F_TextWrite (void) +{ + V_DrawBackground(finaleflat, 0); + { // draw some of the text onto the screen + int cx = 10; + int cy = 10; + const char* ch = finaletext; // CPhipps - const + int count = (int)((float)(finalecount - 10)/Get_TextSpeed()); // phares + int w; + + if (count < 0) + count = 0; + + for ( ; count ; count-- ) { + int c = *ch++; + + if (!c) + break; + if (c == '\n') { + cx = 10; + cy += 11; + continue; + } + + c = toupper(c) - HU_FONTSTART; + if (c < 0 || c> HU_FONTSIZE) { + cx += 4; + continue; + } + + w = hu_font[c].width; + if (cx+w > SCREENWIDTH) + break; + // CPhipps - patch drawing updated + V_DrawNumPatch(cx, cy, 0, hu_font[c].lumpnum, CR_DEFAULT, VPT_STRETCH); + cx+=w; + } + } +} + +// +// Final DOOM 2 animation +// Casting by id Software. +// in order of appearance +// +typedef struct +{ + const char **name; // CPhipps - const** + mobjtype_t type; +} castinfo_t; + +#define MAX_CASTORDER 18 /* Ty - hard coded for now */ +static const castinfo_t castorder[] = { // CPhipps - static const, initialised here + { &s_CC_ZOMBIE, MT_POSSESSED }, + { &s_CC_SHOTGUN, MT_SHOTGUY }, + { &s_CC_HEAVY, MT_CHAINGUY }, + { &s_CC_IMP, MT_TROOP }, + { &s_CC_DEMON, MT_SERGEANT }, + { &s_CC_LOST, MT_SKULL }, + { &s_CC_CACO, MT_HEAD }, + { &s_CC_HELL, MT_KNIGHT }, + { &s_CC_BARON, MT_BRUISER }, + { &s_CC_ARACH, MT_BABY }, + { &s_CC_PAIN, MT_PAIN }, + { &s_CC_REVEN, MT_UNDEAD }, + { &s_CC_MANCU, MT_FATSO }, + { &s_CC_ARCH, MT_VILE }, + { &s_CC_SPIDER, MT_SPIDER }, + { &s_CC_CYBER, MT_CYBORG }, + { &s_CC_HERO, MT_PLAYER }, + { NULL, 0} + }; + +int castnum; +int casttics; +state_t* caststate; +boolean castdeath; +int castframes; +int castonmelee; +boolean castattacking; + + +// +// F_StartCast +// + +void F_StartCast (void) +{ + wipegamestate = -1; // force a screen wipe + castnum = 0; + caststate = &states[mobjinfo[castorder[castnum].type].seestate]; + casttics = caststate->tics; + castdeath = false; + finalestage = 2; + castframes = 0; + castonmelee = 0; + castattacking = false; + S_ChangeMusic(mus_evil, true); +} + + +// +// F_CastTicker +// +void F_CastTicker (void) +{ + int st; + int sfx; + + if (--casttics > 0) + return; // not time to change state yet + + if (caststate->tics == -1 || caststate->nextstate == S_NULL) + { + // switch from deathstate to next monster + castnum++; + castdeath = false; + if (castorder[castnum].name == NULL) + castnum = 0; + if (mobjinfo[castorder[castnum].type].seesound) + S_StartSound (NULL, mobjinfo[castorder[castnum].type].seesound); + caststate = &states[mobjinfo[castorder[castnum].type].seestate]; + castframes = 0; + } + else + { + // just advance to next state in animation + if (caststate == &states[S_PLAY_ATK1]) + goto stopattack; // Oh, gross hack! + st = caststate->nextstate; + caststate = &states[st]; + castframes++; + + // sound hacks.... + switch (st) + { + case S_PLAY_ATK1: sfx = sfx_dshtgn; break; + case S_POSS_ATK2: sfx = sfx_pistol; break; + case S_SPOS_ATK2: sfx = sfx_shotgn; break; + case S_VILE_ATK2: sfx = sfx_vilatk; break; + case S_SKEL_FIST2: sfx = sfx_skeswg; break; + case S_SKEL_FIST4: sfx = sfx_skepch; break; + case S_SKEL_MISS2: sfx = sfx_skeatk; break; + case S_FATT_ATK8: + case S_FATT_ATK5: + case S_FATT_ATK2: sfx = sfx_firsht; break; + case S_CPOS_ATK2: + case S_CPOS_ATK3: + case S_CPOS_ATK4: sfx = sfx_shotgn; break; + case S_TROO_ATK3: sfx = sfx_claw; break; + case S_SARG_ATK2: sfx = sfx_sgtatk; break; + case S_BOSS_ATK2: + case S_BOS2_ATK2: + case S_HEAD_ATK2: sfx = sfx_firsht; break; + case S_SKULL_ATK2: sfx = sfx_sklatk; break; + case S_SPID_ATK2: + case S_SPID_ATK3: sfx = sfx_shotgn; break; + case S_BSPI_ATK2: sfx = sfx_plasma; break; + case S_CYBER_ATK2: + case S_CYBER_ATK4: + case S_CYBER_ATK6: sfx = sfx_rlaunc; break; + case S_PAIN_ATK3: sfx = sfx_sklatk; break; + default: sfx = 0; break; + } + + if (sfx) + S_StartSound (NULL, sfx); + } + + if (castframes == 12) + { + // go into attack frame + castattacking = true; + if (castonmelee) + caststate=&states[mobjinfo[castorder[castnum].type].meleestate]; + else + caststate=&states[mobjinfo[castorder[castnum].type].missilestate]; + castonmelee ^= 1; + if (caststate == &states[S_NULL]) + { + if (castonmelee) + caststate= + &states[mobjinfo[castorder[castnum].type].meleestate]; + else + caststate= + &states[mobjinfo[castorder[castnum].type].missilestate]; + } + } + + if (castattacking) + { + if (castframes == 24 + || caststate == &states[mobjinfo[castorder[castnum].type].seestate] ) + { + stopattack: + castattacking = false; + castframes = 0; + caststate = &states[mobjinfo[castorder[castnum].type].seestate]; + } + } + + casttics = caststate->tics; + if (casttics == -1) + casttics = 15; +} + + +// +// F_CastResponder +// + +boolean F_CastResponder (event_t* ev) +{ + if (ev->type != ev_keydown) + return false; + + if (castdeath) + return true; // already in dying frames + + // go into death frame + castdeath = true; + caststate = &states[mobjinfo[castorder[castnum].type].deathstate]; + casttics = caststate->tics; + castframes = 0; + castattacking = false; + if (mobjinfo[castorder[castnum].type].deathsound) + S_StartSound (NULL, mobjinfo[castorder[castnum].type].deathsound); + + return true; +} + + +static void F_CastPrint (const char* text) // CPhipps - static, const char* +{ + const char* ch; // CPhipps - const + int c; + int cx; + int w; + int width; + + // find width + ch = text; + width = 0; + + while (ch) + { + c = *ch++; + if (!c) + break; + c = toupper(c) - HU_FONTSTART; + if (c < 0 || c> HU_FONTSIZE) + { + width += 4; + continue; + } + + w = hu_font[c].width; + width += w; + } + + // draw it + cx = 160-width/2; + ch = text; + while (ch) + { + c = *ch++; + if (!c) + break; + c = toupper(c) - HU_FONTSTART; + if (c < 0 || c> HU_FONTSIZE) + { + cx += 4; + continue; + } + + w = hu_font[c].width; + // CPhipps - patch drawing updated + V_DrawNumPatch(cx, 180, 0, hu_font[c].lumpnum, CR_DEFAULT, VPT_STRETCH); + cx+=w; + } +} + + +// +// F_CastDrawer +// + +void F_CastDrawer (void) +{ + spritedef_t* sprdef; + spriteframe_t* sprframe; + int lump; + boolean flip; + + // erase the entire screen to a background + // CPhipps - patch drawing updated + V_DrawNamePatch(0,0,0, bgcastcall, CR_DEFAULT, VPT_STRETCH); // Ty 03/30/98 bg texture extern + + F_CastPrint (*(castorder[castnum].name)); + + // draw the current frame in the middle of the screen + sprdef = &sprites[caststate->sprite]; + sprframe = &sprdef->spriteframes[ caststate->frame & FF_FRAMEMASK]; + lump = sprframe->lump[0]; + flip = (boolean)sprframe->flip[0]; + + // CPhipps - patch drawing updated + V_DrawNumPatch(160, 170, 0, lump+firstspritelump, CR_DEFAULT, + VPT_STRETCH | (flip ? VPT_FLIP : 0)); +} + +// +// F_BunnyScroll +// +static const char pfub2[] = { "PFUB2" }; +static const char pfub1[] = { "PFUB1" }; + +static void F_BunnyScroll (void) +{ + char name[10]; + int stage; + static int laststage; + + { + int scrolled = 320 - (finalecount-230)/2; + if (scrolled <= 0) { + V_DrawNamePatch(0, 0, 0, pfub2, CR_DEFAULT, VPT_STRETCH); + } else if (scrolled >= 320) { + V_DrawNamePatch(0, 0, 0, pfub1, CR_DEFAULT, VPT_STRETCH); + } else { + V_DrawNamePatch(320-scrolled, 0, 0, pfub1, CR_DEFAULT, VPT_STRETCH); + V_DrawNamePatch(-scrolled, 0, 0, pfub2, CR_DEFAULT, VPT_STRETCH); + } + } + + if (finalecount < 1130) + return; + if (finalecount < 1180) + { + // CPhipps - patch drawing updated + V_DrawNamePatch((320-13*8)/2, (200-8*8)/2,0, "END0", CR_DEFAULT, VPT_STRETCH); + laststage = 0; + return; + } + + stage = (finalecount-1180) / 5; + if (stage > 6) + stage = 6; + if (stage > laststage) + { + S_StartSound (NULL, sfx_pistol); + laststage = stage; + } + + sprintf (name,"END%i",stage); + // CPhipps - patch drawing updated + V_DrawNamePatch((320-13*8)/2, (200-8*8)/2, 0, name, CR_DEFAULT, VPT_STRETCH); +} + + +// +// F_Drawer +// +void F_Drawer (void) +{ + if (finalestage == 2) + { + F_CastDrawer (); + return; + } + + if (!finalestage) + F_TextWrite (); + else + { + switch (gameepisode) + { + // CPhipps - patch drawing updated + case 1: + if ( gamemode == retail ) + V_DrawNamePatch(0, 0, 0, "CREDIT", CR_DEFAULT, VPT_STRETCH); + else + V_DrawNamePatch(0, 0, 0, "HELP2", CR_DEFAULT, VPT_STRETCH); + break; + case 2: + V_DrawNamePatch(0, 0, 0, "VICTORY2", CR_DEFAULT, VPT_STRETCH); + break; + case 3: + F_BunnyScroll (); + break; + case 4: + V_DrawNamePatch(0, 0, 0, "ENDPIC", CR_DEFAULT, VPT_STRETCH); + break; + } + } +} diff --git a/src/f_finale.h b/src/f_finale.h new file mode 100644 index 0000000..6214f38 --- /dev/null +++ b/src/f_finale.h @@ -0,0 +1,56 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Related to f_finale.c, which is called at the end of a level + * + *-----------------------------------------------------------------------------*/ + + +#ifndef __F_FINALE__ +#define __F_FINALE__ + +#include "doomtype.h" +#include "d_event.h" + +/* + * FINALE + */ + +/* Called by main loop. */ +boolean F_Responder (event_t* ev); + +/* Called by main loop. */ +void F_Ticker (void); + +/* Called by main loop. */ +void F_Drawer (void); + +void F_StartFinale (void); + +#endif diff --git a/src/f_wipe.c b/src/f_wipe.c new file mode 100644 index 0000000..fc3ac1d --- /dev/null +++ b/src/f_wipe.c @@ -0,0 +1,202 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Mission begin melt/wipe screen special effect. + * + *----------------------------------------------------------------------------- + */ + + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "z_zone.h" +#include "doomdef.h" +#include "i_video.h" +#include "v_video.h" +#include "m_random.h" +#include "f_wipe.h" + +// +// SCREEN WIPE PACKAGE +// + +// Parts re-written to support true-color video modes. Column-major +// formatting removed. - POPE + +// CPhipps - macros for the source and destination screens +#define SRC_SCR 2 +#define DEST_SCR 3 + +static screeninfo_t wipe_scr_start; +static screeninfo_t wipe_scr_end; +static screeninfo_t wipe_scr; + +static int y_lookup[MAX_SCREENWIDTH]; + + +static int wipe_initMelt(int ticks) +{ + int i; + + // copy start screen to main screen + for(i=0;i not ready to scroll yet) + y_lookup[0] = -(M_Random()%16); + for (i=1;i 0) + y_lookup[i] = 0; + else + if (y_lookup[i] == -16) + y_lookup[i] = -15; + } + return 0; +} + +static int wipe_doMelt(int ticks) +{ + boolean done = true; + int i; + const int depth = V_GetPixelDepth(); + + while (ticks--) { + for (i=0;i<(SCREENWIDTH);i++) { + if (y_lookup[i]<0) { + y_lookup[i]++; + done = false; + continue; + } + if (y_lookup[i] < SCREENHEIGHT) { + byte *s, *d; + int j, k, dy; + + /* cph 2001/07/29 - + * The original melt rate was 8 pixels/sec, i.e. 25 frames to melt + * the whole screen, so make the melt rate depend on SCREENHEIGHT + * so it takes no longer in high res + */ + dy = (y_lookup[i] < 16) ? y_lookup[i]+1 : SCREENHEIGHT/25; + if (y_lookup[i]+dy >= SCREENHEIGHT) + dy = SCREENHEIGHT - y_lookup[i]; + + s = wipe_scr_end.data + (y_lookup[i]*wipe_scr_end.byte_pitch+(i*depth)); + d = wipe_scr.data + (y_lookup[i]*wipe_scr.byte_pitch+(i*depth)); + for (j=dy;j;j--) { + for (k=0; k +#include +#include +#ifdef _MSC_VER +#define F_OK 0 /* Check for file existence */ +#define W_OK 2 /* Check for write permission */ +#define R_OK 4 /* Check for read permission */ +#include +#else +#include +#endif +#include + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "doomstat.h" +#include "d_net.h" +#include "f_finale.h" +#include "m_argv.h" +#include "m_misc.h" +#include "m_menu.h" +#include "m_random.h" +#include "p_setup.h" +#include "p_saveg.h" +#include "p_tick.h" +#include "p_map.h" +#include "p_checksum.h" +#include "d_main.h" +#include "wi_stuff.h" +#include "hu_stuff.h" +#include "st_stuff.h" +#include "am_map.h" +#include "w_wad.h" +#include "r_main.h" +#include "r_draw.h" +#include "p_map.h" +#include "s_sound.h" +#include "dstrings.h" +#include "sounds.h" +#include "r_data.h" +#include "r_sky.h" +#include "d_deh.h" // Ty 3/27/98 deh declarations +#include "p_inter.h" +#include "g_game.h" +#include "lprintf.h" +#include "i_main.h" +#include "i_system.h" +#include "r_demo.h" +#include "r_fps.h" + +#define SAVEGAMESIZE 0x20000 +#define SAVESTRINGSIZE 24 + +static size_t savegamesize = SAVEGAMESIZE; // killough +static boolean netdemo; +static const byte *demobuffer; /* cph - only used for playback */ +static int demolength; // check for overrun (missing DEMOMARKER) +static FILE *demofp; /* cph - record straight to file */ +static const byte *demo_p; +static short consistancy[MAXPLAYERS][BACKUPTICS]; + +gameaction_t gameaction; +gamestate_t gamestate; +skill_t gameskill; +boolean respawnmonsters; +int gameepisode; +int gamemap; +boolean paused; +// CPhipps - moved *_loadgame vars here +static boolean forced_loadgame = false; +static boolean command_loadgame = false; + +boolean usergame; // ok to save / end game +boolean timingdemo; // if true, exit with report on completion +boolean fastdemo; // if true, run at full speed -- killough +boolean nodrawers; // for comparative timing purposes +boolean noblit; // for comparative timing purposes +int starttime; // for comparative timing purposes +boolean deathmatch; // only if started as net death +boolean netgame; // only true if packets are broadcast +boolean playeringame[MAXPLAYERS]; +player_t players[MAXPLAYERS]; +int consoleplayer; // player taking events and displaying +int displayplayer; // view being displayed +int gametic; +int basetic; /* killough 9/29/98: for demo sync */ +int totalkills, totallive, totalitems, totalsecret; // for intermission +boolean demorecording; +boolean demoplayback; +int demover; +boolean singledemo; // quit after playing a demo from cmdline +wbstartstruct_t wminfo; // parms for world map / intermission +boolean haswolflevels = false;// jff 4/18/98 wolf levels present +static byte *savebuffer; // CPhipps - static +int autorun = false; // always running? // phares +int totalleveltimes; // CPhipps - total time for all completed levels +int longtics; + +// +// controls (have defaults) +// + +int key_right; +int key_left; +int key_up; +int key_down; +int key_menu_right; // phares 3/7/98 +int key_menu_left; // | +int key_menu_up; // V +int key_menu_down; +int key_menu_backspace; // ^ +int key_menu_escape; // | +int key_menu_enter; // phares 3/7/98 +int key_strafeleft; +int key_straferight; +int key_fire; +int key_use; +int key_strafe; +int key_speed; +int key_escape = KEYD_ESCAPE; // phares 4/13/98 +int key_savegame; // phares +int key_loadgame; // | +int key_autorun; // V +int key_reverse; +int key_zoomin; +int key_zoomout; +int key_chat; +int key_backspace; +int key_enter; +int key_map_right; +int key_map_left; +int key_map_up; +int key_map_down; +int key_map_zoomin; +int key_map_zoomout; +int key_map; +int key_map_gobig; +int key_map_follow; +int key_map_mark; +int key_map_clear; +int key_map_grid; +int key_map_overlay; // cph - map overlay +int key_map_rotate; // cph - map rotation +int key_help = KEYD_F1; // phares 4/13/98 +int key_soundvolume; +int key_hud; +int key_quicksave; +int key_endgame; +int key_messages; +int key_quickload; +int key_quit; +int key_gamma; +int key_spy; +int key_pause; +int key_setup; +int destination_keys[MAXPLAYERS]; +int key_weapontoggle; +int key_weapon1; +int key_weapon2; +int key_weapon3; +int key_weapon4; +int key_weapon5; +int key_weapon6; +int key_weapon7; // ^ +int key_weapon8; // | +int key_weapon9; // phares + +int key_screenshot; // killough 2/22/98: screenshot key +int mousebfire; +int mousebstrafe; +int mousebforward; +int joybfire; +int joybstrafe; +int joybuse; +int joybspeed; + +#define MAXPLMOVE (forwardmove[1]) +#define TURBOTHRESHOLD 0x32 +#define SLOWTURNTICS 6 +#define QUICKREVERSE (short)32768 // 180 degree reverse // phares +#define NUMKEYS 512 + +fixed_t forwardmove[2] = {0x19, 0x32}; +fixed_t sidemove[2] = {0x18, 0x28}; +fixed_t angleturn[3] = {640, 1280, 320}; // + slow turn + +// CPhipps - made lots of key/button state vars static +static boolean gamekeydown[NUMKEYS]; +static int turnheld; // for accelerative turning + +static boolean mousearray[4]; +static boolean *mousebuttons = &mousearray[1]; // allow [-1] + +// mouse values are used once +static int mousex; +static int mousey; +static int dclicktime; +static int dclickstate; +static int dclicks; +static int dclicktime2; +static int dclickstate2; +static int dclicks2; + +// joystick values are repeated +static int joyxmove; +static int joyymove; +static boolean joyarray[5]; +static boolean *joybuttons = &joyarray[1]; // allow [-1] + +// Game events info +static buttoncode_t special_event; // Event triggered by local player, to send +static byte savegameslot; // Slot to load if gameaction == ga_loadgame +char savedescription[SAVEDESCLEN]; // Description to save in savegame if gameaction == ga_savegame + +//jff 3/24/98 define defaultskill here +int defaultskill; //note 1-based + +// killough 2/8/98: make corpse queue variable in size +int bodyqueslot, bodyquesize; // killough 2/8/98 +mobj_t **bodyque = 0; // phares 8/10/98 + +static void G_DoSaveGame (boolean menu); +static const byte* G_ReadDemoHeader(const byte* demo_p, size_t size, boolean failonerror); + +// +// G_BuildTiccmd +// Builds a ticcmd from all of the available inputs +// or reads it from the demo buffer. +// If recording a demo, write it out +// +static inline signed char fudgef(signed char b) +{ + static int c; + if (!b || !demo_compatibility || longtics) return b; + if (++c & 0x1f) return b; + b |= 1; if (b>2) b-=2; + return b; +} + +static inline signed short fudgea(signed short b) +{ + if (!b || !demo_compatibility || !longtics) return b; + b |= 1; if (b>2) b-=2; + return b; +} + + +void G_BuildTiccmd(ticcmd_t* cmd) +{ + boolean strafe; + boolean bstrafe; + int speed; + int tspeed; + int forward; + int side; + int newweapon; // phares + /* cphipps - remove needless I_BaseTiccmd call, just set the ticcmd to zero */ + memset(cmd,0,sizeof*cmd); + cmd->consistancy = consistancy[consoleplayer][maketic%BACKUPTICS]; + + strafe = gamekeydown[key_strafe] || mousebuttons[mousebstrafe] + || joybuttons[joybstrafe]; + //e6y: the "RUN" key inverts the autorun state + speed = (gamekeydown[key_speed] || joybuttons[joybspeed] ? !autorun : autorun); // phares + + forward = side = 0; + + // use two stage accelerative turning + // on the keyboard and joystick + if (joyxmove < 0 || joyxmove > 0 || + gamekeydown[key_right] || gamekeydown[key_left]) + turnheld += ticdup; + else + turnheld = 0; + + if (turnheld < SLOWTURNTICS) + tspeed = 2; // slow turn + else + tspeed = speed; + + // turn 180 degrees in one keystroke? // phares + // | + if (gamekeydown[key_reverse]) // V + { + cmd->angleturn += QUICKREVERSE; // ^ + gamekeydown[key_reverse] = false; // | + } // phares + + // let movement keys cancel each other out + + if (strafe) + { + if (gamekeydown[key_right]) + side += sidemove[speed]; + if (gamekeydown[key_left]) + side -= sidemove[speed]; + if (joyxmove > 0) + side += sidemove[speed]; + if (joyxmove < 0) + side -= sidemove[speed]; + } + else + { + if (gamekeydown[key_right]) + cmd->angleturn -= angleturn[tspeed]; + if (gamekeydown[key_left]) + cmd->angleturn += angleturn[tspeed]; + if (joyxmove > 0) + cmd->angleturn -= angleturn[tspeed]; + if (joyxmove < 0) + cmd->angleturn += angleturn[tspeed]; + } + + if (gamekeydown[key_up]) + forward += forwardmove[speed]; + if (gamekeydown[key_down]) + forward -= forwardmove[speed]; + if (joyymove < 0) + forward += forwardmove[speed]; + if (joyymove > 0) + forward -= forwardmove[speed]; + if (gamekeydown[key_straferight]) + side += sidemove[speed]; + if (gamekeydown[key_strafeleft]) + side -= sidemove[speed]; + + // buttons + cmd->chatchar = HU_dequeueChatChar(); + + if (gamekeydown[key_fire] || mousebuttons[mousebfire] || + joybuttons[joybfire]) + cmd->buttons |= BT_ATTACK; + + if (gamekeydown[key_use] || joybuttons[joybuse]) + { + cmd->buttons |= BT_USE; + // clear double clicks if hit use button + dclicks = 0; + } + + // Toggle between the top 2 favorite weapons. // phares + // If not currently aiming one of these, switch to // phares + // the favorite. Only switch if you possess the weapon. // phares + + // killough 3/22/98: + // + // Perform automatic weapons switch here rather than in p_pspr.c, + // except in demo_compatibility mode. + // + // killough 3/26/98, 4/2/98: fix autoswitch when no weapons are left + + if ((!demo_compatibility && players[consoleplayer].attackdown && // killough + !P_CheckAmmo(&players[consoleplayer])) || gamekeydown[key_weapontoggle]) + newweapon = P_SwitchWeapon(&players[consoleplayer]); // phares + else + { // phares 02/26/98: Added gamemode checks + newweapon = + gamekeydown[key_weapon1] ? wp_fist : // killough 5/2/98: reformatted + gamekeydown[key_weapon2] ? wp_pistol : + gamekeydown[key_weapon3] ? wp_shotgun : + gamekeydown[key_weapon4] ? wp_chaingun : + gamekeydown[key_weapon5] ? wp_missile : + gamekeydown[key_weapon6] && gamemode != shareware ? wp_plasma : + gamekeydown[key_weapon7] && gamemode != shareware ? wp_bfg : + gamekeydown[key_weapon8] ? wp_chainsaw : + (!demo_compatibility && gamekeydown[key_weapon9] && gamemode == commercial) ? wp_supershotgun : + wp_nochange; + + // killough 3/22/98: For network and demo consistency with the + // new weapons preferences, we must do the weapons switches here + // instead of in p_user.c. But for old demos we must do it in + // p_user.c according to the old rules. Therefore demo_compatibility + // determines where the weapons switch is made. + + // killough 2/8/98: + // Allow user to switch to fist even if they have chainsaw. + // Switch to fist or chainsaw based on preferences. + // Switch to shotgun or SSG based on preferences. + + if (!demo_compatibility) + { + const player_t *player = &players[consoleplayer]; + + // only select chainsaw from '1' if it's owned, it's + // not already in use, and the player prefers it or + // the fist is already in use, or the player does not + // have the berserker strength. + + if (newweapon==wp_fist && player->weaponowned[wp_chainsaw] && + player->readyweapon!=wp_chainsaw && + (player->readyweapon==wp_fist || + !player->powers[pw_strength] || + P_WeaponPreferred(wp_chainsaw, wp_fist))) + newweapon = wp_chainsaw; + + // Select SSG from '3' only if it's owned and the player + // does not have a shotgun, or if the shotgun is already + // in use, or if the SSG is not already in use and the + // player prefers it. + + if (newweapon == wp_shotgun && gamemode == commercial && + player->weaponowned[wp_supershotgun] && + (!player->weaponowned[wp_shotgun] || + player->readyweapon == wp_shotgun || + (player->readyweapon != wp_supershotgun && + P_WeaponPreferred(wp_supershotgun, wp_shotgun)))) + newweapon = wp_supershotgun; + } + // killough 2/8/98, 3/22/98 -- end of weapon selection changes + } + + if (newweapon != wp_nochange) + { + cmd->buttons |= BT_CHANGE; + cmd->buttons |= newweapon< 1 ) + { + dclickstate = mousebuttons[mousebforward]; + if (dclickstate) + dclicks++; + if (dclicks == 2) + { + cmd->buttons |= BT_USE; + dclicks = 0; + } + else + dclicktime = 0; + } + else + if ((dclicktime += ticdup) > 20) + { + dclicks = 0; + dclickstate = 0; + } + + // strafe double click + + bstrafe = mousebuttons[mousebstrafe] || joybuttons[joybstrafe]; + if (bstrafe != dclickstate2 && dclicktime2 > 1 ) + { + dclickstate2 = bstrafe; + if (dclickstate2) + dclicks2++; + if (dclicks2 == 2) + { + cmd->buttons |= BT_USE; + dclicks2 = 0; + } + else + dclicktime2 = 0; + } + else + if ((dclicktime2 += ticdup) > 20) + { + dclicks2 = 0; + dclickstate2 = 0; + } + forward += mousey; + if (strafe) + side += mousex / 4; /* mead Don't want to strafe as fast as turns.*/ + else + cmd->angleturn -= mousex; /* mead now have enough dynamic range 2-10-00 */ + + mousex = mousey = 0; + + if (forward > MAXPLMOVE) + forward = MAXPLMOVE; + else if (forward < -MAXPLMOVE) + forward = -MAXPLMOVE; + if (side > MAXPLMOVE) + side = MAXPLMOVE; + else if (side < -MAXPLMOVE) + side = -MAXPLMOVE; + + cmd->forwardmove += fudgef((signed char)forward); + cmd->sidemove += side; + cmd->angleturn = fudgea(cmd->angleturn); + + // CPhipps - special events (game new/load/save/pause) + if (special_event & BT_SPECIAL) { + cmd->buttons = special_event; + special_event = 0; + } +} + +// +// G_RestartLevel +// + +void G_RestartLevel(void) +{ + special_event = BT_SPECIAL | (BTS_RESTARTLEVEL & BT_SPECIALMASK); +} + +#include "z_bmalloc.h" +// +// G_DoLoadLevel +// + +static void G_DoLoadLevel (void) +{ + int i; + + // Set the sky map. + // First thing, we have a dummy sky texture name, + // a flat. The data is in the WAD only because + // we look for an actual index, instead of simply + // setting one. + + skyflatnum = R_FlatNumForName ( SKYFLATNAME ); + + // DOOM determines the sky texture to be used + // depending on the current episode, and the game version. + if (gamemode == commercial) + // || gamemode == pack_tnt //jff 3/27/98 sorry guys pack_tnt,pack_plut + // || gamemode == pack_plut) //aren't gamemodes, this was matching retail + { + skytexture = R_TextureNumForName ("SKY3"); + if (gamemap < 12) + skytexture = R_TextureNumForName ("SKY1"); + else + if (gamemap < 21) + skytexture = R_TextureNumForName ("SKY2"); + } + else //jff 3/27/98 and lets not forget about DOOM and Ultimate DOOM huh? + switch (gameepisode) + { + case 1: + skytexture = R_TextureNumForName ("SKY1"); + break; + case 2: + skytexture = R_TextureNumForName ("SKY2"); + break; + case 3: + skytexture = R_TextureNumForName ("SKY3"); + break; + case 4: // Special Edition sky + skytexture = R_TextureNumForName ("SKY4"); + break; + }//jff 3/27/98 end sky setting fix + + /* cph 2006/07/31 - took out unused levelstarttic variable */ + + if (!demo_compatibility && !mbf_features) // killough 9/29/98 + basetic = gametic; + + if (wipegamestate == GS_LEVEL) + wipegamestate = -1; // force a wipe + + gamestate = GS_LEVEL; + + for (i=0 ; idata1 == key_spy && netgame && (demoplayback || !deathmatch) && + gamestate == GS_LEVEL) + { + if (ev->type == ev_keyup) + gamekeydown[key_spy] = false; + if (ev->type == ev_keydown && !gamekeydown[key_spy]) + { + gamekeydown[key_spy] = true; + do // spy mode + if (++displayplayer >= MAXPLAYERS) + displayplayer = 0; + while (!playeringame[displayplayer] && displayplayer!=consoleplayer); + + ST_Start(); // killough 3/7/98: switch status bar views too + HU_Start(); + S_UpdateSounds(players[displayplayer].mo); + R_ActivateSectorInterpolations(); + R_SmoothPlaying_Reset(NULL); + } + return true; + } + + // any other key pops up menu if in demos + // + // killough 8/2/98: enable automap in -timedemo demos + // + // killough 9/29/98: make any key pop up menu regardless of + // which kind of demo, and allow other events during playback + + if (gameaction == ga_nothing && (demoplayback || gamestate == GS_DEMOSCREEN)) + { + // killough 9/29/98: allow user to pause demos during playback + if (ev->type == ev_keydown && ev->data1 == key_pause) + { + if (paused ^= 2) + S_PauseSound(); + else + S_ResumeSound(); + return true; + } + + // killough 10/98: + // Don't pop up menu, if paused in middle + // of demo playback, or if automap active. + // Don't suck up keys, which may be cheats + + return gamestate == GS_DEMOSCREEN && + !(paused & 2) && !(automapmode & am_active) && + ((ev->type == ev_keydown) || + (ev->type == ev_mouse && ev->data1) || + (ev->type == ev_joystick && ev->data1)) ? + M_StartControlPanel(), true : false; + } + + if (gamestate == GS_FINALE && F_Responder(ev)) + return true; // finale ate the event + + switch (ev->type) + { + case ev_keydown: + if (ev->data1 == key_pause) // phares + { + special_event = BT_SPECIAL | (BTS_PAUSE & BT_SPECIALMASK); + return true; + } + if (ev->data1 data1] = true; + return true; // eat key down events + + case ev_keyup: + if (ev->data1 data1] = false; + return false; // always let key up events filter down + + case ev_mouse: + mousebuttons[0] = ev->data1 & 1; + mousebuttons[1] = ev->data1 & 2; + mousebuttons[2] = ev->data1 & 4; + /* + * bmead@surfree.com + * Modified by Barry Mead after adding vastly more resolution + * to the Mouse Sensitivity Slider in the options menu 1-9-2000 + * Removed the mouseSensitivity "*4" to allow more low end + * sensitivity resolution especially for lsdoom users. + */ + mousex += (ev->data2*(mouseSensitivity_horiz))/10; /* killough */ + mousey += (ev->data3*(mouseSensitivity_vert))/10; /*Mead rm *4 */ + return true; // eat events + + case ev_joystick: + joybuttons[0] = ev->data1 & 1; + joybuttons[1] = ev->data1 & 2; + joybuttons[2] = ev->data1 & 4; + joybuttons[3] = ev->data1 & 8; + joyxmove = ev->data2; + joyymove = ev->data3; + return true; // eat events + + default: + break; + } + return false; +} + +// +// G_Ticker +// Make ticcmd_ts for the players. +// + +void G_Ticker (void) +{ + int i; + static gamestate_t prevgamestate; + + // CPhipps - player colour changing + if (!demoplayback && mapcolor_plyr[consoleplayer] != mapcolor_me) { + // Changed my multiplayer colour - Inform the whole game + int net_cl = LONG(mapcolor_me); +#ifdef HAVE_NET + D_NetSendMisc(nm_plcolour, sizeof(net_cl), &net_cl); +#endif + G_ChangedPlayerColour(consoleplayer, mapcolor_me); + } + P_MapStart(); + // do player reborns if needed + for (i=0 ; iforwardmove > TURBOTHRESHOLD && + !(gametic&31) && ((gametic>>5)&3) == i ) + { + extern char *player_names[]; + /* cph - don't use sprintf, use doom_printf */ + doom_printf ("%s is turbo!", player_names[i]); + } + + if (netgame && !netdemo && !(gametic%ticdup) ) + { + if (gametic > BACKUPTICS + && consistancy[i][buf] != cmd->consistancy) + I_Error("G_Ticker: Consistency failure (%i should be %i)", + cmd->consistancy, consistancy[i][buf]); + if (players[i].mo) + consistancy[i][buf] = players[i].mo->x; + else + consistancy[i][buf] = 0; // killough 2/14/98 + } + } + } + + // check for special buttons + for (i=0; i>BTS_SAVESHIFT; + gameaction = ga_savegame; + break; + + // CPhipps - remote loadgame request + case BTS_LOADGAME: + savegameslot = + (players[i].cmd.buttons & BTS_SAVEMASK)>>BTS_SAVESHIFT; + gameaction = ga_loadgame; + forced_loadgame = netgame; // Force if a netgame + command_loadgame = false; + break; + + // CPhipps - Restart the level + case BTS_RESTARTLEVEL: + if (demoplayback || (compatibility_level < lxdoom_1_compatibility)) + break; // CPhipps - Ignore in demos or old games + gameaction = ga_loadlevel; + break; + } + players[i].cmd.buttons = 0; + } + } + } + } + + // cph - if the gamestate changed, we may need to clean up the old gamestate + if (gamestate != prevgamestate) { + switch (prevgamestate) { + case GS_LEVEL: + // This causes crashes at level end - Neil Stevens + // The crash is because the sounds aren't stopped before freeing them + // the following is a possible fix + // This fix does avoid the crash wowever, with this fix in, the exit + // switch sound is cut off + // S_Stop(); + // Z_FreeTags(PU_LEVEL, PU_PURGELEVEL-1); + break; + case GS_INTERMISSION: + WI_End(); + default: + break; + } + prevgamestate = gamestate; + } + + // e6y + // do nothing if a pause has been pressed during playback + // pausing during intermission can cause desynchs without that + if (paused & 2 && gamestate != GS_LEVEL) + return; + + // do main actions + switch (gamestate) + { + case GS_LEVEL: + P_Ticker (); + ST_Ticker (); + AM_Ticker (); + HU_Ticker (); + break; + + case GS_INTERMISSION: + WI_Ticker (); + break; + + case GS_FINALE: + F_Ticker (); + break; + + case GS_DEMOSCREEN: + D_PageTicker (); + break; + } +} + +// +// PLAYER STRUCTURE FUNCTIONS +// also see P_SpawnPlayer in P_Things +// + +// +// G_PlayerFinishLevel +// Can when a player completes a level. +// + +static void G_PlayerFinishLevel(int player) +{ + player_t *p = &players[player]; + memset(p->powers, 0, sizeof p->powers); + memset(p->cards, 0, sizeof p->cards); + p->mo = NULL; // cph - this is allocated PU_LEVEL so it's gone + p->extralight = 0; // cancel gun flashes + p->fixedcolormap = 0; // cancel ir gogles + p->damagecount = 0; // no palette changes + p->bonuscount = 0; +} + +// CPhipps - G_SetPlayerColour +// Player colours stuff +// +// G_SetPlayerColour + +#include "r_draw.h" + +void G_ChangedPlayerColour(int pn, int cl) +{ + int i; + + if (!netgame) return; + + mapcolor_plyr[pn] = cl; + + // Rebuild colour translation tables accordingly + R_InitTranslationTables(); + // Change translations on existing player mobj's + for (i=0; iflags &= ~MF_TRANSLATION; + players[i].mo->flags |= playernumtotrans[i] << MF_TRANSSHIFT; + } + } +} + +// +// G_PlayerReborn +// Called after a player dies +// almost everything is cleared and initialized +// + +void G_PlayerReborn (int player) +{ + player_t *p; + int i; + int frags[MAXPLAYERS]; + int killcount; + int itemcount; + int secretcount; + + memcpy (frags, players[player].frags, sizeof frags); + killcount = players[player].killcount; + itemcount = players[player].itemcount; + secretcount = players[player].secretcount; + + p = &players[player]; + + // killough 3/10/98,3/21/98: preserve cheats across idclev + { + int cheats = p->cheats; + memset (p, 0, sizeof(*p)); + p->cheats = cheats; + } + + memcpy(players[player].frags, frags, sizeof(players[player].frags)); + players[player].killcount = killcount; + players[player].itemcount = itemcount; + players[player].secretcount = secretcount; + + p->usedown = p->attackdown = true; // don't do anything immediately + p->playerstate = PST_LIVE; + p->health = initial_health; // Ty 03/12/98 - use dehacked values + p->readyweapon = p->pendingweapon = wp_pistol; + p->weaponowned[wp_fist] = true; + p->weaponowned[wp_pistol] = true; + p->ammo[am_clip] = initial_bullets; // Ty 03/12/98 - use dehacked values + + for (i=0 ; imaxammo[i] = maxammo[i]; +} + +// +// G_CheckSpot +// Returns false if the player cannot be respawned +// at the given mapthing_t spot +// because something is occupying it +// + +static boolean G_CheckSpot(int playernum, mapthing_t *mthing) +{ + fixed_t x,y; + subsector_t *ss; + int i; + + if (!players[playernum].mo) + { + // first spawn of level, before corpses + for (i=0 ; ix == mthing->x << FRACBITS + && players[i].mo->y == mthing->y << FRACBITS) + return false; + return true; + } + + x = mthing->x << FRACBITS; + y = mthing->y << FRACBITS; + + // killough 4/2/98: fix bug where P_CheckPosition() uses a non-solid + // corpse to detect collisions with other players in DM starts + // + // Old code: + // if (!P_CheckPosition (players[playernum].mo, x, y)) + // return false; + + players[playernum].mo->flags |= MF_SOLID; + i = P_CheckPosition(players[playernum].mo, x, y); + players[playernum].mo->flags &= ~MF_SOLID; + if (!i) + return false; + + // flush an old corpse if needed + // killough 2/8/98: make corpse queue have an adjustable limit + // killough 8/1/98: Fix bugs causing strange crashes + + if (bodyquesize > 0) + { + static int queuesize; + if (queuesize < bodyquesize) + { + bodyque = realloc(bodyque, bodyquesize*sizeof*bodyque); + memset(bodyque+queuesize, 0, + (bodyquesize-queuesize)*sizeof*bodyque); + queuesize = bodyquesize; + } + if (bodyqueslot >= bodyquesize) + P_RemoveMobj(bodyque[bodyqueslot % bodyquesize]); + bodyque[bodyqueslot++ % bodyquesize] = players[playernum].mo; + } + else + if (!bodyquesize) + P_RemoveMobj(players[playernum].mo); + + // spawn a teleport fog + ss = R_PointInSubsector (x,y); + { // Teleport fog at respawn point + fixed_t xa,ya; + int an; + mobj_t *mo; + +/* BUG: an can end up negative, because mthing->angle is (signed) short. + * We have to emulate original Doom's behaviour, deferencing past the start + * of the array, into the previous array (finetangent) */ + an = ( ANG45 * ((signed)mthing->angle/45) ) >> ANGLETOFINESHIFT; + xa = finecosine[an]; + ya = finesine[an]; + + if (compatibility_level <= finaldoom_compatibility || compatibility_level == prboom_4_compatibility) + switch (an) { + case -4096: xa = finetangent[2048]; // finecosine[-4096] + ya = finetangent[0]; // finesine[-4096] + break; + case -3072: xa = finetangent[3072]; // finecosine[-3072] + ya = finetangent[1024]; // finesine[-3072] + break; + case -2048: xa = finesine[0]; // finecosine[-2048] + ya = finetangent[2048]; // finesine[-2048] + break; + case -1024: xa = finesine[1024]; // finecosine[-1024] + ya = finetangent[3072]; // finesine[-1024] + break; + case 1024: + case 2048: + case 3072: + case 4096: + case 0: break; /* correct angles set above */ + default: I_Error("G_CheckSpot: unexpected angle %d\n",an); + } + + mo = P_SpawnMobj(x+20*xa, y+20*ya, ss->sector->floorheight, MT_TFOG); + + if (players[consoleplayer].viewz != 1) + S_StartSound(mo, sfx_telept); // don't start sound on first frame + } + + return true; +} + + +// G_DeathMatchSpawnPlayer +// Spawns a player at one of the random death match spots +// called at level load and each death +// +void G_DeathMatchSpawnPlayer (int playernum) +{ + int j, selections = deathmatch_p - deathmatchstarts; + + if (selections < MAXPLAYERS) + I_Error("G_DeathMatchSpawnPlayer: Only %i deathmatch spots, %d required", + selections, MAXPLAYERS); + + for (j=0 ; j<20 ; j++) + { + int i = P_Random(pr_dmspawn) % selections; + if (G_CheckSpot (playernum, &deathmatchstarts[i]) ) + { + deathmatchstarts[i].type = playernum+1; + P_SpawnPlayer (playernum, &deathmatchstarts[i]); + return; + } + } + + // no good spot, so the player will probably get stuck + P_SpawnPlayer (playernum, &playerstarts[playernum]); +} + +// +// G_DoReborn +// + +void G_DoReborn (int playernum) +{ + if (!netgame) + gameaction = ga_loadlevel; // reload the level from scratch + else + { // respawn at the start + int i; + + // first dissasociate the corpse + players[playernum].mo->player = NULL; + + // spawn at random spot if in death match + if (deathmatch) + { + G_DeathMatchSpawnPlayer (playernum); + return; + } + + if (G_CheckSpot (playernum, &playerstarts[playernum]) ) + { + P_SpawnPlayer (playernum, &playerstarts[playernum]); + return; + } + + // try to spawn at one of the other players spots + for (i=0 ; i" when the player exits the current map + if (nodrawers && (demoplayback || timingdemo)) { + if (gamemode == commercial) + lprintf(LO_INFO, "FINISHED: MAP%02d\n", gamemap); + else + lprintf(LO_INFO, "FINISHED: E%dM%d\n", gameepisode, gamemap); + } + + WI_Start (&wminfo); +} + +// +// G_WorldDone +// + +void G_WorldDone (void) +{ + gameaction = ga_worlddone; + + if (secretexit) + players[consoleplayer].didsecret = true; + + if (gamemode == commercial) + { + switch (gamemap) + { + case 15: + case 31: + if (!secretexit) + break; + case 6: + case 11: + case 20: + case 30: + F_StartFinale (); + break; + } + } + else if (gamemap == 8) + gameaction = ga_victory; // cph - after ExM8 summary screen, show victory stuff +} + +void G_DoWorldDone (void) +{ + idmusnum = -1; //jff 3/17/98 allow new level's music to be loaded + gamestate = GS_LEVEL; + gamemap = wminfo.next+1; + G_DoLoadLevel(); + gameaction = ga_nothing; + AM_clearMarks(); //jff 4/12/98 clear any marks on the automap +} + +// killough 2/28/98: A ridiculously large number +// of players, the most you'll ever need in a demo +// or savegame. This is used to prevent problems, in +// case more players in a game are supported later. + +#define MIN_MAXPLAYERS 32 + +extern boolean setsizeneeded; + +//CPhipps - savename variable redundant + +/* killough 12/98: + * This function returns a signature for the current wad. + * It is used to distinguish between wads, for the purposes + * of savegame compatibility warnings, and options lookups. + */ + +static uint_64_t G_UpdateSignature(uint_64_t s, const char *name) +{ + int i, lump = W_CheckNumForName(name); + if (lump != -1 && (i = lump+10) < numlumps) + do + { + int size = W_LumpLength(i); + const byte *p = W_CacheLumpNum(i); + while (size--) + s <<= 1, s += *p++; + W_UnlockLumpNum(i); + } + while (--i > lump); + return s; +} + +static uint_64_t G_Signature(void) +{ + static uint_64_t s = 0; + static boolean computed = false; + char name[9]; + int episode, map; + + if (!computed) { + computed = true; + if (gamemode == commercial) + for (map = haswolflevels ? 32 : 30; map; map--) + sprintf(name, "map%02d", map), s = G_UpdateSignature(s, name); + else + for (episode = gamemode==retail ? 4 : + gamemode==shareware ? 1 : 3; episode; episode--) + for (map = 9; map; map--) + sprintf(name, "E%dM%d", episode, map), s = G_UpdateSignature(s, name); + } + return s; +} + +// +// killough 5/15/98: add forced loadgames, which allow user to override checks +// + +void G_ForcedLoadGame(void) +{ + // CPhipps - net loadgames are always forced, so we only reach here + // in single player + gameaction = ga_loadgame; + forced_loadgame = true; +} + +// killough 3/16/98: add slot info +// killough 5/15/98: add command-line +void G_LoadGame(int slot, boolean command) +{ + if (!demoplayback && !command) { + // CPhipps - handle savegame filename in G_DoLoadGame + // - Delay load so it can be communicated in net game + // - store info in special_event + special_event = BT_SPECIAL | (BTS_LOADGAME & BT_SPECIALMASK) | + ((slot << BTS_SAVESHIFT) & BTS_SAVEMASK); + forced_loadgame = netgame; // CPhipps - always force load netgames + } else { + // Do the old thing, immediate load + gameaction = ga_loadgame; + forced_loadgame = false; + savegameslot = slot; + demoplayback = false; + // Don't stay in netgame state if loading single player save + // while watching multiplayer demo + netgame = false; + } + command_loadgame = command; + R_SmoothPlaying_Reset(NULL); // e6y +} + +// killough 5/15/98: +// Consistency Error when attempting to load savegame. + +static void G_LoadGameErr(const char *msg) +{ + Z_Free(savebuffer); // Free the savegame buffer + M_ForcedLoadGame(msg); // Print message asking for 'Y' to force + if (command_loadgame) // If this was a command-line -loadgame + { + D_StartTitle(); // Start the title screen + gamestate = GS_DEMOSCREEN; // And set the game state accordingly + } +} + +// CPhipps - size of version header +#define VERSIONSIZE 16 + +const char * comp_lev_str[MAX_COMPATIBILITY_LEVEL] = +{ "doom v1.2", "doom v1.666", "doom/doom2 v1.9", "ultimate doom", "final doom", + "dosdoom compatibility", "tasdoom compatibility", "\"boom compatibility\"", "boom v2.01", "boom v2.02", "lxdoom v1.3.2+", + "MBF", "PrBoom 2.03beta", "PrBoom v2.1.0-2.1.1", "PrBoom v2.1.2-v2.2.6", + "PrBoom v2.3.x", "PrBoom 2.4.0", "Current PrBoom" }; + +// comp_options_by_version removed - see G_Compatibility + +static byte map_old_comp_levels[] = +{ 0, 1, 2, 4, 5, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 }; + +static const struct { + int comp_level; + const char* ver_printf; + int version; +} version_headers[] = { + /* cph - we don't need a new version_header for prboom_3_comp/v2.1.1, since + * the file format is unchanged. */ + { prboom_3_compatibility, "PrBoom %d", 210}, + { prboom_5_compatibility, "PrBoom %d", 211}, + { prboom_6_compatibility, "PrBoom %d", 212} +}; + +static const size_t num_version_headers = sizeof(version_headers) / sizeof(version_headers[0]); + +void G_DoLoadGame(void) +{ + int length, i; + // CPhipps - do savegame filename stuff here + char name[PATH_MAX+1]; // killough 3/22/98 + int savegame_compatibility = -1; + + G_SaveGameName(name,sizeof(name),savegameslot, demoplayback); + + gameaction = ga_nothing; + + length = M_ReadFile(name, &savebuffer); + if (length<=0) + I_Error("Couldn't read file %s: %s", name, "(Unknown Error)"); + save_p = savebuffer + SAVESTRINGSIZE; + + // CPhipps - read the description field, compare with supported ones + for (i=0; (size_t)i= prboom_4_compatibility) ? *save_p : savegame_compatibility; + if (savegame_compatibility < prboom_6_compatibility) + compatibility_level = map_old_comp_levels[compatibility_level]; + save_p++; + + gameskill = *save_p++; + gameepisode = *save_p++; + gamemap = *save_p++; + + for (i=0 ; i= prboom_2_compatibility) { + memcpy(&totalleveltimes, save_p, sizeof totalleveltimes); + save_p += sizeof totalleveltimes; + } + else totalleveltimes = 0; + + // killough 11/98: load revenant tracer state + basetic = gametic - *save_p++; + + // dearchive all the modifications + P_MapStart(); + P_UnArchivePlayers (); + P_UnArchiveWorld (); + P_UnArchiveThinkers (); + P_UnArchiveSpecials (); + P_UnArchiveRNG (); // killough 1/18/98: load RNG information + P_UnArchiveMap (); // killough 1/22/98: load automap information + P_MapEnd(); + R_SmoothPlaying_Reset(NULL); // e6y + + if (*save_p != 0xe6) + I_Error ("G_DoLoadGame: Bad savegame"); + + // done + Z_Free (savebuffer); + + if (setsizeneeded) + R_ExecuteSetViewSize (); + + // draw the pattern into the back screen + R_FillBackScreen (); + + /* killough 12/98: support -recordfrom and -loadgame -playdemo */ + if (!command_loadgame) + singledemo = false; /* Clear singledemo flag if loading from menu */ + else + if (singledemo) { + gameaction = ga_loadgame; /* Mark that we're loading a game before demo */ + G_DoPlayDemo(); /* This will detect it and won't reinit level */ + } else /* Command line + record means it's a recordfrom */ + if (demorecording) + G_BeginRecording(); +} + +// +// G_SaveGame +// Called by the menu task. +// Description is a 24 byte text string +// + +void G_SaveGame(int slot, char *description) +{ + strcpy(savedescription, description); + if (demoplayback) { + /* cph - We're doing a user-initiated save game while a demo is + * running so, go outside normal mechanisms + */ + savegameslot = slot; + G_DoSaveGame(true); + } + // CPhipps - store info in special_event + special_event = BT_SPECIAL | (BTS_SAVEGAME & BT_SPECIALMASK) | + ((slot << BTS_SAVESHIFT) & BTS_SAVEMASK); +#ifdef HAVE_NET + D_NetSendMisc(nm_savegamename, strlen(savedescription)+1, savedescription); +#endif +} + +// Check for overrun and realloc if necessary -- Lee Killough 1/22/98 +void (CheckSaveGame)(size_t size, const char* file, int line) +{ + size_t pos = save_p - savebuffer; + +#ifdef RANGECHECK + /* cph 2006/08/07 - after-the-fact sanity checking of CheckSaveGame calls */ + static size_t prev_check; + static const char* prevf; + static int prevl; + + if (pos > prev_check) + I_Error("CheckSaveGame at %s:%d called for insufficient buffer (%u < %u)", prevf, prevl, prev_check, pos); + prev_check = size + pos; + prevf = file; + prevl = line; +#endif + + size += 1024; // breathing room + if (pos+size > savegamesize) + save_p = (savebuffer = realloc(savebuffer, + savegamesize += (size+1023) & ~1023)) + pos; +} + +/* killough 3/22/98: form savegame name in one location + * (previously code was scattered around in multiple places) + * cph - Avoid possible buffer overflow problems by passing + * size to this function and using snprintf */ + +void G_SaveGameName(char *name, size_t size, int slot, boolean demoplayback) +{ + const char* sgn = demoplayback ? "demosav" : savegamename; +#ifdef HAVE_SNPRINTF + snprintf (name, size, "%s/%s%d.dsg", basesavegame, sgn, slot); +#else + sprintf (name, "%s/%s%d.dsg", basesavegame, sgn, slot); +#endif +} + +static void G_DoSaveGame (boolean menu) +{ + char name[PATH_MAX+1]; + char name2[VERSIONSIZE]; + char *description; + int length, i; + + gameaction = ga_nothing; // cph - cancel savegame at top of this function, + // in case later problems cause a premature exit + + G_SaveGameName(name,sizeof(name),savegameslot, demoplayback && !menu); + + description = savedescription; + + save_p = savebuffer = malloc(savegamesize); + + CheckSaveGame(SAVESTRINGSIZE+VERSIONSIZE+sizeof(uint_64_t)); + memcpy (save_p, description, SAVESTRINGSIZE); + save_p += SAVESTRINGSIZE; + memset (name2,0,sizeof(name2)); + + // CPhipps - scan for the version header + for (i=0; (size_t)i= prboom_2_compatibility) { + memcpy(save_p, &totalleveltimes, sizeof totalleveltimes); + save_p += sizeof totalleveltimes; + } + else totalleveltimes = 0; + + // killough 11/98: save revenant tracer state + *save_p++ = (gametic-basetic) & 255; + + // killough 3/22/98: add Z_CheckHeap after each call to ensure consistency + Z_CheckHeap(); + P_ArchivePlayers(); + Z_CheckHeap(); + + // phares 9/13/98: Move mobj_t->index out of P_ArchiveThinkers so the + // indices can be used by P_ArchiveWorld when the sectors are saved. + // This is so we can save the index of the mobj_t of the thinker that + // caused a sound, referenced by sector_t->soundtarget. + P_ThinkerToIndex(); + + P_ArchiveWorld(); + Z_CheckHeap(); + P_ArchiveThinkers(); + + // phares 9/13/98: Move index->mobj_t out of P_ArchiveThinkers, simply + // for symmetry with the P_ThinkerToIndex call above. + + P_IndexToThinker(); + + Z_CheckHeap(); + P_ArchiveSpecials(); + P_ArchiveRNG(); // killough 1/18/98: save RNG information + Z_CheckHeap(); + P_ArchiveMap(); // killough 1/22/98: save automap information + + *save_p++ = 0xe6; // consistancy marker + + length = save_p - savebuffer; + + Z_CheckHeap(); + doom_printf( "%s", M_WriteFile(name, savebuffer, length) + ? s_GGSAVED /* Ty - externalised */ + : "Game save failed!"); // CPhipps - not externalised + + free(savebuffer); // killough + savebuffer = save_p = NULL; + + savedescription[0] = 0; +} + +static skill_t d_skill; +static int d_episode; +static int d_map; + +void G_DeferedInitNew(skill_t skill, int episode, int map) +{ + d_skill = skill; + d_episode = episode; + d_map = map; + gameaction = ga_newgame; +} + +/* cph - + * G_Compatibility + * + * Initialises the comp[] array based on the compatibility_level + * For reference, MBF did: + * for (i=0; i < COMP_TOTAL; i++) + * comp[i] = compatibility; + * + * Instead, we have a lookup table showing at what version a fix was + * introduced, and made optional (replaces comp_options_by_version) + */ + +void G_Compatibility(void) +{ + static const struct { + complevel_t fix; // level at which fix/change was introduced + complevel_t opt; // level at which fix/change was made optional + } levels[] = { + // comp_telefrag - monsters used to telefrag only on MAP30, now they do it for spawners only + { mbf_compatibility, mbf_compatibility }, + // comp_dropoff - MBF encourages things to drop off of overhangs + { mbf_compatibility, mbf_compatibility }, + // comp_vile - original Doom archville bugs like ghosts + { boom_compatibility, mbf_compatibility }, + // comp_pain - original Doom limits Pain Elementals from spawning too many skulls + { boom_compatibility, mbf_compatibility }, + // comp_skull - original Doom let skulls be spit through walls by Pain Elementals + { boom_compatibility, mbf_compatibility }, + // comp_blazing - original Doom duplicated blazing door sound + { boom_compatibility, mbf_compatibility }, + // e6y: "Tagged doors don't trigger special lighting" handled wrong + // http://sourceforge.net/tracker/index.php?func=detail&aid=1411400&group_id=148658&atid=772943 + // comp_doorlight - MBF made door lighting changes more gradual + { boom_compatibility, mbf_compatibility }, + // comp_model - improvements to the game physics + { boom_compatibility, mbf_compatibility }, + // comp_god - fixes to God mode + { boom_compatibility, mbf_compatibility }, + // comp_falloff - MBF encourages things to drop off of overhangs + { mbf_compatibility, mbf_compatibility }, + // comp_floors - fixes for moving floors bugs + { boom_compatibility_compatibility, mbf_compatibility }, + // comp_skymap + { boom_compatibility, mbf_compatibility }, + // comp_pursuit - MBF AI change, limited pursuit? + { mbf_compatibility, mbf_compatibility }, + // comp_doorstuck - monsters stuck in doors fix + { boom_202_compatibility, mbf_compatibility }, + // comp_staylift - MBF AI change, monsters try to stay on lifts + { mbf_compatibility, mbf_compatibility }, + // comp_zombie - prevent dead players triggering stuff + { lxdoom_1_compatibility, mbf_compatibility }, + // comp_stairs - see p_floor.c + { boom_compatibility_compatibility, mbf_compatibility }, + // comp_infcheat - FIXME + { mbf_compatibility, mbf_compatibility }, + // comp_zerotags - allow zero tags in wads */ + { boom_compatibility, mbf_compatibility }, + // comp_moveblock - enables keygrab and mancubi shots going thru walls + { lxdoom_1_compatibility, prboom_2_compatibility }, + // comp_respawn - objects which aren't on the map at game start respawn at (0,0) + { prboom_2_compatibility, prboom_2_compatibility }, + // comp_sound - see s_sound.c + { boom_compatibility_compatibility, prboom_3_compatibility }, + // comp_666 - enables tag 666 in non-ExM8 levels + { ultdoom_compatibility, prboom_4_compatibility }, + // comp_soul - enables lost souls bouncing (see P_ZMovement) + { prboom_4_compatibility, prboom_4_compatibility }, + // comp_maskedanim - 2s mid textures don't animate + { doom_1666_compatibility, prboom_4_compatibility }, + }; + int i; + + if (sizeof(levels)/sizeof(*levels) != COMP_NUM) + I_Error("G_Compatibility: consistency error"); + + for (i = 0; i < sizeof(levels)/sizeof(*levels); i++) + if (compatibility_level < levels[i].opt) + comp[i] = (compatibility_level < levels[i].fix); + + if (!mbf_features) { + monster_infighting = 1; + monster_backing = 0; + monster_avoid_hazards = 0; + monster_friction = 0; + help_friends = 0; + +#ifdef DOGS + dogs = 0; + dog_jumping = 0; +#endif + + monkeys = 0; + } +} + +#ifdef DOGS +/* killough 7/19/98: Marine's best friend :) */ +static int G_GetHelpers(void) +{ + int j = M_CheckParm ("-dog"); + + if (!j) + j = M_CheckParm ("-dogs"); + return j ? j+1 < myargc ? atoi(myargv[j+1]) : 1 : default_dogs; +} +#endif + +// killough 3/1/98: function to reload all the default parameter +// settings before a new game begins + +void G_ReloadDefaults(void) +{ + // killough 3/1/98: Initialize options based on config file + // (allows functions above to load different values for demos + // and savegames without messing up defaults). + + weapon_recoil = default_weapon_recoil; // weapon recoil + + player_bobbing = default_player_bobbing; // whether player bobs or not + + /* cph 2007/06/31 - for some reason, the default_* of the next 2 vars was never implemented */ + variable_friction = default_variable_friction; + allow_pushers = default_allow_pushers; + + + monsters_remember = default_monsters_remember; // remember former enemies + + monster_infighting = default_monster_infighting; // killough 7/19/98 + +#ifdef DOGS + dogs = netgame ? 0 : G_GetHelpers(); // killough 7/19/98 + dog_jumping = default_dog_jumping; +#endif + + distfriend = default_distfriend; // killough 8/8/98 + + monster_backing = default_monster_backing; // killough 9/8/98 + + monster_avoid_hazards = default_monster_avoid_hazards; // killough 9/9/98 + + monster_friction = default_monster_friction; // killough 10/98 + + help_friends = default_help_friends; // killough 9/9/98 + + monkeys = default_monkeys; + + // jff 1/24/98 reset play mode to command line spec'd version + // killough 3/1/98: moved to here + respawnparm = clrespawnparm; + fastparm = clfastparm; + nomonsters = clnomonsters; + + //jff 3/24/98 set startskill from defaultskill in config file, unless + // it has already been set by a -skill parameter + if (startskill==sk_none) + startskill = (skill_t)(defaultskill-1); + + demoplayback = false; + singledemo = false; // killough 9/29/98: don't stop after 1 demo + netdemo = false; + + // killough 2/21/98: + memset(playeringame+1, 0, sizeof(*playeringame)*(MAXPLAYERS-1)); + + consoleplayer = 0; + + compatibility_level = default_compatibility_level; + { + int i = M_CheckParm("-complevel"); + if (i && (1+i) < myargc) { + int l = atoi(myargv[i+1]);; + if (l >= -1) compatibility_level = l; + } + } + if (compatibility_level == -1) + compatibility_level = best_compatibility; + + if (mbf_features) + memcpy(comp, default_comp, sizeof comp); + G_Compatibility(); + + // killough 3/31/98, 4/5/98: demo sync insurance + demo_insurance = default_demo_insurance == 1; + + rngseed += I_GetRandomTimeSeed() + gametic; // CPhipps +} + +void G_DoNewGame (void) +{ + G_ReloadDefaults(); // killough 3/1/98 + netgame = false; // killough 3/29/98 + deathmatch = false; + G_InitNew (d_skill, d_episode, d_map); + gameaction = ga_nothing; + + //jff 4/26/98 wake up the status bar in case were coming out of a DM demo + ST_Start(); +} + +// killough 4/10/98: New function to fix bug which caused Doom +// lockups when idclev was used in conjunction with -fast. + +void G_SetFastParms(int fast_pending) +{ + static int fast = 0; // remembers fast state + int i; + if (fast != fast_pending) { /* only change if necessary */ + if ((fast = fast_pending)) + { + for (i=S_SARG_RUN1; i<=S_SARG_PAIN2; i++) + if (states[i].tics != 1 || demo_compatibility) // killough 4/10/98 + states[i].tics >>= 1; // don't change 1->0 since it causes cycles + mobjinfo[MT_BRUISERSHOT].speed = 20*FRACUNIT; + mobjinfo[MT_HEADSHOT].speed = 20*FRACUNIT; + mobjinfo[MT_TROOPSHOT].speed = 20*FRACUNIT; + } + else + { + for (i=S_SARG_RUN1; i<=S_SARG_PAIN2; i++) + states[i].tics <<= 1; + mobjinfo[MT_BRUISERSHOT].speed = 15*FRACUNIT; + mobjinfo[MT_HEADSHOT].speed = 10*FRACUNIT; + mobjinfo[MT_TROOPSHOT].speed = 10*FRACUNIT; + } + } +} + +// +// G_InitNew +// Can be called by the startup code or the menu task, +// consoleplayer, displayplayer, playeringame[] should be set. +// + +void G_InitNew(skill_t skill, int episode, int map) +{ + int i; + + if (paused) + { + paused = false; + S_ResumeSound(); + } + + if (skill > sk_nightmare) + skill = sk_nightmare; + + if (episode < 1) + episode = 1; + + if (gamemode == retail) + { + if (episode > 4) + episode = 4; + } + else + if (gamemode == shareware) + { + if (episode > 1) + episode = 1; // only start episode 1 on shareware + } + else + if (episode > 3) + episode = 3; + + if (map < 1) + map = 1; + if (map > 9 && gamemode != commercial) + map = 9; + + G_SetFastParms(fastparm || skill == sk_nightmare); // killough 4/10/98 + + M_ClearRandom(); + + respawnmonsters = skill == sk_nightmare || respawnparm; + + // force players to be initialized upon first level load + for (i=0 ; i demobuffer + demolength) + { + lprintf(LO_WARN, "G_ReadDemoTiccmd: missing DEMOMARKER\n"); + G_CheckDemoStatus(); + } + else + { + cmd->forwardmove = ((signed char)*demo_p++); + cmd->sidemove = ((signed char)*demo_p++); + if (!longtics) { + cmd->angleturn = ((unsigned char)(at = *demo_p++))<<8; + } else { + unsigned int lowbyte = (unsigned char)*demo_p++; + cmd->angleturn = (((signed int)(*demo_p++))<<8) + lowbyte; + } + cmd->buttons = (unsigned char)*demo_p++; + // e6y: ability to play tasdoom demos directly + if (compatibility_level == tasdoom_compatibility) + { + signed char k = cmd->forwardmove; + cmd->forwardmove = cmd->sidemove; + cmd->sidemove = (signed char)at; + cmd->angleturn = ((unsigned char)cmd->buttons)<<8; + cmd->buttons = (byte)k; + } + } +} + +/* Demo limits removed -- killough + * cph - record straight to file + */ +void G_WriteDemoTiccmd (ticcmd_t* cmd) +{ + char buf[5]; + char *p = buf; + + *p++ = cmd->forwardmove; + *p++ = cmd->sidemove; + if (!longtics) { + *p++ = (cmd->angleturn+128)>>8; + } else { + signed short a = cmd->angleturn; + *p++ = a & 0xff; + *p++ = (a >> 8) & 0xff; + } + *p++ = cmd->buttons; + if (fwrite(buf, p-buf, 1, demofp) != 1) + I_Error("G_WriteDemoTiccmd: error writing demo"); + + /* cph - alias demo_p to it so we can read it back */ + demo_p = buf; + G_ReadDemoTiccmd (cmd); // make SURE it is exactly the same +} + +// +// G_RecordDemo +// + +void G_RecordDemo (const char* name) +{ + char demoname[PATH_MAX]; + usergame = false; + AddDefaultExtension(strcpy(demoname, name), ".lmp"); // 1/18/98 killough + demorecording = true; + /* cph - Record demos straight to file + * If file already exists, try to continue existing demo + */ + if (access(demoname, F_OK)) { + demofp = fopen(demoname, "wb"); + } else { + demofp = fopen(demoname, "r+"); + if (demofp) { + int slot = -1; + int rc; + int bytes_per_tic; + const byte* pos; + + { /* Read the demo header for options etc */ + byte buf[200]; + size_t len = fread(buf, 1, sizeof(buf), demofp); + pos = G_ReadDemoHeader(buf, len, false); + if (pos) + { + fseek(demofp, pos - buf, SEEK_SET); + } + } + bytes_per_tic = longtics ? 5 : 4; + if (pos) + /* Now read the demo to find the last save slot */ + do { + byte buf[5]; + + rc = fread(buf, 1, bytes_per_tic, demofp); + if (buf[0] == DEMOMARKER) break; + if (buf[bytes_per_tic-1] & BT_SPECIAL) + if ((buf[bytes_per_tic-1] & BT_SPECIALMASK) == BTS_SAVEGAME) + slot = (buf[bytes_per_tic-1] & BTS_SAVEMASK)>>BTS_SAVESHIFT; + } while (rc == bytes_per_tic); + + if (slot == -1) I_Error("G_RecordDemo: No save in demo, can't continue"); + + /* Return to the last save position, and load the relevant savegame */ + fseek(demofp, -rc, SEEK_CUR); + G_LoadGame(slot, false); + autostart = false; + } + } + if (!demofp) I_Error("G_RecordDemo: failed to open %s", name); +} + +// These functions are used to read and write game-specific options in demos +// and savegames so that demo sync is preserved and savegame restoration is +// complete. Not all options (for example "compatibility"), however, should +// be loaded and saved here. It is extremely important to use the same +// positions as before for the variables, so if one becomes obsolete, the +// byte(s) should still be skipped over or padded with 0's. +// Lee Killough 3/1/98 + +extern int forceOldBsp; + +byte *G_WriteOptions(byte *demo_p) +{ + byte *target = demo_p + GAME_OPTION_SIZE; + + *demo_p++ = monsters_remember; // part of monster AI + + *demo_p++ = variable_friction; // ice & mud + + *demo_p++ = weapon_recoil; // weapon recoil + + *demo_p++ = allow_pushers; // MT_PUSH Things + + *demo_p++ = 0; + + *demo_p++ = player_bobbing; // whether player bobs or not + + // killough 3/6/98: add parameters to savegame, move around some in demos + *demo_p++ = respawnparm; + *demo_p++ = fastparm; + *demo_p++ = nomonsters; + + *demo_p++ = demo_insurance; // killough 3/31/98 + + // killough 3/26/98: Added rngseed. 3/31/98: moved here + *demo_p++ = (byte)((rngseed >> 24) & 0xff); + *demo_p++ = (byte)((rngseed >> 16) & 0xff); + *demo_p++ = (byte)((rngseed >> 8) & 0xff); + *demo_p++ = (byte)( rngseed & 0xff); + + // Options new to v2.03 begin here + + *demo_p++ = monster_infighting; // killough 7/19/98 + +#ifdef DOGS + *demo_p++ = dogs; // killough 7/19/98 +#else + *demo_p++ = 0; +#endif + + *demo_p++ = 0; + *demo_p++ = 0; + + *demo_p++ = (distfriend >> 8) & 0xff; // killough 8/8/98 + *demo_p++ = distfriend & 0xff; // killough 8/8/98 + + *demo_p++ = monster_backing; // killough 9/8/98 + + *demo_p++ = monster_avoid_hazards; // killough 9/9/98 + + *demo_p++ = monster_friction; // killough 10/98 + + *demo_p++ = help_friends; // killough 9/9/98 + +#ifdef DOGS + *demo_p++ = dog_jumping; +#else + *demo_p++ = 0; +#endif + + *demo_p++ = monkeys; + + { // killough 10/98: a compatibility vector now + int i; + for (i=0; i < COMP_TOTAL; i++) + *demo_p++ = comp[i] != 0; + } + + *demo_p++ = (compatibility_level >= prboom_2_compatibility) && forceOldBsp; // cph 2002/07/20 + + //---------------- + // Padding at end + //---------------- + while (demo_p < target) + *demo_p++ = 0; + + if (demo_p != target) + I_Error("G_WriteOptions: GAME_OPTION_SIZE is too small"); + + return target; +} + +/* Same, but read instead of write + * cph - const byte*'s + */ + +const byte *G_ReadOptions(const byte *demo_p) +{ + const byte *target = demo_p + GAME_OPTION_SIZE; + + monsters_remember = *demo_p++; + + variable_friction = *demo_p; // ice & mud + demo_p++; + + weapon_recoil = *demo_p; // weapon recoil + demo_p++; + + allow_pushers = *demo_p; // MT_PUSH Things + demo_p++; + + demo_p++; + + player_bobbing = *demo_p; // whether player bobs or not + demo_p++; + + // killough 3/6/98: add parameters to savegame, move from demo + respawnparm = *demo_p++; + fastparm = *demo_p++; + nomonsters = *demo_p++; + + demo_insurance = *demo_p++; // killough 3/31/98 + + // killough 3/26/98: Added rngseed to demos; 3/31/98: moved here + + rngseed = *demo_p++ & 0xff; + rngseed <<= 8; + rngseed += *demo_p++ & 0xff; + rngseed <<= 8; + rngseed += *demo_p++ & 0xff; + rngseed <<= 8; + rngseed += *demo_p++ & 0xff; + + // Options new to v2.03 + if (mbf_features) + { + monster_infighting = *demo_p++; // killough 7/19/98 + +#ifdef DOGS + dogs = *demo_p++; // killough 7/19/98 +#else + demo_p++; +#endif + + demo_p += 2; + + distfriend = *demo_p++ << 8; // killough 8/8/98 + distfriend+= *demo_p++; + + monster_backing = *demo_p++; // killough 9/8/98 + + monster_avoid_hazards = *demo_p++; // killough 9/9/98 + + monster_friction = *demo_p++; // killough 10/98 + + help_friends = *demo_p++; // killough 9/9/98 + +#ifdef DOGS + dog_jumping = *demo_p++; // killough 10/98 +#else + demo_p++; +#endif + + monkeys = *demo_p++; + + { // killough 10/98: a compatibility vector now + int i; + for (i=0; i < COMP_TOTAL; i++) + comp[i] = *demo_p++; + } + + forceOldBsp = *demo_p++; // cph 2002/07/20 + } + else /* defaults for versions <= 2.02 */ + { + /* G_Compatibility will set these */ + } + + G_Compatibility(); + return target; +} + +void G_BeginRecording (void) +{ + int i; + byte *demostart, *demo_p; + demostart = demo_p = malloc(1000); + longtics = 0; + + /* cph - 3 demo record formats supported: MBF+, BOOM, and Doom v1.9 */ + if (mbf_features) { + { /* Write version code into demo */ + unsigned char v; + switch(compatibility_level) { + case mbf_compatibility: v = 203; break; // e6y: Bug in MBF compatibility mode fixed + case prboom_2_compatibility: v = 210; break; + case prboom_3_compatibility: v = 211; break; + case prboom_4_compatibility: v = 212; break; + case prboom_5_compatibility: v = 213; break; + case prboom_6_compatibility: + v = 214; + longtics = 1; + break; + } + *demo_p++ = v; + } + + // signature + *demo_p++ = 0x1d; + *demo_p++ = 'M'; + *demo_p++ = 'B'; + *demo_p++ = 'F'; + *demo_p++ = 0xe6; + *demo_p++ = '\0'; + + /* killough 2/22/98: save compatibility flag in new demos + * cph - FIXME? MBF demos will always be not in compat. mode */ + *demo_p++ = 0; + + *demo_p++ = gameskill; + *demo_p++ = gameepisode; + *demo_p++ = gamemap; + *demo_p++ = deathmatch; + *demo_p++ = consoleplayer; + + demo_p = G_WriteOptions(demo_p); // killough 3/1/98: Save game options + + for (i=0 ; i boom_compatibility_compatibility) { + byte v, c; /* Nominally, version and compatibility bits */ + switch (compatibility_level) { + case boom_compatibility_compatibility: v = 202, c = 1; break; + case boom_201_compatibility: v = 201; c = 0; break; + case boom_202_compatibility: v = 202, c = 0; break; + default: I_Error("G_BeginRecording: Boom compatibility level unrecognised?"); + } + *demo_p++ = v; + + // signature + *demo_p++ = 0x1d; + *demo_p++ = 'B'; + *demo_p++ = 'o'; + *demo_p++ = 'o'; + *demo_p++ = 'm'; + *demo_p++ = 0xe6; + + /* CPhipps - save compatibility level in demos */ + *demo_p++ = c; + + *demo_p++ = gameskill; + *demo_p++ = gameepisode; + *demo_p++ = gamemap; + *demo_p++ = deathmatch; + *demo_p++ = consoleplayer; + + demo_p = G_WriteOptions(demo_p); // killough 3/1/98: Save game options + + for (i=0 ; i=0) + return lev; + } + } + if (ver < 107) return doom_1666_compatibility; + if (gamemode == retail) return ultdoom_compatibility; + if (gamemission >= pack_tnt) return finaldoom_compatibility; + return doom2_19_compatibility; +} + +//e6y: Check for overrun +static boolean CheckForOverrun(const byte *start_p, const byte *current_p, size_t maxsize, size_t size, boolean failonerror) +{ + size_t pos = current_p - start_p; + if (pos + size > maxsize) + { + if (failonerror) + I_Error("G_ReadDemoHeader: wrong demo header\n"); + else + return true; + } + return false; +} + +static const byte* G_ReadDemoHeader(const byte *demo_p, size_t size, boolean failonerror) +{ + skill_t skill; + int i, episode, map; + + // e6y + // The local variable should be used instead of demobuffer, + // because demobuffer can be uninitialized + const byte *header_p = demo_p; + + const byte *option_p = NULL; /* killough 11/98 */ + + basetic = gametic; // killough 9/29/98 + + // killough 2/22/98, 2/28/98: autodetect old demos and act accordingly. + // Old demos turn on demo_compatibility => compatibility; new demos load + // compatibility flag, and other flags as well, as a part of the demo. + + //e6y: check for overrun + if (CheckForOverrun(header_p, demo_p, size, 1, failonerror)) + return NULL; + + demover = *demo_p++; + longtics = 0; + + // e6y + // Handling of unrecognized demo formats + // Versions up to 1.2 use a 7-byte header - first byte is a skill level. + // Versions after 1.2 use a 13-byte header - first byte is a demoversion. + // BOOM's demoversion starts from 200 + if (!((demover >= 0 && demover <= 4) || + (demover >= 104 && demover <= 111) || + (demover >= 200 && demover <= 214))) + { + I_Error("G_ReadDemoHeader: Unknown demo format %d.", demover); + } + + if (demover < 200) // Autodetect old demos + { + if (demover >= 111) longtics = 1; + + // killough 3/2/98: force these variables to be 0 in demo_compatibility + + variable_friction = 0; + + weapon_recoil = 0; + + allow_pushers = 0; + + monster_infighting = 1; // killough 7/19/98 + +#ifdef DOGS + dogs = 0; // killough 7/19/98 + dog_jumping = 0; // killough 10/98 +#endif + + monster_backing = 0; // killough 9/8/98 + + monster_avoid_hazards = 0; // killough 9/9/98 + + monster_friction = 0; // killough 10/98 + help_friends = 0; // killough 9/9/98 + monkeys = 0; + + // killough 3/6/98: rearrange to fix savegame bugs (moved fastparm, + // respawnparm, nomonsters flags to G_LoadOptions()/G_SaveOptions()) + + if ((skill=demover) >= 100) // For demos from versions >= 1.4 + { + //e6y: check for overrun + if (CheckForOverrun(header_p, demo_p, size, 8, failonerror)) + return NULL; + + compatibility_level = G_GetOriginalDoomCompatLevel(demover); + skill = *demo_p++; + episode = *demo_p++; + map = *demo_p++; + deathmatch = *demo_p++; + respawnparm = *demo_p++; + fastparm = *demo_p++; + nomonsters = *demo_p++; + consoleplayer = *demo_p++; + } + else + { + //e6y: check for overrun + if (CheckForOverrun(header_p, demo_p, size, 2, failonerror)) + return NULL; + + compatibility_level = doom_12_compatibility; + episode = *demo_p++; + map = *demo_p++; + deathmatch = respawnparm = fastparm = + nomonsters = consoleplayer = 0; + } + G_Compatibility(); + } + else // new versions of demos + { + demo_p += 6; // skip signature; + switch (demover) { + case 200: /* BOOM */ + case 201: + //e6y: check for overrun + if (CheckForOverrun(header_p, demo_p, size, 1, failonerror)) + return NULL; + + if (!*demo_p++) + compatibility_level = boom_201_compatibility; + else + compatibility_level = boom_compatibility_compatibility; + break; + case 202: + //e6y: check for overrun + if (CheckForOverrun(header_p, demo_p, size, 1, failonerror)) + return NULL; + + if (!*demo_p++) + compatibility_level = boom_202_compatibility; + else + compatibility_level = boom_compatibility_compatibility; + break; + case 203: + /* LxDoom or MBF - determine from signature + * cph - load compatibility level */ + switch (*(header_p + 2)) { + case 'B': /* LxDoom */ + /* cph - DEMOSYNC - LxDoom demos recorded in compatibility modes support dropped */ + compatibility_level = lxdoom_1_compatibility; + break; + case 'M': + compatibility_level = mbf_compatibility; + demo_p++; + break; + } + break; + case 210: + compatibility_level = prboom_2_compatibility; + demo_p++; + break; + case 211: + compatibility_level = prboom_3_compatibility; + demo_p++; + break; + case 212: + compatibility_level = prboom_4_compatibility; + demo_p++; + break; + case 213: + compatibility_level = prboom_5_compatibility; + demo_p++; + break; + case 214: + compatibility_level = prboom_6_compatibility; + longtics = 1; + demo_p++; + break; + } + //e6y: check for overrun + if (CheckForOverrun(header_p, demo_p, size, 5, failonerror)) + return NULL; + + skill = *demo_p++; + episode = *demo_p++; + map = *demo_p++; + deathmatch = *demo_p++; + consoleplayer = *demo_p++; + + /* killough 11/98: save option pointer for below */ + if (mbf_features) + option_p = demo_p; + + //e6y: check for overrun + if (CheckForOverrun(header_p, demo_p, size, GAME_OPTION_SIZE, failonerror)) + return NULL; + + demo_p = G_ReadOptions(demo_p); // killough 3/1/98: Read game options + + if (demover == 200) // killough 6/3/98: partially fix v2.00 demos + demo_p += 256-GAME_OPTION_SIZE; + } + + if (sizeof(comp_lev_str)/sizeof(comp_lev_str[0]) != MAX_COMPATIBILITY_LEVEL) + I_Error("G_ReadDemoHeader: compatibility level strings incomplete"); + lprintf(LO_INFO, "G_DoPlayDemo: playing demo with %s compatibility\n", + comp_lev_str[compatibility_level]); + + if (demo_compatibility) // only 4 players can exist in old demos + { + //e6y: check for overrun + if (CheckForOverrun(header_p, demo_p, size, 4, failonerror)) + return NULL; + + for (i=0; i<4; i++) // intentionally hard-coded 4 -- killough + playeringame[i] = *demo_p++; + for (;i < MAXPLAYERS; i++) + playeringame[i] = 0; + } + else + { + //e6y: check for overrun + if (CheckForOverrun(header_p, demo_p, size, MAXPLAYERS, failonerror)) + return NULL; + + for (i=0 ; i < MAXPLAYERS; i++) + playeringame[i] = *demo_p++; + demo_p += MIN_MAXPLAYERS - MAXPLAYERS; + } + + if (playeringame[1]) + { + netgame = true; + netdemo = true; + } + + if (gameaction != ga_loadgame) { /* killough 12/98: support -loadgame */ + G_InitNew(skill, episode, map); + } + + for (i=0; imessage=... and so I've added this dprintf. +// +// killough 3/6/98: Made limit static to allow z_zone functions to call +// this function, without calling realloc(), which seems to cause problems. + +#define MAX_MESSAGE_SIZE 1024 + +// CPhipps - renamed to doom_printf to avoid name collision with glibc +void doom_printf(const char *s, ...) +{ + static char msg[MAX_MESSAGE_SIZE]; + va_list v; + va_start(v,s); +#ifdef HAVE_VSNPRINTF + vsnprintf(msg,sizeof(msg),s,v); /* print message in buffer */ +#else + vsprintf(msg,s,v); +#endif + va_end(v); + players[consoleplayer].message = msg; // set new message +} diff --git a/src/g_game.h b/src/g_game.h new file mode 100644 index 0000000..274f20e --- /dev/null +++ b/src/g_game.h @@ -0,0 +1,178 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: Main game control interface. + *-----------------------------------------------------------------------------*/ + +#ifndef __G_GAME__ +#define __G_GAME__ + +#include "doomdef.h" +#include "d_event.h" +#include "d_ticcmd.h" + +// +// GAME +// + +// killough 5/2/98: number of bytes reserved for saving options +#define GAME_OPTION_SIZE 64 + +boolean G_Responder(event_t *ev); +boolean G_CheckDemoStatus(void); +void G_DeathMatchSpawnPlayer(int playernum); +void G_InitNew(skill_t skill, int episode, int map); +void G_DeferedInitNew(skill_t skill, int episode, int map); +void G_DeferedPlayDemo(const char *demo); // CPhipps - const +void G_LoadGame(int slot, boolean is_command); // killough 5/15/98 +void G_ForcedLoadGame(void); // killough 5/15/98: forced loadgames +void G_DoLoadGame(void); +void G_SaveGame(int slot, char *description); // Called by M_Responder. +void G_BeginRecording(void); +// CPhipps - const on these string params +void G_RecordDemo(const char *name); // Only called by startup code. +void G_ExitLevel(void); +void G_SecretExitLevel(void); +void G_WorldDone(void); +void G_EndGame(void); /* cph - make m_menu.c call a G_* function for this */ +void G_Ticker(void); +void G_ReloadDefaults(void); // killough 3/1/98: loads game defaults +void G_SaveGameName(char *, size_t, int, boolean); /* killough 3/22/98: sets savegame filename */ +void G_SetFastParms(int); // killough 4/10/98: sets -fast parameters +void G_DoNewGame(void); +void G_DoReborn(int playernum); +void G_DoPlayDemo(void); +void G_DoCompleted(void); +void G_ReadDemoTiccmd(ticcmd_t *cmd); +void G_WriteDemoTiccmd(ticcmd_t *cmd); +void G_DoWorldDone(void); +void G_Compatibility(void); +const byte *G_ReadOptions(const byte *demo_p); /* killough 3/1/98 - cph: const byte* */ +byte *G_WriteOptions(byte *demo_p); // killough 3/1/98 +void G_PlayerReborn(int player); +void G_RestartLevel(void); // CPhipps - menu involked level restart +void G_DoVictory(void); +void G_BuildTiccmd (ticcmd_t* cmd); // CPhipps - move decl to header +void G_ChangedPlayerColour(int pn, int cl); // CPhipps - On-the-fly player colour changing +void G_MakeSpecialEvent(buttoncode_t bc, ...); /* cph - new event stuff */ + +// killough 1/18/98: Doom-style printf; killough 4/25/98: add gcc attributes +// CPhipps - renames to doom_printf to avoid name collision with glibc +void doom_printf(const char *, ...) __attribute__((format(printf,1,2))); + +// killough 5/2/98: moved from m_misc.c: + +extern int key_right; +extern int key_left; +extern int key_up; +extern int key_down; +extern int key_menu_right; // phares 3/7/98 +extern int key_menu_left; // | +extern int key_menu_up; // V +extern int key_menu_down; +extern int key_menu_backspace; // ^ +extern int key_menu_escape; // | +extern int key_menu_enter; // phares 3/7/98 +extern int key_strafeleft; +extern int key_straferight; + +extern int key_fire; +extern int key_use; +extern int key_strafe; +extern int key_speed; +extern int key_escape; // phares +extern int key_savegame; // | +extern int key_loadgame; // V +extern int key_autorun; +extern int key_reverse; +extern int key_zoomin; +extern int key_zoomout; +extern int key_chat; +extern int key_backspace; +extern int key_enter; +extern int key_help; +extern int key_soundvolume; +extern int key_hud; +extern int key_quicksave; +extern int key_endgame; +extern int key_messages; +extern int key_quickload; +extern int key_quit; +extern int key_gamma; +extern int key_spy; +extern int key_pause; +extern int key_setup; +extern int key_forward; +extern int key_leftturn; +extern int key_rightturn; +extern int key_backward; +extern int key_weapontoggle; +extern int key_weapon1; +extern int key_weapon2; +extern int key_weapon3; +extern int key_weapon4; +extern int key_weapon5; +extern int key_weapon6; +extern int key_weapon7; +extern int key_weapon8; +extern int key_weapon9; +extern int destination_keys[MAXPLAYERS]; +extern int key_map_right; +extern int key_map_left; +extern int key_map_up; +extern int key_map_down; +extern int key_map_zoomin; +extern int key_map_zoomout; +extern int key_map; +extern int key_map_gobig; +extern int key_map_follow; +extern int key_map_mark; // ^ +extern int key_map_clear; // | +extern int key_map_grid; // phares +extern int key_map_rotate; // cph - map rotation +extern int key_map_overlay;// cph - map overlay +extern int key_screenshot; // killough 2/22/98 -- add key for screenshot +extern int autorun; // always running? // phares + +extern int defaultskill; //jff 3/24/98 default skill +extern boolean haswolflevels; //jff 4/18/98 wolf levels present + +extern int bodyquesize; // killough 2/8/98: adustable corpse limit + +// killough 5/2/98: moved from d_deh.c: +// Par times (new item with BOOM) - from g_game.c +extern int pars[4][10]; // hardcoded array size +extern int cpars[32]; // hardcoded array size +// CPhipps - Make savedesciption visible in wider scope +#define SAVEDESCLEN 32 +extern char savedescription[SAVEDESCLEN]; // Description to save in savegame + +/* cph - compatibility level strings */ +extern const char * comp_lev_str[]; + +#endif diff --git a/src/hu_lib.c b/src/hu_lib.c new file mode 100644 index 0000000..93c6a62 --- /dev/null +++ b/src/hu_lib.c @@ -0,0 +1,767 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: heads-up text and input code + * + *----------------------------------------------------------------------------- + */ + +#include "doomdef.h" +#include "doomstat.h" +#include "v_video.h" +#include "m_swap.h" +#include "hu_lib.h" +#include "hu_stuff.h" +#include "r_main.h" +#include "r_draw.h" + +// boolean : whether the screen is always erased +#define noterased viewwindowx + +extern int key_backspace; // phares +extern int key_enter; // phares + +// +// not used currently +// code to initialize HUlib would go here if needed +// +static void HUlib_init(void) +{ +} + +//////////////////////////////////////////////////////// +// +// Basic text line widget +// +//////////////////////////////////////////////////////// + +// +// HUlib_clearTextLine() +// +// Blank the internal text line in a hu_textline_t widget +// +// Passed a hu_textline_t, returns nothing +// +void HUlib_clearTextLine(hu_textline_t* t) +{ + t->linelen = // killough 1/23 98: support multiple lines + t->len = 0; + t->l[0] = 0; + t->needsupdate = true; +} + +// +// HUlib_initTextLine() +// +// Initialize a hu_textline_t widget. Set the position, font, start char +// of the font, and color range to be used. +// +// Passed a hu_textline_t, and the values used to initialize +// Returns nothing +// +void HUlib_initTextLine(hu_textline_t* t, int x, int y, + const patchnum_t* f, int sc, int cm ) + //jff 2/16/98 add color range parameter +{ + t->x = x; + t->y = y; + t->f = f; + t->sc = sc; + t->cm = cm; + HUlib_clearTextLine(t); +} + +// +// HUlib_addCharToTextLine() +// +// Adds a character at the end of the text line in a hu_textline_t widget +// +// Passed the hu_textline_t and the char to add +// Returns false if already at length limit, true if the character added +// +boolean HUlib_addCharToTextLine +( hu_textline_t* t, + char ch ) +{ + // killough 1/23/98 -- support multiple lines + if (t->linelen == HU_MAXLINELENGTH) + return false; + else + { + t->linelen++; + if (ch == '\n') + t->linelen=0; + + t->l[t->len++] = ch; + t->l[t->len] = 0; + t->needsupdate = 4; + return true; + } + +} + +// +// HUlib_delCharFromTextLine() +// +// Deletes a character at the end of the text line in a hu_textline_t widget +// +// Passed the hu_textline_t +// Returns false if already empty, true if the character deleted +// +static boolean HUlib_delCharFromTextLine(hu_textline_t* t) +{ + if (!t->len) return false; + else + { + t->l[--t->len] = 0; + t->needsupdate = 4; + return true; + } +} + +// +// HUlib_drawTextLine() +// +// Draws a hu_textline_t widget +// +// Passed the hu_textline_t and flag whether to draw a cursor +// Returns nothing +// +void HUlib_drawTextLine +( hu_textline_t* l, + boolean drawcursor ) +{ + + int i; + int w; + int x; + unsigned char c; + int oc = l->cm; //jff 2/17/98 remember default color + int y = l->y; // killough 1/18/98 -- support multiple lines + + // draw the new stuff + x = l->x; + for (i=0;ilen;i++) + { + c = toupper(l->l[i]); //jff insure were not getting a cheap toupper conv. + + if (c=='\n') // killough 1/18/98 -- support multiple lines + x=0,y+=8; + else if (c=='\t') // killough 1/23/98 -- support tab stops + x=x-x%80+80; + else if (c=='\x1b') //jff 2/17/98 escape code for color change + { //jff 3/26/98 changed to actual escape char + if (++ilen) + if (l->l[i]>='0' && l->l[i]<='9') + l->cm = l->l[i]-'0'; + } + else if (c != ' ' && c >= l->sc && c <= 127) + { + w = l->f[c - l->sc].width; + if (x+w > BASE_WIDTH) + break; + // killough 1/18/98 -- support multiple lines: + // CPhipps - patch drawing updated + V_DrawNumPatch(x, y, FG, l->f[c - l->sc].lumpnum, l->cm, VPT_TRANS | VPT_STRETCH); + x += w; + } + else + { + x += 4; + if (x >= BASE_WIDTH) + break; + } + } + l->cm = oc; //jff 2/17/98 restore original color + + // draw the cursor if requested + if (drawcursor && x + l->f['_' - l->sc].width <= BASE_WIDTH) + { + // killough 1/18/98 -- support multiple lines + // CPhipps - patch drawing updated + V_DrawNumPatch(x, y, FG, l->f['_' - l->sc].lumpnum, CR_DEFAULT, VPT_NONE | VPT_STRETCH); + } +} + +// +// HUlib_eraseTextLine() +// +// Erases a hu_textline_t widget when screen border is behind text +// Sorta called by HU_Erase and just better darn get things straight +// +// Passed the hu_textline_t +// Returns nothing +// +void HUlib_eraseTextLine(hu_textline_t* l) +{ + int lh; + int y; + + // Only erases when NOT in automap and the screen is reduced, + // and the text must either need updating or refreshing + // (because of a recent change back from the automap) + + if (!(automapmode & am_active) && viewwindowx && l->needsupdate) + { + lh = l->f[0].height + 1; + for (y=l->y; yy+lh ; y++) + { + if (y < viewwindowy || y >= viewwindowy + viewheight) + R_VideoErase(0, y, SCREENWIDTH); // erase entire line + else + { + // erase left border + R_VideoErase(0, y, viewwindowx); + // erase right border + R_VideoErase(viewwindowx + viewwidth, y, viewwindowx); + } + } + } + + if (l->needsupdate) l->needsupdate--; +} + +//////////////////////////////////////////////////////// +// +// Player message widget (up to 4 lines of text) +// +//////////////////////////////////////////////////////// + +// +// HUlib_initSText() +// +// Initialize a hu_stext_t widget. Set the position, number of lines, font, +// start char of the font, and color range to be used, and whether enabled. +// +// Passed a hu_stext_t, and the values used to initialize +// Returns nothing +// +void HUlib_initSText +( hu_stext_t* s, + int x, + int y, + int h, + const patchnum_t* font, + int startchar, + int cm, //jff 2/16/98 add color range parameter + boolean* on ) +{ + + int i; + + s->h = h; + s->on = on; + s->laston = true; + s->cl = 0; + for (i=0;il[i], + x, + y - i*(font[0].height+1), + font, + startchar, + cm + ); +} + +// +// HUlib_addLineToSText() +// +// Adds a blank line to a hu_stext_t widget +// +// Passed a hu_stext_t +// Returns nothing +// +static void HUlib_addLineToSText(hu_stext_t* s) +{ + + int i; + + // add a clear line + if (++s->cl == s->h) + s->cl = 0; + HUlib_clearTextLine(&s->l[s->cl]); + + // everything needs updating + for (i=0 ; ih ; i++) + s->l[i].needsupdate = 4; + +} + +// +// HUlib_addMessageToSText() +// +// Adds a message line with prefix to a hu_stext_t widget +// +// Passed a hu_stext_t, the prefix string, and a message string +// Returns nothing +// +void HUlib_addMessageToSText(hu_stext_t* s, const char* prefix, const char* msg) +{ + HUlib_addLineToSText(s); + if (prefix) + while (*prefix) + HUlib_addCharToTextLine(&s->l[s->cl], *(prefix++)); + + while (*msg) + HUlib_addCharToTextLine(&s->l[s->cl], *(msg++)); +} + +// +// HUlib_drawSText() +// +// Displays a hu_stext_t widget +// +// Passed a hu_stext_t +// Returns nothing +// +void HUlib_drawSText(hu_stext_t* s) +{ + int i, idx; + hu_textline_t *l; + + if (!*s->on) + return; // if not on, don't draw + + // draw everything + for (i=0 ; ih ; i++) + { + idx = s->cl - i; + if (idx < 0) + idx += s->h; // handle queue of lines + + l = &s->l[idx]; + + // need a decision made here on whether to skip the draw + HUlib_drawTextLine(l, false); // no cursor, please + } +} + +// +// HUlib_eraseSText() +// +// Erases a hu_stext_t widget, when the screen is not fullsize +// +// Passed a hu_stext_t +// Returns nothing +// +void HUlib_eraseSText(hu_stext_t* s) +{ + int i; + + for (i=0 ; ih ; i++) + { + if (s->laston && !*s->on) + s->l[i].needsupdate = 4; + HUlib_eraseTextLine(&s->l[i]); + } + s->laston = *s->on; +} + +//////////////////////////////////////////////////////// +// +// Scrolling message review widget +// +// jff added 2/26/98 +// +//////////////////////////////////////////////////////// + +// +// HUlib_initMText() +// +// Initialize a hu_mtext_t widget. Set the position, width, number of lines, +// font, start char of the font, color range, background font, and whether +// enabled. +// +// Passed a hu_mtext_t, and the values used to initialize +// Returns nothing +// +void HUlib_initMText(hu_mtext_t *m, int x, int y, int w, int h, + const patchnum_t* font, int startchar, int cm, + const patchnum_t* bgfont, boolean *on) +{ + int i; + + m->nl = 0; + m->nr = 0; + m->cl = -1; //jff 4/28/98 prepare for pre-increment + m->x = x; + m->y = y; + m->w = w; + m->h = h; + m->bg = bgfont; + m->on = on; + for (i=0;il[i], + x, + y + (hud_list_bgon? i+1 : i)*HU_REFRESHSPACING, + font, + startchar, + cm + ); + } +} + +// +// HUlib_addLineToMText() +// +// Adds a blank line to a hu_mtext_t widget +// +// Passed a hu_mtext_t +// Returns nothing +// +static void HUlib_addLineToMText(hu_mtext_t* m) +{ + // add a clear line + if (++m->cl == hud_msg_lines) + m->cl = 0; + HUlib_clearTextLine(&m->l[m->cl]); + + if (m->nlnl++; + + // needs updating + m->l[m->cl].needsupdate = 4; +} + +// +// HUlib_addMessageToMText() +// +// Adds a message line with prefix to a hu_mtext_t widget +// +// Passed a hu_mtext_t, the prefix string, and a message string +// Returns nothing +// +void HUlib_addMessageToMText(hu_mtext_t* m, const char* prefix, const char* msg) +{ + HUlib_addLineToMText(m); + if (prefix) + while (*prefix) + HUlib_addCharToTextLine(&m->l[m->cl], *(prefix++)); + + while (*msg) + HUlib_addCharToTextLine(&m->l[m->cl], *(msg++)); +} + +// +// HUlib_drawMBg() +// +// Draws a background box which the message display review widget can +// display over +// +// Passed position, width, height, and the background patches +// Returns nothing +// +void HUlib_drawMBg +( int x, + int y, + int w, + int h, + const patchnum_t* bgp +) +{ + int xs = bgp[0].width; + int ys = bgp[0].height; + int i,j; + + // CPhipps - patch drawing updated + // top rows + V_DrawNumPatch(x, y, FG, bgp[0].lumpnum, CR_DEFAULT, VPT_STRETCH); // ul + for (j=x+xs;jon) + return; // if not on, don't draw + + // draw everything + if (hud_list_bgon) + HUlib_drawMBg(m->x,m->y,m->w,m->h,m->bg); + y = m->y + HU_REFRESHSPACING; + for (i=0 ; inl ; i++) + { + idx = m->cl - i; + if (idx < 0) + idx += m->nl; // handle queue of lines + + l = &m->l[idx]; + if (hud_list_bgon) + { + l->x = m->x + 4; + l->y = m->y + (i+1)*HU_REFRESHSPACING; + } + else + { + l->x = m->x; + l->y = m->y + i*HU_REFRESHSPACING; + } + + // need a decision made here on whether to skip the draw + HUlib_drawTextLine(l, false); // no cursor, please + } +} + +// +// HUlib_eraseMBg() +// +// Erases background behind hu_mtext_t widget, when the screen is not fullsize +// +// Passed a hu_mtext_t +// Returns nothing +// +static void HUlib_eraseMBg(hu_mtext_t* m) +{ + int lh; + int y; + + // Only erases when NOT in automap and the screen is reduced, + // and the text must either need updating or refreshing + // (because of a recent change back from the automap) + + if (!(automapmode & am_active) && viewwindowx) + { + lh = m->l[0].f[0].height + 1; + for (y=m->y; yy+lh*(hud_msg_lines+2) ; y++) + { + if (y < viewwindowy || y >= viewwindowy + viewheight) + R_VideoErase(0, y, SCREENWIDTH); // erase entire line + else + { + // erase left border + R_VideoErase(0, y, viewwindowx); + // erase right border + R_VideoErase(viewwindowx + viewwidth, y, viewwindowx); + + } + } + } +} + +// +// HUlib_eraseMText() +// +// Erases a hu_mtext_t widget, when the screen is not fullsize +// +// Passed a hu_mtext_t +// Returns nothing +// +void HUlib_eraseMText(hu_mtext_t* m) +{ + int i; + + if (hud_list_bgon) + HUlib_eraseMBg(m); + + for (i=0 ; i< m->nl ; i++) + { + m->l[i].needsupdate = 4; + HUlib_eraseTextLine(&m->l[i]); + } +} + +//////////////////////////////////////////////////////// +// +// Interactive text entry widget +// +//////////////////////////////////////////////////////// + +// +// HUlib_initIText() +// +// Initialize a hu_itext_t widget. Set the position, font, +// start char of the font, color range, and whether enabled. +// +// Passed a hu_itext_t, and the values used to initialize +// Returns nothing +// +void HUlib_initIText +( hu_itext_t* it, + int x, + int y, + const patchnum_t* font, + int startchar, + int cm, //jff 2/16/98 add color range parameter + boolean* on ) +{ + it->lm = 0; // default left margin is start of text + it->on = on; + it->laston = true; + HUlib_initTextLine(&it->l, x, y, font, startchar, cm); +} + +// The following deletion routines adhere to the left margin restriction + +// +// HUlib_delCharFromIText() +// +// Deletes a character at the end of the text line in a hu_itext_t widget +// +// Passed the hu_itext_t +// Returns nothing +// +static void HUlib_delCharFromIText(hu_itext_t* it) +{ + if (it->l.len != it->lm) + HUlib_delCharFromTextLine(&it->l); +} + +// +// HUlib_eraseLineFromIText() +// +// Deletes all characters from a hu_itext_t widget +// +// Passed the hu_itext_t +// Returns nothing +// +static void HUlib_eraseLineFromIText(hu_itext_t* it) +{ + while (it->lm != it->l.len) + HUlib_delCharFromTextLine(&it->l); +} + +// +// HUlib_resetIText() +// +// Deletes all characters from a hu_itext_t widget +// Resets left margin as well +// +// Passed the hu_itext_t +// Returns nothing +// +void HUlib_resetIText(hu_itext_t* it) +{ + it->lm = 0; + HUlib_clearTextLine(&it->l); +} + +// +// HUlib_addPrefixToIText() +// +// Adds a prefix string passed to a hu_itext_t widget +// Sets left margin to length of string added +// +// Passed the hu_itext_t and the prefix string +// Returns nothing +// +void HUlib_addPrefixToIText +( hu_itext_t* it, + char* str ) +{ + while (*str) + HUlib_addCharToTextLine(&it->l, *(str++)); + it->lm = it->l.len; +} + +// +// HUlib_keyInIText() +// +// Wrapper function for handling general keyed input. +// +// Passed the hu_itext_t and the char input +// Returns true if it ate the key +// +boolean HUlib_keyInIText +( hu_itext_t* it, + unsigned char ch ) +{ + + if (ch >= ' ' && ch <= '_') + HUlib_addCharToTextLine(&it->l, (char) ch); + else if (ch == key_backspace) // phares + HUlib_delCharFromIText(it); + else if (ch != key_enter) // phares + return false; // did not eat key + + return true; // ate the key +} + +// +// HUlib_drawIText() +// +// Displays a hu_itext_t widget +// +// Passed the hu_itext_t +// Returns nothing +// +void HUlib_drawIText(hu_itext_t* it) +{ + hu_textline_t *l = &it->l; + + if (!*it->on) + return; + HUlib_drawTextLine(l, true); // draw the line w/ cursor +} + +// +// HUlib_eraseIText() +// +// Erases a hu_itext_t widget when the screen is not fullsize +// +// Passed the hu_itext_t +// Returns nothing +// +void HUlib_eraseIText(hu_itext_t* it) +{ + if (it->laston && !*it->on) + it->l.needsupdate = 4; + HUlib_eraseTextLine(&it->l); + it->laston = *it->on; +} diff --git a/src/hu_lib.h b/src/hu_lib.h new file mode 100644 index 0000000..db17572 --- /dev/null +++ b/src/hu_lib.h @@ -0,0 +1,247 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: none + * + *-----------------------------------------------------------------------------*/ + +#ifndef __HULIB__ +#define __HULIB__ + +// We are referring to patches. +#include "r_defs.h" +#include "v_video.h" //jff 2/16/52 include color range defs + + +/* background and foreground screen numbers + * different from other modules. */ +#define BG 1 +#define FG 0 + +/* font stuff + * #define HU_CHARERASE KEYD_BACKSPACE / not used / phares + */ + +#define HU_MAXLINES 4 +#define HU_MAXLINELENGTH 80 +#define HU_REFRESHSPACING 8 /*jff 2/26/98 space lines in text refresh widget*/ +/*jff 2/26/98 maximum number of messages allowed in refresh list */ +#define HU_MAXMESSAGES 16 + +/* + * Typedefs of widgets + */ + +/* Text Line widget + * (parent of Scrolling Text and Input Text widgets) */ +typedef struct +{ + // left-justified position of scrolling text window + int x; + int y; + + const patchnum_t* f; // font + int sc; // start character + //const char *cr; //jff 2/16/52 output color range + // Proff - Made this an int again. Needed for OpenGL + int cm; //jff 2/16/52 output color range + + // killough 1/23/98: Support multiple lines: + #define MAXLINES 25 + + int linelen; + char l[HU_MAXLINELENGTH*MAXLINES+1]; // line of text + int len; // current line length + + // whether this line needs to be udpated + int needsupdate; + +} hu_textline_t; + + + +// Scrolling Text window widget +// (child of Text Line widget) +typedef struct +{ + hu_textline_t l[HU_MAXLINES]; // text lines to draw + int h; // height in lines + int cl; // current line number + + // pointer to boolean stating whether to update window + boolean* on; + boolean laston; // last value of *->on. + +} hu_stext_t; + +//jff 2/26/98 new widget to display last hud_msg_lines of messages +// Message refresh window widget +typedef struct +{ + hu_textline_t l[HU_MAXMESSAGES]; // text lines to draw + int nl; // height in lines + int nr; // total height in rows + int cl; // current line number + + int x,y,w,h; // window position and size + const patchnum_t *bg; // patches for background + + // pointer to boolean stating whether to update window + boolean* on; + boolean laston; // last value of *->on. + +} hu_mtext_t; + + + +// Input Text Line widget +// (child of Text Line widget) +typedef struct +{ + hu_textline_t l; // text line to input on + + // left margin past which I am not to delete characters + int lm; + + // pointer to boolean stating whether to update window + boolean* on; + boolean laston; // last value of *->on; + +} hu_itext_t; + + +// +// Widget creation, access, and update routines +// + +// +// textline code +// + +// clear a line of text +void HUlib_clearTextLine(hu_textline_t *t); + +void HUlib_initTextLine +( + hu_textline_t *t, + int x, + int y, + const patchnum_t *f, + int sc, + int cm //jff 2/16/98 add color range parameter +); + +// returns success +boolean HUlib_addCharToTextLine(hu_textline_t *t, char ch); + +// draws tline +void HUlib_drawTextLine(hu_textline_t *l, boolean drawcursor); + +// erases text line +void HUlib_eraseTextLine(hu_textline_t *l); + + +// +// Scrolling Text window widget routines +// + +// initialize an stext widget +void HUlib_initSText +( hu_stext_t* s, + int x, + int y, + int h, + const patchnum_t* font, + int startchar, + int cm, //jff 2/16/98 add color range parameter + boolean* on ); + +// add a text message to an stext widget +void HUlib_addMessageToSText(hu_stext_t* s, const char* prefix, const char* msg); + +// draws stext +void HUlib_drawSText(hu_stext_t* s); + +// erases all stext lines +void HUlib_eraseSText(hu_stext_t* s); + +//jff 2/26/98 message refresh widget +// initialize refresh text widget +void HUlib_initMText(hu_mtext_t *m, int x, int y, int w, int h, const patchnum_t* font, + int startchar, int cm, const patchnum_t* bgfont, boolean *on); + +//jff 2/26/98 message refresh widget +// add a text message to refresh text widget +void HUlib_addMessageToMText(hu_mtext_t* m, const char* prefix, const char* msg); + +//jff 2/26/98 new routine to display a background on which +// the list of last hud_msg_lines are displayed +void HUlib_drawMBg +( int x, + int y, + int w, + int h, + const patchnum_t* bgp +); + +//jff 2/26/98 message refresh widget +// draws mtext +void HUlib_drawMText(hu_mtext_t* m); + +//jff 4/28/98 erases behind message list +void HUlib_eraseMText(hu_mtext_t* m); + +// Input Text Line widget routines +void HUlib_initIText +( hu_itext_t* it, + int x, + int y, + const patchnum_t* font, + int startchar, + int cm, //jff 2/16/98 add color range parameter + boolean* on ); + +// resets line and left margin +void HUlib_resetIText(hu_itext_t* it); + +// left of left-margin +void HUlib_addPrefixToIText +( hu_itext_t* it, + char* str ); + +// whether eaten +boolean HUlib_keyInIText +( hu_itext_t* it, + unsigned char ch ); + +void HUlib_drawIText(hu_itext_t* it); + +// erases all itext lines +void HUlib_eraseIText(hu_itext_t* it); + +#endif diff --git a/src/hu_stuff.c b/src/hu_stuff.c new file mode 100644 index 0000000..976bc2e --- /dev/null +++ b/src/hu_stuff.c @@ -0,0 +1,1593 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: Heads-up displays + * + *----------------------------------------------------------------------------- + */ + +// killough 5/3/98: remove unnecessary headers + +#include "doomstat.h" +#include "hu_stuff.h" +#include "hu_lib.h" +#include "st_stuff.h" /* jff 2/16/98 need loc of status bar */ +#include "w_wad.h" +#include "s_sound.h" +#include "dstrings.h" +#include "sounds.h" +#include "d_deh.h" /* Ty 03/27/98 - externalization of mapnamesx arrays */ +#include "g_game.h" +#include "r_main.h" + +// global heads up display controls + +int hud_active; //jff 2/17/98 controls heads-up display mode +int hud_displayed; //jff 2/23/98 turns heads-up display on/off +int hud_nosecrets; //jff 2/18/98 allows secrets line to be disabled in HUD +int hud_distributed; //jff 3/4/98 display HUD in different places on screen +int hud_graph_keys=1; //jff 3/7/98 display HUD keys as graphics + +// +// Locally used constants, shortcuts. +// +// Ty 03/28/98 - +// These four shortcuts modifed to reflect char ** of mapnamesx[] +#define HU_TITLE (*mapnames[(gameepisode-1)*9+gamemap-1]) +#define HU_TITLE2 (*mapnames2[gamemap-1]) +#define HU_TITLEP (*mapnamesp[gamemap-1]) +#define HU_TITLET (*mapnamest[gamemap-1]) +#define HU_TITLEHEIGHT 1 +#define HU_TITLEX 0 +//jff 2/16/98 change 167 to ST_Y-1 +// CPhipps - changed to ST_TY +// proff - changed to 200-ST_HEIGHT for stretching +#define HU_TITLEY ((200-ST_HEIGHT) - 1 - hu_font[0].height) + +//jff 2/16/98 add coord text widget coordinates +// proff - changed to SCREENWIDTH to 320 for stretching +#define HU_COORDX (320 - 13*hu_font2['A'-HU_FONTSTART].width) +//jff 3/3/98 split coord widget into three lines in upper right of screen +#define HU_COORDX_Y (1 + 0*hu_font['A'-HU_FONTSTART].height) +#define HU_COORDY_Y (2 + 1*hu_font['A'-HU_FONTSTART].height) +#define HU_COORDZ_Y (3 + 2*hu_font['A'-HU_FONTSTART].height) + +//jff 2/16/98 add ammo, health, armor widgets, 2/22/98 less gap +#define HU_GAPY 8 +#define HU_HUDHEIGHT (6*HU_GAPY) +#define HU_HUDX 2 +#define HU_HUDY (200-HU_HUDHEIGHT-1) +#define HU_MONSECX (HU_HUDX) +#define HU_MONSECY (HU_HUDY+0*HU_GAPY) +#define HU_KEYSX (HU_HUDX) +//jff 3/7/98 add offset for graphic key widget +#define HU_KEYSGX (HU_HUDX+4*hu_font2['A'-HU_FONTSTART].width) +#define HU_KEYSY (HU_HUDY+1*HU_GAPY) +#define HU_WEAPX (HU_HUDX) +#define HU_WEAPY (HU_HUDY+2*HU_GAPY) +#define HU_AMMOX (HU_HUDX) +#define HU_AMMOY (HU_HUDY+3*HU_GAPY) +#define HU_HEALTHX (HU_HUDX) +#define HU_HEALTHY (HU_HUDY+4*HU_GAPY) +#define HU_ARMORX (HU_HUDX) +#define HU_ARMORY (HU_HUDY+5*HU_GAPY) + +//jff 3/4/98 distributed HUD positions +#define HU_HUDX_LL 2 +#define HU_HUDY_LL (200-2*HU_GAPY-1) +// proff/nicolas 09/20/98: Changed for high-res +#define HU_HUDX_LR (320-120) +#define HU_HUDY_LR (200-2*HU_GAPY-1) +// proff/nicolas 09/20/98: Changed for high-res +#define HU_HUDX_UR (320-96) +#define HU_HUDY_UR 2 +#define HU_MONSECX_D (HU_HUDX_LL) +#define HU_MONSECY_D (HU_HUDY_LL+0*HU_GAPY) +#define HU_KEYSX_D (HU_HUDX_LL) +#define HU_KEYSGX_D (HU_HUDX_LL+4*hu_font2['A'-HU_FONTSTART].width) +#define HU_KEYSY_D (HU_HUDY_LL+1*HU_GAPY) +#define HU_WEAPX_D (HU_HUDX_LR) +#define HU_WEAPY_D (HU_HUDY_LR+0*HU_GAPY) +#define HU_AMMOX_D (HU_HUDX_LR) +#define HU_AMMOY_D (HU_HUDY_LR+1*HU_GAPY) +#define HU_HEALTHX_D (HU_HUDX_UR) +#define HU_HEALTHY_D (HU_HUDY_UR+0*HU_GAPY) +#define HU_ARMORX_D (HU_HUDX_UR) +#define HU_ARMORY_D (HU_HUDY_UR+1*HU_GAPY) + +//#define HU_INPUTTOGGLE 't' // not used // phares +#define HU_INPUTX HU_MSGX +#define HU_INPUTY (HU_MSGY + HU_MSGHEIGHT*(hu_font[0].height) +1) +#define HU_INPUTWIDTH 64 +#define HU_INPUTHEIGHT 1 + +#define key_alt KEYD_RALT +#define key_shift KEYD_RSHIFT + +const char* chat_macros[] = +// Ty 03/27/98 - *not* externalized +// CPhipps - const char* +{ + HUSTR_CHATMACRO0, + HUSTR_CHATMACRO1, + HUSTR_CHATMACRO2, + HUSTR_CHATMACRO3, + HUSTR_CHATMACRO4, + HUSTR_CHATMACRO5, + HUSTR_CHATMACRO6, + HUSTR_CHATMACRO7, + HUSTR_CHATMACRO8, + HUSTR_CHATMACRO9 +}; + +const char* player_names[] = +// Ty 03/27/98 - *not* externalized +// CPhipps - const char* +{ + HUSTR_PLRGREEN, + HUSTR_PLRINDIGO, + HUSTR_PLRBROWN, + HUSTR_PLRRED +}; + +//jff 3/17/98 translate player colmap to text color ranges +int plyrcoltran[MAXPLAYERS]={CR_GREEN,CR_GRAY,CR_BROWN,CR_RED}; + +char chat_char; // remove later. +static player_t* plr; + +// font sets +patchnum_t hu_font[HU_FONTSIZE]; +patchnum_t hu_font2[HU_FONTSIZE]; +patchnum_t hu_fontk[HU_FONTSIZE];//jff 3/7/98 added for graphic key indicators +patchnum_t hu_msgbg[9]; //jff 2/26/98 add patches for message background + +// widgets +static hu_textline_t w_title; +static hu_stext_t w_message; +static hu_itext_t w_chat; +static hu_itext_t w_inputbuffer[MAXPLAYERS]; +static hu_textline_t w_coordx; //jff 2/16/98 new coord widget for automap +static hu_textline_t w_coordy; //jff 3/3/98 split coord widgets automap +static hu_textline_t w_coordz; //jff 3/3/98 split coord widgets automap +static hu_textline_t w_ammo; //jff 2/16/98 new ammo widget for hud +static hu_textline_t w_health; //jff 2/16/98 new health widget for hud +static hu_textline_t w_armor; //jff 2/16/98 new armor widget for hud +static hu_textline_t w_weapon; //jff 2/16/98 new weapon widget for hud +static hu_textline_t w_keys; //jff 2/16/98 new keys widget for hud +static hu_textline_t w_gkeys; //jff 3/7/98 graphic keys widget for hud +static hu_textline_t w_monsec; //jff 2/16/98 new kill/secret widget for hud +static hu_mtext_t w_rtext; //jff 2/26/98 text message refresh widget + +static boolean always_off = false; +static char chat_dest[MAXPLAYERS]; +boolean chat_on; +static boolean message_on; +static boolean message_list; //2/26/98 enable showing list of messages +boolean message_dontfuckwithme; +static boolean message_nottobefuckedwith; +static int message_counter; +extern int showMessages; +extern boolean automapactive; +static boolean headsupactive = false; + +//jff 2/16/98 hud supported automap colors added +int hudcolor_titl; // color range of automap level title +int hudcolor_xyco; // color range of new coords on automap +//jff 2/16/98 hud text colors, controls added +int hudcolor_mesg; // color range of scrolling messages +int hudcolor_chat; // color range of chat lines +int hud_msg_lines; // number of message lines in window +//jff 2/26/98 hud text colors, controls added +int hudcolor_list; // list of messages color +int hud_list_bgon; // enable for solid window background for message list + +//jff 2/16/98 initialization strings for ammo, health, armor widgets +static char hud_coordstrx[32]; +static char hud_coordstry[32]; +static char hud_coordstrz[32]; +static char hud_ammostr[80]; +static char hud_healthstr[80]; +static char hud_armorstr[80]; +static char hud_weapstr[80]; +static char hud_keysstr[80]; +static char hud_gkeysstr[80]; //jff 3/7/98 add support for graphic key display +static char hud_monsecstr[80]; + +// +// Builtin map names. +// The actual names can be found in DStrings.h. +// +// Ty 03/27/98 - externalized map name arrays - now in d_deh.c +// and converted to arrays of pointers to char * +// See modified HUTITLEx macros +extern char **mapnames[]; +extern char **mapnames2[]; +extern char **mapnamesp[]; +extern char **mapnamest[]; + +extern int map_point_coordinates; + +// key tables +// jff 5/10/98 french support removed, +// as it was not being used and couldn't be easily tested +// +const char* shiftxform; + +const char english_shiftxform[] = +{ + 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, + ' ', '!', '"', '#', '$', '%', '&', + '"', // shift-' + '(', ')', '*', '+', + '<', // shift-, + '_', // shift-- + '>', // shift-. + '?', // shift-/ + ')', // shift-0 + '!', // shift-1 + '@', // shift-2 + '#', // shift-3 + '$', // shift-4 + '%', // shift-5 + '^', // shift-6 + '&', // shift-7 + '*', // shift-8 + '(', // shift-9 + ':', + ':', // shift-; + '<', + '+', // shift-= + '>', '?', '@', + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', + 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', + '[', // shift-[ + '!', // shift-backslash - OH MY GOD DOES WATCOM SUCK + ']', // shift-] + '"', '_', + '\'', // shift-` + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', + 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', + '{', '|', '}', '~', 127 +}; + +// +// HU_Init() +// +// Initialize the heads-up display, text that overwrites the primary display +// +// Passed nothing, returns nothing +// +void HU_Init(void) +{ + + int i; + int j; + char buffer[9]; + + shiftxform = english_shiftxform; + + // load the heads-up font + j = HU_FONTSTART; + for (i=0;i122) + { + sprintf(buffer, "STBR%.3d",j); + R_SetPatchNum(&hu_font2[i], buffer); + R_SetPatchNum(&hu_font[i], buffer); + } + else + hu_font[i] = hu_font[0]; //jff 2/16/98 account for gap + } + + // CPhipps - load patches for message background + for (i=0; i<9; i++) { + sprintf(buffer, "BOX%c%c", "UCL"[i/3], "LCR"[i%3]); + R_SetPatchNum(&hu_msgbg[i], buffer); + } + + // CPhipps - load patches for keys and double keys + for (i=0; i<6; i++) { + sprintf(buffer, "STKEYS%d", i); + R_SetPatchNum(&hu_fontk[i], buffer); + } +} + +// +// HU_Stop() +// +// Make the heads-up displays inactive +// +// Passed nothing, returns nothing +// +static void HU_Stop(void) +{ + headsupactive = false; +} + +// +// HU_Start(void) +// +// Create and initialize the heads-up widgets, software machines to +// maintain, update, and display information over the primary display +// +// This routine must be called after any change to the heads up configuration +// in order for the changes to take effect in the actual displays +// +// Passed nothing, returns nothing +// +void HU_Start(void) +{ + + int i; + const char* s; /* cph - const */ + + if (headsupactive) // stop before starting + HU_Stop(); + + plr = &players[displayplayer]; // killough 3/7/98 + message_on = false; + message_dontfuckwithme = false; + message_nottobefuckedwith = false; + chat_on = false; + + // create the message widget + // messages to player in upper-left of screen + HUlib_initSText + ( + &w_message, + HU_MSGX, + HU_MSGY, + HU_MSGHEIGHT, + hu_font, + HU_FONTSTART, + hudcolor_mesg, + &message_on + ); + + //jff 2/16/98 added some HUD widgets + // create the map title widget - map title display in lower left of automap + HUlib_initTextLine + ( + &w_title, + HU_TITLEX, + HU_TITLEY, + hu_font, + HU_FONTSTART, + hudcolor_titl + ); + + // create the hud health widget + // bargraph and number for amount of health, + // lower left or upper right of screen + HUlib_initTextLine + ( + &w_health, + hud_distributed? HU_HEALTHX_D : HU_HEALTHX, //3/4/98 distribute + hud_distributed? HU_HEALTHY_D : HU_HEALTHY, + hu_font2, + HU_FONTSTART, + CR_GREEN + ); + + // create the hud armor widget + // bargraph and number for amount of armor, + // lower left or upper right of screen + HUlib_initTextLine + ( + &w_armor, + hud_distributed? HU_ARMORX_D : HU_ARMORX, //3/4/98 distribute + hud_distributed? HU_ARMORY_D : HU_ARMORY, + hu_font2, + HU_FONTSTART, + CR_GREEN + ); + + // create the hud ammo widget + // bargraph and number for amount of ammo for current weapon, + // lower left or lower right of screen + HUlib_initTextLine + ( + &w_ammo, + hud_distributed? HU_AMMOX_D : HU_AMMOX, //3/4/98 distribute + hud_distributed? HU_AMMOY_D : HU_AMMOY, + hu_font2, + HU_FONTSTART, + CR_GOLD + ); + + // create the hud weapons widget + // list of numbers of weapons possessed + // lower left or lower right of screen + HUlib_initTextLine + ( + &w_weapon, + hud_distributed? HU_WEAPX_D : HU_WEAPX, //3/4/98 distribute + hud_distributed? HU_WEAPY_D : HU_WEAPY, + hu_font2, + HU_FONTSTART, + CR_GRAY + ); + + // create the hud keys widget + // display of key letters possessed + // lower left of screen + HUlib_initTextLine + ( + &w_keys, + hud_distributed? HU_KEYSX_D : HU_KEYSX, //3/4/98 distribute + hud_distributed? HU_KEYSY_D : HU_KEYSY, + hu_font2, + HU_FONTSTART, + CR_GRAY + ); + + // create the hud graphic keys widget + // display of key graphics possessed + // lower left of screen + HUlib_initTextLine + ( + &w_gkeys, + hud_distributed? HU_KEYSGX_D : HU_KEYSGX, //3/4/98 distribute + hud_distributed? HU_KEYSY_D : HU_KEYSY, + hu_fontk, + HU_FONTSTART, + CR_RED + ); + + // create the hud monster/secret widget + // totals and current values for kills, items, secrets + // lower left of screen + HUlib_initTextLine + ( + &w_monsec, + hud_distributed? HU_MONSECX_D : HU_MONSECX, //3/4/98 distribute + hud_distributed? HU_MONSECY_D : HU_MONSECY, + hu_font2, + HU_FONTSTART, + CR_GRAY + ); + + // create the hud text refresh widget + // scrolling display of last hud_msg_lines messages received + if (hud_msg_lines>HU_MAXMESSAGES) + hud_msg_lines=HU_MAXMESSAGES; + //jff 4/21/98 if setup has disabled message list while active, turn it off + message_list = hud_msg_lines > 1; //jff 8/8/98 initialize both ways + //jff 2/26/98 add the text refresh widget initialization + HUlib_initMText + ( + &w_rtext, + 0, + 0, + 320, +// SCREENWIDTH, + (hud_msg_lines+2)*HU_REFRESHSPACING, + hu_font, + HU_FONTSTART, + hudcolor_list, + hu_msgbg, + &message_list + ); + + // initialize the automap's level title widget + if (gamestate == GS_LEVEL) /* cph - stop SEGV here when not in level */ + switch (gamemode) + { + case shareware: + case registered: + case retail: + s = HU_TITLE; + break; + + case commercial: + default: // Ty 08/27/98 - modified to check mission for TNT/Plutonia + s = (gamemission==pack_tnt) ? HU_TITLET : + (gamemission==pack_plut) ? HU_TITLEP : HU_TITLE2; + break; + } else s = ""; + while (*s) + HUlib_addCharToTextLine(&w_title, *(s++)); + + // create the automaps coordinate widget + // jff 3/3/98 split coord widget into three lines: x,y,z + // jff 2/16/98 added + HUlib_initTextLine + ( + &w_coordx, + HU_COORDX, + HU_COORDX_Y, + hu_font, + HU_FONTSTART, + hudcolor_xyco + ); + HUlib_initTextLine + ( + &w_coordy, + HU_COORDX, + HU_COORDY_Y, + hu_font, + HU_FONTSTART, + hudcolor_xyco + ); + HUlib_initTextLine + ( + &w_coordz, + HU_COORDX, + HU_COORDZ_Y, + hu_font, + HU_FONTSTART, + hudcolor_xyco + ); + + // initialize the automaps coordinate widget + //jff 3/3/98 split coordstr widget into 3 parts + if (map_point_coordinates) + { + sprintf(hud_coordstrx,"X: %-5d",0); //jff 2/22/98 added z + s = hud_coordstrx; + while (*s) + HUlib_addCharToTextLine(&w_coordx, *(s++)); + sprintf(hud_coordstry,"Y: %-5d",0); //jff 3/3/98 split x,y,z + s = hud_coordstry; + while (*s) + HUlib_addCharToTextLine(&w_coordy, *(s++)); + sprintf(hud_coordstrz,"Z: %-5d",0); //jff 3/3/98 split x,y,z + s = hud_coordstrz; + while (*s) + HUlib_addCharToTextLine(&w_coordz, *(s++)); + } + + //jff 2/16/98 initialize ammo widget + strcpy(hud_ammostr,"AMM "); + s = hud_ammostr; + while (*s) + HUlib_addCharToTextLine(&w_ammo, *(s++)); + + //jff 2/16/98 initialize health widget + strcpy(hud_healthstr,"HEL "); + s = hud_healthstr; + while (*s) + HUlib_addCharToTextLine(&w_health, *(s++)); + + //jff 2/16/98 initialize armor widget + strcpy(hud_armorstr,"ARM "); + s = hud_armorstr; + while (*s) + HUlib_addCharToTextLine(&w_armor, *(s++)); + + //jff 2/17/98 initialize weapons widget + strcpy(hud_weapstr,"WEA "); + s = hud_weapstr; + while (*s) + HUlib_addCharToTextLine(&w_weapon, *(s++)); + + //jff 2/17/98 initialize keys widget + if (!deathmatch) //jff 3/17/98 show frags in deathmatch mode + strcpy(hud_keysstr,"KEY "); + else + strcpy(hud_keysstr,"FRG "); + s = hud_keysstr; + while (*s) + HUlib_addCharToTextLine(&w_keys, *(s++)); + + //jff 2/17/98 initialize graphic keys widget + strcpy(hud_gkeysstr," "); + s = hud_gkeysstr; + while (*s) + HUlib_addCharToTextLine(&w_gkeys, *(s++)); + + //jff 2/17/98 initialize kills/items/secret widget + strcpy(hud_monsecstr,"STS "); + s = hud_monsecstr; + while (*s) + HUlib_addCharToTextLine(&w_monsec, *(s++)); + + // create the chat widget + HUlib_initIText + ( + &w_chat, + HU_INPUTX, + HU_INPUTY, + hu_font, + HU_FONTSTART, + hudcolor_chat, + &chat_on + ); + + // create the inputbuffer widgets, one per player + for (i=0 ; imo->x)>>FRACBITS); + HUlib_clearTextLine(&w_coordx); + s = hud_coordstrx; + while (*s) + HUlib_addCharToTextLine(&w_coordx, *(s++)); + HUlib_drawTextLine(&w_coordx, false); + + //jff 3/3/98 split coord display into x,y,z lines + // y-coord + sprintf(hud_coordstry,"Y: %-5d", (plr->mo->y)>>FRACBITS); + HUlib_clearTextLine(&w_coordy); + s = hud_coordstry; + while (*s) + HUlib_addCharToTextLine(&w_coordy, *(s++)); + HUlib_drawTextLine(&w_coordy, false); + + //jff 3/3/98 split coord display into x,y,z lines + //jff 2/22/98 added z + // z-coord + sprintf(hud_coordstrz,"Z: %-5d", (plr->mo->z)>>FRACBITS); + HUlib_clearTextLine(&w_coordz); + s = hud_coordstrz; + while (*s) + HUlib_addCharToTextLine(&w_coordz, *(s++)); + HUlib_drawTextLine(&w_coordz, false); + } + } + + // draw the weapon/health/ammo/armor/kills/keys displays if optioned + //jff 2/17/98 allow new hud stuff to be turned off + // killough 2/21/98: really allow new hud stuff to be turned off COMPLETELY + if + ( + hud_active>0 && // hud optioned on + hud_displayed && // hud on from fullscreen key + viewheight==SCREENHEIGHT && // fullscreen mode is active + !(automapmode & am_active) // automap is not active + ) + { + doit = !(gametic&1); //jff 3/4/98 speed update up for slow systems + if (doit) //jff 8/7/98 update every time, avoid lag in update + { + HU_MoveHud(); // insure HUD display coords are correct + + // do the hud ammo display + // clear the widgets internal line + HUlib_clearTextLine(&w_ammo); + strcpy(hud_ammostr,"AMM "); + if (weaponinfo[plr->readyweapon].ammo == am_noammo) + { // special case for weapon with no ammo selected - blank bargraph + N/A + strcat(hud_ammostr,"\x7f\x7f\x7f\x7f\x7f\x7f\x7f N/A"); + w_ammo.cm = CR_GRAY; + } + else + { + int ammo = plr->ammo[weaponinfo[plr->readyweapon].ammo]; + int fullammo = plr->maxammo[weaponinfo[plr->readyweapon].ammo]; + int ammopct = (100*ammo)/fullammo; + int ammobars = ammopct/4; + + // build the numeric amount init string + sprintf(ammostr,"%d/%d",ammo,fullammo); + // build the bargraph string + // full bargraph chars + for (i=4;i<4+ammobars/4;) + hud_ammostr[i++] = 123; + // plus one last character with 0,1,2,3 bars + switch(ammobars%4) + { + case 0: + break; + case 1: + hud_ammostr[i++] = 126; + break; + case 2: + hud_ammostr[i++] = 125; + break; + case 3: + hud_ammostr[i++] = 124; + break; + } + // pad string with blank bar characters + while(i<4+7) + hud_ammostr[i++] = 127; + hud_ammostr[i] = '\0'; + strcat(hud_ammostr,ammostr); + + // set the display color from the percentage of total ammo held + if (ammopcthealth; + int healthbars = health>100? 25 : health/4; + + // clear the widgets internal line + HUlib_clearTextLine(&w_health); + + // build the numeric amount init string + sprintf(healthstr,"%3d",health); + // build the bargraph string + // full bargraph chars + for (i=4;i<4+healthbars/4;) + hud_healthstr[i++] = 123; + // plus one last character with 0,1,2,3 bars + switch(healthbars%4) + { + case 0: + break; + case 1: + hud_healthstr[i++] = 126; + break; + case 2: + hud_healthstr[i++] = 125; + break; + case 3: + hud_healthstr[i++] = 124; + break; + } + // pad string with blank bar characters + while(i<4+7) + hud_healthstr[i++] = 127; + hud_healthstr[i] = '\0'; + strcat(hud_healthstr,healthstr); + + // set the display color from the amount of health posessed + if (healtharmorpoints; + int armorbars = armor>100? 25 : armor/4; + + // clear the widgets internal line + HUlib_clearTextLine(&w_armor); + // build the numeric amount init string + sprintf(armorstr,"%3d",armor); + // build the bargraph string + // full bargraph chars + for (i=4;i<4+armorbars/4;) + hud_armorstr[i++] = 123; + // plus one last character with 0,1,2,3 bars + switch(armorbars%4) + { + case 0: + break; + case 1: + hud_armorstr[i++] = 126; + break; + case 2: + hud_armorstr[i++] = 125; + break; + case 3: + hud_armorstr[i++] = 124; + break; + } + // pad string with blank bar characters + while(i<4+7) + hud_armorstr[i++] = 127; + hud_armorstr[i] = '\0'; + strcat(hud_armorstr,armorstr); + + // set the display color from the amount of armor posessed + if (armor=wp_plasma && w!=wp_chainsaw) + ok=0; + break; + case retail: + case registered: + if (w>=wp_supershotgun) + ok=0; + break; + default: + case commercial: + break; + } + if (!ok) continue; + + ammo = plr->ammo[weaponinfo[w].ammo]; + fullammo = plr->maxammo[weaponinfo[w].ammo]; + ammopct=0; + + // skip weapons not currently posessed + if (!plr->weaponowned[w]) + continue; + + ammopct = fullammo? (100*ammo)/fullammo : 100; + + // display each weapon number in a color related to the ammo for it + hud_weapstr[i++] = '\x1b'; //jff 3/26/98 use ESC not '\' for paths + if (weaponinfo[w].ammo==am_noammo) //jff 3/14/98 show berserk on HUD + hud_weapstr[i++] = plr->powers[pw_strength]? '0'+CR_GREEN : '0'+CR_GRAY; + else if (ammopct1) + { + int k; + + hud_keysstr[4] = '\0'; //jff 3/7/98 make sure deleted keys go away + //jff add case for graphic key display + if (!deathmatch && hud_graph_keys) + { + i=0; + hud_gkeysstr[i] = '\0'; //jff 3/7/98 init graphic keys widget string + // build text string whose characters call out graphic keys from fontk + for (k=0;k<6;k++) + { + // skip keys not possessed + if (!plr->cards[k]) + continue; + + hud_gkeysstr[i++] = '!'+k; // key number plus '!' is char for key + hud_gkeysstr[i++] = ' '; // spacing + hud_gkeysstr[i++] = ' '; + } + hud_gkeysstr[i]='\0'; + } + else // not possible in current code, unless deathmatching, + { + i=4; + hud_keysstr[i] = '\0'; //jff 3/7/98 make sure deleted keys go away + + // if deathmatch, build string showing top four frag counts + if (deathmatch) //jff 3/17/98 show frags, not keys, in deathmatch + { + int top1=-999,top2=-999,top3=-999,top4=-999; + int idx1=-1,idx2=-1,idx3=-1,idx4=-1; + int fragcount,m; + char numbuf[32]; + + // scan thru players + for (k=0;ktop1) + { + top4=top3; top3=top2; top2 = top1; top1=fragcount; + idx4=idx3; idx3=idx2; idx2 = idx1; idx1=k; + } + else if (fragcount>top2) + { + top4=top3; top3=top2; top2=fragcount; + idx4=idx3; idx3=idx2; idx2=k; + } + else if (fragcount>top3) + { + top4=top3; top3=fragcount; + idx4=idx3; idx3=k; + } + else if (fragcount>top4) + { + top4=fragcount; + idx4=k; + } + } + // if the biggest number exists, put it in the init string + if (idx1>-1) + { + sprintf(numbuf,"%5d",top1); + // make frag count in player's color via escape code + hud_keysstr[i++] = '\x1b'; //jff 3/26/98 use ESC not '\' for paths + hud_keysstr[i++] = '0'+plyrcoltran[idx1&3]; + s = numbuf; + while (*s) + hud_keysstr[i++] = *(s++); + } + // if the second biggest number exists, put it in the init string + if (idx2>-1) + { + sprintf(numbuf,"%5d",top2); + // make frag count in player's color via escape code + hud_keysstr[i++] = '\x1b'; //jff 3/26/98 use ESC not '\' for paths + hud_keysstr[i++] = '0'+plyrcoltran[idx2&3]; + s = numbuf; + while (*s) + hud_keysstr[i++] = *(s++); + } + // if the third biggest number exists, put it in the init string + if (idx3>-1) + { + sprintf(numbuf,"%5d",top3); + // make frag count in player's color via escape code + hud_keysstr[i++] = '\x1b'; //jff 3/26/98 use ESC not '\' for paths + hud_keysstr[i++] = '0'+plyrcoltran[idx3&3]; + s = numbuf; + while (*s) + hud_keysstr[i++] = *(s++); + } + // if the fourth biggest number exists, put it in the init string + if (idx4>-1) + { + sprintf(numbuf,"%5d",top4); + // make frag count in player's color via escape code + hud_keysstr[i++] = '\x1b'; //jff 3/26/98 use ESC not '\' for paths + hud_keysstr[i++] = '0'+plyrcoltran[idx4&3]; + s = numbuf; + while (*s) + hud_keysstr[i++] = *(s++); + } + hud_keysstr[i] = '\0'; + } //jff 3/17/98 end of deathmatch clause + else // build alphabetical key display (not used currently) + { + // scan the keys + for (k=0;k<6;k++) + { + // skip any not possessed by the displayed player's stats + if (!plr->cards[k]) + continue; + + // use color escapes to make text in key's color + hud_keysstr[i++] = '\x1b'; //jff 3/26/98 use ESC not '\' for paths + switch(k) + { + case 0: + hud_keysstr[i++] = '0'+CR_BLUE; + hud_keysstr[i++] = 'B'; + hud_keysstr[i++] = 'C'; + hud_keysstr[i++] = ' '; + break; + case 1: + hud_keysstr[i++] = '0'+CR_GOLD; + hud_keysstr[i++] = 'Y'; + hud_keysstr[i++] = 'C'; + hud_keysstr[i++] = ' '; + break; + case 2: + hud_keysstr[i++] = '0'+CR_RED; + hud_keysstr[i++] = 'R'; + hud_keysstr[i++] = 'C'; + hud_keysstr[i++] = ' '; + break; + case 3: + hud_keysstr[i++] = '0'+CR_BLUE; + hud_keysstr[i++] = 'B'; + hud_keysstr[i++] = 'S'; + hud_keysstr[i++] = ' '; + break; + case 4: + hud_keysstr[i++] = '0'+CR_GOLD; + hud_keysstr[i++] = 'Y'; + hud_keysstr[i++] = 'S'; + hud_keysstr[i++] = ' '; + break; + case 5: + hud_keysstr[i++] = '0'+CR_RED; + hud_keysstr[i++] = 'R'; + hud_keysstr[i++] = 'S'; + hud_keysstr[i++] = ' '; + break; + } + hud_keysstr[i]='\0'; + } + } + } + } + // display the keys/frags line each frame + if (hud_active>1) + { + HUlib_clearTextLine(&w_keys); // clear the widget strings + HUlib_clearTextLine(&w_gkeys); + + // transfer the built string (frags or key title) to the widget + s = hud_keysstr; //jff 3/7/98 display key titles/key text or frags + while (*s) + HUlib_addCharToTextLine(&w_keys, *(s++)); + HUlib_drawTextLine(&w_keys, false); + + //jff 3/17/98 show graphic keys in non-DM only + if (!deathmatch) //jff 3/7/98 display graphic keys + { + // transfer the graphic key text to the widget + s = hud_gkeysstr; + while (*s) + HUlib_addCharToTextLine(&w_gkeys, *(s++)); + // display the widget + HUlib_drawTextLine(&w_gkeys, false); + } + } + + // display the hud kills/items/secret display if optioned + if (!hud_nosecrets) + { + if (hud_active>1 && doit) + { + // clear the internal widget text buffer + HUlib_clearTextLine(&w_monsec); + //jff 3/26/98 use ESC not '\' for paths + // build the init string with fixed colors + sprintf + ( + hud_monsecstr, + "STS \x1b\x36K \x1b\x33%d \x1b\x36M \x1b\x33%d \x1b\x37I \x1b\x33%d/%d \x1b\x35S \x1b\x33%d/%d", + plr->killcount,totallive, + plr->itemcount,totalitems, + plr->secretcount,totalsecret + ); + // transfer the init string to the widget + s = hud_monsecstr; + while (*s) + HUlib_addCharToTextLine(&w_monsec, *(s++)); + } + // display the kills/items/secrets each frame, if optioned + if (hud_active>1) + HUlib_drawTextLine(&w_monsec, false); + } + } + + //jff 3/4/98 display last to give priority + HU_Erase(); // jff 4/24/98 Erase current lines before drawing current + // needed when screen not fullsize + + //jff 4/21/98 if setup has disabled message list while active, turn it off + if (hud_msg_lines<=1) + message_list = false; + + // if the message review not enabled, show the standard message widget + if (!message_list) + HUlib_drawSText(&w_message); + + // if the message review is enabled show the scrolling message review + if (hud_msg_lines>1 && message_list) + HUlib_drawMText(&w_rtext); + + // display the interactive buffer for chat entry + HUlib_drawIText(&w_chat); +} + +// +// HU_Erase() +// +// Erase hud display lines that can be trashed by small screen display +// +// Passed nothing, returns nothing +// +void HU_Erase(void) +{ + // erase the message display or the message review display + if (!message_list) + HUlib_eraseSText(&w_message); + else + HUlib_eraseMText(&w_rtext); + + // erase the interactive text buffer for chat entry + HUlib_eraseIText(&w_chat); + + // erase the automap title + HUlib_eraseTextLine(&w_title); +} + +// +// HU_Ticker() +// +// Update the hud displays once per frame +// +// Passed nothing, returns nothing +// +static boolean bsdown; // Is backspace down? +static int bscounter; + +void HU_Ticker(void) +{ + int i, rc; + char c; + + // tick down message counter if message is up + if (message_counter && !--message_counter) + { + message_on = false; + message_nottobefuckedwith = false; + } + if (bsdown && bscounter++ > 9) { + HUlib_keyInIText(&w_chat, (unsigned char)key_backspace); + bscounter = 8; + } + + // if messages on, or "Messages Off" is being displayed + // this allows the notification of turning messages off to be seen + if (showMessages || message_dontfuckwithme) + { + // display message if necessary + if ((plr->message && !message_nottobefuckedwith) + || (plr->message && message_dontfuckwithme)) + { + //post the message to the message widget + HUlib_addMessageToSText(&w_message, 0, plr->message); + //jff 2/26/98 add message to refresh text widget too + HUlib_addMessageToMText(&w_rtext, 0, plr->message); + + // clear the message to avoid posting multiple times + plr->message = 0; + // note a message is displayed + message_on = true; + // start the message persistence counter + message_counter = HU_MSGTIMEOUT; + // transfer "Messages Off" exception to the "being displayed" variable + message_nottobefuckedwith = message_dontfuckwithme; + // clear the flag that "Messages Off" is being posted + message_dontfuckwithme = 0; + } + } + + // check for incoming chat characters + if (netgame) + { + for (i=0; i= 'a' && c <= 'z') + c = (char) shiftxform[(unsigned char) c]; + rc = HUlib_keyInIText(&w_inputbuffer[i], c); + if (rc && c == KEYD_ENTER) + { + if (w_inputbuffer[i].l.len + && (chat_dest[i] == consoleplayer+1 + || chat_dest[i] == HU_BROADCAST)) + { + HUlib_addMessageToSText(&w_message, + player_names[i], + w_inputbuffer[i].l.l); + + message_nottobefuckedwith = true; + message_on = true; + message_counter = HU_MSGTIMEOUT; + if ( gamemode == commercial ) + S_StartSound(0, sfx_radio); + else + S_StartSound(0, sfx_tink); + } + HUlib_resetIText(&w_inputbuffer[i]); + } + } + players[i].cmd.chatchar = 0; + } + } + } +} + +#define QUEUESIZE 128 + +static char chatchars[QUEUESIZE]; +static int head = 0; +static int tail = 0; + +// +// HU_queueChatChar() +// +// Add an incoming character to the circular chat queue +// +// Passed the character to queue, returns nothing +// +static void HU_queueChatChar(char c) +{ + if (((head + 1) & (QUEUESIZE-1)) == tail) + { + plr->message = HUSTR_MSGU; + } + else + { + chatchars[head] = c; + head = (head + 1) & (QUEUESIZE-1); + } +} + +// +// HU_dequeueChatChar() +// +// Remove the earliest added character from the circular chat queue +// +// Passed nothing, returns the character dequeued +// +char HU_dequeueChatChar(void) +{ + char c; + + if (head != tail) + { + c = chatchars[tail]; + tail = (tail + 1) & (QUEUESIZE-1); + } + else + { + c = 0; + } + return c; +} + +// +// HU_Responder() +// +// Responds to input events that affect the heads up displays +// +// Passed the event to respond to, returns true if the event was handled +// +boolean HU_Responder(event_t *ev) +{ + + static char lastmessage[HU_MAXLINELENGTH+1]; + const char* macromessage; // CPhipps - const char* + boolean eatkey = false; + static boolean shiftdown = false; + static boolean altdown = false; + unsigned char c; + int i; + int numplayers; + + static int num_nobrainers = 0; + + numplayers = 0; + for (i=0 ; idata1 == key_shift) + { + shiftdown = ev->type == ev_keydown; + return false; + } + else if (ev->data1 == key_alt) + { + altdown = ev->type == ev_keydown; + return false; + } + else if (ev->data1 == key_backspace) + { + bsdown = ev->type == ev_keydown; + bscounter = 0; + } + + if (ev->type != ev_keydown) + return false; + + if (!chat_on) + { + if (ev->data1 == key_enter) // phares + { +#ifndef INSTRUMENTED // never turn on message review if INSTRUMENTED defined + if (hud_msg_lines>1) // it posts multi-line messages that will trash + { + if (message_list) HU_Erase(); //jff 4/28/98 erase behind messages + message_list = !message_list; //jff 2/26/98 toggle list of messages + } +#endif + if (!message_list) // if not message list, refresh message + { + message_on = true; + message_counter = HU_MSGTIMEOUT; + } + eatkey = true; + }//jff 2/26/98 no chat if message review is displayed + // killough 10/02/98: no chat if demo playback + // no chat in -solo-net mode + else if (!demoplayback && !message_list && netgame && numplayers > 1) + { + if (ev->data1 == key_chat) + { + eatkey = chat_on = true; + HUlib_resetIText(&w_chat); + HU_queueChatChar(HU_BROADCAST); + } + else if (numplayers > 2) + { + for (i=0; idata1 == destination_keys[i]) + { + if (playeringame[i] && i!=consoleplayer) + { + eatkey = chat_on = true; + HUlib_resetIText(&w_chat); + HU_queueChatChar((char)(i+1)); + break; + } + else if (i == consoleplayer) + { + num_nobrainers++; + if (num_nobrainers < 3) + plr->message = HUSTR_TALKTOSELF1; + else if (num_nobrainers < 6) + plr->message = HUSTR_TALKTOSELF2; + else if (num_nobrainers < 9) + plr->message = HUSTR_TALKTOSELF3; + else if (num_nobrainers < 32) + plr->message = HUSTR_TALKTOSELF4; + else + plr->message = HUSTR_TALKTOSELF5; + } + } + } + } + } + }//jff 2/26/98 no chat functions if message review is displayed + else if (!message_list) + { + c = ev->data1; + // send a macro + if (altdown) + { + c = c - '0'; + if (c > 9) + return false; + macromessage = chat_macros[c]; + + // kill last message with a '\n' + HU_queueChatChar((char)key_enter); // DEBUG!!! // phares + + // send the macro message + while (*macromessage) + HU_queueChatChar(*macromessage++); + HU_queueChatChar((char)key_enter); // phares + + // leave chat mode and notify that it was sent + chat_on = false; + strcpy(lastmessage, chat_macros[c]); + plr->message = lastmessage; + eatkey = true; + } + else + { + if (shiftdown || (c >= 'a' && c <= 'z')) + c = shiftxform[c]; + eatkey = HUlib_keyInIText(&w_chat, c); + if (eatkey) + HU_queueChatChar(c); + + if (c == key_enter) // phares + { + chat_on = false; + if (w_chat.l.len) + { + strcpy(lastmessage, w_chat.l.l); + plr->message = lastmessage; + } + } + else if (c == key_escape) // phares + chat_on = false; + } + } + return eatkey; +} diff --git a/src/hu_stuff.h b/src/hu_stuff.h new file mode 100644 index 0000000..e4cbf24 --- /dev/null +++ b/src/hu_stuff.h @@ -0,0 +1,90 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: Head up display + * + *-----------------------------------------------------------------------------*/ + +#ifndef __HU_STUFF_H__ +#define __HU_STUFF_H__ + +#include "d_event.h" + +/* + * Globally visible constants. + */ +#define HU_FONTSTART '!' /* the first font characters */ +#define HU_FONTEND (0x7f) /*jff 2/16/98 '_' the last font characters */ + +/* Calculate # of glyphs in font. */ +#define HU_FONTSIZE (HU_FONTEND - HU_FONTSTART + 1) + +#define HU_BROADCAST 5 + +/*#define HU_MSGREFRESH KEYD_ENTER phares */ +#define HU_MSGX 0 +#define HU_MSGY 0 +#define HU_MSGWIDTH 64 /* in characters */ +#define HU_MSGHEIGHT 1 /* in lines */ + +#define HU_MSGTIMEOUT (4*TICRATE) + +/* + * Heads up text + */ +void HU_Init(void); +void HU_Start(void); + +boolean HU_Responder(event_t* ev); + +void HU_Ticker(void); +void HU_Drawer(void); +char HU_dequeueChatChar(void); +void HU_Erase(void); +void HU_MoveHud(void); // jff 3/9/98 avoid glitch in HUD display + +/* killough 5/2/98: moved from m_misc.c: */ + +/* jff 2/16/98 hud supported automap colors added */ +extern int hudcolor_titl; /* color range of automap level title */ +extern int hudcolor_xyco; /* color range of new coords on automap */ +/* jff 2/16/98 hud text colors, controls added */ +extern int hudcolor_mesg; /* color range of scrolling messages */ +extern int hudcolor_chat; /* color range of chat lines */ +/* jff 2/26/98 hud message list color and background enable */ +extern int hudcolor_list; /* color of list of past messages */ +extern int hud_list_bgon; /* solid window background for list of messages */ +extern int hud_msg_lines; /* number of message lines in window up to 16 */ +extern int hud_distributed; /* whether hud is all in lower left or distributed */ +/* jff 2/23/98 hud is currently displayed */ +extern int hud_displayed; /* hud is displayed */ +/* jff 2/18/98 hud/status control */ +extern int hud_active; /* hud mode 0=off, 1=small, 2=full */ +extern int hud_nosecrets; /* status does not list secrets/items/kills */ + +#endif diff --git a/src/i_joy.h b/src/i_joy.h new file mode 100644 index 0000000..29f19cb --- /dev/null +++ b/src/i_joy.h @@ -0,0 +1,47 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Joystick interface. + * + *-----------------------------------------------------------------------------*/ + +extern int joybfire; +extern int joybstrafe; +extern int joybuse; +extern int joybspeed; + +extern int joyleft; +extern int joyright; +extern int joyup; +extern int joydown; + +extern int usejoystick; + +void I_InitJoystick(void); +void I_PollJoystick(void); diff --git a/src/i_main.h b/src/i_main.h new file mode 100644 index 0000000..60b4662 --- /dev/null +++ b/src/i_main.h @@ -0,0 +1,44 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * General system functions. Signal related stuff, exit function + * prototypes, and programmable Doom clock. + * + *----------------------------------------------------------------------------- + */ + +#ifndef __I_MAIN__ +#define __I_MAIN__ + +void I_Init(void); +void I_SafeExit(int rc); + +extern int (*I_GetTime)(void); + +#endif diff --git a/src/i_network.h b/src/i_network.h new file mode 100644 index 0000000..532941f --- /dev/null +++ b/src/i_network.h @@ -0,0 +1,74 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Low level network interface. + *-----------------------------------------------------------------------------*/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef USE_SDL_NET + #include "SDL_net.h" + #define UDP_SOCKET UDPsocket + #define UDP_PACKET UDPpacket + #define AF_INET + #define UDP_CHANNEL int + extern UDP_SOCKET udp_socket; +#else + #define UDP_CHANNEL struct sockaddr +#endif + +#ifndef IPPORT_RESERVED + #define IPPORT_RESERVED 1024 +#endif + +void I_InitNetwork(void); +size_t I_GetPacket(packet_header_t* buffer, size_t buflen); +void I_SendPacket(packet_header_t* packet, size_t len); +void I_WaitForPacket(int ms); + +#ifdef USE_SDL_NET +UDP_SOCKET I_Socket(Uint16 port); +int I_ConnectToServer(const char *serv); +UDP_CHANNEL I_RegisterPlayer(IPaddress *ipaddr); +void I_UnRegisterPlayer(UDP_CHANNEL channel); +extern IPaddress sentfrom_addr; +#endif + +#ifdef AF_INET +void I_SendPacketTo(packet_header_t* packet, size_t len, UDP_CHANNEL *to); +void I_SetupSocket(int sock, int port, int family); +void I_PrintAddress(FILE* fp, UDP_CHANNEL *addr); + +extern UDP_CHANNEL sentfrom; +extern int v4socket, v6socket; +#endif + +extern size_t sentbytes, recvdbytes; diff --git a/src/i_sound.h b/src/i_sound.h new file mode 100644 index 0000000..e03ccbf --- /dev/null +++ b/src/i_sound.h @@ -0,0 +1,120 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * System interface, sound. + * + *-----------------------------------------------------------------------------*/ + +#ifndef __I_SOUND__ +#define __I_SOUND__ + +#include "sounds.h" +#include "doomtype.h" + +#define SNDSERV +#undef SNDINTR + +#ifndef SNDSERV +#include "l_soundgen.h" +#endif + +// Init at program start... +void I_InitSound(void); + +// ... shut down and relase at program termination. +void I_ShutdownSound(void); + +// +// SFX I/O +// + +// Initialize channels? +void I_SetChannels(void); + +// Get raw data lump index for sound descriptor. +int I_GetSfxLumpNum (sfxinfo_t *sfxinfo); + +// Starts a sound in a particular sound channel. +int I_StartSound(int id, int channel, int vol, int sep, int pitch, int priority); + +// Stops a sound channel. +void I_StopSound(int handle); + +// Called by S_*() functions +// to see if a channel is still playing. +// Returns 0 if no longer playing, 1 if playing. +boolean I_SoundIsPlaying(int handle); + +// Called by m_menu.c to let the quit sound play and quit right after it stops +boolean I_AnySoundStillPlaying(void); + +// Updates the volume, separation, +// and pitch of a sound channel. +void I_UpdateSoundParams(int handle, int vol, int sep, int pitch); + +// +// MUSIC I/O +// +void I_InitMusic(void); +void I_ShutdownMusic(void); + +void I_UpdateMusic(void); + +// Volume. +void I_SetMusicVolume(int volume); + +// PAUSE game handling. +void I_PauseSong(int handle); +void I_ResumeSong(int handle); + +// Registers a song handle to song data. +int I_RegisterSong(const void *data, size_t len); + +// cournia - tries to load a music file +int I_RegisterMusic( const char* filename, musicinfo_t *music ); + +// Called by anything that wishes to start music. +// plays a song, and when the song is done, +// starts playing it again in an endless loop. +// Horrible thing to do, considering. +void I_PlaySong(int handle, int looping); + +// Stops a song over 3 seconds. +void I_StopSong(int handle); + +// See above (register), then think backwards +void I_UnRegisterSong(int handle); + +// Allegro card support jff 1/18/98 +extern int snd_card; +extern int mus_card; +// CPhipps - put these in config file +extern int snd_samplerate; + +#endif diff --git a/src/i_system.h b/src/i_system.h new file mode 100644 index 0000000..727a5ea --- /dev/null +++ b/src/i_system.h @@ -0,0 +1,77 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * System specific interface stuff. + * + *-----------------------------------------------------------------------------*/ + +#ifndef __I_SYSTEM__ +#define __I_SYSTEM__ + +#ifdef __GNUG__ +#pragma interface +#endif + +extern int ms_to_next_tick; +boolean I_StartDisplay(void); +void I_EndDisplay(void); +int I_GetTime_RealTime(void); /* killough */ +#ifndef PRBOOM_SERVER +fixed_t I_GetTimeFrac (void); +#endif +void I_GetTime_SaveMS(void); + +unsigned long I_GetRandomTimeSeed(void); /* cphipps */ + +void I_uSleep(unsigned long usecs); + +/* cphipps - I_GetVersionString + * Returns a version string in the given buffer + */ +const char* I_GetVersionString(char* buf, size_t sz); + +/* cphipps - I_SigString + * Returns a string describing a signal number + */ +const char* I_SigString(char* buf, size_t sz, int signum); + +const char *I_DoomExeDir(void); // killough 2/16/98: path to executable's dir + +boolean HasTrailingSlash(const char* dn); +char* I_FindFile(const char* wfname, const char* ext); + +/* cph 2001/11/18 - wrapper for read(2) which deals with partial reads */ +void I_Read(int fd, void* buf, size_t sz); + +/* cph 2001/11/18 - Move W_Filelength to i_system.c */ +int I_Filelength(int handle); + +void I_SetAffinityMask(void); + +#endif diff --git a/src/i_video.h b/src/i_video.h new file mode 100644 index 0000000..00d1a04 --- /dev/null +++ b/src/i_video.h @@ -0,0 +1,82 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * System specific interface stuff. + * + *-----------------------------------------------------------------------------*/ + +#ifndef __I_VIDEO__ +#define __I_VIDEO__ + +#include "doomtype.h" +#include "v_video.h" + +#ifdef __GNUG__ +#pragma interface +#endif + +void I_PreInitGraphics(void); /* CPhipps - do stuff immediately on start */ +void I_CalculateRes(unsigned int width, unsigned int height); /* calculate resolution */ +void I_SetRes(void); /* set resolution */ +void I_InitGraphics (void); +void I_UpdateVideoMode(void); +void I_ShutdownGraphics(void); + +/* Takes full 8 bit values. */ +void I_SetPalette(int pal); /* CPhipps - pass down palette number */ + +void I_UpdateNoBlit (void); +void I_FinishUpdate (void); + +int I_ScreenShot (const char *fname); + +/* I_StartTic + * Called by D_DoomLoop, + * called before processing each tic in a frame. + * Quick syncronous operations are performed here. + * Can call D_PostEvent. + */ +void I_StartTic (void); + +/* I_StartFrame + * Called by D_DoomLoop, + * called before processing any tics in a frame + * (just after displaying a frame). + * Time consuming syncronous operations + * are performed here (joystick reading). + * Can call D_PostEvent. + */ + +void I_StartFrame (void); + +extern int use_doublebuffer; /* proff 2001-7-4 - controls wether to use doublebuffering*/ +extern int use_fullscreen; /* proff 21/05/2000 */ +extern int desired_fullscreen; //e6y + +#endif diff --git a/src/info.c b/src/info.c new file mode 100644 index 0000000..2e64614 --- /dev/null +++ b/src/info.c @@ -0,0 +1,4899 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Thing frame/state LUT, + * generated by multigen utilitiy. + * This one is the original DOOM version, preserved. + * BOOM changes include commenting and addition of predefined lumps + * for providing things that aren't in the IWAD without sending a + * separate must-use wad file around with the EXE. + * + *----------------------------------------------------------------------------- + */ + +#include "doomdef.h" +#include "sounds.h" +#include "m_fixed.h" +#include "p_mobj.h" +#include "p_enemy.h" +#include "p_pspr.h" +#include "w_wad.h" + +#ifdef __GNUG__ +#pragma implementation "info.h" +#endif +#include "info.h" + + +// ******************************************************************** +// Sprite names +// ******************************************************************** +// This is the list of sprite 4-character prefixes. They are searched +// through, with a NULL entry terminating the list. In DOOM originally +// this NULL entry was missing, and coincidentally the next thing in +// memory was the dummy state_t[] entry that started with zero bytes. +// killough 1/17/98: add an explicit NULL entry. +// NUMSPRITES is an enum from info.h where all these are listed +// as SPR_xxxx + +const char *sprnames[NUMSPRITES+1] = { + "TROO","SHTG","PUNG","PISG","PISF","SHTF","SHT2","CHGG","CHGF","MISG", + "MISF","SAWG","PLSG","PLSF","BFGG","BFGF","BLUD","PUFF","BAL1","BAL2", + "PLSS","PLSE","MISL","BFS1","BFE1","BFE2","TFOG","IFOG","PLAY","POSS", + "SPOS","VILE","FIRE","FATB","FBXP","SKEL","MANF","FATT","CPOS","SARG", + "HEAD","BAL7","BOSS","BOS2","SKUL","SPID","BSPI","APLS","APBX","CYBR", + "PAIN","SSWV","KEEN","BBRN","BOSF","ARM1","ARM2","BAR1","BEXP","FCAN", + "BON1","BON2","BKEY","RKEY","YKEY","BSKU","RSKU","YSKU","STIM","MEDI", + "SOUL","PINV","PSTR","PINS","MEGA","SUIT","PMAP","PVIS","CLIP","AMMO", + "ROCK","BROK","CELL","CELP","SHEL","SBOX","BPAK","BFUG","MGUN","CSAW", + "LAUN","PLAS","SHOT","SGN2","COLU","SMT2","GOR1","POL2","POL5","POL4", + "POL3","POL1","POL6","GOR2","GOR3","GOR4","GOR5","SMIT","COL1","COL2", + "COL3","COL4","CAND","CBRA","COL6","TRE1","TRE2","ELEC","CEYE","FSKU", + "COL5","TBLU","TGRN","TRED","SMBT","SMGT","SMRT","HDB1","HDB2","HDB3", + "HDB4","HDB5","HDB6","POB1","POB2","BRS1","TLMP","TLP2", + "TNT1", // invisible sprite phares 3/9/98 +#ifdef DOGS + "DOGS", /* killough 7/19/98: Marine's best friend :) */ +#endif + NULL +}; + +// ******************************************************************** +// State or "frame" information +// ******************************************************************** +// Each of the states, otherwise known as "frames", is outlined +// here. The data in each element of the array is the way it is +// initialized, with sprite names identified by their enumerator +// value such as SPR_SHTG. These correlate to the above sprite +// array so don't change them around unless you understand what +// you're doing. +// +// The commented name beginning with S_ at the end of each line +// is there to help figure out where the next-frame pointer is +// pointing. These are also additionally identified in info.h +// as enumerated values. From a change-and-recompile point of +// view this is fairly workable, but it adds a lot to the effort +// when trying to change things externally. See also the d_deh.c +// parts where frame rewiring is done for more details and the +// extended way a BEX file can handle this. + +state_t states[NUMSTATES] = { + {SPR_TROO,0,-1,NULL,S_NULL,0,0}, // S_NULL + {SPR_SHTG,4,0,A_Light0,S_NULL,0,0}, // S_LIGHTDONE + {SPR_PUNG,0,1,A_WeaponReady,S_PUNCH,0,0}, // S_PUNCH + {SPR_PUNG,0,1,A_Lower,S_PUNCHDOWN,0,0}, // S_PUNCHDOWN + {SPR_PUNG,0,1,A_Raise,S_PUNCHUP,0,0}, // S_PUNCHUP + {SPR_PUNG,1,4,NULL,S_PUNCH2,0,0}, // S_PUNCH1 + {SPR_PUNG,2,4,A_Punch,S_PUNCH3,0,0}, // S_PUNCH2 + {SPR_PUNG,3,5,NULL,S_PUNCH4,0,0}, // S_PUNCH3 + {SPR_PUNG,2,4,NULL,S_PUNCH5,0,0}, // S_PUNCH4 + {SPR_PUNG,1,5,A_ReFire,S_PUNCH,0,0}, // S_PUNCH5 + {SPR_PISG,0,1,A_WeaponReady,S_PISTOL,0,0},// S_PISTOL + {SPR_PISG,0,1,A_Lower,S_PISTOLDOWN,0,0}, // S_PISTOLDOWN + {SPR_PISG,0,1,A_Raise,S_PISTOLUP,0,0}, // S_PISTOLUP + {SPR_PISG,0,4,NULL,S_PISTOL2,0,0}, // S_PISTOL1 + {SPR_PISG,1,6,A_FirePistol,S_PISTOL3,0,0},// S_PISTOL2 + {SPR_PISG,2,4,NULL,S_PISTOL4,0,0}, // S_PISTOL3 + {SPR_PISG,1,5,A_ReFire,S_PISTOL,0,0}, // S_PISTOL4 + {SPR_PISF,32768,7,A_Light1,S_LIGHTDONE,0,0}, // S_PISTOLFLASH + {SPR_SHTG,0,1,A_WeaponReady,S_SGUN,0,0}, // S_SGUN + {SPR_SHTG,0,1,A_Lower,S_SGUNDOWN,0,0}, // S_SGUNDOWN + {SPR_SHTG,0,1,A_Raise,S_SGUNUP,0,0}, // S_SGUNUP + {SPR_SHTG,0,3,NULL,S_SGUN2,0,0}, // S_SGUN1 + {SPR_SHTG,0,7,A_FireShotgun,S_SGUN3,0,0}, // S_SGUN2 + {SPR_SHTG,1,5,NULL,S_SGUN4,0,0}, // S_SGUN3 + {SPR_SHTG,2,5,NULL,S_SGUN5,0,0}, // S_SGUN4 + {SPR_SHTG,3,4,NULL,S_SGUN6,0,0}, // S_SGUN5 + {SPR_SHTG,2,5,NULL,S_SGUN7,0,0}, // S_SGUN6 + {SPR_SHTG,1,5,NULL,S_SGUN8,0,0}, // S_SGUN7 + {SPR_SHTG,0,3,NULL,S_SGUN9,0,0}, // S_SGUN8 + {SPR_SHTG,0,7,A_ReFire,S_SGUN,0,0}, // S_SGUN9 + {SPR_SHTF,32768,4,A_Light1,S_SGUNFLASH2,0,0}, // S_SGUNFLASH1 + {SPR_SHTF,32769,3,A_Light2,S_LIGHTDONE,0,0}, // S_SGUNFLASH2 + {SPR_SHT2,0,1,A_WeaponReady,S_DSGUN,0,0}, // S_DSGUN + {SPR_SHT2,0,1,A_Lower,S_DSGUNDOWN,0,0}, // S_DSGUNDOWN + {SPR_SHT2,0,1,A_Raise,S_DSGUNUP,0,0}, // S_DSGUNUP + {SPR_SHT2,0,3,NULL,S_DSGUN2,0,0}, // S_DSGUN1 + {SPR_SHT2,0,7,A_FireShotgun2,S_DSGUN3,0,0}, // S_DSGUN2 + {SPR_SHT2,1,7,NULL,S_DSGUN4,0,0}, // S_DSGUN3 + {SPR_SHT2,2,7,A_CheckReload,S_DSGUN5,0,0}, // S_DSGUN4 + {SPR_SHT2,3,7,A_OpenShotgun2,S_DSGUN6,0,0}, // S_DSGUN5 + {SPR_SHT2,4,7,NULL,S_DSGUN7,0,0}, // S_DSGUN6 + {SPR_SHT2,5,7,A_LoadShotgun2,S_DSGUN8,0,0}, // S_DSGUN7 + {SPR_SHT2,6,6,NULL,S_DSGUN9,0,0}, // S_DSGUN8 + {SPR_SHT2,7,6,A_CloseShotgun2,S_DSGUN10,0,0}, // S_DSGUN9 + {SPR_SHT2,0,5,A_ReFire,S_DSGUN,0,0}, // S_DSGUN10 + {SPR_SHT2,1,7,NULL,S_DSNR2,0,0}, // S_DSNR1 + {SPR_SHT2,0,3,NULL,S_DSGUNDOWN,0,0}, // S_DSNR2 + {SPR_SHT2,32776,5,A_Light1,S_DSGUNFLASH2,0,0}, // S_DSGUNFLASH1 + {SPR_SHT2,32777,4,A_Light2,S_LIGHTDONE,0,0}, // S_DSGUNFLASH2 + {SPR_CHGG,0,1,A_WeaponReady,S_CHAIN,0,0}, // S_CHAIN + {SPR_CHGG,0,1,A_Lower,S_CHAINDOWN,0,0}, // S_CHAINDOWN + {SPR_CHGG,0,1,A_Raise,S_CHAINUP,0,0}, // S_CHAINUP + {SPR_CHGG,0,4,A_FireCGun,S_CHAIN2,0,0}, // S_CHAIN1 + {SPR_CHGG,1,4,A_FireCGun,S_CHAIN3,0,0}, // S_CHAIN2 + {SPR_CHGG,1,0,A_ReFire,S_CHAIN,0,0}, // S_CHAIN3 + {SPR_CHGF,32768,5,A_Light1,S_LIGHTDONE,0,0}, // S_CHAINFLASH1 + {SPR_CHGF,32769,5,A_Light2,S_LIGHTDONE,0,0}, // S_CHAINFLASH2 + {SPR_MISG,0,1,A_WeaponReady,S_MISSILE,0,0}, // S_MISSILE + {SPR_MISG,0,1,A_Lower,S_MISSILEDOWN,0,0}, // S_MISSILEDOWN + {SPR_MISG,0,1,A_Raise,S_MISSILEUP,0,0}, // S_MISSILEUP + {SPR_MISG,1,8,A_GunFlash,S_MISSILE2,0,0}, // S_MISSILE1 + {SPR_MISG,1,12,A_FireMissile,S_MISSILE3,0,0}, // S_MISSILE2 + {SPR_MISG,1,0,A_ReFire,S_MISSILE,0,0}, // S_MISSILE3 + {SPR_MISF,32768,3,A_Light1,S_MISSILEFLASH2,0,0}, // S_MISSILEFLASH1 + {SPR_MISF,32769,4,NULL,S_MISSILEFLASH3,0,0}, // S_MISSILEFLASH2 + {SPR_MISF,32770,4,A_Light2,S_MISSILEFLASH4,0,0}, // S_MISSILEFLASH3 + {SPR_MISF,32771,4,A_Light2,S_LIGHTDONE,0,0}, // S_MISSILEFLASH4 + {SPR_SAWG,2,4,A_WeaponReady,S_SAWB,0,0}, // S_SAW + {SPR_SAWG,3,4,A_WeaponReady,S_SAW,0,0}, // S_SAWB + {SPR_SAWG,2,1,A_Lower,S_SAWDOWN,0,0}, // S_SAWDOWN + {SPR_SAWG,2,1,A_Raise,S_SAWUP,0,0}, // S_SAWUP + {SPR_SAWG,0,4,A_Saw,S_SAW2,0,0}, // S_SAW1 + {SPR_SAWG,1,4,A_Saw,S_SAW3,0,0}, // S_SAW2 + {SPR_SAWG,1,0,A_ReFire,S_SAW,0,0}, // S_SAW3 + {SPR_PLSG,0,1,A_WeaponReady,S_PLASMA,0,0}, // S_PLASMA + {SPR_PLSG,0,1,A_Lower,S_PLASMADOWN,0,0}, // S_PLASMADOWN + {SPR_PLSG,0,1,A_Raise,S_PLASMAUP,0,0}, // S_PLASMAUP + {SPR_PLSG,0,3,A_FirePlasma,S_PLASMA2,0,0}, // S_PLASMA1 + {SPR_PLSG,1,20,A_ReFire,S_PLASMA,0,0}, // S_PLASMA2 + {SPR_PLSF,32768,4,A_Light1,S_LIGHTDONE,0,0}, // S_PLASMAFLASH1 + {SPR_PLSF,32769,4,A_Light1,S_LIGHTDONE,0,0}, // S_PLASMAFLASH2 + {SPR_BFGG,0,1,A_WeaponReady,S_BFG,0,0}, // S_BFG + {SPR_BFGG,0,1,A_Lower,S_BFGDOWN,0,0}, // S_BFGDOWN + {SPR_BFGG,0,1,A_Raise,S_BFGUP,0,0}, // S_BFGUP + {SPR_BFGG,0,20,A_BFGsound,S_BFG2,0,0}, // S_BFG1 + {SPR_BFGG,1,10,A_GunFlash,S_BFG3,0,0}, // S_BFG2 + {SPR_BFGG,1,10,A_FireBFG,S_BFG4,0,0}, // S_BFG3 + {SPR_BFGG,1,20,A_ReFire,S_BFG,0,0}, // S_BFG4 + {SPR_BFGF,32768,11,A_Light1,S_BFGFLASH2,0,0}, // S_BFGFLASH1 + {SPR_BFGF,32769,6,A_Light2,S_LIGHTDONE,0,0}, // S_BFGFLASH2 + {SPR_BLUD,2,8,NULL,S_BLOOD2,0,0}, // S_BLOOD1 + {SPR_BLUD,1,8,NULL,S_BLOOD3,0,0}, // S_BLOOD2 + {SPR_BLUD,0,8,NULL,S_NULL,0,0}, // S_BLOOD3 + {SPR_PUFF,32768,4,NULL,S_PUFF2,0,0}, // S_PUFF1 + {SPR_PUFF,1,4,NULL,S_PUFF3,0,0}, // S_PUFF2 + {SPR_PUFF,2,4,NULL,S_PUFF4,0,0}, // S_PUFF3 + {SPR_PUFF,3,4,NULL,S_NULL,0,0}, // S_PUFF4 + {SPR_BAL1,32768,4,NULL,S_TBALL2,0,0}, // S_TBALL1 + {SPR_BAL1,32769,4,NULL,S_TBALL1,0,0}, // S_TBALL2 + {SPR_BAL1,32770,6,NULL,S_TBALLX2,0,0}, // S_TBALLX1 + {SPR_BAL1,32771,6,NULL,S_TBALLX3,0,0}, // S_TBALLX2 + {SPR_BAL1,32772,6,NULL,S_NULL,0,0}, // S_TBALLX3 + {SPR_BAL2,32768,4,NULL,S_RBALL2,0,0}, // S_RBALL1 + {SPR_BAL2,32769,4,NULL,S_RBALL1,0,0}, // S_RBALL2 + {SPR_BAL2,32770,6,NULL,S_RBALLX2,0,0}, // S_RBALLX1 + {SPR_BAL2,32771,6,NULL,S_RBALLX3,0,0}, // S_RBALLX2 + {SPR_BAL2,32772,6,NULL,S_NULL,0,0}, // S_RBALLX3 + {SPR_PLSS,32768,6,NULL,S_PLASBALL2,0,0}, // S_PLASBALL + {SPR_PLSS,32769,6,NULL,S_PLASBALL,0,0}, // S_PLASBALL2 + {SPR_PLSE,32768,4,NULL,S_PLASEXP2,0,0}, // S_PLASEXP + {SPR_PLSE,32769,4,NULL,S_PLASEXP3,0,0}, // S_PLASEXP2 + {SPR_PLSE,32770,4,NULL,S_PLASEXP4,0,0}, // S_PLASEXP3 + {SPR_PLSE,32771,4,NULL,S_PLASEXP5,0,0}, // S_PLASEXP4 + {SPR_PLSE,32772,4,NULL,S_NULL,0,0}, // S_PLASEXP5 + {SPR_MISL,32768,1,NULL,S_ROCKET,0,0}, // S_ROCKET + {SPR_BFS1,32768,4,NULL,S_BFGSHOT2,0,0}, // S_BFGSHOT + {SPR_BFS1,32769,4,NULL,S_BFGSHOT,0,0}, // S_BFGSHOT2 + {SPR_BFE1,32768,8,NULL,S_BFGLAND2,0,0}, // S_BFGLAND + {SPR_BFE1,32769,8,NULL,S_BFGLAND3,0,0}, // S_BFGLAND2 + {SPR_BFE1,32770,8,A_BFGSpray,S_BFGLAND4,0,0}, // S_BFGLAND3 + {SPR_BFE1,32771,8,NULL,S_BFGLAND5,0,0}, // S_BFGLAND4 + {SPR_BFE1,32772,8,NULL,S_BFGLAND6,0,0}, // S_BFGLAND5 + {SPR_BFE1,32773,8,NULL,S_NULL,0,0}, // S_BFGLAND6 + {SPR_BFE2,32768,8,NULL,S_BFGEXP2,0,0}, // S_BFGEXP + {SPR_BFE2,32769,8,NULL,S_BFGEXP3,0,0}, // S_BFGEXP2 + {SPR_BFE2,32770,8,NULL,S_BFGEXP4,0,0}, // S_BFGEXP3 + {SPR_BFE2,32771,8,NULL,S_NULL,0,0}, // S_BFGEXP4 + {SPR_MISL,32769,8,A_Explode,S_EXPLODE2,0,0}, // S_EXPLODE1 + {SPR_MISL,32770,6,NULL,S_EXPLODE3,0,0}, // S_EXPLODE2 + {SPR_MISL,32771,4,NULL,S_NULL,0,0}, // S_EXPLODE3 + {SPR_TFOG,32768,6,NULL,S_TFOG01,0,0}, // S_TFOG + {SPR_TFOG,32769,6,NULL,S_TFOG02,0,0}, // S_TFOG01 + {SPR_TFOG,32768,6,NULL,S_TFOG2,0,0}, // S_TFOG02 + {SPR_TFOG,32769,6,NULL,S_TFOG3,0,0}, // S_TFOG2 + {SPR_TFOG,32770,6,NULL,S_TFOG4,0,0}, // S_TFOG3 + {SPR_TFOG,32771,6,NULL,S_TFOG5,0,0}, // S_TFOG4 + {SPR_TFOG,32772,6,NULL,S_TFOG6,0,0}, // S_TFOG5 + {SPR_TFOG,32773,6,NULL,S_TFOG7,0,0}, // S_TFOG6 + {SPR_TFOG,32774,6,NULL,S_TFOG8,0,0}, // S_TFOG7 + {SPR_TFOG,32775,6,NULL,S_TFOG9,0,0}, // S_TFOG8 + {SPR_TFOG,32776,6,NULL,S_TFOG10,0,0}, // S_TFOG9 + {SPR_TFOG,32777,6,NULL,S_NULL,0,0}, // S_TFOG10 + {SPR_IFOG,32768,6,NULL,S_IFOG01,0,0}, // S_IFOG + {SPR_IFOG,32769,6,NULL,S_IFOG02,0,0}, // S_IFOG01 + {SPR_IFOG,32768,6,NULL,S_IFOG2,0,0}, // S_IFOG02 + {SPR_IFOG,32769,6,NULL,S_IFOG3,0,0}, // S_IFOG2 + {SPR_IFOG,32770,6,NULL,S_IFOG4,0,0}, // S_IFOG3 + {SPR_IFOG,32771,6,NULL,S_IFOG5,0,0}, // S_IFOG4 + {SPR_IFOG,32772,6,NULL,S_NULL,0,0}, // S_IFOG5 + {SPR_PLAY,0,-1,NULL,S_NULL,0,0}, // S_PLAY + {SPR_PLAY,0,4,NULL,S_PLAY_RUN2,0,0}, // S_PLAY_RUN1 + {SPR_PLAY,1,4,NULL,S_PLAY_RUN3,0,0}, // S_PLAY_RUN2 + {SPR_PLAY,2,4,NULL,S_PLAY_RUN4,0,0}, // S_PLAY_RUN3 + {SPR_PLAY,3,4,NULL,S_PLAY_RUN1,0,0}, // S_PLAY_RUN4 + {SPR_PLAY,4,12,NULL,S_PLAY,0,0}, // S_PLAY_ATK1 + {SPR_PLAY,32773,6,NULL,S_PLAY_ATK1,0,0}, // S_PLAY_ATK2 + {SPR_PLAY,6,4,NULL,S_PLAY_PAIN2,0,0}, // S_PLAY_PAIN + {SPR_PLAY,6,4,A_Pain,S_PLAY,0,0}, // S_PLAY_PAIN2 + {SPR_PLAY,7,10,NULL,S_PLAY_DIE2,0,0}, // S_PLAY_DIE1 + {SPR_PLAY,8,10,A_PlayerScream,S_PLAY_DIE3,0,0}, // S_PLAY_DIE2 + {SPR_PLAY,9,10,A_Fall,S_PLAY_DIE4,0,0}, // S_PLAY_DIE3 + {SPR_PLAY,10,10,NULL,S_PLAY_DIE5,0,0}, // S_PLAY_DIE4 + {SPR_PLAY,11,10,NULL,S_PLAY_DIE6,0,0}, // S_PLAY_DIE5 + {SPR_PLAY,12,10,NULL,S_PLAY_DIE7,0,0}, // S_PLAY_DIE6 + {SPR_PLAY,13,-1,NULL,S_NULL,0,0}, // S_PLAY_DIE7 + {SPR_PLAY,14,5,NULL,S_PLAY_XDIE2,0,0}, // S_PLAY_XDIE1 + {SPR_PLAY,15,5,A_XScream,S_PLAY_XDIE3,0,0}, // S_PLAY_XDIE2 + {SPR_PLAY,16,5,A_Fall,S_PLAY_XDIE4,0,0}, // S_PLAY_XDIE3 + {SPR_PLAY,17,5,NULL,S_PLAY_XDIE5,0,0}, // S_PLAY_XDIE4 + {SPR_PLAY,18,5,NULL,S_PLAY_XDIE6,0,0}, // S_PLAY_XDIE5 + {SPR_PLAY,19,5,NULL,S_PLAY_XDIE7,0,0}, // S_PLAY_XDIE6 + {SPR_PLAY,20,5,NULL,S_PLAY_XDIE8,0,0}, // S_PLAY_XDIE7 + {SPR_PLAY,21,5,NULL,S_PLAY_XDIE9,0,0}, // S_PLAY_XDIE8 + {SPR_PLAY,22,-1,NULL,S_NULL,0,0}, // S_PLAY_XDIE9 + {SPR_POSS,0,10,A_Look,S_POSS_STND2,0,0}, // S_POSS_STND + {SPR_POSS,1,10,A_Look,S_POSS_STND,0,0}, // S_POSS_STND2 + {SPR_POSS,0,4,A_Chase,S_POSS_RUN2,0,0}, // S_POSS_RUN1 + {SPR_POSS,0,4,A_Chase,S_POSS_RUN3,0,0}, // S_POSS_RUN2 + {SPR_POSS,1,4,A_Chase,S_POSS_RUN4,0,0}, // S_POSS_RUN3 + {SPR_POSS,1,4,A_Chase,S_POSS_RUN5,0,0}, // S_POSS_RUN4 + {SPR_POSS,2,4,A_Chase,S_POSS_RUN6,0,0}, // S_POSS_RUN5 + {SPR_POSS,2,4,A_Chase,S_POSS_RUN7,0,0}, // S_POSS_RUN6 + {SPR_POSS,3,4,A_Chase,S_POSS_RUN8,0,0}, // S_POSS_RUN7 + {SPR_POSS,3,4,A_Chase,S_POSS_RUN1,0,0}, // S_POSS_RUN8 + {SPR_POSS,4,10,A_FaceTarget,S_POSS_ATK2,0,0}, // S_POSS_ATK1 + {SPR_POSS,5,8,A_PosAttack,S_POSS_ATK3,0,0}, // S_POSS_ATK2 + {SPR_POSS,4,8,NULL,S_POSS_RUN1,0,0}, // S_POSS_ATK3 + {SPR_POSS,6,3,NULL,S_POSS_PAIN2,0,0}, // S_POSS_PAIN + {SPR_POSS,6,3,A_Pain,S_POSS_RUN1,0,0}, // S_POSS_PAIN2 + {SPR_POSS,7,5,NULL,S_POSS_DIE2,0,0}, // S_POSS_DIE1 + {SPR_POSS,8,5,A_Scream,S_POSS_DIE3,0,0}, // S_POSS_DIE2 + {SPR_POSS,9,5,A_Fall,S_POSS_DIE4,0,0}, // S_POSS_DIE3 + {SPR_POSS,10,5,NULL,S_POSS_DIE5,0,0}, // S_POSS_DIE4 + {SPR_POSS,11,-1,NULL,S_NULL,0,0}, // S_POSS_DIE5 + {SPR_POSS,12,5,NULL,S_POSS_XDIE2,0,0}, // S_POSS_XDIE1 + {SPR_POSS,13,5,A_XScream,S_POSS_XDIE3,0,0}, // S_POSS_XDIE2 + {SPR_POSS,14,5,A_Fall,S_POSS_XDIE4,0,0}, // S_POSS_XDIE3 + {SPR_POSS,15,5,NULL,S_POSS_XDIE5,0,0}, // S_POSS_XDIE4 + {SPR_POSS,16,5,NULL,S_POSS_XDIE6,0,0}, // S_POSS_XDIE5 + {SPR_POSS,17,5,NULL,S_POSS_XDIE7,0,0}, // S_POSS_XDIE6 + {SPR_POSS,18,5,NULL,S_POSS_XDIE8,0,0}, // S_POSS_XDIE7 + {SPR_POSS,19,5,NULL,S_POSS_XDIE9,0,0}, // S_POSS_XDIE8 + {SPR_POSS,20,-1,NULL,S_NULL,0,0}, // S_POSS_XDIE9 + {SPR_POSS,10,5,NULL,S_POSS_RAISE2,0,0}, // S_POSS_RAISE1 + {SPR_POSS,9,5,NULL,S_POSS_RAISE3,0,0}, // S_POSS_RAISE2 + {SPR_POSS,8,5,NULL,S_POSS_RAISE4,0,0}, // S_POSS_RAISE3 + {SPR_POSS,7,5,NULL,S_POSS_RUN1,0,0}, // S_POSS_RAISE4 + {SPR_SPOS,0,10,A_Look,S_SPOS_STND2,0,0}, // S_SPOS_STND + {SPR_SPOS,1,10,A_Look,S_SPOS_STND,0,0}, // S_SPOS_STND2 + {SPR_SPOS,0,3,A_Chase,S_SPOS_RUN2,0,0}, // S_SPOS_RUN1 + {SPR_SPOS,0,3,A_Chase,S_SPOS_RUN3,0,0}, // S_SPOS_RUN2 + {SPR_SPOS,1,3,A_Chase,S_SPOS_RUN4,0,0}, // S_SPOS_RUN3 + {SPR_SPOS,1,3,A_Chase,S_SPOS_RUN5,0,0}, // S_SPOS_RUN4 + {SPR_SPOS,2,3,A_Chase,S_SPOS_RUN6,0,0}, // S_SPOS_RUN5 + {SPR_SPOS,2,3,A_Chase,S_SPOS_RUN7,0,0}, // S_SPOS_RUN6 + {SPR_SPOS,3,3,A_Chase,S_SPOS_RUN8,0,0}, // S_SPOS_RUN7 + {SPR_SPOS,3,3,A_Chase,S_SPOS_RUN1,0,0}, // S_SPOS_RUN8 + {SPR_SPOS,4,10,A_FaceTarget,S_SPOS_ATK2,0,0}, // S_SPOS_ATK1 + {SPR_SPOS,32773,10,A_SPosAttack,S_SPOS_ATK3,0,0}, // S_SPOS_ATK2 + {SPR_SPOS,4,10,NULL,S_SPOS_RUN1,0,0}, // S_SPOS_ATK3 + {SPR_SPOS,6,3,NULL,S_SPOS_PAIN2,0,0}, // S_SPOS_PAIN + {SPR_SPOS,6,3,A_Pain,S_SPOS_RUN1,0,0}, // S_SPOS_PAIN2 + {SPR_SPOS,7,5,NULL,S_SPOS_DIE2,0,0}, // S_SPOS_DIE1 + {SPR_SPOS,8,5,A_Scream,S_SPOS_DIE3,0,0}, // S_SPOS_DIE2 + {SPR_SPOS,9,5,A_Fall,S_SPOS_DIE4,0,0}, // S_SPOS_DIE3 + {SPR_SPOS,10,5,NULL,S_SPOS_DIE5,0,0}, // S_SPOS_DIE4 + {SPR_SPOS,11,-1,NULL,S_NULL,0,0}, // S_SPOS_DIE5 + {SPR_SPOS,12,5,NULL,S_SPOS_XDIE2,0,0}, // S_SPOS_XDIE1 + {SPR_SPOS,13,5,A_XScream,S_SPOS_XDIE3,0,0}, // S_SPOS_XDIE2 + {SPR_SPOS,14,5,A_Fall,S_SPOS_XDIE4,0,0}, // S_SPOS_XDIE3 + {SPR_SPOS,15,5,NULL,S_SPOS_XDIE5,0,0}, // S_SPOS_XDIE4 + {SPR_SPOS,16,5,NULL,S_SPOS_XDIE6,0,0}, // S_SPOS_XDIE5 + {SPR_SPOS,17,5,NULL,S_SPOS_XDIE7,0,0}, // S_SPOS_XDIE6 + {SPR_SPOS,18,5,NULL,S_SPOS_XDIE8,0,0}, // S_SPOS_XDIE7 + {SPR_SPOS,19,5,NULL,S_SPOS_XDIE9,0,0}, // S_SPOS_XDIE8 + {SPR_SPOS,20,-1,NULL,S_NULL,0,0}, // S_SPOS_XDIE9 + {SPR_SPOS,11,5,NULL,S_SPOS_RAISE2,0,0}, // S_SPOS_RAISE1 + {SPR_SPOS,10,5,NULL,S_SPOS_RAISE3,0,0}, // S_SPOS_RAISE2 + {SPR_SPOS,9,5,NULL,S_SPOS_RAISE4,0,0}, // S_SPOS_RAISE3 + {SPR_SPOS,8,5,NULL,S_SPOS_RAISE5,0,0}, // S_SPOS_RAISE4 + {SPR_SPOS,7,5,NULL,S_SPOS_RUN1,0,0}, // S_SPOS_RAISE5 + {SPR_VILE,0,10,A_Look,S_VILE_STND2,0,0}, // S_VILE_STND + {SPR_VILE,1,10,A_Look,S_VILE_STND,0,0}, // S_VILE_STND2 + {SPR_VILE,0,2,A_VileChase,S_VILE_RUN2,0,0}, // S_VILE_RUN1 + {SPR_VILE,0,2,A_VileChase,S_VILE_RUN3,0,0}, // S_VILE_RUN2 + {SPR_VILE,1,2,A_VileChase,S_VILE_RUN4,0,0}, // S_VILE_RUN3 + {SPR_VILE,1,2,A_VileChase,S_VILE_RUN5,0,0}, // S_VILE_RUN4 + {SPR_VILE,2,2,A_VileChase,S_VILE_RUN6,0,0}, // S_VILE_RUN5 + {SPR_VILE,2,2,A_VileChase,S_VILE_RUN7,0,0}, // S_VILE_RUN6 + {SPR_VILE,3,2,A_VileChase,S_VILE_RUN8,0,0}, // S_VILE_RUN7 + {SPR_VILE,3,2,A_VileChase,S_VILE_RUN9,0,0}, // S_VILE_RUN8 + {SPR_VILE,4,2,A_VileChase,S_VILE_RUN10,0,0}, // S_VILE_RUN9 + {SPR_VILE,4,2,A_VileChase,S_VILE_RUN11,0,0}, // S_VILE_RUN10 + {SPR_VILE,5,2,A_VileChase,S_VILE_RUN12,0,0}, // S_VILE_RUN11 + {SPR_VILE,5,2,A_VileChase,S_VILE_RUN1,0,0}, // S_VILE_RUN12 + {SPR_VILE,32774,0,A_VileStart,S_VILE_ATK2,0,0}, // S_VILE_ATK1 + {SPR_VILE,32774,10,A_FaceTarget,S_VILE_ATK3,0,0}, // S_VILE_ATK2 + {SPR_VILE,32775,8,A_VileTarget,S_VILE_ATK4,0,0}, // S_VILE_ATK3 + {SPR_VILE,32776,8,A_FaceTarget,S_VILE_ATK5,0,0}, // S_VILE_ATK4 + {SPR_VILE,32777,8,A_FaceTarget,S_VILE_ATK6,0,0}, // S_VILE_ATK5 + {SPR_VILE,32778,8,A_FaceTarget,S_VILE_ATK7,0,0}, // S_VILE_ATK6 + {SPR_VILE,32779,8,A_FaceTarget,S_VILE_ATK8,0,0}, // S_VILE_ATK7 + {SPR_VILE,32780,8,A_FaceTarget,S_VILE_ATK9,0,0}, // S_VILE_ATK8 + {SPR_VILE,32781,8,A_FaceTarget,S_VILE_ATK10,0,0}, // S_VILE_ATK9 + {SPR_VILE,32782,8,A_VileAttack,S_VILE_ATK11,0,0}, // S_VILE_ATK10 + {SPR_VILE,32783,20,NULL,S_VILE_RUN1,0,0}, // S_VILE_ATK11 + {SPR_VILE,32794,10,NULL,S_VILE_HEAL2,0,0}, // S_VILE_HEAL1 + {SPR_VILE,32795,10,NULL,S_VILE_HEAL3,0,0}, // S_VILE_HEAL2 + {SPR_VILE,32796,10,NULL,S_VILE_RUN1,0,0}, // S_VILE_HEAL3 + {SPR_VILE,16,5,NULL,S_VILE_PAIN2,0,0}, // S_VILE_PAIN + {SPR_VILE,16,5,A_Pain,S_VILE_RUN1,0,0}, // S_VILE_PAIN2 + {SPR_VILE,16,7,NULL,S_VILE_DIE2,0,0}, // S_VILE_DIE1 + {SPR_VILE,17,7,A_Scream,S_VILE_DIE3,0,0}, // S_VILE_DIE2 + {SPR_VILE,18,7,A_Fall,S_VILE_DIE4,0,0}, // S_VILE_DIE3 + {SPR_VILE,19,7,NULL,S_VILE_DIE5,0,0}, // S_VILE_DIE4 + {SPR_VILE,20,7,NULL,S_VILE_DIE6,0,0}, // S_VILE_DIE5 + {SPR_VILE,21,7,NULL,S_VILE_DIE7,0,0}, // S_VILE_DIE6 + {SPR_VILE,22,7,NULL,S_VILE_DIE8,0,0}, // S_VILE_DIE7 + {SPR_VILE,23,5,NULL,S_VILE_DIE9,0,0}, // S_VILE_DIE8 + {SPR_VILE,24,5,NULL,S_VILE_DIE10,0,0}, // S_VILE_DIE9 + {SPR_VILE,25,-1,NULL,S_NULL,0,0}, // S_VILE_DIE10 + {SPR_FIRE,32768,2,A_StartFire,S_FIRE2,0,0}, // S_FIRE1 + {SPR_FIRE,32769,2,A_Fire,S_FIRE3,0,0}, // S_FIRE2 + {SPR_FIRE,32768,2,A_Fire,S_FIRE4,0,0}, // S_FIRE3 + {SPR_FIRE,32769,2,A_Fire,S_FIRE5,0,0}, // S_FIRE4 + {SPR_FIRE,32770,2,A_FireCrackle,S_FIRE6,0,0}, // S_FIRE5 + {SPR_FIRE,32769,2,A_Fire,S_FIRE7,0,0}, // S_FIRE6 + {SPR_FIRE,32770,2,A_Fire,S_FIRE8,0,0}, // S_FIRE7 + {SPR_FIRE,32769,2,A_Fire,S_FIRE9,0,0}, // S_FIRE8 + {SPR_FIRE,32770,2,A_Fire,S_FIRE10,0,0}, // S_FIRE9 + {SPR_FIRE,32771,2,A_Fire,S_FIRE11,0,0}, // S_FIRE10 + {SPR_FIRE,32770,2,A_Fire,S_FIRE12,0,0}, // S_FIRE11 + {SPR_FIRE,32771,2,A_Fire,S_FIRE13,0,0}, // S_FIRE12 + {SPR_FIRE,32770,2,A_Fire,S_FIRE14,0,0}, // S_FIRE13 + {SPR_FIRE,32771,2,A_Fire,S_FIRE15,0,0}, // S_FIRE14 + {SPR_FIRE,32772,2,A_Fire,S_FIRE16,0,0}, // S_FIRE15 + {SPR_FIRE,32771,2,A_Fire,S_FIRE17,0,0}, // S_FIRE16 + {SPR_FIRE,32772,2,A_Fire,S_FIRE18,0,0}, // S_FIRE17 + {SPR_FIRE,32771,2,A_Fire,S_FIRE19,0,0}, // S_FIRE18 + {SPR_FIRE,32772,2,A_FireCrackle,S_FIRE20,0,0}, // S_FIRE19 + {SPR_FIRE,32773,2,A_Fire,S_FIRE21,0,0}, // S_FIRE20 + {SPR_FIRE,32772,2,A_Fire,S_FIRE22,0,0}, // S_FIRE21 + {SPR_FIRE,32773,2,A_Fire,S_FIRE23,0,0}, // S_FIRE22 + {SPR_FIRE,32772,2,A_Fire,S_FIRE24,0,0}, // S_FIRE23 + {SPR_FIRE,32773,2,A_Fire,S_FIRE25,0,0}, // S_FIRE24 + {SPR_FIRE,32774,2,A_Fire,S_FIRE26,0,0}, // S_FIRE25 + {SPR_FIRE,32775,2,A_Fire,S_FIRE27,0,0}, // S_FIRE26 + {SPR_FIRE,32774,2,A_Fire,S_FIRE28,0,0}, // S_FIRE27 + {SPR_FIRE,32775,2,A_Fire,S_FIRE29,0,0}, // S_FIRE28 + {SPR_FIRE,32774,2,A_Fire,S_FIRE30,0,0}, // S_FIRE29 + {SPR_FIRE,32775,2,A_Fire,S_NULL,0,0}, // S_FIRE30 + {SPR_PUFF,1,4,NULL,S_SMOKE2,0,0}, // S_SMOKE1 + {SPR_PUFF,2,4,NULL,S_SMOKE3,0,0}, // S_SMOKE2 + {SPR_PUFF,1,4,NULL,S_SMOKE4,0,0}, // S_SMOKE3 + {SPR_PUFF,2,4,NULL,S_SMOKE5,0,0}, // S_SMOKE4 + {SPR_PUFF,3,4,NULL,S_NULL,0,0}, // S_SMOKE5 + {SPR_FATB,32768,2,A_Tracer,S_TRACER2,0,0}, // S_TRACER + {SPR_FATB,32769,2,A_Tracer,S_TRACER,0,0}, // S_TRACER2 + {SPR_FBXP,32768,8,NULL,S_TRACEEXP2,0,0}, // S_TRACEEXP1 + {SPR_FBXP,32769,6,NULL,S_TRACEEXP3,0,0}, // S_TRACEEXP2 + {SPR_FBXP,32770,4,NULL,S_NULL,0,0}, // S_TRACEEXP3 + {SPR_SKEL,0,10,A_Look,S_SKEL_STND2,0,0}, // S_SKEL_STND + {SPR_SKEL,1,10,A_Look,S_SKEL_STND,0,0}, // S_SKEL_STND2 + {SPR_SKEL,0,2,A_Chase,S_SKEL_RUN2,0,0}, // S_SKEL_RUN1 + {SPR_SKEL,0,2,A_Chase,S_SKEL_RUN3,0,0}, // S_SKEL_RUN2 + {SPR_SKEL,1,2,A_Chase,S_SKEL_RUN4,0,0}, // S_SKEL_RUN3 + {SPR_SKEL,1,2,A_Chase,S_SKEL_RUN5,0,0}, // S_SKEL_RUN4 + {SPR_SKEL,2,2,A_Chase,S_SKEL_RUN6,0,0}, // S_SKEL_RUN5 + {SPR_SKEL,2,2,A_Chase,S_SKEL_RUN7,0,0}, // S_SKEL_RUN6 + {SPR_SKEL,3,2,A_Chase,S_SKEL_RUN8,0,0}, // S_SKEL_RUN7 + {SPR_SKEL,3,2,A_Chase,S_SKEL_RUN9,0,0}, // S_SKEL_RUN8 + {SPR_SKEL,4,2,A_Chase,S_SKEL_RUN10,0,0}, // S_SKEL_RUN9 + {SPR_SKEL,4,2,A_Chase,S_SKEL_RUN11,0,0}, // S_SKEL_RUN10 + {SPR_SKEL,5,2,A_Chase,S_SKEL_RUN12,0,0}, // S_SKEL_RUN11 + {SPR_SKEL,5,2,A_Chase,S_SKEL_RUN1,0,0}, // S_SKEL_RUN12 + {SPR_SKEL,6,0,A_FaceTarget,S_SKEL_FIST2,0,0}, // S_SKEL_FIST1 + {SPR_SKEL,6,6,A_SkelWhoosh,S_SKEL_FIST3,0,0}, // S_SKEL_FIST2 + {SPR_SKEL,7,6,A_FaceTarget,S_SKEL_FIST4,0,0}, // S_SKEL_FIST3 + {SPR_SKEL,8,6,A_SkelFist,S_SKEL_RUN1,0,0}, // S_SKEL_FIST4 + {SPR_SKEL,32777,0,A_FaceTarget,S_SKEL_MISS2,0,0}, // S_SKEL_MISS1 + {SPR_SKEL,32777,10,A_FaceTarget,S_SKEL_MISS3,0,0}, // S_SKEL_MISS2 + {SPR_SKEL,10,10,A_SkelMissile,S_SKEL_MISS4,0,0}, // S_SKEL_MISS3 + {SPR_SKEL,10,10,A_FaceTarget,S_SKEL_RUN1,0,0}, // S_SKEL_MISS4 + {SPR_SKEL,11,5,NULL,S_SKEL_PAIN2,0,0}, // S_SKEL_PAIN + {SPR_SKEL,11,5,A_Pain,S_SKEL_RUN1,0,0}, // S_SKEL_PAIN2 + {SPR_SKEL,11,7,NULL,S_SKEL_DIE2,0,0}, // S_SKEL_DIE1 + {SPR_SKEL,12,7,NULL,S_SKEL_DIE3,0,0}, // S_SKEL_DIE2 + {SPR_SKEL,13,7,A_Scream,S_SKEL_DIE4,0,0}, // S_SKEL_DIE3 + {SPR_SKEL,14,7,A_Fall,S_SKEL_DIE5,0,0}, // S_SKEL_DIE4 + {SPR_SKEL,15,7,NULL,S_SKEL_DIE6,0,0}, // S_SKEL_DIE5 + {SPR_SKEL,16,-1,NULL,S_NULL,0,0}, // S_SKEL_DIE6 + {SPR_SKEL,16,5,NULL,S_SKEL_RAISE2,0,0}, // S_SKEL_RAISE1 + {SPR_SKEL,15,5,NULL,S_SKEL_RAISE3,0,0}, // S_SKEL_RAISE2 + {SPR_SKEL,14,5,NULL,S_SKEL_RAISE4,0,0}, // S_SKEL_RAISE3 + {SPR_SKEL,13,5,NULL,S_SKEL_RAISE5,0,0}, // S_SKEL_RAISE4 + {SPR_SKEL,12,5,NULL,S_SKEL_RAISE6,0,0}, // S_SKEL_RAISE5 + {SPR_SKEL,11,5,NULL,S_SKEL_RUN1,0,0}, // S_SKEL_RAISE6 + {SPR_MANF,32768,4,NULL,S_FATSHOT2,0,0}, // S_FATSHOT1 + {SPR_MANF,32769,4,NULL,S_FATSHOT1,0,0}, // S_FATSHOT2 + {SPR_MISL,32769,8,NULL,S_FATSHOTX2,0,0}, // S_FATSHOTX1 + {SPR_MISL,32770,6,NULL,S_FATSHOTX3,0,0}, // S_FATSHOTX2 + {SPR_MISL,32771,4,NULL,S_NULL,0,0}, // S_FATSHOTX3 + {SPR_FATT,0,15,A_Look,S_FATT_STND2,0,0}, // S_FATT_STND + {SPR_FATT,1,15,A_Look,S_FATT_STND,0,0}, // S_FATT_STND2 + {SPR_FATT,0,4,A_Chase,S_FATT_RUN2,0,0}, // S_FATT_RUN1 + {SPR_FATT,0,4,A_Chase,S_FATT_RUN3,0,0}, // S_FATT_RUN2 + {SPR_FATT,1,4,A_Chase,S_FATT_RUN4,0,0}, // S_FATT_RUN3 + {SPR_FATT,1,4,A_Chase,S_FATT_RUN5,0,0}, // S_FATT_RUN4 + {SPR_FATT,2,4,A_Chase,S_FATT_RUN6,0,0}, // S_FATT_RUN5 + {SPR_FATT,2,4,A_Chase,S_FATT_RUN7,0,0}, // S_FATT_RUN6 + {SPR_FATT,3,4,A_Chase,S_FATT_RUN8,0,0}, // S_FATT_RUN7 + {SPR_FATT,3,4,A_Chase,S_FATT_RUN9,0,0}, // S_FATT_RUN8 + {SPR_FATT,4,4,A_Chase,S_FATT_RUN10,0,0}, // S_FATT_RUN9 + {SPR_FATT,4,4,A_Chase,S_FATT_RUN11,0,0}, // S_FATT_RUN10 + {SPR_FATT,5,4,A_Chase,S_FATT_RUN12,0,0}, // S_FATT_RUN11 + {SPR_FATT,5,4,A_Chase,S_FATT_RUN1,0,0}, // S_FATT_RUN12 + {SPR_FATT,6,20,A_FatRaise,S_FATT_ATK2,0,0}, // S_FATT_ATK1 + {SPR_FATT,32775,10,A_FatAttack1,S_FATT_ATK3,0,0}, // S_FATT_ATK2 + {SPR_FATT,8,5,A_FaceTarget,S_FATT_ATK4,0,0}, // S_FATT_ATK3 + {SPR_FATT,6,5,A_FaceTarget,S_FATT_ATK5,0,0}, // S_FATT_ATK4 + {SPR_FATT,32775,10,A_FatAttack2,S_FATT_ATK6,0,0}, // S_FATT_ATK5 + {SPR_FATT,8,5,A_FaceTarget,S_FATT_ATK7,0,0}, // S_FATT_ATK6 + {SPR_FATT,6,5,A_FaceTarget,S_FATT_ATK8,0,0}, // S_FATT_ATK7 + {SPR_FATT,32775,10,A_FatAttack3,S_FATT_ATK9,0,0}, // S_FATT_ATK8 + {SPR_FATT,8,5,A_FaceTarget,S_FATT_ATK10,0,0}, // S_FATT_ATK9 + {SPR_FATT,6,5,A_FaceTarget,S_FATT_RUN1,0,0}, // S_FATT_ATK10 + {SPR_FATT,9,3,NULL,S_FATT_PAIN2,0,0}, // S_FATT_PAIN + {SPR_FATT,9,3,A_Pain,S_FATT_RUN1,0,0}, // S_FATT_PAIN2 + {SPR_FATT,10,6,NULL,S_FATT_DIE2,0,0}, // S_FATT_DIE1 + {SPR_FATT,11,6,A_Scream,S_FATT_DIE3,0,0}, // S_FATT_DIE2 + {SPR_FATT,12,6,A_Fall,S_FATT_DIE4,0,0}, // S_FATT_DIE3 + {SPR_FATT,13,6,NULL,S_FATT_DIE5,0,0}, // S_FATT_DIE4 + {SPR_FATT,14,6,NULL,S_FATT_DIE6,0,0}, // S_FATT_DIE5 + {SPR_FATT,15,6,NULL,S_FATT_DIE7,0,0}, // S_FATT_DIE6 + {SPR_FATT,16,6,NULL,S_FATT_DIE8,0,0}, // S_FATT_DIE7 + {SPR_FATT,17,6,NULL,S_FATT_DIE9,0,0}, // S_FATT_DIE8 + {SPR_FATT,18,6,NULL,S_FATT_DIE10,0,0}, // S_FATT_DIE9 + {SPR_FATT,19,-1,A_BossDeath,S_NULL,0,0}, // S_FATT_DIE10 + {SPR_FATT,17,5,NULL,S_FATT_RAISE2,0,0}, // S_FATT_RAISE1 + {SPR_FATT,16,5,NULL,S_FATT_RAISE3,0,0}, // S_FATT_RAISE2 + {SPR_FATT,15,5,NULL,S_FATT_RAISE4,0,0}, // S_FATT_RAISE3 + {SPR_FATT,14,5,NULL,S_FATT_RAISE5,0,0}, // S_FATT_RAISE4 + {SPR_FATT,13,5,NULL,S_FATT_RAISE6,0,0}, // S_FATT_RAISE5 + {SPR_FATT,12,5,NULL,S_FATT_RAISE7,0,0}, // S_FATT_RAISE6 + {SPR_FATT,11,5,NULL,S_FATT_RAISE8,0,0}, // S_FATT_RAISE7 + {SPR_FATT,10,5,NULL,S_FATT_RUN1,0,0}, // S_FATT_RAISE8 + {SPR_CPOS,0,10,A_Look,S_CPOS_STND2,0,0}, // S_CPOS_STND + {SPR_CPOS,1,10,A_Look,S_CPOS_STND,0,0}, // S_CPOS_STND2 + {SPR_CPOS,0,3,A_Chase,S_CPOS_RUN2,0,0}, // S_CPOS_RUN1 + {SPR_CPOS,0,3,A_Chase,S_CPOS_RUN3,0,0}, // S_CPOS_RUN2 + {SPR_CPOS,1,3,A_Chase,S_CPOS_RUN4,0,0}, // S_CPOS_RUN3 + {SPR_CPOS,1,3,A_Chase,S_CPOS_RUN5,0,0}, // S_CPOS_RUN4 + {SPR_CPOS,2,3,A_Chase,S_CPOS_RUN6,0,0}, // S_CPOS_RUN5 + {SPR_CPOS,2,3,A_Chase,S_CPOS_RUN7,0,0}, // S_CPOS_RUN6 + {SPR_CPOS,3,3,A_Chase,S_CPOS_RUN8,0,0}, // S_CPOS_RUN7 + {SPR_CPOS,3,3,A_Chase,S_CPOS_RUN1,0,0}, // S_CPOS_RUN8 + {SPR_CPOS,4,10,A_FaceTarget,S_CPOS_ATK2,0,0}, // S_CPOS_ATK1 + {SPR_CPOS,32773,4,A_CPosAttack,S_CPOS_ATK3,0,0}, // S_CPOS_ATK2 + {SPR_CPOS,32772,4,A_CPosAttack,S_CPOS_ATK4,0,0}, // S_CPOS_ATK3 + {SPR_CPOS,5,1,A_CPosRefire,S_CPOS_ATK2,0,0}, // S_CPOS_ATK4 + {SPR_CPOS,6,3,NULL,S_CPOS_PAIN2,0,0}, // S_CPOS_PAIN + {SPR_CPOS,6,3,A_Pain,S_CPOS_RUN1,0,0}, // S_CPOS_PAIN2 + {SPR_CPOS,7,5,NULL,S_CPOS_DIE2,0,0}, // S_CPOS_DIE1 + {SPR_CPOS,8,5,A_Scream,S_CPOS_DIE3,0,0}, // S_CPOS_DIE2 + {SPR_CPOS,9,5,A_Fall,S_CPOS_DIE4,0,0}, // S_CPOS_DIE3 + {SPR_CPOS,10,5,NULL,S_CPOS_DIE5,0,0}, // S_CPOS_DIE4 + {SPR_CPOS,11,5,NULL,S_CPOS_DIE6,0,0}, // S_CPOS_DIE5 + {SPR_CPOS,12,5,NULL,S_CPOS_DIE7,0,0}, // S_CPOS_DIE6 + {SPR_CPOS,13,-1,NULL,S_NULL,0,0}, // S_CPOS_DIE7 + {SPR_CPOS,14,5,NULL,S_CPOS_XDIE2,0,0}, // S_CPOS_XDIE1 + {SPR_CPOS,15,5,A_XScream,S_CPOS_XDIE3,0,0}, // S_CPOS_XDIE2 + {SPR_CPOS,16,5,A_Fall,S_CPOS_XDIE4,0,0}, // S_CPOS_XDIE3 + {SPR_CPOS,17,5,NULL,S_CPOS_XDIE5,0,0}, // S_CPOS_XDIE4 + {SPR_CPOS,18,5,NULL,S_CPOS_XDIE6,0,0}, // S_CPOS_XDIE5 + {SPR_CPOS,19,-1,NULL,S_NULL,0,0}, // S_CPOS_XDIE6 + {SPR_CPOS,13,5,NULL,S_CPOS_RAISE2,0,0}, // S_CPOS_RAISE1 + {SPR_CPOS,12,5,NULL,S_CPOS_RAISE3,0,0}, // S_CPOS_RAISE2 + {SPR_CPOS,11,5,NULL,S_CPOS_RAISE4,0,0}, // S_CPOS_RAISE3 + {SPR_CPOS,10,5,NULL,S_CPOS_RAISE5,0,0}, // S_CPOS_RAISE4 + {SPR_CPOS,9,5,NULL,S_CPOS_RAISE6,0,0}, // S_CPOS_RAISE5 + {SPR_CPOS,8,5,NULL,S_CPOS_RAISE7,0,0}, // S_CPOS_RAISE6 + {SPR_CPOS,7,5,NULL,S_CPOS_RUN1,0,0}, // S_CPOS_RAISE7 + {SPR_TROO,0,10,A_Look,S_TROO_STND2,0,0}, // S_TROO_STND + {SPR_TROO,1,10,A_Look,S_TROO_STND,0,0}, // S_TROO_STND2 + {SPR_TROO,0,3,A_Chase,S_TROO_RUN2,0,0}, // S_TROO_RUN1 + {SPR_TROO,0,3,A_Chase,S_TROO_RUN3,0,0}, // S_TROO_RUN2 + {SPR_TROO,1,3,A_Chase,S_TROO_RUN4,0,0}, // S_TROO_RUN3 + {SPR_TROO,1,3,A_Chase,S_TROO_RUN5,0,0}, // S_TROO_RUN4 + {SPR_TROO,2,3,A_Chase,S_TROO_RUN6,0,0}, // S_TROO_RUN5 + {SPR_TROO,2,3,A_Chase,S_TROO_RUN7,0,0}, // S_TROO_RUN6 + {SPR_TROO,3,3,A_Chase,S_TROO_RUN8,0,0}, // S_TROO_RUN7 + {SPR_TROO,3,3,A_Chase,S_TROO_RUN1,0,0}, // S_TROO_RUN8 + {SPR_TROO,4,8,A_FaceTarget,S_TROO_ATK2,0,0}, // S_TROO_ATK1 + {SPR_TROO,5,8,A_FaceTarget,S_TROO_ATK3,0,0}, // S_TROO_ATK2 + {SPR_TROO,6,6,A_TroopAttack,S_TROO_RUN1,0,0}, // S_TROO_ATK3 + {SPR_TROO,7,2,NULL,S_TROO_PAIN2,0,0}, // S_TROO_PAIN + {SPR_TROO,7,2,A_Pain,S_TROO_RUN1,0,0}, // S_TROO_PAIN2 + {SPR_TROO,8,8,NULL,S_TROO_DIE2,0,0}, // S_TROO_DIE1 + {SPR_TROO,9,8,A_Scream,S_TROO_DIE3,0,0}, // S_TROO_DIE2 + {SPR_TROO,10,6,NULL,S_TROO_DIE4,0,0}, // S_TROO_DIE3 + {SPR_TROO,11,6,A_Fall,S_TROO_DIE5,0,0}, // S_TROO_DIE4 + {SPR_TROO,12,-1,NULL,S_NULL,0,0}, // S_TROO_DIE5 + {SPR_TROO,13,5,NULL,S_TROO_XDIE2,0,0}, // S_TROO_XDIE1 + {SPR_TROO,14,5,A_XScream,S_TROO_XDIE3,0,0}, // S_TROO_XDIE2 + {SPR_TROO,15,5,NULL,S_TROO_XDIE4,0,0}, // S_TROO_XDIE3 + {SPR_TROO,16,5,A_Fall,S_TROO_XDIE5,0,0}, // S_TROO_XDIE4 + {SPR_TROO,17,5,NULL,S_TROO_XDIE6,0,0}, // S_TROO_XDIE5 + {SPR_TROO,18,5,NULL,S_TROO_XDIE7,0,0}, // S_TROO_XDIE6 + {SPR_TROO,19,5,NULL,S_TROO_XDIE8,0,0}, // S_TROO_XDIE7 + {SPR_TROO,20,-1,NULL,S_NULL,0,0}, // S_TROO_XDIE8 + {SPR_TROO,12,8,NULL,S_TROO_RAISE2,0,0}, // S_TROO_RAISE1 + {SPR_TROO,11,8,NULL,S_TROO_RAISE3,0,0}, // S_TROO_RAISE2 + {SPR_TROO,10,6,NULL,S_TROO_RAISE4,0,0}, // S_TROO_RAISE3 + {SPR_TROO,9,6,NULL,S_TROO_RAISE5,0,0}, // S_TROO_RAISE4 + {SPR_TROO,8,6,NULL,S_TROO_RUN1,0,0}, // S_TROO_RAISE5 + {SPR_SARG,0,10,A_Look,S_SARG_STND2,0,0}, // S_SARG_STND + {SPR_SARG,1,10,A_Look,S_SARG_STND,0,0}, // S_SARG_STND2 + {SPR_SARG,0,2,A_Chase,S_SARG_RUN2,0,0}, // S_SARG_RUN1 + {SPR_SARG,0,2,A_Chase,S_SARG_RUN3,0,0}, // S_SARG_RUN2 + {SPR_SARG,1,2,A_Chase,S_SARG_RUN4,0,0}, // S_SARG_RUN3 + {SPR_SARG,1,2,A_Chase,S_SARG_RUN5,0,0}, // S_SARG_RUN4 + {SPR_SARG,2,2,A_Chase,S_SARG_RUN6,0,0}, // S_SARG_RUN5 + {SPR_SARG,2,2,A_Chase,S_SARG_RUN7,0,0}, // S_SARG_RUN6 + {SPR_SARG,3,2,A_Chase,S_SARG_RUN8,0,0}, // S_SARG_RUN7 + {SPR_SARG,3,2,A_Chase,S_SARG_RUN1,0,0}, // S_SARG_RUN8 + {SPR_SARG,4,8,A_FaceTarget,S_SARG_ATK2,0,0}, // S_SARG_ATK1 + {SPR_SARG,5,8,A_FaceTarget,S_SARG_ATK3,0,0}, // S_SARG_ATK2 + {SPR_SARG,6,8,A_SargAttack,S_SARG_RUN1,0,0}, // S_SARG_ATK3 + {SPR_SARG,7,2,NULL,S_SARG_PAIN2,0,0}, // S_SARG_PAIN + {SPR_SARG,7,2,A_Pain,S_SARG_RUN1,0,0}, // S_SARG_PAIN2 + {SPR_SARG,8,8,NULL,S_SARG_DIE2,0,0}, // S_SARG_DIE1 + {SPR_SARG,9,8,A_Scream,S_SARG_DIE3,0,0}, // S_SARG_DIE2 + {SPR_SARG,10,4,NULL,S_SARG_DIE4,0,0}, // S_SARG_DIE3 + {SPR_SARG,11,4,A_Fall,S_SARG_DIE5,0,0}, // S_SARG_DIE4 + {SPR_SARG,12,4,NULL,S_SARG_DIE6,0,0}, // S_SARG_DIE5 + {SPR_SARG,13,-1,NULL,S_NULL,0,0}, // S_SARG_DIE6 + {SPR_SARG,13,5,NULL,S_SARG_RAISE2,0,0}, // S_SARG_RAISE1 + {SPR_SARG,12,5,NULL,S_SARG_RAISE3,0,0}, // S_SARG_RAISE2 + {SPR_SARG,11,5,NULL,S_SARG_RAISE4,0,0}, // S_SARG_RAISE3 + {SPR_SARG,10,5,NULL,S_SARG_RAISE5,0,0}, // S_SARG_RAISE4 + {SPR_SARG,9,5,NULL,S_SARG_RAISE6,0,0}, // S_SARG_RAISE5 + {SPR_SARG,8,5,NULL,S_SARG_RUN1,0,0}, // S_SARG_RAISE6 + {SPR_HEAD,0,10,A_Look,S_HEAD_STND,0,0}, // S_HEAD_STND + {SPR_HEAD,0,3,A_Chase,S_HEAD_RUN1,0,0}, // S_HEAD_RUN1 + {SPR_HEAD,1,5,A_FaceTarget,S_HEAD_ATK2,0,0}, // S_HEAD_ATK1 + {SPR_HEAD,2,5,A_FaceTarget,S_HEAD_ATK3,0,0}, // S_HEAD_ATK2 + {SPR_HEAD,32771,5,A_HeadAttack,S_HEAD_RUN1,0,0}, // S_HEAD_ATK3 + {SPR_HEAD,4,3,NULL,S_HEAD_PAIN2,0,0}, // S_HEAD_PAIN + {SPR_HEAD,4,3,A_Pain,S_HEAD_PAIN3,0,0}, // S_HEAD_PAIN2 + {SPR_HEAD,5,6,NULL,S_HEAD_RUN1,0,0}, // S_HEAD_PAIN3 + {SPR_HEAD,6,8,NULL,S_HEAD_DIE2,0,0}, // S_HEAD_DIE1 + {SPR_HEAD,7,8,A_Scream,S_HEAD_DIE3,0,0}, // S_HEAD_DIE2 + {SPR_HEAD,8,8,NULL,S_HEAD_DIE4,0,0}, // S_HEAD_DIE3 + {SPR_HEAD,9,8,NULL,S_HEAD_DIE5,0,0}, // S_HEAD_DIE4 + {SPR_HEAD,10,8,A_Fall,S_HEAD_DIE6,0,0}, // S_HEAD_DIE5 + {SPR_HEAD,11,-1,NULL,S_NULL,0,0}, // S_HEAD_DIE6 + {SPR_HEAD,11,8,NULL,S_HEAD_RAISE2,0,0}, // S_HEAD_RAISE1 + {SPR_HEAD,10,8,NULL,S_HEAD_RAISE3,0,0}, // S_HEAD_RAISE2 + {SPR_HEAD,9,8,NULL,S_HEAD_RAISE4,0,0}, // S_HEAD_RAISE3 + {SPR_HEAD,8,8,NULL,S_HEAD_RAISE5,0,0}, // S_HEAD_RAISE4 + {SPR_HEAD,7,8,NULL,S_HEAD_RAISE6,0,0}, // S_HEAD_RAISE5 + {SPR_HEAD,6,8,NULL,S_HEAD_RUN1,0,0}, // S_HEAD_RAISE6 + {SPR_BAL7,32768,4,NULL,S_BRBALL2,0,0}, // S_BRBALL1 + {SPR_BAL7,32769,4,NULL,S_BRBALL1,0,0}, // S_BRBALL2 + {SPR_BAL7,32770,6,NULL,S_BRBALLX2,0,0}, // S_BRBALLX1 + {SPR_BAL7,32771,6,NULL,S_BRBALLX3,0,0}, // S_BRBALLX2 + {SPR_BAL7,32772,6,NULL,S_NULL,0,0}, // S_BRBALLX3 + {SPR_BOSS,0,10,A_Look,S_BOSS_STND2,0,0}, // S_BOSS_STND + {SPR_BOSS,1,10,A_Look,S_BOSS_STND,0,0}, // S_BOSS_STND2 + {SPR_BOSS,0,3,A_Chase,S_BOSS_RUN2,0,0}, // S_BOSS_RUN1 + {SPR_BOSS,0,3,A_Chase,S_BOSS_RUN3,0,0}, // S_BOSS_RUN2 + {SPR_BOSS,1,3,A_Chase,S_BOSS_RUN4,0,0}, // S_BOSS_RUN3 + {SPR_BOSS,1,3,A_Chase,S_BOSS_RUN5,0,0}, // S_BOSS_RUN4 + {SPR_BOSS,2,3,A_Chase,S_BOSS_RUN6,0,0}, // S_BOSS_RUN5 + {SPR_BOSS,2,3,A_Chase,S_BOSS_RUN7,0,0}, // S_BOSS_RUN6 + {SPR_BOSS,3,3,A_Chase,S_BOSS_RUN8,0,0}, // S_BOSS_RUN7 + {SPR_BOSS,3,3,A_Chase,S_BOSS_RUN1,0,0}, // S_BOSS_RUN8 + {SPR_BOSS,4,8,A_FaceTarget,S_BOSS_ATK2,0,0}, // S_BOSS_ATK1 + {SPR_BOSS,5,8,A_FaceTarget,S_BOSS_ATK3,0,0}, // S_BOSS_ATK2 + {SPR_BOSS,6,8,A_BruisAttack,S_BOSS_RUN1,0,0}, // S_BOSS_ATK3 + {SPR_BOSS,7,2,NULL,S_BOSS_PAIN2,0,0}, // S_BOSS_PAIN + {SPR_BOSS,7,2,A_Pain,S_BOSS_RUN1,0,0}, // S_BOSS_PAIN2 + {SPR_BOSS,8,8,NULL,S_BOSS_DIE2,0,0}, // S_BOSS_DIE1 + {SPR_BOSS,9,8,A_Scream,S_BOSS_DIE3,0,0}, // S_BOSS_DIE2 + {SPR_BOSS,10,8,NULL,S_BOSS_DIE4,0,0}, // S_BOSS_DIE3 + {SPR_BOSS,11,8,A_Fall,S_BOSS_DIE5,0,0}, // S_BOSS_DIE4 + {SPR_BOSS,12,8,NULL,S_BOSS_DIE6,0,0}, // S_BOSS_DIE5 + {SPR_BOSS,13,8,NULL,S_BOSS_DIE7,0,0}, // S_BOSS_DIE6 + {SPR_BOSS,14,-1,A_BossDeath,S_NULL,0,0}, // S_BOSS_DIE7 + {SPR_BOSS,14,8,NULL,S_BOSS_RAISE2,0,0}, // S_BOSS_RAISE1 + {SPR_BOSS,13,8,NULL,S_BOSS_RAISE3,0,0}, // S_BOSS_RAISE2 + {SPR_BOSS,12,8,NULL,S_BOSS_RAISE4,0,0}, // S_BOSS_RAISE3 + {SPR_BOSS,11,8,NULL,S_BOSS_RAISE5,0,0}, // S_BOSS_RAISE4 + {SPR_BOSS,10,8,NULL,S_BOSS_RAISE6,0,0}, // S_BOSS_RAISE5 + {SPR_BOSS,9,8,NULL,S_BOSS_RAISE7,0,0}, // S_BOSS_RAISE6 + {SPR_BOSS,8,8,NULL,S_BOSS_RUN1,0,0}, // S_BOSS_RAISE7 + {SPR_BOS2,0,10,A_Look,S_BOS2_STND2,0,0}, // S_BOS2_STND + {SPR_BOS2,1,10,A_Look,S_BOS2_STND,0,0}, // S_BOS2_STND2 + {SPR_BOS2,0,3,A_Chase,S_BOS2_RUN2,0,0}, // S_BOS2_RUN1 + {SPR_BOS2,0,3,A_Chase,S_BOS2_RUN3,0,0}, // S_BOS2_RUN2 + {SPR_BOS2,1,3,A_Chase,S_BOS2_RUN4,0,0}, // S_BOS2_RUN3 + {SPR_BOS2,1,3,A_Chase,S_BOS2_RUN5,0,0}, // S_BOS2_RUN4 + {SPR_BOS2,2,3,A_Chase,S_BOS2_RUN6,0,0}, // S_BOS2_RUN5 + {SPR_BOS2,2,3,A_Chase,S_BOS2_RUN7,0,0}, // S_BOS2_RUN6 + {SPR_BOS2,3,3,A_Chase,S_BOS2_RUN8,0,0}, // S_BOS2_RUN7 + {SPR_BOS2,3,3,A_Chase,S_BOS2_RUN1,0,0}, // S_BOS2_RUN8 + {SPR_BOS2,4,8,A_FaceTarget,S_BOS2_ATK2,0,0}, // S_BOS2_ATK1 + {SPR_BOS2,5,8,A_FaceTarget,S_BOS2_ATK3,0,0}, // S_BOS2_ATK2 + {SPR_BOS2,6,8,A_BruisAttack,S_BOS2_RUN1,0,0}, // S_BOS2_ATK3 + {SPR_BOS2,7,2,NULL,S_BOS2_PAIN2,0,0}, // S_BOS2_PAIN + {SPR_BOS2,7,2,A_Pain,S_BOS2_RUN1,0,0}, // S_BOS2_PAIN2 + {SPR_BOS2,8,8,NULL,S_BOS2_DIE2,0,0}, // S_BOS2_DIE1 + {SPR_BOS2,9,8,A_Scream,S_BOS2_DIE3,0,0}, // S_BOS2_DIE2 + {SPR_BOS2,10,8,NULL,S_BOS2_DIE4,0,0}, // S_BOS2_DIE3 + {SPR_BOS2,11,8,A_Fall,S_BOS2_DIE5,0,0}, // S_BOS2_DIE4 + {SPR_BOS2,12,8,NULL,S_BOS2_DIE6,0,0}, // S_BOS2_DIE5 + {SPR_BOS2,13,8,NULL,S_BOS2_DIE7,0,0}, // S_BOS2_DIE6 + {SPR_BOS2,14,-1,NULL,S_NULL,0,0}, // S_BOS2_DIE7 + {SPR_BOS2,14,8,NULL,S_BOS2_RAISE2,0,0}, // S_BOS2_RAISE1 + {SPR_BOS2,13,8,NULL,S_BOS2_RAISE3,0,0}, // S_BOS2_RAISE2 + {SPR_BOS2,12,8,NULL,S_BOS2_RAISE4,0,0}, // S_BOS2_RAISE3 + {SPR_BOS2,11,8,NULL,S_BOS2_RAISE5,0,0}, // S_BOS2_RAISE4 + {SPR_BOS2,10,8,NULL,S_BOS2_RAISE6,0,0}, // S_BOS2_RAISE5 + {SPR_BOS2,9,8,NULL,S_BOS2_RAISE7,0,0}, // S_BOS2_RAISE6 + {SPR_BOS2,8,8,NULL,S_BOS2_RUN1,0,0}, // S_BOS2_RAISE7 + {SPR_SKUL,32768,10,A_Look,S_SKULL_STND2,0,0}, // S_SKULL_STND + {SPR_SKUL,32769,10,A_Look,S_SKULL_STND,0,0}, // S_SKULL_STND2 + {SPR_SKUL,32768,6,A_Chase,S_SKULL_RUN2,0,0}, // S_SKULL_RUN1 + {SPR_SKUL,32769,6,A_Chase,S_SKULL_RUN1,0,0}, // S_SKULL_RUN2 + {SPR_SKUL,32770,10,A_FaceTarget,S_SKULL_ATK2,0,0}, // S_SKULL_ATK1 + {SPR_SKUL,32771,4,A_SkullAttack,S_SKULL_ATK3,0,0}, // S_SKULL_ATK2 + {SPR_SKUL,32770,4,NULL,S_SKULL_ATK4,0,0}, // S_SKULL_ATK3 + {SPR_SKUL,32771,4,NULL,S_SKULL_ATK3,0,0}, // S_SKULL_ATK4 + {SPR_SKUL,32772,3,NULL,S_SKULL_PAIN2,0,0}, // S_SKULL_PAIN + {SPR_SKUL,32772,3,A_Pain,S_SKULL_RUN1,0,0}, // S_SKULL_PAIN2 + {SPR_SKUL,32773,6,NULL,S_SKULL_DIE2,0,0}, // S_SKULL_DIE1 + {SPR_SKUL,32774,6,A_Scream,S_SKULL_DIE3,0,0}, // S_SKULL_DIE2 + {SPR_SKUL,32775,6,NULL,S_SKULL_DIE4,0,0}, // S_SKULL_DIE3 + {SPR_SKUL,32776,6,A_Fall,S_SKULL_DIE5,0,0}, // S_SKULL_DIE4 + {SPR_SKUL,9,6,NULL,S_SKULL_DIE6,0,0}, // S_SKULL_DIE5 + {SPR_SKUL,10,6,NULL,S_NULL,0,0}, // S_SKULL_DIE6 + {SPR_SPID,0,10,A_Look,S_SPID_STND2,0,0}, // S_SPID_STND + {SPR_SPID,1,10,A_Look,S_SPID_STND,0,0}, // S_SPID_STND2 + {SPR_SPID,0,3,A_Metal,S_SPID_RUN2,0,0}, // S_SPID_RUN1 + {SPR_SPID,0,3,A_Chase,S_SPID_RUN3,0,0}, // S_SPID_RUN2 + {SPR_SPID,1,3,A_Chase,S_SPID_RUN4,0,0}, // S_SPID_RUN3 + {SPR_SPID,1,3,A_Chase,S_SPID_RUN5,0,0}, // S_SPID_RUN4 + {SPR_SPID,2,3,A_Metal,S_SPID_RUN6,0,0}, // S_SPID_RUN5 + {SPR_SPID,2,3,A_Chase,S_SPID_RUN7,0,0}, // S_SPID_RUN6 + {SPR_SPID,3,3,A_Chase,S_SPID_RUN8,0,0}, // S_SPID_RUN7 + {SPR_SPID,3,3,A_Chase,S_SPID_RUN9,0,0}, // S_SPID_RUN8 + {SPR_SPID,4,3,A_Metal,S_SPID_RUN10,0,0}, // S_SPID_RUN9 + {SPR_SPID,4,3,A_Chase,S_SPID_RUN11,0,0}, // S_SPID_RUN10 + {SPR_SPID,5,3,A_Chase,S_SPID_RUN12,0,0}, // S_SPID_RUN11 + {SPR_SPID,5,3,A_Chase,S_SPID_RUN1,0,0}, // S_SPID_RUN12 + {SPR_SPID,32768,20,A_FaceTarget,S_SPID_ATK2,0,0}, // S_SPID_ATK1 + {SPR_SPID,32774,4,A_SPosAttack,S_SPID_ATK3,0,0}, // S_SPID_ATK2 + {SPR_SPID,32775,4,A_SPosAttack,S_SPID_ATK4,0,0}, // S_SPID_ATK3 + {SPR_SPID,32775,1,A_SpidRefire,S_SPID_ATK2,0,0}, // S_SPID_ATK4 + {SPR_SPID,8,3,NULL,S_SPID_PAIN2,0,0}, // S_SPID_PAIN + {SPR_SPID,8,3,A_Pain,S_SPID_RUN1,0,0}, // S_SPID_PAIN2 + {SPR_SPID,9,20,A_Scream,S_SPID_DIE2,0,0}, // S_SPID_DIE1 + {SPR_SPID,10,10,A_Fall,S_SPID_DIE3,0,0}, // S_SPID_DIE2 + {SPR_SPID,11,10,NULL,S_SPID_DIE4,0,0}, // S_SPID_DIE3 + {SPR_SPID,12,10,NULL,S_SPID_DIE5,0,0}, // S_SPID_DIE4 + {SPR_SPID,13,10,NULL,S_SPID_DIE6,0,0}, // S_SPID_DIE5 + {SPR_SPID,14,10,NULL,S_SPID_DIE7,0,0}, // S_SPID_DIE6 + {SPR_SPID,15,10,NULL,S_SPID_DIE8,0,0}, // S_SPID_DIE7 + {SPR_SPID,16,10,NULL,S_SPID_DIE9,0,0}, // S_SPID_DIE8 + {SPR_SPID,17,10,NULL,S_SPID_DIE10,0,0}, // S_SPID_DIE9 + {SPR_SPID,18,30,NULL,S_SPID_DIE11,0,0}, // S_SPID_DIE10 + {SPR_SPID,18,-1,A_BossDeath,S_NULL,0,0}, // S_SPID_DIE11 + {SPR_BSPI,0,10,A_Look,S_BSPI_STND2,0,0}, // S_BSPI_STND + {SPR_BSPI,1,10,A_Look,S_BSPI_STND,0,0}, // S_BSPI_STND2 + {SPR_BSPI,0,20,NULL,S_BSPI_RUN1,0,0}, // S_BSPI_SIGHT + {SPR_BSPI,0,3,A_BabyMetal,S_BSPI_RUN2,0,0}, // S_BSPI_RUN1 + {SPR_BSPI,0,3,A_Chase,S_BSPI_RUN3,0,0}, // S_BSPI_RUN2 + {SPR_BSPI,1,3,A_Chase,S_BSPI_RUN4,0,0}, // S_BSPI_RUN3 + {SPR_BSPI,1,3,A_Chase,S_BSPI_RUN5,0,0}, // S_BSPI_RUN4 + {SPR_BSPI,2,3,A_Chase,S_BSPI_RUN6,0,0}, // S_BSPI_RUN5 + {SPR_BSPI,2,3,A_Chase,S_BSPI_RUN7,0,0}, // S_BSPI_RUN6 + {SPR_BSPI,3,3,A_BabyMetal,S_BSPI_RUN8,0,0}, // S_BSPI_RUN7 + {SPR_BSPI,3,3,A_Chase,S_BSPI_RUN9,0,0}, // S_BSPI_RUN8 + {SPR_BSPI,4,3,A_Chase,S_BSPI_RUN10,0,0}, // S_BSPI_RUN9 + {SPR_BSPI,4,3,A_Chase,S_BSPI_RUN11,0,0}, // S_BSPI_RUN10 + {SPR_BSPI,5,3,A_Chase,S_BSPI_RUN12,0,0}, // S_BSPI_RUN11 + {SPR_BSPI,5,3,A_Chase,S_BSPI_RUN1,0,0}, // S_BSPI_RUN12 + {SPR_BSPI,32768,20,A_FaceTarget,S_BSPI_ATK2,0,0}, // S_BSPI_ATK1 + {SPR_BSPI,32774,4,A_BspiAttack,S_BSPI_ATK3,0,0}, // S_BSPI_ATK2 + {SPR_BSPI,32775,4,NULL,S_BSPI_ATK4,0,0}, // S_BSPI_ATK3 + {SPR_BSPI,32775,1,A_SpidRefire,S_BSPI_ATK2,0,0}, // S_BSPI_ATK4 + {SPR_BSPI,8,3,NULL,S_BSPI_PAIN2,0,0}, // S_BSPI_PAIN + {SPR_BSPI,8,3,A_Pain,S_BSPI_RUN1,0,0}, // S_BSPI_PAIN2 + {SPR_BSPI,9,20,A_Scream,S_BSPI_DIE2,0,0}, // S_BSPI_DIE1 + {SPR_BSPI,10,7,A_Fall,S_BSPI_DIE3,0,0}, // S_BSPI_DIE2 + {SPR_BSPI,11,7,NULL,S_BSPI_DIE4,0,0}, // S_BSPI_DIE3 + {SPR_BSPI,12,7,NULL,S_BSPI_DIE5,0,0}, // S_BSPI_DIE4 + {SPR_BSPI,13,7,NULL,S_BSPI_DIE6,0,0}, // S_BSPI_DIE5 + {SPR_BSPI,14,7,NULL,S_BSPI_DIE7,0,0}, // S_BSPI_DIE6 + {SPR_BSPI,15,-1,A_BossDeath,S_NULL,0,0}, // S_BSPI_DIE7 + {SPR_BSPI,15,5,NULL,S_BSPI_RAISE2,0,0}, // S_BSPI_RAISE1 + {SPR_BSPI,14,5,NULL,S_BSPI_RAISE3,0,0}, // S_BSPI_RAISE2 + {SPR_BSPI,13,5,NULL,S_BSPI_RAISE4,0,0}, // S_BSPI_RAISE3 + {SPR_BSPI,12,5,NULL,S_BSPI_RAISE5,0,0}, // S_BSPI_RAISE4 + {SPR_BSPI,11,5,NULL,S_BSPI_RAISE6,0,0}, // S_BSPI_RAISE5 + {SPR_BSPI,10,5,NULL,S_BSPI_RAISE7,0,0}, // S_BSPI_RAISE6 + {SPR_BSPI,9,5,NULL,S_BSPI_RUN1,0,0}, // S_BSPI_RAISE7 + {SPR_APLS,32768,5,NULL,S_ARACH_PLAZ2,0,0}, // S_ARACH_PLAZ + {SPR_APLS,32769,5,NULL,S_ARACH_PLAZ,0,0}, // S_ARACH_PLAZ2 + {SPR_APBX,32768,5,NULL,S_ARACH_PLEX2,0,0}, // S_ARACH_PLEX + {SPR_APBX,32769,5,NULL,S_ARACH_PLEX3,0,0}, // S_ARACH_PLEX2 + {SPR_APBX,32770,5,NULL,S_ARACH_PLEX4,0,0}, // S_ARACH_PLEX3 + {SPR_APBX,32771,5,NULL,S_ARACH_PLEX5,0,0}, // S_ARACH_PLEX4 + {SPR_APBX,32772,5,NULL,S_NULL,0,0}, // S_ARACH_PLEX5 + {SPR_CYBR,0,10,A_Look,S_CYBER_STND2,0,0}, // S_CYBER_STND + {SPR_CYBR,1,10,A_Look,S_CYBER_STND,0,0}, // S_CYBER_STND2 + {SPR_CYBR,0,3,A_Hoof,S_CYBER_RUN2,0,0}, // S_CYBER_RUN1 + {SPR_CYBR,0,3,A_Chase,S_CYBER_RUN3,0,0}, // S_CYBER_RUN2 + {SPR_CYBR,1,3,A_Chase,S_CYBER_RUN4,0,0}, // S_CYBER_RUN3 + {SPR_CYBR,1,3,A_Chase,S_CYBER_RUN5,0,0}, // S_CYBER_RUN4 + {SPR_CYBR,2,3,A_Chase,S_CYBER_RUN6,0,0}, // S_CYBER_RUN5 + {SPR_CYBR,2,3,A_Chase,S_CYBER_RUN7,0,0}, // S_CYBER_RUN6 + {SPR_CYBR,3,3,A_Metal,S_CYBER_RUN8,0,0}, // S_CYBER_RUN7 + {SPR_CYBR,3,3,A_Chase,S_CYBER_RUN1,0,0}, // S_CYBER_RUN8 + {SPR_CYBR,4,6,A_FaceTarget,S_CYBER_ATK2,0,0}, // S_CYBER_ATK1 + {SPR_CYBR,5,12,A_CyberAttack,S_CYBER_ATK3,0,0}, // S_CYBER_ATK2 + {SPR_CYBR,4,12,A_FaceTarget,S_CYBER_ATK4,0,0}, // S_CYBER_ATK3 + {SPR_CYBR,5,12,A_CyberAttack,S_CYBER_ATK5,0,0}, // S_CYBER_ATK4 + {SPR_CYBR,4,12,A_FaceTarget,S_CYBER_ATK6,0,0}, // S_CYBER_ATK5 + {SPR_CYBR,5,12,A_CyberAttack,S_CYBER_RUN1,0,0}, // S_CYBER_ATK6 + {SPR_CYBR,6,10,A_Pain,S_CYBER_RUN1,0,0}, // S_CYBER_PAIN + {SPR_CYBR,7,10,NULL,S_CYBER_DIE2,0,0}, // S_CYBER_DIE1 + {SPR_CYBR,8,10,A_Scream,S_CYBER_DIE3,0,0}, // S_CYBER_DIE2 + {SPR_CYBR,9,10,NULL,S_CYBER_DIE4,0,0}, // S_CYBER_DIE3 + {SPR_CYBR,10,10,NULL,S_CYBER_DIE5,0,0}, // S_CYBER_DIE4 + {SPR_CYBR,11,10,NULL,S_CYBER_DIE6,0,0}, // S_CYBER_DIE5 + {SPR_CYBR,12,10,A_Fall,S_CYBER_DIE7,0,0}, // S_CYBER_DIE6 + {SPR_CYBR,13,10,NULL,S_CYBER_DIE8,0,0}, // S_CYBER_DIE7 + {SPR_CYBR,14,10,NULL,S_CYBER_DIE9,0,0}, // S_CYBER_DIE8 + {SPR_CYBR,15,30,NULL,S_CYBER_DIE10,0,0}, // S_CYBER_DIE9 + {SPR_CYBR,15,-1,A_BossDeath,S_NULL,0,0}, // S_CYBER_DIE10 + {SPR_PAIN,0,10,A_Look,S_PAIN_STND,0,0}, // S_PAIN_STND + {SPR_PAIN,0,3,A_Chase,S_PAIN_RUN2,0,0}, // S_PAIN_RUN1 + {SPR_PAIN,0,3,A_Chase,S_PAIN_RUN3,0,0}, // S_PAIN_RUN2 + {SPR_PAIN,1,3,A_Chase,S_PAIN_RUN4,0,0}, // S_PAIN_RUN3 + {SPR_PAIN,1,3,A_Chase,S_PAIN_RUN5,0,0}, // S_PAIN_RUN4 + {SPR_PAIN,2,3,A_Chase,S_PAIN_RUN6,0,0}, // S_PAIN_RUN5 + {SPR_PAIN,2,3,A_Chase,S_PAIN_RUN1,0,0}, // S_PAIN_RUN6 + {SPR_PAIN,3,5,A_FaceTarget,S_PAIN_ATK2,0,0}, // S_PAIN_ATK1 + {SPR_PAIN,4,5,A_FaceTarget,S_PAIN_ATK3,0,0}, // S_PAIN_ATK2 + {SPR_PAIN,32773,5,A_FaceTarget,S_PAIN_ATK4,0,0}, // S_PAIN_ATK3 + {SPR_PAIN,32773,0,A_PainAttack,S_PAIN_RUN1,0,0}, // S_PAIN_ATK4 + {SPR_PAIN,6,6,NULL,S_PAIN_PAIN2,0,0}, // S_PAIN_PAIN + {SPR_PAIN,6,6,A_Pain,S_PAIN_RUN1,0,0}, // S_PAIN_PAIN2 + {SPR_PAIN,32775,8,NULL,S_PAIN_DIE2,0,0}, // S_PAIN_DIE1 + {SPR_PAIN,32776,8,A_Scream,S_PAIN_DIE3,0,0}, // S_PAIN_DIE2 + {SPR_PAIN,32777,8,NULL,S_PAIN_DIE4,0,0}, // S_PAIN_DIE3 + {SPR_PAIN,32778,8,NULL,S_PAIN_DIE5,0,0}, // S_PAIN_DIE4 + {SPR_PAIN,32779,8,A_PainDie,S_PAIN_DIE6,0,0}, // S_PAIN_DIE5 + {SPR_PAIN,32780,8,NULL,S_NULL,0,0}, // S_PAIN_DIE6 + {SPR_PAIN,12,8,NULL,S_PAIN_RAISE2,0,0}, // S_PAIN_RAISE1 + {SPR_PAIN,11,8,NULL,S_PAIN_RAISE3,0,0}, // S_PAIN_RAISE2 + {SPR_PAIN,10,8,NULL,S_PAIN_RAISE4,0,0}, // S_PAIN_RAISE3 + {SPR_PAIN,9,8,NULL,S_PAIN_RAISE5,0,0}, // S_PAIN_RAISE4 + {SPR_PAIN,8,8,NULL,S_PAIN_RAISE6,0,0}, // S_PAIN_RAISE5 + {SPR_PAIN,7,8,NULL,S_PAIN_RUN1,0,0}, // S_PAIN_RAISE6 + {SPR_SSWV,0,10,A_Look,S_SSWV_STND2,0,0}, // S_SSWV_STND + {SPR_SSWV,1,10,A_Look,S_SSWV_STND,0,0}, // S_SSWV_STND2 + {SPR_SSWV,0,3,A_Chase,S_SSWV_RUN2,0,0}, // S_SSWV_RUN1 + {SPR_SSWV,0,3,A_Chase,S_SSWV_RUN3,0,0}, // S_SSWV_RUN2 + {SPR_SSWV,1,3,A_Chase,S_SSWV_RUN4,0,0}, // S_SSWV_RUN3 + {SPR_SSWV,1,3,A_Chase,S_SSWV_RUN5,0,0}, // S_SSWV_RUN4 + {SPR_SSWV,2,3,A_Chase,S_SSWV_RUN6,0,0}, // S_SSWV_RUN5 + {SPR_SSWV,2,3,A_Chase,S_SSWV_RUN7,0,0}, // S_SSWV_RUN6 + {SPR_SSWV,3,3,A_Chase,S_SSWV_RUN8,0,0}, // S_SSWV_RUN7 + {SPR_SSWV,3,3,A_Chase,S_SSWV_RUN1,0,0}, // S_SSWV_RUN8 + {SPR_SSWV,4,10,A_FaceTarget,S_SSWV_ATK2,0,0}, // S_SSWV_ATK1 + {SPR_SSWV,5,10,A_FaceTarget,S_SSWV_ATK3,0,0}, // S_SSWV_ATK2 + {SPR_SSWV,32774,4,A_CPosAttack,S_SSWV_ATK4,0,0}, // S_SSWV_ATK3 + {SPR_SSWV,5,6,A_FaceTarget,S_SSWV_ATK5,0,0}, // S_SSWV_ATK4 + {SPR_SSWV,32774,4,A_CPosAttack,S_SSWV_ATK6,0,0}, // S_SSWV_ATK5 + {SPR_SSWV,5,1,A_CPosRefire,S_SSWV_ATK2,0,0}, // S_SSWV_ATK6 + {SPR_SSWV,7,3,NULL,S_SSWV_PAIN2,0,0}, // S_SSWV_PAIN + {SPR_SSWV,7,3,A_Pain,S_SSWV_RUN1,0,0}, // S_SSWV_PAIN2 + {SPR_SSWV,8,5,NULL,S_SSWV_DIE2,0,0}, // S_SSWV_DIE1 + {SPR_SSWV,9,5,A_Scream,S_SSWV_DIE3,0,0}, // S_SSWV_DIE2 + {SPR_SSWV,10,5,A_Fall,S_SSWV_DIE4,0,0}, // S_SSWV_DIE3 + {SPR_SSWV,11,5,NULL,S_SSWV_DIE5,0,0}, // S_SSWV_DIE4 + {SPR_SSWV,12,-1,NULL,S_NULL,0,0}, // S_SSWV_DIE5 + {SPR_SSWV,13,5,NULL,S_SSWV_XDIE2,0,0}, // S_SSWV_XDIE1 + {SPR_SSWV,14,5,A_XScream,S_SSWV_XDIE3,0,0}, // S_SSWV_XDIE2 + {SPR_SSWV,15,5,A_Fall,S_SSWV_XDIE4,0,0}, // S_SSWV_XDIE3 + {SPR_SSWV,16,5,NULL,S_SSWV_XDIE5,0,0}, // S_SSWV_XDIE4 + {SPR_SSWV,17,5,NULL,S_SSWV_XDIE6,0,0}, // S_SSWV_XDIE5 + {SPR_SSWV,18,5,NULL,S_SSWV_XDIE7,0,0}, // S_SSWV_XDIE6 + {SPR_SSWV,19,5,NULL,S_SSWV_XDIE8,0,0}, // S_SSWV_XDIE7 + {SPR_SSWV,20,5,NULL,S_SSWV_XDIE9,0,0}, // S_SSWV_XDIE8 + {SPR_SSWV,21,-1,NULL,S_NULL,0,0}, // S_SSWV_XDIE9 + {SPR_SSWV,12,5,NULL,S_SSWV_RAISE2,0,0}, // S_SSWV_RAISE1 + {SPR_SSWV,11,5,NULL,S_SSWV_RAISE3,0,0}, // S_SSWV_RAISE2 + {SPR_SSWV,10,5,NULL,S_SSWV_RAISE4,0,0}, // S_SSWV_RAISE3 + {SPR_SSWV,9,5,NULL,S_SSWV_RAISE5,0,0}, // S_SSWV_RAISE4 + {SPR_SSWV,8,5,NULL,S_SSWV_RUN1,0,0}, // S_SSWV_RAISE5 + {SPR_KEEN,0,-1,NULL,S_KEENSTND,0,0}, // S_KEENSTND + {SPR_KEEN,0,6,NULL,S_COMMKEEN2,0,0}, // S_COMMKEEN + {SPR_KEEN,1,6,NULL,S_COMMKEEN3,0,0}, // S_COMMKEEN2 + {SPR_KEEN,2,6,A_Scream,S_COMMKEEN4,0,0}, // S_COMMKEEN3 + {SPR_KEEN,3,6,NULL,S_COMMKEEN5,0,0}, // S_COMMKEEN4 + {SPR_KEEN,4,6,NULL,S_COMMKEEN6,0,0}, // S_COMMKEEN5 + {SPR_KEEN,5,6,NULL,S_COMMKEEN7,0,0}, // S_COMMKEEN6 + {SPR_KEEN,6,6,NULL,S_COMMKEEN8,0,0}, // S_COMMKEEN7 + {SPR_KEEN,7,6,NULL,S_COMMKEEN9,0,0}, // S_COMMKEEN8 + {SPR_KEEN,8,6,NULL,S_COMMKEEN10,0,0}, // S_COMMKEEN9 + {SPR_KEEN,9,6,NULL,S_COMMKEEN11,0,0}, // S_COMMKEEN10 + {SPR_KEEN,10,6,A_KeenDie,S_COMMKEEN12,0,0},// S_COMMKEEN11 + {SPR_KEEN,11,-1,NULL,S_NULL,0,0}, // S_COMMKEEN12 + {SPR_KEEN,12,4,NULL,S_KEENPAIN2,0,0}, // S_KEENPAIN + {SPR_KEEN,12,8,A_Pain,S_KEENSTND,0,0}, // S_KEENPAIN2 + {SPR_BBRN,0,-1,NULL,S_NULL,0,0}, // S_BRAIN + {SPR_BBRN,1,36,A_BrainPain,S_BRAIN,0,0}, // S_BRAIN_PAIN + {SPR_BBRN,0,100,A_BrainScream,S_BRAIN_DIE2,0,0}, // S_BRAIN_DIE1 + {SPR_BBRN,0,10,NULL,S_BRAIN_DIE3,0,0}, // S_BRAIN_DIE2 + {SPR_BBRN,0,10,NULL,S_BRAIN_DIE4,0,0}, // S_BRAIN_DIE3 + {SPR_BBRN,0,-1,A_BrainDie,S_NULL,0,0}, // S_BRAIN_DIE4 + {SPR_SSWV,0,10,A_Look,S_BRAINEYE,0,0}, // S_BRAINEYE + {SPR_SSWV,0,181,A_BrainAwake,S_BRAINEYE1,0,0}, // S_BRAINEYESEE + {SPR_SSWV,0,150,A_BrainSpit,S_BRAINEYE1,0,0}, // S_BRAINEYE1 + {SPR_BOSF,32768,3,A_SpawnSound,S_SPAWN2,0,0}, // S_SPAWN1 + {SPR_BOSF,32769,3,A_SpawnFly,S_SPAWN3,0,0}, // S_SPAWN2 + {SPR_BOSF,32770,3,A_SpawnFly,S_SPAWN4,0,0}, // S_SPAWN3 + {SPR_BOSF,32771,3,A_SpawnFly,S_SPAWN1,0,0}, // S_SPAWN4 + {SPR_FIRE,32768,4,A_Fire,S_SPAWNFIRE2,0,0}, // S_SPAWNFIRE1 + {SPR_FIRE,32769,4,A_Fire,S_SPAWNFIRE3,0,0}, // S_SPAWNFIRE2 + {SPR_FIRE,32770,4,A_Fire,S_SPAWNFIRE4,0,0}, // S_SPAWNFIRE3 + {SPR_FIRE,32771,4,A_Fire,S_SPAWNFIRE5,0,0}, // S_SPAWNFIRE4 + {SPR_FIRE,32772,4,A_Fire,S_SPAWNFIRE6,0,0}, // S_SPAWNFIRE5 + {SPR_FIRE,32773,4,A_Fire,S_SPAWNFIRE7,0,0}, // S_SPAWNFIRE6 + {SPR_FIRE,32774,4,A_Fire,S_SPAWNFIRE8,0,0}, // S_SPAWNFIRE7 + {SPR_FIRE,32775,4,A_Fire,S_NULL,0,0}, // S_SPAWNFIRE8 + {SPR_MISL,32769,10,NULL,S_BRAINEXPLODE2,0,0}, // S_BRAINEXPLODE1 + {SPR_MISL,32770,10,NULL,S_BRAINEXPLODE3,0,0}, // S_BRAINEXPLODE2 + {SPR_MISL,32771,10,A_BrainExplode,S_NULL,0,0}, // S_BRAINEXPLODE3 + {SPR_ARM1,0,6,NULL,S_ARM1A,0,0}, // S_ARM1 + {SPR_ARM1,32769,7,NULL,S_ARM1,0,0}, // S_ARM1A + {SPR_ARM2,0,6,NULL,S_ARM2A,0,0}, // S_ARM2 + {SPR_ARM2,32769,6,NULL,S_ARM2,0,0}, // S_ARM2A + {SPR_BAR1,0,6,NULL,S_BAR2,0,0}, // S_BAR1 + {SPR_BAR1,1,6,NULL,S_BAR1,0,0}, // S_BAR2 + {SPR_BEXP,32768,5,NULL,S_BEXP2,0,0}, // S_BEXP + {SPR_BEXP,32769,5,A_Scream,S_BEXP3,0,0}, // S_BEXP2 + {SPR_BEXP,32770,5,NULL,S_BEXP4,0,0}, // S_BEXP3 + {SPR_BEXP,32771,10,A_Explode,S_BEXP5,0,0}, // S_BEXP4 + {SPR_BEXP,32772,10,NULL,S_NULL,0,0}, // S_BEXP5 + {SPR_FCAN,32768,4,NULL,S_BBAR2,0,0}, // S_BBAR1 + {SPR_FCAN,32769,4,NULL,S_BBAR3,0,0}, // S_BBAR2 + {SPR_FCAN,32770,4,NULL,S_BBAR1,0,0}, // S_BBAR3 + {SPR_BON1,0,6,NULL,S_BON1A,0,0}, // S_BON1 + {SPR_BON1,1,6,NULL,S_BON1B,0,0}, // S_BON1A + {SPR_BON1,2,6,NULL,S_BON1C,0,0}, // S_BON1B + {SPR_BON1,3,6,NULL,S_BON1D,0,0}, // S_BON1C + {SPR_BON1,2,6,NULL,S_BON1E,0,0}, // S_BON1D + {SPR_BON1,1,6,NULL,S_BON1,0,0}, // S_BON1E + {SPR_BON2,0,6,NULL,S_BON2A,0,0}, // S_BON2 + {SPR_BON2,1,6,NULL,S_BON2B,0,0}, // S_BON2A + {SPR_BON2,2,6,NULL,S_BON2C,0,0}, // S_BON2B + {SPR_BON2,3,6,NULL,S_BON2D,0,0}, // S_BON2C + {SPR_BON2,2,6,NULL,S_BON2E,0,0}, // S_BON2D + {SPR_BON2,1,6,NULL,S_BON2,0,0}, // S_BON2E + {SPR_BKEY,0,10,NULL,S_BKEY2,0,0}, // S_BKEY + {SPR_BKEY,32769,10,NULL,S_BKEY,0,0}, // S_BKEY2 + {SPR_RKEY,0,10,NULL,S_RKEY2,0,0}, // S_RKEY + {SPR_RKEY,32769,10,NULL,S_RKEY,0,0}, // S_RKEY2 + {SPR_YKEY,0,10,NULL,S_YKEY2,0,0}, // S_YKEY + {SPR_YKEY,32769,10,NULL,S_YKEY,0,0}, // S_YKEY2 + {SPR_BSKU,0,10,NULL,S_BSKULL2,0,0}, // S_BSKULL + {SPR_BSKU,32769,10,NULL,S_BSKULL,0,0}, // S_BSKULL2 + {SPR_RSKU,0,10,NULL,S_RSKULL2,0,0}, // S_RSKULL + {SPR_RSKU,32769,10,NULL,S_RSKULL,0,0}, // S_RSKULL2 + {SPR_YSKU,0,10,NULL,S_YSKULL2,0,0}, // S_YSKULL + {SPR_YSKU,32769,10,NULL,S_YSKULL,0,0}, // S_YSKULL2 + {SPR_STIM,0,-1,NULL,S_NULL,0,0}, // S_STIM + {SPR_MEDI,0,-1,NULL,S_NULL,0,0}, // S_MEDI + {SPR_SOUL,32768,6,NULL,S_SOUL2,0,0}, // S_SOUL + {SPR_SOUL,32769,6,NULL,S_SOUL3,0,0}, // S_SOUL2 + {SPR_SOUL,32770,6,NULL,S_SOUL4,0,0}, // S_SOUL3 + {SPR_SOUL,32771,6,NULL,S_SOUL5,0,0}, // S_SOUL4 + {SPR_SOUL,32770,6,NULL,S_SOUL6,0,0}, // S_SOUL5 + {SPR_SOUL,32769,6,NULL,S_SOUL,0,0}, // S_SOUL6 + {SPR_PINV,32768,6,NULL,S_PINV2,0,0}, // S_PINV + {SPR_PINV,32769,6,NULL,S_PINV3,0,0}, // S_PINV2 + {SPR_PINV,32770,6,NULL,S_PINV4,0,0}, // S_PINV3 + {SPR_PINV,32771,6,NULL,S_PINV,0,0}, // S_PINV4 + {SPR_PSTR,32768,-1,NULL,S_NULL,0,0}, // S_PSTR + {SPR_PINS,32768,6,NULL,S_PINS2,0,0}, // S_PINS + {SPR_PINS,32769,6,NULL,S_PINS3,0,0}, // S_PINS2 + {SPR_PINS,32770,6,NULL,S_PINS4,0,0}, // S_PINS3 + {SPR_PINS,32771,6,NULL,S_PINS,0,0}, // S_PINS4 + {SPR_MEGA,32768,6,NULL,S_MEGA2,0,0}, // S_MEGA + {SPR_MEGA,32769,6,NULL,S_MEGA3,0,0}, // S_MEGA2 + {SPR_MEGA,32770,6,NULL,S_MEGA4,0,0}, // S_MEGA3 + {SPR_MEGA,32771,6,NULL,S_MEGA,0,0}, // S_MEGA4 + {SPR_SUIT,32768,-1,NULL,S_NULL,0,0}, // S_SUIT + {SPR_PMAP,32768,6,NULL,S_PMAP2,0,0}, // S_PMAP + {SPR_PMAP,32769,6,NULL,S_PMAP3,0,0}, // S_PMAP2 + {SPR_PMAP,32770,6,NULL,S_PMAP4,0,0}, // S_PMAP3 + {SPR_PMAP,32771,6,NULL,S_PMAP5,0,0}, // S_PMAP4 + {SPR_PMAP,32770,6,NULL,S_PMAP6,0,0}, // S_PMAP5 + {SPR_PMAP,32769,6,NULL,S_PMAP,0,0}, // S_PMAP6 + {SPR_PVIS,32768,6,NULL,S_PVIS2,0,0}, // S_PVIS + {SPR_PVIS,1,6,NULL,S_PVIS,0,0}, // S_PVIS2 + {SPR_CLIP,0,-1,NULL,S_NULL,0,0}, // S_CLIP + {SPR_AMMO,0,-1,NULL,S_NULL,0,0}, // S_AMMO + {SPR_ROCK,0,-1,NULL,S_NULL,0,0}, // S_ROCK + {SPR_BROK,0,-1,NULL,S_NULL,0,0}, // S_BROK + {SPR_CELL,0,-1,NULL,S_NULL,0,0}, // S_CELL + {SPR_CELP,0,-1,NULL,S_NULL,0,0}, // S_CELP + {SPR_SHEL,0,-1,NULL,S_NULL,0,0}, // S_SHEL + {SPR_SBOX,0,-1,NULL,S_NULL,0,0}, // S_SBOX + {SPR_BPAK,0,-1,NULL,S_NULL,0,0}, // S_BPAK + {SPR_BFUG,0,-1,NULL,S_NULL,0,0}, // S_BFUG + {SPR_MGUN,0,-1,NULL,S_NULL,0,0}, // S_MGUN + {SPR_CSAW,0,-1,NULL,S_NULL,0,0}, // S_CSAW + {SPR_LAUN,0,-1,NULL,S_NULL,0,0}, // S_LAUN + {SPR_PLAS,0,-1,NULL,S_NULL,0,0}, // S_PLAS + {SPR_SHOT,0,-1,NULL,S_NULL,0,0}, // S_SHOT + {SPR_SGN2,0,-1,NULL,S_NULL,0,0}, // S_SHOT2 + {SPR_COLU,32768,-1,NULL,S_NULL,0,0}, // S_COLU + {SPR_SMT2,0,-1,NULL,S_NULL,0,0}, // S_STALAG + {SPR_GOR1,0,10,NULL,S_BLOODYTWITCH2,0,0}, // S_BLOODYTWITCH + {SPR_GOR1,1,15,NULL,S_BLOODYTWITCH3,0,0}, // S_BLOODYTWITCH2 + {SPR_GOR1,2,8,NULL,S_BLOODYTWITCH4,0,0}, // S_BLOODYTWITCH3 + {SPR_GOR1,1,6,NULL,S_BLOODYTWITCH,0,0}, // S_BLOODYTWITCH4 + {SPR_PLAY,13,-1,NULL,S_NULL,0,0}, // S_DEADTORSO + {SPR_PLAY,18,-1,NULL,S_NULL,0,0}, // S_DEADBOTTOM + {SPR_POL2,0,-1,NULL,S_NULL,0,0}, // S_HEADSONSTICK + {SPR_POL5,0,-1,NULL,S_NULL,0,0}, // S_GIBS + {SPR_POL4,0,-1,NULL,S_NULL,0,0}, // S_HEADONASTICK + {SPR_POL3,32768,6,NULL,S_HEADCANDLES2,0,0}, // S_HEADCANDLES + {SPR_POL3,32769,6,NULL,S_HEADCANDLES,0,0}, // S_HEADCANDLES2 + {SPR_POL1,0,-1,NULL,S_NULL,0,0}, // S_DEADSTICK + {SPR_POL6,0,6,NULL,S_LIVESTICK2,0,0}, // S_LIVESTICK + {SPR_POL6,1,8,NULL,S_LIVESTICK,0,0}, // S_LIVESTICK2 + {SPR_GOR2,0,-1,NULL,S_NULL,0,0}, // S_MEAT2 + {SPR_GOR3,0,-1,NULL,S_NULL,0,0}, // S_MEAT3 + {SPR_GOR4,0,-1,NULL,S_NULL,0,0}, // S_MEAT4 + {SPR_GOR5,0,-1,NULL,S_NULL,0,0}, // S_MEAT5 + {SPR_SMIT,0,-1,NULL,S_NULL,0,0}, // S_STALAGTITE + {SPR_COL1,0,-1,NULL,S_NULL,0,0}, // S_TALLGRNCOL + {SPR_COL2,0,-1,NULL,S_NULL,0,0}, // S_SHRTGRNCOL + {SPR_COL3,0,-1,NULL,S_NULL,0,0}, // S_TALLREDCOL + {SPR_COL4,0,-1,NULL,S_NULL,0,0}, // S_SHRTREDCOL + {SPR_CAND,32768,-1,NULL,S_NULL,0,0}, // S_CANDLESTIK + {SPR_CBRA,32768,-1,NULL,S_NULL,0,0}, // S_CANDELABRA + {SPR_COL6,0,-1,NULL,S_NULL,0,0}, // S_SKULLCOL + {SPR_TRE1,0,-1,NULL,S_NULL,0,0}, // S_TORCHTREE + {SPR_TRE2,0,-1,NULL,S_NULL,0,0}, // S_BIGTREE + {SPR_ELEC,0,-1,NULL,S_NULL,0,0}, // S_TECHPILLAR + {SPR_CEYE,32768,6,NULL,S_EVILEYE2,0,0}, // S_EVILEYE + {SPR_CEYE,32769,6,NULL,S_EVILEYE3,0,0}, // S_EVILEYE2 + {SPR_CEYE,32770,6,NULL,S_EVILEYE4,0,0}, // S_EVILEYE3 + {SPR_CEYE,32769,6,NULL,S_EVILEYE,0,0}, // S_EVILEYE4 + {SPR_FSKU,32768,6,NULL,S_FLOATSKULL2,0,0}, // S_FLOATSKULL + {SPR_FSKU,32769,6,NULL,S_FLOATSKULL3,0,0}, // S_FLOATSKULL2 + {SPR_FSKU,32770,6,NULL,S_FLOATSKULL,0,0}, // S_FLOATSKULL3 + {SPR_COL5,0,14,NULL,S_HEARTCOL2,0,0}, // S_HEARTCOL + {SPR_COL5,1,14,NULL,S_HEARTCOL,0,0}, // S_HEARTCOL2 + {SPR_TBLU,32768,4,NULL,S_BLUETORCH2,0,0}, // S_BLUETORCH + {SPR_TBLU,32769,4,NULL,S_BLUETORCH3,0,0}, // S_BLUETORCH2 + {SPR_TBLU,32770,4,NULL,S_BLUETORCH4,0,0}, // S_BLUETORCH3 + {SPR_TBLU,32771,4,NULL,S_BLUETORCH,0,0}, // S_BLUETORCH4 + {SPR_TGRN,32768,4,NULL,S_GREENTORCH2,0,0}, // S_GREENTORCH + {SPR_TGRN,32769,4,NULL,S_GREENTORCH3,0,0}, // S_GREENTORCH2 + {SPR_TGRN,32770,4,NULL,S_GREENTORCH4,0,0}, // S_GREENTORCH3 + {SPR_TGRN,32771,4,NULL,S_GREENTORCH,0,0}, // S_GREENTORCH4 + {SPR_TRED,32768,4,NULL,S_REDTORCH2,0,0}, // S_REDTORCH + {SPR_TRED,32769,4,NULL,S_REDTORCH3,0,0}, // S_REDTORCH2 + {SPR_TRED,32770,4,NULL,S_REDTORCH4,0,0}, // S_REDTORCH3 + {SPR_TRED,32771,4,NULL,S_REDTORCH,0,0}, // S_REDTORCH4 + {SPR_SMBT,32768,4,NULL,S_BTORCHSHRT2,0,0}, // S_BTORCHSHRT + {SPR_SMBT,32769,4,NULL,S_BTORCHSHRT3,0,0}, // S_BTORCHSHRT2 + {SPR_SMBT,32770,4,NULL,S_BTORCHSHRT4,0,0}, // S_BTORCHSHRT3 + {SPR_SMBT,32771,4,NULL,S_BTORCHSHRT,0,0}, // S_BTORCHSHRT4 + {SPR_SMGT,32768,4,NULL,S_GTORCHSHRT2,0,0}, // S_GTORCHSHRT + {SPR_SMGT,32769,4,NULL,S_GTORCHSHRT3,0,0}, // S_GTORCHSHRT2 + {SPR_SMGT,32770,4,NULL,S_GTORCHSHRT4,0,0}, // S_GTORCHSHRT3 + {SPR_SMGT,32771,4,NULL,S_GTORCHSHRT,0,0}, // S_GTORCHSHRT4 + {SPR_SMRT,32768,4,NULL,S_RTORCHSHRT2,0,0}, // S_RTORCHSHRT + {SPR_SMRT,32769,4,NULL,S_RTORCHSHRT3,0,0}, // S_RTORCHSHRT2 + {SPR_SMRT,32770,4,NULL,S_RTORCHSHRT4,0,0}, // S_RTORCHSHRT3 + {SPR_SMRT,32771,4,NULL,S_RTORCHSHRT,0,0}, // S_RTORCHSHRT4 + {SPR_HDB1,0,-1,NULL,S_NULL,0,0}, // S_HANGNOGUTS + {SPR_HDB2,0,-1,NULL,S_NULL,0,0}, // S_HANGBNOBRAIN + {SPR_HDB3,0,-1,NULL,S_NULL,0,0}, // S_HANGTLOOKDN + {SPR_HDB4,0,-1,NULL,S_NULL,0,0}, // S_HANGTSKULL + {SPR_HDB5,0,-1,NULL,S_NULL,0,0}, // S_HANGTLOOKUP + {SPR_HDB6,0,-1,NULL,S_NULL,0,0}, // S_HANGTNOBRAIN + {SPR_POB1,0,-1,NULL,S_NULL,0,0}, // S_COLONGIBS + {SPR_POB2,0,-1,NULL,S_NULL,0,0}, // S_SMALLPOOL + {SPR_BRS1,0,-1,NULL,S_NULL,0,0}, // S_BRAINSTEM + {SPR_TLMP,32768,4,NULL,S_TECHLAMP2,0,0}, // S_TECHLAMP + {SPR_TLMP,32769,4,NULL,S_TECHLAMP3,0,0}, // S_TECHLAMP2 + {SPR_TLMP,32770,4,NULL,S_TECHLAMP4,0,0}, // S_TECHLAMP3 + {SPR_TLMP,32771,4,NULL,S_TECHLAMP,0,0}, // S_TECHLAMP4 + {SPR_TLP2,32768,4,NULL,S_TECH2LAMP2,0,0}, // S_TECH2LAMP + {SPR_TLP2,32769,4,NULL,S_TECH2LAMP3,0,0}, // S_TECH2LAMP2 + {SPR_TLP2,32770,4,NULL,S_TECH2LAMP4,0,0}, // S_TECH2LAMP3 + {SPR_TLP2,32771,4,NULL,S_TECH2LAMP,0,0}, // S_TECH2LAMP4 + {SPR_TNT1,0,-1,NULL,S_TNT1,0,0}, // S_TNT1 // phares 3/8/98 + + // killough 8/9/98: grenade + {SPR_MISL,32768,1000,A_Die,S_GRENADE}, // S_GRENADE + + // killough 8/10/98: variable damage explosion + {SPR_MISL,32769,4,A_Scream,S_DETONATE2}, // S_DETONATE + {SPR_MISL,32770,6,A_Detonate,S_DETONATE3}, // S_DETONATE2 + {SPR_MISL,32771,10,NULL,S_NULL}, // S_DETONATE3 + +#ifdef DOGS + // killough 7/19/98: Marine's best friend :) + {SPR_DOGS,0,10,A_Look,S_DOGS_STND2}, // S_DOGS_STND + {SPR_DOGS,1,10,A_Look,S_DOGS_STND}, // S_DOGS_STND2 + {SPR_DOGS,0,2,A_Chase,S_DOGS_RUN2}, // S_DOGS_RUN1 + {SPR_DOGS,0,2,A_Chase,S_DOGS_RUN3}, // S_DOGS_RUN2 + {SPR_DOGS,1,2,A_Chase,S_DOGS_RUN4}, // S_DOGS_RUN3 + {SPR_DOGS,1,2,A_Chase,S_DOGS_RUN5}, // S_DOGS_RUN4 + {SPR_DOGS,2,2,A_Chase,S_DOGS_RUN6}, // S_DOGS_RUN5 + {SPR_DOGS,2,2,A_Chase,S_DOGS_RUN7}, // S_DOGS_RUN6 + {SPR_DOGS,3,2,A_Chase,S_DOGS_RUN8}, // S_DOGS_RUN7 + {SPR_DOGS,3,2,A_Chase,S_DOGS_RUN1}, // S_DOGS_RUN8 + {SPR_DOGS,4,8,A_FaceTarget,S_DOGS_ATK2}, // S_DOGS_ATK1 + {SPR_DOGS,5,8,A_FaceTarget,S_DOGS_ATK3}, // S_DOGS_ATK2 + {SPR_DOGS,6,8,A_SargAttack,S_DOGS_RUN1}, // S_DOGS_ATK3 + {SPR_DOGS,7,2,NULL,S_DOGS_PAIN2}, // S_DOGS_PAIN + {SPR_DOGS,7,2,A_Pain,S_DOGS_RUN1}, // S_DOGS_PAIN2 + {SPR_DOGS,8,8,NULL,S_DOGS_DIE2}, // S_DOGS_DIE1 + {SPR_DOGS,9,8,A_Scream,S_DOGS_DIE3}, // S_DOGS_DIE2 + {SPR_DOGS,10,4,NULL,S_DOGS_DIE4}, // S_DOGS_DIE3 + {SPR_DOGS,11,4,A_Fall,S_DOGS_DIE5}, // S_DOGS_DIE4 + {SPR_DOGS,12,4,NULL,S_DOGS_DIE6}, // S_DOGS_DIE5 + {SPR_DOGS,13,-1,NULL,S_NULL}, // S_DOGS_DIE6 + {SPR_DOGS,13,5,NULL,S_DOGS_RAISE2}, // S_DOGS_RAISE1 + {SPR_DOGS,12,5,NULL,S_DOGS_RAISE3}, // S_DOGS_RAISE2 + {SPR_DOGS,11,5,NULL,S_DOGS_RAISE4}, // S_DOGS_RAISE3 + {SPR_DOGS,10,5,NULL,S_DOGS_RAISE5}, // S_DOGS_RAISE4 + {SPR_DOGS,9,5,NULL,S_DOGS_RAISE6}, // S_DOGS_RAISE5 + {SPR_DOGS,8,5,NULL,S_DOGS_RUN1}, // S_DOGS_RAISE6 +#else + // if dogs are disabled, dummy states are required for dehacked compatibility + {0,0,-1,NULL,S_NULL}, // S_DOGS_STND + {0,0,-1,NULL,S_NULL}, // S_DOGS_STND2 + {0,0,-1,NULL,S_NULL}, // S_DOGS_RUN1 + {0,0,-1,NULL,S_NULL}, // S_DOGS_RUN2 + {0,0,-1,NULL,S_NULL}, // S_DOGS_RUN3 + {0,0,-1,NULL,S_NULL}, // S_DOGS_RUN4 + {0,0,-1,NULL,S_NULL}, // S_DOGS_RUN5 + {0,0,-1,NULL,S_NULL}, // S_DOGS_RUN6 + {0,0,-1,NULL,S_NULL}, // S_DOGS_RUN7 + {0,0,-1,NULL,S_NULL}, // S_DOGS_RUN8 + {0,0,-1,NULL,S_NULL}, // S_DOGS_ATK1 + {0,0,-1,NULL,S_NULL}, // S_DOGS_ATK2 + {0,0,-1,NULL,S_NULL}, // S_DOGS_ATK3 + {0,0,-1,NULL,S_NULL}, // S_DOGS_PAIN + {0,0,-1,NULL,S_NULL}, // S_DOGS_PAIN2 + {0,0,-1,NULL,S_NULL}, // S_DOGS_DIE1 + {0,0,-1,NULL,S_NULL}, // S_DOGS_DIE2 + {0,0,-1,NULL,S_NULL}, // S_DOGS_DIE3 + {0,0,-1,NULL,S_NULL}, // S_DOGS_DIE4 + {0,0,-1,NULL,S_NULL}, // S_DOGS_DIE5 + {0,0,-1,NULL,S_NULL}, // S_DOGS_DIE6 + {0,0,-1,NULL,S_NULL}, // S_DOGS_RAISE1 + {0,0,-1,NULL,S_NULL}, // S_DOGS_RAISE2 + {0,0,-1,NULL,S_NULL}, // S_DOGS_RAISE3 + {0,0,-1,NULL,S_NULL}, // S_DOGS_RAISE4 + {0,0,-1,NULL,S_NULL}, // S_DOGS_RAISE5 + {0,0,-1,NULL,S_NULL}, // S_DOGS_RAISE6 +#endif + + // add dummy beta bfg / lost soul frames for dehacked compatibility + // fixes bug #1576151 (part 2) + {0,0,-1,NULL,S_NULL}, // S_OLDBFG1 + {0,0,-1,NULL,S_NULL}, // S_OLDBFG2 + {0,0,-1,NULL,S_NULL}, // S_OLDBFG3 + {0,0,-1,NULL,S_NULL}, // S_OLDBFG4 + {0,0,-1,NULL,S_NULL}, // S_OLDBFG5 + {0,0,-1,NULL,S_NULL}, // S_OLDBFG6 + {0,0,-1,NULL,S_NULL}, // S_OLDBFG7 + {0,0,-1,NULL,S_NULL}, // S_OLDBFG8 + {0,0,-1,NULL,S_NULL}, // S_OLDBFG9 + {0,0,-1,NULL,S_NULL}, // S_OLDBFG10 + {0,0,-1,NULL,S_NULL}, // S_OLDBFG11 + {0,0,-1,NULL,S_NULL}, // S_OLDBFG12 + {0,0,-1,NULL,S_NULL}, // S_OLDBFG13 + {0,0,-1,NULL,S_NULL}, // S_OLDBFG14 + {0,0,-1,NULL,S_NULL}, // S_OLDBFG15 + {0,0,-1,NULL,S_NULL}, // S_OLDBFG16 + {0,0,-1,NULL,S_NULL}, // S_OLDBFG17 + {0,0,-1,NULL,S_NULL}, // S_OLDBFG18 + {0,0,-1,NULL,S_NULL}, // S_OLDBFG19 + {0,0,-1,NULL,S_NULL}, // S_OLDBFG20 + {0,0,-1,NULL,S_NULL}, // S_OLDBFG21 + {0,0,-1,NULL,S_NULL}, // S_OLDBFG22 + {0,0,-1,NULL,S_NULL}, // S_OLDBFG23 + {0,0,-1,NULL,S_NULL}, // S_OLDBFG24 + {0,0,-1,NULL,S_NULL}, // S_OLDBFG25 + {0,0,-1,NULL,S_NULL}, // S_OLDBFG26 + {0,0,-1,NULL,S_NULL}, // S_OLDBFG27 + {0,0,-1,NULL,S_NULL}, // S_OLDBFG28 + {0,0,-1,NULL,S_NULL}, // S_OLDBFG29 + {0,0,-1,NULL,S_NULL}, // S_OLDBFG30 + {0,0,-1,NULL,S_NULL}, // S_OLDBFG31 + {0,0,-1,NULL,S_NULL}, // S_OLDBFG32 + {0,0,-1,NULL,S_NULL}, // S_OLDBFG33 + {0,0,-1,NULL,S_NULL}, // S_OLDBFG34 + {0,0,-1,NULL,S_NULL}, // S_OLDBFG35 + {0,0,-1,NULL,S_NULL}, // S_OLDBFG36 + {0,0,-1,NULL,S_NULL}, // S_OLDBFG37 + {0,0,-1,NULL,S_NULL}, // S_OLDBFG38 + {0,0,-1,NULL,S_NULL}, // S_OLDBFG39 + {0,0,-1,NULL,S_NULL}, // S_OLDBFG40 + {0,0,-1,NULL,S_NULL}, // S_OLDBFG41 + {0,0,-1,NULL,S_NULL}, // S_OLDBFG42 + {0,0,-1,NULL,S_NULL}, // S_OLDBFG43 + + {0,0,-1,NULL,S_NULL}, // S_PLS1BALL + {0,0,-1,NULL,S_NULL}, // S_PLS1BALL2 + {0,0,-1,NULL,S_NULL}, // S_PLS1EXP + {0,0,-1,NULL,S_NULL}, // S_PLS1EXP2 + {0,0,-1,NULL,S_NULL}, // S_PLS1EXP3 + {0,0,-1,NULL,S_NULL}, // S_PLS1EXP4 + {0,0,-1,NULL,S_NULL}, // S_PLS1EXP5 + + {0,0,-1,NULL,S_NULL}, // S_PLS2BALL + {0,0,-1,NULL,S_NULL}, // S_PLS2BALL2 + {0,0,-1,NULL,S_NULL}, // S_PLS2BALLX1 + {0,0,-1,NULL,S_NULL}, // S_PLS2BALLX2 + {0,0,-1,NULL,S_NULL}, // S_PLS2BALLX3 + + {0,0,-1,NULL,S_NULL}, // S_BON3 + {0,0,-1,NULL,S_NULL}, // S_BON4 + + {0,0,-1,NULL,S_NULL}, // S_BSKUL_STND + {0,0,-1,NULL,S_NULL}, // S_BSKUL_RUN1 + {0,0,-1,NULL,S_NULL}, // S_BSKUL_RUN2 + {0,0,-1,NULL,S_NULL}, // S_BSKUL_RUN3 + {0,0,-1,NULL,S_NULL}, // S_BSKUL_RUN4 + {0,0,-1,NULL,S_NULL}, // S_BSKUL_ATK1 + {0,0,-1,NULL,S_NULL}, // S_BSKUL_ATK2 + {0,0,-1,NULL,S_NULL}, // S_BSKUL_ATK3 + {0,0,-1,NULL,S_NULL}, // S_BSKUL_PAIN1 + {0,0,-1,NULL,S_NULL}, // S_BSKUL_PAIN2 + {0,0,-1,NULL,S_NULL}, // S_BSKUL_PAIN3 + {0,0,-1,NULL,S_NULL}, // S_BSKUL_DIE1 + {0,0,-1,NULL,S_NULL}, // S_BSKUL_DIE2 + {0,0,-1,NULL,S_NULL}, // S_BSKUL_DIE3 + {0,0,-1,NULL,S_NULL}, // S_BSKUL_DIE4 + {0,0,-1,NULL,S_NULL}, // S_BSKUL_DIE5 + {0,0,-1,NULL,S_NULL}, // S_BSKUL_DIE6 + {0,0,-1,NULL,S_NULL}, // S_BSKUL_DIE7 + {0,0,-1,NULL,S_NULL}, // S_BSKUL_DIE8 + + // killough 10/98: mushroom effect + {SPR_MISL,32769,8,A_Mushroom,S_EXPLODE2}, // S_MUSHROOM +}; + +// ******************************************************************** +// Object "Thing" definitions +// ******************************************************************** +// Now we get to the actual objects and their characteristics. If +// you've seen Dehacked, much of this is where the Bits are set, +// commented below as "flags", as well as where you wire in which +// frames are the beginning frames for near and far attack, death, +// and such. Sounds are hooked in here too, as well as how much +// mass, speed and so forth a Thing has. Everything you ever wanted +// to know... +// +// Like all this other stuff, the MT_* entries are enumerated in info.h +// +// Note that these are all just indices of the elements involved, and +// not real pointers to them. For example, the player's death sequence +// is S_PLAY_DIE1, which just evaluates to the index in the states[] +// array above, which actually knows what happens then and what the +// sprite looks like, if it makes noise or not, etc. +// +// Additional comments about each of the entries are located in info.h +// next to the mobjinfo_t structure definition. +// +// This goes on for the next 3000+ lines... + +mobjinfo_t mobjinfo[NUMMOBJTYPES] = { + { // MT_PLAYER + -1, // doomednum + S_PLAY, // spawnstate + 100, // spawnhealth + S_PLAY_RUN1, // seestate + sfx_None, // seesound + 0, // reactiontime + sfx_None, // attacksound + S_PLAY_PAIN, // painstate + 255, // painchance + sfx_plpain, // painsound + S_NULL, // meleestate + S_PLAY_ATK1, // missilestate + S_PLAY_DIE1, // deathstate + S_PLAY_XDIE1, // xdeathstate + sfx_pldeth, // deathsound + 0, // speed + 16*FRACUNIT, // radius + 56*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID|MF_SHOOTABLE|MF_DROPOFF|MF_PICKUP|MF_NOTDMATCH, // flags + S_NULL // raisestate + }, + + { // MT_POSSESSED + 3004, // doomednum + S_POSS_STND, // spawnstate + 20, // spawnhealth + S_POSS_RUN1, // seestate + sfx_posit1, // seesound + 8, // reactiontime + sfx_pistol, // attacksound + S_POSS_PAIN, // painstate + 200, // painchance + sfx_popain, // painsound + 0, // meleestate + S_POSS_ATK1, // missilestate + S_POSS_DIE1, // deathstate + S_POSS_XDIE1, // xdeathstate + sfx_podth1, // deathsound + 8, // speed + 20*FRACUNIT, // radius + 56*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_posact, // activesound + MF_SOLID|MF_SHOOTABLE|MF_COUNTKILL, // flags + S_POSS_RAISE1 // raisestate + }, + + { // MT_SHOTGUY + 9, // doomednum + S_SPOS_STND, // spawnstate + 30, // spawnhealth + S_SPOS_RUN1, // seestate + sfx_posit2, // seesound + 8, // reactiontime + 0, // attacksound + S_SPOS_PAIN, // painstate + 170, // painchance + sfx_popain, // painsound + 0, // meleestate + S_SPOS_ATK1, // missilestate + S_SPOS_DIE1, // deathstate + S_SPOS_XDIE1, // xdeathstate + sfx_podth2, // deathsound + 8, // speed + 20*FRACUNIT, // radius + 56*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_posact, // activesound + MF_SOLID|MF_SHOOTABLE|MF_COUNTKILL, // flags + S_SPOS_RAISE1 // raisestate + }, + + { // MT_VILE + 64, // doomednum + S_VILE_STND, // spawnstate + 700, // spawnhealth + S_VILE_RUN1, // seestate + sfx_vilsit, // seesound + 8, // reactiontime + 0, // attacksound + S_VILE_PAIN, // painstate + 10, // painchance + sfx_vipain, // painsound + 0, // meleestate + S_VILE_ATK1, // missilestate + S_VILE_DIE1, // deathstate + S_NULL, // xdeathstate + sfx_vildth, // deathsound + 15, // speed + 20*FRACUNIT, // radius + 56*FRACUNIT, // height + 500, // mass + 0, // damage + sfx_vilact, // activesound + MF_SOLID|MF_SHOOTABLE|MF_COUNTKILL, // flags + S_NULL // raisestate + }, + + { // MT_FIRE + -1, // doomednum + S_FIRE1, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_NOBLOCKMAP|MF_NOGRAVITY|MF_TRANSLUCENT, // flags // killough 2/21/98 + S_NULL // raisestate + }, + + { // MT_UNDEAD + 66, // doomednum + S_SKEL_STND, // spawnstate + 300, // spawnhealth + S_SKEL_RUN1, // seestate + sfx_skesit, // seesound + 8, // reactiontime + 0, // attacksound + S_SKEL_PAIN, // painstate + 100, // painchance + sfx_popain, // painsound + S_SKEL_FIST1, // meleestate + S_SKEL_MISS1, // missilestate + S_SKEL_DIE1, // deathstate + S_NULL, // xdeathstate + sfx_skedth, // deathsound + 10, // speed + 20*FRACUNIT, // radius + 56*FRACUNIT, // height + 500, // mass + 0, // damage + sfx_skeact, // activesound + MF_SOLID|MF_SHOOTABLE|MF_COUNTKILL, // flags + S_SKEL_RAISE1 // raisestate + }, + + { // MT_TRACER + -1, // doomednum + S_TRACER, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_skeatk, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_TRACEEXP1, // deathstate + S_NULL, // xdeathstate + sfx_barexp, // deathsound + 10*FRACUNIT, // speed + 11*FRACUNIT, // radius + 8*FRACUNIT, // height + 100, // mass + 10, // damage + sfx_None, // activesound + MF_NOBLOCKMAP|MF_MISSILE|MF_DROPOFF|MF_NOGRAVITY, // flags + S_NULL // raisestate + }, + + { // MT_SMOKE + -1, // doomednum + S_SMOKE1, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_NOBLOCKMAP|MF_NOGRAVITY|MF_TRANSLUCENT, // flags // phares + S_NULL // raisestate + }, + + { // MT_FATSO + 67, // doomednum + S_FATT_STND, // spawnstate + 600, // spawnhealth + S_FATT_RUN1, // seestate + sfx_mansit, // seesound + 8, // reactiontime + 0, // attacksound + S_FATT_PAIN, // painstate + 80, // painchance + sfx_mnpain, // painsound + 0, // meleestate + S_FATT_ATK1, // missilestate + S_FATT_DIE1, // deathstate + S_NULL, // xdeathstate + sfx_mandth, // deathsound + 8, // speed + 48*FRACUNIT, // radius + 64*FRACUNIT, // height + 1000, // mass + 0, // damage + sfx_posact, // activesound + MF_SOLID|MF_SHOOTABLE|MF_COUNTKILL, // flags + S_FATT_RAISE1 // raisestate + }, + + { // MT_FATSHOT + -1, // doomednum + S_FATSHOT1, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_firsht, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_FATSHOTX1, // deathstate + S_NULL, // xdeathstate + sfx_firxpl, // deathsound + 20*FRACUNIT, // speed + 6*FRACUNIT, // radius + 8*FRACUNIT, // height + 100, // mass + 8, // damage + sfx_None, // activesound + MF_NOBLOCKMAP|MF_MISSILE|MF_DROPOFF|MF_NOGRAVITY|MF_TRANSLUCENT, // flags \\ killough 2/21/98 + S_NULL // raisestate + }, + + { // MT_CHAINGUY + 65, // doomednum + S_CPOS_STND, // spawnstate + 70, // spawnhealth + S_CPOS_RUN1, // seestate + sfx_posit2, // seesound + 8, // reactiontime + 0, // attacksound + S_CPOS_PAIN, // painstate + 170, // painchance + sfx_popain, // painsound + 0, // meleestate + S_CPOS_ATK1, // missilestate + S_CPOS_DIE1, // deathstate + S_CPOS_XDIE1, // xdeathstate + sfx_podth2, // deathsound + 8, // speed + 20*FRACUNIT, // radius + 56*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_posact, // activesound + MF_SOLID|MF_SHOOTABLE|MF_COUNTKILL, // flags + S_CPOS_RAISE1 // raisestate + }, + + { // MT_TROOP + 3001, // doomednum + S_TROO_STND, // spawnstate + 60, // spawnhealth + S_TROO_RUN1, // seestate + sfx_bgsit1, // seesound + 8, // reactiontime + 0, // attacksound + S_TROO_PAIN, // painstate + 200, // painchance + sfx_popain, // painsound + S_TROO_ATK1, // meleestate + S_TROO_ATK1, // missilestate + S_TROO_DIE1, // deathstate + S_TROO_XDIE1, // xdeathstate + sfx_bgdth1, // deathsound + 8, // speed + 20*FRACUNIT, // radius + 56*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_bgact, // activesound + MF_SOLID|MF_SHOOTABLE|MF_COUNTKILL, // killough |MF_TRANSLUCENT, // flags // phares + S_TROO_RAISE1 // raisestate + }, + + { // MT_SERGEANT + 3002, // doomednum + S_SARG_STND, // spawnstate + 150, // spawnhealth + S_SARG_RUN1, // seestate + sfx_sgtsit, // seesound + 8, // reactiontime + sfx_sgtatk, // attacksound + S_SARG_PAIN, // painstate + 180, // painchance + sfx_dmpain, // painsound + S_SARG_ATK1, // meleestate + 0, // missilestate + S_SARG_DIE1, // deathstate + S_NULL, // xdeathstate + sfx_sgtdth, // deathsound + 10, // speed + 30*FRACUNIT, // radius + 56*FRACUNIT, // height + 400, // mass + 0, // damage + sfx_dmact, // activesound + MF_SOLID|MF_SHOOTABLE|MF_COUNTKILL, // flags + S_SARG_RAISE1 // raisestate + }, + + { // MT_SHADOWS + 58, // doomednum + S_SARG_STND, // spawnstate + 150, // spawnhealth + S_SARG_RUN1, // seestate + sfx_sgtsit, // seesound + 8, // reactiontime + sfx_sgtatk, // attacksound + S_SARG_PAIN, // painstate + 180, // painchance + sfx_dmpain, // painsound + S_SARG_ATK1, // meleestate + 0, // missilestate + S_SARG_DIE1, // deathstate + S_NULL, // xdeathstate + sfx_sgtdth, // deathsound + 10, // speed + 30*FRACUNIT, // radius + 56*FRACUNIT, // height + 400, // mass + 0, // damage + sfx_dmact, // activesound + MF_SOLID|MF_SHOOTABLE|MF_SHADOW|MF_COUNTKILL, // flags + S_SARG_RAISE1 // raisestate + }, + + { // MT_HEAD + 3005, // doomednum + S_HEAD_STND, // spawnstate + 400, // spawnhealth + S_HEAD_RUN1, // seestate + sfx_cacsit, // seesound + 8, // reactiontime + 0, // attacksound + S_HEAD_PAIN, // painstate + 128, // painchance + sfx_dmpain, // painsound + 0, // meleestate + S_HEAD_ATK1, // missilestate + S_HEAD_DIE1, // deathstate + S_NULL, // xdeathstate + sfx_cacdth, // deathsound + 8, // speed + 31*FRACUNIT, // radius + 56*FRACUNIT, // height + 400, // mass + 0, // damage + sfx_dmact, // activesound + MF_SOLID|MF_SHOOTABLE|MF_FLOAT|MF_NOGRAVITY|MF_COUNTKILL, // flags + S_HEAD_RAISE1 // raisestate + }, + + { // MT_BRUISER + 3003, // doomednum + S_BOSS_STND, // spawnstate + 1000, // spawnhealth + S_BOSS_RUN1, // seestate + sfx_brssit, // seesound + 8, // reactiontime + 0, // attacksound + S_BOSS_PAIN, // painstate + 50, // painchance + sfx_dmpain, // painsound + S_BOSS_ATK1, // meleestate + S_BOSS_ATK1, // missilestate + S_BOSS_DIE1, // deathstate + S_NULL, // xdeathstate + sfx_brsdth, // deathsound + 8, // speed + 24*FRACUNIT, // radius + 64*FRACUNIT, // height + 1000, // mass + 0, // damage + sfx_dmact, // activesound + MF_SOLID|MF_SHOOTABLE|MF_COUNTKILL, // flags + S_BOSS_RAISE1 // raisestate + }, + + { // MT_BRUISERSHOT + -1, // doomednum + S_BRBALL1, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_firsht, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_BRBALLX1, // deathstate + S_NULL, // xdeathstate + sfx_firxpl, // deathsound + 15*FRACUNIT, // speed + 6*FRACUNIT, // radius + 8*FRACUNIT, // height + 100, // mass + 8, // damage + sfx_None, // activesound + MF_NOBLOCKMAP|MF_MISSILE|MF_DROPOFF|MF_NOGRAVITY|MF_TRANSLUCENT, // flags killough 2/21/98 + S_NULL // raisestate + }, + + { // MT_KNIGHT + 69, // doomednum + S_BOS2_STND, // spawnstate + 500, // spawnhealth + S_BOS2_RUN1, // seestate + sfx_kntsit, // seesound + 8, // reactiontime + 0, // attacksound + S_BOS2_PAIN, // painstate + 50, // painchance + sfx_dmpain, // painsound + S_BOS2_ATK1, // meleestate + S_BOS2_ATK1, // missilestate + S_BOS2_DIE1, // deathstate + S_NULL, // xdeathstate + sfx_kntdth, // deathsound + 8, // speed + 24*FRACUNIT, // radius + 64*FRACUNIT, // height + 1000, // mass + 0, // damage + sfx_dmact, // activesound + MF_SOLID|MF_SHOOTABLE|MF_COUNTKILL, // flags + S_BOS2_RAISE1 // raisestate + }, + + { // MT_SKULL + 3006, // doomednum + S_SKULL_STND, // spawnstate + 100, // spawnhealth + S_SKULL_RUN1, // seestate + 0, // seesound + 8, // reactiontime + sfx_sklatk, // attacksound + S_SKULL_PAIN, // painstate + 256, // painchance + sfx_dmpain, // painsound + 0, // meleestate + S_SKULL_ATK1, // missilestate + S_SKULL_DIE1, // deathstate + S_NULL, // xdeathstate + sfx_firxpl, // deathsound + 8, // speed + 16*FRACUNIT, // radius + 56*FRACUNIT, // height + 50, // mass + 3, // damage + sfx_dmact, // activesound + MF_SOLID|MF_SHOOTABLE|MF_FLOAT|MF_NOGRAVITY, // flags + S_NULL // raisestate + }, + + { // MT_SPIDER + 7, // doomednum + S_SPID_STND, // spawnstate + 3000, // spawnhealth + S_SPID_RUN1, // seestate + sfx_spisit, // seesound + 8, // reactiontime + sfx_shotgn, // attacksound + S_SPID_PAIN, // painstate + 40, // painchance + sfx_dmpain, // painsound + 0, // meleestate + S_SPID_ATK1, // missilestate + S_SPID_DIE1, // deathstate + S_NULL, // xdeathstate + sfx_spidth, // deathsound + 12, // speed + 128*FRACUNIT, // radius + 100*FRACUNIT, // height + 1000, // mass + 0, // damage + sfx_dmact, // activesound + MF_SOLID|MF_SHOOTABLE|MF_COUNTKILL, // flags + S_NULL // raisestate + }, + + { // MT_BABY + 68, // doomednum + S_BSPI_STND, // spawnstate + 500, // spawnhealth + S_BSPI_SIGHT, // seestate + sfx_bspsit, // seesound + 8, // reactiontime + 0, // attacksound + S_BSPI_PAIN, // painstate + 128, // painchance + sfx_dmpain, // painsound + 0, // meleestate + S_BSPI_ATK1, // missilestate + S_BSPI_DIE1, // deathstate + S_NULL, // xdeathstate + sfx_bspdth, // deathsound + 12, // speed + 64*FRACUNIT, // radius + 64*FRACUNIT, // height + 600, // mass + 0, // damage + sfx_bspact, // activesound + MF_SOLID|MF_SHOOTABLE|MF_COUNTKILL, // flags + S_BSPI_RAISE1 // raisestate + }, + + { // MT_CYBORG + 16, // doomednum + S_CYBER_STND, // spawnstate + 4000, // spawnhealth + S_CYBER_RUN1, // seestate + sfx_cybsit, // seesound + 8, // reactiontime + 0, // attacksound + S_CYBER_PAIN, // painstate + 20, // painchance + sfx_dmpain, // painsound + 0, // meleestate + S_CYBER_ATK1, // missilestate + S_CYBER_DIE1, // deathstate + S_NULL, // xdeathstate + sfx_cybdth, // deathsound + 16, // speed + 40*FRACUNIT, // radius + 110*FRACUNIT, // height + 1000, // mass + 0, // damage + sfx_dmact, // activesound + MF_SOLID|MF_SHOOTABLE|MF_COUNTKILL, // flags + S_NULL // raisestate + }, + + { // MT_PAIN + 71, // doomednum + S_PAIN_STND, // spawnstate + 400, // spawnhealth + S_PAIN_RUN1, // seestate + sfx_pesit, // seesound + 8, // reactiontime + 0, // attacksound + S_PAIN_PAIN, // painstate + 128, // painchance + sfx_pepain, // painsound + 0, // meleestate + S_PAIN_ATK1, // missilestate + S_PAIN_DIE1, // deathstate + S_NULL, // xdeathstate + sfx_pedth, // deathsound + 8, // speed + 31*FRACUNIT, // radius + 56*FRACUNIT, // height + 400, // mass + 0, // damage + sfx_dmact, // activesound + MF_SOLID|MF_SHOOTABLE|MF_FLOAT|MF_NOGRAVITY|MF_COUNTKILL, // flags + S_PAIN_RAISE1 // raisestate + }, + + { // MT_WOLFSS + 84, // doomednum + S_SSWV_STND, // spawnstate + 50, // spawnhealth + S_SSWV_RUN1, // seestate + sfx_sssit, // seesound + 8, // reactiontime + 0, // attacksound + S_SSWV_PAIN, // painstate + 170, // painchance + sfx_popain, // painsound + 0, // meleestate + S_SSWV_ATK1, // missilestate + S_SSWV_DIE1, // deathstate + S_SSWV_XDIE1, // xdeathstate + sfx_ssdth, // deathsound + 8, // speed + 20*FRACUNIT, // radius + 56*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_posact, // activesound + MF_SOLID|MF_SHOOTABLE|MF_COUNTKILL, // flags + S_SSWV_RAISE1 // raisestate + }, + + { // MT_KEEN + 72, // doomednum + S_KEENSTND, // spawnstate + 100, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_KEENPAIN, // painstate + 256, // painchance + sfx_keenpn, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_COMMKEEN, // deathstate + S_NULL, // xdeathstate + sfx_keendt, // deathsound + 0, // speed + 16*FRACUNIT, // radius + 72*FRACUNIT, // height + 10000000, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID|MF_SPAWNCEILING|MF_NOGRAVITY|MF_SHOOTABLE|MF_COUNTKILL, // flags + S_NULL // raisestate + }, + + { // MT_BOSSBRAIN + 88, // doomednum + S_BRAIN, // spawnstate + 250, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_BRAIN_PAIN, // painstate + 255, // painchance + sfx_bospn, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_BRAIN_DIE1, // deathstate + S_NULL, // xdeathstate + sfx_bosdth, // deathsound + 0, // speed + 16*FRACUNIT, // radius + 16*FRACUNIT, // height + 10000000, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID|MF_SHOOTABLE, // flags + S_NULL // raisestate + }, + + { // MT_BOSSSPIT + 89, // doomednum + S_BRAINEYE, // spawnstate + 1000, // spawnhealth + S_BRAINEYESEE, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20*FRACUNIT, // radius + 32*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_NOBLOCKMAP|MF_NOSECTOR, // flags + S_NULL // raisestate + }, + + { // MT_BOSSTARGET + 87, // doomednum + S_NULL, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20*FRACUNIT, // radius + 32*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_NOBLOCKMAP|MF_NOSECTOR, // flags + S_NULL // raisestate + }, + + { // MT_SPAWNSHOT + -1, // doomednum + S_SPAWN1, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_bospit, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_firxpl, // deathsound + 10*FRACUNIT, // speed + 6*FRACUNIT, // radius + 32*FRACUNIT, // height + 100, // mass + 3, // damage + sfx_None, // activesound + MF_NOBLOCKMAP|MF_MISSILE|MF_DROPOFF|MF_NOGRAVITY|MF_NOCLIP, // flags + S_NULL // raisestate + }, + + { // MT_SPAWNFIRE + -1, // doomednum + S_SPAWNFIRE1, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_NOBLOCKMAP|MF_NOGRAVITY|MF_TRANSLUCENT, // flags // phares + S_NULL // raisestate + }, + + { // MT_BARREL + 2035, // doomednum + S_BAR1, // spawnstate + 20, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_BEXP, // deathstate + S_NULL, // xdeathstate + sfx_barexp, // deathsound + 0, // speed + 10*FRACUNIT, // radius + 42*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID|MF_SHOOTABLE|MF_NOBLOOD, // flags + S_NULL // raisestate + }, + + { // MT_TROOPSHOT + -1, // doomednum + S_TBALL1, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_firsht, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_TBALLX1, // deathstate + S_NULL, // xdeathstate + sfx_firxpl, // deathsound + 10*FRACUNIT, // speed + 6*FRACUNIT, // radius + 8*FRACUNIT, // height + 100, // mass + 3, // damage + sfx_None, // activesound + MF_NOBLOCKMAP|MF_MISSILE|MF_DROPOFF|MF_NOGRAVITY|MF_TRANSLUCENT, // flags // phares + S_NULL // raisestate + }, + + { // MT_HEADSHOT + -1, // doomednum + S_RBALL1, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_firsht, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_RBALLX1, // deathstate + S_NULL, // xdeathstate + sfx_firxpl, // deathsound + 10*FRACUNIT, // speed + 6*FRACUNIT, // radius + 8*FRACUNIT, // height + 100, // mass + 5, // damage + sfx_None, // activesound + MF_NOBLOCKMAP|MF_MISSILE|MF_DROPOFF|MF_NOGRAVITY|MF_TRANSLUCENT, // flags // phares, // flags + S_NULL // raisestate + }, + + { // MT_ROCKET + -1, // doomednum + S_ROCKET, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_rlaunc, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_EXPLODE1, // deathstate + S_NULL, // xdeathstate + sfx_barexp, // deathsound + 20*FRACUNIT, // speed + 11*FRACUNIT, // radius + 8*FRACUNIT, // height + 100, // mass + 20, // damage + sfx_None, // activesound + MF_NOBLOCKMAP|MF_MISSILE|MF_DROPOFF|MF_NOGRAVITY, // flags + S_NULL // raisestate + }, + + { // MT_PLASMA + -1, // doomednum + S_PLASBALL, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_plasma, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_PLASEXP, // deathstate + S_NULL, // xdeathstate + sfx_firxpl, // deathsound + 25*FRACUNIT, // speed + 13*FRACUNIT, // radius + 8*FRACUNIT, // height + 100, // mass + 5, // damage + sfx_None, // activesound + MF_NOBLOCKMAP|MF_MISSILE|MF_DROPOFF|MF_NOGRAVITY|MF_TRANSLUCENT, // flags // phares + S_NULL // raisestate + }, + + { // MT_BFG + -1, // doomednum + S_BFGSHOT, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + 0, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_BFGLAND, // deathstate + S_NULL, // xdeathstate + sfx_rxplod, // deathsound + 25*FRACUNIT, // speed + 13*FRACUNIT, // radius + 8*FRACUNIT, // height + 100, // mass + 100, // damage + sfx_None, // activesound + MF_NOBLOCKMAP|MF_MISSILE|MF_DROPOFF|MF_NOGRAVITY|MF_TRANSLUCENT, // flags // phares + S_NULL // raisestate + }, + + { // MT_ARACHPLAZ + -1, // doomednum + S_ARACH_PLAZ, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_plasma, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_ARACH_PLEX, // deathstate + S_NULL, // xdeathstate + sfx_firxpl, // deathsound + 25*FRACUNIT, // speed + 13*FRACUNIT, // radius + 8*FRACUNIT, // height + 100, // mass + 5, // damage + sfx_None, // activesound + MF_NOBLOCKMAP|MF_MISSILE|MF_DROPOFF|MF_NOGRAVITY|MF_TRANSLUCENT, // flags // phares + S_NULL // raisestate + }, + + { // MT_PUFF + -1, // doomednum + S_PUFF1, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_NOBLOCKMAP|MF_NOGRAVITY|MF_TRANSLUCENT, // flags // phares + S_NULL // raisestate + }, + + { // MT_BLOOD + -1, // doomednum + S_BLOOD1, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_NOBLOCKMAP, // flags + S_NULL // raisestate + }, + + { // MT_TFOG + -1, // doomednum + S_TFOG, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_NOBLOCKMAP|MF_NOGRAVITY|MF_TRANSLUCENT, // flags // phares + S_NULL // raisestate + }, + + { // MT_IFOG + -1, // doomednum + S_IFOG, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_NOBLOCKMAP|MF_NOGRAVITY|MF_TRANSLUCENT, // flags // phares + S_NULL // raisestate + }, + + { // MT_TELEPORTMAN + 14, // doomednum + S_NULL, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_NOBLOCKMAP|MF_NOSECTOR, // flags + S_NULL // raisestate + }, + + { // MT_EXTRABFG + -1, // doomednum + S_BFGEXP, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_NOBLOCKMAP|MF_NOGRAVITY, // flags + S_NULL // raisestate + }, + + { // MT_MISC0 + 2018, // doomednum + S_ARM1, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SPECIAL, // flags + S_NULL // raisestate + }, + + { // MT_MISC1 + 2019, // doomednum + S_ARM2, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SPECIAL, // flags + S_NULL // raisestate + }, + + { // MT_MISC2 + 2014, // doomednum + S_BON1, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SPECIAL|MF_COUNTITEM, // flags + S_NULL // raisestate + }, + + { // MT_MISC3 + 2015, // doomednum + S_BON2, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SPECIAL|MF_COUNTITEM, // flags + S_NULL // raisestate + }, + + { // MT_MISC4 + 5, // doomednum + S_BKEY, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SPECIAL|MF_NOTDMATCH, // flags + S_NULL // raisestate + }, + + { // MT_MISC5 + 13, // doomednum + S_RKEY, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SPECIAL|MF_NOTDMATCH, // flags + S_NULL // raisestate + }, + + { // MT_MISC6 + 6, // doomednum + S_YKEY, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SPECIAL|MF_NOTDMATCH, // flags + S_NULL // raisestate + }, + + { // MT_MISC7 + 39, // doomednum + S_YSKULL, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SPECIAL|MF_NOTDMATCH, // flags + S_NULL // raisestate + }, + + { // MT_MISC8 + 38, // doomednum + S_RSKULL, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SPECIAL|MF_NOTDMATCH, // flags + S_NULL // raisestate + }, + + { // MT_MISC9 + 40, // doomednum + S_BSKULL, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SPECIAL|MF_NOTDMATCH, // flags + S_NULL // raisestate + }, + + { // MT_MISC10 + 2011, // doomednum + S_STIM, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SPECIAL, // flags + S_NULL // raisestate + }, + + { // MT_MISC11 + 2012, // doomednum + S_MEDI, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SPECIAL, // flags + S_NULL // raisestate + }, + + { // MT_MISC12 + 2013, // doomednum + S_SOUL, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SPECIAL|MF_COUNTITEM|MF_TRANSLUCENT, // flags // killough 2/21/98 + S_NULL // raisestate + }, + + { // MT_INV + 2022, // doomednum + S_PINV, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SPECIAL|MF_COUNTITEM|MF_TRANSLUCENT, // flags // killough 2/21/98 + S_NULL // raisestate + }, + + { // MT_MISC13 + 2023, // doomednum + S_PSTR, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SPECIAL|MF_COUNTITEM, // flags + S_NULL // raisestate + }, + + { // MT_INS + 2024, // doomednum + S_PINS, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SPECIAL|MF_COUNTITEM|MF_TRANSLUCENT, // flags // killough 2/21/98 + S_NULL // raisestate + }, + + { // MT_MISC14 + 2025, // doomednum + S_SUIT, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SPECIAL, // flags + S_NULL // raisestate + }, + + { // MT_MISC15 + 2026, // doomednum + S_PMAP, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SPECIAL|MF_COUNTITEM, // flags + S_NULL // raisestate + }, + + { // MT_MISC16 + 2045, // doomednum + S_PVIS, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SPECIAL|MF_COUNTITEM, // flags + S_NULL // raisestate + }, + + { // MT_MEGA + 83, // doomednum + S_MEGA, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SPECIAL|MF_COUNTITEM|MF_TRANSLUCENT, // flags // killough 2/21/98 + S_NULL // raisestate + }, + + { // MT_CLIP + 2007, // doomednum + S_CLIP, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SPECIAL, // flags + S_NULL // raisestate + }, + + { // MT_MISC17 + 2048, // doomednum + S_AMMO, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SPECIAL, // flags + S_NULL // raisestate + }, + + { // MT_MISC18 + 2010, // doomednum + S_ROCK, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SPECIAL, // flags + S_NULL // raisestate + }, + + { // MT_MISC19 + 2046, // doomednum + S_BROK, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SPECIAL, // flags + S_NULL // raisestate + }, + + { // MT_MISC20 + 2047, // doomednum + S_CELL, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SPECIAL, // flags + S_NULL // raisestate + }, + + { // MT_MISC21 + 17, // doomednum + S_CELP, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SPECIAL, // flags + S_NULL // raisestate + }, + + { // MT_MISC22 + 2008, // doomednum + S_SHEL, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SPECIAL, // flags + S_NULL // raisestate + }, + + { // MT_MISC23 + 2049, // doomednum + S_SBOX, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SPECIAL, // flags + S_NULL // raisestate + }, + + { // MT_MISC24 + 8, // doomednum + S_BPAK, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SPECIAL, // flags + S_NULL // raisestate + }, + + { // MT_MISC25 + 2006, // doomednum + S_BFUG, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SPECIAL, // flags + S_NULL // raisestate + }, + + { // MT_CHAINGUN + 2002, // doomednum + S_MGUN, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SPECIAL, // flags + S_NULL // raisestate + }, + + { // MT_MISC26 + 2005, // doomednum + S_CSAW, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SPECIAL, // flags + S_NULL // raisestate + }, + + { // MT_MISC27 + 2003, // doomednum + S_LAUN, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SPECIAL, // flags + S_NULL // raisestate + }, + + { // MT_MISC28 + 2004, // doomednum + S_PLAS, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SPECIAL, // flags + S_NULL // raisestate + }, + + { // MT_SHOTGUN + 2001, // doomednum + S_SHOT, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SPECIAL, // flags + S_NULL // raisestate + }, + + { // MT_SUPERSHOTGUN + 82, // doomednum + S_SHOT2, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SPECIAL, // flags + S_NULL // raisestate + }, + + { // MT_MISC29 + 85, // doomednum + S_TECHLAMP, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 16*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID, // flags + S_NULL // raisestate + }, + + { // MT_MISC30 + 86, // doomednum + S_TECH2LAMP, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 16*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID, // flags + S_NULL // raisestate + }, + + { // MT_MISC31 + 2028, // doomednum + S_COLU, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 16*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID, // flags + S_NULL // raisestate + }, + + { // MT_MISC32 + 30, // doomednum + S_TALLGRNCOL, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 16*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID, // flags + S_NULL // raisestate + }, + + { // MT_MISC33 + 31, // doomednum + S_SHRTGRNCOL, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 16*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID, // flags + S_NULL // raisestate + }, + + { // MT_MISC34 + 32, // doomednum + S_TALLREDCOL, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 16*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID, // flags + S_NULL // raisestate + }, + + { // MT_MISC35 + 33, // doomednum + S_SHRTREDCOL, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 16*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID, // flags + S_NULL // raisestate + }, + + { // MT_MISC36 + 37, // doomednum + S_SKULLCOL, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 16*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID, // flags + S_NULL // raisestate + }, + + { // MT_MISC37 + 36, // doomednum + S_HEARTCOL, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 16*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID, // flags + S_NULL // raisestate + }, + + { // MT_MISC38 + 41, // doomednum + S_EVILEYE, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 16*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID, // flags + S_NULL // raisestate + }, + + { // MT_MISC39 + 42, // doomednum + S_FLOATSKULL, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 16*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID, // flags + S_NULL // raisestate + }, + + { // MT_MISC40 + 43, // doomednum + S_TORCHTREE, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 16*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID, // flags + S_NULL // raisestate + }, + + { // MT_MISC41 + 44, // doomednum + S_BLUETORCH, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 16*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID, // flags + S_NULL // raisestate + }, + + { // MT_MISC42 + 45, // doomednum + S_GREENTORCH, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 16*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID, // flags + S_NULL // raisestate + }, + + { // MT_MISC43 + 46, // doomednum + S_REDTORCH, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 16*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID, // flags + S_NULL // raisestate + }, + + { // MT_MISC44 + 55, // doomednum + S_BTORCHSHRT, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 16*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID, // flags + S_NULL // raisestate + }, + + { // MT_MISC45 + 56, // doomednum + S_GTORCHSHRT, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 16*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID, // flags + S_NULL // raisestate + }, + + { // MT_MISC46 + 57, // doomednum + S_RTORCHSHRT, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 16*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID, // flags + S_NULL // raisestate + }, + + { // MT_MISC47 + 47, // doomednum + S_STALAGTITE, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 16*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID, // flags + S_NULL // raisestate + }, + + { // MT_MISC48 + 48, // doomednum + S_TECHPILLAR, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 16*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID, // flags + S_NULL // raisestate + }, + + { // MT_MISC49 + 34, // doomednum + S_CANDLESTIK, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + 0, // flags + S_NULL // raisestate + }, + + { // MT_MISC50 + 35, // doomednum + S_CANDELABRA, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 16*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID, // flags + S_NULL // raisestate + }, + + { // MT_MISC51 + 49, // doomednum + S_BLOODYTWITCH, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 16*FRACUNIT, // radius + 68*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID|MF_SPAWNCEILING|MF_NOGRAVITY, // flags + S_NULL // raisestate + }, + + { // MT_MISC52 + 50, // doomednum + S_MEAT2, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 16*FRACUNIT, // radius + 84*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID|MF_SPAWNCEILING|MF_NOGRAVITY, // flags + S_NULL // raisestate + }, + + { // MT_MISC53 + 51, // doomednum + S_MEAT3, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 16*FRACUNIT, // radius + 84*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID|MF_SPAWNCEILING|MF_NOGRAVITY, // flags + S_NULL // raisestate + }, + + { // MT_MISC54 + 52, // doomednum + S_MEAT4, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 16*FRACUNIT, // radius + 68*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID|MF_SPAWNCEILING|MF_NOGRAVITY, // flags + S_NULL // raisestate + }, + + { // MT_MISC55 + 53, // doomednum + S_MEAT5, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 16*FRACUNIT, // radius + 52*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID|MF_SPAWNCEILING|MF_NOGRAVITY, // flags + S_NULL // raisestate + }, + + { // MT_MISC56 + 59, // doomednum + S_MEAT2, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20*FRACUNIT, // radius + 84*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SPAWNCEILING|MF_NOGRAVITY, // flags + S_NULL // raisestate + }, + + { // MT_MISC57 + 60, // doomednum + S_MEAT4, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20*FRACUNIT, // radius + 68*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SPAWNCEILING|MF_NOGRAVITY, // flags + S_NULL // raisestate + }, + + { // MT_MISC58 + 61, // doomednum + S_MEAT3, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20*FRACUNIT, // radius + 52*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SPAWNCEILING|MF_NOGRAVITY, // flags + S_NULL // raisestate + }, + + { // MT_MISC59 + 62, // doomednum + S_MEAT5, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20*FRACUNIT, // radius + 52*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SPAWNCEILING|MF_NOGRAVITY, // flags + S_NULL // raisestate + }, + + { // MT_MISC60 + 63, // doomednum + S_BLOODYTWITCH, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20*FRACUNIT, // radius + 68*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SPAWNCEILING|MF_NOGRAVITY, // flags + S_NULL // raisestate + }, + + { // MT_MISC61 + 22, // doomednum + S_HEAD_DIE6, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + 0, // flags + S_NULL // raisestate + }, + + { // MT_MISC62 + 15, // doomednum + S_PLAY_DIE7, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + 0, // flags + S_NULL // raisestate + }, + + { // MT_MISC63 + 18, // doomednum + S_POSS_DIE5, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + 0, // flags + S_NULL // raisestate + }, + + { // MT_MISC64 + 21, // doomednum + S_SARG_DIE6, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + 0, // flags + S_NULL // raisestate + }, + + { // MT_MISC65 + 23, // doomednum + S_SKULL_DIE6, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + 0, // flags + S_NULL // raisestate + }, + + { // MT_MISC66 + 20, // doomednum + S_TROO_DIE5, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + 0, // flags + S_NULL // raisestate + }, + + { // MT_MISC67 + 19, // doomednum + S_SPOS_DIE5, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + 0, // flags + S_NULL // raisestate + }, + + { // MT_MISC68 + 10, // doomednum + S_PLAY_XDIE9, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + 0, // flags + S_NULL // raisestate + }, + + { // MT_MISC69 + 12, // doomednum + S_PLAY_XDIE9, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + 0, // flags + S_NULL // raisestate + }, + + { // MT_MISC70 + 28, // doomednum + S_HEADSONSTICK, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 16*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID, // flags + S_NULL // raisestate + }, + + { // MT_MISC71 + 24, // doomednum + S_GIBS, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + 0, // flags + S_NULL // raisestate + }, + + { // MT_MISC72 + 27, // doomednum + S_HEADONASTICK, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 16*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID, // flags + S_NULL // raisestate + }, + + { // MT_MISC73 + 29, // doomednum + S_HEADCANDLES, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 16*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID, // flags + S_NULL // raisestate + }, + + { // MT_MISC74 + 25, // doomednum + S_DEADSTICK, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 16*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID, // flags + S_NULL // raisestate + }, + + { // MT_MISC75 + 26, // doomednum + S_LIVESTICK, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 16*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID, // flags + S_NULL // raisestate + }, + + { // MT_MISC76 + 54, // doomednum + S_BIGTREE, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 32*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID, // flags + S_NULL // raisestate + }, + + { // MT_MISC77 + 70, // doomednum + S_BBAR1, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 16*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID, // flags + S_NULL // raisestate + }, + + { // MT_MISC78 + 73, // doomednum + S_HANGNOGUTS, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 16*FRACUNIT, // radius + 88*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID|MF_SPAWNCEILING|MF_NOGRAVITY, // flags + S_NULL // raisestate + }, + + { // MT_MISC79 + 74, // doomednum + S_HANGBNOBRAIN, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 16*FRACUNIT, // radius + 88*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID|MF_SPAWNCEILING|MF_NOGRAVITY, // flags + S_NULL // raisestate + }, + + { // MT_MISC80 + 75, // doomednum + S_HANGTLOOKDN, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 16*FRACUNIT, // radius + 64*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID|MF_SPAWNCEILING|MF_NOGRAVITY, // flags + S_NULL // raisestate + }, + + { // MT_MISC81 + 76, // doomednum + S_HANGTSKULL, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 16*FRACUNIT, // radius + 64*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID|MF_SPAWNCEILING|MF_NOGRAVITY, // flags + S_NULL // raisestate + }, + + { // MT_MISC82 + 77, // doomednum + S_HANGTLOOKUP, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 16*FRACUNIT, // radius + 64*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID|MF_SPAWNCEILING|MF_NOGRAVITY, // flags + S_NULL // raisestate + }, + + { // MT_MISC83 + 78, // doomednum + S_HANGTNOBRAIN, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 16*FRACUNIT, // radius + 64*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID|MF_SPAWNCEILING|MF_NOGRAVITY, // flags + S_NULL // raisestate + }, + + { // MT_MISC84 + 79, // doomednum + S_COLONGIBS, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_NOBLOCKMAP, // flags + S_NULL // raisestate + }, + + { // MT_MISC85 + 80, // doomednum + S_SMALLPOOL, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_NOBLOCKMAP, // flags + S_NULL // raisestate + }, + + { // MT_MISC86 + 81, // doomednum + S_BRAINSTEM, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_NOBLOCKMAP, // flags + S_NULL // raisestate + }, + + // For use with wind and current effects + { // MT_PUSH // phares + 5001, // doomednum // | //jff 5/11/98 deconflict + S_TNT1, // spawnstate // V // with DOSDoom + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 8, // radius + 8, // height + 10, // mass + 0, // damage + sfx_None, // activesound + MF_NOBLOCKMAP, // flags + S_NULL // raisestate + }, + + // For use with wind and current effects + { // MT_PULL + 5002, // doomednum //jff 5/11/98 deconflict + S_TNT1, // spawnstate // with DOSDoom + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 8, // radius + 8, // height + 10, // mass + 0, // damage + sfx_None, // activesound + MF_NOBLOCKMAP, // flags + S_NULL // raisestate + }, +#ifdef DOGS + // Marine's best friend :) // killough 7/19/98 + { // MT_DOGS + 888, // doomednum + S_DOGS_STND, // spawnstate + 500, // spawnhealth + S_DOGS_RUN1, // seestate + sfx_dgsit, // seesound + 8, // reactiontime + sfx_dgatk, // attacksound + S_DOGS_PAIN, // painstate + 180, // painchance + sfx_dgpain, // painsound + S_DOGS_ATK1, // meleestate + 0, // missilestate + S_DOGS_DIE1, // deathstate + S_NULL, // xdeathstate + sfx_dgdth, // deathsound + 10, // speed + 12*FRACUNIT, // radius + 28*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_dgact, // activesound + MF_SOLID|MF_SHOOTABLE|MF_COUNTKILL, // flags + S_DOGS_RAISE1 // raisestate + }, +#endif +}; diff --git a/src/info.h b/src/info.h new file mode 100644 index 0000000..11cfb47 --- /dev/null +++ b/src/info.h @@ -0,0 +1,1498 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Thing frame/state LUT, + * generated by multigen utilitiy. + * This one is the original DOOM version, preserved. + * + *-----------------------------------------------------------------------------*/ + +#ifndef __INFO__ +#define __INFO__ + +/* Needed for action function pointer handling. */ +#include "d_think.h" + +/******************************************************************** + * Sprite name enumeration - must match info.c * + ********************************************************************/ +typedef enum +{ + SPR_TROO, + SPR_SHTG, + SPR_PUNG, + SPR_PISG, + SPR_PISF, + SPR_SHTF, + SPR_SHT2, + SPR_CHGG, + SPR_CHGF, + SPR_MISG, + SPR_MISF, + SPR_SAWG, + SPR_PLSG, + SPR_PLSF, + SPR_BFGG, + SPR_BFGF, + SPR_BLUD, + SPR_PUFF, + SPR_BAL1, + SPR_BAL2, + SPR_PLSS, + SPR_PLSE, + SPR_MISL, + SPR_BFS1, + SPR_BFE1, + SPR_BFE2, + SPR_TFOG, + SPR_IFOG, + SPR_PLAY, + SPR_POSS, + SPR_SPOS, + SPR_VILE, + SPR_FIRE, + SPR_FATB, + SPR_FBXP, + SPR_SKEL, + SPR_MANF, + SPR_FATT, + SPR_CPOS, + SPR_SARG, + SPR_HEAD, + SPR_BAL7, + SPR_BOSS, + SPR_BOS2, + SPR_SKUL, + SPR_SPID, + SPR_BSPI, + SPR_APLS, + SPR_APBX, + SPR_CYBR, + SPR_PAIN, + SPR_SSWV, + SPR_KEEN, + SPR_BBRN, + SPR_BOSF, + SPR_ARM1, + SPR_ARM2, + SPR_BAR1, + SPR_BEXP, + SPR_FCAN, + SPR_BON1, + SPR_BON2, + SPR_BKEY, + SPR_RKEY, + SPR_YKEY, + SPR_BSKU, + SPR_RSKU, + SPR_YSKU, + SPR_STIM, + SPR_MEDI, + SPR_SOUL, + SPR_PINV, + SPR_PSTR, + SPR_PINS, + SPR_MEGA, + SPR_SUIT, + SPR_PMAP, + SPR_PVIS, + SPR_CLIP, + SPR_AMMO, + SPR_ROCK, + SPR_BROK, + SPR_CELL, + SPR_CELP, + SPR_SHEL, + SPR_SBOX, + SPR_BPAK, + SPR_BFUG, + SPR_MGUN, + SPR_CSAW, + SPR_LAUN, + SPR_PLAS, + SPR_SHOT, + SPR_SGN2, + SPR_COLU, + SPR_SMT2, + SPR_GOR1, + SPR_POL2, + SPR_POL5, + SPR_POL4, + SPR_POL3, + SPR_POL1, + SPR_POL6, + SPR_GOR2, + SPR_GOR3, + SPR_GOR4, + SPR_GOR5, + SPR_SMIT, + SPR_COL1, + SPR_COL2, + SPR_COL3, + SPR_COL4, + SPR_CAND, + SPR_CBRA, + SPR_COL6, + SPR_TRE1, + SPR_TRE2, + SPR_ELEC, + SPR_CEYE, + SPR_FSKU, + SPR_COL5, + SPR_TBLU, + SPR_TGRN, + SPR_TRED, + SPR_SMBT, + SPR_SMGT, + SPR_SMRT, + SPR_HDB1, + SPR_HDB2, + SPR_HDB3, + SPR_HDB4, + SPR_HDB5, + SPR_HDB6, + SPR_POB1, + SPR_POB2, + SPR_BRS1, + SPR_TLMP, + SPR_TLP2, + SPR_TNT1, /* add invisible sprite phares 3/8/98 */ + +#ifdef DOGS + SPR_DOGS, /* killough 7/19/98: Marine's best friend :) */ +#endif + + NUMSPRITES /* counter of how many there are */ + +} spritenum_t; + +/******************************************************************** + * States (frames) enumeration -- must match info.c * + ********************************************************************/ + +typedef enum +{ + S_NULL, + S_LIGHTDONE, + S_PUNCH, + S_PUNCHDOWN, + S_PUNCHUP, + S_PUNCH1, + S_PUNCH2, + S_PUNCH3, + S_PUNCH4, + S_PUNCH5, + S_PISTOL, + S_PISTOLDOWN, + S_PISTOLUP, + S_PISTOL1, + S_PISTOL2, + S_PISTOL3, + S_PISTOL4, + S_PISTOLFLASH, + S_SGUN, + S_SGUNDOWN, + S_SGUNUP, + S_SGUN1, + S_SGUN2, + S_SGUN3, + S_SGUN4, + S_SGUN5, + S_SGUN6, + S_SGUN7, + S_SGUN8, + S_SGUN9, + S_SGUNFLASH1, + S_SGUNFLASH2, + S_DSGUN, + S_DSGUNDOWN, + S_DSGUNUP, + S_DSGUN1, + S_DSGUN2, + S_DSGUN3, + S_DSGUN4, + S_DSGUN5, + S_DSGUN6, + S_DSGUN7, + S_DSGUN8, + S_DSGUN9, + S_DSGUN10, + S_DSNR1, + S_DSNR2, + S_DSGUNFLASH1, + S_DSGUNFLASH2, + S_CHAIN, + S_CHAINDOWN, + S_CHAINUP, + S_CHAIN1, + S_CHAIN2, + S_CHAIN3, + S_CHAINFLASH1, + S_CHAINFLASH2, + S_MISSILE, + S_MISSILEDOWN, + S_MISSILEUP, + S_MISSILE1, + S_MISSILE2, + S_MISSILE3, + S_MISSILEFLASH1, + S_MISSILEFLASH2, + S_MISSILEFLASH3, + S_MISSILEFLASH4, + S_SAW, + S_SAWB, + S_SAWDOWN, + S_SAWUP, + S_SAW1, + S_SAW2, + S_SAW3, + S_PLASMA, + S_PLASMADOWN, + S_PLASMAUP, + S_PLASMA1, + S_PLASMA2, + S_PLASMAFLASH1, + S_PLASMAFLASH2, + S_BFG, + S_BFGDOWN, + S_BFGUP, + S_BFG1, + S_BFG2, + S_BFG3, + S_BFG4, + S_BFGFLASH1, + S_BFGFLASH2, + S_BLOOD1, + S_BLOOD2, + S_BLOOD3, + S_PUFF1, + S_PUFF2, + S_PUFF3, + S_PUFF4, + S_TBALL1, + S_TBALL2, + S_TBALLX1, + S_TBALLX2, + S_TBALLX3, + S_RBALL1, + S_RBALL2, + S_RBALLX1, + S_RBALLX2, + S_RBALLX3, + S_PLASBALL, + S_PLASBALL2, + S_PLASEXP, + S_PLASEXP2, + S_PLASEXP3, + S_PLASEXP4, + S_PLASEXP5, + S_ROCKET, + S_BFGSHOT, + S_BFGSHOT2, + S_BFGLAND, + S_BFGLAND2, + S_BFGLAND3, + S_BFGLAND4, + S_BFGLAND5, + S_BFGLAND6, + S_BFGEXP, + S_BFGEXP2, + S_BFGEXP3, + S_BFGEXP4, + S_EXPLODE1, + S_EXPLODE2, + S_EXPLODE3, + S_TFOG, + S_TFOG01, + S_TFOG02, + S_TFOG2, + S_TFOG3, + S_TFOG4, + S_TFOG5, + S_TFOG6, + S_TFOG7, + S_TFOG8, + S_TFOG9, + S_TFOG10, + S_IFOG, + S_IFOG01, + S_IFOG02, + S_IFOG2, + S_IFOG3, + S_IFOG4, + S_IFOG5, + S_PLAY, + S_PLAY_RUN1, + S_PLAY_RUN2, + S_PLAY_RUN3, + S_PLAY_RUN4, + S_PLAY_ATK1, + S_PLAY_ATK2, + S_PLAY_PAIN, + S_PLAY_PAIN2, + S_PLAY_DIE1, + S_PLAY_DIE2, + S_PLAY_DIE3, + S_PLAY_DIE4, + S_PLAY_DIE5, + S_PLAY_DIE6, + S_PLAY_DIE7, + S_PLAY_XDIE1, + S_PLAY_XDIE2, + S_PLAY_XDIE3, + S_PLAY_XDIE4, + S_PLAY_XDIE5, + S_PLAY_XDIE6, + S_PLAY_XDIE7, + S_PLAY_XDIE8, + S_PLAY_XDIE9, + S_POSS_STND, + S_POSS_STND2, + S_POSS_RUN1, + S_POSS_RUN2, + S_POSS_RUN3, + S_POSS_RUN4, + S_POSS_RUN5, + S_POSS_RUN6, + S_POSS_RUN7, + S_POSS_RUN8, + S_POSS_ATK1, + S_POSS_ATK2, + S_POSS_ATK3, + S_POSS_PAIN, + S_POSS_PAIN2, + S_POSS_DIE1, + S_POSS_DIE2, + S_POSS_DIE3, + S_POSS_DIE4, + S_POSS_DIE5, + S_POSS_XDIE1, + S_POSS_XDIE2, + S_POSS_XDIE3, + S_POSS_XDIE4, + S_POSS_XDIE5, + S_POSS_XDIE6, + S_POSS_XDIE7, + S_POSS_XDIE8, + S_POSS_XDIE9, + S_POSS_RAISE1, + S_POSS_RAISE2, + S_POSS_RAISE3, + S_POSS_RAISE4, + S_SPOS_STND, + S_SPOS_STND2, + S_SPOS_RUN1, + S_SPOS_RUN2, + S_SPOS_RUN3, + S_SPOS_RUN4, + S_SPOS_RUN5, + S_SPOS_RUN6, + S_SPOS_RUN7, + S_SPOS_RUN8, + S_SPOS_ATK1, + S_SPOS_ATK2, + S_SPOS_ATK3, + S_SPOS_PAIN, + S_SPOS_PAIN2, + S_SPOS_DIE1, + S_SPOS_DIE2, + S_SPOS_DIE3, + S_SPOS_DIE4, + S_SPOS_DIE5, + S_SPOS_XDIE1, + S_SPOS_XDIE2, + S_SPOS_XDIE3, + S_SPOS_XDIE4, + S_SPOS_XDIE5, + S_SPOS_XDIE6, + S_SPOS_XDIE7, + S_SPOS_XDIE8, + S_SPOS_XDIE9, + S_SPOS_RAISE1, + S_SPOS_RAISE2, + S_SPOS_RAISE3, + S_SPOS_RAISE4, + S_SPOS_RAISE5, + S_VILE_STND, + S_VILE_STND2, + S_VILE_RUN1, + S_VILE_RUN2, + S_VILE_RUN3, + S_VILE_RUN4, + S_VILE_RUN5, + S_VILE_RUN6, + S_VILE_RUN7, + S_VILE_RUN8, + S_VILE_RUN9, + S_VILE_RUN10, + S_VILE_RUN11, + S_VILE_RUN12, + S_VILE_ATK1, + S_VILE_ATK2, + S_VILE_ATK3, + S_VILE_ATK4, + S_VILE_ATK5, + S_VILE_ATK6, + S_VILE_ATK7, + S_VILE_ATK8, + S_VILE_ATK9, + S_VILE_ATK10, + S_VILE_ATK11, + S_VILE_HEAL1, + S_VILE_HEAL2, + S_VILE_HEAL3, + S_VILE_PAIN, + S_VILE_PAIN2, + S_VILE_DIE1, + S_VILE_DIE2, + S_VILE_DIE3, + S_VILE_DIE4, + S_VILE_DIE5, + S_VILE_DIE6, + S_VILE_DIE7, + S_VILE_DIE8, + S_VILE_DIE9, + S_VILE_DIE10, + S_FIRE1, + S_FIRE2, + S_FIRE3, + S_FIRE4, + S_FIRE5, + S_FIRE6, + S_FIRE7, + S_FIRE8, + S_FIRE9, + S_FIRE10, + S_FIRE11, + S_FIRE12, + S_FIRE13, + S_FIRE14, + S_FIRE15, + S_FIRE16, + S_FIRE17, + S_FIRE18, + S_FIRE19, + S_FIRE20, + S_FIRE21, + S_FIRE22, + S_FIRE23, + S_FIRE24, + S_FIRE25, + S_FIRE26, + S_FIRE27, + S_FIRE28, + S_FIRE29, + S_FIRE30, + S_SMOKE1, + S_SMOKE2, + S_SMOKE3, + S_SMOKE4, + S_SMOKE5, + S_TRACER, + S_TRACER2, + S_TRACEEXP1, + S_TRACEEXP2, + S_TRACEEXP3, + S_SKEL_STND, + S_SKEL_STND2, + S_SKEL_RUN1, + S_SKEL_RUN2, + S_SKEL_RUN3, + S_SKEL_RUN4, + S_SKEL_RUN5, + S_SKEL_RUN6, + S_SKEL_RUN7, + S_SKEL_RUN8, + S_SKEL_RUN9, + S_SKEL_RUN10, + S_SKEL_RUN11, + S_SKEL_RUN12, + S_SKEL_FIST1, + S_SKEL_FIST2, + S_SKEL_FIST3, + S_SKEL_FIST4, + S_SKEL_MISS1, + S_SKEL_MISS2, + S_SKEL_MISS3, + S_SKEL_MISS4, + S_SKEL_PAIN, + S_SKEL_PAIN2, + S_SKEL_DIE1, + S_SKEL_DIE2, + S_SKEL_DIE3, + S_SKEL_DIE4, + S_SKEL_DIE5, + S_SKEL_DIE6, + S_SKEL_RAISE1, + S_SKEL_RAISE2, + S_SKEL_RAISE3, + S_SKEL_RAISE4, + S_SKEL_RAISE5, + S_SKEL_RAISE6, + S_FATSHOT1, + S_FATSHOT2, + S_FATSHOTX1, + S_FATSHOTX2, + S_FATSHOTX3, + S_FATT_STND, + S_FATT_STND2, + S_FATT_RUN1, + S_FATT_RUN2, + S_FATT_RUN3, + S_FATT_RUN4, + S_FATT_RUN5, + S_FATT_RUN6, + S_FATT_RUN7, + S_FATT_RUN8, + S_FATT_RUN9, + S_FATT_RUN10, + S_FATT_RUN11, + S_FATT_RUN12, + S_FATT_ATK1, + S_FATT_ATK2, + S_FATT_ATK3, + S_FATT_ATK4, + S_FATT_ATK5, + S_FATT_ATK6, + S_FATT_ATK7, + S_FATT_ATK8, + S_FATT_ATK9, + S_FATT_ATK10, + S_FATT_PAIN, + S_FATT_PAIN2, + S_FATT_DIE1, + S_FATT_DIE2, + S_FATT_DIE3, + S_FATT_DIE4, + S_FATT_DIE5, + S_FATT_DIE6, + S_FATT_DIE7, + S_FATT_DIE8, + S_FATT_DIE9, + S_FATT_DIE10, + S_FATT_RAISE1, + S_FATT_RAISE2, + S_FATT_RAISE3, + S_FATT_RAISE4, + S_FATT_RAISE5, + S_FATT_RAISE6, + S_FATT_RAISE7, + S_FATT_RAISE8, + S_CPOS_STND, + S_CPOS_STND2, + S_CPOS_RUN1, + S_CPOS_RUN2, + S_CPOS_RUN3, + S_CPOS_RUN4, + S_CPOS_RUN5, + S_CPOS_RUN6, + S_CPOS_RUN7, + S_CPOS_RUN8, + S_CPOS_ATK1, + S_CPOS_ATK2, + S_CPOS_ATK3, + S_CPOS_ATK4, + S_CPOS_PAIN, + S_CPOS_PAIN2, + S_CPOS_DIE1, + S_CPOS_DIE2, + S_CPOS_DIE3, + S_CPOS_DIE4, + S_CPOS_DIE5, + S_CPOS_DIE6, + S_CPOS_DIE7, + S_CPOS_XDIE1, + S_CPOS_XDIE2, + S_CPOS_XDIE3, + S_CPOS_XDIE4, + S_CPOS_XDIE5, + S_CPOS_XDIE6, + S_CPOS_RAISE1, + S_CPOS_RAISE2, + S_CPOS_RAISE3, + S_CPOS_RAISE4, + S_CPOS_RAISE5, + S_CPOS_RAISE6, + S_CPOS_RAISE7, + S_TROO_STND, + S_TROO_STND2, + S_TROO_RUN1, + S_TROO_RUN2, + S_TROO_RUN3, + S_TROO_RUN4, + S_TROO_RUN5, + S_TROO_RUN6, + S_TROO_RUN7, + S_TROO_RUN8, + S_TROO_ATK1, + S_TROO_ATK2, + S_TROO_ATK3, + S_TROO_PAIN, + S_TROO_PAIN2, + S_TROO_DIE1, + S_TROO_DIE2, + S_TROO_DIE3, + S_TROO_DIE4, + S_TROO_DIE5, + S_TROO_XDIE1, + S_TROO_XDIE2, + S_TROO_XDIE3, + S_TROO_XDIE4, + S_TROO_XDIE5, + S_TROO_XDIE6, + S_TROO_XDIE7, + S_TROO_XDIE8, + S_TROO_RAISE1, + S_TROO_RAISE2, + S_TROO_RAISE3, + S_TROO_RAISE4, + S_TROO_RAISE5, + S_SARG_STND, + S_SARG_STND2, + S_SARG_RUN1, + S_SARG_RUN2, + S_SARG_RUN3, + S_SARG_RUN4, + S_SARG_RUN5, + S_SARG_RUN6, + S_SARG_RUN7, + S_SARG_RUN8, + S_SARG_ATK1, + S_SARG_ATK2, + S_SARG_ATK3, + S_SARG_PAIN, + S_SARG_PAIN2, + S_SARG_DIE1, + S_SARG_DIE2, + S_SARG_DIE3, + S_SARG_DIE4, + S_SARG_DIE5, + S_SARG_DIE6, + S_SARG_RAISE1, + S_SARG_RAISE2, + S_SARG_RAISE3, + S_SARG_RAISE4, + S_SARG_RAISE5, + S_SARG_RAISE6, + S_HEAD_STND, + S_HEAD_RUN1, + S_HEAD_ATK1, + S_HEAD_ATK2, + S_HEAD_ATK3, + S_HEAD_PAIN, + S_HEAD_PAIN2, + S_HEAD_PAIN3, + S_HEAD_DIE1, + S_HEAD_DIE2, + S_HEAD_DIE3, + S_HEAD_DIE4, + S_HEAD_DIE5, + S_HEAD_DIE6, + S_HEAD_RAISE1, + S_HEAD_RAISE2, + S_HEAD_RAISE3, + S_HEAD_RAISE4, + S_HEAD_RAISE5, + S_HEAD_RAISE6, + S_BRBALL1, + S_BRBALL2, + S_BRBALLX1, + S_BRBALLX2, + S_BRBALLX3, + S_BOSS_STND, + S_BOSS_STND2, + S_BOSS_RUN1, + S_BOSS_RUN2, + S_BOSS_RUN3, + S_BOSS_RUN4, + S_BOSS_RUN5, + S_BOSS_RUN6, + S_BOSS_RUN7, + S_BOSS_RUN8, + S_BOSS_ATK1, + S_BOSS_ATK2, + S_BOSS_ATK3, + S_BOSS_PAIN, + S_BOSS_PAIN2, + S_BOSS_DIE1, + S_BOSS_DIE2, + S_BOSS_DIE3, + S_BOSS_DIE4, + S_BOSS_DIE5, + S_BOSS_DIE6, + S_BOSS_DIE7, + S_BOSS_RAISE1, + S_BOSS_RAISE2, + S_BOSS_RAISE3, + S_BOSS_RAISE4, + S_BOSS_RAISE5, + S_BOSS_RAISE6, + S_BOSS_RAISE7, + S_BOS2_STND, + S_BOS2_STND2, + S_BOS2_RUN1, + S_BOS2_RUN2, + S_BOS2_RUN3, + S_BOS2_RUN4, + S_BOS2_RUN5, + S_BOS2_RUN6, + S_BOS2_RUN7, + S_BOS2_RUN8, + S_BOS2_ATK1, + S_BOS2_ATK2, + S_BOS2_ATK3, + S_BOS2_PAIN, + S_BOS2_PAIN2, + S_BOS2_DIE1, + S_BOS2_DIE2, + S_BOS2_DIE3, + S_BOS2_DIE4, + S_BOS2_DIE5, + S_BOS2_DIE6, + S_BOS2_DIE7, + S_BOS2_RAISE1, + S_BOS2_RAISE2, + S_BOS2_RAISE3, + S_BOS2_RAISE4, + S_BOS2_RAISE5, + S_BOS2_RAISE6, + S_BOS2_RAISE7, + S_SKULL_STND, + S_SKULL_STND2, + S_SKULL_RUN1, + S_SKULL_RUN2, + S_SKULL_ATK1, + S_SKULL_ATK2, + S_SKULL_ATK3, + S_SKULL_ATK4, + S_SKULL_PAIN, + S_SKULL_PAIN2, + S_SKULL_DIE1, + S_SKULL_DIE2, + S_SKULL_DIE3, + S_SKULL_DIE4, + S_SKULL_DIE5, + S_SKULL_DIE6, + S_SPID_STND, + S_SPID_STND2, + S_SPID_RUN1, + S_SPID_RUN2, + S_SPID_RUN3, + S_SPID_RUN4, + S_SPID_RUN5, + S_SPID_RUN6, + S_SPID_RUN7, + S_SPID_RUN8, + S_SPID_RUN9, + S_SPID_RUN10, + S_SPID_RUN11, + S_SPID_RUN12, + S_SPID_ATK1, + S_SPID_ATK2, + S_SPID_ATK3, + S_SPID_ATK4, + S_SPID_PAIN, + S_SPID_PAIN2, + S_SPID_DIE1, + S_SPID_DIE2, + S_SPID_DIE3, + S_SPID_DIE4, + S_SPID_DIE5, + S_SPID_DIE6, + S_SPID_DIE7, + S_SPID_DIE8, + S_SPID_DIE9, + S_SPID_DIE10, + S_SPID_DIE11, + S_BSPI_STND, + S_BSPI_STND2, + S_BSPI_SIGHT, + S_BSPI_RUN1, + S_BSPI_RUN2, + S_BSPI_RUN3, + S_BSPI_RUN4, + S_BSPI_RUN5, + S_BSPI_RUN6, + S_BSPI_RUN7, + S_BSPI_RUN8, + S_BSPI_RUN9, + S_BSPI_RUN10, + S_BSPI_RUN11, + S_BSPI_RUN12, + S_BSPI_ATK1, + S_BSPI_ATK2, + S_BSPI_ATK3, + S_BSPI_ATK4, + S_BSPI_PAIN, + S_BSPI_PAIN2, + S_BSPI_DIE1, + S_BSPI_DIE2, + S_BSPI_DIE3, + S_BSPI_DIE4, + S_BSPI_DIE5, + S_BSPI_DIE6, + S_BSPI_DIE7, + S_BSPI_RAISE1, + S_BSPI_RAISE2, + S_BSPI_RAISE3, + S_BSPI_RAISE4, + S_BSPI_RAISE5, + S_BSPI_RAISE6, + S_BSPI_RAISE7, + S_ARACH_PLAZ, + S_ARACH_PLAZ2, + S_ARACH_PLEX, + S_ARACH_PLEX2, + S_ARACH_PLEX3, + S_ARACH_PLEX4, + S_ARACH_PLEX5, + S_CYBER_STND, + S_CYBER_STND2, + S_CYBER_RUN1, + S_CYBER_RUN2, + S_CYBER_RUN3, + S_CYBER_RUN4, + S_CYBER_RUN5, + S_CYBER_RUN6, + S_CYBER_RUN7, + S_CYBER_RUN8, + S_CYBER_ATK1, + S_CYBER_ATK2, + S_CYBER_ATK3, + S_CYBER_ATK4, + S_CYBER_ATK5, + S_CYBER_ATK6, + S_CYBER_PAIN, + S_CYBER_DIE1, + S_CYBER_DIE2, + S_CYBER_DIE3, + S_CYBER_DIE4, + S_CYBER_DIE5, + S_CYBER_DIE6, + S_CYBER_DIE7, + S_CYBER_DIE8, + S_CYBER_DIE9, + S_CYBER_DIE10, + S_PAIN_STND, + S_PAIN_RUN1, + S_PAIN_RUN2, + S_PAIN_RUN3, + S_PAIN_RUN4, + S_PAIN_RUN5, + S_PAIN_RUN6, + S_PAIN_ATK1, + S_PAIN_ATK2, + S_PAIN_ATK3, + S_PAIN_ATK4, + S_PAIN_PAIN, + S_PAIN_PAIN2, + S_PAIN_DIE1, + S_PAIN_DIE2, + S_PAIN_DIE3, + S_PAIN_DIE4, + S_PAIN_DIE5, + S_PAIN_DIE6, + S_PAIN_RAISE1, + S_PAIN_RAISE2, + S_PAIN_RAISE3, + S_PAIN_RAISE4, + S_PAIN_RAISE5, + S_PAIN_RAISE6, + S_SSWV_STND, + S_SSWV_STND2, + S_SSWV_RUN1, + S_SSWV_RUN2, + S_SSWV_RUN3, + S_SSWV_RUN4, + S_SSWV_RUN5, + S_SSWV_RUN6, + S_SSWV_RUN7, + S_SSWV_RUN8, + S_SSWV_ATK1, + S_SSWV_ATK2, + S_SSWV_ATK3, + S_SSWV_ATK4, + S_SSWV_ATK5, + S_SSWV_ATK6, + S_SSWV_PAIN, + S_SSWV_PAIN2, + S_SSWV_DIE1, + S_SSWV_DIE2, + S_SSWV_DIE3, + S_SSWV_DIE4, + S_SSWV_DIE5, + S_SSWV_XDIE1, + S_SSWV_XDIE2, + S_SSWV_XDIE3, + S_SSWV_XDIE4, + S_SSWV_XDIE5, + S_SSWV_XDIE6, + S_SSWV_XDIE7, + S_SSWV_XDIE8, + S_SSWV_XDIE9, + S_SSWV_RAISE1, + S_SSWV_RAISE2, + S_SSWV_RAISE3, + S_SSWV_RAISE4, + S_SSWV_RAISE5, + S_KEENSTND, + S_COMMKEEN, + S_COMMKEEN2, + S_COMMKEEN3, + S_COMMKEEN4, + S_COMMKEEN5, + S_COMMKEEN6, + S_COMMKEEN7, + S_COMMKEEN8, + S_COMMKEEN9, + S_COMMKEEN10, + S_COMMKEEN11, + S_COMMKEEN12, + S_KEENPAIN, + S_KEENPAIN2, + S_BRAIN, + S_BRAIN_PAIN, + S_BRAIN_DIE1, + S_BRAIN_DIE2, + S_BRAIN_DIE3, + S_BRAIN_DIE4, + S_BRAINEYE, + S_BRAINEYESEE, + S_BRAINEYE1, + S_SPAWN1, + S_SPAWN2, + S_SPAWN3, + S_SPAWN4, + S_SPAWNFIRE1, + S_SPAWNFIRE2, + S_SPAWNFIRE3, + S_SPAWNFIRE4, + S_SPAWNFIRE5, + S_SPAWNFIRE6, + S_SPAWNFIRE7, + S_SPAWNFIRE8, + S_BRAINEXPLODE1, + S_BRAINEXPLODE2, + S_BRAINEXPLODE3, + S_ARM1, + S_ARM1A, + S_ARM2, + S_ARM2A, + S_BAR1, + S_BAR2, + S_BEXP, + S_BEXP2, + S_BEXP3, + S_BEXP4, + S_BEXP5, + S_BBAR1, + S_BBAR2, + S_BBAR3, + S_BON1, + S_BON1A, + S_BON1B, + S_BON1C, + S_BON1D, + S_BON1E, + S_BON2, + S_BON2A, + S_BON2B, + S_BON2C, + S_BON2D, + S_BON2E, + S_BKEY, + S_BKEY2, + S_RKEY, + S_RKEY2, + S_YKEY, + S_YKEY2, + S_BSKULL, + S_BSKULL2, + S_RSKULL, + S_RSKULL2, + S_YSKULL, + S_YSKULL2, + S_STIM, + S_MEDI, + S_SOUL, + S_SOUL2, + S_SOUL3, + S_SOUL4, + S_SOUL5, + S_SOUL6, + S_PINV, + S_PINV2, + S_PINV3, + S_PINV4, + S_PSTR, + S_PINS, + S_PINS2, + S_PINS3, + S_PINS4, + S_MEGA, + S_MEGA2, + S_MEGA3, + S_MEGA4, + S_SUIT, + S_PMAP, + S_PMAP2, + S_PMAP3, + S_PMAP4, + S_PMAP5, + S_PMAP6, + S_PVIS, + S_PVIS2, + S_CLIP, + S_AMMO, + S_ROCK, + S_BROK, + S_CELL, + S_CELP, + S_SHEL, + S_SBOX, + S_BPAK, + S_BFUG, + S_MGUN, + S_CSAW, + S_LAUN, + S_PLAS, + S_SHOT, + S_SHOT2, + S_COLU, + S_STALAG, + S_BLOODYTWITCH, + S_BLOODYTWITCH2, + S_BLOODYTWITCH3, + S_BLOODYTWITCH4, + S_DEADTORSO, + S_DEADBOTTOM, + S_HEADSONSTICK, + S_GIBS, + S_HEADONASTICK, + S_HEADCANDLES, + S_HEADCANDLES2, + S_DEADSTICK, + S_LIVESTICK, + S_LIVESTICK2, + S_MEAT2, + S_MEAT3, + S_MEAT4, + S_MEAT5, + S_STALAGTITE, + S_TALLGRNCOL, + S_SHRTGRNCOL, + S_TALLREDCOL, + S_SHRTREDCOL, + S_CANDLESTIK, + S_CANDELABRA, + S_SKULLCOL, + S_TORCHTREE, + S_BIGTREE, + S_TECHPILLAR, + S_EVILEYE, + S_EVILEYE2, + S_EVILEYE3, + S_EVILEYE4, + S_FLOATSKULL, + S_FLOATSKULL2, + S_FLOATSKULL3, + S_HEARTCOL, + S_HEARTCOL2, + S_BLUETORCH, + S_BLUETORCH2, + S_BLUETORCH3, + S_BLUETORCH4, + S_GREENTORCH, + S_GREENTORCH2, + S_GREENTORCH3, + S_GREENTORCH4, + S_REDTORCH, + S_REDTORCH2, + S_REDTORCH3, + S_REDTORCH4, + S_BTORCHSHRT, + S_BTORCHSHRT2, + S_BTORCHSHRT3, + S_BTORCHSHRT4, + S_GTORCHSHRT, + S_GTORCHSHRT2, + S_GTORCHSHRT3, + S_GTORCHSHRT4, + S_RTORCHSHRT, + S_RTORCHSHRT2, + S_RTORCHSHRT3, + S_RTORCHSHRT4, + S_HANGNOGUTS, + S_HANGBNOBRAIN, + S_HANGTLOOKDN, + S_HANGTSKULL, + S_HANGTLOOKUP, + S_HANGTNOBRAIN, + S_COLONGIBS, + S_SMALLPOOL, + S_BRAINSTEM, + S_TECHLAMP, + S_TECHLAMP2, + S_TECHLAMP3, + S_TECHLAMP4, + S_TECH2LAMP, + S_TECH2LAMP2, + S_TECH2LAMP3, + S_TECH2LAMP4, + S_TNT1, /* add state for invisible sprite phares 3/8/98 */ + + S_GRENADE, /* killough 8/9/98: grenade launcher */ + S_DETONATE, /* killough 8/9/98: detonation of objects */ + S_DETONATE2, + S_DETONATE3, + + // always count dog states, even if dogs are disabled + S_DOGS_STND, /* killough 7/19/98: Marine's best friend :) */ + S_DOGS_STND2, + S_DOGS_RUN1, + S_DOGS_RUN2, + S_DOGS_RUN3, + S_DOGS_RUN4, + S_DOGS_RUN5, + S_DOGS_RUN6, + S_DOGS_RUN7, + S_DOGS_RUN8, + S_DOGS_ATK1, + S_DOGS_ATK2, + S_DOGS_ATK3, + S_DOGS_PAIN, + S_DOGS_PAIN2, + S_DOGS_DIE1, + S_DOGS_DIE2, + S_DOGS_DIE3, + S_DOGS_DIE4, + S_DOGS_DIE5, + S_DOGS_DIE6, + S_DOGS_RAISE1, + S_DOGS_RAISE2, + S_DOGS_RAISE3, + S_DOGS_RAISE4, + S_DOGS_RAISE5, + S_DOGS_RAISE6, + + // add dummy beta bfg / lost soul frames for dehacked compatibility + // fixes bug #1576151 (part 2) + S_OLDBFG1, // killough 7/11/98: the old BFG's 43 firing frames + S_OLDBFG42 = S_OLDBFG1+41, + S_OLDBFG43, + + S_PLS1BALL, // killough 7/19/98: first plasma fireball in the beta + S_PLS1BALL2, + S_PLS1EXP, + S_PLS1EXP2, + S_PLS1EXP3, + S_PLS1EXP4, + S_PLS1EXP5, + + S_PLS2BALL, // killough 7/19/98: second plasma fireball in the beta + S_PLS2BALL2, + S_PLS2BALLX1, + S_PLS2BALLX2, + S_PLS2BALLX3, + S_BON3, // killough 7/11/98: evil sceptre in beta version + S_BON4, // killough 7/11/98: unholy bible in beta version + + // killough 10/98: beta lost souls were different from their modern cousins + S_BSKUL_STND, + S_BSKUL_RUN1, + S_BSKUL_RUN2, + S_BSKUL_RUN3, + S_BSKUL_RUN4, + S_BSKUL_ATK1, + S_BSKUL_ATK2, + S_BSKUL_ATK3, + S_BSKUL_PAIN1, + S_BSKUL_PAIN2, + S_BSKUL_PAIN3, + S_BSKUL_DIE1, + S_BSKUL_DIE2, + S_BSKUL_DIE3, + S_BSKUL_DIE4, + S_BSKUL_DIE5, + S_BSKUL_DIE6, + S_BSKUL_DIE7, + S_BSKUL_DIE8, + + S_MUSHROOM, /* killough 10/98: mushroom explosion effect */ + + NUMSTATES /* Counter of how many there are */ + +} statenum_t; + +/******************************************************************** + * Definition of the state (frames) structure * + ********************************************************************/ + +typedef struct +{ + spritenum_t sprite; /* sprite number to show */ + long frame; /* which frame/subframe of the sprite is shown */ + long tics; /* number of gametics this frame should last */ + actionf_t action; /* code pointer to function for action if any */ + statenum_t nextstate; /* linked list pointer to next state or zero */ + long misc1, misc2; /* apparently never used in DOOM */ +} state_t; + +/* these are in info.c */ +extern state_t states[NUMSTATES]; +extern const char *sprnames[]; /* 1/17/98 killough - CPhipps - const */ + +/******************************************************************** + * Thing enumeration -- must match info.c * + ******************************************************************** + * Note that many of these are generically named for the ornamentals + */ + +typedef enum { + MT_PLAYER, + MT_POSSESSED, + MT_SHOTGUY, + MT_VILE, + MT_FIRE, + MT_UNDEAD, + MT_TRACER, + MT_SMOKE, + MT_FATSO, + MT_FATSHOT, + MT_CHAINGUY, + MT_TROOP, + MT_SERGEANT, + MT_SHADOWS, + MT_HEAD, + MT_BRUISER, + MT_BRUISERSHOT, + MT_KNIGHT, + MT_SKULL, + MT_SPIDER, + MT_BABY, + MT_CYBORG, + MT_PAIN, + MT_WOLFSS, + MT_KEEN, + MT_BOSSBRAIN, + MT_BOSSSPIT, + MT_BOSSTARGET, + MT_SPAWNSHOT, + MT_SPAWNFIRE, + MT_BARREL, + MT_TROOPSHOT, + MT_HEADSHOT, + MT_ROCKET, + MT_PLASMA, + MT_BFG, + MT_ARACHPLAZ, + MT_PUFF, + MT_BLOOD, + MT_TFOG, + MT_IFOG, + MT_TELEPORTMAN, + MT_EXTRABFG, + MT_MISC0, + MT_MISC1, + MT_MISC2, + MT_MISC3, + MT_MISC4, + MT_MISC5, + MT_MISC6, + MT_MISC7, + MT_MISC8, + MT_MISC9, + MT_MISC10, + MT_MISC11, + MT_MISC12, + MT_INV, + MT_MISC13, + MT_INS, + MT_MISC14, + MT_MISC15, + MT_MISC16, + MT_MEGA, + MT_CLIP, + MT_MISC17, + MT_MISC18, + MT_MISC19, + MT_MISC20, + MT_MISC21, + MT_MISC22, + MT_MISC23, + MT_MISC24, + MT_MISC25, + MT_CHAINGUN, + MT_MISC26, + MT_MISC27, + MT_MISC28, + MT_SHOTGUN, + MT_SUPERSHOTGUN, + MT_MISC29, + MT_MISC30, + MT_MISC31, + MT_MISC32, + MT_MISC33, + MT_MISC34, + MT_MISC35, + MT_MISC36, + MT_MISC37, + MT_MISC38, + MT_MISC39, + MT_MISC40, + MT_MISC41, + MT_MISC42, + MT_MISC43, + MT_MISC44, + MT_MISC45, + MT_MISC46, + MT_MISC47, + MT_MISC48, + MT_MISC49, + MT_MISC50, + MT_MISC51, + MT_MISC52, + MT_MISC53, + MT_MISC54, + MT_MISC55, + MT_MISC56, + MT_MISC57, + MT_MISC58, + MT_MISC59, + MT_MISC60, + MT_MISC61, + MT_MISC62, + MT_MISC63, + MT_MISC64, + MT_MISC65, + MT_MISC66, + MT_MISC67, + MT_MISC68, + MT_MISC69, + MT_MISC70, + MT_MISC71, + MT_MISC72, + MT_MISC73, + MT_MISC74, + MT_MISC75, + MT_MISC76, + MT_MISC77, + MT_MISC78, + MT_MISC79, + MT_MISC80, + MT_MISC81, + MT_MISC82, + MT_MISC83, + MT_MISC84, + MT_MISC85, + MT_MISC86, + MT_PUSH, /* controls push source - phares */ + MT_PULL, /* controls pull source - phares 3/20/98 */ + +#ifdef DOGS + MT_DOGS, /* killough 7/19/98: Marine's best friend */ +#endif + + /* proff 11/22/98: Andy Baker's stealth monsters (next 12) + * cph - moved below the MBF stuff, no need to displace them */ + MT_STEALTHBABY, + MT_STEALTHVILE, + MT_STEALTHBRUISER, + MT_STEALTHHEAD, + MT_STEALTHCHAINGUY, + MT_STEALTHSERGEANT, + MT_STEALTHKNIGHT, + MT_STEALTHIMP, + MT_STEALTHFATSO, + MT_STEALTHUNDEAD, + MT_STEALTHSHOTGUY, + MT_STEALTHZOMBIE, + + NUMMOBJTYPES // Counter of how many there are +} mobjtype_t; + +/******************************************************************** + * Definition of the Thing structure + ********************************************************************/ +/* Note that these are only indices to the state, sound, etc. arrays + * and not actual pointers. Most can be set to zero if the action or + * sound doesn't apply (like lamps generally don't attack or whistle). + */ + +typedef struct +{ + int doomednum; /* Thing number used in id's editor, and now + probably by every other editor too */ + int spawnstate; /* The state (frame) index when this Thing is + first created */ + int spawnhealth; /* The initial hit points for this Thing */ + int seestate; /* The state when it sees you or wakes up */ + int seesound; /* The sound it makes when waking */ + int reactiontime; /* How many tics it waits after it wakes up + before it will start to attack, in normal + skills (halved for nightmare) */ + int attacksound; /* The sound it makes when it attacks */ + int painstate; /* The state to indicate pain */ + int painchance; /* A number that is checked against a random + number 0-255 to see if the Thing is supposed + to go to its painstate or not. Note this + has absolutely nothing to do with the chance + it will get hurt, just the chance of it + reacting visibly. */ + int painsound; /* The sound it emits when it feels pain */ + int meleestate; /* Melee==close attack */ + int missilestate; /* What states to use when it's in the air, if + in fact it is ever used as a missile */ + int deathstate; /* What state begins the death sequence */ + int xdeathstate; /* What state begins the horrible death sequence + like when a rocket takes out a trooper */ + int deathsound; /* The death sound. See also A_Scream() in + p_enemy.c for some tweaking that goes on + for certain monsters */ + int speed; /* How fast it moves. Too fast and it can miss + collision logic. */ + int radius; /* An often incorrect radius */ + int height; /* An often incorrect height, used only to see + if a monster can enter a sector */ + int mass; /* How much an impact will move it. Cacodemons + seem to retreat when shot because they have + very little mass and are moved by impact */ + int damage; /* If this is a missile, how much does it hurt? */ + int activesound; /* What sound it makes wandering around, once + in a while. Chance is 3/256 it will. */ + uint_64_t flags; /* Bit masks for lots of things. See p_mobj.h */ + int raisestate; /* The first state for an Archvile or respawn + resurrection. Zero means it won't come + back to life. */ +} mobjinfo_t; + +/* See p_mobj_h for addition more technical info */ +extern mobjinfo_t mobjinfo[NUMMOBJTYPES]; + +#endif diff --git a/src/lprintf.c b/src/lprintf.c new file mode 100644 index 0000000..0951cf3 --- /dev/null +++ b/src/lprintf.c @@ -0,0 +1,382 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Provides a logical console output routine that allows what is + * output to console normally and when output is redirected to + * be controlled.. + * + *-----------------------------------------------------------------------------*/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#ifdef _WIN32 +#define WIN32_LEAN_AND_MEAN +#include +#endif +#ifdef _MSC_VER +#include +#endif +#include +#include +#include +#ifdef HAVE_UNISTD_H +#include +#endif +#include "doomtype.h" +#include "lprintf.h" +#include "i_main.h" +#include "m_argv.h" + +int cons_error_mask = -1-LO_INFO; /* all but LO_INFO when redir'd */ +int cons_output_mask = -1; /* all output enabled */ + +/* cphipps - enlarged message buffer and made non-static + * We still have to be careful here, this function can be called after exit + */ +#define MAX_MESSAGE_SIZE 2048 + +#ifdef _WIN32 +// Variables for the console +HWND con_hWnd=0; +HFONT OemFont; +LONG OemWidth, OemHeight; +int ConWidth,ConHeight; +char szConName[] = "PrBoomConWinClass"; +char Lines[(80+2)*25+1]; +char *Last = NULL; +boolean console_inited=FALSE; +static boolean should_exit = 0; + +static CALLBACK ConWndProc(HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam) +{ + PAINTSTRUCT paint; + HDC dc; + + switch (iMsg) { + case WM_KEYDOWN: + if (wParam == VK_ESCAPE) + should_exit = 1; + break; + case WM_CLOSE: + return 1; + break; + case WM_PAINT: + if (dc = BeginPaint (con_hWnd, &paint)) + { + if (Last) + { + char *row; + int line, last; + + line = paint.rcPaint.top / OemHeight; + last = paint.rcPaint.bottom / OemHeight; + for (row = Lines + (line*(80+2)); line < last; line++) + { + if (row[1]>0) + TextOut (dc, 0, line * OemHeight, &row[2], row[1]); + row += 80 + 2; + } + } + EndPaint (con_hWnd, &paint); + } + return 0; + break; + default: + break; + } + return(DefWindowProc(hwnd,iMsg,wParam,lParam)); +} + +static void I_PrintStr (int xp, const char *cp, int count, BOOL scroll) { + RECT rect; + HDC conDC; + + if ((!con_hWnd) || (!console_inited)) + return; + if (count) + { + conDC=GetDC(con_hWnd); + TextOut (conDC, xp * OemWidth, ConHeight - OemHeight, cp, count); + ReleaseDC(con_hWnd,conDC); + } + if (scroll) { + rect.left = 0; + rect.top = 0; + rect.right = ConWidth; + rect.bottom = ConHeight; + ScrollWindowEx (con_hWnd, 0, -OemHeight, NULL, &rect, NULL, NULL, SW_ERASE|SW_INVALIDATE); + UpdateWindow (con_hWnd); + } +} + +static int I_ConPrintString (const char *outline) +{ + const char *cp, *newcp; + static int xp = 0; + int newxp; + BOOL scroll; + + if (!console_inited) + return 0; + cp = outline; + while (*cp) { + for (newcp = cp, newxp = xp; + *newcp != '\n' && *newcp != '\0' && newxp < 80; + newcp++) { + if (*newcp == '\x08') { + newxp--; + break; + } + else if (*newcp == '\t') { + newxp = ((newxp + 8) / 8) * 8; + break; + } + else + newxp++; + } + + if (*cp) { + const char *poop; + int x; + + for (x = xp, poop = cp; poop < newcp; poop++, x++) { + Last[x+2] = ((*poop) < 32) ? 32 : (*poop); + } + + if (*newcp == '\t') + for (x = xp; x < newxp; x++) + Last[x+2] = ' '; + + if (Last[1] < xp + (newcp - cp)) + Last[1] = xp + (newcp - cp); + + if (*newcp == '\n' || xp == 80) { + if (*newcp != '\n') { + Last[0] = 1; + } + memmove (Lines, Lines + (80 + 2), (80 + 2) * (25 - 1)); + Last[0] = 0; + Last[1] = 0; + newxp = 0; + scroll = TRUE; + } else { + scroll = FALSE; + } + I_PrintStr (xp, cp, newcp - cp, scroll); + + xp = newxp; + + if ((*newcp == '\n') || (*newcp == '\x08') || (*newcp == '\t')) + cp = newcp + 1; + else + cp = newcp; + } + } + + return strlen (outline); +} + +void I_ConTextAttr(unsigned char a) +{ + int r,g,b,col; + HDC conDC; + + if (!console_inited) + return; + conDC=GetDC(con_hWnd); + r=0; g=0; b=0; + if (a & FOREGROUND_INTENSITY) col=255; + else col=128; + if (a & FOREGROUND_RED) r=col; + if (a & FOREGROUND_GREEN) g=col; + if (a & FOREGROUND_BLUE) b=col; + SetTextColor(conDC, PALETTERGB(r,g,b)); + r=0; g=0; b=0; + if (a & BACKGROUND_INTENSITY) col=255; + else col=128; + if (a & BACKGROUND_RED) r=col; + if (a & BACKGROUND_GREEN) g=col; + if (a & BACKGROUND_BLUE) b=col; + SetBkColor(conDC, PALETTERGB(r,g,b)); + ReleaseDC(con_hWnd,conDC); +} + +void I_UpdateConsole(void) +{ + MSG msg; + + UpdateWindow(con_hWnd); + while(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE) > 0) + { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + if (should_exit) + exit(0); +} + +static void Init_Console(void) +{ + memset(Lines,0,25*(80+2)+1); + Last = Lines + (25 - 1) * (80 + 2); + console_inited=TRUE; +} + +int Init_ConsoleWin(void) +{ + HDC conDC; + WNDCLASS wndclass; + TEXTMETRIC metrics; + RECT cRect; + int width,height; + int scr_width,scr_height; + HINSTANCE hInstance; + char titlebuffer[2048]; + + if (con_hWnd) + return TRUE; + hInstance = GetModuleHandle(NULL); + Init_Console(); + /* Register the frame class */ + wndclass.style = CS_OWNDC; + wndclass.lpfnWndProc = (WNDPROC)ConWndProc; + wndclass.cbClsExtra = 0; + wndclass.cbWndExtra = 0; + wndclass.hInstance = hInstance; + wndclass.hIcon = LoadIcon (hInstance, IDI_WINLOGO); + wndclass.hCursor = LoadCursor (NULL,IDC_ARROW); + wndclass.hbrBackground = (HBRUSH)GetStockObject (BLACK_BRUSH); + wndclass.lpszMenuName = szConName; + wndclass.lpszClassName = szConName; + + if (!RegisterClass(&wndclass)) + return FALSE; + + width=100; + height=100; + strcpy(titlebuffer,PACKAGE); + strcat(titlebuffer," "); + strcat(titlebuffer,VERSION); + strcat(titlebuffer," console"); + con_hWnd = CreateWindow(szConName, titlebuffer, + WS_CAPTION | WS_POPUP, + 0, 0, width, height, + NULL, NULL, hInstance, NULL); + conDC=GetDC(con_hWnd); + OemFont = GetStockObject(OEM_FIXED_FONT); + SelectObject(conDC, OemFont); + GetTextMetrics(conDC, &metrics); + OemWidth = metrics.tmAveCharWidth; + OemHeight = metrics.tmHeight; + GetClientRect(con_hWnd, &cRect); + width += (OemWidth * 80) - cRect.right; + height += (OemHeight * 25) - cRect.bottom; + // proff 11/09/98: Added code for centering console + scr_width = GetSystemMetrics(SM_CXFULLSCREEN); + scr_height = GetSystemMetrics(SM_CYFULLSCREEN); + MoveWindow(con_hWnd, (scr_width-width)/2, (scr_height-height)/2, width, height, TRUE); + GetClientRect(con_hWnd, &cRect); + ConWidth = cRect.right; + ConHeight = cRect.bottom; + SetTextColor(conDC, RGB(192,192,192)); + SetBkColor(conDC, RGB(0,0,0)); + SetBkMode(conDC, OPAQUE); + ReleaseDC(con_hWnd,conDC); + ShowWindow(con_hWnd, SW_SHOW); + UpdateWindow(con_hWnd); + return TRUE; +} + +void Done_ConsoleWin(void) +{ + if (con_hWnd) + DestroyWindow(con_hWnd); + UnregisterClass(szConName,GetModuleHandle(NULL)); + con_hWnd=0; +} +#endif + +int lprintf(OutputLevels pri, const char *s, ...) +{ + int r=0; + char msg[MAX_MESSAGE_SIZE]; + int lvl=pri; + + va_list v; + va_start(v,s); +#ifdef HAVE_VSNPRINTF + vsnprintf(msg,sizeof(msg),s,v); /* print message in buffer */ +#else + vsprintf(msg,s,v); +#endif + va_end(v); + + if (lvl&cons_output_mask) /* mask output as specified */ + { + r=fprintf(stdout,"%s",msg); +#ifdef _WIN32 + I_ConPrintString(msg); +#endif + } + if (!isatty(1) && lvl&cons_error_mask) /* if stdout redirected */ + r=fprintf(stderr,"%s",msg); /* select output at console */ + + return r; +} + +/* + * I_Error + * + * cphipps - moved out of i_* headers, to minimise source files that depend on + * the low-level headers. All this does is print the error, then call the + * low-level safe exit function. + * killough 3/20/98: add const + */ + +void I_Error(const char *error, ...) +{ + char errmsg[MAX_MESSAGE_SIZE]; + va_list argptr; + va_start(argptr,error); +#ifdef HAVE_VSNPRINTF + vsnprintf(errmsg,sizeof(errmsg),error,argptr); +#else + vsprintf(errmsg,error,argptr); +#endif + va_end(argptr); + lprintf(LO_ERROR, "%s\n", errmsg); +#ifdef _MSC_VER + if (!M_CheckParm ("-nodraw")) { + //Init_ConsoleWin(); + MessageBox(con_hWnd,errmsg,"PrBoom",MB_OK | MB_TASKMODAL | MB_TOPMOST); + } +#endif + I_SafeExit(-1); +} diff --git a/src/lprintf.h b/src/lprintf.h new file mode 100644 index 0000000..9871688 --- /dev/null +++ b/src/lprintf.h @@ -0,0 +1,68 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Declarations etc. for logical console output + * + *-----------------------------------------------------------------------------*/ + +#ifndef __LPRINTF__ +#define __LPRINTF__ + +typedef enum /* Logical output levels */ +{ + LO_INFO=1, /* One of these is used in each physical output */ + LO_CONFIRM=2, /* call. Which are output, or echoed to console */ + LO_WARN=4, /* if output redirected is determined by the */ + LO_ERROR=8, /* global masks: cons_output_mask,cons_error_mask. */ + LO_FATAL=16, + LO_DEBUG=32, + LO_ALWAYS=64, +} OutputLevels; + +#ifndef __GNUC__ +#define __attribute__(x) +#endif + +extern int lprintf(OutputLevels pri, const char *fmt, ...) __attribute__((format(printf,2,3))); +extern int cons_output_mask; +extern int cons_error_mask; + +/* killough 3/20/98: add const + * killough 4/25/98: add gcc attributes + * cphipps 01/11- moved from i_system.h */ +void I_Error(const char *error, ...) __attribute__((format(printf,1,2))); + +#ifdef _WIN32 +void I_ConTextAttr(unsigned char a); +void I_UpdateConsole(void); +int Init_ConsoleWin(void); +void Done_ConsoleWin(void); +#endif + +#endif diff --git a/src/m_argv.c b/src/m_argv.c new file mode 100644 index 0000000..9392840 --- /dev/null +++ b/src/m_argv.c @@ -0,0 +1,58 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Some argument handling. + * + *-----------------------------------------------------------------------------*/ + +#include +// CPhipps - include the correct header +#include "doomtype.h" +#include "m_argv.h" + +int myargc; +const char * const * myargv; // CPhipps - not sure if ANSI C allows you to +// modify contents of argv, but I can't imagine it does. + +// +// M_CheckParm +// Checks for the given parameter +// in the program's command line arguments. +// Returns the argument number (1 to argc-1) +// or 0 if not present +// + +int M_CheckParm(const char *check) +{ + signed int i = myargc; + while (--i>0) + if (!strcasecmp(check, myargv[i])) + return i; + return 0; +} diff --git a/src/m_argv.h b/src/m_argv.h new file mode 100644 index 0000000..5340c15 --- /dev/null +++ b/src/m_argv.h @@ -0,0 +1,47 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Argument handling. + * + *-----------------------------------------------------------------------------*/ + + +#ifndef __M_ARGV__ +#define __M_ARGV__ + +/* + * MISC + */ +extern int myargc; +extern const char * const * myargv; /* CPhipps - const * const * */ + +/* Returns the position of the given parameter in the arg list (0 if not found). */ +int M_CheckParm(const char *check); + +#endif diff --git a/src/m_bbox.c b/src/m_bbox.c new file mode 100644 index 0000000..f2097ce --- /dev/null +++ b/src/m_bbox.c @@ -0,0 +1,58 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Main loop menu stuff. + * Random number LUT. + * Default Config File. + * PCX Screenshots. + * + *-----------------------------------------------------------------------------*/ + +#ifdef __GNUG__ +#pragma implementation "m_bbox.h" +#endif +#include "m_bbox.h" + +void M_ClearBox (fixed_t *box) +{ + box[BOXTOP] = box[BOXRIGHT] = INT_MIN; + box[BOXBOTTOM] = box[BOXLEFT] = INT_MAX; +} + +void M_AddToBox(fixed_t* box,fixed_t x,fixed_t y) + { + if (xbox[BOXRIGHT]) + box[BOXRIGHT] = x; + if (ybox[BOXTOP]) + box[BOXTOP] = y; + } diff --git a/src/m_bbox.h b/src/m_bbox.h new file mode 100644 index 0000000..5c2f2df --- /dev/null +++ b/src/m_bbox.h @@ -0,0 +1,56 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Simple bounding box datatype and functions. + * + *-----------------------------------------------------------------------------*/ + + +#ifndef __M_BBOX__ +#define __M_BBOX__ + +#include +#include "m_fixed.h" + +/* Bounding box coordinate storage. */ +enum +{ + BOXTOP, + BOXBOTTOM, + BOXLEFT, + BOXRIGHT +}; /* bbox coordinates */ + +/* Bounding box functions. */ + +void M_ClearBox(fixed_t* box); + +void M_AddToBox(fixed_t* box,fixed_t x,fixed_t y); + +#endif diff --git a/src/m_cheat.c b/src/m_cheat.c new file mode 100644 index 0000000..aebad97 --- /dev/null +++ b/src/m_cheat.c @@ -0,0 +1,744 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2002 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Cheat sequence checking. + * + *-----------------------------------------------------------------------------*/ + +#include "doomstat.h" +#include "g_game.h" +#include "r_data.h" +#include "p_inter.h" +#include "p_tick.h" +#include "m_cheat.h" +#include "m_argv.h" +#include "s_sound.h" +#include "sounds.h" +#include "dstrings.h" +#include "r_main.h" +#include "p_map.h" +#include "d_deh.h" // Ty 03/27/98 - externalized strings +/* cph 2006/07/23 - needs direct access to thinkercap */ +#include "p_tick.h" + +#define plyr (players+consoleplayer) /* the console player */ + +//----------------------------------------------------------------------------- +// +// CHEAT SEQUENCE PACKAGE +// +//----------------------------------------------------------------------------- + +static void cheat_mus(); +static void cheat_choppers(); +static void cheat_god(); +static void cheat_fa(); +static void cheat_k(); +static void cheat_kfa(); +static void cheat_noclip(); +static void cheat_pw(); +static void cheat_behold(); +static void cheat_clev(); +static void cheat_mypos(); +static void cheat_rate(); +static void cheat_comp(); +static void cheat_friction(); +static void cheat_pushers(); +static void cheat_tnttran(); +static void cheat_massacre(); +static void cheat_ddt(); +static void cheat_hom(); +static void cheat_fast(); +static void cheat_tntkey(); +static void cheat_tntkeyx(); +static void cheat_tntkeyxx(); +static void cheat_tntweap(); +static void cheat_tntweapx(); +static void cheat_tntammo(); +static void cheat_tntammox(); +static void cheat_smart(); +static void cheat_pitch(); +static void cheat_megaarmour(); +static void cheat_health(); + +//----------------------------------------------------------------------------- +// +// List of cheat codes, functions, and special argument indicators. +// +// The first argument is the cheat code. +// +// The second argument is its DEH name, or NULL if it's not supported by -deh. +// +// The third argument is a combination of the bitmasks: +// {always, not_dm, not_coop, not_net, not_menu, not_demo, not_deh}, +// which excludes the cheat during certain modes of play. +// +// The fourth argument is the handler function. +// +// The fifth argument is passed to the handler function if it's non-negative; +// if negative, then its negative indicates the number of extra characters +// expected after the cheat code, which are passed to the handler function +// via a pointer to a buffer (after folding any letters to lowercase). +// +//----------------------------------------------------------------------------- + +struct cheat_s cheat[] = { + {"idmus", "Change music", always, + cheat_mus, -2}, + + {"idchoppers", "Chainsaw", not_net | not_demo, + cheat_choppers }, + + {"iddqd", "God mode", not_net | not_demo, + cheat_god }, + +#if 0 + {"idk", NULL, not_net | not_demo | not_deh, + cheat_k }, // The most controversial cheat code in Doom history!!! +#endif + + {"idkfa", "Ammo & Keys", not_net | not_demo, + cheat_kfa }, + + {"idfa", "Ammo", not_net | not_demo, + cheat_fa }, + + {"idspispopd", "No Clipping 1", not_net | not_demo, + cheat_noclip }, + + {"idclip", "No Clipping 2", not_net | not_demo, + cheat_noclip }, + + {"idbeholdh", "Invincibility", not_net | not_demo, + cheat_health }, + + {"idbeholdm", "Invincibility", not_net | not_demo, + cheat_megaarmour }, + + {"idbeholdv", "Invincibility", not_net | not_demo, + cheat_pw, pw_invulnerability }, + + {"idbeholds", "Berserk", not_net | not_demo, + cheat_pw, pw_strength }, + + {"idbeholdi", "Invisibility", not_net | not_demo, + cheat_pw, pw_invisibility }, + + {"idbeholdr", "Radiation Suit", not_net | not_demo, + cheat_pw, pw_ironfeet }, + + {"idbeholda", "Auto-map", not_dm, + cheat_pw, pw_allmap }, + + {"idbeholdl", "Lite-Amp Goggles", not_dm, + cheat_pw, pw_infrared }, + + {"idbehold", "BEHOLD menu", not_dm, + cheat_behold }, + + {"idclev", "Level Warp", not_net | not_demo | not_menu, + cheat_clev, -2}, + + {"idmypos", "Player Position", not_dm, + cheat_mypos }, + + {"idrate", "Frame rate", 0, + cheat_rate }, + + {"tntcomp", NULL, not_net | not_demo, + cheat_comp }, // phares + + {"tntem", NULL, not_net | not_demo, + cheat_massacre }, // jff 2/01/98 kill all monsters + + {"iddt", "Map cheat", not_dm, + cheat_ddt }, // killough 2/07/98: moved from am_map.c + + {"tnthom", NULL, always, + cheat_hom }, // killough 2/07/98: HOM autodetector + + {"tntkey", NULL, not_net | not_demo, + cheat_tntkey }, // killough 2/16/98: generalized key cheats + + {"tntkeyr", NULL, not_net | not_demo, + cheat_tntkeyx }, + + {"tntkeyy", NULL, not_net | not_demo, + cheat_tntkeyx }, + + {"tntkeyb", NULL, not_net | not_demo, + cheat_tntkeyx }, + + {"tntkeyrc", NULL, not_net | not_demo, + cheat_tntkeyxx, it_redcard }, + + {"tntkeyyc", NULL, not_net | not_demo, + cheat_tntkeyxx, it_yellowcard }, + + {"tntkeybc", NULL, not_net | not_demo, + cheat_tntkeyxx, it_bluecard }, + + {"tntkeyrs", NULL, not_net | not_demo, + cheat_tntkeyxx, it_redskull }, + + {"tntkeyys", NULL, not_net | not_demo, + cheat_tntkeyxx, it_yellowskull}, + + {"tntkeybs", NULL, not_net | not_demo, + cheat_tntkeyxx, it_blueskull }, // killough 2/16/98: end generalized keys + + {"tntka", NULL, not_net | not_demo, + cheat_k }, // Ty 04/11/98 - Added TNTKA + + {"tntweap", NULL, not_net | not_demo, + cheat_tntweap }, // killough 2/16/98: generalized weapon cheats + + {"tntweap", NULL, not_net | not_demo, + cheat_tntweapx, -1}, + + {"tntammo", NULL, not_net | not_demo, + cheat_tntammo }, + + {"tntammo", NULL, not_net | not_demo, + cheat_tntammox, -1}, // killough 2/16/98: end generalized weapons + + {"tnttran", NULL, always, + cheat_tnttran }, // invoke translucency // phares + + {"tntsmart", NULL, not_net | not_demo, + cheat_smart}, // killough 2/21/98: smart monster toggle + + {"tntpitch", NULL, always, + cheat_pitch}, // killough 2/21/98: pitched sound toggle + + // killough 2/21/98: reduce RSI injury by adding simpler alias sequences: + {"tntran", NULL, always, + cheat_tnttran }, // killough 2/21/98: same as tnttran + + {"tntamo", NULL, not_net | not_demo, + cheat_tntammo }, // killough 2/21/98: same as tntammo + + {"tntamo", NULL, not_net | not_demo, + cheat_tntammox, -1}, // killough 2/21/98: same as tntammo + + {"tntfast", NULL, not_net | not_demo, + cheat_fast }, // killough 3/6/98: -fast toggle + + {"tntice", NULL, not_net | not_demo, + cheat_friction }, // phares 3/10/98: toggle variable friction effects + + {"tntpush", NULL, not_net | not_demo, + cheat_pushers }, // phares 3/10/98: toggle pushers + + {NULL} // end-of-list marker +}; + +//----------------------------------------------------------------------------- + +static void cheat_mus(buf) +char buf[3]; +{ + int musnum; + + //jff 3/20/98 note: this cheat allowed in netgame/demorecord + + //jff 3/17/98 avoid musnum being negative and crashing + if (!isdigit(buf[0]) || !isdigit(buf[1])) + return; + + plyr->message = s_STSTR_MUS; // Ty 03/27/98 - externalized + + if (gamemode == commercial) + { + musnum = mus_runnin + (buf[0]-'0')*10 + buf[1]-'0' - 1; + + //jff 4/11/98 prevent IDMUS00 in DOOMII and IDMUS36 or greater + if (musnum < mus_runnin || ((buf[0]-'0')*10 + buf[1]-'0') > 35) + plyr->message = s_STSTR_NOMUS; // Ty 03/27/98 - externalized + else + { + S_ChangeMusic(musnum, 1); + idmusnum = musnum; //jff 3/17/98 remember idmus number for restore + } + } + else + { + musnum = mus_e1m1 + (buf[0]-'1')*9 + (buf[1]-'1'); + + //jff 4/11/98 prevent IDMUS0x IDMUSx0 in DOOMI and greater than introa + if (buf[0] < '1' || buf[1] < '1' || ((buf[0]-'1')*9 + buf[1]-'1') > 31) + plyr->message = s_STSTR_NOMUS; // Ty 03/27/98 - externalized + else + { + S_ChangeMusic(musnum, 1); + idmusnum = musnum; //jff 3/17/98 remember idmus number for restore + } + } +} + +// 'choppers' invulnerability & chainsaw +static void cheat_choppers() +{ + plyr->weaponowned[wp_chainsaw] = true; + plyr->powers[pw_invulnerability] = true; + plyr->message = s_STSTR_CHOPPERS; // Ty 03/27/98 - externalized +} + +static void cheat_god() +{ // 'dqd' cheat for toggleable god mode + plyr->cheats ^= CF_GODMODE; + if (plyr->cheats & CF_GODMODE) + { + if (plyr->mo) + plyr->mo->health = god_health; // Ty 03/09/98 - deh + + plyr->health = god_health; + plyr->message = s_STSTR_DQDON; // Ty 03/27/98 - externalized + } + else + plyr->message = s_STSTR_DQDOFF; // Ty 03/27/98 - externalized +} + +// CPhipps - new health and armour cheat codes +static void cheat_health() +{ + if (!(plyr->cheats & CF_GODMODE)) { + if (plyr->mo) + plyr->mo->health = mega_health; + plyr->health = mega_health; + plyr->message = s_STSTR_BEHOLDX; // Ty 03/27/98 - externalized + } +} + +static void cheat_megaarmour() +{ + plyr->armorpoints = idfa_armor; // Ty 03/09/98 - deh + plyr->armortype = idfa_armor_class; // Ty 03/09/98 - deh + plyr->message = s_STSTR_BEHOLDX; // Ty 03/27/98 - externalized +} + +static void cheat_fa() +{ + int i; + + if (!plyr->backpack) + { + for (i=0 ; imaxammo[i] *= 2; + plyr->backpack = true; + } + + plyr->armorpoints = idfa_armor; // Ty 03/09/98 - deh + plyr->armortype = idfa_armor_class; // Ty 03/09/98 - deh + + // You can't own weapons that aren't in the game // phares 02/27/98 + for (i=0;iweaponowned[i] = true; + + for (i=0;iammo[i] = plyr->maxammo[i]; + + plyr->message = s_STSTR_FAADDED; +} + +static void cheat_k() +{ + int i; + for (i=0;icards[i]) // only print message if at least one key added + { // however, caller may overwrite message anyway + plyr->cards[i] = true; + plyr->message = "Keys Added"; + } +} + +static void cheat_kfa() +{ + cheat_k(); + cheat_fa(); + plyr->message = STSTR_KFAADDED; +} + +static void cheat_noclip() +{ + // Simplified, accepting both "noclip" and "idspispopd". + // no clipping mode cheat + + plyr->message = (plyr->cheats ^= CF_NOCLIP) & CF_NOCLIP ? + s_STSTR_NCON : s_STSTR_NCOFF; // Ty 03/27/98 - externalized +} + +// 'behold?' power-up cheats (modified for infinite duration -- killough) +static void cheat_pw(int pw) +{ + if (plyr->powers[pw]) + plyr->powers[pw] = pw!=pw_strength && pw!=pw_allmap; // killough + else + { + P_GivePower(plyr, pw); + if (pw != pw_strength) + plyr->powers[pw] = -1; // infinite duration -- killough + } + plyr->message = s_STSTR_BEHOLDX; // Ty 03/27/98 - externalized +} + +// 'behold' power-up menu +static void cheat_behold() +{ + plyr->message = s_STSTR_BEHOLD; // Ty 03/27/98 - externalized +} + +// 'clev' change-level cheat +static void cheat_clev(char buf[3]) +{ + int epsd, map; + + if (gamemode == commercial) + { + epsd = 1; //jff was 0, but espd is 1-based + map = (buf[0] - '0')*10 + buf[1] - '0'; + } + else + { + epsd = buf[0] - '0'; + map = buf[1] - '0'; + } + + // Catch invalid maps. + if (epsd < 1 || map < 1 || // Ohmygod - this is not going to work. + (gamemode == retail && (epsd > 4 || map > 9 )) || + (gamemode == registered && (epsd > 3 || map > 9 )) || + (gamemode == shareware && (epsd > 1 || map > 9 )) || + (gamemode == commercial && (epsd > 1 || map > 32 )) ) //jff no 33 and 34 + return; //8/14/98 allowed + + // So be it. + + idmusnum = -1; //jff 3/17/98 revert to normal level music on IDCLEV + + plyr->message = s_STSTR_CLEV; // Ty 03/27/98 - externalized + + G_DeferedInitNew(gameskill, epsd, map); +} + +// 'mypos' for player position +// killough 2/7/98: simplified using dprintf and made output more user-friendly +static void cheat_mypos() +{ + doom_printf("Position (%d,%d,%d)\tAngle %-.0f", + players[consoleplayer].mo->x >> FRACBITS, + players[consoleplayer].mo->y >> FRACBITS, + players[consoleplayer].mo->z >> FRACBITS, + players[consoleplayer].mo->angle * (90.0/ANG90)); +} + +// cph - cheat to toggle frame rate/rendering stats display +static void cheat_rate() +{ + rendering_stats ^= 1; +} + +// compatibility cheat + +static void cheat_comp() +{ + // CPhipps - modified for new compatibility system + compatibility_level++; compatibility_level %= MAX_COMPATIBILITY_LEVEL; + // must call G_Compatibility after changing compatibility_level + // (fixes sf bug number 1558738) + G_Compatibility(); + doom_printf("New compatibility level:\n%s", + comp_lev_str[compatibility_level]); +} + +// variable friction cheat +static void cheat_friction() +{ + plyr->message = // Ty 03/27/98 - *not* externalized + (variable_friction = !variable_friction) ? "Variable Friction enabled" : + "Variable Friction disabled"; +} + + +// Pusher cheat +// phares 3/10/98 +static void cheat_pushers() +{ + plyr->message = // Ty 03/27/98 - *not* externalized + (allow_pushers = !allow_pushers) ? "Pushers enabled" : "Pushers disabled"; +} + +// translucency cheat +static void cheat_tnttran() +{ + plyr->message = // Ty 03/27/98 - *not* externalized + (general_translucency = !general_translucency) ? "Translucency enabled" : + "Translucency disabled"; + + // killough 3/1/98, 4/11/98: cache translucency map on a demand basis + if (general_translucency && !main_tranmap) + R_InitTranMap(0); +} + +static void cheat_massacre() // jff 2/01/98 kill all monsters +{ + // jff 02/01/98 'em' cheat - kill all monsters + // partially taken from Chi's .46 port + // + // killough 2/7/98: cleaned up code and changed to use dprintf; + // fixed lost soul bug (LSs left behind when PEs are killed) + + int killcount=0; + thinker_t *currentthinker = NULL; + extern void A_PainDie(mobj_t *); + + // killough 7/20/98: kill friendly monsters only if no others to kill + uint_64_t mask = MF_FRIEND; + P_MapStart(); + do + while ((currentthinker = P_NextThinker(currentthinker,th_all)) != NULL) + if (currentthinker->function == P_MobjThinker && + !(((mobj_t *) currentthinker)->flags & mask) && // killough 7/20/98 + (((mobj_t *) currentthinker)->flags & MF_COUNTKILL || + ((mobj_t *) currentthinker)->type == MT_SKULL)) + { // killough 3/6/98: kill even if PE is dead + if (((mobj_t *) currentthinker)->health > 0) + { + killcount++; + P_DamageMobj((mobj_t *)currentthinker, NULL, NULL, 10000); + } + if (((mobj_t *) currentthinker)->type == MT_PAIN) + { + A_PainDie((mobj_t *) currentthinker); // killough 2/8/98 + P_SetMobjState ((mobj_t *) currentthinker, S_PAIN_DIE6); + } + } + while (!killcount && mask ? mask=0, 1 : 0); // killough 7/20/98 + P_MapEnd(); + // killough 3/22/98: make more intelligent about plural + // Ty 03/27/98 - string(s) *not* externalized + doom_printf("%d Monster%s Killed", killcount, killcount==1 ? "" : "s"); +} + +// killough 2/7/98: move iddt cheat from am_map.c to here +// killough 3/26/98: emulate Doom better +static void cheat_ddt() +{ + extern int ddt_cheating; + if (automapmode & am_active) + ddt_cheating = (ddt_cheating+1) % 3; +} + +// killough 2/7/98: HOM autodetection +static void cheat_hom() +{ + extern int autodetect_hom; // Ty 03/27/98 - *not* externalized + plyr->message = (autodetect_hom = !autodetect_hom) ? "HOM Detection On" : + "HOM Detection Off"; +} + +// killough 3/6/98: -fast parameter toggle +static void cheat_fast() +{ + plyr->message = (fastparm = !fastparm) ? "Fast Monsters On" : + "Fast Monsters Off"; // Ty 03/27/98 - *not* externalized + G_SetFastParms(fastparm); // killough 4/10/98: set -fast parameter correctly +} + +// killough 2/16/98: keycard/skullkey cheat functions +static void cheat_tntkey() +{ + plyr->message = "Red, Yellow, Blue"; // Ty 03/27/98 - *not* externalized +} + +static void cheat_tntkeyx() +{ + plyr->message = "Card, Skull"; // Ty 03/27/98 - *not* externalized +} + +static void cheat_tntkeyxx(int key) +{ + plyr->message = (plyr->cards[key] = !plyr->cards[key]) ? + "Key Added" : "Key Removed"; // Ty 03/27/98 - *not* externalized +} + +// killough 2/16/98: generalized weapon cheats + +static void cheat_tntweap() +{ // Ty 03/27/98 - *not* externalized + plyr->message = gamemode==commercial ? // killough 2/28/98 + "Weapon number 1-9" : "Weapon number 1-8"; +} + +static void cheat_tntweapx(buf) +char buf[3]; +{ + int w = *buf - '1'; + + if ((w==wp_supershotgun && gamemode!=commercial) || // killough 2/28/98 + ((w==wp_bfg || w==wp_plasma) && gamemode==shareware)) + return; + + if (w==wp_fist) // make '1' apply beserker strength toggle + cheat_pw(pw_strength); + else + if (w >= 0 && w < NUMWEAPONS) { + if ((plyr->weaponowned[w] = !plyr->weaponowned[w])) + plyr->message = "Weapon Added"; // Ty 03/27/98 - *not* externalized + else + { + plyr->message = "Weapon Removed"; // Ty 03/27/98 - *not* externalized + if (w==plyr->readyweapon) // maybe switch if weapon removed + plyr->pendingweapon = P_SwitchWeapon(plyr); + } + } +} + +// killough 2/16/98: generalized ammo cheats +static void cheat_tntammo() +{ + plyr->message = "Ammo 1-4, Backpack"; // Ty 03/27/98 - *not* externalized +} + +static void cheat_tntammox(buf) +char buf[1]; +{ + int a = *buf - '1'; + if (*buf == 'b') // Ty 03/27/98 - strings *not* externalized + if ((plyr->backpack = !plyr->backpack)) + for (plyr->message = "Backpack Added", a=0 ; amaxammo[a] <<= 1; + else + for (plyr->message = "Backpack Removed", a=0 ; aammo[a] > (plyr->maxammo[a] >>= 1)) + plyr->ammo[a] = plyr->maxammo[a]; + } + else + if (a>=0 && amessage = (plyr->ammo[a] = !plyr->ammo[a]) ? + plyr->ammo[a] = plyr->maxammo[a], "Ammo Added" : "Ammo Removed"; + } +} + +static void cheat_smart() +{ + plyr->message = (monsters_remember = !monsters_remember) ? + "Smart Monsters Enabled" : "Smart Monsters Disabled"; +} + +static void cheat_pitch() +{ + plyr->message=(pitched_sounds = !pitched_sounds) ? "Pitch Effects Enabled" : + "Pitch Effects Disabled"; +} + +//----------------------------------------------------------------------------- +// 2/7/98: Cheat detection rewritten by Lee Killough, to avoid +// scrambling and to use a more general table-driven approach. +//----------------------------------------------------------------------------- + +#define CHEAT_ARGS_MAX 8 /* Maximum number of args at end of cheats */ + +boolean M_FindCheats(int key) +{ + static uint_64_t sr; + static char argbuf[CHEAT_ARGS_MAX+1], *arg; + static int init, argsleft, cht; + int i, ret, matchedbefore; + + // If we are expecting arguments to a cheat + // (e.g. idclev), put them in the arg buffer + + if (argsleft) + { + *arg++ = tolower(key); // store key in arg buffer + if (!--argsleft) // if last key in arg list, + cheat[cht].func(argbuf); // process the arg buffer + return 1; // affirmative response + } + + key = tolower(key) - 'a'; + if (key < 0 || key >= 32) // ignore most non-alpha cheat letters + { + sr = 0; // clear shift register + return 0; + } + + if (!init) // initialize aux entries of table + { + init = 1; + for (i=0;cheat[i].cheat;i++) + { + uint_64_t c=0, m=0; + const char *p; + + for (p=cheat[i].cheat; *p; p++) + { + unsigned key = tolower(*p)-'a'; // convert to 0-31 + if (key >= 32) // ignore most non-alpha cheat letters + continue; + c = (c<<5) + key; // shift key into code + m = (m<<5) + 31; // shift 1's into mask + } + cheat[i].code = c; // code for this cheat key + cheat[i].mask = m; // mask for this cheat key + } + } + + sr = (sr<<5) + key; // shift this key into shift register + + for (matchedbefore = ret = i = 0; cheat[i].cheat; i++) + if ((sr & cheat[i].mask) == cheat[i].code && // if match found + !(cheat[i].when & not_dm && deathmatch) && // and if cheat allowed + !(cheat[i].when & not_coop && netgame && !deathmatch) && + !(cheat[i].when & not_demo && (demorecording || demoplayback)) && + !(cheat[i].when & not_menu && menuactive) && + !(cheat[i].when & not_deh && M_CheckParm("-deh"))) { + if (cheat[i].arg < 0) // if additional args are required + { + cht = i; // remember this cheat code + arg = argbuf; // point to start of arg buffer + argsleft = -cheat[i].arg; // number of args expected + ret = 1; // responder has eaten key + } + else + if (!matchedbefore) // allow only one cheat at a time + { + matchedbefore = ret = 1; // responder has eaten key + cheat[i].func(cheat[i].arg); // call cheat handler + } + } + return ret; +} diff --git a/src/m_cheat.h b/src/m_cheat.h new file mode 100644 index 0000000..f7bafce --- /dev/null +++ b/src/m_cheat.h @@ -0,0 +1,58 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Cheat code checking. + * + *-----------------------------------------------------------------------------*/ + +#ifndef __M_CHEAT__ +#define __M_CHEAT__ + +/* killough 4/16/98: Cheat table structure */ + +extern struct cheat_s { + const char * cheat; + const char *const deh_cheat; + enum { + always = 0, + not_dm = 1, + not_coop = 2, + not_demo = 4, + not_menu = 8, + not_deh = 16, + not_net = not_dm | not_coop + } const when; + void (*const func)(); + const int arg; + uint_64_t code, mask; +} cheat[]; + +boolean M_FindCheats(int key); + +#endif diff --git a/src/m_fixed.h b/src/m_fixed.h new file mode 100644 index 0000000..2eb36db --- /dev/null +++ b/src/m_fixed.h @@ -0,0 +1,101 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Fixed point arithemtics, implementation. + * + *-----------------------------------------------------------------------------*/ + +#ifndef __M_FIXED__ +#define __M_FIXED__ + +#include "config.h" +#include "doomtype.h" + +/* + * Fixed point, 32bit as 16.16. + */ + +#define FRACBITS 16 +#define FRACUNIT (1<> (8*sizeof _t-1); + return (_t^_s)-_s; +} +/* + * Fixed Point Multiplication + */ + +/* CPhipps - made __inline__ to inline, as specified in the gcc docs + * Also made const */ + +inline static CONSTFUNC fixed_t FixedMul(fixed_t a, fixed_t b) +{ + return (fixed_t)((int_64_t) a*b >> FRACBITS); +} +/* + * Fixed Point Division + */ + +/* CPhipps - made __inline__ to inline, as specified in the gcc docs + * Also made const */ + +inline static CONSTFUNC fixed_t FixedDiv(fixed_t a, fixed_t b) +{ + return ((unsigned)D_abs(a)>>14) >= (unsigned)D_abs(b) ? ((a^b)>>31) ^ INT_MAX : + (fixed_t)(((int_64_t) a << FRACBITS) / b); +} + + +/* CPhipps - + * FixedMod - returns a % b, guaranteeing 0<=a +#include + +#include "doomdef.h" +#include "doomstat.h" +#include "dstrings.h" +#include "d_main.h" +#include "v_video.h" +#include "w_wad.h" +#include "r_main.h" +#include "hu_stuff.h" +#include "g_game.h" +#include "s_sound.h" +#include "sounds.h" +#include "m_menu.h" +#include "d_deh.h" +#include "m_misc.h" +#include "lprintf.h" +#include "am_map.h" +#include "i_main.h" +#include "i_system.h" +#include "i_video.h" +#include "i_sound.h" +#include "r_demo.h" +#include "r_fps.h" + +extern patchnum_t hu_font[HU_FONTSIZE]; +extern boolean message_dontfuckwithme; + +extern boolean chat_on; // in heads-up code + +// +// defaulted values +// + +int mouseSensitivity_horiz; // has default // killough +int mouseSensitivity_vert; // has default + +int showMessages; // Show messages has default, 0 = off, 1 = on + +int hide_setup=1; // killough 5/15/98 + +// Blocky mode, has default, 0 = high, 1 = normal +//int detailLevel; obsolete -- killough +int screenblocks; // has default + +int screenSize; // temp for screenblocks (0-9) + +int quickSaveSlot; // -1 = no quicksave slot picked! + +int messageToPrint; // 1 = message to be printed + +// CPhipps - static const +static const char* messageString; // ...and here is the message string! + +// message x & y +int messx; +int messy; +int messageLastMenuActive; + +boolean messageNeedsInput; // timed message = no input from user + +void (*messageRoutine)(int response); + +#define SAVESTRINGSIZE 24 + +/* killough 8/15/98: when changes are allowed to sync-critical variables */ +static int allow_changes(void) +{ + return !(demoplayback || demorecording || netgame); +} + +static void M_UpdateCurrent(default_t* def) +{ + /* cph - requires rewrite of m_misc.c */ + if (def->current) { + if (allow_changes()) /* killough 8/15/98 */ + *def->current = *def->location.pi; + else if (*def->current != *def->location.pi) + warn_about_changes(S_LEVWARN); /* killough 8/15/98 */ + } +} + +int warning_about_changes, print_warning_about_changes; + +/* cphipps - M_DrawBackground renamed and moved to v_video.c */ +#define M_DrawBackground V_DrawBackground + +// we are going to be entering a savegame string + +int saveStringEnter; +int saveSlot; // which slot to save in +int saveCharIndex; // which char we're editing +// old save description before edit +char saveOldString[SAVESTRINGSIZE]; + +boolean inhelpscreens; // indicates we are in or just left a help screen + +boolean menuactive; // The menus are up + +#define SKULLXOFF -32 +#define LINEHEIGHT 16 + +char savegamestrings[10][SAVESTRINGSIZE]; + +// +// MENU TYPEDEFS +// + +typedef struct +{ + short status; // 0 = no cursor here, 1 = ok, 2 = arrows ok + char name[10]; + + // choice = menu item #. + // if status = 2, + // choice=0:leftarrow,1:rightarrow + void (*routine)(int choice); + char alphaKey; // hotkey in menu +} menuitem_t; + +typedef struct menu_s +{ + short numitems; // # of menu items + struct menu_s* prevMenu; // previous menu + menuitem_t* menuitems; // menu items + void (*routine)(); // draw routine + short x; + short y; // x,y of menu + short lastOn; // last item user was on in menu +} menu_t; + +short itemOn; // menu item skull is on (for Big Font menus) +short skullAnimCounter; // skull animation counter +short whichSkull; // which skull to draw (he blinks) + +// graphic name of skulls + +const char skullName[2][/*8*/9] = {"M_SKULL1","M_SKULL2"}; + +menu_t* currentMenu; // current menudef + +// phares 3/30/98 +// externs added for setup menus + +extern int mousebfire; +extern int mousebstrafe; +extern int mousebforward; +// proff 08/17/98: Added backward to mousebuttons +extern int mousebbackward; +extern int joybfire; +extern int joybstrafe; +extern int joybuse; +extern int joybspeed; +int mapcolor_me; // cph + +extern int map_point_coordinates; // killough 10/98 + +extern char* chat_macros[]; // chat macros +extern const char* shiftxform; +extern default_t defaults[]; +extern int numdefaults; + +// end of externs added for setup menus + +// +// PROTOTYPES +// +void M_NewGame(int choice); +void M_Episode(int choice); +void M_ChooseSkill(int choice); +void M_LoadGame(int choice); +void M_SaveGame(int choice); +void M_Options(int choice); +void M_EndGame(int choice); +void M_ReadThis(int choice); +void M_ReadThis2(int choice); +void M_QuitDOOM(int choice); + +void M_ChangeMessages(int choice); +void M_ChangeSensitivity(int choice); +void M_SfxVol(int choice); +void M_MusicVol(int choice); +/* void M_ChangeDetail(int choice); unused -- killough */ +void M_SizeDisplay(int choice); +void M_StartGame(int choice); +void M_Sound(int choice); + +void M_Mouse(int choice, int *sens); /* killough */ +void M_MouseVert(int choice); +void M_MouseHoriz(int choice); +void M_DrawMouse(void); + +void M_FinishReadThis(int choice); +void M_FinishHelp(int choice); // killough 10/98 +void M_LoadSelect(int choice); +void M_SaveSelect(int choice); +void M_ReadSaveStrings(void); +void M_QuickSave(void); +void M_QuickLoad(void); + +void M_DrawMainMenu(void); +void M_DrawReadThis1(void); +void M_DrawReadThis2(void); +void M_DrawNewGame(void); +void M_DrawEpisode(void); +void M_DrawOptions(void); +void M_DrawSound(void); +void M_DrawLoad(void); +void M_DrawSave(void); +void M_DrawSetup(void); // phares 3/21/98 +void M_DrawHelp (void); // phares 5/04/98 + +void M_DrawSaveLoadBorder(int x,int y); +void M_SetupNextMenu(menu_t *menudef); +void M_DrawThermo(int x,int y,int thermWidth,int thermDot); +void M_DrawEmptyCell(menu_t *menu,int item); +void M_DrawSelCell(menu_t *menu,int item); +void M_WriteText(int x, int y, const char *string); +int M_StringWidth(const char *string); +int M_StringHeight(const char *string); +void M_StartMessage(const char *string,void *routine,boolean input); +void M_StopMessage(void); +void M_ClearMenus (void); + +// phares 3/30/98 +// prototypes added to support Setup Menus and Extended HELP screens + +int M_GetKeyString(int,int); +void M_Setup(int choice); +void M_KeyBindings(int choice); +void M_Weapons(int); +void M_StatusBar(int); +void M_Automap(int); +void M_Enemy(int); +void M_Messages(int); +void M_ChatStrings(int); +void M_InitExtendedHelp(void); +void M_ExtHelpNextScreen(int); +void M_ExtHelp(int); +static int M_GetPixelWidth(const char*); +void M_DrawKeybnd(void); +void M_DrawWeapons(void); +static void M_DrawMenuString(int,int,int); +static void M_DrawStringCentered(int,int,int,const char*); +void M_DrawStatusHUD(void); +void M_DrawExtHelp(void); +void M_DrawAutoMap(void); +void M_DrawEnemy(void); +void M_DrawMessages(void); +void M_DrawChatStrings(void); +void M_Compat(int); // killough 10/98 +void M_ChangeDemoSmoothTurns(void); +void M_General(int); // killough 10/98 +void M_DrawCompat(void); // killough 10/98 +void M_DrawGeneral(void); // killough 10/98 +void M_FullScreen(void); // nathanh 01/01 + +menu_t NewDef; // phares 5/04/98 + +// end of prototypes added to support Setup Menus and Extended HELP screens + +///////////////////////////////////////////////////////////////////////////// +// +// DOOM MENUS +// + +///////////////////////////// +// +// MAIN MENU +// + +// main_e provides numerical values for which Big Font screen you're on + +enum +{ + newgame = 0, + loadgame, + savegame, + options, + readthis, + quitdoom, + main_end +} main_e; + +// +// MainMenu is the definition of what the main menu Screen should look +// like. Each entry shows that the cursor can land on each item (1), the +// built-in graphic lump (i.e. "M_NGAME") that should be displayed, +// the program which takes over when an item is selected, and the hotkey +// associated with the item. +// + +menuitem_t MainMenu[]= +{ + {1,"M_NGAME", M_NewGame, 'n'}, + {1,"M_OPTION",M_Options, 'o'}, + {1,"M_LOADG", M_LoadGame,'l'}, + {1,"M_SAVEG", M_SaveGame,'s'}, + // Another hickup with Special edition. + {1,"M_RDTHIS",M_ReadThis,'r'}, + {1,"M_QUITG", M_QuitDOOM,'q'} +}; + +menu_t MainDef = +{ + main_end, // number of menu items + NULL, // previous menu screen + MainMenu, // table that defines menu items + M_DrawMainMenu, // drawing routine + 97,64, // initial cursor position + 0 // last menu item the user was on +}; + +// +// M_DrawMainMenu +// + +void M_DrawMainMenu(void) +{ + // CPhipps - patch drawing updated + V_DrawNamePatch(94, 2, 0, "M_DOOM", CR_DEFAULT, VPT_STRETCH); +} + +///////////////////////////// +// +// Read This! MENU 1 & 2 +// + +// There are no menu items on the Read This! screens, so read_e just +// provides a placeholder to maintain structure. + +enum +{ + rdthsempty1, + read1_end +} read_e; + +enum +{ + rdthsempty2, + read2_end +} read_e2; + +enum // killough 10/98 +{ + helpempty, + help_end +} help_e; + + +// The definitions of the Read This! screens + +menuitem_t ReadMenu1[] = +{ + {1,"",M_ReadThis2,0} +}; + +menuitem_t ReadMenu2[]= +{ + {1,"",M_FinishReadThis,0} +}; + +menuitem_t HelpMenu[]= // killough 10/98 +{ + {1,"",M_FinishHelp,0} +}; + +menu_t ReadDef1 = +{ + read1_end, + &MainDef, + ReadMenu1, + M_DrawReadThis1, + 330,175, + //280,185, // killough 2/21/98: fix help screens + 0 +}; + +menu_t ReadDef2 = +{ + read2_end, + &ReadDef1, + ReadMenu2, + M_DrawReadThis2, + 330,175, + 0 +}; + +menu_t HelpDef = // killough 10/98 +{ + help_end, + &HelpDef, + HelpMenu, + M_DrawHelp, + 330,175, + 0 +}; + +// +// M_ReadThis +// + +void M_ReadThis(int choice) +{ + M_SetupNextMenu(&ReadDef1); +} + +void M_ReadThis2(int choice) +{ + M_SetupNextMenu(&ReadDef2); +} + +void M_FinishReadThis(int choice) +{ + M_SetupNextMenu(&MainDef); +} + +void M_FinishHelp(int choice) // killough 10/98 +{ + M_SetupNextMenu(&MainDef); +} + +// +// Read This Menus +// Had a "quick hack to fix romero bug" +// +// killough 10/98: updated with new screens + +void M_DrawReadThis1(void) +{ + inhelpscreens = true; + if (gamemode == shareware) + V_DrawNamePatch(0, 0, 0, "HELP2", CR_DEFAULT, VPT_STRETCH); + else + M_DrawCredits(); +} + +// +// Read This Menus - optional second page. +// +// killough 10/98: updated with new screens + +void M_DrawReadThis2(void) +{ + inhelpscreens = true; + if (gamemode == shareware) + M_DrawCredits(); + else + V_DrawNamePatch(0, 0, 0, "CREDIT", CR_DEFAULT, VPT_STRETCH); +} + +///////////////////////////// +// +// EPISODE SELECT +// + +// +// episodes_e provides numbers for the episode menu items. The default is +// 4, to accomodate Ultimate Doom. If the user is running anything else, +// this is accounted for in the code. +// + +enum +{ + ep1, + ep2, + ep3, + ep4, + ep_end +} episodes_e; + +// The definitions of the Episodes menu + +menuitem_t EpisodeMenu[]= +{ + {1,"M_EPI1", M_Episode,'k'}, + {1,"M_EPI2", M_Episode,'t'}, + {1,"M_EPI3", M_Episode,'i'}, + {1,"M_EPI4", M_Episode,'t'} +}; + +menu_t EpiDef = +{ + ep_end, // # of menu items + &MainDef, // previous menu + EpisodeMenu, // menuitem_t -> + M_DrawEpisode, // drawing routine -> + 48,63, // x,y + ep1 // lastOn +}; + +// +// M_Episode +// +int epi; + +void M_DrawEpisode(void) +{ + // CPhipps - patch drawing updated + V_DrawNamePatch(54, 38, 0, "M_EPISOD", CR_DEFAULT, VPT_STRETCH); +} + +void M_Episode(int choice) +{ + if ( (gamemode == shareware) && choice) { + M_StartMessage(s_SWSTRING,NULL,false); // Ty 03/27/98 - externalized + M_SetupNextMenu(&ReadDef1); + return; + } + + // Yet another hack... + if ( (gamemode == registered) && (choice > 2)) + { + lprintf( LO_WARN, + "M_Episode: 4th episode requires UltimateDOOM\n"); + choice = 0; + } + + epi = choice; + M_SetupNextMenu(&NewDef); +} + +///////////////////////////// +// +// NEW GAME +// + +// numerical values for the New Game menu items + +enum +{ + killthings, + toorough, + hurtme, + violence, + nightmare, + newg_end +} newgame_e; + +// The definitions of the New Game menu + +menuitem_t NewGameMenu[]= +{ + {1,"M_JKILL", M_ChooseSkill, 'i'}, + {1,"M_ROUGH", M_ChooseSkill, 'h'}, + {1,"M_HURT", M_ChooseSkill, 'h'}, + {1,"M_ULTRA", M_ChooseSkill, 'u'}, + {1,"M_NMARE", M_ChooseSkill, 'n'} +}; + +menu_t NewDef = +{ + newg_end, // # of menu items + &EpiDef, // previous menu + NewGameMenu, // menuitem_t -> + M_DrawNewGame, // drawing routine -> + 48,63, // x,y + hurtme // lastOn +}; + +// +// M_NewGame +// + +void M_DrawNewGame(void) +{ + // CPhipps - patch drawing updated + V_DrawNamePatch(96, 14, 0, "M_NEWG", CR_DEFAULT, VPT_STRETCH); + V_DrawNamePatch(54, 38, 0, "M_SKILL",CR_DEFAULT, VPT_STRETCH); +} + +/* cph - make `New Game' restart the level in a netgame */ +static void M_RestartLevelResponse(int ch) +{ + if (ch != 'y') + return; + + if (demorecording) + exit(0); + + currentMenu->lastOn = itemOn; + M_ClearMenus (); + G_RestartLevel (); +} + +void M_NewGame(int choice) +{ + if (netgame && !demoplayback) { + if (compatibility_level < lxdoom_1_compatibility) + M_StartMessage(s_NEWGAME,NULL,false); // Ty 03/27/98 - externalized + else // CPhipps - query restarting the level + M_StartMessage(s_RESTARTLEVEL,M_RestartLevelResponse,true); + return; + } + + if (demorecording) { /* killough 5/26/98: exclude during demo recordings */ + M_StartMessage("you can't start a new game\n" + "while recording a demo!\n\n"PRESSKEY, + NULL, false); // killough 5/26/98: not externalized + return; + } + + if ( gamemode == commercial ) + M_SetupNextMenu(&NewDef); + else + M_SetupNextMenu(&EpiDef); +} + +// CPhipps - static +static void M_VerifyNightmare(int ch) +{ + if (ch != 'y') + return; + + G_DeferedInitNew(nightmare,epi+1,1); + M_ClearMenus (); +} + +void M_ChooseSkill(int choice) +{ + if (choice == nightmare) + { // Ty 03/27/98 - externalized + M_StartMessage(s_NIGHTMARE,M_VerifyNightmare,true); + return; + } + + G_DeferedInitNew(choice,epi+1,1); + M_ClearMenus (); +} + +///////////////////////////// +// +// LOAD GAME MENU +// + +// numerical values for the Load Game slots + +enum +{ + load1, + load2, + load3, + load4, + load5, + load6, + load7, //jff 3/15/98 extend number of slots + load8, + load_end +} load_e; + +// The definitions of the Load Game screen + +menuitem_t LoadMenue[]= +{ + {1,"", M_LoadSelect,'1'}, + {1,"", M_LoadSelect,'2'}, + {1,"", M_LoadSelect,'3'}, + {1,"", M_LoadSelect,'4'}, + {1,"", M_LoadSelect,'5'}, + {1,"", M_LoadSelect,'6'}, + {1,"", M_LoadSelect,'7'}, //jff 3/15/98 extend number of slots + {1,"", M_LoadSelect,'8'}, +}; + +menu_t LoadDef = +{ + load_end, + &MainDef, + LoadMenue, + M_DrawLoad, + 80,34, //jff 3/15/98 move menu up + 0 +}; + +#define LOADGRAPHIC_Y 8 + +// +// M_LoadGame & Cie. +// + +void M_DrawLoad(void) +{ + int i; + + //jff 3/15/98 use symbolic load position + // CPhipps - patch drawing updated + V_DrawNamePatch(72 ,LOADGRAPHIC_Y, 0, "M_LOADG", CR_DEFAULT, VPT_STRETCH); + for (i = 0 ; i < load_end ; i++) { + M_DrawSaveLoadBorder(LoadDef.x,LoadDef.y+LINEHEIGHT*i); + M_WriteText(LoadDef.x,LoadDef.y+LINEHEIGHT*i,savegamestrings[i]); + } +} + +// +// Draw border for the savegame description +// + +void M_DrawSaveLoadBorder(int x,int y) +{ + int i; + + V_DrawNamePatch(x-8, y+7, 0, "M_LSLEFT", CR_DEFAULT, VPT_STRETCH); + + for (i = 0 ; i < 24 ; i++) + { + V_DrawNamePatch(x, y+7, 0, "M_LSCNTR", CR_DEFAULT, VPT_STRETCH); + x += 8; + } + + V_DrawNamePatch(x, y+7, 0, "M_LSRGHT", CR_DEFAULT, VPT_STRETCH); +} + +// +// User wants to load this game +// + +void M_LoadSelect(int choice) +{ + // CPhipps - Modified so savegame filename is worked out only internal + // to g_game.c, this only passes the slot. + + G_LoadGame(choice, false); // killough 3/16/98, 5/15/98: add slot, cmd + + M_ClearMenus (); +} + +// +// killough 5/15/98: add forced loadgames +// + +static void M_VerifyForcedLoadGame(int ch) +{ + if (ch=='y') + G_ForcedLoadGame(); + free((char*)messageString); // free the message strdup()'ed below + M_ClearMenus(); +} + +void M_ForcedLoadGame(const char *msg) +{ + M_StartMessage(strdup(msg), M_VerifyForcedLoadGame, true); // free()'d above +} + +// +// Selected from DOOM menu +// + +void M_LoadGame (int choice) +{ + /* killough 5/26/98: exclude during demo recordings + * cph - unless a new demo */ + if (demorecording && (compatibility_level < prboom_2_compatibility)) + { + M_StartMessage("you can't load a game\n" + "while recording an old demo!\n\n"PRESSKEY, + NULL, false); // killough 5/26/98: not externalized + return; + } + + M_SetupNextMenu(&LoadDef); + M_ReadSaveStrings(); +} + +///////////////////////////// +// +// SAVE GAME MENU +// + +// The definitions of the Save Game screen + +menuitem_t SaveMenu[]= +{ + {1,"", M_SaveSelect,'1'}, + {1,"", M_SaveSelect,'2'}, + {1,"", M_SaveSelect,'3'}, + {1,"", M_SaveSelect,'4'}, + {1,"", M_SaveSelect,'5'}, + {1,"", M_SaveSelect,'6'}, + {1,"", M_SaveSelect,'7'}, //jff 3/15/98 extend number of slots + {1,"", M_SaveSelect,'8'}, +}; + +menu_t SaveDef = +{ + load_end, // same number of slots as the Load Game screen + &MainDef, + SaveMenu, + M_DrawSave, + 80,34, //jff 3/15/98 move menu up + 0 +}; + +// +// M_ReadSaveStrings +// read the strings from the savegame files +// +void M_ReadSaveStrings(void) +{ + int i; + + for (i = 0 ; i < load_end ; i++) { + char name[PATH_MAX+1]; // killough 3/22/98 + FILE *fp; // killough 11/98: change to use stdio + + /* killough 3/22/98 + * cph - add not-demoplayback parameter */ + G_SaveGameName(name,sizeof(name),i,false); + fp = fopen(name,"rb"); + if (!fp) { // Ty 03/27/98 - externalized: + strcpy(&savegamestrings[i][0],s_EMPTYSTRING); + LoadMenue[i].status = 0; + continue; + } + fread(&savegamestrings[i], SAVESTRINGSIZE, 1, fp); + fclose(fp); + LoadMenue[i].status = 1; + } +} + +// +// M_SaveGame & Cie. +// +void M_DrawSave(void) +{ + int i; + + //jff 3/15/98 use symbolic load position + // CPhipps - patch drawing updated + V_DrawNamePatch(72, LOADGRAPHIC_Y, 0, "M_SAVEG", CR_DEFAULT, VPT_STRETCH); + for (i = 0 ; i < load_end ; i++) + { + M_DrawSaveLoadBorder(LoadDef.x,LoadDef.y+LINEHEIGHT*i); + M_WriteText(LoadDef.x,LoadDef.y+LINEHEIGHT*i,savegamestrings[i]); + } + + if (saveStringEnter) + { + i = M_StringWidth(savegamestrings[saveSlot]); + M_WriteText(LoadDef.x + i,LoadDef.y+LINEHEIGHT*saveSlot,"_"); + } +} + +// +// M_Responder calls this when user is finished +// +static void M_DoSave(int slot) +{ + G_SaveGame (slot,savegamestrings[slot]); + M_ClearMenus (); + + // PICK QUICKSAVE SLOT YET? + if (quickSaveSlot == -2) + quickSaveSlot = slot; +} + +// +// User wants to save. Start string input for M_Responder +// +void M_SaveSelect(int choice) +{ + // we are going to be intercepting all chars + saveStringEnter = 1; + + saveSlot = choice; + strcpy(saveOldString,savegamestrings[choice]); + if (!strcmp(savegamestrings[choice],s_EMPTYSTRING)) // Ty 03/27/98 - externalized + savegamestrings[choice][0] = 0; + saveCharIndex = strlen(savegamestrings[choice]); +} + +// +// Selected from DOOM menu +// +void M_SaveGame (int choice) +{ + // killough 10/6/98: allow savegames during single-player demo playback + if (!usergame && (!demoplayback || netgame)) + { + M_StartMessage(s_SAVEDEAD,NULL,false); // Ty 03/27/98 - externalized + return; + } + + if (gamestate != GS_LEVEL) + return; + + M_SetupNextMenu(&SaveDef); + M_ReadSaveStrings(); +} + +///////////////////////////// +// +// OPTIONS MENU +// + +// numerical values for the Options menu items + +enum +{ + general, // killough 10/98 + // killough 4/6/98: move setup to be a sub-menu of OPTIONs + setup, // phares 3/21/98 + endgame, + messages, + /* detail, obsolete -- killough */ + scrnsize, + option_empty1, + mousesens, + /* option_empty2, submenu now -- killough */ + soundvol, + opt_end +} options_e; + +// The definitions of the Options menu + +menuitem_t OptionsMenu[]= +{ + // killough 4/6/98: move setup to be a sub-menu of OPTIONs + {1,"M_GENERL", M_General, 'g'}, // killough 10/98 + {1,"M_SETUP", M_Setup, 's'}, // phares 3/21/98 + {1,"M_ENDGAM", M_EndGame,'e'}, + {1,"M_MESSG", M_ChangeMessages,'m'}, + /* {1,"M_DETAIL", M_ChangeDetail,'g'}, unused -- killough */ + {2,"M_SCRNSZ", M_SizeDisplay,'s'}, + {-1,"",0}, + {1,"M_MSENS", M_ChangeSensitivity,'m'}, + /* {-1,"",0}, replaced with submenu -- killough */ + {1,"M_SVOL", M_Sound,'s'} +}; + +menu_t OptionsDef = +{ + opt_end, + &MainDef, + OptionsMenu, + M_DrawOptions, + 60,37, + 0 +}; + +// +// M_Options +// +char detailNames[2][9] = {"M_GDHIGH","M_GDLOW"}; +char msgNames[2][9] = {"M_MSGOFF","M_MSGON"}; + + +void M_DrawOptions(void) +{ + // CPhipps - patch drawing updated + // proff/nicolas 09/20/98 -- changed for hi-res + V_DrawNamePatch(108, 15, 0, "M_OPTTTL", CR_DEFAULT, VPT_STRETCH); + + V_DrawNamePatch(OptionsDef.x + 120, OptionsDef.y+LINEHEIGHT*messages, 0, + msgNames[showMessages], CR_DEFAULT, VPT_STRETCH); + + M_DrawThermo(OptionsDef.x,OptionsDef.y+LINEHEIGHT*(scrnsize+1), + 9,screenSize); +} + +void M_Options(int choice) +{ + M_SetupNextMenu(&OptionsDef); +} + +///////////////////////////// +// +// M_QuitDOOM +// +int quitsounds[8] = +{ + sfx_pldeth, + sfx_dmpain, + sfx_popain, + sfx_slop, + sfx_telept, + sfx_posit1, + sfx_posit3, + sfx_sgtatk +}; + +int quitsounds2[8] = +{ + sfx_vilact, + sfx_getpow, + sfx_boscub, + sfx_slop, + sfx_skeswg, + sfx_kntdth, + sfx_bspact, + sfx_sgtatk +}; + +static void M_QuitResponse(int ch) +{ + if (ch != 'y') + return; + if ((!netgame || demoplayback) // killough 12/98 + && !nosfxparm && snd_card) // avoid delay if no sound card + { + int i; + + if (gamemode == commercial) + S_StartSound(NULL,quitsounds2[(gametic>>2)&7]); + else + S_StartSound(NULL,quitsounds[(gametic>>2)&7]); + + // wait till all sounds stopped or 3 seconds are over + i = 30; + while (i>0) { + I_uSleep(100000); // CPhipps - don't thrash cpu in this loop + if (!I_AnySoundStillPlaying()) + break; + i--; + } + } + exit(0); // killough +} + +void M_QuitDOOM(int choice) +{ + static char endstring[160]; + + // We pick index 0 which is language sensitive, + // or one at random, between 1 and maximum number. + // Ty 03/27/98 - externalized DOSY as a string s_DOSY that's in the sprintf + if (language != english) + sprintf(endstring,"%s\n\n%s",s_DOSY, endmsg[0] ); + else // killough 1/18/98: fix endgame message calculation: + sprintf(endstring,"%s\n\n%s", endmsg[gametic%(NUM_QUITMESSAGES-1)+1], s_DOSY); + + M_StartMessage(endstring,M_QuitResponse,true); +} + +///////////////////////////// +// +// SOUND VOLUME MENU +// + +// numerical values for the Sound Volume menu items +// The 'empty' slots are where the sliding scales appear. + +enum +{ + sfx_vol, + sfx_empty1, + music_vol, + sfx_empty2, + sound_end +} sound_e; + +// The definitions of the Sound Volume menu + +menuitem_t SoundMenu[]= +{ + {2,"M_SFXVOL",M_SfxVol,'s'}, + {-1,"",0}, + {2,"M_MUSVOL",M_MusicVol,'m'}, + {-1,"",0} +}; + +menu_t SoundDef = +{ + sound_end, + &OptionsDef, + SoundMenu, + M_DrawSound, + 80,64, + 0 +}; + +// +// Change Sfx & Music volumes +// + +void M_DrawSound(void) +{ + // CPhipps - patch drawing updated + V_DrawNamePatch(60, 38, 0, "M_SVOL", CR_DEFAULT, VPT_STRETCH); + + M_DrawThermo(SoundDef.x,SoundDef.y+LINEHEIGHT*(sfx_vol+1),16,snd_SfxVolume); + + M_DrawThermo(SoundDef.x,SoundDef.y+LINEHEIGHT*(music_vol+1),16,snd_MusicVolume); +} + +void M_Sound(int choice) +{ + M_SetupNextMenu(&SoundDef); +} + +void M_SfxVol(int choice) +{ + switch(choice) + { + case 0: + if (snd_SfxVolume) + snd_SfxVolume--; + break; + case 1: + if (snd_SfxVolume < 15) + snd_SfxVolume++; + break; + } + + S_SetSfxVolume(snd_SfxVolume /* *8 */); +} + +void M_MusicVol(int choice) +{ + switch(choice) + { + case 0: + if (snd_MusicVolume) + snd_MusicVolume--; + break; + case 1: + if (snd_MusicVolume < 15) + snd_MusicVolume++; + break; + } + + S_SetMusicVolume(snd_MusicVolume /* *8 */); +} + +///////////////////////////// +// +// MOUSE SENSITIVITY MENU -- killough +// + +// numerical values for the Mouse Sensitivity menu items +// The 'empty' slots are where the sliding scales appear. + +enum +{ + mouse_horiz, + mouse_empty1, + mouse_vert, + mouse_empty2, + mouse_end +} mouse_e; + +// The definitions of the Mouse Sensitivity menu + +menuitem_t MouseMenu[]= +{ + {2,"M_HORSEN",M_MouseHoriz,'h'}, + {-1,"",0}, + {2,"M_VERSEN",M_MouseVert,'v'}, + {-1,"",0} +}; + +menu_t MouseDef = +{ + mouse_end, + &OptionsDef, + MouseMenu, + M_DrawMouse, + 60,64, + 0 +}; + + +// I'm using a scale of 100 since I don't know what's normal -- killough. + +#define MOUSE_SENS_MAX 100 + +// +// Change Mouse Sensitivities -- killough +// + +void M_DrawMouse(void) +{ + int mhmx,mvmx; /* jff 4/3/98 clamp drawn position 99max mead */ + + // CPhipps - patch drawing updated + V_DrawNamePatch(60, 38, 0, "M_MSENS", CR_DEFAULT, VPT_STRETCH); + + //jff 4/3/98 clamp horizontal sensitivity display + mhmx = mouseSensitivity_horiz>99? 99 : mouseSensitivity_horiz; /*mead*/ + M_DrawThermo(MouseDef.x,MouseDef.y+LINEHEIGHT*(mouse_horiz+1),100,mhmx); + //jff 4/3/98 clamp vertical sensitivity display + mvmx = mouseSensitivity_vert>99? 99 : mouseSensitivity_vert; /*mead*/ + M_DrawThermo(MouseDef.x,MouseDef.y+LINEHEIGHT*(mouse_vert+1),100,mvmx); +} + +void M_ChangeSensitivity(int choice) +{ + M_SetupNextMenu(&MouseDef); // killough + + // switch(choice) + // { + // case 0: + // if (mouseSensitivity) + // mouseSensitivity--; + // break; + // case 1: + // if (mouseSensitivity < 9) + // mouseSensitivity++; + // break; + // } +} + +void M_MouseHoriz(int choice) +{ + M_Mouse(choice, &mouseSensitivity_horiz); +} + +void M_MouseVert(int choice) +{ + M_Mouse(choice, &mouseSensitivity_vert); +} + +void M_Mouse(int choice, int *sens) +{ + switch(choice) + { + case 0: + if (*sens) + --*sens; + break; + case 1: + if (*sens < 99) + ++*sens; /*mead*/ + break; + } +} + +///////////////////////////// +// +// M_QuickSave +// + +char tempstring[80]; + +static void M_QuickSaveResponse(int ch) +{ + if (ch == 'y') { + M_DoSave(quickSaveSlot); + S_StartSound(NULL,sfx_swtchx); + } +} + +void M_QuickSave(void) +{ + if (!usergame && (!demoplayback || netgame)) { /* killough 10/98 */ + S_StartSound(NULL,sfx_oof); + return; + } + + if (gamestate != GS_LEVEL) + return; + + if (quickSaveSlot < 0) { + M_StartControlPanel(); + M_ReadSaveStrings(); + M_SetupNextMenu(&SaveDef); + quickSaveSlot = -2; // means to pick a slot now + return; + } + sprintf(tempstring,s_QSPROMPT,savegamestrings[quickSaveSlot]); // Ty 03/27/98 - externalized + M_StartMessage(tempstring,M_QuickSaveResponse,true); +} + +///////////////////////////// +// +// M_QuickLoad +// + +static void M_QuickLoadResponse(int ch) +{ + if (ch == 'y') { + M_LoadSelect(quickSaveSlot); + S_StartSound(NULL,sfx_swtchx); + } +} + +void M_QuickLoad(void) +{ + // cph - removed restriction against quickload in a netgame + + if (demorecording) { // killough 5/26/98: exclude during demo recordings + M_StartMessage("you can't quickload\n" + "while recording a demo!\n\n"PRESSKEY, + NULL, false); // killough 5/26/98: not externalized + return; + } + + if (quickSaveSlot < 0) { + M_StartMessage(s_QSAVESPOT,NULL,false); // Ty 03/27/98 - externalized + return; + } + sprintf(tempstring,s_QLPROMPT,savegamestrings[quickSaveSlot]); // Ty 03/27/98 - externalized + M_StartMessage(tempstring,M_QuickLoadResponse,true); +} + +///////////////////////////// +// +// M_EndGame +// + +static void M_EndGameResponse(int ch) +{ + if (ch != 'y') + return; + + // killough 5/26/98: make endgame quit if recording or playing back demo + if (demorecording || singledemo) + G_CheckDemoStatus(); + + currentMenu->lastOn = itemOn; + M_ClearMenus (); + D_StartTitle (); +} + +void M_EndGame(int choice) +{ + if (netgame) + { + M_StartMessage(s_NETEND,NULL,false); // Ty 03/27/98 - externalized + return; + } + M_StartMessage(s_ENDGAME,M_EndGameResponse,true); // Ty 03/27/98 - externalized +} + +///////////////////////////// +// +// Toggle messages on/off +// + +void M_ChangeMessages(int choice) +{ + // warning: unused parameter `int choice' + choice = 0; + showMessages = 1 - showMessages; + + if (!showMessages) + players[consoleplayer].message = s_MSGOFF; // Ty 03/27/98 - externalized + else + players[consoleplayer].message = s_MSGON ; // Ty 03/27/98 - externalized + + message_dontfuckwithme = true; +} + +///////////////////////////// +// +// CHANGE DISPLAY SIZE +// +// jff 2/23/98 restored to pre-HUD state +// hud_active controlled soley by F5=key_detail (key_hud) +// hud_displayed is toggled by + or = in fullscreen +// hud_displayed is cleared by - + +void M_SizeDisplay(int choice) +{ + switch(choice) { + case 0: + if (screenSize > 0) { + screenblocks--; + screenSize--; + hud_displayed = 0; + } + break; + case 1: + if (screenSize < 8) { + screenblocks++; + screenSize++; + } + else + hud_displayed = !hud_displayed; + break; + } + R_SetViewSize (screenblocks /*, detailLevel obsolete -- killough */); +} + +// +// End of Original Menus +// +///////////////////////////////////////////////////////////////////////////// + +///////////////////////////////////////////////////////////////////////////// +// +// SETUP MENU (phares) +// +// We've added a set of Setup Screens from which you can configure a number +// of variables w/o having to restart the game. There are 7 screens: +// +// Key Bindings +// Weapons +// Status Bar / HUD +// Automap +// Enemies +// Messages +// Chat Strings +// +// killough 10/98: added Compatibility and General menus +// + +///////////////////////////// +// +// booleans for setup screens +// these tell you what state the setup screens are in, and whether any of +// the overlay screens (automap colors, reset button message) should be +// displayed + +boolean setup_active = false; // in one of the setup screens +boolean set_keybnd_active = false; // in key binding setup screens +boolean set_weapon_active = false; // in weapons setup screen +boolean set_status_active = false; // in status bar/hud setup screen +boolean set_auto_active = false; // in automap setup screen +boolean set_enemy_active = false; // in enemies setup screen +boolean set_mess_active = false; // in messages setup screen +boolean set_chat_active = false; // in chat string setup screen +boolean setup_select = false; // changing an item +boolean setup_gather = false; // gathering keys for value +boolean colorbox_active = false; // color palette being shown +boolean default_verify = false; // verify reset defaults decision +boolean set_general_active = false; +boolean set_compat_active = false; + +///////////////////////////// +// +// set_menu_itemon is an index that starts at zero, and tells you which +// item on the current screen the cursor is sitting on. +// +// current_setup_menu is a pointer to the current setup menu table. + +static int set_menu_itemon; // which setup item is selected? // phares 3/98 +setup_menu_t* current_setup_menu; // points to current setup menu table + +///////////////////////////// +// +// The menu_buffer is used to construct strings for display on the screen. + +static char menu_buffer[64]; + +///////////////////////////// +// +// The setup_e enum is used to provide a unique number for each group of Setup +// Screens. + +enum +{ + set_compat, + set_key_bindings, + set_weapons, + set_statbar, + set_automap, + set_enemy, + set_messages, + set_chatstrings, + set_setup_end +} setup_e; + +int setup_screen; // the current setup screen. takes values from setup_e + +///////////////////////////// +// +// SetupMenu is the definition of what the main Setup Screen should look +// like. Each entry shows that the cursor can land on each item (1), the +// built-in graphic lump (i.e. "M_KEYBND") that should be displayed, +// the program which takes over when an item is selected, and the hotkey +// associated with the item. + +menuitem_t SetupMenu[]= +{ + {1,"M_COMPAT",M_Compat, 'p'}, + {1,"M_KEYBND",M_KeyBindings,'k'}, + {1,"M_WEAP" ,M_Weapons, 'w'}, + {1,"M_STAT" ,M_StatusBar, 's'}, + {1,"M_AUTO" ,M_Automap, 'a'}, + {1,"M_ENEM" ,M_Enemy, 'e'}, + {1,"M_MESS" ,M_Messages, 'm'}, + {1,"M_CHAT" ,M_ChatStrings,'c'}, +}; + +///////////////////////////// +// +// M_DoNothing does just that: nothing. Just a placeholder. + +static void M_DoNothing(int choice) +{ +} + +///////////////////////////// +// +// Items needed to satisfy the 'Big Font' menu structures: +// +// the generic_setup_e enum mimics the 'Big Font' menu structures, but +// means nothing to the Setup Menus. + +enum +{ + generic_setupempty1, + generic_setup_end +} generic_setup_e; + +// Generic_Setup is a do-nothing definition that the mainstream Menu code +// can understand, while the Setup Menu code is working. Another placeholder. + +menuitem_t Generic_Setup[] = +{ + {1,"",M_DoNothing,0} +}; + +///////////////////////////// +// +// SetupDef is the menu definition that the mainstream Menu code understands. +// This is used by M_Setup (below) to define what is drawn and what is done +// with the main Setup screen. + +menu_t SetupDef = +{ + set_setup_end, // number of Setup Menu items (Key Bindings, etc.) + &OptionsDef, // menu to return to when BACKSPACE is hit on this menu + SetupMenu, // definition of items to show on the Setup Screen + M_DrawSetup, // program that draws the Setup Screen + 59,37, // x,y position of the skull (modified when the skull is + // drawn). The skull is parked on the upper-left corner + // of the Setup screens, since it isn't needed as a cursor + 0 // last item the user was on for this menu +}; + +///////////////////////////// +// +// Here are the definitions of the individual Setup Menu screens. They +// follow the format of the 'Big Font' menu structures. See the comments +// for SetupDef (above) to help understand what each of these says. + +menu_t KeybndDef = +{ + generic_setup_end, + &SetupDef, + Generic_Setup, + M_DrawKeybnd, + 34,5, // skull drawn here + 0 +}; + +menu_t WeaponDef = +{ + generic_setup_end, + &SetupDef, + Generic_Setup, + M_DrawWeapons, + 34,5, // skull drawn here + 0 +}; + +menu_t StatusHUDDef = +{ + generic_setup_end, + &SetupDef, + Generic_Setup, + M_DrawStatusHUD, + 34,5, // skull drawn here + 0 +}; + +menu_t AutoMapDef = +{ + generic_setup_end, + &SetupDef, + Generic_Setup, + M_DrawAutoMap, + 34,5, // skull drawn here + 0 +}; + +menu_t EnemyDef = // phares 4/08/98 +{ + generic_setup_end, + &SetupDef, + Generic_Setup, + M_DrawEnemy, + 34,5, // skull drawn here + 0 +}; + +menu_t MessageDef = // phares 4/08/98 +{ + generic_setup_end, + &SetupDef, + Generic_Setup, + M_DrawMessages, + 34,5, // skull drawn here + 0 +}; + +menu_t ChatStrDef = // phares 4/10/98 +{ + generic_setup_end, + &SetupDef, + Generic_Setup, + M_DrawChatStrings, + 34,5, // skull drawn here + 0 +}; + +menu_t GeneralDef = // killough 10/98 +{ + generic_setup_end, + &OptionsDef, + Generic_Setup, + M_DrawGeneral, + 34,5, // skull drawn here + 0 +}; + +menu_t CompatDef = // killough 10/98 +{ + generic_setup_end, + &SetupDef, + Generic_Setup, + M_DrawCompat, + 34,5, // skull drawn here + 0 +}; + +///////////////////////////// +// +// Draws the Title for the main Setup screen + +void M_DrawSetup(void) +{ + // CPhipps - patch drawing updated + V_DrawNamePatch(124, 15, 0, "M_SETUP", CR_DEFAULT, VPT_STRETCH); +} + +///////////////////////////// +// +// Uses the SetupDef structure to draw the menu items for the main +// Setup screen + +void M_Setup(int choice) +{ + M_SetupNextMenu(&SetupDef); +} + +///////////////////////////// +// +// Data that's used by the Setup screen code +// +// Establish the message colors to be used + +#define CR_TITLE CR_GOLD +#define CR_SET CR_GREEN +#define CR_ITEM CR_RED +#define CR_HILITE CR_ORANGE +#define CR_SELECT CR_GRAY + +// Data used by the Automap color selection code + +#define CHIP_SIZE 7 // size of color block for colored items + +#define COLORPALXORIG ((320 - 16*(CHIP_SIZE+1))/2) +#define COLORPALYORIG ((200 - 16*(CHIP_SIZE+1))/2) + +#define PAL_BLACK 0 +#define PAL_WHITE 4 + +// Data used by the Chat String editing code + +#define CHAT_STRING_BFR_SIZE 128 + +// chat strings must fit in this screen space +// killough 10/98: reduced, for more general uses +#define MAXCHATWIDTH 272 + +int chat_index; +char* chat_string_buffer; // points to new chat strings while editing + +///////////////////////////// +// +// phares 4/17/98: +// Added 'Reset to Defaults' Button to Setup Menu screens +// This is a small button that sits in the upper-right-hand corner of +// the first screen for each group. It blinks when selected, thus the +// two patches, which it toggles back and forth. + +char ResetButtonName[2][8] = {"M_BUTT1","M_BUTT2"}; + +///////////////////////////// +// +// phares 4/18/98: +// Consolidate Item drawing code +// +// M_DrawItem draws the description of the provided item (the left-hand +// part). A different color is used for the text depending on whether the +// item is selected or not, or whether it's about to change. + +// CPhipps - static, hanging else removed, const parameter +static void M_DrawItem(const setup_menu_t* s) +{ + int x = s->m_x; + int y = s->m_y; + int flags = s->m_flags; + if (flags & S_RESET) + + // This item is the reset button + // Draw the 'off' version if this isn't the current menu item + // Draw the blinking version in tune with the blinking skull otherwise + + // proff/nicolas 09/20/98 -- changed for hi-res + // CPhipps - Patch drawing updated, reformatted + + V_DrawNamePatch(x, y, 0, ResetButtonName[(flags & (S_HILITE|S_SELECT)) ? whichSkull : 0], + CR_DEFAULT, VPT_STRETCH); + + else { // Draw the item string + char *p, *t; + int w = 0; + int color = + flags & S_SELECT ? CR_SELECT : + flags & S_HILITE ? CR_HILITE : + flags & (S_TITLE|S_NEXT|S_PREV) ? CR_TITLE : CR_ITEM; // killough 10/98 + + /* killough 10/98: + * Enhance to support multiline text separated by newlines. + * This supports multiline items on horizontally-crowded menus. + */ + + for (p = t = strdup(s->m_text); (p = strtok(p,"\n")); y += 8, p = NULL) + { /* killough 10/98: support left-justification: */ + strcpy(menu_buffer,p); + if (!(flags & S_LEFTJUST)) + w = M_GetPixelWidth(menu_buffer) + 4; + M_DrawMenuString(x - w, y ,color); + } + free(t); + } +} + +// If a number item is being changed, allow up to N keystrokes to 'gather' +// the value. Gather_count tells you how many you have so far. The legality +// of what is gathered is determined by the low/high settings for the item. + +#define MAXGATHER 5 +int gather_count; +char gather_buffer[MAXGATHER+1]; // killough 10/98: make input character-based + +///////////////////////////// +// +// phares 4/18/98: +// Consolidate Item Setting drawing code +// +// M_DrawSetting draws the setting of the provided item (the right-hand +// part. It determines the text color based on whether the item is +// selected or being changed. Then, depending on the type of item, it +// displays the appropriate setting value: yes/no, a key binding, a number, +// a paint chip, etc. + +static void M_DrawSetting(const setup_menu_t* s) +{ + int x = s->m_x, y = s->m_y, flags = s->m_flags, color; + + // Determine color of the text. This may or may not be used later, + // depending on whether the item is a text string or not. + + color = flags & S_SELECT ? CR_SELECT : flags & S_HILITE ? CR_HILITE : CR_SET; + + // Is the item a YES/NO item? + + if (flags & S_YESNO) { + strcpy(menu_buffer,*s->var.def->location.pi ? "YES" : "NO"); + M_DrawMenuString(x,y,color); + return; + } + + // Is the item a simple number? + + if (flags & S_NUM) { + // killough 10/98: We must draw differently for items being gathered. + if (flags & (S_HILITE|S_SELECT) && setup_gather) { + gather_buffer[gather_count] = 0; + strcpy(menu_buffer, gather_buffer); + } + else + sprintf(menu_buffer,"%d",*s->var.def->location.pi); + M_DrawMenuString(x,y,color); + return; + } + + // Is the item a key binding? + + if (flags & S_KEY) { // Key Binding + int *key = s->var.m_key; + + // Draw the key bound to the action + + if (key) { + M_GetKeyString(*key,0); // string to display + if (key == &key_use) { + // For the 'use' key, you have to build the string + + if (s->m_mouse) + sprintf(menu_buffer+strlen(menu_buffer), "/DBL-CLK MB%d",*s->m_mouse+1); + if (s->m_joy) + sprintf(menu_buffer+strlen(menu_buffer), "/JSB%d",*s->m_joy+1); + } + else if (key == &key_up || key == &key_speed || + key == &key_fire || key == &key_strafe) + { + if (s->m_mouse) + sprintf(menu_buffer+strlen(menu_buffer), "/MB%d", + *s->m_mouse+1); + if (s->m_joy) + sprintf(menu_buffer+strlen(menu_buffer), "/JSB%d", + *s->m_joy+1); + } + M_DrawMenuString(x,y,color); + } + return; + } + + // Is the item a weapon number? + // OR, Is the item a colored text string from the Automap? + // + // killough 10/98: removed special code, since the rest of the engine + // already takes care of it, and this code prevented the user from setting + // their overall weapons preferences while playing Doom 1. + // + // killough 11/98: consolidated weapons code with color range code + + if (flags & (S_WEAP|S_CRITEM)) // weapon number or color range + { + sprintf(menu_buffer,"%d", *s->var.def->location.pi); + M_DrawMenuString(x,y, flags & S_CRITEM ? *s->var.def->location.pi : color); + return; + } + + // Is the item a paint chip? + + if (flags & S_COLOR) // Automap paint chip + { + int ch; + + ch = *s->var.def->location.pi; + // proff 12/6/98: Drawing of colorchips completly changed for hi-res, it now uses a patch + // draw the paint chip + V_FillRect(0, x*SCREENWIDTH/320, (y-1)*SCREENHEIGHT/200, + 8*SCREENWIDTH/320, 8*SCREENHEIGHT/200, + PAL_BLACK); + V_FillRect(0, (x+1)*SCREENWIDTH/320, y*SCREENHEIGHT/200, + 6*SCREENWIDTH/320, 6*SCREENHEIGHT/200, + (byte)ch); + + if (!ch) // don't show this item in automap mode + V_DrawNamePatch(x+1,y,0,"M_PALNO", CR_DEFAULT, VPT_STRETCH); + return; + } + + // Is the item a chat string? + // killough 10/98: or a filename? + + if (flags & S_STRING) { + /* cph - cast to char* as it's really a Z_Strdup'd string (see m_misc.h) */ + char *text = (char*)*s->var.def->location.ppsz; + + // Are we editing this string? If so, display a cursor under + // the correct character. + + if (setup_select && (s->m_flags & (S_HILITE|S_SELECT))) { + int cursor_start, char_width; + char c[2]; + + // If the string is too wide for the screen, trim it back, + // one char at a time until it fits. This should only occur + // while you're editing the string. + + while (M_GetPixelWidth(text) >= MAXCHATWIDTH) { + int len = strlen(text); + text[--len] = 0; + if (chat_index > len) + chat_index--; + } + + // Find the distance from the beginning of the string to + // where the cursor should be drawn, plus the width of + // the char the cursor is under.. + + *c = text[chat_index]; // hold temporarily + c[1] = 0; + char_width = M_GetPixelWidth(c); + if (char_width == 1) + char_width = 7; // default for end of line + text[chat_index] = 0; // NULL to get cursor position + cursor_start = M_GetPixelWidth(text); + text[chat_index] = *c; // replace stored char + + // Now draw the cursor + // proff 12/6/98: Drawing of cursor changed for hi-res + V_FillRect(0, ((x+cursor_start-1)*SCREENWIDTH)/320, (y*SCREENHEIGHT)/200, + (char_width*SCREENWIDTH)/320, 9*SCREENHEIGHT/200, PAL_WHITE); + } + + // Draw the setting for the item + + strcpy(menu_buffer,text); + M_DrawMenuString(x,y,color); + return; + } + + // Is the item a selection of choices? + + if (flags & S_CHOICE) { + if (s->var.def->type == def_int) { + if (s->selectstrings == NULL) { + sprintf(menu_buffer,"%d",*s->var.def->location.pi); + } else { + strcpy(menu_buffer,s->selectstrings[*s->var.def->location.pi]); + } + } + + if (s->var.def->type == def_str) { + sprintf(menu_buffer,"%s", *s->var.def->location.ppsz); + } + + M_DrawMenuString(x,y,color); + return; + } +} + +///////////////////////////// +// +// M_DrawScreenItems takes the data for each menu item and gives it to +// the drawing routines above. + +// CPhipps - static, const parameter, formatting +static void M_DrawScreenItems(const setup_menu_t* src) +{ + if (print_warning_about_changes > 0) { /* killough 8/15/98: print warning */ + if (warning_about_changes & S_BADVAL) { + strcpy(menu_buffer, "Value out of Range"); + M_DrawMenuString(100,176,CR_RED); + } else if (warning_about_changes & S_PRGWARN) { + strcpy(menu_buffer, "Warning: Program must be restarted to see changes"); + M_DrawMenuString(3, 176, CR_RED); + } else if (warning_about_changes & S_BADVID) { + strcpy(menu_buffer, "Video mode not supported"); + M_DrawMenuString(80,176,CR_RED); + } else { + strcpy(menu_buffer, "Warning: Changes are pending until next game"); + M_DrawMenuString(18,184,CR_RED); + } + } + + while (!(src->m_flags & S_END)) { + + // See if we're to draw the item description (left-hand part) + + if (src->m_flags & S_SHOWDESC) + M_DrawItem(src); + + // See if we're to draw the setting (right-hand part) + + if (src->m_flags & S_SHOWSET) + M_DrawSetting(src); + src++; + } +} + +///////////////////////////// +// +// Data used to draw the "are you sure?" dialogue box when resetting +// to defaults. + +#define VERIFYBOXXORG 66 +#define VERIFYBOXYORG 88 +#define PAL_GRAY1 91 +#define PAL_GRAY2 98 +#define PAL_GRAY3 105 + +// And the routine to draw it. + +static void M_DrawDefVerify(void) +{ + // proff 12/6/98: Drawing of verify box changed for hi-res, it now uses a patch + V_DrawNamePatch(VERIFYBOXXORG,VERIFYBOXYORG,0,"M_VBOX",CR_DEFAULT,VPT_STRETCH); + // The blinking messages is keyed off of the blinking of the + // cursor skull. + + if (whichSkull) { // blink the text + strcpy(menu_buffer,"Reset to defaults? (Y or N)"); + M_DrawMenuString(VERIFYBOXXORG+8,VERIFYBOXYORG+8,CR_RED); + } +} + + +///////////////////////////// +// +// phares 4/18/98: +// M_DrawInstructions writes the instruction text just below the screen title +// +// cph 2006/08/06 - go back to the Boom version, and then clean up by using +// M_DrawStringCentered (much better than all those magic 'x' valies!) + +static void M_DrawInstructions(void) +{ + int flags = current_setup_menu[set_menu_itemon].m_flags; + + // There are different instruction messages depending on whether you + // are changing an item or just sitting on it. + + if (setup_select) { + switch (flags & (S_KEY | S_YESNO | S_WEAP | S_NUM | S_COLOR | S_CRITEM | S_CHAT | S_RESET | S_FILE | S_CHOICE)) { + case S_KEY: + // See if a joystick or mouse button setting is allowed for + // this item. + if (current_setup_menu[set_menu_itemon].m_mouse || current_setup_menu[set_menu_itemon].m_joy) + M_DrawStringCentered(160, 20, CR_SELECT, "Press key or button for this action"); + else + M_DrawStringCentered(160, 20, CR_SELECT, "Press key for this action"); + break; + + case S_YESNO: + M_DrawStringCentered(160, 20, CR_SELECT, "Press ENTER key to toggle"); + break; + case S_WEAP: + M_DrawStringCentered(160, 20, CR_SELECT, "Enter weapon number"); + break; + case S_NUM: + M_DrawStringCentered(160, 20, CR_SELECT, "Enter value. Press ENTER when finished."); + break; + case S_COLOR: + M_DrawStringCentered(160, 20, CR_SELECT, "Select color and press enter"); + break; + case S_CRITEM: + M_DrawStringCentered(160, 20, CR_SELECT, "Enter value"); + break; + case S_CHAT: + M_DrawStringCentered(160, 20, CR_SELECT, "Type/edit chat string and Press ENTER"); + break; + case S_FILE: + M_DrawStringCentered(160, 20, CR_SELECT, "Type/edit filename and Press ENTER"); + break; + case S_CHOICE: + M_DrawStringCentered(160, 20, CR_SELECT, "Press left or right to choose"); + break; + case S_RESET: + break; +#ifdef SIMPLECHECKS + default: + lprintf(LO_WARN,"Unrecognised menu item type %d", flags); +#endif + } + } else { + if (flags & S_RESET) + M_DrawStringCentered(160, 20, CR_HILITE, "Press ENTER key to reset to defaults"); + else + M_DrawStringCentered(160, 20, CR_HILITE, "Press Enter to Change"); + } +} + + +///////////////////////////// +// +// The Key Binding Screen tables. + +#define KB_X 160 +#define KB_PREV 57 +#define KB_NEXT 310 +#define KB_Y 31 + +// phares 4/16/98: +// X,Y position of reset button. This is the same for every screen, and is +// only defined once here. + +#define X_BUTTON 301 +#define Y_BUTTON 3 + +// Definitions of the (in this case) four key binding screens. + +setup_menu_t keys_settings1[]; +setup_menu_t keys_settings2[]; +setup_menu_t keys_settings3[]; +setup_menu_t keys_settings4[]; + +// The table which gets you from one screen table to the next. + +setup_menu_t* keys_settings[] = +{ + keys_settings1, + keys_settings2, + keys_settings3, + keys_settings4, + NULL +}; + +int mult_screens_index; // the index of the current screen in a set + +// Here's an example from this first screen, with explanations. +// +// { +// "STRAFE", // The description of the item ('strafe' key) +// S_KEY, // This is a key binding item +// m_scrn, // It belongs to the m_scrn group. Its key cannot be +// // bound to two items in this group. +// KB_X, // The X offset of the start of the right-hand side +// KB_Y+ 8*8, // The Y offset of the start of the right-hand side. +// // Always given in multiples off a baseline. +// &key_strafe, // The variable that holds the key value bound to this +// OR a string that holds the config variable name. +// OR a pointer to another setup_menu +// &mousebstrafe, // The variable that holds the mouse button bound to + // this. If zero, no mouse button can be bound here. +// &joybstrafe, // The variable that holds the joystick button bound to + // this. If zero, no mouse button can be bound here. +// } + +// The first Key Binding screen table. +// Note that the Y values are ascending. If you need to add something to +// this table, (well, this one's not a good example, because it's full) +// you need to make sure the Y values still make sense so everything gets +// displayed. +// +// Note also that the first screen of each set has a line for the reset +// button. If there is more than one screen in a set, the others don't get +// the reset button. +// +// Note also that this screen has a "NEXT ->" line. This acts like an +// item, in that 'activating' it moves you along to the next screen. If +// there's a "<- PREV" item on a screen, it behaves similarly, moving you +// to the previous screen. If you leave these off, you can't move from +// screen to screen. + +setup_menu_t keys_settings1[] = // Key Binding screen strings +{ + {"MOVEMENT" ,S_SKIP|S_TITLE,m_null,KB_X,KB_Y}, + {"FORWARD" ,S_KEY ,m_scrn,KB_X,KB_Y+1*8,{&key_up},&mousebforward}, + {"BACKWARD" ,S_KEY ,m_scrn,KB_X,KB_Y+2*8,{&key_down}}, + {"TURN LEFT" ,S_KEY ,m_scrn,KB_X,KB_Y+3*8,{&key_left}}, + {"TURN RIGHT" ,S_KEY ,m_scrn,KB_X,KB_Y+4*8,{&key_right}}, + {"RUN" ,S_KEY ,m_scrn,KB_X,KB_Y+5*8,{&key_speed},0,&joybspeed}, + {"STRAFE LEFT" ,S_KEY ,m_scrn,KB_X,KB_Y+6*8,{&key_strafeleft}}, + {"STRAFE RIGHT",S_KEY ,m_scrn,KB_X,KB_Y+7*8,{&key_straferight}}, + {"STRAFE" ,S_KEY ,m_scrn,KB_X,KB_Y+8*8,{&key_strafe},&mousebstrafe,&joybstrafe}, + {"AUTORUN" ,S_KEY ,m_scrn,KB_X,KB_Y+9*8,{&key_autorun}}, + {"180 TURN" ,S_KEY ,m_scrn,KB_X,KB_Y+10*8,{&key_reverse}}, + {"USE" ,S_KEY ,m_scrn,KB_X,KB_Y+11*8,{&key_use},&mousebforward,&joybuse}, + + {"MENUS" ,S_SKIP|S_TITLE,m_null,KB_X,KB_Y+12*8}, + {"NEXT ITEM" ,S_KEY ,m_menu,KB_X,KB_Y+13*8,{&key_menu_down}}, + {"PREV ITEM" ,S_KEY ,m_menu,KB_X,KB_Y+14*8,{&key_menu_up}}, + {"LEFT" ,S_KEY ,m_menu,KB_X,KB_Y+15*8,{&key_menu_left}}, + {"RIGHT" ,S_KEY ,m_menu,KB_X,KB_Y+16*8,{&key_menu_right}}, + {"BACKSPACE" ,S_KEY ,m_menu,KB_X,KB_Y+17*8,{&key_menu_backspace}}, + {"SELECT ITEM" ,S_KEY ,m_menu,KB_X,KB_Y+18*8,{&key_menu_enter}}, + {"EXIT" ,S_KEY ,m_menu,KB_X,KB_Y+19*8,{&key_menu_escape}}, + + // Button for resetting to defaults + {0,S_RESET,m_null,X_BUTTON,Y_BUTTON}, + + {"NEXT ->",S_SKIP|S_NEXT,m_null,KB_NEXT,KB_Y+20*8, {keys_settings2}}, + + // Final entry + {0,S_SKIP|S_END,m_null} + +}; + +setup_menu_t keys_settings2[] = // Key Binding screen strings +{ + {"SCREEN" ,S_SKIP|S_TITLE,m_null,KB_X,KB_Y}, + + // phares 4/13/98: + // key_help and key_escape can no longer be rebound. This keeps the + // player from getting themselves in a bind where they can't remember how + // to get to the menus, and can't remember how to get to the help screen + // to give them a clue as to how to get to the menus. :) + + // Also, the keys assigned to these functions cannot be bound to other + // functions. Introduce an S_KEEP flag to show that you cannot swap this + // key with other keys in the same 'group'. (m_scrn, etc.) + + {"HELP" ,S_SKIP|S_KEEP ,m_scrn,0 ,0 ,{&key_help}}, + {"MENU" ,S_SKIP|S_KEEP ,m_scrn,0 ,0 ,{&key_escape}}, + // killough 10/98: hotkey for entering setup menu: + {"SETUP" ,S_KEY ,m_scrn,KB_X,KB_Y+ 1*8,{&key_setup}}, + {"PAUSE" ,S_KEY ,m_scrn,KB_X,KB_Y+ 2*8,{&key_pause}}, + {"AUTOMAP" ,S_KEY ,m_scrn,KB_X,KB_Y+ 3*8,{&key_map}}, + {"VOLUME" ,S_KEY ,m_scrn,KB_X,KB_Y+ 4*8,{&key_soundvolume}}, + {"HUD" ,S_KEY ,m_scrn,KB_X,KB_Y+ 5*8,{&key_hud}}, + {"MESSAGES" ,S_KEY ,m_scrn,KB_X,KB_Y+ 6*8,{&key_messages}}, + {"GAMMA FIX" ,S_KEY ,m_scrn,KB_X,KB_Y+ 7*8,{&key_gamma}}, + {"SPY" ,S_KEY ,m_scrn,KB_X,KB_Y+ 8*8,{&key_spy}}, + {"LARGER VIEW" ,S_KEY ,m_scrn,KB_X,KB_Y+ 9*8,{&key_zoomin}}, + {"SMALLER VIEW",S_KEY ,m_scrn,KB_X,KB_Y+10*8,{&key_zoomout}}, + {"SCREENSHOT" ,S_KEY ,m_scrn,KB_X,KB_Y+11*8,{&key_screenshot}}, + {"GAME" ,S_SKIP|S_TITLE,m_null,KB_X,KB_Y+12*8}, + {"SAVE" ,S_KEY ,m_scrn,KB_X,KB_Y+13*8,{&key_savegame}}, + {"LOAD" ,S_KEY ,m_scrn,KB_X,KB_Y+14*8,{&key_loadgame}}, + {"QUICKSAVE" ,S_KEY ,m_scrn,KB_X,KB_Y+15*8,{&key_quicksave}}, + {"QUICKLOAD" ,S_KEY ,m_scrn,KB_X,KB_Y+16*8,{&key_quickload}}, + {"END GAME" ,S_KEY ,m_scrn,KB_X,KB_Y+17*8,{&key_endgame}}, + {"QUIT" ,S_KEY ,m_scrn,KB_X,KB_Y+18*8,{&key_quit}}, + {"<- PREV", S_SKIP|S_PREV,m_null,KB_PREV,KB_Y+20*8, {keys_settings1}}, + {"NEXT ->", S_SKIP|S_NEXT,m_null,KB_NEXT,KB_Y+20*8, {keys_settings3}}, + + // Final entry + + {0,S_SKIP|S_END,m_null} +}; + +setup_menu_t keys_settings3[] = // Key Binding screen strings +{ + {"WEAPONS" ,S_SKIP|S_TITLE,m_null,KB_X,KB_Y}, + {"FIST" ,S_KEY ,m_scrn,KB_X,KB_Y+ 1*8,{&key_weapon1}}, + {"PISTOL" ,S_KEY ,m_scrn,KB_X,KB_Y+ 2*8,{&key_weapon2}}, + {"SHOTGUN" ,S_KEY ,m_scrn,KB_X,KB_Y+ 3*8,{&key_weapon3}}, + {"CHAINGUN",S_KEY ,m_scrn,KB_X,KB_Y+ 4*8,{&key_weapon4}}, + {"ROCKET" ,S_KEY ,m_scrn,KB_X,KB_Y+ 5*8,{&key_weapon5}}, + {"PLASMA" ,S_KEY ,m_scrn,KB_X,KB_Y+ 6*8,{&key_weapon6}}, + {"BFG", S_KEY ,m_scrn,KB_X,KB_Y+ 7*8,{&key_weapon7}}, + {"CHAINSAW",S_KEY ,m_scrn,KB_X,KB_Y+ 8*8,{&key_weapon8}}, + {"SSG" ,S_KEY ,m_scrn,KB_X,KB_Y+ 9*8,{&key_weapon9}}, + {"BEST" ,S_KEY ,m_scrn,KB_X,KB_Y+10*8,{&key_weapontoggle}}, + {"FIRE" ,S_KEY ,m_scrn,KB_X,KB_Y+11*8,{&key_fire},&mousebfire,&joybfire}, + + {"<- PREV",S_SKIP|S_PREV,m_null,KB_PREV,KB_Y+20*8, {keys_settings2}}, + {"NEXT ->",S_SKIP|S_NEXT,m_null,KB_NEXT,KB_Y+20*8, {keys_settings4}}, + + // Final entry + + {0,S_SKIP|S_END,m_null} + +}; + +setup_menu_t keys_settings4[] = // Key Binding screen strings +{ + {"AUTOMAP" ,S_SKIP|S_TITLE,m_null,KB_X,KB_Y}, + {"FOLLOW" ,S_KEY ,m_map ,KB_X,KB_Y+ 1*8,{&key_map_follow}}, + {"ZOOM IN" ,S_KEY ,m_map ,KB_X,KB_Y+ 2*8,{&key_map_zoomin}}, + {"ZOOM OUT" ,S_KEY ,m_map ,KB_X,KB_Y+ 3*8,{&key_map_zoomout}}, + {"SHIFT UP" ,S_KEY ,m_map ,KB_X,KB_Y+ 4*8,{&key_map_up}}, + {"SHIFT DOWN" ,S_KEY ,m_map ,KB_X,KB_Y+ 5*8,{&key_map_down}}, + {"SHIFT LEFT" ,S_KEY ,m_map ,KB_X,KB_Y+ 6*8,{&key_map_left}}, + {"SHIFT RIGHT",S_KEY ,m_map ,KB_X,KB_Y+ 7*8,{&key_map_right}}, + {"MARK PLACE" ,S_KEY ,m_map ,KB_X,KB_Y+ 8*8,{&key_map_mark}}, + {"CLEAR MARKS",S_KEY ,m_map ,KB_X,KB_Y+ 9*8,{&key_map_clear}}, + {"FULL/ZOOM" ,S_KEY ,m_map ,KB_X,KB_Y+10*8,{&key_map_gobig}}, + {"GRID" ,S_KEY ,m_map ,KB_X,KB_Y+11*8,{&key_map_grid}}, + + {"CHATTING" ,S_SKIP|S_TITLE,m_null,KB_X,KB_Y+12*8}, + {"BEGIN CHAT" ,S_KEY ,m_scrn,KB_X,KB_Y+13*8,{&key_chat}}, + {"PLAYER 1" ,S_KEY ,m_scrn,KB_X,KB_Y+14*8,{&destination_keys[0]}}, + {"PLAYER 2" ,S_KEY ,m_scrn,KB_X,KB_Y+15*8,{&destination_keys[1]}}, + {"PLAYER 3" ,S_KEY ,m_scrn,KB_X,KB_Y+16*8,{&destination_keys[2]}}, + {"PLAYER 4" ,S_KEY ,m_scrn,KB_X,KB_Y+17*8,{&destination_keys[3]}}, + {"BACKSPACE" ,S_KEY ,m_scrn,KB_X,KB_Y+18*8,{&key_backspace}}, + {"ENTER" ,S_KEY ,m_scrn,KB_X,KB_Y+19*8,{&key_enter}}, + + {"<- PREV" ,S_SKIP|S_PREV,m_null,KB_PREV,KB_Y+20*8, {keys_settings3}}, + + // Final entry + + {0,S_SKIP|S_END,m_null} + +}; + +// Setting up for the Key Binding screen. Turn on flags, set pointers, +// locate the first item on the screen where the cursor is allowed to +// land. + +void M_KeyBindings(int choice) +{ + M_SetupNextMenu(&KeybndDef); + + setup_active = true; + setup_screen = ss_keys; + set_keybnd_active = true; + setup_select = false; + default_verify = false; + setup_gather = false; + mult_screens_index = 0; + current_setup_menu = keys_settings[0]; + set_menu_itemon = 0; + while (current_setup_menu[set_menu_itemon++].m_flags & S_SKIP); + current_setup_menu[--set_menu_itemon].m_flags |= S_HILITE; +} + +// The drawing part of the Key Bindings Setup initialization. Draw the +// background, title, instruction line, and items. + +void M_DrawKeybnd(void) + +{ + inhelpscreens = true; // killough 4/6/98: Force status bar redraw + + // Set up the Key Binding screen + + M_DrawBackground("FLOOR4_6", 0); // Draw background + // proff/nicolas 09/20/98 -- changed for hi-res + V_DrawNamePatch(84, 2, 0, "M_KEYBND", CR_DEFAULT, VPT_STRETCH); + M_DrawInstructions(); + M_DrawScreenItems(current_setup_menu); + + // If the Reset Button has been selected, an "Are you sure?" message + // is overlayed across everything else. + + if (default_verify) + M_DrawDefVerify(); +} + +///////////////////////////// +// +// The Weapon Screen tables. + +#define WP_X 203 +#define WP_Y 33 + +// There's only one weapon settings screen (for now). But since we're +// trying to fit a common description for screens, it gets a setup_menu_t, +// which only has one screen definition in it. +// +// Note that this screen has no PREV or NEXT items, since there are no +// neighboring screens. + +enum { // killough 10/98: enum for y-offset info + weap_recoil, + weap_bobbing, + weap_bfg, + weap_stub1, + weap_pref1, + weap_pref2, + weap_pref3, + weap_pref4, + weap_pref5, + weap_pref6, + weap_pref7, + weap_pref8, + weap_pref9, + weap_stub2, + weap_toggle, + weap_toggle2, +}; + +setup_menu_t weap_settings1[]; + +setup_menu_t* weap_settings[] = +{ + weap_settings1, + NULL +}; + +setup_menu_t weap_settings1[] = // Weapons Settings screen +{ + {"ENABLE RECOIL", S_YESNO,m_null,WP_X, WP_Y+ weap_recoil*8, {"weapon_recoil"}}, + {"ENABLE BOBBING",S_YESNO,m_null,WP_X, WP_Y+weap_bobbing*8, {"player_bobbing"}}, + + {"1ST CHOICE WEAPON",S_WEAP,m_null,WP_X,WP_Y+weap_pref1*8, {"weapon_choice_1"}}, + {"2nd CHOICE WEAPON",S_WEAP,m_null,WP_X,WP_Y+weap_pref2*8, {"weapon_choice_2"}}, + {"3rd CHOICE WEAPON",S_WEAP,m_null,WP_X,WP_Y+weap_pref3*8, {"weapon_choice_3"}}, + {"4th CHOICE WEAPON",S_WEAP,m_null,WP_X,WP_Y+weap_pref4*8, {"weapon_choice_4"}}, + {"5th CHOICE WEAPON",S_WEAP,m_null,WP_X,WP_Y+weap_pref5*8, {"weapon_choice_5"}}, + {"6th CHOICE WEAPON",S_WEAP,m_null,WP_X,WP_Y+weap_pref6*8, {"weapon_choice_6"}}, + {"7th CHOICE WEAPON",S_WEAP,m_null,WP_X,WP_Y+weap_pref7*8, {"weapon_choice_7"}}, + {"8th CHOICE WEAPON",S_WEAP,m_null,WP_X,WP_Y+weap_pref8*8, {"weapon_choice_8"}}, + {"9th CHOICE WEAPON",S_WEAP,m_null,WP_X,WP_Y+weap_pref9*8, {"weapon_choice_9"}}, + + {"Enable Fist/Chainsaw\n& SG/SSG toggle", S_YESNO, m_null, WP_X, + WP_Y+ weap_toggle*8, {"doom_weapon_toggles"}}, + + // Button for resetting to defaults + {0,S_RESET,m_null,X_BUTTON,Y_BUTTON}, + + // Final entry + {0,S_SKIP|S_END,m_null} + +}; + +// Setting up for the Weapons screen. Turn on flags, set pointers, +// locate the first item on the screen where the cursor is allowed to +// land. + +void M_Weapons(int choice) +{ + M_SetupNextMenu(&WeaponDef); + + setup_active = true; + setup_screen = ss_weap; + set_weapon_active = true; + setup_select = false; + default_verify = false; + setup_gather = false; + mult_screens_index = 0; + current_setup_menu = weap_settings[0]; + set_menu_itemon = 0; + while (current_setup_menu[set_menu_itemon++].m_flags & S_SKIP); + current_setup_menu[--set_menu_itemon].m_flags |= S_HILITE; +} + + +// The drawing part of the Weapons Setup initialization. Draw the +// background, title, instruction line, and items. + +void M_DrawWeapons(void) +{ + inhelpscreens = true; // killough 4/6/98: Force status bar redraw + + M_DrawBackground("FLOOR4_6", 0); // Draw background + // proff/nicolas 09/20/98 -- changed for hi-res + V_DrawNamePatch(109, 2, 0, "M_WEAP", CR_DEFAULT, VPT_STRETCH); + M_DrawInstructions(); + M_DrawScreenItems(current_setup_menu); + + // If the Reset Button has been selected, an "Are you sure?" message + // is overlayed across everything else. + + if (default_verify) + M_DrawDefVerify(); +} + +///////////////////////////// +// +// The Status Bar / HUD tables. + +#define ST_X 203 +#define ST_Y 31 + +// Screen table definitions + +setup_menu_t stat_settings1[]; + +setup_menu_t* stat_settings[] = +{ + stat_settings1, + NULL +}; + +setup_menu_t stat_settings1[] = // Status Bar and HUD Settings screen +{ + {"STATUS BAR" ,S_SKIP|S_TITLE,m_null,ST_X,ST_Y+ 1*8 }, + + {"USE RED NUMBERS" ,S_YESNO, m_null,ST_X,ST_Y+ 2*8, {"sts_always_red"}}, + {"GRAY %" ,S_YESNO, m_null,ST_X,ST_Y+ 3*8, {"sts_pct_always_gray"}}, + {"SINGLE KEY DISPLAY",S_YESNO, m_null,ST_X,ST_Y+ 4*8, {"sts_traditional_keys"}}, + + {"HEADS-UP DISPLAY" ,S_SKIP|S_TITLE,m_null,ST_X,ST_Y+ 6*8}, + + {"HIDE SECRETS" ,S_YESNO ,m_null,ST_X,ST_Y+ 7*8, {"hud_nosecrets"}}, + {"HEALTH LOW/OK" ,S_NUM ,m_null,ST_X,ST_Y+ 8*8, {"health_red"}}, + {"HEALTH OK/GOOD" ,S_NUM ,m_null,ST_X,ST_Y+ 9*8, {"health_yellow"}}, + {"HEALTH GOOD/EXTRA" ,S_NUM ,m_null,ST_X,ST_Y+10*8, {"health_green"}}, + {"ARMOR LOW/OK" ,S_NUM ,m_null,ST_X,ST_Y+11*8, {"armor_red"}}, + {"ARMOR OK/GOOD" ,S_NUM ,m_null,ST_X,ST_Y+12*8, {"armor_yellow"}}, + {"ARMOR GOOD/EXTRA" ,S_NUM ,m_null,ST_X,ST_Y+13*8, {"armor_green"}}, + {"AMMO LOW/OK" ,S_NUM ,m_null,ST_X,ST_Y+14*8, {"ammo_red"}}, + {"AMMO OK/GOOD" ,S_NUM ,m_null,ST_X,ST_Y+15*8, {"ammo_yellow"}}, + + // Button for resetting to defaults + {0,S_RESET,m_null,X_BUTTON,Y_BUTTON}, + + // Final entry + {0,S_SKIP|S_END,m_null} +}; + +// Setting up for the Status Bar / HUD screen. Turn on flags, set pointers, +// locate the first item on the screen where the cursor is allowed to +// land. + +void M_StatusBar(int choice) +{ + M_SetupNextMenu(&StatusHUDDef); + + setup_active = true; + setup_screen = ss_stat; + set_status_active = true; + setup_select = false; + default_verify = false; + setup_gather = false; + mult_screens_index = 0; + current_setup_menu = stat_settings[0]; + set_menu_itemon = 0; + while (current_setup_menu[set_menu_itemon++].m_flags & S_SKIP); + current_setup_menu[--set_menu_itemon].m_flags |= S_HILITE; +} + + +// The drawing part of the Status Bar / HUD Setup initialization. Draw the +// background, title, instruction line, and items. + +void M_DrawStatusHUD(void) + +{ + inhelpscreens = true; // killough 4/6/98: Force status bar redraw + + M_DrawBackground("FLOOR4_6", 0); // Draw background + // proff/nicolas 09/20/98 -- changed for hi-res + V_DrawNamePatch(59, 2, 0, "M_STAT", CR_DEFAULT, VPT_STRETCH); + M_DrawInstructions(); + M_DrawScreenItems(current_setup_menu); + + // If the Reset Button has been selected, an "Are you sure?" message + // is overlayed across everything else. + + if (default_verify) + M_DrawDefVerify(); +} + + +///////////////////////////// +// +// The Automap tables. + +#define AU_X 250 +#define AU_Y 31 +#define AU_PREV KB_PREV +#define AU_NEXT KB_NEXT + +setup_menu_t auto_settings1[]; +setup_menu_t auto_settings2[]; + +setup_menu_t* auto_settings[] = +{ + auto_settings1, + auto_settings2, + NULL +}; + +setup_menu_t auto_settings1[] = // 1st AutoMap Settings screen +{ + {"background", S_COLOR, m_null, AU_X, AU_Y, {"mapcolor_back"}}, + {"grid lines", S_COLOR, m_null, AU_X, AU_Y + 1*8, {"mapcolor_grid"}}, + {"normal 1s wall", S_COLOR, m_null,AU_X,AU_Y+ 2*8, {"mapcolor_wall"}}, + {"line at floor height change", S_COLOR, m_null, AU_X, AU_Y+ 3*8, {"mapcolor_fchg"}}, + {"line at ceiling height change" ,S_COLOR,m_null,AU_X,AU_Y+ 4*8, {"mapcolor_cchg"}}, + {"line at sector with floor = ceiling",S_COLOR,m_null,AU_X,AU_Y+ 5*8, {"mapcolor_clsd"}}, + {"red key" ,S_COLOR,m_null,AU_X,AU_Y+ 6*8, {"mapcolor_rkey"}}, + {"blue key" ,S_COLOR,m_null,AU_X,AU_Y+ 7*8, {"mapcolor_bkey"}}, + {"yellow key" ,S_COLOR,m_null,AU_X,AU_Y+ 8*8, {"mapcolor_ykey"}}, + {"red door" ,S_COLOR,m_null,AU_X,AU_Y+ 9*8, {"mapcolor_rdor"}}, + {"blue door" ,S_COLOR,m_null,AU_X,AU_Y+10*8, {"mapcolor_bdor"}}, + {"yellow door" ,S_COLOR,m_null,AU_X,AU_Y+11*8, {"mapcolor_ydor"}}, + + {"AUTOMAP LEVEL TITLE COLOR" ,S_CRITEM,m_null,AU_X,AU_Y+13*8, {"hudcolor_titl"}}, + {"AUTOMAP COORDINATES COLOR" ,S_CRITEM,m_null,AU_X,AU_Y+14*8, {"hudcolor_xyco"}}, + + {"Show Secrets only after entering",S_YESNO,m_null,AU_X,AU_Y+15*8, {"map_secret_after"}}, + + {"Show coordinates of automap pointer",S_YESNO,m_null,AU_X,AU_Y+16*8, {"map_point_coord"}}, // killough 10/98 + + // Button for resetting to defaults + {0,S_RESET,m_null,X_BUTTON,Y_BUTTON}, + + {"NEXT ->",S_SKIP|S_NEXT,m_null,AU_NEXT,AU_Y+20*8, {auto_settings2}}, + + // Final entry + {0,S_SKIP|S_END,m_null} + +}; + +setup_menu_t auto_settings2[] = // 2nd AutoMap Settings screen +{ + {"teleporter line" ,S_COLOR ,m_null,AU_X,AU_Y, {"mapcolor_tele"}}, + {"secret sector boundary" ,S_COLOR ,m_null,AU_X,AU_Y+ 1*8, {"mapcolor_secr"}}, + //jff 4/23/98 add exit line to automap + {"exit line" ,S_COLOR ,m_null,AU_X,AU_Y+ 2*8, {"mapcolor_exit"}}, + {"computer map unseen line" ,S_COLOR ,m_null,AU_X,AU_Y+ 3*8, {"mapcolor_unsn"}}, + {"line w/no floor/ceiling changes",S_COLOR ,m_null,AU_X,AU_Y+ 4*8, {"mapcolor_flat"}}, + {"general sprite" ,S_COLOR ,m_null,AU_X,AU_Y+ 5*8, {"mapcolor_sprt"}}, + {"countable enemy sprite" ,S_COLOR ,m_null,AU_X,AU_Y+ 6*8, {"mapcolor_enemy"}}, // cph 2006/06/30 + {"countable item sprite" ,S_COLOR ,m_null,AU_X,AU_Y+ 7*8, {"mapcolor_item"}}, // mead 3/4/2003 + {"crosshair" ,S_COLOR ,m_null,AU_X,AU_Y+ 8*8, {"mapcolor_hair"}}, + {"single player arrow" ,S_COLOR ,m_null,AU_X,AU_Y+ 9*8, {"mapcolor_sngl"}}, + {"your colour in multiplayer" ,S_COLOR ,m_null,AU_X,AU_Y+10*8, {"mapcolor_me"}}, + + {"friends" ,S_COLOR ,m_null,AU_X,AU_Y+12*8, {"mapcolor_frnd"}}, // killough 8/8/98 + + {"<- PREV",S_SKIP|S_PREV,m_null,AU_PREV,AU_Y+20*8, {auto_settings1}}, + + // Final entry + + {0,S_SKIP|S_END,m_null} + +}; + + +// Setting up for the Automap screen. Turn on flags, set pointers, +// locate the first item on the screen where the cursor is allowed to +// land. + +void M_Automap(int choice) +{ + M_SetupNextMenu(&AutoMapDef); + + setup_active = true; + setup_screen = ss_auto; + set_auto_active = true; + setup_select = false; + colorbox_active = false; + default_verify = false; + setup_gather = false; + set_menu_itemon = 0; + mult_screens_index = 0; + current_setup_menu = auto_settings[0]; + while (current_setup_menu[set_menu_itemon++].m_flags & S_SKIP); + current_setup_menu[--set_menu_itemon].m_flags |= S_HILITE; +} + +// Data used by the color palette that is displayed for the player to +// select colors. + +int color_palette_x; // X position of the cursor on the color palette +int color_palette_y; // Y position of the cursor on the color palette +byte palette_background[16*(CHIP_SIZE+1)+8]; + +// M_DrawColPal() draws the color palette when the user needs to select a +// color. + +// phares 4/1/98: now uses a single lump for the palette instead of +// building the image out of individual paint chips. + +static void M_DrawColPal(void) +{ + int cpx, cpy; + + // Draw a background, border, and paint chips + + // proff/nicolas 09/20/98 -- changed for hi-res + // CPhipps - patch drawing updated + V_DrawNamePatch(COLORPALXORIG-5, COLORPALYORIG-5, 0, "M_COLORS", CR_DEFAULT, VPT_STRETCH); + + // Draw the cursor around the paint chip + // (cpx,cpy) is the upper left-hand corner of the paint chip + + cpx = COLORPALXORIG+color_palette_x*(CHIP_SIZE+1)-1; + cpy = COLORPALYORIG+color_palette_y*(CHIP_SIZE+1)-1; + // proff 12/6/98: Drawing of colorchips completly changed for hi-res, it now uses a patch + V_DrawNamePatch(cpx,cpy,0,"M_PALSEL",CR_DEFAULT,VPT_STRETCH); // PROFF_GL_FIX +} + +// The drawing part of the Automap Setup initialization. Draw the +// background, title, instruction line, and items. + +void M_DrawAutoMap(void) + +{ + inhelpscreens = true; // killough 4/6/98: Force status bar redraw + + M_DrawBackground("FLOOR4_6", 0); // Draw background + // CPhipps - patch drawing updated + V_DrawNamePatch(109, 2, 0, "M_AUTO", CR_DEFAULT, VPT_STRETCH); + M_DrawInstructions(); + M_DrawScreenItems(current_setup_menu); + + // If a color is being selected, need to show color paint chips + + if (colorbox_active) + M_DrawColPal(); + + // If the Reset Button has been selected, an "Are you sure?" message + // is overlayed across everything else. + + else if (default_verify) + M_DrawDefVerify(); +} + + +///////////////////////////// +// +// The Enemies table. + +#define E_X 250 +#define E_Y 31 + +setup_menu_t enem_settings1[]; + +setup_menu_t* enem_settings[] = +{ + enem_settings1, + NULL +}; + +enum { + enem_infighting, + + enem_remember = 1, + + enem_backing, + enem_monkeys, + enem_avoid_hazards, + enem_friction, + enem_help_friends, + +#ifdef DOGS + enem_helpers, +#endif + + enem_distfriend, + +#ifdef DOGS + enem_dog_jumping, +#endif + + enem_end +}; + +setup_menu_t enem_settings1[] = // Enemy Settings screen +{ + // killough 7/19/98 + {"Monster Infighting When Provoked",S_YESNO,m_null,E_X,E_Y+ enem_infighting*8, {"monster_infighting"}}, + + {"Remember Previous Enemy",S_YESNO,m_null,E_X,E_Y+ enem_remember*8, {"monsters_remember"}}, + + // killough 9/8/98 + {"Monster Backing Out",S_YESNO,m_null,E_X,E_Y+ enem_backing*8, {"monster_backing"}}, + + {"Climb Steep Stairs", S_YESNO,m_null,E_X,E_Y+enem_monkeys*8, {"monkeys"}}, + + // killough 9/9/98 + {"Intelligently Avoid Hazards",S_YESNO,m_null,E_X,E_Y+ enem_avoid_hazards*8, {"monster_avoid_hazards"}}, + + // killough 10/98 + {"Affected by Friction",S_YESNO,m_null,E_X,E_Y+ enem_friction*8, {"monster_friction"}}, + + {"Rescue Dying Friends",S_YESNO,m_null,E_X,E_Y+ enem_help_friends*8, {"help_friends"}}, + +#ifdef DOGS + // killough 7/19/98 + {"Number Of Single-Player Helper Dogs",S_NUM|S_LEVWARN,m_null,E_X,E_Y+ enem_helpers*8, {"player_helpers"}}, + + // killough 8/8/98 + {"Distance Friends Stay Away",S_NUM,m_null,E_X,E_Y+ enem_distfriend*8, {"friend_distance"}}, + + {"Allow dogs to jump down",S_YESNO,m_null,E_X,E_Y+ enem_dog_jumping*8, {"dog_jumping"}}, +#endif + + // Button for resetting to defaults + {0,S_RESET,m_null,X_BUTTON,Y_BUTTON}, + + // Final entry + {0,S_SKIP|S_END,m_null} + +}; + +///////////////////////////// + +// Setting up for the Enemies screen. Turn on flags, set pointers, +// locate the first item on the screen where the cursor is allowed to +// land. + +void M_Enemy(int choice) +{ + M_SetupNextMenu(&EnemyDef); + + setup_active = true; + setup_screen = ss_enem; + set_enemy_active = true; + setup_select = false; + default_verify = false; + setup_gather = false; + mult_screens_index = 0; + current_setup_menu = enem_settings[0]; + set_menu_itemon = 0; + while (current_setup_menu[set_menu_itemon++].m_flags & S_SKIP); + current_setup_menu[--set_menu_itemon].m_flags |= S_HILITE; +} + +// The drawing part of the Enemies Setup initialization. Draw the +// background, title, instruction line, and items. + +void M_DrawEnemy(void) + +{ + inhelpscreens = true; + + M_DrawBackground("FLOOR4_6", 0); // Draw background + // proff/nicolas 09/20/98 -- changed for hi-res + V_DrawNamePatch(114, 2, 0, "M_ENEM", CR_DEFAULT, VPT_STRETCH); + M_DrawInstructions(); + M_DrawScreenItems(current_setup_menu); + + // If the Reset Button has been selected, an "Are you sure?" message + // is overlayed across everything else. + + if (default_verify) + M_DrawDefVerify(); +} + + +///////////////////////////// +// +// The General table. +// killough 10/10/98 + +extern int usejoystick, usemouse, default_mus_card, default_snd_card; +extern int detect_voices, realtic_clock_rate, tran_filter_pct; + +setup_menu_t gen_settings1[], gen_settings2[], gen_settings3[]; + +setup_menu_t* gen_settings[] = +{ + gen_settings1, + gen_settings2, + gen_settings3, + NULL +}; + +enum { + general_trans, + general_transpct, + general_fullscreen, + general_videomode, +// general_pcx, +// general_diskicon, + general_uncapped, +}; + +enum { + general_gl_texfilter, + general_gl_texformat, + general_flooroffset, +}; + +enum { +// general_sndcard, +// general_muscard, +// general_detvoices, + general_sndchan, + general_pitch +}; + +#define G_X 250 +#define G_YA 44 +#define G_YA2 (G_YA+9*8) +#define G_YA3 (G_YA2+5*8) +#define GF_X 76 + +static const char *videomodes[] = {"8bit","15bit","16bit", + "32bit","OpenGL", NULL}; + +static const char *gltexfilters[] = {"GL_NEAREST","GL_LINEAR", + "GL_LINEAR_MIPMAP_LINEAR", + NULL}; + +static const char *gltexformats[] = {"GL_RGBA","GL_RGB5_A1", + "GL_RGBA4", NULL}; + +setup_menu_t gen_settings1[] = { // General Settings screen1 + + {"Video" ,S_SKIP|S_TITLE, m_null, G_X, G_YA - 12}, + + {"Enable Translucency", S_YESNO, m_null, G_X, + G_YA + general_trans*8, {"translucency"}, 0, 0, M_Trans}, + + {"Translucency filter percentage", S_NUM, m_null, G_X, + G_YA + general_transpct*8, {"tran_filter_pct"}, 0, 0, M_Trans}, + + {"Fullscreen Video mode", S_YESNO|S_PRGWARN, m_null, G_X, + G_YA + general_fullscreen*8, {"use_fullscreen"}, 0, 0, NULL}, + + {"Video mode", S_CHOICE|S_PRGWARN, m_null, G_X, + G_YA + general_videomode*8, {"videomode"}, 0, 0, NULL, videomodes}, + + {"Uncapped Framerate", S_YESNO, m_null, G_X, + G_YA + general_uncapped*8, {"uncapped_framerate"}}, + +#ifdef GL_DOOM + {"OpenGL", S_SKIP|S_TITLE, m_null, G_X, G_YA2 - 12}, + + {"Texture filter", S_CHOICE|S_PRGWARN, m_null, G_X, + G_YA2 + general_gl_texfilter*8, {"gl_tex_filter_string"}, 0, 0, NULL, gltexfilters}, + + {"Texture format", S_CHOICE|S_PRGWARN, m_null, G_X, + G_YA2 + general_gl_texformat*8, {"gl_tex_format_string"}, 0, 0, NULL, gltexformats}, + + {"Item out of Floor offset", S_NUM, m_null, G_X, + G_YA2 + general_flooroffset*8, {"gl_sprite_offset"}}, +#endif + +#if 0 + {"PCX instead of BMP for screenshots", S_YESNO, m_null, G_X, + G_YA + general_pcx*8, {"screenshot_pcx"}}, +#endif + +#if 0 // MBF + {"Flash Icon During Disk IO", S_YESNO, m_null, G_X, + G_YA + general_diskicon*8, {"disk_icon"}}, +#endif + + {"Sound & Music", S_SKIP|S_TITLE, m_null, G_X, G_YA3 - 12}, +#if 0 // MBF + {"Sound Card", S_NUM|S_PRGWARN, m_null, G_X, + G_YA2 + general_sndcard*8, {"sound_card"}}, + + {"Music Card", S_NUM|S_PRGWARN, m_null, G_X, + G_YA2 + general_muscard*8, {"music_card"}}, + + {"Autodetect Number of Voices", S_YESNO|S_PRGWARN, m_null, G_X, + G_YA2 + general_detvoices*8, {"detect_voices"}}, +#endif + + {"Number of Sound Channels", S_NUM|S_PRGWARN, m_null, G_X, + G_YA3 + general_sndchan*8, {"snd_channels"}}, + + {"Enable v1.1 Pitch Effects", S_YESNO, m_null, G_X, + G_YA3 + general_pitch*8, {"pitched_sounds"}}, + + // Button for resetting to defaults + {0,S_RESET,m_null,X_BUTTON,Y_BUTTON}, + + {"NEXT ->",S_SKIP|S_NEXT,m_null,KB_NEXT,KB_Y+20*8, {gen_settings2}}, + + // Final entry + {0,S_SKIP|S_END,m_null} +}; + +enum { + general_mouse, + general_joy, + general_leds +}; + +enum { + general_wad1, + general_wad2, + general_deh1, + general_deh2 +}; + +enum { + general_corpse, + general_realtic, + general_smooth, + general_smoothfactor, + general_defskill, +}; + +#define G_YB 44 +#define G_YB1 (G_YB+44) +#define G_YB2 (G_YB1+52) + +static const char *gen_skillstrings[] = { + // Dummy first option because defaultskill is 1-based + "", "ITYTD", "HNTR", "HMP", "UV", "NM", NULL +}; + +setup_menu_t gen_settings2[] = { // General Settings screen2 + + {"Input Devices" ,S_SKIP|S_TITLE, m_null, G_X, G_YB - 12}, + + {"Enable Mouse", S_YESNO, m_null, G_X, + G_YB + general_mouse*8, {"use_mouse"}}, + + {"Enable Joystick", S_YESNO, m_null, G_X, + G_YB + general_joy*8, {"use_joystick"}}, + + {"Files Preloaded at Game Startup",S_SKIP|S_TITLE, m_null, G_X, + G_YB1 - 12}, + + {"WAD # 1", S_FILE, m_null, GF_X, G_YB1 + general_wad1*8, {"wadfile_1"}}, + + {"WAD #2", S_FILE, m_null, GF_X, G_YB1 + general_wad2*8, {"wadfile_2"}}, + + {"DEH/BEX # 1", S_FILE, m_null, GF_X, G_YB1 + general_deh1*8, {"dehfile_1"}}, + + {"DEH/BEX #2", S_FILE, m_null, GF_X, G_YB1 + general_deh2*8, {"dehfile_2"}}, + + {"Miscellaneous" ,S_SKIP|S_TITLE, m_null, G_X, G_YB2 - 12}, + + {"Maximum number of player corpses", S_NUM|S_PRGWARN, m_null, G_X, + G_YB2 + general_corpse*8, {"max_player_corpse"}}, + + {"Game speed, percentage of normal", S_NUM|S_PRGWARN, m_null, G_X, + G_YB2 + general_realtic*8, {"realtic_clock_rate"}}, + + {"Smooth Demo Playback", S_YESNO, m_null, G_X, + G_YB2 + general_smooth*8, {"demo_smoothturns"}, 0, 0, M_ChangeDemoSmoothTurns}, + + {"Smooth Demo Playback Factor", S_NUM, m_null, G_X, + G_YB2 + general_smoothfactor*8, {"demo_smoothturnsfactor"}, 0, 0, M_ChangeDemoSmoothTurns}, + + {"Default skill level", S_CHOICE, m_null, G_X, + G_YB2 + general_defskill*8, {"default_skill"}, 0, 0, NULL, gen_skillstrings}, + + {"<- PREV",S_SKIP|S_PREV, m_null, KB_PREV, KB_Y+20*8, {gen_settings1}}, + + {"NEXT ->",S_SKIP|S_NEXT,m_null,KB_NEXT,KB_Y+20*8, {gen_settings3}}, + + // Final entry + + {0,S_SKIP|S_END,m_null} +}; + +enum { + general_filterwall, + general_filterfloor, + general_filtersprite, + general_filterpatch, + general_filterz, + general_filter_threshold, + general_spriteedges, + general_patchedges, + general_hom, +}; + +#define G_YC 44 + +static const char *renderfilters[] = {"none", "point", "linear", "rounded"}; +static const char *edgetypes[] = {"jagged", "sloped"}; + +setup_menu_t gen_settings3[] = { // General Settings screen2 + + {"Renderer settings" ,S_SKIP|S_TITLE, m_null, G_X, G_YB - 12}, + + {"Filter for walls", S_CHOICE, m_null, G_X, + G_YC + general_filterwall*8, {"filter_wall"}, 0, 0, NULL, renderfilters}, + + {"Filter for floors/ceilings", S_CHOICE, m_null, G_X, + G_YC + general_filterfloor*8, {"filter_floor"}, 0, 0, NULL, renderfilters}, + + {"Filter for sprites", S_CHOICE, m_null, G_X, + G_YC + general_filtersprite*8, {"filter_sprite"}, 0, 0, NULL, renderfilters}, + + {"Filter for patches", S_CHOICE, m_null, G_X, + G_YC + general_filterpatch*8, {"filter_patch"}, 0, 0, NULL, renderfilters}, + + {"Filter for lighting", S_CHOICE, m_null, G_X, + G_YC + general_filterz*8, {"filter_z"}, 0, 0, NULL, renderfilters}, + + {"Drawing of sprite edges", S_CHOICE, m_null, G_X, + G_YC + general_spriteedges*8, {"sprite_edges"}, 0, 0, NULL, edgetypes}, + + {"Drawing of patch edges", S_CHOICE, m_null, G_X, + G_YC + general_patchedges*8, {"patch_edges"}, 0, 0, NULL, edgetypes}, + + {"Flashing HOM indicator", S_YESNO, m_null, G_X, + G_YC + general_hom*8, {"flashing_hom"}}, + + {"<- PREV",S_SKIP|S_PREV, m_null, KB_PREV, KB_Y+20*8, {gen_settings2}}, + + // Final entry + + {0,S_SKIP|S_END,m_null} +}; + +void M_Trans(void) // To reset translucency after setting it in menu +{ + general_translucency = default_translucency; //e6y: Fix for "translucency won't change until you restart the engine" + + if (general_translucency) + R_InitTranMap(0); +} + +void M_FullScreen(void) // To (un)set fullscreen video after menu changes +{ + I_UpdateVideoMode(); + V_SetPalette(0); +} + +void M_ChangeDemoSmoothTurns(void) +{ + if (demo_smoothturns) + gen_settings2[12].m_flags &= ~(S_SKIP|S_SELECT); + else + gen_settings2[12].m_flags |= (S_SKIP|S_SELECT); + + R_SmoothPlaying_Reset(NULL); +} + +// Setting up for the General screen. Turn on flags, set pointers, +// locate the first item on the screen where the cursor is allowed to +// land. + +void M_General(int choice) +{ + M_SetupNextMenu(&GeneralDef); + + setup_active = true; + setup_screen = ss_gen; + set_general_active = true; + setup_select = false; + default_verify = false; + setup_gather = false; + mult_screens_index = 0; + current_setup_menu = gen_settings[0]; + set_menu_itemon = 0; + while (current_setup_menu[set_menu_itemon++].m_flags & S_SKIP); + current_setup_menu[--set_menu_itemon].m_flags |= S_HILITE; +} + +// The drawing part of the General Setup initialization. Draw the +// background, title, instruction line, and items. + +void M_DrawGeneral(void) +{ + inhelpscreens = true; + + M_DrawBackground("FLOOR4_6", 0); // Draw background + // proff/nicolas 09/20/98 -- changed for hi-res + V_DrawNamePatch(114, 2, 0, "M_GENERL", CR_DEFAULT, VPT_STRETCH); + M_DrawInstructions(); + M_DrawScreenItems(current_setup_menu); + + // If the Reset Button has been selected, an "Are you sure?" message + // is overlayed across everything else. + + if (default_verify) + M_DrawDefVerify(); +} + +///////////////////////////// +// +// The Compatibility table. +// killough 10/10/98 + +#define C_X 284 +#define C_Y 32 +#define COMP_SPC 12 +#define C_NEXTPREV 131 + +setup_menu_t comp_settings1[], comp_settings2[], comp_settings3[]; + +setup_menu_t* comp_settings[] = +{ + comp_settings1, + comp_settings2, + comp_settings3, + NULL +}; + +enum +{ + compat_telefrag, + compat_dropoff, + compat_falloff, + compat_staylift, + compat_doorstuck, + compat_pursuit, + compat_vile, + compat_pain, + compat_skull, + compat_blazing, + compat_doorlight = 0, + compat_god, + compat_infcheat, + compat_zombie, + compat_skymap, + compat_stairs, + compat_floors, + compat_moveblock, + compat_model, + compat_zerotags, + compat_666 = 0, + compat_soul, + compat_maskedanim, +}; + +setup_menu_t comp_settings1[] = // Compatibility Settings screen #1 +{ + {"Any monster can telefrag on MAP30", S_YESNO, m_null, C_X, + C_Y + compat_telefrag * COMP_SPC, {"comp_telefrag"}}, + + {"Some objects never hang over tall ledges", S_YESNO, m_null, C_X, + C_Y + compat_dropoff * COMP_SPC, {"comp_dropoff"}}, + + {"Objects don't fall under their own weight", S_YESNO, m_null, C_X, + C_Y + compat_falloff * COMP_SPC, {"comp_falloff"}}, + + {"Monsters randomly walk off of moving lifts", S_YESNO, m_null, C_X, + C_Y + compat_staylift * COMP_SPC, {"comp_staylift"}}, + + {"Monsters get stuck on doortracks", S_YESNO, m_null, C_X, + C_Y + compat_doorstuck * COMP_SPC, {"comp_doorstuck"}}, + + {"Monsters don't give up pursuit of targets", S_YESNO, m_null, C_X, + C_Y + compat_pursuit * COMP_SPC, {"comp_pursuit"}}, + + {"Arch-Vile resurrects invincible ghosts", S_YESNO, m_null, C_X, + C_Y + compat_vile * COMP_SPC, {"comp_vile"}}, + + {"Pain Elementals limited to 21 lost souls", S_YESNO, m_null, C_X, + C_Y + compat_pain * COMP_SPC, {"comp_pain"}}, + + {"Lost souls get stuck behind walls", S_YESNO, m_null, C_X, + C_Y + compat_skull * COMP_SPC, {"comp_skull"}}, + + {"Blazing doors make double closing sounds", S_YESNO, m_null, C_X, + C_Y + compat_blazing * COMP_SPC, {"comp_blazing"}}, + + // Button for resetting to defaults + {0,S_RESET,m_null,X_BUTTON,Y_BUTTON}, + + {"NEXT ->",S_SKIP|S_NEXT, m_null, KB_NEXT, C_Y+C_NEXTPREV, {comp_settings2}}, + + // Final entry + {0,S_SKIP|S_END,m_null} +}; + +setup_menu_t comp_settings2[] = // Compatibility Settings screen #2 +{ + {"Tagged doors don't trigger special lighting", S_YESNO, m_null, C_X, + C_Y + compat_doorlight * COMP_SPC, {"comp_doorlight"}}, + + {"God mode isn't absolute", S_YESNO, m_null, C_X, + C_Y + compat_god * COMP_SPC, {"comp_god"}}, + + {"Powerup cheats are not infinite duration", S_YESNO, m_null, C_X, + C_Y + compat_infcheat * COMP_SPC, {"comp_infcheat"}}, + + {"Zombie players can exit levels", S_YESNO, m_null, C_X, + C_Y + compat_zombie * COMP_SPC, {"comp_zombie"}}, + + {"Sky is unaffected by invulnerability", S_YESNO, m_null, C_X, + C_Y + compat_skymap * COMP_SPC, {"comp_skymap"}}, + + {"Use exactly Doom's stairbuilding method", S_YESNO, m_null, C_X, + C_Y + compat_stairs * COMP_SPC, {"comp_stairs"}}, + + {"Use exactly Doom's floor motion behavior", S_YESNO, m_null, C_X, + C_Y + compat_floors * COMP_SPC, {"comp_floors"}}, + + {"Use exactly Doom's movement clipping code", S_YESNO, m_null, C_X, + C_Y + compat_moveblock * COMP_SPC, {"comp_moveblock"}}, + + {"Use exactly Doom's linedef trigger model", S_YESNO, m_null, C_X, + C_Y + compat_model * COMP_SPC, {"comp_model"}}, + + {"Linedef effects work with sector tag = 0", S_YESNO, m_null, C_X, + C_Y + compat_zerotags * COMP_SPC, {"comp_zerotags"}}, + + {"<- PREV", S_SKIP|S_PREV, m_null, KB_PREV, C_Y+C_NEXTPREV,{comp_settings1}}, + + {"NEXT ->",S_SKIP|S_NEXT, m_null, KB_NEXT, C_Y+C_NEXTPREV, {comp_settings3}}, + + // Final entry + + {0,S_SKIP|S_END,m_null} +}; + +setup_menu_t comp_settings3[] = // Compatibility Settings screen #2 +{ + {"All boss types can trigger tag 666 at ExM8", S_YESNO, m_null, C_X, + C_Y + compat_666 * COMP_SPC, {"comp_666"}}, + + {"Lost souls don't bounce off flat surfaces", S_YESNO, m_null, C_X, + C_Y + compat_soul * COMP_SPC, {"comp_soul"}}, + + {"2S middle textures do not animate", S_YESNO, m_null, C_X, + C_Y + compat_maskedanim * COMP_SPC, {"comp_maskedanim"}}, + + {"<- PREV", S_SKIP|S_PREV, m_null, KB_PREV, C_Y+C_NEXTPREV,{comp_settings2}}, + + // Final entry + + {0,S_SKIP|S_END,m_null} +}; + +// Setting up for the Compatibility screen. Turn on flags, set pointers, +// locate the first item on the screen where the cursor is allowed to +// land. + +void M_Compat(int choice) +{ + M_SetupNextMenu(&CompatDef); + + setup_active = true; + setup_screen = ss_comp; + set_general_active = true; + setup_select = false; + default_verify = false; + setup_gather = false; + mult_screens_index = 0; + current_setup_menu = comp_settings[0]; + set_menu_itemon = 0; + while (current_setup_menu[set_menu_itemon++].m_flags & S_SKIP); + current_setup_menu[--set_menu_itemon].m_flags |= S_HILITE; +} + +// The drawing part of the Compatibility Setup initialization. Draw the +// background, title, instruction line, and items. + +void M_DrawCompat(void) +{ + inhelpscreens = true; + + M_DrawBackground("FLOOR4_6", 0); // Draw background + V_DrawNamePatch(52,2,0,"M_COMPAT", CR_DEFAULT, VPT_STRETCH); + M_DrawInstructions(); + M_DrawScreenItems(current_setup_menu); + + // If the Reset Button has been selected, an "Are you sure?" message + // is overlayed across everything else. + + if (default_verify) + M_DrawDefVerify(); +} + +///////////////////////////// +// +// The Messages table. + +#define M_X 230 +#define M_Y 39 + +// killough 11/98: enumerated + +enum { + mess_color_play, + mess_timer, + mess_color_chat, + mess_chat_timer, + mess_color_review, + mess_timed, + mess_hud_timer, + mess_lines, + mess_scrollup, + mess_background, +}; + +setup_menu_t mess_settings1[]; + +setup_menu_t* mess_settings[] = +{ + mess_settings1, + NULL +}; + +setup_menu_t mess_settings1[] = // Messages screen +{ + {"Message Color During Play", S_CRITEM, m_null, M_X, + M_Y + mess_color_play*8, {"hudcolor_mesg"}}, + +#if 0 + {"Message Duration During Play (ms)", S_NUM, m_null, M_X, + M_Y + mess_timer*8, {"message_timer"}}, +#endif + + {"Chat Message Color", S_CRITEM, m_null, M_X, + M_Y + mess_color_chat*8, {"hudcolor_chat"}}, + +#if 0 + {"Chat Message Duration (ms)", S_NUM, m_null, M_X, + M_Y + mess_chat_timer*8, {"chat_msg_timer"}}, +#endif + + {"Message Review Color", S_CRITEM, m_null, M_X, + M_Y + mess_color_review*8, {"hudcolor_list"}}, + +#if 0 + {"Message Listing Review is Temporary", S_YESNO, m_null, M_X, + M_Y + mess_timed*8, {"hud_msg_timed"}}, + + {"Message Review Duration (ms)", S_NUM, m_null, M_X, + M_Y + mess_hud_timer*8, {"hud_msg_timer"}}, +#endif + + {"Number of Review Message Lines", S_NUM, m_null, M_X, + M_Y + mess_lines*8, {"hud_msg_lines"}}, + +#if 0 + {"Message Listing Scrolls Upwards", S_YESNO, m_null, M_X, + M_Y + mess_scrollup*8, {"hud_msg_scrollup"}}, +#endif + + {"Message Background", S_YESNO, m_null, M_X, + M_Y + mess_background*8, {"hud_list_bgon"}}, + + // Button for resetting to defaults + {0,S_RESET,m_null,X_BUTTON,Y_BUTTON}, + + // Final entry + + {0,S_SKIP|S_END,m_null} +}; + + +// Setting up for the Messages screen. Turn on flags, set pointers, +// locate the first item on the screen where the cursor is allowed to +// land. + +void M_Messages(int choice) +{ + M_SetupNextMenu(&MessageDef); + + setup_active = true; + setup_screen = ss_mess; + set_mess_active = true; + setup_select = false; + default_verify = false; + setup_gather = false; + mult_screens_index = 0; + current_setup_menu = mess_settings[0]; + set_menu_itemon = 0; + while (current_setup_menu[set_menu_itemon++].m_flags & S_SKIP); + current_setup_menu[--set_menu_itemon].m_flags |= S_HILITE; +} + + +// The drawing part of the Messages Setup initialization. Draw the +// background, title, instruction line, and items. + +void M_DrawMessages(void) + +{ + inhelpscreens = true; + M_DrawBackground("FLOOR4_6", 0); // Draw background + // CPhipps - patch drawing updated + V_DrawNamePatch(103, 2, 0, "M_MESS", CR_DEFAULT, VPT_STRETCH); + M_DrawInstructions(); + M_DrawScreenItems(current_setup_menu); + if (default_verify) + M_DrawDefVerify(); +} + + +///////////////////////////// +// +// The Chat Strings table. + +#define CS_X 20 +#define CS_Y (31+8) + +setup_menu_t chat_settings1[]; + +setup_menu_t* chat_settings[] = +{ + chat_settings1, + NULL +}; + +setup_menu_t chat_settings1[] = // Chat Strings screen +{ + {"1",S_CHAT,m_null,CS_X,CS_Y+ 1*8, {"chatmacro1"}}, + {"2",S_CHAT,m_null,CS_X,CS_Y+ 2*8, {"chatmacro2"}}, + {"3",S_CHAT,m_null,CS_X,CS_Y+ 3*8, {"chatmacro3"}}, + {"4",S_CHAT,m_null,CS_X,CS_Y+ 4*8, {"chatmacro4"}}, + {"5",S_CHAT,m_null,CS_X,CS_Y+ 5*8, {"chatmacro5"}}, + {"6",S_CHAT,m_null,CS_X,CS_Y+ 6*8, {"chatmacro6"}}, + {"7",S_CHAT,m_null,CS_X,CS_Y+ 7*8, {"chatmacro7"}}, + {"8",S_CHAT,m_null,CS_X,CS_Y+ 8*8, {"chatmacro8"}}, + {"9",S_CHAT,m_null,CS_X,CS_Y+ 9*8, {"chatmacro9"}}, + {"0",S_CHAT,m_null,CS_X,CS_Y+10*8, {"chatmacro0"}}, + + // Button for resetting to defaults + {0,S_RESET,m_null,X_BUTTON,Y_BUTTON}, + + // Final entry + {0,S_SKIP|S_END,m_null} + +}; + +// Setting up for the Chat Strings screen. Turn on flags, set pointers, +// locate the first item on the screen where the cursor is allowed to +// land. + +void M_ChatStrings(int choice) +{ + M_SetupNextMenu(&ChatStrDef); + setup_active = true; + setup_screen = ss_chat; + set_chat_active = true; + setup_select = false; + default_verify = false; + setup_gather = false; + mult_screens_index = 0; + current_setup_menu = chat_settings[0]; + set_menu_itemon = 0; + while (current_setup_menu[set_menu_itemon++].m_flags & S_SKIP); + current_setup_menu[--set_menu_itemon].m_flags |= S_HILITE; +} + +// The drawing part of the Chat Strings Setup initialization. Draw the +// background, title, instruction line, and items. + +void M_DrawChatStrings(void) + +{ + inhelpscreens = true; + M_DrawBackground("FLOOR4_6", 0); // Draw background + // CPhipps - patch drawing updated + V_DrawNamePatch(83, 2, 0, "M_CHAT", CR_DEFAULT, VPT_STRETCH); + M_DrawInstructions(); + M_DrawScreenItems(current_setup_menu); + + // If the Reset Button has been selected, an "Are you sure?" message + // is overlayed across everything else. + + if (default_verify) + M_DrawDefVerify(); +} + +///////////////////////////// +// +// General routines used by the Setup screens. +// + +static boolean shiftdown = false; // phares 4/10/98: SHIFT key down or not + +// phares 4/17/98: +// M_SelectDone() gets called when you have finished entering your +// Setup Menu item change. + +static void M_SelectDone(setup_menu_t* ptr) +{ + ptr->m_flags &= ~S_SELECT; + ptr->m_flags |= S_HILITE; + S_StartSound(NULL,sfx_itemup); + setup_select = false; + colorbox_active = false; + if (print_warning_about_changes) // killough 8/15/98 + print_warning_about_changes--; +} + +// phares 4/21/98: +// Array of setup screens used by M_ResetDefaults() + +static setup_menu_t **setup_screens[] = +{ + keys_settings, + weap_settings, + stat_settings, + auto_settings, + enem_settings, + mess_settings, + chat_settings, + gen_settings, // killough 10/98 + comp_settings, +}; + +// phares 4/19/98: +// M_ResetDefaults() resets all values for a setup screen to default values +// +// killough 10/98: rewritten to fix bugs and warn about pending changes + +static void M_ResetDefaults(void) +{ + int i; //e6y + + default_t *dp; + int warn = 0; + + // Look through the defaults table and reset every variable that + // belongs to the group we're interested in. + // + // killough: However, only reset variables whose field in the + // current setup screen is the same as in the defaults table. + // i.e. only reset variables really in the current setup screen. + + // e6y + // Fixed crash while trying to read data past array end + // All previous versions of prboom worked only by a lucky accident + // old code: for (dp = defaults; dp->name; dp++) + for (i = 0; i < numdefaults ; i++) + { + dp = &defaults[i]; + + if (dp->setupscreen == setup_screen) + { + setup_menu_t **l, *p; + for (l = setup_screens[setup_screen-1]; *l; l++) + for (p = *l; !(p->m_flags & S_END); p++) + if (p->m_flags & S_HASDEFPTR ? p->var.def == dp : + p->var.m_key == dp->location.pi || + p->m_mouse == dp->location.pi || + p->m_joy == dp->location.pi) + { + if (IS_STRING(*dp)) + free((char*)*dp->location.ppsz), + *dp->location.ppsz = strdup(dp->defaultvalue.psz); + else + *dp->location.pi = dp->defaultvalue.i; + +#if 0 + if (p->m_flags & (S_LEVWARN | S_PRGWARN)) + warn |= p->m_flags & (S_LEVWARN | S_PRGWARN); + else + if (dp->current) + if (allow_changes()) + *dp->current = *dp->location.pi; + else + warn |= S_LEVWARN; +#endif + if (p->action) + p->action(); + + goto end; + } + end:; + } + } + + if (warn) + warn_about_changes(warn); +} + +// +// M_InitDefaults() +// +// killough 11/98: +// +// This function converts all setup menu entries consisting of cfg +// variable names, into pointers to the corresponding default[] +// array entry. var.name becomes converted to var.def. +// + +static void M_InitDefaults(void) +{ + setup_menu_t *const *p, *t; + default_t *dp; + int i; + for (i = 0; i < ss_max-1; i++) + for (p = setup_screens[i]; *p; p++) + for (t = *p; !(t->m_flags & S_END); t++) + if (t->m_flags & S_HASDEFPTR) { + if (!(dp = M_LookupDefault(t->var.name))) + I_Error("M_InitDefaults: Couldn't find config variable %s", t->var.name); + else + (t->var.def = dp)->setup_menu = t; + } +} + +// +// End of Setup Screens. +// +///////////////////////////////////////////////////////////////////////////// + +///////////////////////////////////////////////////////////////////////////// +// +// Start of Extended HELP screens // phares 3/30/98 +// +// The wad designer can define a set of extended HELP screens for their own +// information display. These screens should be 320x200 graphic lumps +// defined in a separate wad. They should be named "HELP01" through "HELP99". +// "HELP01" is shown after the regular BOOM Dynamic HELP screen, and ENTER +// and BACKSPACE keys move the player through the HELP set. +// +// Rather than define a set of menu definitions for each of the possible +// HELP screens, one definition is used, and is altered on the fly +// depending on what HELPnn lumps the game finds. + +// phares 3/30/98: +// Extended Help Screen variables + +int extended_help_count; // number of user-defined help screens found +int extended_help_index; // index of current extended help screen + +menuitem_t ExtHelpMenu[] = +{ + {1,"",M_ExtHelpNextScreen,0} +}; + +menu_t ExtHelpDef = +{ + 1, // # of menu items + &ReadDef1, // previous menu + ExtHelpMenu, // menuitem_t -> + M_DrawExtHelp, // drawing routine -> + 330,181, // x,y + 0 // lastOn +}; + +// M_ExtHelpNextScreen establishes the number of the next HELP screen in +// the series. + +void M_ExtHelpNextScreen(int choice) +{ + choice = 0; + if (++extended_help_index > extended_help_count) + { + + // when finished with extended help screens, return to Main Menu + + extended_help_index = 1; + M_SetupNextMenu(&MainDef); + } +} + +// phares 3/30/98: +// Routine to look for HELPnn screens and create a menu +// definition structure that defines extended help screens. + +void M_InitExtendedHelp(void) + +{ + int index,i; + char namebfr[] = { "HELPnn"} ; + + extended_help_count = 0; + for (index = 1 ; index < 100 ; index++) { + namebfr[4] = index/10 + 0x30; + namebfr[5] = index%10 + 0x30; + i = W_CheckNumForName(namebfr); + if (i == -1) { + if (extended_help_count) { + if (gamemode == commercial) { + ExtHelpDef.prevMenu = &ReadDef1; /* previous menu */ + ReadMenu1[0].routine = M_ExtHelp; + } else { + ExtHelpDef.prevMenu = &ReadDef2; /* previous menu */ + ReadMenu2[0].routine = M_ExtHelp; + } + } + return; + } + extended_help_count++; + } + +} + +// Initialization for the extended HELP screens. + +void M_ExtHelp(int choice) +{ + choice = 0; + extended_help_index = 1; // Start with first extended help screen + M_SetupNextMenu(&ExtHelpDef); +} + +// Initialize the drawing part of the extended HELP screens. + +void M_DrawExtHelp(void) +{ + char namebfr[10] = { "HELPnn" }; // CPhipps - make it local & writable + + inhelpscreens = true; // killough 5/1/98 + namebfr[4] = extended_help_index/10 + 0x30; + namebfr[5] = extended_help_index%10 + 0x30; + // CPhipps - patch drawing updated + V_DrawNamePatch(0, 0, 0, namebfr, CR_DEFAULT, VPT_STRETCH); +} + +// +// End of Extended HELP screens // phares 3/30/98 +// +//////////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////////// +// +// Dynamic HELP screen // phares 3/2/98 +// +// Rather than providing the static HELP screens from DOOM and its versions, +// BOOM provides the player with a dynamic HELP screen that displays the +// current settings of major key bindings. +// +// The Dynamic HELP screen is defined in a manner similar to that used for +// the Setup Screens above. +// +// M_GetKeyString finds the correct string to represent the key binding +// for the current item being drawn. + +int M_GetKeyString(int c,int offset) +{ + const char* s; + + if (c >= 33 && c <= 126) { + + // The '=', ',', and '.' keys originally meant the shifted + // versions of those keys, but w/o having to shift them in + // the game. Any actions that are mapped to these keys will + // still mean their shifted versions. Could be changed later + // if someone can come up with a better way to deal with them. + + if (c == '=') // probably means the '+' key? + c = '+'; + else if (c == ',') // probably means the '<' key? + c = '<'; + else if (c == '.') // probably means the '>' key? + c = '>'; + menu_buffer[offset++] = c; // Just insert the ascii key + menu_buffer[offset] = 0; + + } else { + + // Retrieve 4-letter (max) string representing the key + + // cph - Keypad keys, general code reorganisation to + // make this smaller and neater. + if ((0x100 <= c) && (c < 0x200)) { + if (c == KEYD_KEYPADENTER) + s = "PADE"; + else { + strcpy(&menu_buffer[offset], "PAD"); + offset+=4; + menu_buffer[offset-1] = c & 0xff; + menu_buffer[offset] = 0; + } + } else if ((KEYD_F1 <= c) && (c < KEYD_F10)) { + menu_buffer[offset++] = 'F'; + menu_buffer[offset++] = '1' + c - KEYD_F1; + menu_buffer[offset] = 0; + } else { + switch(c) { + case KEYD_TAB: s = "TAB"; break; + case KEYD_ENTER: s = "ENTR"; break; + case KEYD_ESCAPE: s = "ESC"; break; + case KEYD_SPACEBAR: s = "SPAC"; break; + case KEYD_BACKSPACE: s = "BACK"; break; + case KEYD_RCTRL: s = "CTRL"; break; + case KEYD_LEFTARROW: s = "LARR"; break; + case KEYD_UPARROW: s = "UARR"; break; + case KEYD_RIGHTARROW: s = "RARR"; break; + case KEYD_DOWNARROW: s = "DARR"; break; + case KEYD_RSHIFT: s = "SHFT"; break; + case KEYD_RALT: s = "ALT"; break; + case KEYD_CAPSLOCK: s = "CAPS"; break; + case KEYD_SCROLLLOCK: s = "SCRL"; break; + case KEYD_HOME: s = "HOME"; break; + case KEYD_PAGEUP: s = "PGUP"; break; + case KEYD_END: s = "END"; break; + case KEYD_PAGEDOWN: s = "PGDN"; break; + case KEYD_INSERT: s = "INST"; break; + case KEYD_DEL: s = "DEL"; break; + case KEYD_F10: s = "F10"; break; + case KEYD_F11: s = "F11"; break; + case KEYD_F12: s = "F12"; break; + case KEYD_PAUSE: s = "PAUS"; break; + default: s = "JUNK"; break; + } + + if (s) { // cph - Slight code change + strcpy(&menu_buffer[offset],s); // string to display + offset += strlen(s); + } + } + } + return offset; +} + +// +// The Dynamic HELP screen table. + +#define KT_X1 283 +#define KT_X2 172 +#define KT_X3 87 + +#define KT_Y1 2 +#define KT_Y2 118 +#define KT_Y3 102 + +setup_menu_t helpstrings[] = // HELP screen strings +{ + {"SCREEN" ,S_SKIP|S_TITLE,m_null,KT_X1,KT_Y1}, + {"HELP" ,S_SKIP|S_KEY,m_null,KT_X1,KT_Y1+ 1*8,{&key_help}}, + {"MENU" ,S_SKIP|S_KEY,m_null,KT_X1,KT_Y1+ 2*8,{&key_escape}}, + {"SETUP" ,S_SKIP|S_KEY,m_null,KT_X1,KT_Y1+ 3*8,{&key_setup}}, + {"PAUSE" ,S_SKIP|S_KEY,m_null,KT_X1,KT_Y1+ 4*8,{&key_pause}}, + {"AUTOMAP" ,S_SKIP|S_KEY,m_null,KT_X1,KT_Y1+ 5*8,{&key_map}}, + {"SOUND VOLUME",S_SKIP|S_KEY,m_null,KT_X1,KT_Y1+ 6*8,{&key_soundvolume}}, + {"HUD" ,S_SKIP|S_KEY,m_null,KT_X1,KT_Y1+ 7*8,{&key_hud}}, + {"MESSAGES" ,S_SKIP|S_KEY,m_null,KT_X1,KT_Y1+ 8*8,{&key_messages}}, + {"GAMMA FIX" ,S_SKIP|S_KEY,m_null,KT_X1,KT_Y1+ 9*8,{&key_gamma}}, + {"SPY" ,S_SKIP|S_KEY,m_null,KT_X1,KT_Y1+10*8,{&key_spy}}, + {"LARGER VIEW" ,S_SKIP|S_KEY,m_null,KT_X1,KT_Y1+11*8,{&key_zoomin}}, + {"SMALLER VIEW",S_SKIP|S_KEY,m_null,KT_X1,KT_Y1+12*8,{&key_zoomout}}, + {"SCREENSHOT" ,S_SKIP|S_KEY,m_null,KT_X1,KT_Y1+13*8,{&key_screenshot}}, + + {"AUTOMAP" ,S_SKIP|S_TITLE,m_null,KT_X1,KT_Y2}, + {"FOLLOW MODE" ,S_SKIP|S_KEY,m_null,KT_X1,KT_Y2+ 1*8,{&key_map_follow}}, + {"ZOOM IN" ,S_SKIP|S_KEY,m_null,KT_X1,KT_Y2+ 2*8,{&key_map_zoomin}}, + {"ZOOM OUT" ,S_SKIP|S_KEY,m_null,KT_X1,KT_Y2+ 3*8,{&key_map_zoomout}}, + {"MARK PLACE" ,S_SKIP|S_KEY,m_null,KT_X1,KT_Y2+ 4*8,{&key_map_mark}}, + {"CLEAR MARKS" ,S_SKIP|S_KEY,m_null,KT_X1,KT_Y2+ 5*8,{&key_map_clear}}, + {"FULL/ZOOM" ,S_SKIP|S_KEY,m_null,KT_X1,KT_Y2+ 6*8,{&key_map_gobig}}, + {"GRID" ,S_SKIP|S_KEY,m_null,KT_X1,KT_Y2+ 7*8,{&key_map_grid}}, + + {"WEAPONS" ,S_SKIP|S_TITLE,m_null,KT_X3,KT_Y1}, + {"FIST" ,S_SKIP|S_KEY,m_null,KT_X3,KT_Y1+ 1*8,{&key_weapon1}}, + {"PISTOL" ,S_SKIP|S_KEY,m_null,KT_X3,KT_Y1+ 2*8,{&key_weapon2}}, + {"SHOTGUN" ,S_SKIP|S_KEY,m_null,KT_X3,KT_Y1+ 3*8,{&key_weapon3}}, + {"CHAINGUN" ,S_SKIP|S_KEY,m_null,KT_X3,KT_Y1+ 4*8,{&key_weapon4}}, + {"ROCKET" ,S_SKIP|S_KEY,m_null,KT_X3,KT_Y1+ 5*8,{&key_weapon5}}, + {"PLASMA" ,S_SKIP|S_KEY,m_null,KT_X3,KT_Y1+ 6*8,{&key_weapon6}}, + {"BFG 9000" ,S_SKIP|S_KEY,m_null,KT_X3,KT_Y1+ 7*8,{&key_weapon7}}, + {"CHAINSAW" ,S_SKIP|S_KEY,m_null,KT_X3,KT_Y1+ 8*8,{&key_weapon8}}, + {"SSG" ,S_SKIP|S_KEY,m_null,KT_X3,KT_Y1+ 9*8,{&key_weapon9}}, + {"BEST" ,S_SKIP|S_KEY,m_null,KT_X3,KT_Y1+10*8,{&key_weapontoggle}}, + {"FIRE" ,S_SKIP|S_KEY,m_null,KT_X3,KT_Y1+11*8,{&key_fire},&mousebfire,&joybfire}, + + {"MOVEMENT" ,S_SKIP|S_TITLE,m_null,KT_X3,KT_Y3}, + {"FORWARD" ,S_SKIP|S_KEY,m_null,KT_X3,KT_Y3+ 1*8,{&key_up},&mousebforward}, + {"BACKWARD" ,S_SKIP|S_KEY,m_null,KT_X3,KT_Y3+ 2*8,{&key_down}}, + {"TURN LEFT" ,S_SKIP|S_KEY,m_null,KT_X3,KT_Y3+ 3*8,{&key_left}}, + {"TURN RIGHT" ,S_SKIP|S_KEY,m_null,KT_X3,KT_Y3+ 4*8,{&key_right}}, + {"RUN" ,S_SKIP|S_KEY,m_null,KT_X3,KT_Y3+ 5*8,{&key_speed},0,&joybspeed}, + {"STRAFE LEFT" ,S_SKIP|S_KEY,m_null,KT_X3,KT_Y3+ 6*8,{&key_strafeleft}}, + {"STRAFE RIGHT",S_SKIP|S_KEY,m_null,KT_X3,KT_Y3+ 7*8,{&key_straferight}}, + {"STRAFE" ,S_SKIP|S_KEY,m_null,KT_X3,KT_Y3+ 8*8,{&key_strafe},&mousebstrafe,&joybstrafe}, + {"AUTORUN" ,S_SKIP|S_KEY,m_null,KT_X3,KT_Y3+ 9*8,{&key_autorun}}, + {"180 TURN" ,S_SKIP|S_KEY,m_null,KT_X3,KT_Y3+10*8,{&key_reverse}}, + {"USE" ,S_SKIP|S_KEY,m_null,KT_X3,KT_Y3+11*8,{&key_use},&mousebforward,&joybuse}, + + {"GAME" ,S_SKIP|S_TITLE,m_null,KT_X2,KT_Y1}, + {"SAVE" ,S_SKIP|S_KEY,m_null,KT_X2,KT_Y1+ 1*8,{&key_savegame}}, + {"LOAD" ,S_SKIP|S_KEY,m_null,KT_X2,KT_Y1+ 2*8,{&key_loadgame}}, + {"QUICKSAVE" ,S_SKIP|S_KEY,m_null,KT_X2,KT_Y1+ 3*8,{&key_quicksave}}, + {"END GAME" ,S_SKIP|S_KEY,m_null,KT_X2,KT_Y1+ 4*8,{&key_endgame}}, + {"QUICKLOAD" ,S_SKIP|S_KEY,m_null,KT_X2,KT_Y1+ 5*8,{&key_quickload}}, + {"QUIT" ,S_SKIP|S_KEY,m_null,KT_X2,KT_Y1+ 6*8,{&key_quit}}, + + // Final entry + + {0,S_SKIP|S_END,m_null} +}; + +#define SPACEWIDTH 4 + +/* cph 2006/08/06 + * M_DrawString() is the old M_DrawMenuString, except that it is not tied to + * menu_buffer - no reason to force all the callers to write into one array! */ + +static void M_DrawString(int cx, int cy, int color, const char* ch) +{ + int w; + int c; + + while (*ch) { + c = *ch++; // get next char + c = toupper(c) - HU_FONTSTART; + if (c < 0 || c> HU_FONTSIZE) + { + cx += SPACEWIDTH; // space + continue; + } + w = hu_font[c].width; + if (cx + w > 320) + break; + + // V_DrawpatchTranslated() will draw the string in the + // desired color, colrngs[color] + + // CPhipps - patch drawing updated + V_DrawNumPatch(cx, cy, 0, hu_font[c].lumpnum, color, VPT_STRETCH | VPT_TRANS); + // The screen is cramped, so trim one unit from each + // character so they butt up against each other. + cx += w - 1; + } +} + +// M_DrawMenuString() draws the string in menu_buffer[] + +static void M_DrawMenuString(int cx, int cy, int color) +{ + M_DrawString(cx, cy, color, menu_buffer); +} + +// M_GetPixelWidth() returns the number of pixels in the width of +// the string, NOT the number of chars in the string. + +static int M_GetPixelWidth(const char* ch) +{ + int len = 0; + int c; + + while (*ch) { + c = *ch++; // pick up next char + c = toupper(c) - HU_FONTSTART; + if (c < 0 || c > HU_FONTSIZE) + { + len += SPACEWIDTH; // space + continue; + } + len += hu_font[c].width; + len--; // adjust so everything fits + } + len++; // replace what you took away on the last char only + return len; +} + +static void M_DrawStringCentered(int cx, int cy, int color, const char* ch) +{ + M_DrawString(cx - M_GetPixelWidth(ch)/2, cy, color, ch); +} + +// +// M_DrawHelp +// +// This displays the help screen + +void M_DrawHelp (void) +{ + inhelpscreens = true; // killough 10/98 + M_DrawBackground("FLOOR4_6", 0); + + M_DrawScreenItems(helpstrings); +} + +// +// End of Dynamic HELP screen // phares 3/2/98 +// +//////////////////////////////////////////////////////////////////////////// + +enum { + prog, + prog_stub, + prog_stub1, + prog_stub2, + adcr +}; + +enum { + cr_prog=0, + cr_adcr=2, +}; + +#define CR_S 9 +#define CR_X 20 +#define CR_X2 50 +#define CR_Y 32 +#define CR_SH 9 + +setup_menu_t cred_settings[]={ + + {"Programmers",S_SKIP|S_CREDIT|S_LEFTJUST,m_null, CR_X, CR_Y + CR_S*prog + CR_SH*cr_prog}, + {"Florian 'Proff' Schulze",S_SKIP|S_CREDIT|S_LEFTJUST,m_null, CR_X2, CR_Y + CR_S*(prog+1) + CR_SH*cr_prog}, + {"Colin Phipps",S_SKIP|S_CREDIT|S_LEFTJUST,m_null, CR_X2, CR_Y + CR_S*(prog+2) + CR_SH*cr_prog}, + {"Neil Stevens",S_SKIP|S_CREDIT|S_LEFTJUST,m_null, CR_X2, CR_Y + CR_S*(prog+3) + CR_SH*cr_prog}, + {"Andrey Budko",S_SKIP|S_CREDIT|S_LEFTJUST,m_null, CR_X2, CR_Y + CR_S*(prog+4) + CR_SH*cr_prog}, + + {"Additional Credit To",S_SKIP|S_CREDIT|S_LEFTJUST,m_null, CR_X, CR_Y + CR_S*adcr + CR_SH*cr_adcr}, + {"id Software for DOOM",S_SKIP|S_CREDIT|S_LEFTJUST,m_null, CR_X2, CR_Y + CR_S*(adcr+1)+CR_SH*cr_adcr}, + {"TeamTNT for BOOM",S_SKIP|S_CREDIT|S_LEFTJUST,m_null, CR_X2, CR_Y + CR_S*(adcr+2)+CR_SH*cr_adcr}, + {"Lee Killough for MBF",S_SKIP|S_CREDIT|S_LEFTJUST,m_null, CR_X2, CR_Y + CR_S*(adcr+3)+CR_SH*cr_adcr}, + {"The DOSDoom-Team for DOSDOOM",S_SKIP|S_CREDIT|S_LEFTJUST,m_null, CR_X2, CR_Y + CR_S*(adcr+4)+CR_SH*cr_adcr}, + {"Randy Heit for ZDOOM",S_SKIP|S_CREDIT|S_LEFTJUST,m_null, CR_X2, CR_Y + CR_S*(adcr+5)+CR_SH*cr_adcr}, + {"Michael 'Kodak' Ryssen for DOOMGL",S_SKIP|S_CREDIT|S_LEFTJUST,m_null, CR_X2, CR_Y + CR_S*(adcr+6)+CR_SH*cr_adcr}, + {"Jess Haas for lSDLDoom",S_SKIP|S_CREDIT|S_LEFTJUST,m_null, CR_X2, CR_Y + CR_S*(adcr+7) + CR_SH*cr_adcr}, + {"all others who helped (see AUTHORS file)",S_SKIP|S_CREDIT|S_LEFTJUST,m_null, CR_X2, CR_Y + CR_S*(adcr+8)+CR_SH*cr_adcr}, + + {0,S_SKIP|S_END,m_null} +}; + +void M_DrawCredits(void) // killough 10/98: credit screen +{ + inhelpscreens = true; + M_DrawBackground(gamemode==shareware ? "CEIL5_1" : "MFLR8_4", 0); + V_DrawNamePatch(115,9,0, "PRBOOM",CR_GOLD, VPT_TRANS | VPT_STRETCH); + M_DrawScreenItems(cred_settings); +} + +static int M_IndexInChoices(const char *str, const char **choices) { + int i = 0; + + while (*choices != NULL) { + if (!strcmp(str, *choices)) + return i; + i++; + choices++; + } + return 0; +} + +///////////////////////////////////////////////////////////////////////////// +// +// M_Responder +// +// Examines incoming keystrokes and button pushes and determines some +// action based on the state of the system. +// + +boolean M_Responder (event_t* ev) { + int ch; + int i; + static int joywait = 0; + static int mousewait = 0; + static int mousey = 0; + static int lasty = 0; + static int mousex = 0; + static int lastx = 0; + + ch = -1; // will be changed to a legit char if we're going to use it here + + // Process joystick input + + if (ev->type == ev_joystick && joywait < I_GetTime()) { + if (ev->data3 == -1) + { + ch = key_menu_up; // phares 3/7/98 + joywait = I_GetTime() + 5; + } + else if (ev->data3 == 1) + { + ch = key_menu_down; // phares 3/7/98 + joywait = I_GetTime() + 5; + } + + if (ev->data2 == -1) + { + ch = key_menu_left; // phares 3/7/98 + joywait = I_GetTime() + 2; + } + else if (ev->data2 == 1) + { + ch = key_menu_right; // phares 3/7/98 + joywait = I_GetTime() + 2; + } + + if (ev->data1&1) + { + ch = key_menu_enter; // phares 3/7/98 + joywait = I_GetTime() + 5; + } + + if (ev->data1&2) + { + ch = key_menu_backspace; // phares 3/7/98 + joywait = I_GetTime() + 5; + } + + // phares 4/4/98: + // Handle joystick buttons 3 and 4, and allow them to pass down + // to where key binding can eat them. + + if (setup_active && set_keybnd_active) { + if (ev->data1&4) { + ch = 0; // meaningless, just to get you past the check for -1 + joywait = I_GetTime() + 5; + } + if (ev->data1&8) { + ch = 0; // meaningless, just to get you past the check for -1 + joywait = I_GetTime() + 5; + } + } + + } else { + // Mouse input processing removed + + // Process keyboard input + + if (ev->type == ev_keydown) + { + ch = ev->data1; // phares 4/11/98: + if (ch == KEYD_RSHIFT) // For chat string processing, need + shiftdown = true; // to know when shift key is up or + } // down so you can get at the !,#, + else if (ev->type == ev_keyup) // etc. keys. Keydowns are allowed + if (ev->data1 == KEYD_RSHIFT) // past this point, but keyups aren't + shiftdown = false; // so we need to note the difference + } // here using the 'shiftdown' boolean. + + if (ch == -1) + return false; // we can't use the event here + + // Save Game string input + + if (saveStringEnter) { + if (ch == key_menu_backspace) // phares 3/7/98 + { + if (saveCharIndex > 0) + { + saveCharIndex--; + savegamestrings[saveSlot][saveCharIndex] = 0; + } + } + + else if (ch == key_menu_escape) // phares 3/7/98 + { + saveStringEnter = 0; + strcpy(&savegamestrings[saveSlot][0],saveOldString); + } + + else if (ch == key_menu_enter) // phares 3/7/98 + { + saveStringEnter = 0; + if (savegamestrings[saveSlot][0]) + M_DoSave(saveSlot); + } + + else + { + ch = toupper(ch); + if (ch >= 32 && ch <= 127 && + saveCharIndex < SAVESTRINGSIZE-1 && + M_StringWidth(savegamestrings[saveSlot]) < (SAVESTRINGSIZE-2)*8) + { + savegamestrings[saveSlot][saveCharIndex++] = ch; + savegamestrings[saveSlot][saveCharIndex] = 0; + } + } + return true; + } + + // Take care of any messages that need input + + if (messageToPrint) { + if (messageNeedsInput == true && + !(ch == ' ' || ch == 'n' || ch == 'y' || ch == key_escape)) // phares + return false; + + menuactive = messageLastMenuActive; + messageToPrint = 0; + if (messageRoutine) + messageRoutine(ch); + + menuactive = false; + S_StartSound(NULL,sfx_swtchx); + return true; + } + + /* killough 2/22/98: add support for screenshot key: + * cph 2001/02/04: no need for this to be a gameaction, just do it + */ + if (ch == key_screenshot) + { + M_ScreenShot (); + // Don't eat the keypress in this case. See sf bug #1843280. + } + + // If there is no active menu displayed... + + if (!menuactive) { // phares + if (ch == key_autorun) // Autorun // V + { + autorun = !autorun; + return true; + } + + if (ch == key_help) // Help key + { + M_StartControlPanel (); + + currentMenu = &HelpDef; // killough 10/98: new help screen + + itemOn = 0; + S_StartSound(NULL,sfx_swtchn); + return true; + } + + if (ch == key_savegame) // Save Game + { + M_StartControlPanel(); + S_StartSound(NULL,sfx_swtchn); + M_SaveGame(0); + return true; + } + + if (ch == key_loadgame) // Load Game + { + M_StartControlPanel(); + S_StartSound(NULL,sfx_swtchn); + M_LoadGame(0); + return true; + } + + if (ch == key_soundvolume) // Sound Volume + { + M_StartControlPanel (); + currentMenu = &SoundDef; + itemOn = sfx_vol; + S_StartSound(NULL,sfx_swtchn); + return true; + } + + if (ch == key_quicksave) // Quicksave + { + S_StartSound(NULL,sfx_swtchn); + M_QuickSave(); + return true; + } + + if (ch == key_endgame) // End game + { + S_StartSound(NULL,sfx_swtchn); + M_EndGame(0); + return true; + } + + if (ch == key_messages) // Toggle messages + { + M_ChangeMessages(0); + S_StartSound(NULL,sfx_swtchn); + return true; + } + + if (ch == key_quickload) // Quickload + { + S_StartSound(NULL,sfx_swtchn); + M_QuickLoad(); + return true; + } + + if (ch == key_quit) // Quit DOOM + { + S_StartSound(NULL,sfx_swtchn); + M_QuitDOOM(0); + return true; + } + + if (ch == key_gamma) // gamma toggle + { + usegamma++; + if (usegamma > 4) + usegamma = 0; + players[consoleplayer].message = + usegamma == 0 ? s_GAMMALVL0 : + usegamma == 1 ? s_GAMMALVL1 : + usegamma == 2 ? s_GAMMALVL2 : + usegamma == 3 ? s_GAMMALVL3 : + s_GAMMALVL4; + V_SetPalette(0); + return true; + } + + + if (ch == key_zoomout) // zoom out + { + if ((automapmode & am_active) || chat_on) + return false; + M_SizeDisplay(0); + S_StartSound(NULL,sfx_stnmov); + return true; + } + + if (ch == key_zoomin) // zoom in + { // jff 2/23/98 + if ((automapmode & am_active) || chat_on) // allow + return false; // key_hud==key_zoomin + M_SizeDisplay(1); // ^ + S_StartSound(NULL,sfx_stnmov); // | + return true; // phares + } + + if (ch == key_hud) // heads-up mode + { + if ((automapmode & am_active) || chat_on) // jff 2/22/98 + return false; // HUD mode control + if (screenSize<8) // function on default F5 + while (screenSize<8 || !hud_displayed) // make hud visible + M_SizeDisplay(1); // when configuring it + else + { + hud_displayed = 1; //jff 3/3/98 turn hud on + hud_active = (hud_active+1)%3; // cycle hud_active + if (!hud_active) //jff 3/4/98 add distributed + { + hud_distributed = !hud_distributed; // to cycle + HU_MoveHud(); //jff 3/9/98 move it now to avoid glitch + } + } + return true; + } + + /* killough 10/98: allow key shortcut into Setup menu */ + if (ch == key_setup) { + M_StartControlPanel(); + S_StartSound(NULL,sfx_swtchn); + M_SetupNextMenu(&SetupDef); + return true; + } + } + // Pop-up Main menu? + + if (!menuactive) + { + if (ch == key_escape) // phares + { + M_StartControlPanel (); + S_StartSound(NULL,sfx_swtchn); + return true; + } + return false; + } + + // phares 3/26/98 - 4/11/98: + // Setup screen key processing + + if (setup_active) { + setup_menu_t* ptr1= current_setup_menu + set_menu_itemon; + setup_menu_t* ptr2 = NULL; + + // phares 4/19/98: + // Catch the response to the 'reset to default?' verification + // screen + + if (default_verify) + { + if (toupper(ch) == 'Y') { + M_ResetDefaults(); + default_verify = false; + M_SelectDone(ptr1); + } + else if (toupper(ch) == 'N') { + default_verify = false; + M_SelectDone(ptr1); + } + return true; + } + + // Common processing for some items + + if (setup_select) { // changing an entry + if (ch == key_menu_escape) // Exit key = no change + { + M_SelectDone(ptr1); // phares 4/17/98 + setup_gather = false; // finished gathering keys, if any + return true; + } + + if (ptr1->m_flags & S_YESNO) // yes or no setting? + { + if (ch == key_menu_enter) { + *ptr1->var.def->location.pi = !*ptr1->var.def->location.pi; // killough 8/15/98 + + // phares 4/14/98: + // If not in demoplayback, demorecording, or netgame, + // and there's a second variable in var2, set that + // as well + + // killough 8/15/98: add warning messages + + if (ptr1->m_flags & (S_LEVWARN | S_PRGWARN)) + warn_about_changes(ptr1->m_flags & // killough 10/98 + (S_LEVWARN | S_PRGWARN)); + else + M_UpdateCurrent(ptr1->var.def); + + if (ptr1->action) // killough 10/98 + ptr1->action(); + } + M_SelectDone(ptr1); // phares 4/17/98 + return true; + } + + if (ptr1->m_flags & S_CRITEM) + { + if (ch != key_menu_enter) + { + ch -= 0x30; // out of ascii + if (ch < 0 || ch > 9) + return true; // ignore + *ptr1->var.def->location.pi = ch; + } + if (ptr1->action) // killough 10/98 + ptr1->action(); + M_SelectDone(ptr1); // phares 4/17/98 + return true; + } + + if (ptr1->m_flags & S_NUM) // number? + { + if (setup_gather) { // gathering keys for a value? + /* killough 10/98: Allow negatives, and use a more + * friendly input method (e.g. don't clear value early, + * allow backspace, and return to original value if bad + * value is entered). + */ + if (ch == key_menu_enter) { + if (gather_count) { // Any input? + int value; + + gather_buffer[gather_count] = 0; + value = atoi(gather_buffer); // Integer value + + if ((ptr1->var.def->minvalue != UL && + value < ptr1->var.def->minvalue) || + (ptr1->var.def->maxvalue != UL && + value > ptr1->var.def->maxvalue)) + warn_about_changes(S_BADVAL); + else { + *ptr1->var.def->location.pi = value; + + /* killough 8/9/98: fix numeric vars + * killough 8/15/98: add warning message + */ + if (ptr1->m_flags & (S_LEVWARN | S_PRGWARN)) + warn_about_changes(ptr1->m_flags & + (S_LEVWARN | S_PRGWARN)); + else + M_UpdateCurrent(ptr1->var.def); + + if (ptr1->action) // killough 10/98 + ptr1->action(); + } + } + M_SelectDone(ptr1); // phares 4/17/98 + setup_gather = false; // finished gathering keys + return true; + } + + if (ch == key_menu_backspace && gather_count) { + gather_count--; + return true; + } + + if (gather_count >= MAXGATHER) + return true; + + if (!isdigit(ch) && ch != '-') + return true; // ignore + + /* killough 10/98: character-based numerical input */ + gather_buffer[gather_count++] = ch; + } + return true; + } + + if (ptr1->m_flags & S_CHOICE) // selection of choices? + { + if (ch == key_menu_left) { + if (ptr1->var.def->type == def_int) { + int value = *ptr1->var.def->location.pi; + + value = value - 1; + if ((ptr1->var.def->minvalue != UL && + value < ptr1->var.def->minvalue)) + value = ptr1->var.def->minvalue; + if ((ptr1->var.def->maxvalue != UL && + value > ptr1->var.def->maxvalue)) + value = ptr1->var.def->maxvalue; + if (*ptr1->var.def->location.pi != value) + S_StartSound(NULL,sfx_pstop); + *ptr1->var.def->location.pi = value; + } + if (ptr1->var.def->type == def_str) { + int old_value, value; + + old_value = M_IndexInChoices(*ptr1->var.def->location.ppsz, + ptr1->selectstrings); + value = old_value - 1; + if (value < 0) + value = 0; + if (old_value != value) + S_StartSound(NULL,sfx_pstop); + *ptr1->var.def->location.ppsz = ptr1->selectstrings[value]; + } + } + if (ch == key_menu_right) { + if (ptr1->var.def->type == def_int) { + int value = *ptr1->var.def->location.pi; + + value = value + 1; + if ((ptr1->var.def->minvalue != UL && + value < ptr1->var.def->minvalue)) + value = ptr1->var.def->minvalue; + if ((ptr1->var.def->maxvalue != UL && + value > ptr1->var.def->maxvalue)) + value = ptr1->var.def->maxvalue; + if (*ptr1->var.def->location.pi != value) + S_StartSound(NULL,sfx_pstop); + *ptr1->var.def->location.pi = value; + } + if (ptr1->var.def->type == def_str) { + int old_value, value; + + old_value = M_IndexInChoices(*ptr1->var.def->location.ppsz, + ptr1->selectstrings); + value = old_value + 1; + if (ptr1->selectstrings[value] == NULL) + value = old_value; + if (old_value != value) + S_StartSound(NULL,sfx_pstop); + *ptr1->var.def->location.ppsz = ptr1->selectstrings[value]; + } + } + if (ch == key_menu_enter) { + // phares 4/14/98: + // If not in demoplayback, demorecording, or netgame, + // and there's a second variable in var2, set that + // as well + + // killough 8/15/98: add warning messages + + if (ptr1->m_flags & (S_LEVWARN | S_PRGWARN)) + warn_about_changes(ptr1->m_flags & // killough 10/98 + (S_LEVWARN | S_PRGWARN)); + else + M_UpdateCurrent(ptr1->var.def); + + if (ptr1->action) // killough 10/98 + ptr1->action(); + M_SelectDone(ptr1); // phares 4/17/98 + } + return true; + } + + } + + // Key Bindings + + if (set_keybnd_active) // on a key binding setup screen + if (setup_select) // incoming key or button gets bound + { + if (ev->type == ev_joystick) + { + int oldbutton,group; + boolean search = true; + + if (!ptr1->m_joy) + return true; // not a legal action here (yet) + + // see if the button is already bound elsewhere. if so, you + // have to swap bindings so the action where it's currently + // bound doesn't go dead. Since there is more than one + // keybinding screen, you have to search all of them for + // any duplicates. You're only interested in the items + // that belong to the same group as the one you're changing. + + oldbutton = *ptr1->m_joy; + group = ptr1->m_group; + if (ev->data1 & 1) + ch = 0; + else if (ev->data1 & 2) + ch = 1; + else if (ev->data1 & 4) + ch = 2; + else if (ev->data1 & 8) + ch = 3; + else + return true; + for (i = 0 ; keys_settings[i] && search ; i++) + for (ptr2 = keys_settings[i] ; !(ptr2->m_flags & S_END) ; ptr2++) + if (ptr2->m_group == group && ptr1 != ptr2) + if (ptr2->m_flags & S_KEY && ptr2->m_joy) + if (*ptr2->m_joy == ch) + { + *ptr2->m_joy = oldbutton; + search = false; + break; + } + *ptr1->m_joy = ch; + } + else if (ev->type == ev_mouse) + { + int i,oldbutton,group; + boolean search = true; + + if (!ptr1->m_mouse) + return true; // not a legal action here (yet) + + // see if the button is already bound elsewhere. if so, you + // have to swap bindings so the action where it's currently + // bound doesn't go dead. Since there is more than one + // keybinding screen, you have to search all of them for + // any duplicates. You're only interested in the items + // that belong to the same group as the one you're changing. + + oldbutton = *ptr1->m_mouse; + group = ptr1->m_group; + if (ev->data1 & 1) + ch = 0; + else if (ev->data1 & 2) + ch = 1; + else if (ev->data1 & 4) + ch = 2; + else + return true; + for (i = 0 ; keys_settings[i] && search ; i++) + for (ptr2 = keys_settings[i] ; !(ptr2->m_flags & S_END) ; ptr2++) + if (ptr2->m_group == group && ptr1 != ptr2) + if (ptr2->m_flags & S_KEY && ptr2->m_mouse) + if (*ptr2->m_mouse == ch) + { + *ptr2->m_mouse = oldbutton; + search = false; + break; + } + *ptr1->m_mouse = ch; + } + else // keyboard key + { + int i,oldkey,group; + boolean search = true; + + // see if 'ch' is already bound elsewhere. if so, you have + // to swap bindings so the action where it's currently + // bound doesn't go dead. Since there is more than one + // keybinding screen, you have to search all of them for + // any duplicates. You're only interested in the items + // that belong to the same group as the one you're changing. + + // if you find that you're trying to swap with an action + // that has S_KEEP set, you can't bind ch; it's already + // bound to that S_KEEP action, and that action has to + // keep that key. + + oldkey = *ptr1->var.m_key; + group = ptr1->m_group; + for (i = 0 ; keys_settings[i] && search ; i++) + for (ptr2 = keys_settings[i] ; !(ptr2->m_flags & S_END) ; ptr2++) + if (ptr2->m_flags & (S_KEY|S_KEEP) && + ptr2->m_group == group && + ptr1 != ptr2) + if (*ptr2->var.m_key == ch) + { + if (ptr2->m_flags & S_KEEP) + return true; // can't have it! + *ptr2->var.m_key = oldkey; + search = false; + break; + } + *ptr1->var.m_key = ch; + } + + M_SelectDone(ptr1); // phares 4/17/98 + return true; + } + + // Weapons + + if (set_weapon_active) // on the weapons setup screen + if (setup_select) // changing an entry + { + if (ch != key_menu_enter) + { + ch -= '0'; // out of ascii + if (ch < 1 || ch > 9) + return true; // ignore + + // Plasma and BFG don't exist in shareware + // killough 10/98: allow it anyway, since this + // isn't the game itself, just setting preferences + + // see if 'ch' is already assigned elsewhere. if so, + // you have to swap assignments. + + // killough 11/98: simplified + + for (i = 0; (ptr2 = weap_settings[i]); i++) + for (; !(ptr2->m_flags & S_END); ptr2++) + if (ptr2->m_flags & S_WEAP && + *ptr2->var.def->location.pi == ch && ptr1 != ptr2) + { + *ptr2->var.def->location.pi = *ptr1->var.def->location.pi; + goto end; + } + end: + *ptr1->var.def->location.pi = ch; + } + + M_SelectDone(ptr1); // phares 4/17/98 + return true; + } + + // Automap + + if (set_auto_active) // on the automap setup screen + if (setup_select) // incoming key + { + if (ch == key_menu_down) + { + if (++color_palette_y == 16) + color_palette_y = 0; + S_StartSound(NULL,sfx_itemup); + return true; + } + + if (ch == key_menu_up) + { + if (--color_palette_y < 0) + color_palette_y = 15; + S_StartSound(NULL,sfx_itemup); + return true; + } + + if (ch == key_menu_left) + { + if (--color_palette_x < 0) + color_palette_x = 15; + S_StartSound(NULL,sfx_itemup); + return true; + } + + if (ch == key_menu_right) + { + if (++color_palette_x == 16) + color_palette_x = 0; + S_StartSound(NULL,sfx_itemup); + return true; + } + + if (ch == key_menu_enter) + { + *ptr1->var.def->location.pi = color_palette_x + 16*color_palette_y; + M_SelectDone(ptr1); // phares 4/17/98 + colorbox_active = false; + return true; + } + } + + // killough 10/98: consolidate handling into one place: + if (setup_select && + set_enemy_active | set_general_active | set_chat_active | + set_mess_active | set_status_active | set_compat_active) + { + if (ptr1->m_flags & S_STRING) // creating/editing a string? + { + if (ch == key_menu_backspace) // backspace and DEL + { + if (chat_string_buffer[chat_index] == 0) + { + if (chat_index > 0) + chat_string_buffer[--chat_index] = 0; + } + // shift the remainder of the text one char left + else + strcpy(&chat_string_buffer[chat_index], + &chat_string_buffer[chat_index+1]); + } + else if (ch == key_menu_left) // move cursor left + { + if (chat_index > 0) + chat_index--; + } + else if (ch == key_menu_right) // move cursor right + { + if (chat_string_buffer[chat_index] != 0) + chat_index++; + } + else if ((ch == key_menu_enter) || + (ch == key_menu_escape)) + { + *ptr1->var.def->location.ppsz = chat_string_buffer; + M_SelectDone(ptr1); // phares 4/17/98 + } + + // Adding a char to the text. Has to be a printable + // char, and you can't overrun the buffer. If the + // chat string gets larger than what the screen can hold, + // it is dealt with when the string is drawn (above). + + else if ((ch >= 32) && (ch <= 126)) + if ((chat_index+1) < CHAT_STRING_BFR_SIZE) + { + if (shiftdown) + ch = shiftxform[ch]; + if (chat_string_buffer[chat_index] == 0) + { + chat_string_buffer[chat_index++] = ch; + chat_string_buffer[chat_index] = 0; + } + else + chat_string_buffer[chat_index++] = ch; + } + return true; + } + + M_SelectDone(ptr1); // phares 4/17/98 + return true; + } + + // Not changing any items on the Setup screens. See if we're + // navigating the Setup menus or selecting an item to change. + + if (ch == key_menu_down) + { + ptr1->m_flags &= ~S_HILITE; // phares 4/17/98 + do + if (ptr1->m_flags & S_END) + { + set_menu_itemon = 0; + ptr1 = current_setup_menu; + } + else + { + set_menu_itemon++; + ptr1++; + } + while (ptr1->m_flags & S_SKIP); + M_SelectDone(ptr1); // phares 4/17/98 + return true; + } + + if (ch == key_menu_up) + { + ptr1->m_flags &= ~S_HILITE; // phares 4/17/98 + do + { + if (set_menu_itemon == 0) + do + set_menu_itemon++; + while(!((current_setup_menu + set_menu_itemon)->m_flags & S_END)); + set_menu_itemon--; + } + while((current_setup_menu + set_menu_itemon)->m_flags & S_SKIP); + M_SelectDone(current_setup_menu + set_menu_itemon); // phares 4/17/98 + return true; + } + + if (ch == key_menu_enter) + { + int flags = ptr1->m_flags; + + // You've selected an item to change. Highlight it, post a new + // message about what to do, and get ready to process the + // change. + // + // killough 10/98: use friendlier char-based input buffer + + if (flags & S_NUM) + { + setup_gather = true; + print_warning_about_changes = false; + gather_count = 0; + } + else if (flags & S_COLOR) + { + int color = *ptr1->var.def->location.pi; + + if (color < 0 || color > 255) // range check the value + color = 0; // 'no show' if invalid + + color_palette_x = *ptr1->var.def->location.pi & 15; + color_palette_y = *ptr1->var.def->location.pi >> 4; + colorbox_active = true; + } + else if (flags & S_STRING) + { + // copy chat string into working buffer; trim if needed. + // free the old chat string memory and replace it with + // the (possibly larger) new memory for editing purposes + // + // killough 10/98: fix bugs, simplify + + chat_string_buffer = malloc(CHAT_STRING_BFR_SIZE); + strncpy(chat_string_buffer, + *ptr1->var.def->location.ppsz, CHAT_STRING_BFR_SIZE); + + // guarantee null delimiter + chat_string_buffer[CHAT_STRING_BFR_SIZE-1] = 0; + + // set chat table pointer to working buffer + // and free old string's memory. + + free((char*)*ptr1->var.def->location.ppsz); + *ptr1->var.def->location.ppsz = chat_string_buffer; + chat_index = 0; // current cursor position in chat_string_buffer + } + else if (flags & S_RESET) + default_verify = true; + + ptr1->m_flags |= S_SELECT; + setup_select = true; + S_StartSound(NULL,sfx_itemup); + return true; + } + + if ((ch == key_menu_escape) || (ch == key_menu_backspace)) + { + if (ch == key_menu_escape) // Clear all menus + M_ClearMenus(); + else // key_menu_backspace = return to Setup Menu + if (currentMenu->prevMenu) + { + currentMenu = currentMenu->prevMenu; + itemOn = currentMenu->lastOn; + S_StartSound(NULL,sfx_swtchn); + } + ptr1->m_flags &= ~(S_HILITE|S_SELECT);// phares 4/19/98 + setup_active = false; + set_keybnd_active = false; + set_weapon_active = false; + set_status_active = false; + set_auto_active = false; + set_enemy_active = false; + set_mess_active = false; + set_chat_active = false; + colorbox_active = false; + default_verify = false; // phares 4/19/98 + set_general_active = false; // killough 10/98 + set_compat_active = false; // killough 10/98 + HU_Start(); // catch any message changes // phares 4/19/98 + S_StartSound(NULL,sfx_swtchx); + return true; + } + + // Some setup screens may have multiple screens. + // When there are multiple screens, m_prev and m_next items need to + // be placed on the appropriate screen tables so the user can + // move among the screens using the left and right arrow keys. + // The m_var1 field contains a pointer to the appropriate screen + // to move to. + + if (ch == key_menu_left) + { + ptr2 = ptr1; + do + { + ptr2++; + if (ptr2->m_flags & S_PREV) + { + ptr1->m_flags &= ~S_HILITE; + mult_screens_index--; + current_setup_menu = ptr2->var.menu; + set_menu_itemon = 0; + print_warning_about_changes = false; // killough 10/98 + while (current_setup_menu[set_menu_itemon++].m_flags&S_SKIP); + current_setup_menu[--set_menu_itemon].m_flags |= S_HILITE; + S_StartSound(NULL,sfx_pstop); // killough 10/98 + return true; + } + } + while (!(ptr2->m_flags & S_END)); + } + + if (ch == key_menu_right) + { + ptr2 = ptr1; + do + { + ptr2++; + if (ptr2->m_flags & S_NEXT) + { + ptr1->m_flags &= ~S_HILITE; + mult_screens_index++; + current_setup_menu = ptr2->var.menu; + set_menu_itemon = 0; + print_warning_about_changes = false; // killough 10/98 + while (current_setup_menu[set_menu_itemon++].m_flags&S_SKIP); + current_setup_menu[--set_menu_itemon].m_flags |= S_HILITE; + S_StartSound(NULL,sfx_pstop); // killough 10/98 + return true; + } + } + while (!(ptr2->m_flags & S_END)); + } + + } // End of Setup Screen processing + + // From here on, these navigation keys are used on the BIG FONT menus + // like the Main Menu. + + if (ch == key_menu_down) // phares 3/7/98 + { + do + { + if (itemOn+1 > currentMenu->numitems-1) + itemOn = 0; + else + itemOn++; + S_StartSound(NULL,sfx_pstop); + } + while(currentMenu->menuitems[itemOn].status==-1); + return true; + } + + if (ch == key_menu_up) // phares 3/7/98 + { + do + { + if (!itemOn) + itemOn = currentMenu->numitems-1; + else + itemOn--; + S_StartSound(NULL,sfx_pstop); + } + while(currentMenu->menuitems[itemOn].status==-1); + return true; + } + + if (ch == key_menu_left) // phares 3/7/98 + { + if (currentMenu->menuitems[itemOn].routine && + currentMenu->menuitems[itemOn].status == 2) + { + S_StartSound(NULL,sfx_stnmov); + currentMenu->menuitems[itemOn].routine(0); + } + return true; + } + + if (ch == key_menu_right) // phares 3/7/98 + { + if (currentMenu->menuitems[itemOn].routine && + currentMenu->menuitems[itemOn].status == 2) + { + S_StartSound(NULL,sfx_stnmov); + currentMenu->menuitems[itemOn].routine(1); + } + return true; + } + + if (ch == key_menu_enter) // phares 3/7/98 + { + if (currentMenu->menuitems[itemOn].routine && + currentMenu->menuitems[itemOn].status) + { + currentMenu->lastOn = itemOn; + if (currentMenu->menuitems[itemOn].status == 2) + { + currentMenu->menuitems[itemOn].routine(1); // right arrow + S_StartSound(NULL,sfx_stnmov); + } + else + { + currentMenu->menuitems[itemOn].routine(itemOn); + S_StartSound(NULL,sfx_pistol); + } + } + //jff 3/24/98 remember last skill selected + // killough 10/98 moved to skill-specific functions + return true; + } + + if (ch == key_menu_escape) // phares 3/7/98 + { + currentMenu->lastOn = itemOn; + M_ClearMenus (); + S_StartSound(NULL,sfx_swtchx); + return true; + } + + if (ch == key_menu_backspace) // phares 3/7/98 + { + currentMenu->lastOn = itemOn; + + // phares 3/30/98: + // add checks to see if you're in the extended help screens + // if so, stay with the same menu definition, but bump the + // index back one. if the index bumps back far enough ( == 0) + // then you can return to the Read_Thisn menu definitions + + if (currentMenu->prevMenu) + { + if (currentMenu == &ExtHelpDef) + { + if (--extended_help_index == 0) + { + currentMenu = currentMenu->prevMenu; + extended_help_index = 1; // reset + } + } + else + currentMenu = currentMenu->prevMenu; + itemOn = currentMenu->lastOn; + S_StartSound(NULL,sfx_swtchn); + } + return true; + } + + else + { + for (i = itemOn+1;i < currentMenu->numitems;i++) + if (currentMenu->menuitems[i].alphaKey == ch) + { + itemOn = i; + S_StartSound(NULL,sfx_pstop); + return true; + } + for (i = 0;i <= itemOn;i++) + if (currentMenu->menuitems[i].alphaKey == ch) + { + itemOn = i; + S_StartSound(NULL,sfx_pstop); + return true; + } + } + return false; +} + +// +// End of M_Responder +// +///////////////////////////////////////////////////////////////////////////// + +///////////////////////////////////////////////////////////////////////////// +// +// General Routines +// +// This displays the Main menu and gets the menu screens rolling. +// Plus a variety of routines that control the Big Font menu display. +// Plus some initialization for game-dependant situations. + +void M_StartControlPanel (void) +{ + // intro might call this repeatedly + + if (menuactive) + return; + + //jff 3/24/98 make default skill menu choice follow -skill or defaultskill + //from command line or config file + // + // killough 10/98: + // Fix to make "always floating" with menu selections, and to always follow + // defaultskill, instead of -skill. + + NewDef.lastOn = defaultskill - 1; + + default_verify = 0; // killough 10/98 + menuactive = 1; + currentMenu = &MainDef; // JDC + itemOn = currentMenu->lastOn; // JDC + print_warning_about_changes = false; // killough 11/98 +} + +// +// M_Drawer +// Called after the view has been rendered, +// but before it has been blitted. +// +// killough 9/29/98: Significantly reformatted source +// + +void M_Drawer (void) +{ + inhelpscreens = false; + + // Horiz. & Vertically center string and print it. + // killough 9/29/98: simplified code, removed 40-character width limit + if (messageToPrint) + { + /* cph - strdup string to writable memory */ + char *ms = strdup(messageString); + char *p = ms; + + int y = 100 - M_StringHeight(messageString)/2; + while (*p) + { + char *string = p, c; + while ((c = *p) && *p != '\n') + p++; + *p = 0; + M_WriteText(160 - M_StringWidth(string)/2, y, string); + y += hu_font[0].height; + if ((*p = c)) + p++; + } + free(ms); + } + else + if (menuactive) + { + int x,y,max,i; + + if (currentMenu->routine) + currentMenu->routine(); // call Draw routine + + // DRAW MENU + + x = currentMenu->x; + y = currentMenu->y; + max = currentMenu->numitems; + + for (i=0;imenuitems[i].name[0]) + V_DrawNamePatch(x,y,0,currentMenu->menuitems[i].name, + CR_DEFAULT, VPT_STRETCH); + y += LINEHEIGHT; + } + + // DRAW SKULL + + // CPhipps - patch drawing updated + V_DrawNamePatch(x + SKULLXOFF, currentMenu->y - 5 + itemOn*LINEHEIGHT,0, + skullName[whichSkull], CR_DEFAULT, VPT_STRETCH); + } +} + +// +// M_ClearMenus +// +// Called when leaving the menu screens for the real world + +void M_ClearMenus (void) +{ + menuactive = 0; + print_warning_about_changes = 0; // killough 8/15/98 + default_verify = 0; // killough 10/98 + + // if (!netgame && usergame && paused) + // sendpause = true; +} + +// +// M_SetupNextMenu +// +void M_SetupNextMenu(menu_t *menudef) +{ + currentMenu = menudef; + itemOn = currentMenu->lastOn; +} + +///////////////////////////// +// +// M_Ticker +// +void M_Ticker (void) +{ + if (--skullAnimCounter <= 0) + { + whichSkull ^= 1; + skullAnimCounter = 8; + } +} + +///////////////////////////// +// +// Message Routines +// + +void M_StartMessage (const char* string,void* routine,boolean input) +{ + messageLastMenuActive = menuactive; + messageToPrint = 1; + messageString = string; + messageRoutine = routine; + messageNeedsInput = input; + menuactive = true; + return; +} + +void M_StopMessage(void) +{ + menuactive = messageLastMenuActive; + messageToPrint = 0; +} + +///////////////////////////// +// +// Thermometer Routines +// + +// +// M_DrawThermo draws the thermometer graphic for Mouse Sensitivity, +// Sound Volume, etc. +// +// proff/nicolas 09/20/98 -- changed for hi-res +// CPhipps - patch drawing updated +// +void M_DrawThermo(int x,int y,int thermWidth,int thermDot ) + { + int xx; + int i; + /* + * Modification By Barry Mead to allow the Thermometer to have vastly + * larger ranges. (the thermWidth parameter can now have a value as + * large as 200. Modified 1-9-2000 Originally I used it to make + * the sensitivity range for the mouse better. It could however also + * be used to improve the dynamic range of music and sound affect + * volume controls for example. + */ + int horizScaler; //Used to allow more thermo range for mouse sensitivity. + thermWidth = (thermWidth > 200) ? 200 : thermWidth; //Clamp to 200 max + horizScaler = (thermWidth > 23) ? (200 / thermWidth) : 8; //Dynamic range + xx = x; + V_DrawNamePatch(xx, y, 0, "M_THERML", CR_DEFAULT, VPT_STRETCH); + xx += 8; + for (i=0;ix - 10, menu->y+item*LINEHEIGHT - 1, 0, + "M_CELL1", CR_DEFAULT, VPT_STRETCH); +} + +// +// Draw a full cell in the thermometer +// + +void M_DrawSelCell (menu_t* menu,int item) +{ + // CPhipps - patch drawing updated + V_DrawNamePatch(menu->x - 10, menu->y+item*LINEHEIGHT - 1, 0, + "M_CELL2", CR_DEFAULT, VPT_STRETCH); +} + +///////////////////////////// +// +// String-drawing Routines +// + +// +// Find string width from hu_font chars +// + +int M_StringWidth(const char* string) +{ + int i, c, w = 0; + for (i = 0;(size_t)i < strlen(string);i++) + w += (c = toupper(string[i]) - HU_FONTSTART) < 0 || c >= HU_FONTSIZE ? + 4 : hu_font[c].width; + return w; +} + +// +// Find string height from hu_font chars +// + +int M_StringHeight(const char* string) +{ + int i, h, height = h = hu_font[0].height; + for (i = 0;string[i];i++) // killough 1/31/98 + if (string[i] == '\n') + h += height; + return h; +} + +// +// Write a string using the hu_font +// +void M_WriteText (int x,int y,const char* string) +{ + int w; + const char* ch; + int c; + int cx; + int cy; + + ch = string; + cx = x; + cy = y; + + while(1) { + c = *ch++; + if (!c) + break; + if (c == '\n') { + cx = x; + cy += 12; + continue; + } + + c = toupper(c) - HU_FONTSTART; + if (c < 0 || c>= HU_FONTSIZE) { + cx += 4; + continue; + } + + w = hu_font[c].width; + if (cx+w > SCREENWIDTH) + break; + // proff/nicolas 09/20/98 -- changed for hi-res + // CPhipps - patch drawing updated + V_DrawNumPatch(cx, cy, 0, hu_font[c].lumpnum, CR_DEFAULT, VPT_STRETCH); + cx+=w; + } +} + +///////////////////////////// +// +// Initialization Routines to take care of one-time setup +// + +// phares 4/08/98: +// M_InitHelpScreen() clears the weapons from the HELP +// screen that don't exist in this version of the game. + +void M_InitHelpScreen(void) +{ + setup_menu_t* src; + + src = helpstrings; + while (!(src->m_flags & S_END)) { + + if ((strncmp(src->m_text,"PLASMA",6) == 0) && (gamemode == shareware)) + src->m_flags = S_SKIP; // Don't show setting or item + if ((strncmp(src->m_text,"BFG",3) == 0) && (gamemode == shareware)) + src->m_flags = S_SKIP; // Don't show setting or item + if ((strncmp(src->m_text,"SSG",3) == 0) && (gamemode != commercial)) + src->m_flags = S_SKIP; // Don't show setting or item + src++; + } +} + +// +// M_Init +// +void M_Init(void) +{ + M_InitDefaults(); // killough 11/98 + currentMenu = &MainDef; + menuactive = 0; + itemOn = currentMenu->lastOn; + whichSkull = 0; + skullAnimCounter = 10; + screenSize = screenblocks - 3; + messageToPrint = 0; + messageString = NULL; + messageLastMenuActive = menuactive; + quickSaveSlot = -1; + + // Here we could catch other version dependencies, + // like HELP1/2, and four episodes. + + switch(gamemode) + { + case commercial: + // This is used because DOOM 2 had only one HELP + // page. I use CREDIT as second page now, but + // kept this hack for educational purposes. + MainMenu[readthis] = MainMenu[quitdoom]; + MainDef.numitems--; + MainDef.y += 8; + NewDef.prevMenu = &MainDef; + ReadDef1.routine = M_DrawReadThis1; + ReadDef1.x = 330; + ReadDef1.y = 165; + ReadMenu1[0].routine = M_FinishReadThis; + break; + case registered: + // Episode 2 and 3 are handled, + // branching to an ad screen. + + // killough 2/21/98: Fix registered Doom help screen + // killough 10/98: moved to second screen, moved up to the top + ReadDef2.y = 15; + + case shareware: + // We need to remove the fourth episode. + EpiDef.numitems--; + break; + case retail: + // We are fine. + default: + break; + } + + M_InitHelpScreen(); // init the help screen // phares 4/08/98 + M_InitExtendedHelp(); // init extended help screens // phares 3/30/98 + + M_ChangeDemoSmoothTurns(); +} + +// +// End of General Routines +// +///////////////////////////////////////////////////////////////////////////// diff --git a/src/m_menu.h b/src/m_menu.h new file mode 100644 index 0000000..7a391fd --- /dev/null +++ b/src/m_menu.h @@ -0,0 +1,182 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Menu widget stuff, episode selection and such. + * + *-----------------------------------------------------------------------------*/ + +#ifndef __M_MENU__ +#define __M_MENU__ + +#include "d_event.h" + +// +// MENUS +// +// Called by main loop, +// saves config file and calls I_Quit when user exits. +// Even when the menu is not displayed, +// this can resize the view and change game parameters. +// Does all the real work of the menu interaction. + +boolean M_Responder (event_t *ev); + +// Called by main loop, +// only used for menu (skull cursor) animation. + +void M_Ticker (void); + +// Called by main loop, +// draws the menus directly into the screen buffer. + +void M_Drawer (void); + +// Called by D_DoomMain, +// loads the config file. + +void M_Init (void); + +// Called by intro code to force menu up upon a keypress, +// does nothing if menu is already up. + +void M_StartControlPanel (void); + +void M_ForcedLoadGame(const char *msg); // killough 5/15/98: forced loadgames + +void M_Trans(void); // killough 11/98: reset translucency + +void M_ResetMenu(void); // killough 11/98: reset main menu ordering + +void M_DrawCredits(void); // killough 11/98 + +/* killough 8/15/98: warn about changes not being committed until next game */ +#define warn_about_changes(x) (warning_about_changes=(x), \ + print_warning_about_changes = 2) + +extern int warning_about_changes, print_warning_about_changes; + +/**************************** + * + * The following #defines are for the m_flags field of each item on every + * Setup Screen. They can be OR'ed together where appropriate + */ + +#define S_HILITE 0x1 // Cursor is sitting on this item +#define S_SELECT 0x2 // We're changing this item +#define S_TITLE 0x4 // Title item +#define S_YESNO 0x8 // Yes or No item +#define S_CRITEM 0x10 // Message color +#define S_COLOR 0x20 // Automap color +#define S_CHAT 0x40 // Chat String +#define S_RESET 0x80 // Reset to Defaults Button +#define S_PREV 0x100 // Previous menu exists +#define S_NEXT 0x200 // Next menu exists +#define S_KEY 0x400 // Key Binding +#define S_WEAP 0x800 // Weapon # +#define S_NUM 0x1000 // Numerical item +#define S_SKIP 0x2000 // Cursor can't land here +#define S_KEEP 0x4000 // Don't swap key out +#define S_END 0x8000 // Last item in list (dummy) +#define S_LEVWARN 0x10000// killough 8/30/98: Always warn about pending change +#define S_PRGWARN 0x20000// killough 10/98: Warn about change until next run +#define S_BADVAL 0x40000// killough 10/98: Warn about bad value +#define S_FILE 0x80000// killough 10/98: Filenames +#define S_LEFTJUST 0x100000 // killough 10/98: items which are left-justified +#define S_CREDIT 0x200000 // killough 10/98: credit +#define S_BADVID 0x400000 // killough 12/98: video mode change error +#define S_CHOICE 0x800000 // this item has several values + +/* S_SHOWDESC = the set of items whose description should be displayed + * S_SHOWSET = the set of items whose setting should be displayed + * S_STRING = the set of items whose settings are strings -- killough 10/98: + * S_HASDEFPTR = the set of items whose var field points to default array + */ + +#define S_SHOWDESC (S_TITLE|S_YESNO|S_CRITEM|S_COLOR|S_CHAT|S_RESET|S_PREV|S_NEXT|S_KEY|S_WEAP|S_NUM|S_FILE|S_CREDIT|S_CHOICE) + +#define S_SHOWSET (S_YESNO|S_CRITEM|S_COLOR|S_CHAT|S_KEY|S_WEAP|S_NUM|S_FILE|S_CHOICE) + +#define S_STRING (S_CHAT|S_FILE) + +#define S_HASDEFPTR (S_STRING|S_YESNO|S_NUM|S_WEAP|S_COLOR|S_CRITEM|S_CHOICE) + +/**************************** + * + * The setup_group enum is used to show which 'groups' keys fall into so + * that you can bind a key differently in each 'group'. + */ + +typedef enum { + m_null, // Has no meaning; not applicable + m_scrn, // A key can not be assigned to more than one action + m_map, // in the same group. A key can be assigned to one + m_menu, // action in one group, and another action in another. +} setup_group; + +/**************************** + * + * phares 4/17/98: + * State definition for each item. + * This is the definition of the structure for each setup item. Not all + * fields are used by all items. + * + * A setup screen is defined by an array of these items specific to + * that screen. + * + * killough 11/98: + * + * Restructured to allow simpler table entries, + * and to Xref with defaults[] array in m_misc.c. + * Moved from m_menu.c to m_menu.h so that m_misc.c can use it. + */ + +typedef struct setup_menu_s +{ + const char *m_text; /* text to display */ + int m_flags; /* phares 4/17/98: flag bits S_* (defined above) */ + setup_group m_group; /* Group */ + short m_x; /* screen x position (left is 0) */ + short m_y; /* screen y position (top is 0) */ + + union /* killough 11/98: The first field is a union of several types */ + { + const void *var; /* generic variable */ + int *m_key; /* key value, or 0 if not shown */ + const char *name; /* name */ + struct default_s *def; /* default[] table entry */ + struct setup_menu_s *menu; /* next or prev menu */ + } var; + + int *m_mouse; /* mouse button value, or 0 if not shown */ + int *m_joy; /* joystick button value, or 0 if not shown */ + void (*action)(void); /* killough 10/98: function to call after changing */ + const char **selectstrings; /* list of strings for choice value */ +} setup_menu_t; + +#endif diff --git a/src/m_misc.c b/src/m_misc.c new file mode 100644 index 0000000..a393587 --- /dev/null +++ b/src/m_misc.c @@ -0,0 +1,1081 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Main loop menu stuff. + * Default Config File. + * PCX Screenshots. + * + *-----------------------------------------------------------------------------*/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#ifdef HAVE_UNISTD_H +#include +#endif +#ifdef _MSC_VER +#include +#endif +#include +#include + +#include "doomstat.h" +#include "m_argv.h" +#include "g_game.h" +#include "m_menu.h" +#include "am_map.h" +#include "w_wad.h" +#include "i_system.h" +#include "i_sound.h" +#include "i_video.h" +#include "v_video.h" +#include "hu_stuff.h" +#include "st_stuff.h" +#include "dstrings.h" +#include "m_misc.h" +#include "s_sound.h" +#include "sounds.h" +#include "i_joy.h" +#include "lprintf.h" +#include "d_main.h" +#include "r_draw.h" +#include "r_demo.h" +#include "r_fps.h" + +/* cph - disk icon not implemented */ +static inline void I_BeginRead(void) {} +static inline void I_EndRead(void) {} + +/* + * M_WriteFile + * + * killough 9/98: rewritten to use stdio and to flash disk icon + */ + +boolean M_WriteFile(char const *name, void *source, int length) +{ + FILE *fp; + + errno = 0; + + if (!(fp = fopen(name, "wb"))) // Try opening file + return 0; // Could not open file for writing + + I_BeginRead(); // Disk icon on + length = fwrite(source, 1, length, fp) == (size_t)length; // Write data + fclose(fp); + I_EndRead(); // Disk icon off + + if (!length) // Remove partially written file + remove(name); + + return length; +} + +/* + * M_ReadFile + * + * killough 9/98: rewritten to use stdio and to flash disk icon + */ + +int M_ReadFile(char const *name, byte **buffer) +{ + FILE *fp; + + if ((fp = fopen(name, "rb"))) + { + size_t length; + + I_BeginRead(); + fseek(fp, 0, SEEK_END); + length = ftell(fp); + fseek(fp, 0, SEEK_SET); + *buffer = Z_Malloc(length, PU_STATIC, 0); + if (fread(*buffer, 1, length, fp) == length) + { + fclose(fp); + I_EndRead(); + return length; + } + fclose(fp); + } + + /* cph 2002/08/10 - this used to return 0 on error, but that's ambiguous, + * because we could have a legit 0-length file. So make it -1. */ + return -1; +} + +// +// DEFAULTS +// + +int usemouse; +boolean precache = true; /* if true, load all graphics at start */ + +extern int mousebfire; +extern int mousebstrafe; +extern int mousebforward; + +extern int viewwidth; +extern int viewheight; +#ifdef GL_DOOM +extern int gl_nearclip; +extern int gl_colorbuffer_bits; +extern int gl_depthbuffer_bits; +extern char *gl_tex_filter_string; +extern char *gl_tex_format_string; +extern int gl_drawskys; +extern int gl_sortsprites; +extern int gl_use_paletted_texture; +extern int gl_use_shared_texture_palette; +extern int gl_sprite_offset; +#endif + +extern int realtic_clock_rate; // killough 4/13/98: adjustable timer +extern int tran_filter_pct; // killough 2/21/98 + +extern int screenblocks; +extern int showMessages; + +#ifndef DJGPP +int mus_pause_opt; // 0 = kill music, 1 = pause, 2 = continue +#endif + +extern const char* chat_macros[]; + +extern int endoom_mode; + +extern const char* S_music_files[]; // cournia + +/* cph - Some MBF stuff parked here for now + * killough 10/98 + */ +int map_point_coordinates; + +default_t defaults[] = +{ + {"Misc settings",{NULL},{0},UL,UL,def_none,ss_none}, + {"default_compatibility_level",{(int*)&default_compatibility_level}, + {-1},-1,MAX_COMPATIBILITY_LEVEL-1, + def_int,ss_none}, // compatibility level" - CPhipps + {"realtic_clock_rate",{&realtic_clock_rate},{100},0,UL, + def_int,ss_none}, // percentage of normal speed (35 fps) realtic clock runs at + {"max_player_corpse", {&bodyquesize}, {32},-1,UL, // killough 2/8/98 + def_int,ss_none}, // number of dead bodies in view supported (-1 = no limit) + {"flashing_hom",{&flashing_hom},{0},0,1, + def_bool,ss_none}, // killough 10/98 - enable flashing HOM indicator + {"demo_insurance",{&default_demo_insurance},{2},0,2, // killough 3/31/98 + def_int,ss_none}, // 1=take special steps ensuring demo sync, 2=only during recordings + {"endoom_mode", {&endoom_mode},{5},0,7, // CPhipps - endoom flags + def_hex, ss_none}, // 0, +1 for colours, +2 for non-ascii chars, +4 for skip-last-line + {"level_precache",{(int*)&precache},{0},0,1, + def_bool,ss_none}, // precache level data? + {"demo_smoothturns", {&demo_smoothturns}, {0},0,1, + def_bool,ss_stat}, + {"demo_smoothturnsfactor", {&demo_smoothturnsfactor}, {6},1,SMOOTH_PLAYING_MAXFACTOR, + def_int,ss_stat}, + + {"Files",{NULL},{0},UL,UL,def_none,ss_none}, + /* cph - MBF-like wad/deh/bex autoload code */ + {"wadfile_1",{NULL,&wad_files[0]},{0,""},UL,UL,def_str,ss_none}, + {"wadfile_2",{NULL,&wad_files[1]},{0,""},UL,UL,def_str,ss_none}, + {"dehfile_1",{NULL,&deh_files[0]},{0,""},UL,UL,def_str,ss_none}, + {"dehfile_2",{NULL,&deh_files[1]},{0,""},UL,UL,def_str,ss_none}, + + {"Game settings",{NULL},{0},UL,UL,def_none,ss_none}, + {"default_skill",{&defaultskill},{3},1,5, // jff 3/24/98 allow default skill setting + def_int,ss_none}, // selects default skill 1=TYTD 2=NTR 3=HMP 4=UV 5=NM + {"weapon_recoil",{&default_weapon_recoil},{0},0,1, + def_bool,ss_weap, &weapon_recoil}, + /* killough 10/98 - toggle between SG/SSG and Fist/Chainsaw */ + {"doom_weapon_toggles",{&doom_weapon_toggles}, {1}, 0, 1, + def_bool, ss_weap }, + {"player_bobbing",{&default_player_bobbing},{1},0,1, // phares 2/25/98 + def_bool,ss_weap, &player_bobbing}, + {"monsters_remember",{&default_monsters_remember},{1},0,1, // killough 3/1/98 + def_bool,ss_enem, &monsters_remember}, + /* MBF AI enhancement options */ + {"monster_infighting",{&default_monster_infighting}, {1}, 0, 1, + def_bool, ss_enem, &monster_infighting}, + {"monster_backing",{&default_monster_backing}, {0}, 0, 1, + def_bool, ss_enem, &monster_backing}, + {"monster_avoid_hazards",{&default_monster_avoid_hazards}, {1}, 0, 1, + def_bool, ss_enem, &monster_avoid_hazards}, + {"monkeys",{&default_monkeys}, {0}, 0, 1, + def_bool, ss_enem, &monkeys}, + {"monster_friction",{&default_monster_friction}, {1}, 0, 1, + def_bool, ss_enem, &monster_friction}, + {"help_friends",{&default_help_friends}, {1}, 0, 1, + def_bool, ss_enem, &help_friends}, + {"allow_pushers",{&default_allow_pushers},{1},0,1, + def_bool,ss_weap, &allow_pushers}, + {"variable_friction",{&default_variable_friction},{1},0,1, + def_bool,ss_weap, &variable_friction}, +#ifdef DOGS + {"player_helpers",{&default_dogs}, {0}, 0, 3, + def_bool, ss_enem }, + {"friend_distance",{&default_distfriend}, {128}, 0, 999, + def_int, ss_enem, &distfriend}, + {"dog_jumping",{&default_dog_jumping}, {1}, 0, 1, + def_bool, ss_enem, &dog_jumping}, +#endif + /* End of MBF AI extras */ + + {"sts_always_red",{&sts_always_red},{1},0,1, // no color changes on status bar + def_bool,ss_stat}, + {"sts_pct_always_gray",{&sts_pct_always_gray},{0},0,1, // 2/23/98 chg default + def_bool,ss_stat}, // makes percent signs on status bar always gray + {"sts_traditional_keys",{&sts_traditional_keys},{0},0,1, // killough 2/28/98 + def_bool,ss_stat}, // disables doubled card and skull key display on status bar + {"show_messages",{&showMessages},{1},0,1, + def_bool,ss_none}, // enables message display + {"autorun",{&autorun},{0},0,1, // killough 3/6/98: preserve autorun across games + def_bool,ss_none}, + + {"Compatibility settings",{NULL},{0},UL,UL,def_none,ss_none}, + {"comp_zombie",{&default_comp[comp_zombie]},{0},0,1,def_bool,ss_comp,&comp[comp_zombie]}, + {"comp_infcheat",{&default_comp[comp_infcheat]},{0},0,1,def_bool,ss_comp,&comp[comp_infcheat]}, + {"comp_stairs",{&default_comp[comp_stairs]},{0},0,1,def_bool,ss_comp,&comp[comp_stairs]}, + {"comp_telefrag",{&default_comp[comp_telefrag]},{0},0,1,def_bool,ss_comp,&comp[comp_telefrag]}, + {"comp_dropoff",{&default_comp[comp_dropoff]},{0},0,1,def_bool,ss_comp,&comp[comp_dropoff]}, + {"comp_falloff",{&default_comp[comp_falloff]},{0},0,1,def_bool,ss_comp,&comp[comp_falloff]}, + {"comp_staylift",{&default_comp[comp_staylift]},{0},0,1,def_bool,ss_comp,&comp[comp_staylift]}, + {"comp_doorstuck",{&default_comp[comp_doorstuck]},{0},0,1,def_bool,ss_comp,&comp[comp_doorstuck]}, + {"comp_pursuit",{&default_comp[comp_pursuit]},{0},0,1,def_bool,ss_comp,&comp[comp_pursuit]}, + {"comp_vile",{&default_comp[comp_vile]},{0},0,1,def_bool,ss_comp,&comp[comp_vile]}, + {"comp_pain",{&default_comp[comp_pain]},{0},0,1,def_bool,ss_comp,&comp[comp_pain]}, + {"comp_skull",{&default_comp[comp_skull]},{0},0,1,def_bool,ss_comp,&comp[comp_skull]}, + {"comp_blazing",{&default_comp[comp_blazing]},{0},0,1,def_bool,ss_comp,&comp[comp_blazing]}, + {"comp_doorlight",{&default_comp[comp_doorlight]},{0},0,1,def_bool,ss_comp,&comp[comp_doorlight]}, + {"comp_god",{&default_comp[comp_god]},{0},0,1,def_bool,ss_comp,&comp[comp_god]}, + {"comp_skymap",{&default_comp[comp_skymap]},{0},0,1,def_bool,ss_comp,&comp[comp_skymap]}, + {"comp_floors",{&default_comp[comp_floors]},{0},0,1,def_bool,ss_comp,&comp[comp_floors]}, + {"comp_model",{&default_comp[comp_model]},{0},0,1,def_bool,ss_comp,&comp[comp_model]}, + {"comp_zerotags",{&default_comp[comp_zerotags]},{0},0,1,def_bool,ss_comp,&comp[comp_zerotags]}, + {"comp_moveblock",{&default_comp[comp_moveblock]},{0},0,1,def_bool,ss_comp,&comp[comp_moveblock]}, + {"comp_sound",{&default_comp[comp_sound]},{0},0,1,def_bool,ss_comp,&comp[comp_sound]}, + {"comp_666",{&default_comp[comp_666]},{0},0,1,def_bool,ss_comp,&comp[comp_666]}, + {"comp_soul",{&default_comp[comp_soul]},{0},0,1,def_bool,ss_comp,&comp[comp_soul]}, + {"comp_maskedanim",{&default_comp[comp_maskedanim]},{0},0,1,def_bool,ss_comp,&comp[comp_maskedanim]}, + + {"Sound settings",{NULL},{0},UL,UL,def_none,ss_none}, + {"sound_card",{&snd_card},{-1},-1,7, // jff 1/18/98 allow Allegro drivers + def_int,ss_none}, // select sounds driver (DOS), -1 is autodetect, 0 is none; in Linux, non-zero enables sound + {"music_card",{&mus_card},{-1},-1,9, // to be set, -1 = autodetect + def_int,ss_none}, // select music driver (DOS), -1 is autodetect, 0 is none"; in Linux, non-zero enables music + {"pitched_sounds",{&pitched_sounds},{0},0,1, // killough 2/21/98 + def_bool,ss_none}, // enables variable pitch in sound effects (from id's original code) + {"samplerate",{&snd_samplerate},{22050},11025,48000, def_int,ss_none}, + {"sfx_volume",{&snd_SfxVolume},{8},0,15, def_int,ss_none}, + {"music_volume",{&snd_MusicVolume},{8},0,15, def_int,ss_none}, + {"mus_pause_opt",{&mus_pause_opt},{2},0,2, // CPhipps - music pausing + def_int, ss_none}, // 0 = kill music when paused, 1 = pause music, 2 = let music continue + {"snd_channels",{&default_numChannels},{8},1,32, + def_int,ss_none}, // number of audio events simultaneously // killough + + {"Video settings",{NULL},{0},UL,UL,def_none,ss_none}, +#ifdef GL_DOOM + #ifdef _MSC_VER + {"videomode",{NULL, &default_videomode},{0,"gl"},UL,UL,def_str,ss_none}, + #else + {"videomode",{NULL, &default_videomode},{0,"8"},UL,UL,def_str,ss_none}, + #endif +#else + {"videomode",{NULL, &default_videomode},{0,"8"},UL,UL,def_str,ss_none}, +#endif + /* 640x480 default resolution */ + {"screen_width",{&desired_screenwidth},{640}, 320, MAX_SCREENWIDTH, + def_int,ss_none}, + {"screen_height",{&desired_screenheight},{480},200,MAX_SCREENHEIGHT, + def_int,ss_none}, + {"use_fullscreen",{&use_fullscreen},{1},0,1, /* proff 21/05/2000 */ + def_bool,ss_none}, +#ifndef DISABLE_DOUBLEBUFFER + {"use_doublebuffer",{&use_doublebuffer},{1},0,1, // proff 2001-7-4 + def_bool,ss_none}, // enable doublebuffer to avoid display tearing (fullscreen) +#endif + {"translucency",{&default_translucency},{1},0,1, // phares + def_bool,ss_none}, // enables translucency + {"tran_filter_pct",{&tran_filter_pct},{66},0,100, // killough 2/21/98 + def_int,ss_none}, // set percentage of foreground/background translucency mix + {"screenblocks",{&screenblocks},{10},3,11, // killough 2/21/98: default to 10 + def_int,ss_none}, + {"usegamma",{&usegamma},{3},0,4, //jff 3/6/98 fix erroneous upper limit in range + def_int,ss_none}, // gamma correction level // killough 1/18/98 + {"uncapped_framerate", {&movement_smooth}, {0},0,1, + def_bool,ss_stat}, + {"filter_wall",{(int*)&drawvars.filterwall},{RDRAW_FILTER_POINT}, + RDRAW_FILTER_POINT, RDRAW_FILTER_ROUNDED, def_int,ss_none}, + {"filter_floor",{(int*)&drawvars.filterfloor},{RDRAW_FILTER_POINT}, + RDRAW_FILTER_POINT, RDRAW_FILTER_ROUNDED, def_int,ss_none}, + {"filter_sprite",{(int*)&drawvars.filtersprite},{RDRAW_FILTER_POINT}, + RDRAW_FILTER_POINT, RDRAW_FILTER_ROUNDED, def_int,ss_none}, + {"filter_z",{(int*)&drawvars.filterz},{RDRAW_FILTER_POINT}, + RDRAW_FILTER_POINT, RDRAW_FILTER_LINEAR, def_int,ss_none}, + {"filter_patch",{(int*)&drawvars.filterpatch},{RDRAW_FILTER_POINT}, + RDRAW_FILTER_POINT, RDRAW_FILTER_ROUNDED, def_int,ss_none}, + {"filter_threshold",{(int*)&drawvars.mag_threshold},{49152}, + 0, UL, def_int,ss_none}, + {"sprite_edges",{(int*)&drawvars.sprite_edges},{RDRAW_MASKEDCOLUMNEDGE_SQUARE}, + RDRAW_MASKEDCOLUMNEDGE_SQUARE, RDRAW_MASKEDCOLUMNEDGE_SLOPED, def_int,ss_none}, + {"patch_edges",{(int*)&drawvars.patch_edges},{RDRAW_MASKEDCOLUMNEDGE_SQUARE}, + RDRAW_MASKEDCOLUMNEDGE_SQUARE, RDRAW_MASKEDCOLUMNEDGE_SLOPED, def_int,ss_none}, + +#ifdef GL_DOOM + {"OpenGL settings",{NULL},{0},UL,UL,def_none,ss_none}, + {"gl_nearclip",{&gl_nearclip},{5},0,UL, + def_int,ss_none}, /* near clipping plane pos */ + {"gl_colorbuffer_bits",{&gl_colorbuffer_bits},{16},16,32, + def_int,ss_none}, + {"gl_depthbuffer_bits",{&gl_depthbuffer_bits},{16},16,32, + def_int,ss_none}, + {"gl_tex_filter_string", {NULL,&gl_tex_filter_string}, {0,"GL_LINEAR"},UL,UL, + def_str,ss_none}, + {"gl_tex_format_string", {NULL,&gl_tex_format_string}, {0,"GL_RGB5_A1"},UL,UL, + def_str,ss_none}, + {"gl_drawskys",{&gl_drawskys},{1},0,1, + def_bool,ss_none}, + {"gl_sortsprites",{&gl_sortsprites},{1},0,1, + def_bool,ss_none}, + {"gl_use_paletted_texture",{&gl_use_paletted_texture},{0},0,1, + def_bool,ss_none}, + {"gl_use_shared_texture_palette",{&gl_use_shared_texture_palette},{0},0,1, + def_bool,ss_none}, +#ifdef GL_DOOM + {"gl_sprite_offset",{&gl_sprite_offset},{0}, 0, 5, + def_int,ss_none}, // amount to bring items out of floor (GL) Mead 8/13/03 +#endif +#endif + + {"Mouse settings",{NULL},{0},UL,UL,def_none,ss_none}, + {"use_mouse",{&usemouse},{1},0,1, + def_bool,ss_none}, // enables use of mouse with DOOM + //jff 4/3/98 allow unlimited sensitivity + {"mouse_sensitivity_horiz",{&mouseSensitivity_horiz},{10},0,UL, + def_int,ss_none}, /* adjust horizontal (x) mouse sensitivity killough/mead */ + //jff 4/3/98 allow unlimited sensitivity + {"mouse_sensitivity_vert",{&mouseSensitivity_vert},{10},0,UL, + def_int,ss_none}, /* adjust vertical (y) mouse sensitivity killough/mead */ + //jff 3/8/98 allow -1 in mouse bindings to disable mouse function + {"mouseb_fire",{&mousebfire},{0},-1,MAX_MOUSEB, + def_int,ss_keys}, // mouse button number to use for fire + {"mouseb_strafe",{&mousebstrafe},{1},-1,MAX_MOUSEB, + def_int,ss_keys}, // mouse button number to use for strafing + {"mouseb_forward",{&mousebforward},{2},-1,MAX_MOUSEB, + def_int,ss_keys}, // mouse button number to use for forward motion + //jff 3/8/98 end of lower range change for -1 allowed in mouse binding + +// For key bindings, the values stored in the key_* variables // phares +// are the internal Doom Codes. The values stored in the default.cfg +// file are the keyboard codes. +// CPhipps - now they're the doom codes, so default.cfg can be portable + + {"Key bindings",{NULL},{0},UL,UL,def_none,ss_none}, + {"key_right", {&key_right}, {KEYD_RIGHTARROW}, + 0,MAX_KEY,def_key,ss_keys}, // key to turn right + {"key_left", {&key_left}, {KEYD_LEFTARROW} , + 0,MAX_KEY,def_key,ss_keys}, // key to turn left + {"key_up", {&key_up}, {KEYD_UPARROW} , + 0,MAX_KEY,def_key,ss_keys}, // key to move forward + {"key_down", {&key_down}, {KEYD_DOWNARROW}, + 0,MAX_KEY,def_key,ss_keys}, // key to move backward + {"key_menu_right", {&key_menu_right}, {KEYD_RIGHTARROW},// phares 3/7/98 + 0,MAX_KEY,def_key,ss_keys}, // key to move right in a menu // | + {"key_menu_left", {&key_menu_left}, {KEYD_LEFTARROW} ,// V + 0,MAX_KEY,def_key,ss_keys}, // key to move left in a menu + {"key_menu_up", {&key_menu_up}, {KEYD_UPARROW} , + 0,MAX_KEY,def_key,ss_keys}, // key to move up in a menu + {"key_menu_down", {&key_menu_down}, {KEYD_DOWNARROW} , + 0,MAX_KEY,def_key,ss_keys}, // key to move down in a menu + {"key_menu_backspace",{&key_menu_backspace},{KEYD_BACKSPACE} , + 0,MAX_KEY,def_key,ss_keys}, // delete key in a menu + {"key_menu_escape", {&key_menu_escape}, {KEYD_ESCAPE} , + 0,MAX_KEY,def_key,ss_keys}, // key to leave a menu , // phares 3/7/98 + {"key_menu_enter", {&key_menu_enter}, {KEYD_ENTER} , + 0,MAX_KEY,def_key,ss_keys}, // key to select from menu + {"key_strafeleft", {&key_strafeleft}, {','} , + 0,MAX_KEY,def_key,ss_keys}, // key to strafe left + {"key_straferight", {&key_straferight}, {'.'} , + 0,MAX_KEY,def_key,ss_keys}, // key to strafe right + + {"key_fire", {&key_fire}, {KEYD_RCTRL} , + 0,MAX_KEY,def_key,ss_keys}, // duh + {"key_use", {&key_use}, {' '} , + 0,MAX_KEY,def_key,ss_keys}, // key to open a door, use a switch + {"key_strafe", {&key_strafe}, {KEYD_RALT} , + 0,MAX_KEY,def_key,ss_keys}, // key to use with arrows to strafe + {"key_speed", {&key_speed}, {KEYD_RSHIFT} , + 0,MAX_KEY,def_key,ss_keys}, // key to run + + {"key_savegame", {&key_savegame}, {KEYD_F2} , + 0,MAX_KEY,def_key,ss_keys}, // key to save current game + {"key_loadgame", {&key_loadgame}, {KEYD_F3} , + 0,MAX_KEY,def_key,ss_keys}, // key to restore from saved games + {"key_soundvolume", {&key_soundvolume}, {KEYD_F4} , + 0,MAX_KEY,def_key,ss_keys}, // key to bring up sound controls + {"key_hud", {&key_hud}, {KEYD_F5} , + 0,MAX_KEY,def_key,ss_keys}, // key to adjust HUD + {"key_quicksave", {&key_quicksave}, {KEYD_F6} , + 0,MAX_KEY,def_key,ss_keys}, // key to to quicksave + {"key_endgame", {&key_endgame}, {KEYD_F7} , + 0,MAX_KEY,def_key,ss_keys}, // key to end the game + {"key_messages", {&key_messages}, {KEYD_F8} , + 0,MAX_KEY,def_key,ss_keys}, // key to toggle message enable + {"key_quickload", {&key_quickload}, {KEYD_F9} , + 0,MAX_KEY,def_key,ss_keys}, // key to load from quicksave + {"key_quit", {&key_quit}, {KEYD_F10} , + 0,MAX_KEY,def_key,ss_keys}, // key to quit game + {"key_gamma", {&key_gamma}, {KEYD_F11} , + 0,MAX_KEY,def_key,ss_keys}, // key to adjust gamma correction + {"key_spy", {&key_spy}, {KEYD_F12} , + 0,MAX_KEY,def_key,ss_keys}, // key to view from another coop player's view + {"key_pause", {&key_pause}, {KEYD_PAUSE} , + 0,MAX_KEY,def_key,ss_keys}, // key to pause the game + {"key_autorun", {&key_autorun}, {KEYD_CAPSLOCK} , + 0,MAX_KEY,def_key,ss_keys}, // key to toggle always run mode + {"key_chat", {&key_chat}, {'t'} , + 0,MAX_KEY,def_key,ss_keys}, // key to enter a chat message + {"key_backspace", {&key_backspace}, {KEYD_BACKSPACE} , + 0,MAX_KEY,def_key,ss_keys}, // backspace key + {"key_enter", {&key_enter}, {KEYD_ENTER} , + 0,MAX_KEY,def_key,ss_keys}, // key to select from menu or see last message + {"key_map", {&key_map}, {KEYD_TAB} , + 0,MAX_KEY,def_key,ss_keys}, // key to toggle automap display + {"key_map_right", {&key_map_right}, {KEYD_RIGHTARROW},// phares 3/7/98 + 0,MAX_KEY,def_key,ss_keys}, // key to shift automap right // | + {"key_map_left", {&key_map_left}, {KEYD_LEFTARROW} ,// V + 0,MAX_KEY,def_key,ss_keys}, // key to shift automap left + {"key_map_up", {&key_map_up}, {KEYD_UPARROW} , + 0,MAX_KEY,def_key,ss_keys}, // key to shift automap up + {"key_map_down", {&key_map_down}, {KEYD_DOWNARROW} , + 0,MAX_KEY,def_key,ss_keys}, // key to shift automap down + {"key_map_zoomin", {&key_map_zoomin}, {'='} , + 0,MAX_KEY,def_key,ss_keys}, // key to enlarge automap + {"key_map_zoomout", {&key_map_zoomout}, {'-'} , + 0,MAX_KEY,def_key,ss_keys}, // key to reduce automap + {"key_map_gobig", {&key_map_gobig}, {'0'} , + 0,MAX_KEY,def_key,ss_keys}, // key to get max zoom for automap + {"key_map_follow", {&key_map_follow}, {'f'} , + 0,MAX_KEY,def_key,ss_keys}, // key to toggle follow mode + {"key_map_mark", {&key_map_mark}, {'m'} , + 0,MAX_KEY,def_key,ss_keys}, // key to drop a marker on automap + {"key_map_clear", {&key_map_clear}, {'c'} , + 0,MAX_KEY,def_key,ss_keys}, // key to clear all markers on automap + {"key_map_grid", {&key_map_grid}, {'g'} , + 0,MAX_KEY,def_key,ss_keys}, // key to toggle grid display over automap + {"key_map_rotate", {&key_map_rotate}, {'r'} , + 0,MAX_KEY,def_key,ss_keys}, // key to toggle rotating the automap to match the player's orientation + {"key_map_overlay", {&key_map_overlay}, {'o'} , + 0,MAX_KEY,def_key,ss_keys}, // key to toggle overlaying the automap on the rendered display + {"key_reverse", {&key_reverse}, {'/'} , + 0,MAX_KEY,def_key,ss_keys}, // key to spin 180 instantly + {"key_zoomin", {&key_zoomin}, {'='} , + 0,MAX_KEY,def_key,ss_keys}, // key to enlarge display + {"key_zoomout", {&key_zoomout}, {'-'} , + 0,MAX_KEY,def_key,ss_keys}, // key to reduce display + {"key_chatplayer1", {&destination_keys[0]}, {'g'} , + 0,MAX_KEY,def_key,ss_keys}, // key to chat with player 1 + // killough 11/98: fix 'i'/'b' reversal + {"key_chatplayer2", {&destination_keys[1]}, {'i'} , + 0,MAX_KEY,def_key,ss_keys}, // key to chat with player 2 + {"key_chatplayer3", {&destination_keys[2]}, {'b'} , + 0,MAX_KEY,def_key,ss_keys}, // key to chat with player 3 + {"key_chatplayer4", {&destination_keys[3]}, {'r'} , + 0,MAX_KEY,def_key,ss_keys}, // key to chat with player 4 + {"key_weapontoggle",{&key_weapontoggle}, {'0'} , + 0,MAX_KEY,def_key,ss_keys}, // key to toggle between two most preferred weapons with ammo + {"key_weapon1", {&key_weapon1}, {'1'} , + 0,MAX_KEY,def_key,ss_keys}, // key to switch to weapon 1 (fist/chainsaw) + {"key_weapon2", {&key_weapon2}, {'2'} , + 0,MAX_KEY,def_key,ss_keys}, // key to switch to weapon 2 (pistol) + {"key_weapon3", {&key_weapon3}, {'3'} , + 0,MAX_KEY,def_key,ss_keys}, // key to switch to weapon 3 (supershotgun/shotgun) + {"key_weapon4", {&key_weapon4}, {'4'} , + 0,MAX_KEY,def_key,ss_keys}, // key to switch to weapon 4 (chaingun) + {"key_weapon5", {&key_weapon5}, {'5'} , + 0,MAX_KEY,def_key,ss_keys}, // key to switch to weapon 5 (rocket launcher) + {"key_weapon6", {&key_weapon6}, {'6'} , + 0,MAX_KEY,def_key,ss_keys}, // key to switch to weapon 6 (plasma rifle) + {"key_weapon7", {&key_weapon7}, {'7'} , + 0,MAX_KEY,def_key,ss_keys}, // key to switch to weapon 7 (bfg9000) // ^ + {"key_weapon8", {&key_weapon8}, {'8'} , + 0,MAX_KEY,def_key,ss_keys}, // key to switch to weapon 8 (chainsaw) // | + {"key_weapon9", {&key_weapon9}, {'9'} , + 0,MAX_KEY,def_key,ss_keys}, // key to switch to weapon 9 (supershotgun) // phares + + // killough 2/22/98: screenshot key + {"key_screenshot", {&key_screenshot}, {'*'} , + 0,MAX_KEY,def_key,ss_keys}, // key to take a screenshot + + {"Joystick settings",{NULL},{0},UL,UL,def_none,ss_none}, + {"use_joystick",{&usejoystick},{0},0,2, + def_int,ss_none}, // number of joystick to use (0 for none) + {"joy_left",{&joyleft},{0}, UL,UL,def_int,ss_none}, + {"joy_right",{&joyright},{0},UL,UL,def_int,ss_none}, + {"joy_up", {&joyup}, {0}, UL,UL,def_int,ss_none}, + {"joy_down",{&joydown},{0}, UL,UL,def_int,ss_none}, + {"joyb_fire",{&joybfire},{0},0,UL, + def_int,ss_keys}, // joystick button number to use for fire + {"joyb_strafe",{&joybstrafe},{1},0,UL, + def_int,ss_keys}, // joystick button number to use for strafing + {"joyb_speed",{&joybspeed},{2},0,UL, + def_int,ss_keys}, // joystick button number to use for running + {"joyb_use",{&joybuse},{3},0,UL, + def_int,ss_keys}, // joystick button number to use for use/open + + {"Chat macros",{NULL},{0},UL,UL,def_none,ss_none}, + {"chatmacro0", {0,&chat_macros[0]}, {0,HUSTR_CHATMACRO0},UL,UL, + def_str,ss_chat}, // chat string associated with 0 key + {"chatmacro1", {0,&chat_macros[1]}, {0,HUSTR_CHATMACRO1},UL,UL, + def_str,ss_chat}, // chat string associated with 1 key + {"chatmacro2", {0,&chat_macros[2]}, {0,HUSTR_CHATMACRO2},UL,UL, + def_str,ss_chat}, // chat string associated with 2 key + {"chatmacro3", {0,&chat_macros[3]}, {0,HUSTR_CHATMACRO3},UL,UL, + def_str,ss_chat}, // chat string associated with 3 key + {"chatmacro4", {0,&chat_macros[4]}, {0,HUSTR_CHATMACRO4},UL,UL, + def_str,ss_chat}, // chat string associated with 4 key + {"chatmacro5", {0,&chat_macros[5]}, {0,HUSTR_CHATMACRO5},UL,UL, + def_str,ss_chat}, // chat string associated with 5 key + {"chatmacro6", {0,&chat_macros[6]}, {0,HUSTR_CHATMACRO6},UL,UL, + def_str,ss_chat}, // chat string associated with 6 key + {"chatmacro7", {0,&chat_macros[7]}, {0,HUSTR_CHATMACRO7},UL,UL, + def_str,ss_chat}, // chat string associated with 7 key + {"chatmacro8", {0,&chat_macros[8]}, {0,HUSTR_CHATMACRO8},UL,UL, + def_str,ss_chat}, // chat string associated with 8 key + {"chatmacro9", {0,&chat_macros[9]}, {0,HUSTR_CHATMACRO9},UL,UL, + def_str,ss_chat}, // chat string associated with 9 key + + {"Automap settings",{NULL},{0},UL,UL,def_none,ss_none}, + //jff 1/7/98 defaults for automap colors + //jff 4/3/98 remove -1 in lower range, 0 now disables new map features + {"mapcolor_back", {&mapcolor_back}, {247},0,255, // black //jff 4/6/98 new black + def_colour,ss_auto}, // color used as background for automap + {"mapcolor_grid", {&mapcolor_grid}, {104},0,255, // dk gray + def_colour,ss_auto}, // color used for automap grid lines + {"mapcolor_wall", {&mapcolor_wall}, {23},0,255, // red-brown + def_colour,ss_auto}, // color used for one side walls on automap + {"mapcolor_fchg", {&mapcolor_fchg}, {55},0,255, // lt brown + def_colour,ss_auto}, // color used for lines floor height changes across + {"mapcolor_cchg", {&mapcolor_cchg}, {215},0,255, // orange + def_colour,ss_auto}, // color used for lines ceiling height changes across + {"mapcolor_clsd", {&mapcolor_clsd}, {208},0,255, // white + def_colour,ss_auto}, // color used for lines denoting closed doors, objects + {"mapcolor_rkey", {&mapcolor_rkey}, {175},0,255, // red + def_colour,ss_auto}, // color used for red key sprites + {"mapcolor_bkey", {&mapcolor_bkey}, {204},0,255, // blue + def_colour,ss_auto}, // color used for blue key sprites + {"mapcolor_ykey", {&mapcolor_ykey}, {231},0,255, // yellow + def_colour,ss_auto}, // color used for yellow key sprites + {"mapcolor_rdor", {&mapcolor_rdor}, {175},0,255, // red + def_colour,ss_auto}, // color used for closed red doors + {"mapcolor_bdor", {&mapcolor_bdor}, {204},0,255, // blue + def_colour,ss_auto}, // color used for closed blue doors + {"mapcolor_ydor", {&mapcolor_ydor}, {231},0,255, // yellow + def_colour,ss_auto}, // color used for closed yellow doors + {"mapcolor_tele", {&mapcolor_tele}, {119},0,255, // dk green + def_colour,ss_auto}, // color used for teleporter lines + {"mapcolor_secr", {&mapcolor_secr}, {252},0,255, // purple + def_colour,ss_auto}, // color used for lines around secret sectors + {"mapcolor_exit", {&mapcolor_exit}, {0},0,255, // none + def_colour,ss_auto}, // color used for exit lines + {"mapcolor_unsn", {&mapcolor_unsn}, {104},0,255, // dk gray + def_colour,ss_auto}, // color used for lines not seen without computer map + {"mapcolor_flat", {&mapcolor_flat}, {88},0,255, // lt gray + def_colour,ss_auto}, // color used for lines with no height changes + {"mapcolor_sprt", {&mapcolor_sprt}, {112},0,255, // green + def_colour,ss_auto}, // color used as things + {"mapcolor_item", {&mapcolor_item}, {231},0,255, // yellow + def_colour,ss_auto}, // color used for counted items + {"mapcolor_hair", {&mapcolor_hair}, {208},0,255, // white + def_colour,ss_auto}, // color used for dot crosshair denoting center of map + {"mapcolor_sngl", {&mapcolor_sngl}, {208},0,255, // white + def_colour,ss_auto}, // color used for the single player arrow + {"mapcolor_me", {&mapcolor_me}, {112},0,255, // green + def_colour,ss_auto}, // your (player) colour + {"mapcolor_enemy", {&mapcolor_enemy}, {177},0,255, + def_colour,ss_auto}, + {"mapcolor_frnd", {&mapcolor_frnd}, {112},0,255, + def_colour,ss_auto}, + //jff 3/9/98 add option to not show secrets til after found + {"map_secret_after", {&map_secret_after}, {0},0,1, // show secret after gotten + def_bool,ss_auto}, // prevents showing secret sectors till after entered + {"map_point_coord", {&map_point_coordinates}, {0},0,1, + def_bool,ss_auto}, + //jff 1/7/98 end additions for automap + {"automapmode", {(int*)&automapmode}, {0}, 0, 31, // CPhipps - remember automap mode + def_hex,ss_none}, // automap mode + + {"Heads-up display settings",{NULL},{0},UL,UL,def_none,ss_none}, + //jff 2/16/98 defaults for color ranges in hud and status + {"hudcolor_titl", {&hudcolor_titl}, {5},0,9, // gold range + def_int,ss_auto}, // color range used for automap level title + {"hudcolor_xyco", {&hudcolor_xyco}, {3},0,9, // green range + def_int,ss_auto}, // color range used for automap coordinates + {"hudcolor_mesg", {&hudcolor_mesg}, {6},0,9, // red range + def_int,ss_mess}, // color range used for messages during play + {"hudcolor_chat", {&hudcolor_chat}, {5},0,9, // gold range + def_int,ss_mess}, // color range used for chat messages and entry + {"hudcolor_list", {&hudcolor_list}, {5},0,9, // gold range //jff 2/26/98 + def_int,ss_mess}, // color range used for message review + {"hud_msg_lines", {&hud_msg_lines}, {1},1,16, // 1 line scrolling window + def_int,ss_mess}, // number of messages in review display (1=disable) + {"hud_list_bgon", {&hud_list_bgon}, {0},0,1, // solid window bg ena //jff 2/26/98 + def_bool,ss_mess}, // enables background window behind message review + {"hud_distributed",{&hud_distributed},{0},0,1, // hud broken up into 3 displays //jff 3/4/98 + def_bool,ss_none}, // splits HUD into three 2 line displays + + {"health_red", {&health_red} , {25},0,200, // below is red + def_int,ss_stat}, // amount of health for red to yellow transition + {"health_yellow", {&health_yellow}, {50},0,200, // below is yellow + def_int,ss_stat}, // amount of health for yellow to green transition + {"health_green", {&health_green} , {100},0,200,// below is green, above blue + def_int,ss_stat}, // amount of health for green to blue transition + {"armor_red", {&armor_red} , {25},0,200, // below is red + def_int,ss_stat}, // amount of armor for red to yellow transition + {"armor_yellow", {&armor_yellow} , {50},0,200, // below is yellow + def_int,ss_stat}, // amount of armor for yellow to green transition + {"armor_green", {&armor_green} , {100},0,200,// below is green, above blue + def_int,ss_stat}, // amount of armor for green to blue transition + {"ammo_red", {&ammo_red} , {25},0,100, // below 25% is red + def_int,ss_stat}, // percent of ammo for red to yellow transition + {"ammo_yellow", {&ammo_yellow} , {50},0,100, // below 50% is yellow, above green + def_int,ss_stat}, // percent of ammo for yellow to green transition + + //jff 2/16/98 HUD and status feature controls + {"hud_active", {&hud_active}, {2},0,2, // 0=off, 1=small, 2=full + def_int,ss_none}, // 0 for HUD off, 1 for HUD small, 2 for full HUD + //jff 2/23/98 + {"hud_displayed", {&hud_displayed}, {0},0,1, // whether hud is displayed + def_bool,ss_none}, // enables display of HUD + {"hud_nosecrets", {&hud_nosecrets}, {0},0,1, // no secrets/items/kills HUD line + def_bool,ss_stat}, // disables display of kills/items/secrets on HUD + + {"Weapon preferences",{NULL},{0},UL,UL,def_none,ss_none}, + // killough 2/8/98: weapon preferences set by user: + {"weapon_choice_1", {&weapon_preferences[0][0]}, {6}, 0,9, + def_int,ss_weap}, // first choice for weapon (best) + {"weapon_choice_2", {&weapon_preferences[0][1]}, {9}, 0,9, + def_int,ss_weap}, // second choice for weapon + {"weapon_choice_3", {&weapon_preferences[0][2]}, {4}, 0,9, + def_int,ss_weap}, // third choice for weapon + {"weapon_choice_4", {&weapon_preferences[0][3]}, {3}, 0,9, + def_int,ss_weap}, // fourth choice for weapon + {"weapon_choice_5", {&weapon_preferences[0][4]}, {2}, 0,9, + def_int,ss_weap}, // fifth choice for weapon + {"weapon_choice_6", {&weapon_preferences[0][5]}, {8}, 0,9, + def_int,ss_weap}, // sixth choice for weapon + {"weapon_choice_7", {&weapon_preferences[0][6]}, {5}, 0,9, + def_int,ss_weap}, // seventh choice for weapon + {"weapon_choice_8", {&weapon_preferences[0][7]}, {7}, 0,9, + def_int,ss_weap}, // eighth choice for weapon + {"weapon_choice_9", {&weapon_preferences[0][8]}, {1}, 0,9, + def_int,ss_weap}, // ninth choice for weapon (worst) + + // cournia - support for arbitrary music file (defaults are mp3) + {"Music", {NULL},{0},UL,UL,def_none,ss_none}, + {"mus_e1m1", {0,&S_music_files[mus_e1m1]}, {0,"e1m1.mp3"},UL,UL, + def_str,ss_none}, + {"mus_e1m2", {0,&S_music_files[mus_e1m2]}, {0,"e1m2.mp3"},UL,UL, + def_str,ss_none}, + {"mus_e1m3", {0,&S_music_files[mus_e1m3]}, {0,"e1m3.mp3"},UL,UL, + def_str,ss_none}, + {"mus_e1m4", {0,&S_music_files[mus_e1m4]}, {0,"e1m4.mp3"},UL,UL, + def_str,ss_none}, + {"mus_e1m5", {0,&S_music_files[mus_e1m5]}, {0,"e1m5.mp3"},UL,UL, + def_str,ss_none}, + {"mus_e1m6", {0,&S_music_files[mus_e1m6]}, {0,"e1m6.mp3"},UL,UL, + def_str,ss_none}, + {"mus_e1m7", {0,&S_music_files[mus_e1m7]}, {0,"e1m7.mp3"},UL,UL, + def_str,ss_none}, + {"mus_e1m8", {0,&S_music_files[mus_e1m8]}, {0,"e1m8.mp3"},UL,UL, + def_str,ss_none}, + {"mus_e1m9", {0,&S_music_files[mus_e1m9]}, {0,"e1m9.mp3"},UL,UL, + def_str,ss_none}, + {"mus_e2m1", {0,&S_music_files[mus_e2m1]}, {0,"e2m1.mp3"},UL,UL, + def_str,ss_none}, + {"mus_e2m2", {0,&S_music_files[mus_e2m2]}, {0,"e2m2.mp3"},UL,UL, + def_str,ss_none}, + {"mus_e2m3", {0,&S_music_files[mus_e2m3]}, {0,"e2m3.mp3"},UL,UL, + def_str,ss_none}, + {"mus_e2m4", {0,&S_music_files[mus_e2m4]}, {0,"e2m4.mp3"},UL,UL, + def_str,ss_none}, + {"mus_e2m5", {0,&S_music_files[mus_e2m5]}, {0,"e1m7.mp3"},UL,UL, + def_str,ss_none}, + {"mus_e2m6", {0,&S_music_files[mus_e2m6]}, {0,"e2m6.mp3"},UL,UL, + def_str,ss_none}, + {"mus_e2m7", {0,&S_music_files[mus_e2m7]}, {0,"e2m7.mp3"},UL,UL, + def_str,ss_none}, + {"mus_e2m8", {0,&S_music_files[mus_e2m8]}, {0,"e2m8.mp3"},UL,UL, + def_str,ss_none}, + {"mus_e2m9", {0,&S_music_files[mus_e2m9]}, {0,"e3m1.mp3"},UL,UL, + def_str,ss_none}, + {"mus_e3m1", {0,&S_music_files[mus_e3m1]}, {0,"e3m1.mp3"},UL,UL, + def_str,ss_none}, + {"mus_e3m2", {0,&S_music_files[mus_e3m2]}, {0,"e3m2.mp3"},UL,UL, + def_str,ss_none}, + {"mus_e3m3", {0,&S_music_files[mus_e3m3]}, {0,"e3m3.mp3"},UL,UL, + def_str,ss_none}, + {"mus_e3m4", {0,&S_music_files[mus_e3m4]}, {0,"e1m8.mp3"},UL,UL, + def_str,ss_none}, + {"mus_e3m5", {0,&S_music_files[mus_e3m5]}, {0,"e1m7.mp3"},UL,UL, + def_str,ss_none}, + {"mus_e3m6", {0,&S_music_files[mus_e3m6]}, {0,"e1m6.mp3"},UL,UL, + def_str,ss_none}, + {"mus_e3m7", {0,&S_music_files[mus_e3m7]}, {0,"e2m7.mp3"},UL,UL, + def_str,ss_none}, + {"mus_e3m8", {0,&S_music_files[mus_e3m8]}, {0,"e3m8.mp3"},UL,UL, + def_str,ss_none}, + {"mus_e3m9", {0,&S_music_files[mus_e3m9]}, {0,"e1m9.mp3"},UL,UL, + def_str,ss_none}, + {"mus_inter", {0,&S_music_files[mus_inter]}, {0,"e2m3.mp3"},UL,UL, + def_str,ss_none}, + {"mus_intro", {0,&S_music_files[mus_intro]}, {0,"intro.mp3"},UL,UL, + def_str,ss_none}, + {"mus_bunny", {0,&S_music_files[mus_bunny]}, {0,"bunny.mp3"},UL,UL, + def_str,ss_none}, + {"mus_victor", {0,&S_music_files[mus_victor]}, {0,"victor.mp3"},UL,UL, + def_str,ss_none}, + {"mus_introa", {0,&S_music_files[mus_introa]}, {0,"intro.mp3"},UL,UL, + def_str,ss_none}, + {"mus_runnin", {0,&S_music_files[mus_runnin]}, {0,"runnin.mp3"},UL,UL, + def_str,ss_none}, + {"mus_stalks", {0,&S_music_files[mus_stalks]}, {0,"stalks.mp3"},UL,UL, + def_str,ss_none}, + {"mus_countd", {0,&S_music_files[mus_countd]}, {0,"countd.mp3"},UL,UL, + def_str,ss_none}, + {"mus_betwee", {0,&S_music_files[mus_betwee]}, {0,"betwee.mp3"},UL,UL, + def_str,ss_none}, + {"mus_doom", {0,&S_music_files[mus_doom]}, {0,"doom.mp3"},UL,UL, + def_str,ss_none}, + {"mus_the_da", {0,&S_music_files[mus_the_da]}, {0,"the_da.mp3"},UL,UL, + def_str,ss_none}, + {"mus_shawn", {0,&S_music_files[mus_shawn]}, {0,"shawn.mp3"},UL,UL, + def_str,ss_none}, + {"mus_ddtblu", {0,&S_music_files[mus_ddtblu]}, {0,"ddtblu.mp3"},UL,UL, + def_str,ss_none}, + {"mus_in_cit", {0,&S_music_files[mus_in_cit]}, {0,"in_cit.mp3"},UL,UL, + def_str,ss_none}, + {"mus_dead", {0,&S_music_files[mus_dead]}, {0,"dead.mp3"},UL,UL, + def_str,ss_none}, + {"mus_stlks2", {0,&S_music_files[mus_stlks2]}, {0,"stalks.mp3"},UL,UL, + def_str,ss_none}, + {"mus_theda2", {0,&S_music_files[mus_theda2]}, {0,"the_da.mp3"},UL,UL, + def_str,ss_none}, + {"mus_doom2", {0,&S_music_files[mus_doom2]}, {0,"doom.mp3"},UL,UL, + def_str,ss_none}, + {"mus_ddtbl2", {0,&S_music_files[mus_ddtbl2]}, {0,"ddtblu.mp3"},UL,UL, + def_str,ss_none}, + {"mus_runni2", {0,&S_music_files[mus_runni2]}, {0,"runnin.mp3"},UL,UL, + def_str,ss_none}, + {"mus_dead2", {0,&S_music_files[mus_dead2]}, {0,"dead.mp3"},UL,UL, + def_str,ss_none}, + {"mus_stlks3", {0,&S_music_files[mus_stlks3]}, {0,"stalks.mp3"},UL,UL, + def_str,ss_none}, + {"mus_romero", {0,&S_music_files[mus_romero]}, {0,"romero.mp3"},UL,UL, + def_str,ss_none}, + {"mus_shawn2", {0,&S_music_files[mus_shawn2]}, {0,"shawn.mp3"},UL,UL, + def_str,ss_none}, + {"mus_messag", {0,&S_music_files[mus_messag]}, {0,"messag.mp3"},UL,UL, + def_str,ss_none}, + {"mus_count2", {0,&S_music_files[mus_count2]}, {0,"countd.mp3"},UL,UL, + def_str,ss_none}, + {"mus_ddtbl3", {0,&S_music_files[mus_ddtbl3]}, {0,"ddtblu.mp3"},UL,UL, + def_str,ss_none}, + {"mus_ampie", {0,&S_music_files[mus_ampie]}, {0,"ampie.mp3"},UL,UL, + def_str,ss_none}, + {"mus_theda3", {0,&S_music_files[mus_theda3]}, {0,"the_da.mp3"},UL,UL, + def_str,ss_none}, + {"mus_adrian", {0,&S_music_files[mus_adrian]}, {0,"adrian.mp3"},UL,UL, + def_str,ss_none}, + {"mus_messg2", {0,&S_music_files[mus_messg2]}, {0,"messag.mp3"},UL,UL, + def_str,ss_none}, + {"mus_romer2", {0,&S_music_files[mus_romer2]}, {0,"romero.mp3"},UL,UL, + def_str,ss_none}, + {"mus_tense", {0,&S_music_files[mus_tense]}, {0,"tense.mp3"},UL,UL, + def_str,ss_none}, + {"mus_shawn3", {0,&S_music_files[mus_shawn3]}, {0,"shawn.mp3"},UL,UL, + def_str,ss_none}, + {"mus_openin", {0,&S_music_files[mus_openin]}, {0,"openin.mp3"},UL,UL, + def_str,ss_none}, + {"mus_evil", {0,&S_music_files[mus_evil]}, {0,"evil.mp3"},UL,UL, + def_str,ss_none}, + {"mus_ultima", {0,&S_music_files[mus_ultima]}, {0,"ultima.mp3"},UL,UL, + def_str,ss_none}, + {"mus_read_m", {0,&S_music_files[mus_read_m]}, {0,"read_m.mp3"},UL,UL, + def_str,ss_none}, + {"mus_dm2ttl", {0,&S_music_files[mus_dm2ttl]}, {0,"dm2ttl.mp3"},UL,UL, + def_str,ss_none}, + {"mus_dm2int", {0,&S_music_files[mus_dm2int]}, {0,"dm2int.mp3"},UL,UL, + def_str,ss_none}, +}; + +int numdefaults; +static const char* defaultfile; // CPhipps - static, const + +// +// M_SaveDefaults +// + +void M_SaveDefaults (void) + { + int i; + FILE* f; + + f = fopen (defaultfile, "w"); + if (!f) + return; // can't write the file, but don't complain + + // 3/3/98 explain format of file + + fprintf(f,"# Doom config file\n"); + fprintf(f,"# Format:\n"); + fprintf(f,"# variable value\n"); + + for (i = 0 ; i < numdefaults ; i++) { + if (defaults[i].type == def_none) { + // CPhipps - pure headers + fprintf(f, "\n# %s\n", defaults[i].name); + } else + // CPhipps - modified for new default_t form + if (!IS_STRING(defaults[i])) //jff 4/10/98 kill super-hack on pointer value + { + // CPhipps - remove keycode hack + // killough 3/6/98: use spaces instead of tabs for uniform justification + if (defaults[i].type == def_hex) + fprintf (f,"%-25s 0x%x\n",defaults[i].name,*(defaults[i].location.pi)); + else + fprintf (f,"%-25s %5i\n",defaults[i].name,*(defaults[i].location.pi)); + } + else + { + fprintf (f,"%-25s \"%s\"\n",defaults[i].name,*(defaults[i].location.ppsz)); + } + } + + fclose (f); + } + +/* + * M_LookupDefault + * + * cph - mimic MBF function for now. Yes it's crap. + */ + +struct default_s *M_LookupDefault(const char *name) +{ + int i; + for (i = 0 ; i < numdefaults ; i++) + if ((defaults[i].type != def_none) && !strcmp(name, defaults[i].name)) + return &defaults[i]; + I_Error("M_LookupDefault: %s not found",name); + return NULL; +} + +// +// M_LoadDefaults +// + +#define NUMCHATSTRINGS 10 // phares 4/13/98 + +void M_LoadDefaults (void) +{ + int i; + int len; + FILE* f; + char def[80]; + char strparm[100]; + char* newstring = NULL; // killough + int parm; + boolean isstring; + + // set everything to base values + + numdefaults = sizeof(defaults)/sizeof(defaults[0]); + for (i = 0 ; i < numdefaults ; i++) { + if (defaults[i].location.ppsz) + *defaults[i].location.ppsz = strdup(defaults[i].defaultvalue.psz); + if (defaults[i].location.pi) + *defaults[i].location.pi = defaults[i].defaultvalue.i; + } + + // check for a custom default file + + i = M_CheckParm ("-config"); + if (i && i < myargc-1) + defaultfile = myargv[i+1]; + else { + const char* exedir = I_DoomExeDir(); + defaultfile = malloc(PATH_MAX+1); + /* get config file from same directory as executable */ +#ifdef HAVE_SNPRINTF + snprintf((char *)defaultfile, PATH_MAX, +#else + sprintf ((char *)defaultfile, +#endif + "%s%s%sboom.cfg", exedir, HasTrailingSlash(exedir) ? "" : "/", +#if ((defined GL_DOOM) && (defined _MSC_VER)) + "gl"); +#else + "pr"); +#endif + } + + lprintf (LO_CONFIRM, " default file: %s\n",defaultfile); + + // read the file in, overriding any set defaults + + f = fopen (defaultfile, "r"); + if (f) + { + while (!feof(f)) + { + isstring = false; + if (fscanf (f, "%79s %[^\n]\n", def, strparm) == 2) + { + + //jff 3/3/98 skip lines not starting with an alphanum + + if (!isalnum(def[0])) + continue; + + if (strparm[0] == '"') { + // get a string default + + isstring = true; + len = strlen(strparm); + newstring = (char *) malloc(len); + strparm[len-1] = 0; // clears trailing double-quote mark + strcpy(newstring, strparm+1); // clears leading double-quote mark + } else if ((strparm[0] == '0') && (strparm[1] == 'x')) { + // CPhipps - allow ints to be specified in hex + sscanf(strparm+2, "%x", &parm); + } else { + sscanf(strparm, "%i", &parm); + // Keycode hack removed + } + + for (i = 0 ; i < numdefaults ; i++) + if ((defaults[i].type != def_none) && !strcmp(def, defaults[i].name)) + { + // CPhipps - safety check + if (isstring != IS_STRING(defaults[i])) { + lprintf(LO_WARN, "M_LoadDefaults: Type mismatch reading %s\n", defaults[i].name); + continue; + } + if (!isstring) + { + + //jff 3/4/98 range check numeric parameters + + if ((defaults[i].minvalue==UL || defaults[i].minvalue<=parm) && + (defaults[i].maxvalue==UL || defaults[i].maxvalue>=parm)) + *(defaults[i].location.pi) = parm; + } + else + { + free((char*)*(defaults[i].location.ppsz)); /* phares 4/13/98 */ + *(defaults[i].location.ppsz) = newstring; + } + break; + } + } + } + + fclose (f); + } + //jff 3/4/98 redundant range checks for hud deleted here + /* proff 2001/7/1 - added prboom.wad as last entry so it's always loaded and + doesn't overlap with the cfg settings */ + wad_files[MAXLOADFILES-1]="prboom.wad"; +} + + +// +// SCREEN SHOTS +// + +// +// M_ScreenShot +// +// Modified by Lee Killough so that any number of shots can be taken, +// the code is faster, and no annoying "screenshot" message appears. + +// CPhipps - modified to use its own buffer for the image +// - checks for the case where no file can be created (doesn't occur on POSIX systems, would on DOS) +// - track errors better +// - split into 2 functions + +// +// M_DoScreenShot +// Takes a screenshot into the names file + +void M_DoScreenShot (const char* fname) +{ + if (I_ScreenShot(fname) != 0) + doom_printf("M_ScreenShot: Error writing screenshot\n"); +} + +#ifndef SCREENSHOT_DIR +#define SCREENSHOT_DIR "." +#endif + +#ifdef HAVE_LIBPNG +#define SCREENSHOT_EXT ".png" +#else +#define SCREENSHOT_EXT ".bmp" +#endif + +void M_ScreenShot(void) +{ + static int shot; + char lbmname[PATH_MAX + 1]; + int startshot; + + if (!access(SCREENSHOT_DIR,2)) + { + startshot = shot; // CPhipps - prevent infinite loop + + do { + sprintf(lbmname,"%s/doom%02d" SCREENSHOT_EXT, SCREENSHOT_DIR, shot++); + } while (!access(lbmname,0) && (shot != startshot) && (shot < 10000)); + + if (access(lbmname,0)) + { + S_StartSound(NULL,gamemode==commercial ? sfx_radio : sfx_tink); + M_DoScreenShot(lbmname); // cph + return; + } + } + + doom_printf ("M_ScreenShot: Couldn't create screenshot"); + return; +} diff --git a/src/m_misc.h b/src/m_misc.h new file mode 100644 index 0000000..2e4d545 --- /dev/null +++ b/src/m_misc.h @@ -0,0 +1,111 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * External non-system-specific stuff, like storing config settings, + * simple file handling, and saving screnshots. + * + *-----------------------------------------------------------------------------*/ + + +#ifndef __M_MISC__ +#define __M_MISC__ + + +#include "doomtype.h" +// +// MISC +// + +boolean M_WriteFile (char const* name,void* source,int length); + +int M_ReadFile (char const* name,byte** buffer); + +void M_ScreenShot (void); +void M_DoScreenShot (const char*); // cph + +void M_LoadDefaults (void); + +void M_SaveDefaults (void); + +struct default_s *M_LookupDefault(const char *name); /* killough 11/98 */ + +// phares 4/21/98: +// Moved from m_misc.c so m_menu.c could see it. + +// CPhipps - struct to hold a value in a config file +// Cannot be a union, as it must be initialised +typedef struct default_s +{ + const char* name; + /* cph - + * The location struct holds the pointer to the variable holding the + * setting. For int's we do nothing special. + * For strings, the string is actually stored on our heap with Z_Strdup() + * BUT we don't want the rest of the program to be able to modify them, + * so we declare it const. It's not really const though, and m_misc.c and + * m_menu.c cast it back when they need to change it. Possibly this is + * more trouble than it's worth. + */ + struct { + int* pi; + const char** ppsz; + } location; + struct { + int i; + const char* psz; + } defaultvalue; // CPhipps - default value + // Limits (for an int) + int minvalue; // jff 3/3/98 minimum allowed value + int maxvalue; // jff 3/3/98 maximum allowed value + enum { + def_none, // Dummy entry + def_str, // A string + def_int, // Integer + def_hex, // Integer (write in hex) + def_bool = def_int, // Boolean + def_key = def_hex, // Key code (byte) + def_mouseb = def_int,// Mouse button + def_colour = def_hex // Colour (256 colour palette entry) + } type; // CPhipps - type of entry + int setupscreen; // phares 4/19/98: setup screen where this appears + int *current; /* cph - MBF-like pointer to current value */ + // cph - removed the help strings from the config file + // const char* help; // jff 3/3/98 description of parameter + // CPhipps - remove unused "lousy hack" code + struct setup_menu_s *setup_menu; /* Xref to setup menu item, if any */ +} default_t; + +#define IS_STRING(dv) ((dv).type == def_str) +// CPhipps - What is the max. key code that X will send us? +#define MAX_KEY 65536 +#define MAX_MOUSEB 2 + +#define UL (-123456789) /* magic number for no min or max for parameter */ + +#endif diff --git a/src/m_random.c b/src/m_random.c new file mode 100644 index 0000000..bd0c7bc --- /dev/null +++ b/src/m_random.c @@ -0,0 +1,147 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Random number LUT. + * + * 1/19/98 killough: Rewrote random number generator for better randomness, + * while at the same time maintaining demo sync and backward compatibility. + * + * 2/16/98 killough: Made each RNG local to each control-equivalent block, + * to reduce the chances of demo sync problems. + * + *-----------------------------------------------------------------------------*/ + + +#include "doomstat.h" +#include "m_random.h" +#include "lprintf.h" + +// +// M_Random +// Returns a 0-255 number +// +static const unsigned char rndtable[256] = { // 1/19/98 killough -- made const + 0, 8, 109, 220, 222, 241, 149, 107, 75, 248, 254, 140, 16, 66 , + 74, 21, 211, 47, 80, 242, 154, 27, 205, 128, 161, 89, 77, 36 , + 95, 110, 85, 48, 212, 140, 211, 249, 22, 79, 200, 50, 28, 188 , + 52, 140, 202, 120, 68, 145, 62, 70, 184, 190, 91, 197, 152, 224 , + 149, 104, 25, 178, 252, 182, 202, 182, 141, 197, 4, 81, 181, 242 , + 145, 42, 39, 227, 156, 198, 225, 193, 219, 93, 122, 175, 249, 0 , + 175, 143, 70, 239, 46, 246, 163, 53, 163, 109, 168, 135, 2, 235 , + 25, 92, 20, 145, 138, 77, 69, 166, 78, 176, 173, 212, 166, 113 , + 94, 161, 41, 50, 239, 49, 111, 164, 70, 60, 2, 37, 171, 75 , + 136, 156, 11, 56, 42, 146, 138, 229, 73, 146, 77, 61, 98, 196 , + 135, 106, 63, 197, 195, 86, 96, 203, 113, 101, 170, 247, 181, 113 , + 80, 250, 108, 7, 255, 237, 129, 226, 79, 107, 112, 166, 103, 241 , + 24, 223, 239, 120, 198, 58, 60, 82, 128, 3, 184, 66, 143, 224 , + 145, 224, 81, 206, 163, 45, 63, 90, 168, 114, 59, 33, 159, 95 , + 28, 139, 123, 98, 125, 196, 15, 70, 194, 253, 54, 14, 109, 226 , + 71, 17, 161, 93, 186, 87, 244, 138, 20, 52, 123, 251, 26, 36 , + 17, 46, 52, 231, 232, 76, 31, 221, 84, 37, 216, 165, 212, 106 , + 197, 242, 98, 43, 39, 175, 254, 145, 190, 84, 118, 222, 187, 136 , + 120, 163, 236, 249 +}; + +rng_t rng; // the random number state + +unsigned long rngseed = 1993; // killough 3/26/98: The seed + +int (P_Random)(pr_class_t pr_class +#ifdef INSTRUMENTED + , const char *file, int line +#endif +) +{ + // killough 2/16/98: We always update both sets of random number + // generators, to ensure repeatability if the demo_compatibility + // flag is changed while the program is running. Changing the + // demo_compatibility flag does not change the sequences generated, + // only which one is selected from. + // + // All of this RNG stuff is tricky as far as demo sync goes -- + // it's like playing with explosives :) Lee + +#ifdef INSTRUMENTED + //lprintf(LO_DEBUG, "%.10d: %.10d - %s:%.5d\n", gametic, pr_class, file, line); +#endif + + int compat = pr_class == pr_misc ? + (rng.prndindex = (rng.prndindex + 1) & 255) : + (rng. rndindex = (rng. rndindex + 1) & 255) ; + + unsigned long boom; + + // killough 3/31/98: + // If demo sync insurance is not requested, use + // much more unstable method by putting everything + // except pr_misc into pr_all_in_one + + if (pr_class != pr_misc && !demo_insurance) // killough 3/31/98 + pr_class = pr_all_in_one; + + boom = rng.seed[pr_class]; + + // killough 3/26/98: add pr_class*2 to addend + + rng.seed[pr_class] = boom * 1664525ul + 221297ul + pr_class*2; + + if (demo_compatibility) + return rndtable[compat]; + + boom >>= 20; + + /* killough 3/30/98: use gametic-levelstarttic to shuffle RNG + * killough 3/31/98: but only if demo insurance requested, + * since it's unnecessary for random shuffling otherwise + * killough 9/29/98: but use basetic now instead of levelstarttic + * cph - DEMOSYNC - this change makes MBF demos work, + * but does it break Boom ones? + */ + + if (demo_insurance) + boom += (gametic-basetic)*7; + + return boom & 255; +} + +// Initialize all the seeds +// +// This initialization method is critical to maintaining demo sync. +// Each seed is initialized according to its class, so if new classes +// are added they must be added to end of pr_class_t list. killough +// + +void M_ClearRandom (void) +{ + int i; + unsigned long seed = rngseed*2+1; // add 3/26/98: add rngseed + for (i=0; i +#ifdef __arch__swab16 +#define doom_swap_s (signed short)__arch__swab16 +#endif +#ifdef __arch__swab32 +#define doom_swap_l (signed long)__arch__swab32 +#endif +#endif /* HAVE_ASM_BYTEORDER_H */ + +#ifdef HAVE_LIBKERN_OSBYTEORDER_H +#include + +#define doom_swap_s (short)OSSwapInt16 +#define doom_swap_l (long)OSSwapInt32 +#endif + +#ifndef doom_swap_l +#define doom_swap_l(x) \ + ((long int)((((unsigned long int)(x) & 0x000000ffU) << 24) | \ + (((unsigned long int)(x) & 0x0000ff00U) << 8) | \ + (((unsigned long int)(x) & 0x00ff0000U) >> 8) | \ + (((unsigned long int)(x) & 0xff000000U) >> 24))) +#endif + +#ifndef doom_swap_s +#define doom_swap_s(x) \ + ((short int)((((unsigned short int)(x) & 0x00ff) << 8) | \ + (((unsigned short int)(x) & 0xff00) >> 8))) +#endif + +/* Macros are named doom_XtoYT, where + * X is thing to convert from, Y is thing to convert to, chosen from + * n for network, h for host (i.e our machine's), w for WAD (Doom data files) + * and T is the type, l or s for long or short + * + * CPhipps - all WADs and network packets will be little endian for now + * Use separate macros so network could be converted to big-endian later. + */ + +#ifdef WORDS_BIGENDIAN + +#define doom_wtohl(x) doom_swap_l(x) +#define doom_htowl(x) doom_swap_l(x) +#define doom_wtohs(x) doom_swap_s(x) +#define doom_htows(x) doom_swap_s(x) + +#define doom_ntohl(x) doom_swap_l(x) +#define doom_htonl(x) doom_swap_l(x) +#define doom_ntohs(x) doom_swap_s(x) +#define doom_htons(x) doom_swap_s(x) + +#else + +#define doom_wtohl(x) (long int)(x) +#define doom_htowl(x) (long int)(x) +#define doom_wtohs(x) (short int)(x) +#define doom_htows(x) (short int)(x) + +#define doom_ntohl(x) (long int)(x) +#define doom_htonl(x) (long int)(x) +#define doom_ntohs(x) (short int)(x) +#define doom_htons(x) (short int)(x) + +#endif + +/* CPhipps - Boom's old LONG and SHORT endianness macros are for WAD stuff */ + +#define LONG(x) doom_wtohl(x) +#define SHORT(x) doom_htows(x) + +#endif diff --git a/src/md5.c b/src/md5.c new file mode 100644 index 0000000..d69ec0c --- /dev/null +++ b/src/md5.c @@ -0,0 +1,240 @@ +/* + * This code implements the MD5 message-digest algorithm. + * The algorithm is due to Ron Rivest. This code was + * written by Colin Plumb in 1993, no copyright is claimed. + * This code is in the public domain; do with it what you wish. + * + * Equivalent code is available from RSA Data Security, Inc. + * This code has been tested against that, and is equivalent, + * except that you don't need to include two pages of legalese + * with every copy. + * + * To compute the message digest of a chunk of bytes, declare an + * MD5Context structure, pass it to MD5Init, call MD5Update as + * needed on buffers full of bytes, and then call MD5Final, which + * will fill a supplied 16-byte array with the digest. + * + * Changed so as no longer to depend on Colin Plumb's `usual.h' header + * definitions; now uses stuff from dpkg's config.h. + * - Ian Jackson . + * Still in the public domain. + */ +#include "config.h" + +#include /* for memcpy() */ +#include /* for stupid systems */ + +#include "md5.h" + +#ifdef WORDS_BIGENDIAN +void +byteSwap(UWORD32 *buf, unsigned words) +{ + md5byte *p = (md5byte *)buf; + + do { + *buf++ = (UWORD32)((unsigned)p[3] << 8 | p[2]) << 16 | + ((unsigned)p[1] << 8 | p[0]); + p += 4; + } while (--words); +} +#else +#define byteSwap(buf,words) +#endif + +/* + * Start MD5 accumulation. Set bit count to 0 and buffer to mysterious + * initialization constants. + */ +void +MD5Init(struct MD5Context *ctx) +{ + ctx->buf[0] = 0x67452301; + ctx->buf[1] = 0xefcdab89; + ctx->buf[2] = 0x98badcfe; + ctx->buf[3] = 0x10325476; + + ctx->bytes[0] = 0; + ctx->bytes[1] = 0; +} + +/* + * Update context to reflect the concatenation of another buffer full + * of bytes. + */ +void +MD5Update(struct MD5Context *ctx, md5byte const *buf, unsigned len) +{ + UWORD32 t; + + /* Update byte count */ + + t = ctx->bytes[0]; + if ((ctx->bytes[0] = t + len) < t) + ctx->bytes[1]++; /* Carry from low to high */ + + t = 64 - (t & 0x3f); /* Space available in ctx->in (at least 1) */ + if (t > len) { + memcpy((md5byte *)ctx->in + 64 - t, buf, len); + return; + } + /* First chunk is an odd size */ + memcpy((md5byte *)ctx->in + 64 - t, buf, t); + byteSwap(ctx->in, 16); + MD5Transform(ctx->buf, ctx->in); + buf += t; + len -= t; + + /* Process data in 64-byte chunks */ + while (len >= 64) { + memcpy(ctx->in, buf, 64); + byteSwap(ctx->in, 16); + MD5Transform(ctx->buf, ctx->in); + buf += 64; + len -= 64; + } + + /* Handle any remaining bytes of data. */ + memcpy(ctx->in, buf, len); +} + +/* + * Final wrapup - pad to 64-byte boundary with the bit pattern + * 1 0* (64-bit count of bits processed, MSB-first) + */ +void +MD5Final(md5byte digest[16], struct MD5Context *ctx) +{ + int count = ctx->bytes[0] & 0x3f; /* Number of bytes in ctx->in */ + md5byte *p = (md5byte *)ctx->in + count; + + /* Set the first char of padding to 0x80. There is always room. */ + *p++ = 0x80; + + /* Bytes of padding needed to make 56 bytes (-8..55) */ + count = 56 - 1 - count; + + if (count < 0) { /* Padding forces an extra block */ + memset(p, 0, count + 8); + byteSwap(ctx->in, 16); + MD5Transform(ctx->buf, ctx->in); + p = (md5byte *)ctx->in; + count = 56; + } + memset(p, 0, count); + byteSwap(ctx->in, 14); + + /* Append length in bits and transform */ + ctx->in[14] = ctx->bytes[0] << 3; + ctx->in[15] = ctx->bytes[1] << 3 | ctx->bytes[0] >> 29; + MD5Transform(ctx->buf, ctx->in); + + byteSwap(ctx->buf, 4); + memcpy(digest, ctx->buf, 16); + memset(ctx, 0, sizeof(ctx)); /* In case it's sensitive */ +} + +#ifndef ASM_MD5 + +/* The four core functions - F1 is optimized somewhat */ + +/* #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,in,s) \ + (w += f(x,y,z) + in, w = (w<>(32-s)) + 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 +MD5Transform(UWORD32 buf[4], UWORD32 const in[16]) +{ + register UWORD32 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; +} + +#endif diff --git a/src/md5.h b/src/md5.h new file mode 100644 index 0000000..3ebeb36 --- /dev/null +++ b/src/md5.h @@ -0,0 +1,47 @@ +/* + * This is the header file for the MD5 message-digest algorithm. + * The algorithm is due to Ron Rivest. This code was + * written by Colin Plumb in 1993, no copyright is claimed. + * This code is in the public domain; do with it what you wish. + * + * Equivalent code is available from RSA Data Security, Inc. + * This code has been tested against that, and is equivalent, + * except that you don't need to include two pages of legalese + * with every copy. + * + * To compute the message digest of a chunk of bytes, declare an + * MD5Context structure, pass it to MD5Init, call MD5Update as + * needed on buffers full of bytes, and then call MD5Final, which + * will fill a supplied 16-byte array with the digest. + * + * Changed so as no longer to depend on Colin Plumb's `usual.h' + * header definitions; now uses stuff from dpkg's config.h + * - Ian Jackson . + * Still in the public domain. + */ + +#ifndef MD5_H +#define MD5_H + +#ifdef _MSC_VER +#define WIN32_LEAN_AND_MEAN +#include +#define UWORD32 DWORD +#else +#include +#define UWORD32 uint32_t +#endif +#define md5byte unsigned char + +struct MD5Context { + UWORD32 buf[4]; + UWORD32 bytes[2]; + UWORD32 in[16]; +}; + +void MD5Init(struct MD5Context *context); +void MD5Update(struct MD5Context *context, md5byte const *buf, unsigned len); +void MD5Final(unsigned char digest[16], struct MD5Context *context); +void MD5Transform(UWORD32 buf[4], UWORD32 const in[16]); + +#endif /* !MD5_H */ diff --git a/src/mmus2mid.c b/src/mmus2mid.c new file mode 100644 index 0000000..3669c36 --- /dev/null +++ b/src/mmus2mid.c @@ -0,0 +1,866 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * This file supports conversion of MUS format music in memory + * to MIDI format 1 music in memory. + * + * The primary routine, mmus2mid, converts a block of memory in MUS format + * to an Allegro MIDI structure. This supports playing MUS lumps in a wad + * file with BOOM. + * + * Another routine, Midi2MIDI, converts a block of memory in MIDI format 1 to + * an Allegro MIDI structure. This supports playing MIDI lumps in a wad + * file with BOOM. + * + * For testing purposes, and to make a utility if desired, if the symbol + * STANDALONE is defined by uncommenting the definition below, a main + * routine is compiled that will convert a possibly wildcarded set of MUS + * files to a similarly named set of MIDI files. + * + * Much of the code here is thanks to S. Bacquet's source for QMUS2MID.C + * + *----------------------------------------------------------------------------- + */ + + +#include +#include +#include +#include +#include +#include +#include +#ifdef MSDOS /* proff: I don't use allegro in windows */ +#include +#endif /* !MSDOS */ +#include "mmus2mid.h" +#include "lprintf.h" // jff 08/03/98 - declaration of lprintf + +//#define STANDALONE /* uncomment this to make MMUS2MID.EXE */ +#ifndef STANDALONE +#include "m_swap.h" +#include "z_zone.h" +#endif + +// some macros to decode mus event bit fields + +#define last(e) ((UBYTE)((e) & 0x80)) +#define event_type(e) ((UBYTE)(((e) & 0x7F) >> 4)) +#define channel(e) ((UBYTE)((e) & 0x0F)) + +// event types + +typedef enum +{ + RELEASE_NOTE, + PLAY_NOTE, + BEND_NOTE, + SYS_EVENT, + CNTL_CHANGE, + UNKNOWN_EVENT1, + SCORE_END, + UNKNOWN_EVENT2, +} mus_event_t; + +// MUS format header structure + +typedef struct +{ + char ID[4]; // identifier "MUS"0x1A + UWORD ScoreLength; // length of music portion + UWORD ScoreStart; // offset of music portion + UWORD channels; // count of primary channels + UWORD SecChannels; // count of secondary channels + UWORD InstrCnt; // number of instruments +} PACKEDATTR MUSheader; + +// to keep track of information in a MIDI track + +typedef struct Track +{ + char velocity; + long deltaT; + UBYTE lastEvt; + long alloced; +} TrackInfo; + +// array of info about tracks + +static TrackInfo track[MIDI_TRACKS]; + +// initial track size allocation +#define TRACKBUFFERSIZE 1024 + +// lookup table MUS -> MID controls +static UBYTE MUS2MIDcontrol[15] = +{ + 0, // Program change - not a MIDI control change + 0x00, // Bank select + 0x01, // Modulation pot + 0x07, // Volume + 0x0A, // Pan pot + 0x0B, // Expression pot + 0x5B, // Reverb depth + 0x5D, // Chorus depth + 0x40, // Sustain pedal + 0x43, // Soft pedal + 0x78, // All sounds off + 0x7B, // All notes off + 0x7E, // Mono + 0x7F, // Poly + 0x79 // Reset all controllers +}; + +// some strings of bytes used in the midi format + +static UBYTE midikey[] = +{0x00,0xff,0x59,0x02,0x00,0x00}; // C major +static UBYTE miditempo[] = +{0x00,0xff,0x51,0x03,0x09,0xa3,0x1a}; // uS/qnote +static UBYTE midihdr[] = +{'M','T','h','d',0,0,0,6,0,1,0,0,0,0}; // header (length 6, format 1) +static UBYTE trackhdr[] = +{'M','T','r','k'}; // track header + +// static routine prototypes + +static int TWriteByte(MIDI *mididata, int MIDItrack, UBYTE byte); +static int TWriteVarLen(MIDI *mididata, int MIDItrack, register ULONG value); +static ULONG ReadTime(const UBYTE **musptrp); +static int FirstChannelAvailable(int MUS2MIDchannel[]); +static UBYTE MidiEvent(MIDI *mididata,UBYTE midicode,UBYTE MIDIchannel, + UBYTE MIDItrack,int nocomp); + +// +// TWriteByte() +// +// write one byte to the selected MIDItrack, update current position +// if track allocation exceeded, double it +// if track not allocated, initially allocate TRACKBUFFERSIZE bytes +// +// Passed pointer to Allegro MIDI structure, number of the MIDI track being +// written, and the byte to write. +// +// Returns 0 on success, MEMALLOC if a memory allocation error occurs +// +static int TWriteByte(MIDI *mididata, int MIDItrack, UBYTE byte) +{ + ULONG pos ; + + pos = mididata->track[MIDItrack].len; + if (pos >= (ULONG)track[MIDItrack].alloced) + { + track[MIDItrack].alloced = // double allocation + track[MIDItrack].alloced? // or set initial TRACKBUFFERSIZE + 2*track[MIDItrack].alloced : + TRACKBUFFERSIZE; + + if (!(mididata->track[MIDItrack].data = // attempt to reallocate + realloc(mididata->track[MIDItrack].data, + track[MIDItrack].alloced))) + return MEMALLOC; + } + mididata->track[MIDItrack].data[pos] = byte; + mididata->track[MIDItrack].len++; + return 0; +} + +// +// TWriteVarLen() +// +// write the ULONG value to tracknum-th track, in midi format, which is +// big endian, 7 bits per byte, with all bytes but the last flagged by +// bit 8 being set, allowing the length to vary. +// +// Passed the Allegro MIDI structure, the track number to write, +// and the ULONG value to encode in midi format there +// +// Returns 0 if sucessful, MEMALLOC if a memory allocation error occurs +// +static int TWriteVarLen(MIDI *mididata, int tracknum, register ULONG value) +{ + register ULONG buffer; + + buffer = value & 0x7f; + while ((value >>= 7)) // terminates because value unsigned + { + buffer <<= 8; // note first value shifted in has bit 8 clear + buffer |= 0x80; // all succeeding values do not + buffer += (value & 0x7f); + } + while (1) // write bytes out in opposite order + { + if (TWriteByte(mididata, tracknum, (UBYTE)(buffer&0xff))) // insure buffer masked + return MEMALLOC; + + if (buffer & 0x80) + buffer >>= 8; + else // terminate on the byte with bit 8 clear + break; + } + return 0; +} + +// +// ReadTime() +// +// Read a time value from the MUS buffer, advancing the position in it +// +// A time value is a variable length sequence of 8 bit bytes, with all +// but the last having bit 8 set. +// +// Passed a pointer to the pointer to the MUS buffer +// Returns the integer unsigned long time value there and advances the pointer +// +static ULONG ReadTime(const UBYTE **musptrp) +{ + register ULONG timeval = 0; + int byte; + + do // shift each byte read up in the result until a byte with bit 8 clear + { + byte = *(*musptrp)++; + timeval = (timeval << 7) + (byte & 0x7F); + } + while(byte & 0x80); + + return timeval; +} + +// +// FirstChannelAvailable() +// +// Return the next unassigned MIDI channel number +// +// The assignment for MUS channel 15 is not counted in the caculation, that +// being percussion and always assigned to MIDI channel 9 (base 0). +// +// Passed the array of MIDI channels assigned to MUS channels +// Returns the maximum channel number unassigned unless that is 9 in which +// case 10 is returned. +// +// killough 10/7/98: changed char parameter, return values to int + +static int FirstChannelAvailable(int MUS2MIDchannel[]) +{ + int i ; + int max = -1 ; + + // find the largest MIDI channel assigned so far + for (i = 0; i < 15; i++) + if (MUS2MIDchannel[i] > max) + max = MUS2MIDchannel[i]; + + return (max == 8 ? 10 : max+1); // skip MIDI channel 9 (percussion) +} + +// +// MidiEvent() +// +// Constructs a MIDI event code, and writes it to the current MIDI track +// unless its the same as the last event code and compressio is enabled +// in which case nothing is written. +// +// Passed the Allegro MIDI structure, the midi event code, the current +// MIDI channel number, the current MIDI track number, and whether compression +// (running status) is enabled. +// +// Returns the new event code if successful, 0 if a memory allocation error +// +static UBYTE MidiEvent(MIDI *mididata,UBYTE midicode,UBYTE MIDIchannel, + UBYTE MIDItrack,int nocomp) +{ + UBYTE newevent; + + newevent = midicode | MIDIchannel; + if ((newevent != track[MIDItrack].lastEvt) || nocomp) + { + if (TWriteByte(mididata,MIDItrack, newevent)) + return 0; // indicates MEMALLOC error + track[MIDItrack].lastEvt = newevent; + } + return newevent; +} + +// +// mmus2mid() +// +// Convert a memory buffer contain MUS data to an Allegro MIDI structure +// with specified time division and compression. +// +// Passed a pointer to the buffer containing MUS data, a pointer to the +// Allegro MIDI structure, the divisions, and a flag whether to compress. +// +// Returns 0 if successful, otherwise an error code (see mmus2mid.h). +// +int mmus2mid(const UBYTE *mus, MIDI *mididata, UWORD division, int nocomp) +{ + UWORD TrackCnt = 0; + UBYTE evt, MUSchannel, MIDIchannel, MIDItrack=0, NewEvent; + int i, event, data; + const UBYTE *musptr; + size_t muslen; + static MUSheader MUSh; + UBYTE MIDIchan2track[MIDI_TRACKS]; // killough 10/7/98: fix too small array + int MUS2MIDchannel[MIDI_TRACKS]; // killough 10/7/98: fix too small array + + // copy the MUS header from the MUS buffer to the MUSh header structure + + memcpy(&MUSh,mus,sizeof(MUSheader)); + MUSh.ScoreLength = doom_wtohs(MUSh.ScoreLength); + MUSh.ScoreStart = doom_wtohs(MUSh.ScoreStart); + MUSh.channels = doom_wtohs(MUSh.channels); + MUSh.SecChannels = doom_wtohs(MUSh.SecChannels); + MUSh.InstrCnt = doom_wtohs(MUSh.InstrCnt); + + // check some things and set length of MUS buffer from internal data + + if (!(muslen = MUSh.ScoreLength + MUSh.ScoreStart)) + return MUSDATAMT; // MUS file empty + + if (MUSh.channels > 15) // MUSchannels + drum channel > 16 + return TOOMCHAN ; + + musptr = mus+MUSh.ScoreStart; // init musptr to start of score + + for (i = 0; i < MIDI_TRACKS; i++) // init the track structure's tracks + { + MUS2MIDchannel[i] = -1; // flag for channel not used yet + track[i].velocity = 64; + track[i].deltaT = 0; + track[i].lastEvt = 0; + //free(mididata->track[i].data);//jff 3/5/98 remove old allocations + mididata->track[i].data=NULL; + track[i].alloced = 0; + mididata->track[i].len = 0; + } + + if (!division) + division = 70; + + // allocate the first track which is a special tempo/key track + // note multiple tracks means midi format 1 + + // set the divisions (ticks per quarter note) + mididata->divisions = division; + + // allocate for midi tempo/key track, allow for end of track + if (!(mididata->track[0].data = + realloc(mididata->track[0].data,sizeof(midikey)+sizeof(miditempo)+4))) + return MEMALLOC; + + // key C major + memcpy(mididata->track[0].data,midikey,sizeof(midikey)); + // tempo uS/qnote + memcpy(mididata->track[0].data+sizeof(midikey),miditempo,sizeof(miditempo)); + mididata->track[0].len = sizeof(midikey)+sizeof(miditempo); + + TrackCnt++; // music tracks start at 1 + + // process the MUS events in the MUS buffer + + do + { + // get a mus event, decode its type and channel fields + + event = *musptr++; + if ((evt = event_type(event)) == SCORE_END) //jff 1/23/98 use symbol + break; // if end of score event, leave + MUSchannel = channel(event); + + // if this channel not initialized, do so + + if (MUS2MIDchannel[MUSchannel] == -1) + { + // set MIDIchannel and MIDItrack + + MIDIchannel = MUS2MIDchannel[MUSchannel] = + (MUSchannel == 15 ? 9 : FirstChannelAvailable(MUS2MIDchannel)); + MIDItrack = MIDIchan2track[MIDIchannel] = (UBYTE)TrackCnt++; + } + else // channel already allocated as a track, use those values + { + MIDIchannel = MUS2MIDchannel[MUSchannel]; + MIDItrack = MIDIchan2track[MIDIchannel]; + } + + if (TWriteVarLen(mididata, MIDItrack, track[MIDItrack].deltaT)) + return MEMALLOC; + track[MIDItrack].deltaT = 0; + + switch(evt) + { + case RELEASE_NOTE: + // killough 10/7/98: Fix noise problems by not allowing compression + if (!(NewEvent=MidiEvent(mididata,0x90,MIDIchannel,MIDItrack,1))) + return MEMALLOC; + + data = *musptr++; + if (TWriteByte(mididata, MIDItrack, (UBYTE)(data & 0x7F))) + return MEMALLOC; + if (TWriteByte(mididata, MIDItrack, 0)) + return MEMALLOC; + break; + + case PLAY_NOTE: + if (!(NewEvent=MidiEvent(mididata,0x90,MIDIchannel,MIDItrack,nocomp))) + return MEMALLOC; + + data = *musptr++; + if (TWriteByte(mididata, MIDItrack, (UBYTE)(data & 0x7F))) + return MEMALLOC; + if( data & 0x80 ) + track[MIDItrack].velocity = (*musptr++) & 0x7f; + if (TWriteByte(mididata, MIDItrack, track[MIDItrack].velocity)) + return MEMALLOC; + break; + + case BEND_NOTE: + if (!(NewEvent=MidiEvent(mididata,0xE0,MIDIchannel,MIDItrack,nocomp))) + return MEMALLOC; + + data = *musptr++; + if (TWriteByte(mididata, MIDItrack, (UBYTE)((data & 1) << 6))) + return MEMALLOC; + if (TWriteByte(mididata, MIDItrack, (UBYTE)(data >> 1))) + return MEMALLOC; + break; + + case SYS_EVENT: + if (!(NewEvent=MidiEvent(mididata,0xB0,MIDIchannel,MIDItrack,nocomp))) + return MEMALLOC; + + data = *musptr++; + if (data<10 || data>14) + return BADSYSEVT; + + if (TWriteByte(mididata, MIDItrack, MUS2MIDcontrol[data])) + return MEMALLOC; + if (data == 12) + { + if (TWriteByte(mididata, MIDItrack, (UBYTE)(MUSh.channels+1))) + return MEMALLOC; + } + else + if (TWriteByte(mididata, MIDItrack, 0)) + return MEMALLOC; + break; + + case CNTL_CHANGE: + data = *musptr++; + if (data>9) + return BADCTLCHG; + + if (data) + { + if (!(NewEvent=MidiEvent(mididata,0xB0,MIDIchannel,MIDItrack,nocomp))) + return MEMALLOC; + + if (TWriteByte(mididata, MIDItrack, MUS2MIDcontrol[data])) + return MEMALLOC; + } + else + { + if (!(NewEvent=MidiEvent(mididata,0xC0,MIDIchannel,MIDItrack,nocomp))) + return MEMALLOC; + } + data = *musptr++; + if (TWriteByte(mididata, MIDItrack, (UBYTE)(data & 0x7F))) + return MEMALLOC; + break; + + case UNKNOWN_EVENT1: // mus events 5 and 7 + case UNKNOWN_EVENT2: // meaning not known + return BADMUSCTL; + + case SCORE_END: + break; + + default: + return BADMUSCTL; // exit with error + } + if (last(event)) + { + ULONG DeltaTime = ReadTime(&musptr); // killough 10/7/98: make local + for (i = 0;i < MIDI_TRACKS; i++) //jff 3/13/98 update all tracks + track[i].deltaT += DeltaTime; //whether allocated yet or not + } + + } + while ((evt != SCORE_END) && ((size_t)(musptr-mus) < muslen)); + + if (evt!=SCORE_END) + return MUSDATACOR; + + // Now add an end of track to each mididata track, correct allocation + + for (i = 0; i < MIDI_TRACKS; i++) + if (mididata->track[i].len) + { // killough 10/7/98: simplify code + if (TWriteByte(mididata, i, 0x00) || // midi end of track code + TWriteByte(mididata, i, 0xFF) || + TWriteByte(mididata, i, 0x2F) || + TWriteByte(mididata, i, 0x00)) + return MEMALLOC; + + // jff 1/23/98 fix failure to set data NULL, len 0 for unused tracks + // shorten allocation to proper length (important for Allegro) + if (!(mididata->track[i].data = + realloc(mididata->track[i].data,mididata->track[i].len))) + return MEMALLOC; + } + else + { + free(mididata->track[i].data); + mididata->track[i].data = NULL; + } + + return 0; +} + +void free_mididata(MIDI *mid) +{ + int i; + + for (i = 0; i < MIDI_TRACKS; i++) + if (mid->track[i].data) + free(mid->track[i].data); +} + +// +// ReadLength() +// +// Reads the length of a chunk in a midi buffer, advancing the pointer +// 4 bytes, bigendian +// +// Passed a pointer to the pointer to a MIDI buffer +// Returns the chunk length at the pointer position +// +static size_t ReadLength(UBYTE **mid) +{ + UBYTE *midptr = *mid; + + size_t length = (*midptr++)<<24; + length += (*midptr++)<<16; + length += (*midptr++)<<8; + length += *midptr++; + *mid = midptr; + return length; +} + +// +// MidiToMIDI() +// +// Convert an in-memory copy of a MIDI format 0 or 1 file to +// an Allegro MIDI structure, that is valid or has been zeroed +// +// Passed a pointer to a memory buffer with MIDI format music in it and a +// pointer to an Allegro MIDI structure. +// +// Returns 0 if successful, BADMIDHDR if the buffer is not MIDI format +// +int MidiToMIDI(UBYTE *mid,MIDI *mididata) +{ + int i; + int ntracks; + + // read the midi header + + if (memcmp(mid,midihdr,4)) + return BADMIDHDR; + + mididata->divisions = (mid[12]<<8)+mid[13]; + ntracks = (mid[10]<<8)+mid[11]; + + if (ntracks>=MIDI_TRACKS) + return BADMIDHDR; + + mid += 4; + { // killough 10/7/98: fix mid from being modified twice before sequence pt. + size_t t = ReadLength(&mid); // seek past header + mid += t; + } + + // now read each track + + for (i=0;itrack[i].len = ReadLength(&mid); // get length, move mid past it + + // read a track + mididata->track[i].data = realloc(mididata->track[i].data,mididata->track[i].len); + memcpy(mididata->track[i].data,mid,mididata->track[i].len); + mid += mididata->track[i].len; + } + for (;itrack[i].len) + { + free(mididata->track[i].data); + mididata->track[i].data = NULL; + mididata->track[i].len = 0; + } + return 0; +} + +//#ifdef STANDALONE /* this code unused by BOOM provided for future portability */ +// /* it also provides a MUS to MID file converter*/ +// proff: I moved this down, because I need MIDItoMidi + +static void FreeTracks(MIDI *mididata); +static void TWriteLength(UBYTE **midiptr,ULONG length); + +// +// FreeTracks() +// +// Free all track allocations in the MIDI structure +// +// Passed a pointer to an Allegro MIDI structure +// Returns nothing +// +static void FreeTracks(MIDI *mididata) +{ + int i; + + for (i=0; itrack[i].data); + mididata->track[i].data = NULL; + mididata->track[i].len = 0; + } +} + +// +// TWriteLength() +// +// Write the length of a MIDI chunk to a midi buffer. The length is four +// bytes and is written byte-reversed for bigendian. The pointer to the +// midi buffer is advanced. +// +// Passed a pointer to the pointer to a midi buffer, and the length to write +// Returns nothing +// +static void TWriteLength(UBYTE **midiptr,ULONG length) +{ +// proff: Added typecast to avoid warning + *(*midiptr)++ = (unsigned char)((length>>24)&0xff); + *(*midiptr)++ = (unsigned char)((length>>16)&0xff); + *(*midiptr)++ = (unsigned char)((length>>8)&0xff); + *(*midiptr)++ = (unsigned char)((length)&0xff); +} + +// +// MIDIToMidi() +// +// This routine converts an Allegro MIDI structure to a midi 1 format file +// in memory. It is used to support memory MUS -> MIDI conversion +// +// Passed a pointer to an Allegro MIDI structure, a pointer to a pointer to +// a buffer containing midi data, and a pointer to a length return. +// Returns 0 if successful, MEMALLOC if a memory allocation error occurs +// +int MIDIToMidi(MIDI *mididata,UBYTE **mid,int *midlen) +{ + size_t total; + int i,ntrks; + UBYTE *midiptr; + + // calculate how long the mid buffer must be, and allocate + + total = sizeof(midihdr); + for (i=0,ntrks=0;itrack[i].len) + { + total += 8 + mididata->track[i].len; // Track hdr + track length + ntrks++; + } + if ((*mid = malloc(total))==NULL) + return MEMALLOC; + + + // fill in number of tracks and bigendian divisions (ticks/qnote) + + midihdr[10] = 0; + midihdr[11] = (UBYTE)ntrks; // set number of tracks in header + midihdr[12] = (mididata->divisions>>8) & 0x7f; + midihdr[13] = (mididata->divisions) & 0xff; + + // write the midi header + + midiptr = *mid; + memcpy(midiptr,midihdr,sizeof(midihdr)); + midiptr += sizeof(midihdr); + + // write the tracks + + for (i=0;itrack[i].len) + { + memcpy(midiptr,trackhdr,sizeof(trackhdr)); // header + midiptr += sizeof(trackhdr); + TWriteLength(&midiptr,mididata->track[i].len); // track length + // data + memcpy(midiptr,mididata->track[i].data,mididata->track[i].len); + midiptr += mididata->track[i].len; + } + } + + // return length information + + *midlen = midiptr - *mid; + + return 0; +} + +#ifdef STANDALONE /* this code unused by BOOM provided for future portability */ + /* it also provides a MUS to MID file converter*/ +// proff: I moved this down, because I need MIDItoMidi + +// +// main() +// +// Main routine that will convert a globbed set of MUS files to the +// correspondingly named MID files using mmus2mid(). Only compiled +// if the STANDALONE symbol is defined. +// +// Passed the command line arguments, returns 0 if successful +// +int main(int argc,char **argv) +{ + FILE *musst,*midst; + char musfile[FILENAME_MAX],midfile[FILENAME_MAX]; + MUSheader MUSh; + UBYTE *mus,*mid; + static MIDI mididata; + int err,midlen; + char *p,*q; + int i; + + if (argc<2) + { + //jff 8/3/98 use logical output routine + lprintf(LO_INFO,"Usage: MMUS2MID musfile[.MUS]\n"); + lprintf(LO_INFO,"writes musfile.MID as output\n"); + lprintf(LO_INFO,"musfile may contain wildcards\n"); + exit(1); + } + + for (i=1;idirection) + { + case 0: + // If ceiling in stasis, do nothing + break; + + case 1: + // Ceiling is moving up + res = T_MovePlane + ( + ceiling->sector, + ceiling->speed, + ceiling->topheight, + false, + 1, + ceiling->direction + ); + + // if not a silent crusher, make moving sound + if (!(leveltime&7)) + { + switch(ceiling->type) + { + case silentCrushAndRaise: + case genSilentCrusher: + break; + default: + S_StartSound((mobj_t *)&ceiling->sector->soundorg,sfx_stnmov); + break; + } + } + + // handle reaching destination height + if (res == pastdest) + { + switch(ceiling->type) + { + // plain movers are just removed + case raiseToHighest: + case genCeiling: + P_RemoveActiveCeiling(ceiling); + break; + + // movers with texture change, change the texture then get removed + case genCeilingChgT: + case genCeilingChg0: + ceiling->sector->special = ceiling->newspecial; + //jff 3/14/98 transfer old special field as well + ceiling->sector->oldspecial = ceiling->oldspecial; + case genCeilingChg: + ceiling->sector->ceilingpic = ceiling->texture; + P_RemoveActiveCeiling(ceiling); + break; + + // crushers reverse direction at the top + case silentCrushAndRaise: + S_StartSound((mobj_t *)&ceiling->sector->soundorg,sfx_pstop); + case genSilentCrusher: + case genCrusher: + case fastCrushAndRaise: + case crushAndRaise: + ceiling->direction = -1; + break; + + default: + break; + } + } + break; + + case -1: + // Ceiling moving down + res = T_MovePlane + ( + ceiling->sector, + ceiling->speed, + ceiling->bottomheight, + ceiling->crush, + 1, + ceiling->direction + ); + + // if not silent crusher type make moving sound + if (!(leveltime&7)) + { + switch(ceiling->type) + { + case silentCrushAndRaise: + case genSilentCrusher: + break; + default: + S_StartSound((mobj_t *)&ceiling->sector->soundorg,sfx_stnmov); + } + } + + // handle reaching destination height + if (res == pastdest) + { + switch(ceiling->type) + { + // 02/09/98 jff change slow crushers' speed back to normal + // start back up + case genSilentCrusher: + case genCrusher: + if (ceiling->oldspeedspeed = ceiling->oldspeed; + ceiling->direction = 1; //jff 2/22/98 make it go back up! + break; + + // make platform stop at bottom of all crusher strokes + // except generalized ones, reset speed, start back up + case silentCrushAndRaise: + S_StartSound((mobj_t *)&ceiling->sector->soundorg,sfx_pstop); + case crushAndRaise: + ceiling->speed = CEILSPEED; + case fastCrushAndRaise: + ceiling->direction = 1; + break; + + // in the case of ceiling mover/changer, change the texture + // then remove the active ceiling + case genCeilingChgT: + case genCeilingChg0: + ceiling->sector->special = ceiling->newspecial; + //jff add to fix bug in special transfers from changes + ceiling->sector->oldspecial = ceiling->oldspecial; + case genCeilingChg: + ceiling->sector->ceilingpic = ceiling->texture; + P_RemoveActiveCeiling(ceiling); + break; + + // all other case, just remove the active ceiling + case lowerAndCrush: + case lowerToFloor: + case lowerToLowest: + case lowerToMaxFloor: + case genCeiling: + P_RemoveActiveCeiling(ceiling); + break; + + default: + break; + } + } + else // ( res != pastdest ) + { + // handle the crusher encountering an obstacle + if (res == crushed) + { + switch(ceiling->type) + { + //jff 02/08/98 slow down slow crushers on obstacle + case genCrusher: + case genSilentCrusher: + if (ceiling->oldspeed < CEILSPEED*3) + ceiling->speed = CEILSPEED / 8; + break; + case silentCrushAndRaise: + case crushAndRaise: + case lowerAndCrush: + ceiling->speed = CEILSPEED / 8; + break; + + default: + break; + } + } + } + break; + } +} + + +// +// EV_DoCeiling +// +// Move a ceiling up/down or start a crusher +// +// Passed the linedef activating the function and the type of function desired +// returns true if a thinker started +// +int EV_DoCeiling +( line_t* line, + ceiling_e type ) +{ + int secnum; + int rtn; + sector_t* sec; + ceiling_t* ceiling; + + secnum = -1; + rtn = 0; + + // Reactivate in-stasis ceilings...for certain types. + // This restarts a crusher after it has been stopped + switch(type) + { + case fastCrushAndRaise: + case silentCrushAndRaise: + case crushAndRaise: + //jff 4/5/98 return if activated + rtn = P_ActivateInStasisCeiling(line); + default: + break; + } + + // affects all sectors with the same tag as the linedef + while ((secnum = P_FindSectorFromLineTag(line,secnum)) >= 0) + { + sec = §ors[secnum]; + + // if ceiling already moving, don't start a second function on it + if (P_SectorActive(ceiling_special,sec)) //jff 2/22/98 + continue; + + // create a new ceiling thinker + rtn = 1; + ceiling = Z_Malloc (sizeof(*ceiling), PU_LEVSPEC, 0); + memset(ceiling, 0, sizeof(*ceiling)); + P_AddThinker (&ceiling->thinker); + sec->ceilingdata = ceiling; //jff 2/22/98 + ceiling->thinker.function = T_MoveCeiling; + ceiling->sector = sec; + ceiling->crush = false; + + // setup ceiling structure according to type of function + switch(type) + { + case fastCrushAndRaise: + ceiling->crush = true; + ceiling->topheight = sec->ceilingheight; + ceiling->bottomheight = sec->floorheight + (8*FRACUNIT); + ceiling->direction = -1; + ceiling->speed = CEILSPEED * 2; + break; + + case silentCrushAndRaise: + case crushAndRaise: + ceiling->crush = true; + ceiling->topheight = sec->ceilingheight; + case lowerAndCrush: + case lowerToFloor: + ceiling->bottomheight = sec->floorheight; + if (type != lowerToFloor) + ceiling->bottomheight += 8*FRACUNIT; + ceiling->direction = -1; + ceiling->speed = CEILSPEED; + break; + + case raiseToHighest: + ceiling->topheight = P_FindHighestCeilingSurrounding(sec); + ceiling->direction = 1; + ceiling->speed = CEILSPEED; + break; + + case lowerToLowest: + ceiling->bottomheight = P_FindLowestCeilingSurrounding(sec); + ceiling->direction = -1; + ceiling->speed = CEILSPEED; + break; + + case lowerToMaxFloor: + ceiling->bottomheight = P_FindHighestFloorSurrounding(sec); + ceiling->direction = -1; + ceiling->speed = CEILSPEED; + break; + + default: + break; + } + + // add the ceiling to the active list + ceiling->tag = sec->tag; + ceiling->type = type; + P_AddActiveCeiling(ceiling); + } + return rtn; +} + +////////////////////////////////////////////////////////////////////// +// +// Active ceiling list primitives +// +///////////////////////////////////////////////////////////////////// + +// jff 2/22/98 - modified Lee's plat code to work for ceilings +// +// The following were all rewritten by Lee Killough +// to use the new structure which places no limits +// on active ceilings. It also avoids spending as much +// time searching for active ceilings. Previously a +// fixed-size array was used, with NULL indicating +// empty entries, while now a doubly-linked list +// is used. + +// +// P_ActivateInStasisCeiling() +// +// Reactivates all stopped crushers with the right tag +// +// Passed the line reactivating the crusher +// Returns true if a ceiling reactivated +// +//jff 4/5/98 return if activated +int P_ActivateInStasisCeiling(line_t *line) +{ + ceilinglist_t *cl; + int rtn=0; + + for (cl=activeceilings; cl; cl=cl->next) + { + ceiling_t *ceiling = cl->ceiling; + if (ceiling->tag == line->tag && ceiling->direction == 0) + { + ceiling->direction = ceiling->olddirection; + ceiling->thinker.function = T_MoveCeiling; + //jff 4/5/98 return if activated + rtn=1; + } + } + return rtn; +} + +// +// EV_CeilingCrushStop() +// +// Stops all active ceilings with the right tag +// +// Passed the linedef stopping the ceilings +// Returns true if a ceiling put in stasis +// +int EV_CeilingCrushStop(line_t* line) +{ + int rtn=0; + + ceilinglist_t *cl; + for (cl=activeceilings; cl; cl=cl->next) + { + ceiling_t *ceiling = cl->ceiling; + if (ceiling->direction != 0 && ceiling->tag == line->tag) + { + ceiling->olddirection = ceiling->direction; + ceiling->direction = 0; + ceiling->thinker.function = NULL; + rtn=1; + } + } + return rtn; +} + +// +// P_AddActiveCeiling() +// +// Adds a ceiling to the head of the list of active ceilings +// +// Passed the ceiling motion structure +// Returns nothing +// +void P_AddActiveCeiling(ceiling_t* ceiling) +{ + ceilinglist_t *list = malloc(sizeof *list); + list->ceiling = ceiling; + ceiling->list = list; + if ((list->next = activeceilings)) + list->next->prev = &list->next; + list->prev = &activeceilings; + activeceilings = list; +} + +// +// P_RemoveActiveCeiling() +// +// Removes a ceiling from the list of active ceilings +// +// Passed the ceiling motion structure +// Returns nothing +// +void P_RemoveActiveCeiling(ceiling_t* ceiling) +{ + ceilinglist_t *list = ceiling->list; + ceiling->sector->ceilingdata = NULL; //jff 2/22/98 + P_RemoveThinker(&ceiling->thinker); + if ((*list->prev = list->next)) + list->next->prev = list->prev; + free(list); +} + +// +// P_RemoveAllActiveCeilings() +// +// Removes all ceilings from the active ceiling list +// +// Passed nothing, returns nothing +// +void P_RemoveAllActiveCeilings(void) +{ + while (activeceilings) + { + ceilinglist_t *next = activeceilings->next; + free(activeceilings); + activeceilings = next; + } +} diff --git a/src/p_checksum.c b/src/p_checksum.c new file mode 100644 index 0000000..5fb6101 --- /dev/null +++ b/src/p_checksum.c @@ -0,0 +1,100 @@ +#include +#include +#include +#include /* exit(), atexit() */ + +#include "p_checksum.h" +#include "md5.h" +#include "doomstat.h" /* players{,ingame} */ +#include "lprintf.h" + +/* forward decls */ +static void p_checksum_cleanup(void); +void checksum_gamestate(int tic); + +/* vars */ +static void p_checksum_nop(int tic){} /* do nothing */ +void (*P_Checksum)(int) = p_checksum_nop; + +/* + * P_RecordChecksum + * sets up the file and function pointers to write out checksum data + */ +static FILE *outfile = NULL; +static struct MD5Context md5global; + +void P_RecordChecksum(const char *file) { + size_t fnsize; + + fnsize = strlen(file); + + /* special case: write to stdout */ + if(0 == strncmp("-",file,MIN(1,fnsize))) + outfile = stdout; + else { + outfile = fopen(file,"wb"); + if(NULL == outfile) { + I_Error("cannot open %s for writing checksum:\n%s\n", + file, strerror(errno)); + } + atexit(p_checksum_cleanup); + } + + MD5Init(&md5global); + + P_Checksum = checksum_gamestate; +} + +void P_ChecksumFinal(void) { + int i; + unsigned char digest[16]; + + if (!outfile) + return; + + MD5Final(digest, &md5global); + fprintf(outfile, "final: "); + for (i=0; i<16; i++) + fprintf(outfile,"%x", digest[i]); + fprintf(outfile, "\n"); + MD5Init(&md5global); +} + +static void p_checksum_cleanup(void) { + if (outfile && (outfile != stdout)) + fclose(outfile); +} + +/* + * runs on each tic when recording checksums + */ +void checksum_gamestate(int tic) { + int i; + struct MD5Context md5ctx; + unsigned char digest[16]; + char buffer[2048]; + + fprintf(outfile,"%6d, ", tic); + + /* based on "ArchivePlayers" */ + MD5Init(&md5ctx); + for (i=0 ; idirection) + { + case 0: + // Door is waiting + if (!--door->topcountdown) // downcount and check + { + switch(door->type) + { + case blazeRaise: + case genBlazeRaise: + door->direction = -1; // time to go back down + S_StartSound((mobj_t *)&door->sector->soundorg,sfx_bdcls); + break; + + case normal: + case genRaise: + door->direction = -1; // time to go back down + S_StartSound((mobj_t *)&door->sector->soundorg,sfx_dorcls); + break; + + case close30ThenOpen: + case genCdO: + door->direction = 1; // time to go back up + S_StartSound((mobj_t *)&door->sector->soundorg,sfx_doropn); + break; + + case genBlazeCdO: + door->direction = 1; // time to go back up + S_StartSound((mobj_t *)&door->sector->soundorg,sfx_bdopn); + break; + + default: + break; + } + } + break; + + case 2: + // Special case for sector type door that opens in 5 mins + if (!--door->topcountdown) // 5 minutes up? + { + switch(door->type) + { + case raiseIn5Mins: + door->direction = 1; // time to raise then + door->type = normal; // door acts just like normal 1 DR door now + S_StartSound((mobj_t *)&door->sector->soundorg,sfx_doropn); + break; + + default: + break; + } + } + break; + + case -1: + // Door is moving down + res = T_MovePlane + ( + door->sector, + door->speed, + door->sector->floorheight, + false, + 1, + door->direction + ); + + /* killough 10/98: implement gradual lighting effects */ + // e6y: "Tagged doors don't trigger special lighting" handled wrong + // http://sourceforge.net/tracker/index.php?func=detail&aid=1411400&group_id=148658&atid=772943 + // Old code: if (door->lighttag && door->topheight - door->sector->floorheight) + if (door->lighttag && door->topheight - door->sector->floorheight && compatibility_level >= mbf_compatibility) + EV_LightTurnOnPartway(door->line, + FixedDiv(door->sector->ceilingheight - + door->sector->floorheight, + door->topheight - + door->sector->floorheight)); + + // handle door reaching bottom + if (res == pastdest) + { + switch(door->type) + { + // regular open and close doors are all done, remove them + case blazeRaise: + case blazeClose: + case genBlazeRaise: + case genBlazeClose: + door->sector->ceilingdata = NULL; //jff 2/22/98 + P_RemoveThinker (&door->thinker); // unlink and free + // killough 4/15/98: remove double-closing sound of blazing doors + if (comp[comp_blazing]) + S_StartSound((mobj_t *)&door->sector->soundorg,sfx_bdcls); + break; + + case normal: + case close: + case genRaise: + case genClose: + door->sector->ceilingdata = NULL; //jff 2/22/98 + P_RemoveThinker (&door->thinker); // unlink and free + break; + + // close then open doors start waiting + case close30ThenOpen: + door->direction = 0; + door->topcountdown = TICRATE*30; + break; + + case genCdO: + case genBlazeCdO: + door->direction = 0; + door->topcountdown = door->topwait; // jff 5/8/98 insert delay + break; + + default: + break; + } + // e6y: "Tagged doors don't trigger special lighting" handled wrong + // http://sourceforge.net/tracker/index.php?func=detail&aid=1411400&group_id=148658&atid=772943 + if (door->lighttag && door->topheight - door->sector->floorheight && compatibility_level < mbf_compatibility) + EV_LightTurnOnPartway(door->line,0); + } + /* jff 1/31/98 turn lighting off in tagged sectors of manual doors + * killough 10/98: replaced with gradual lighting code + */ + else if (res == crushed) // handle door meeting obstruction on way down + { + switch(door->type) + { + case genClose: + case genBlazeClose: + case blazeClose: + case close: // Close types do not bounce, merely wait + break; + + case blazeRaise: + case genBlazeRaise: + door->direction = 1; + if (!comp[comp_blazing]) { + S_StartSound((mobj_t *)&door->sector->soundorg,sfx_bdopn); + break; + } + + default: // other types bounce off the obstruction + door->direction = 1; + S_StartSound((mobj_t *)&door->sector->soundorg,sfx_doropn); + break; + } + } + break; + + case 1: + // Door is moving up + res = T_MovePlane + ( + door->sector, + door->speed, + door->topheight, + false, + 1, + door->direction + ); + + /* killough 10/98: implement gradual lighting effects */ + // e6y: "Tagged doors don't trigger special lighting" handled wrong + // http://sourceforge.net/tracker/index.php?func=detail&aid=1411400&group_id=148658&atid=772943 + // Old code: if (door->lighttag && door->topheight - door->sector->floorheight) + if (door->lighttag && door->topheight - door->sector->floorheight && compatibility_level >= mbf_compatibility) + EV_LightTurnOnPartway(door->line, + FixedDiv(door->sector->ceilingheight - + door->sector->floorheight, + door->topheight - + door->sector->floorheight)); + + // handle door reaching the top + if (res == pastdest) + { + switch(door->type) + { + case blazeRaise: // regular open/close doors start waiting + case normal: + case genRaise: + case genBlazeRaise: + door->direction = 0; // wait at top with delay + door->topcountdown = door->topwait; + break; + + case close30ThenOpen: // close and close/open doors are done + case blazeOpen: + case open: + case genBlazeOpen: + case genOpen: + case genCdO: + case genBlazeCdO: + door->sector->ceilingdata = NULL; //jff 2/22/98 + P_RemoveThinker (&door->thinker); // unlink and free + break; + + default: + break; + } + + /* jff 1/31/98 turn lighting on in tagged sectors of manual doors + * killough 10/98: replaced with gradual lighting code */ + // e6y: "Tagged doors don't trigger special lighting" handled wrong + // http://sourceforge.net/tracker/index.php?func=detail&aid=1411400&group_id=148658&atid=772943 + if (door->lighttag && door->topheight - door->sector->floorheight && compatibility_level < mbf_compatibility) + EV_LightTurnOnPartway(door->line,FRACUNIT); + } + break; + } +} + +/////////////////////////////////////////////////////////////// +// +// Door linedef handlers +// +/////////////////////////////////////////////////////////////// + +// +// EV_DoLockedDoor +// +// Handle opening a tagged locked door +// +// Passed the line activating the door, the type of door, +// and the thing that activated the line +// Returns true if a thinker created +// +int EV_DoLockedDoor +( line_t* line, + vldoor_e type, + mobj_t* thing ) +{ + player_t* p; + + // only players can open locked doors + p = thing->player; + if (!p) + return 0; + + // check type of linedef, and if key is possessed to open it + switch(line->special) + { + case 99: // Blue Lock + case 133: + if (!p->cards[it_bluecard] && !p->cards[it_blueskull]) + { + p->message = s_PD_BLUEO; // Ty 03/27/98 - externalized + S_StartSound(p->mo,sfx_oof); // killough 3/20/98 + return 0; + } + break; + + case 134: // Red Lock + case 135: + if (!p->cards[it_redcard] && !p->cards[it_redskull]) + { + p->message = s_PD_REDO; // Ty 03/27/98 - externalized + S_StartSound(p->mo,sfx_oof); // killough 3/20/98 + return 0; + } + break; + + case 136: // Yellow Lock + case 137: + if (!p->cards[it_yellowcard] && !p->cards[it_yellowskull]) + { + p->message = s_PD_YELLOWO; // Ty 03/27/98 - externalized + S_StartSound(p->mo,sfx_oof); // killough 3/20/98 + return 0; + } + break; + } + + // got the key, so open the door + return EV_DoDoor(line,type); +} + + +// +// EV_DoDoor +// +// Handle opening a tagged door +// +// Passed the line activating the door and the type of door +// Returns true if a thinker created +// +int EV_DoDoor +( line_t* line, + vldoor_e type ) +{ + int secnum,rtn; + sector_t* sec; + vldoor_t* door; + + secnum = -1; + rtn = 0; + + // open all doors with the same tag as the activating line + while ((secnum = P_FindSectorFromLineTag(line,secnum)) >= 0) + { + sec = §ors[secnum]; + // if the ceiling already moving, don't start the door action + if (P_SectorActive(ceiling_special,sec)) //jff 2/22/98 + continue; + + // new door thinker + rtn = 1; + door = Z_Malloc (sizeof(*door), PU_LEVSPEC, 0); + memset(door, 0, sizeof(*door)); + P_AddThinker (&door->thinker); + sec->ceilingdata = door; //jff 2/22/98 + + door->thinker.function = T_VerticalDoor; + door->sector = sec; + door->type = type; + door->topwait = VDOORWAIT; + door->speed = VDOORSPEED; + door->line = line; // jff 1/31/98 remember line that triggered us + door->lighttag = 0; /* killough 10/98: no light effects with tagged doors */ + + // setup door parameters according to type of door + switch(type) + { + case blazeClose: + door->topheight = P_FindLowestCeilingSurrounding(sec); + door->topheight -= 4*FRACUNIT; + door->direction = -1; + door->speed = VDOORSPEED * 4; + S_StartSound((mobj_t *)&door->sector->soundorg,sfx_bdcls); + break; + + case close: + door->topheight = P_FindLowestCeilingSurrounding(sec); + door->topheight -= 4*FRACUNIT; + door->direction = -1; + S_StartSound((mobj_t *)&door->sector->soundorg,sfx_dorcls); + break; + + case close30ThenOpen: + door->topheight = sec->ceilingheight; + door->direction = -1; + S_StartSound((mobj_t *)&door->sector->soundorg,sfx_dorcls); + break; + + case blazeRaise: + case blazeOpen: + door->direction = 1; + door->topheight = P_FindLowestCeilingSurrounding(sec); + door->topheight -= 4*FRACUNIT; + door->speed = VDOORSPEED * 4; + if (door->topheight != sec->ceilingheight) + S_StartSound((mobj_t *)&door->sector->soundorg,sfx_bdopn); + break; + + case normal: + case open: + door->direction = 1; + door->topheight = P_FindLowestCeilingSurrounding(sec); + door->topheight -= 4*FRACUNIT; + if (door->topheight != sec->ceilingheight) + S_StartSound((mobj_t *)&door->sector->soundorg,sfx_doropn); + break; + + default: + break; + } + } + return rtn; +} + + +// +// EV_VerticalDoor +// +// Handle opening a door manually, no tag value +// +// Passed the line activating the door and the thing activating it +// Returns true if a thinker created +// +// jff 2/12/98 added int return value, fixed all returns +// +int EV_VerticalDoor +( line_t* line, + mobj_t* thing ) +{ + player_t* player; + int secnum; + sector_t* sec; + vldoor_t* door; + + // Check for locks + player = thing->player; + + switch(line->special) + { + case 26: // Blue Lock + case 32: + if ( !player ) + return 0; + if (!player->cards[it_bluecard] && !player->cards[it_blueskull]) + { + player->message = s_PD_BLUEK; // Ty 03/27/98 - externalized + S_StartSound(player->mo,sfx_oof); // killough 3/20/98 + return 0; + } + break; + + case 27: // Yellow Lock + case 34: + if ( !player ) + return 0; + if (!player->cards[it_yellowcard] && !player->cards[it_yellowskull]) + { + player->message = s_PD_YELLOWK; // Ty 03/27/98 - externalized + S_StartSound(player->mo,sfx_oof); // killough 3/20/98 + return 0; + } + break; + + case 28: // Red Lock + case 33: + if ( !player ) + return 0; + if (!player->cards[it_redcard] && !player->cards[it_redskull]) + { + player->message = s_PD_REDK; // Ty 03/27/98 - externalized + S_StartSound(player->mo,sfx_oof); // killough 3/20/98 + return 0; + } + break; + + default: + break; + } + + // if the wrong side of door is pushed, give oof sound + if (line->sidenum[1]==NO_INDEX) // killough + { + S_StartSound(player->mo,sfx_oof); // killough 3/20/98 + return 0; + } + + // get the sector on the second side of activating linedef + sec = sides[line->sidenum[1]].sector; + secnum = sec-sectors; + + /* if door already has a thinker, use it + * cph 2001/04/05 - + * Ok, this is a disaster area. We're assuming that sec->ceilingdata + * is a vldoor_t! What if this door is controlled by both DR lines + * and by switches? I don't know how to fix that. + * Secondly, original Doom didn't distinguish floor/lighting/ceiling + * actions, so we need to do the same in demo compatibility mode. + */ + door = sec->ceilingdata; + if (demo_compatibility) { + if (!door) door = sec->floordata; + if (!door) door = sec->lightingdata; + } + /* If this is a repeatable line, and the door is already moving, then we can just reverse the current action. Note that in prboom 2.3.0 I erroneously removed the if-this-is-repeatable check, hence the prboom_4_compatibility clause below (foolishly assumed that already moving implies repeatable - but it could be moving due to another switch, e.g. lv19-509) */ + if (door && + ((compatibility_level == prboom_4_compatibility) || + (line->special == 1) || (line->special == 117) || (line->special == 26) || (line->special == 27) || (line->special == 28) + ) + ) { + /* For old demos we have to emulate the old buggy behavior and + * mess up non-T_VerticalDoor actions. + */ + if (compatibility_level < prboom_4_compatibility || + door->thinker.function == T_VerticalDoor) { + /* cph - we are writing outval to door->direction iff it is non-zero */ + signed int outval = 0; + + /* An already moving repeatable door which is being re-pressed, or a + * monster is trying to open a closing door - so change direction + * DEMOSYNC: we only read door->direction now if it really is a door. + */ + if (door->thinker.function == T_VerticalDoor && door->direction == -1) { + outval = 1; /* go back up */ + } else if (player) { + outval = -1; /* go back down */ + } + + /* Write this to the thinker. In demo compatibility mode, we might be + * overwriting a field of a non-vldoor_t thinker - we need to add any + * other thinker types here if any demos depend on specific fields + * being corrupted by this. + */ + if (outval) { + if (door->thinker.function == T_VerticalDoor) { + door->direction = outval; + } else if (door->thinker.function == T_PlatRaise) { + plat_t* p = (plat_t*)door; + p->wait = outval; + } else { + lprintf(LO_DEBUG, "EV_VerticalDoor: unknown thinker.function in thinker corruption emulation"); + } + + return 1; + } + } + /* Either we're in prboom >=v2.3 and it's not a door, or it's a door but + * we're a monster and don't want to shut it; exit with no action. + */ + return 0; + } + + // emit proper sound + switch(line->special) + { + case 117: // blazing door raise + case 118: // blazing door open + S_StartSound((mobj_t *)&sec->soundorg,sfx_bdopn); + break; + + default: // normal or locked door sound + S_StartSound((mobj_t *)&sec->soundorg,sfx_doropn); + break; + } + + // new door thinker + door = Z_Malloc (sizeof(*door), PU_LEVSPEC, 0); + memset(door, 0, sizeof(*door)); + P_AddThinker (&door->thinker); + sec->ceilingdata = door; //jff 2/22/98 + door->thinker.function = T_VerticalDoor; + door->sector = sec; + door->direction = 1; + door->speed = VDOORSPEED; + door->topwait = VDOORWAIT; + door->line = line; // jff 1/31/98 remember line that triggered us + + /* killough 10/98: use gradual lighting changes if nonzero tag given */ + door->lighttag = comp[comp_doorlight] ? 0 : line->tag; + + // set the type of door from the activating linedef type + switch(line->special) + { + case 1: + case 26: + case 27: + case 28: + door->type = normal; + break; + + case 31: + case 32: + case 33: + case 34: + door->type = open; + line->special = 0; + break; + + case 117: // blazing door raise + door->type = blazeRaise; + door->speed = VDOORSPEED*4; + break; + case 118: // blazing door open + door->type = blazeOpen; + line->special = 0; + door->speed = VDOORSPEED*4; + break; + + default: + door->lighttag = 0; // killough 10/98 + break; + } + + // find the top and bottom of the movement range + door->topheight = P_FindLowestCeilingSurrounding(sec); + door->topheight -= 4*FRACUNIT; + return 1; +} + + +/////////////////////////////////////////////////////////////// +// +// Sector type door spawners +// +/////////////////////////////////////////////////////////////// + +// +// P_SpawnDoorCloseIn30() +// +// Spawn a door that closes after 30 seconds (called at level init) +// +// Passed the sector of the door, whose type specified the door action +// Returns nothing +// +void P_SpawnDoorCloseIn30 (sector_t* sec) +{ + vldoor_t* door; + + door = Z_Malloc ( sizeof(*door), PU_LEVSPEC, 0); + + memset(door, 0, sizeof(*door)); + P_AddThinker (&door->thinker); + + sec->ceilingdata = door; //jff 2/22/98 + sec->special = 0; + + door->thinker.function = T_VerticalDoor; + door->sector = sec; + door->direction = 0; + door->type = normal; + door->speed = VDOORSPEED; + door->topcountdown = 30 * 35; + door->line = NULL; // jff 1/31/98 remember line that triggered us + door->lighttag = 0; /* killough 10/98: no lighting changes */ +} + +// +// P_SpawnDoorRaiseIn5Mins() +// +// Spawn a door that opens after 5 minutes (called at level init) +// +// Passed the sector of the door, whose type specified the door action +// Returns nothing +// +void P_SpawnDoorRaiseIn5Mins +( sector_t* sec, + int secnum ) +{ + vldoor_t* door; + + door = Z_Malloc ( sizeof(*door), PU_LEVSPEC, 0); + + memset(door, 0, sizeof(*door)); + P_AddThinker (&door->thinker); + + sec->ceilingdata = door; //jff 2/22/98 + sec->special = 0; + + door->thinker.function = T_VerticalDoor; + door->sector = sec; + door->direction = 2; + door->type = raiseIn5Mins; + door->speed = VDOORSPEED; + door->topheight = P_FindLowestCeilingSurrounding(sec); + door->topheight -= 4*FRACUNIT; + door->topwait = VDOORWAIT; + door->topcountdown = 5 * 60 * 35; + door->line = NULL; // jff 1/31/98 remember line that triggered us + door->lighttag = 0; /* killough 10/98: no lighting changes */ +} diff --git a/src/p_enemy.c b/src/p_enemy.c new file mode 100644 index 0000000..937321e --- /dev/null +++ b/src/p_enemy.c @@ -0,0 +1,2601 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000,2002 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Enemy thinking, AI. + * Action Pointer Functions + * that are associated with states/frames. + * + *-----------------------------------------------------------------------------*/ + +#include "doomstat.h" +#include "m_random.h" +#include "r_main.h" +#include "p_maputl.h" +#include "p_map.h" +#include "p_setup.h" +#include "p_spec.h" +#include "s_sound.h" +#include "sounds.h" +#include "p_inter.h" +#include "g_game.h" +#include "p_enemy.h" +#include "p_tick.h" +#include "m_bbox.h" +#include "lprintf.h" + +static mobj_t *current_actor; + +typedef enum { + DI_EAST, + DI_NORTHEAST, + DI_NORTH, + DI_NORTHWEST, + DI_WEST, + DI_SOUTHWEST, + DI_SOUTH, + DI_SOUTHEAST, + DI_NODIR, + NUMDIRS +} dirtype_t; + +static void P_NewChaseDir(mobj_t *actor); +void P_ZBumpCheck(mobj_t *); // phares + +// +// ENEMY THINKING +// Enemies are allways spawned +// with targetplayer = -1, threshold = 0 +// Most monsters are spawned unaware of all players, +// but some can be made preaware +// + +// +// Called by P_NoiseAlert. +// Recursively traverse adjacent sectors, +// sound blocking lines cut off traversal. +// +// killough 5/5/98: reformatted, cleaned up + +static void P_RecursiveSound(sector_t *sec, int soundblocks, + mobj_t *soundtarget) +{ + int i; + + // wake up all monsters in this sector + if (sec->validcount == validcount && sec->soundtraversed <= soundblocks+1) + return; // already flooded + + sec->validcount = validcount; + sec->soundtraversed = soundblocks+1; + P_SetTarget(&sec->soundtarget, soundtarget); + + for (i=0; ilinecount; i++) + { + sector_t *other; + line_t *check = sec->lines[i]; + + if (!(check->flags & ML_TWOSIDED)) + continue; + + P_LineOpening(check); + + if (openrange <= 0) + continue; // closed door + + other=sides[check->sidenum[sides[check->sidenum[0]].sector==sec]].sector; + + if (!(check->flags & ML_SOUNDBLOCK)) + P_RecursiveSound(other, soundblocks, soundtarget); + else + if (!soundblocks) + P_RecursiveSound(other, 1, soundtarget); + } +} + +// +// P_NoiseAlert +// If a monster yells at a player, +// it will alert other monsters to the player. +// +void P_NoiseAlert(mobj_t *target, mobj_t *emitter) +{ + validcount++; + P_RecursiveSound(emitter->subsector->sector, 0, target); +} + +// +// P_CheckMeleeRange +// + +static boolean P_CheckMeleeRange(mobj_t *actor) +{ + mobj_t *pl = actor->target; + + return // killough 7/18/98: friendly monsters don't attack other friends + pl && !(actor->flags & pl->flags & MF_FRIEND) && + (P_AproxDistance(pl->x-actor->x, pl->y-actor->y) < + MELEERANGE - 20*FRACUNIT + pl->info->radius) && + P_CheckSight(actor, actor->target); +} + +// +// P_HitFriend() +// +// killough 12/98 +// This function tries to prevent shooting at friends + +static boolean P_HitFriend(mobj_t *actor) +{ + return actor->flags & MF_FRIEND && actor->target && + (P_AimLineAttack(actor, + R_PointToAngle2(actor->x, actor->y, + actor->target->x, actor->target->y), + P_AproxDistance(actor->x-actor->target->x, + actor->y-actor->target->y), 0), + linetarget) && linetarget != actor->target && + !((linetarget->flags ^ actor->flags) & MF_FRIEND); +} + +// +// P_CheckMissileRange +// +static boolean P_CheckMissileRange(mobj_t *actor) +{ + fixed_t dist; + + if (!P_CheckSight(actor, actor->target)) + return false; + + if (actor->flags & MF_JUSTHIT) + { // the target just hit the enemy, so fight back! + actor->flags &= ~MF_JUSTHIT; + + /* killough 7/18/98: no friendly fire at corpses + * killough 11/98: prevent too much infighting among friends + * cph - yikes, talk about fitting everything on one line... */ + + return + !(actor->flags & MF_FRIEND) || + (actor->target->health > 0 && + (!(actor->target->flags & MF_FRIEND) || + (actor->target->player ? + monster_infighting || P_Random(pr_defect) >128 : + !(actor->target->flags & MF_JUSTHIT) && P_Random(pr_defect) >128))); + } + + /* killough 7/18/98: friendly monsters don't attack other friendly + * monsters or players (except when attacked, and then only once) + */ + if (actor->flags & actor->target->flags & MF_FRIEND) + return false; + + if (actor->reactiontime) + return false; // do not attack yet + + // OPTIMIZE: get this from a global checksight + dist = P_AproxDistance ( actor->x-actor->target->x, + actor->y-actor->target->y) - 64*FRACUNIT; + + if (!actor->info->meleestate) + dist -= 128*FRACUNIT; // no melee attack, so fire more + + dist >>= FRACBITS; + + if (actor->type == MT_VILE) + if (dist > 14*64) + return false; // too far away + + + if (actor->type == MT_UNDEAD) + { + if (dist < 196) + return false; // close for fist attack + dist >>= 1; + } + + if (actor->type == MT_CYBORG || + actor->type == MT_SPIDER || + actor->type == MT_SKULL) + dist >>= 1; + + if (dist > 200) + dist = 200; + + if (actor->type == MT_CYBORG && dist > 160) + dist = 160; + + if (P_Random(pr_missrange) < dist) + return false; + + if (P_HitFriend(actor)) + return false; + + return true; +} + +/* + * P_IsOnLift + * + * killough 9/9/98: + * + * Returns true if the object is on a lift. Used for AI, + * since it may indicate the need for crowded conditions, + * or that a monster should stay on the lift for a while + * while it goes up or down. + */ + +static boolean P_IsOnLift(const mobj_t *actor) +{ + const sector_t *sec = actor->subsector->sector; + line_t line; + int l; + + // Short-circuit: it's on a lift which is active. + if (sec->floordata && ((thinker_t *) sec->floordata)->function==T_PlatRaise) + return true; + + // Check to see if it's in a sector which can be activated as a lift. + if ((line.tag = sec->tag)) + for (l = -1; (l = P_FindLineFromLineTag(&line, l)) >= 0;) + switch (lines[l].special) + { + case 10: case 14: case 15: case 20: case 21: case 22: + case 47: case 53: case 62: case 66: case 67: case 68: + case 87: case 88: case 95: case 120: case 121: case 122: + case 123: case 143: case 162: case 163: case 181: case 182: + case 144: case 148: case 149: case 211: case 227: case 228: + case 231: case 232: case 235: case 236: + return true; + } + + return false; +} + +/* + * P_IsUnderDamage + * + * killough 9/9/98: + * + * Returns nonzero if the object is under damage based on + * their current position. Returns 1 if the damage is moderate, + * -1 if it is serious. Used for AI. + */ + +static int P_IsUnderDamage(mobj_t *actor) +{ + const struct msecnode_s *seclist; + const ceiling_t *cl; // Crushing ceiling + int dir = 0; + for (seclist=actor->touching_sectorlist; seclist; seclist=seclist->m_tnext) + if ((cl = seclist->m_sector->ceilingdata) && + cl->thinker.function == T_MoveCeiling) + dir |= cl->direction; + return dir; +} + +// +// P_Move +// Move in the current direction, +// returns false if the move is blocked. +// + +static fixed_t xspeed[8] = {FRACUNIT,47000,0,-47000,-FRACUNIT,-47000,0,47000}; +static fixed_t yspeed[8] = {0,47000,FRACUNIT,47000,0,-47000,-FRACUNIT,-47000}; + +// 1/11/98 killough: Limit removed on special lines crossed +extern line_t **spechit; // New code -- killough +extern int numspechit; + +static boolean P_Move(mobj_t *actor, boolean dropoff) /* killough 9/12/98 */ +{ + fixed_t tryx, tryy, deltax, deltay, origx, origy; + boolean try_ok; + int movefactor = ORIG_FRICTION_FACTOR; // killough 10/98 + int friction = ORIG_FRICTION; + int speed; + + if (actor->movedir == DI_NODIR) + return false; + +#ifdef RANGECHECK + if ((unsigned)actor->movedir >= 8) + I_Error ("P_Move: Weird actor->movedir!"); +#endif + + // killough 10/98: make monsters get affected by ice and sludge too: + + if (monster_friction) + movefactor = P_GetMoveFactor(actor, &friction); + + speed = actor->info->speed; + + if (friction < ORIG_FRICTION && // sludge + !(speed = ((ORIG_FRICTION_FACTOR - (ORIG_FRICTION_FACTOR-movefactor)/2) + * speed) / ORIG_FRICTION_FACTOR)) + speed = 1; // always give the monster a little bit of speed + + tryx = (origx = actor->x) + (deltax = speed * xspeed[actor->movedir]); + tryy = (origy = actor->y) + (deltay = speed * yspeed[actor->movedir]); + + try_ok = P_TryMove(actor, tryx, tryy, dropoff); + + // killough 10/98: + // Let normal momentum carry them, instead of steptoeing them across ice. + + if (try_ok && friction > ORIG_FRICTION) + { + actor->x = origx; + actor->y = origy; + movefactor *= FRACUNIT / ORIG_FRICTION_FACTOR / 4; + actor->momx += FixedMul(deltax, movefactor); + actor->momy += FixedMul(deltay, movefactor); + } + + if (!try_ok) + { // open any specials + int good; + + if (actor->flags & MF_FLOAT && floatok) + { + if (actor->z < tmfloorz) // must adjust height + actor->z += FLOATSPEED; + else + actor->z -= FLOATSPEED; + + actor->flags |= MF_INFLOAT; + + return true; + } + + if (!numspechit) + return false; + + actor->movedir = DI_NODIR; + + /* if the special is not a door that can be opened, return false + * + * killough 8/9/98: this is what caused monsters to get stuck in + * doortracks, because it thought that the monster freed itself + * by opening a door, even if it was moving towards the doortrack, + * and not the door itself. + * + * killough 9/9/98: If a line blocking the monster is activated, + * return true 90% of the time. If a line blocking the monster is + * not activated, but some other line is, return false 90% of the + * time. A bit of randomness is needed to ensure it's free from + * lockups, but for most cases, it returns the correct result. + * + * Do NOT simply return false 1/4th of the time (causes monsters to + * back out when they shouldn't, and creates secondary stickiness). + */ + + for (good = false; numspechit--; ) + if (P_UseSpecialLine(actor, spechit[numspechit], 0)) + good |= spechit[numspechit] == blockline ? 1 : 2; + + /* cph - compatibility maze here + * Boom v2.01 and orig. Doom return "good" + * Boom v2.02 and LxDoom return good && (P_Random(pr_trywalk)&3) + * MBF plays even more games + */ + if (!good || comp[comp_doorstuck]) return good; + if (!mbf_features) + return (P_Random(pr_trywalk)&3); /* jff 8/13/98 */ + else /* finally, MBF code */ + return ((P_Random(pr_opendoor) >= 230) ^ (good & 1)); + } + else + actor->flags &= ~MF_INFLOAT; + + /* killough 11/98: fall more slowly, under gravity, if felldown==true */ + if (!(actor->flags & MF_FLOAT) && + (!felldown || !mbf_features)) + actor->z = actor->floorz; + + return true; +} + +/* + * P_SmartMove + * + * killough 9/12/98: Same as P_Move, except smarter + */ + +static boolean P_SmartMove(mobj_t *actor) +{ + mobj_t *target = actor->target; + int on_lift, dropoff = false, under_damage; + + /* killough 9/12/98: Stay on a lift if target is on one */ + on_lift = !comp[comp_staylift] + && target && target->health > 0 + && target->subsector->sector->tag==actor->subsector->sector->tag && + P_IsOnLift(actor); + + under_damage = monster_avoid_hazards && P_IsUnderDamage(actor); + + // killough 10/98: allow dogs to drop off of taller ledges sometimes. + // dropoff==1 means always allow it, dropoff==2 means only up to 128 high, + // and only if the target is immediately on the other side of the line. + +#ifdef DOGS + // haleyjd: allow all friends of HelperType to also jump down + + if ((actor->type == MT_DOGS || (actor->type == (HelperThing-1) && actor->flags&MF_FRIEND)) + && target && dog_jumping && + !((target->flags ^ actor->flags) & MF_FRIEND) && + P_AproxDistance(actor->x - target->x, + actor->y - target->y) < FRACUNIT*144 && + P_Random(pr_dropoff) < 235) + dropoff = 2; +#endif + + if (!P_Move(actor, dropoff)) + return false; + + // killough 9/9/98: avoid crushing ceilings or other damaging areas + if ( + (on_lift && P_Random(pr_stayonlift) < 230 && // Stay on lift + !P_IsOnLift(actor)) + || + (monster_avoid_hazards && !under_damage && // Get away from damage + (under_damage = P_IsUnderDamage(actor)) && + (under_damage < 0 || P_Random(pr_avoidcrush) < 200)) + ) + actor->movedir = DI_NODIR; // avoid the area (most of the time anyway) + + return true; +} + +// +// TryWalk +// Attempts to move actor on +// in its current (ob->moveangle) direction. +// If blocked by either a wall or an actor +// returns FALSE +// If move is either clear or blocked only by a door, +// returns TRUE and sets... +// If a door is in the way, +// an OpenDoor call is made to start it opening. +// + +static boolean P_TryWalk(mobj_t *actor) +{ + if (!P_SmartMove(actor)) + return false; + actor->movecount = P_Random(pr_trywalk)&15; + return true; +} + +// +// P_DoNewChaseDir +// +// killough 9/8/98: +// +// Most of P_NewChaseDir(), except for what +// determines the new direction to take +// + +static void P_DoNewChaseDir(mobj_t *actor, fixed_t deltax, fixed_t deltay) +{ + dirtype_t xdir, ydir, tdir; + dirtype_t olddir = actor->movedir; + dirtype_t turnaround = olddir; + + if (turnaround != DI_NODIR) // find reverse direction + turnaround ^= 4; + + xdir = + deltax > 10*FRACUNIT ? DI_EAST : + deltax < -10*FRACUNIT ? DI_WEST : DI_NODIR; + + ydir = + deltay < -10*FRACUNIT ? DI_SOUTH : + deltay > 10*FRACUNIT ? DI_NORTH : DI_NODIR; + + // try direct route + if (xdir != DI_NODIR && ydir != DI_NODIR && turnaround != + (actor->movedir = deltay < 0 ? deltax > 0 ? DI_SOUTHEAST : DI_SOUTHWEST : + deltax > 0 ? DI_NORTHEAST : DI_NORTHWEST) && P_TryWalk(actor)) + return; + + // try other directions + if (P_Random(pr_newchase) > 200 || D_abs(deltay)>D_abs(deltax)) + tdir = xdir, xdir = ydir, ydir = tdir; + + if ((xdir == turnaround ? xdir = DI_NODIR : xdir) != DI_NODIR && + (actor->movedir = xdir, P_TryWalk(actor))) + return; // either moved forward or attacked + + if ((ydir == turnaround ? ydir = DI_NODIR : ydir) != DI_NODIR && + (actor->movedir = ydir, P_TryWalk(actor))) + return; + + // there is no direct path to the player, so pick another direction. + if (olddir != DI_NODIR && (actor->movedir = olddir, P_TryWalk(actor))) + return; + + // randomly determine direction of search + if (P_Random(pr_newchasedir) & 1) + { + for (tdir = DI_EAST; tdir <= DI_SOUTHEAST; tdir++) + if (tdir != turnaround && (actor->movedir = tdir, P_TryWalk(actor))) + return; + } + else + for (tdir = DI_SOUTHEAST; tdir != DI_EAST-1; tdir--) + if (tdir != turnaround && (actor->movedir = tdir, P_TryWalk(actor))) + return; + + if ((actor->movedir = turnaround) != DI_NODIR && !P_TryWalk(actor)) + actor->movedir = DI_NODIR; +} + +// +// killough 11/98: +// +// Monsters try to move away from tall dropoffs. +// +// In Doom, they were never allowed to hang over dropoffs, +// and would remain stuck if involuntarily forced over one. +// This logic, combined with p_map.c (P_TryMove), allows +// monsters to free themselves without making them tend to +// hang over dropoffs. + +static fixed_t dropoff_deltax, dropoff_deltay, floorz; + +static boolean PIT_AvoidDropoff(line_t *line) +{ + if (line->backsector && // Ignore one-sided linedefs + tmbbox[BOXRIGHT] > line->bbox[BOXLEFT] && + tmbbox[BOXLEFT] < line->bbox[BOXRIGHT] && + tmbbox[BOXTOP] > line->bbox[BOXBOTTOM] && // Linedef must be contacted + tmbbox[BOXBOTTOM] < line->bbox[BOXTOP] && + P_BoxOnLineSide(tmbbox, line) == -1) + { + fixed_t front = line->frontsector->floorheight; + fixed_t back = line->backsector->floorheight; + angle_t angle; + + // The monster must contact one of the two floors, + // and the other must be a tall dropoff (more than 24). + + if (back == floorz && front < floorz - FRACUNIT*24) + angle = R_PointToAngle2(0,0,line->dx,line->dy); // front side dropoff + else + if (front == floorz && back < floorz - FRACUNIT*24) + angle = R_PointToAngle2(line->dx,line->dy,0,0); // back side dropoff + else + return true; + + // Move away from dropoff at a standard speed. + // Multiple contacted linedefs are cumulative (e.g. hanging over corner) + dropoff_deltax -= finesine[angle >> ANGLETOFINESHIFT]*32; + dropoff_deltay += finecosine[angle >> ANGLETOFINESHIFT]*32; + } + return true; +} + +// +// Driver for above +// + +static fixed_t P_AvoidDropoff(mobj_t *actor) +{ + int yh=((tmbbox[BOXTOP] = actor->y+actor->radius)-bmaporgy)>>MAPBLOCKSHIFT; + int yl=((tmbbox[BOXBOTTOM]= actor->y-actor->radius)-bmaporgy)>>MAPBLOCKSHIFT; + int xh=((tmbbox[BOXRIGHT] = actor->x+actor->radius)-bmaporgx)>>MAPBLOCKSHIFT; + int xl=((tmbbox[BOXLEFT] = actor->x-actor->radius)-bmaporgx)>>MAPBLOCKSHIFT; + int bx, by; + + floorz = actor->z; // remember floor height + + dropoff_deltax = dropoff_deltay = 0; + + // check lines + + validcount++; + for (bx=xl ; bx<=xh ; bx++) + for (by=yl ; by<=yh ; by++) + P_BlockLinesIterator(bx, by, PIT_AvoidDropoff); // all contacted lines + + return dropoff_deltax | dropoff_deltay; // Non-zero if movement prescribed +} + +// +// P_NewChaseDir +// +// killough 9/8/98: Split into two functions +// + +static void P_NewChaseDir(mobj_t *actor) +{ + mobj_t *target = actor->target; + fixed_t deltax = target->x - actor->x; + fixed_t deltay = target->y - actor->y; + + // killough 8/8/98: sometimes move away from target, keeping distance + // + // 1) Stay a certain distance away from a friend, to avoid being in their way + // 2) Take advantage over an enemy without missiles, by keeping distance + + actor->strafecount = 0; + + if (mbf_features) { + if (actor->floorz - actor->dropoffz > FRACUNIT*24 && + actor->z <= actor->floorz && + !(actor->flags & (MF_DROPOFF|MF_FLOAT)) && + !comp[comp_dropoff] && + P_AvoidDropoff(actor)) /* Move away from dropoff */ + { + P_DoNewChaseDir(actor, dropoff_deltax, dropoff_deltay); + + // If moving away from dropoff, set movecount to 1 so that + // small steps are taken to get monster away from dropoff. + + actor->movecount = 1; + return; + } + else + { + fixed_t dist = P_AproxDistance(deltax, deltay); + + // Move away from friends when too close, except + // in certain situations (e.g. a crowded lift) + + if (actor->flags & target->flags & MF_FRIEND && + distfriend << FRACBITS > dist && + !P_IsOnLift(target) && !P_IsUnderDamage(actor)) + { + deltax = -deltax, deltay = -deltay; + } else + if (target->health > 0 && (actor->flags ^ target->flags) & MF_FRIEND) + { // Live enemy target + if (monster_backing && + actor->info->missilestate && actor->type != MT_SKULL && + ((!target->info->missilestate && dist < MELEERANGE*2) || + (target->player && dist < MELEERANGE*3 && + (target->player->readyweapon == wp_fist || + target->player->readyweapon == wp_chainsaw)))) + { // Back away from melee attacker + actor->strafecount = P_Random(pr_enemystrafe) & 15; + deltax = -deltax, deltay = -deltay; + } + } + } + } + + P_DoNewChaseDir(actor, deltax, deltay); + + // If strafing, set movecount to strafecount so that old Doom + // logic still works the same, except in the strafing part + + if (actor->strafecount) + actor->movecount = actor->strafecount; +} + +// +// P_IsVisible +// +// killough 9/9/98: whether a target is visible to a monster +// + +static boolean P_IsVisible(mobj_t *actor, mobj_t *mo, boolean allaround) +{ + if (!allaround) + { + angle_t an = R_PointToAngle2(actor->x, actor->y, + mo->x, mo->y) - actor->angle; + if (an > ANG90 && an < ANG270 && + P_AproxDistance(mo->x-actor->x, mo->y-actor->y) > MELEERANGE) + return false; + } + return P_CheckSight(actor, mo); +} + +// +// PIT_FindTarget +// +// killough 9/5/98 +// +// Finds monster targets for other monsters +// + +static int current_allaround; + +static boolean PIT_FindTarget(mobj_t *mo) +{ + mobj_t *actor = current_actor; + + if (!((mo->flags ^ actor->flags) & MF_FRIEND && // Invalid target + mo->health > 0 && (mo->flags & MF_COUNTKILL || mo->type == MT_SKULL))) + return true; + + // If the monster is already engaged in a one-on-one attack + // with a healthy friend, don't attack around 60% the time + { + const mobj_t *targ = mo->target; + if (targ && targ->target == mo && + P_Random(pr_skiptarget) > 100 && + (targ->flags ^ mo->flags) & MF_FRIEND && + targ->health*2 >= targ->info->spawnhealth) + return true; + } + + if (!P_IsVisible(actor, mo, current_allaround)) + return true; + + P_SetTarget(&actor->lastenemy, actor->target); // Remember previous target + P_SetTarget(&actor->target, mo); // Found target + + // Move the selected monster to the end of its associated + // list, so that it gets searched last next time. + + { + thinker_t *cap = &thinkerclasscap[mo->flags & MF_FRIEND ? + th_friends : th_enemies]; + (mo->thinker.cprev->cnext = mo->thinker.cnext)->cprev = mo->thinker.cprev; + (mo->thinker.cprev = cap->cprev)->cnext = &mo->thinker; + (mo->thinker.cnext = cap)->cprev = &mo->thinker; + } + + return false; +} + +// +// P_LookForPlayers +// If allaround is false, only look 180 degrees in front. +// Returns true if a player is targeted. +// + +static boolean P_LookForPlayers(mobj_t *actor, boolean allaround) +{ + player_t *player; + int stop, stopc, c; + + if (actor->flags & MF_FRIEND) + { // killough 9/9/98: friendly monsters go about players differently + int anyone; + +#if 0 + if (!allaround) // If you want friendly monsters not to awaken unprovoked + return false; +#endif + + // Go back to a player, no matter whether it's visible or not + for (anyone=0; anyone<=1; anyone++) + for (c=0; ctarget, players[c].mo); + + // killough 12/98: + // get out of refiring loop, to avoid hitting player accidentally + + if (actor->info->missilestate) + { + P_SetMobjState(actor, actor->info->seestate); + actor->flags &= ~MF_JUSTHIT; + } + + return true; + } + + return false; + } + + // Change mask of 3 to (MAXPLAYERS-1) -- killough 2/15/98: + stop = (actor->lastlook-1)&(MAXPLAYERS-1); + + c = 0; + + stopc = !mbf_features && + !demo_compatibility && monsters_remember ? + MAXPLAYERS : 2; // killough 9/9/98 + + for (;; actor->lastlook = (actor->lastlook+1)&(MAXPLAYERS-1)) + { + if (!playeringame[actor->lastlook]) + continue; + + // killough 2/15/98, 9/9/98: + if (c++ == stopc || actor->lastlook == stop) // done looking + { + // e6y + // Fixed Boom incompatibilities. The following code was missed. + // There are no more desyncs on Donce's demos on horror.wad + + // Use last known enemy if no players sighted -- killough 2/15/98: + if (!mbf_features && !demo_compatibility && monsters_remember) + { + if (actor->lastenemy && actor->lastenemy->health > 0) + { + actor->target = actor->lastenemy; + actor->lastenemy = NULL; + return true; + } + } + + return false; + } + + player = &players[actor->lastlook]; + + if (player->health <= 0) + continue; // dead + + if (!P_IsVisible(actor, player->mo, allaround)) + continue; + + P_SetTarget(&actor->target, player->mo); + + /* killough 9/9/98: give monsters a threshold towards getting players + * (we don't want it to be too easy for a player with dogs :) + */ + if (!comp[comp_pursuit]) + actor->threshold = 60; + + return true; + } +} + +// +// Friendly monsters, by Lee Killough 7/18/98 +// +// Friendly monsters go after other monsters first, but +// also return to owner if they cannot find any targets. +// A marine's best friend :) killough 7/18/98, 9/98 +// + +static boolean P_LookForMonsters(mobj_t *actor, boolean allaround) +{ + thinker_t *cap, *th; + + if (demo_compatibility) + return false; + + if (actor->lastenemy && actor->lastenemy->health > 0 && monsters_remember && + !(actor->lastenemy->flags & actor->flags & MF_FRIEND)) // not friends + { + P_SetTarget(&actor->target, actor->lastenemy); + P_SetTarget(&actor->lastenemy, NULL); + return true; + } + + /* Old demos do not support monster-seeking bots */ + if (!mbf_features) + return false; + + // Search the threaded list corresponding to this object's potential targets + cap = &thinkerclasscap[actor->flags & MF_FRIEND ? th_enemies : th_friends]; + + // Search for new enemy + + if (cap->cnext != cap) // Empty list? bail out early + { + int x = (actor->x - bmaporgx)>>MAPBLOCKSHIFT; + int y = (actor->y - bmaporgy)>>MAPBLOCKSHIFT; + int d; + + current_actor = actor; + current_allaround = allaround; + + // Search first in the immediate vicinity. + + if (!P_BlockThingsIterator(x, y, PIT_FindTarget)) + return true; + + for (d=1; d<5; d++) + { + int i = 1 - d; + do + if (!P_BlockThingsIterator(x+i, y-d, PIT_FindTarget) || + !P_BlockThingsIterator(x+i, y+d, PIT_FindTarget)) + return true; + while (++i < d); + do + if (!P_BlockThingsIterator(x-d, y+i, PIT_FindTarget) || + !P_BlockThingsIterator(x+d, y+i, PIT_FindTarget)) + return true; + while (--i + d >= 0); + } + + { // Random number of monsters, to prevent patterns from forming + int n = (P_Random(pr_friends) & 31) + 15; + + for (th = cap->cnext; th != cap; th = th->cnext) + if (--n < 0) + { + // Only a subset of the monsters were searched. Move all of + // the ones which were searched so far, to the end of the list. + + (cap->cnext->cprev = cap->cprev)->cnext = cap->cnext; + (cap->cprev = th->cprev)->cnext = cap; + (th->cprev = cap)->cnext = th; + break; + } + else + if (!PIT_FindTarget((mobj_t *) th)) // If target sighted + return true; + } + } + + return false; // No monster found +} + +// +// P_LookForTargets +// +// killough 9/5/98: look for targets to go after, depending on kind of monster +// + +static boolean P_LookForTargets(mobj_t *actor, int allaround) +{ + return actor->flags & MF_FRIEND ? + P_LookForMonsters(actor, allaround) || P_LookForPlayers (actor, allaround): + P_LookForPlayers (actor, allaround) || P_LookForMonsters(actor, allaround); +} + +// +// P_HelpFriend +// +// killough 9/8/98: Help friends in danger of dying +// + +static boolean P_HelpFriend(mobj_t *actor) +{ + thinker_t *cap, *th; + + // If less than 33% health, self-preservation rules + if (actor->health*3 < actor->info->spawnhealth) + return false; + + current_actor = actor; + current_allaround = true; + + // Possibly help a friend under 50% health + cap = &thinkerclasscap[actor->flags & MF_FRIEND ? th_friends : th_enemies]; + + for (th = cap->cnext; th != cap; th = th->cnext) + if (((mobj_t *) th)->health*2 >= ((mobj_t *) th)->info->spawnhealth) + { + if (P_Random(pr_helpfriend) < 180) + break; + } + else + if (((mobj_t *) th)->flags & MF_JUSTHIT && + ((mobj_t *) th)->target && + ((mobj_t *) th)->target != actor->target && + !PIT_FindTarget(((mobj_t *) th)->target)) + { + // Ignore any attacking monsters, while searching for friend + actor->threshold = BASETHRESHOLD; + return true; + } + + return false; +} + +// +// A_KeenDie +// DOOM II special, map 32. +// Uses special tag 666. +// +void A_KeenDie(mobj_t* mo) +{ + thinker_t *th; + line_t junk; + + A_Fall(mo); + + // scan the remaining thinkers to see if all Keens are dead + + for (th = thinkercap.next ; th != &thinkercap ; th=th->next) + if (th->function == P_MobjThinker) + { + mobj_t *mo2 = (mobj_t *) th; + if (mo2 != mo && mo2->type == mo->type && mo2->health > 0) + return; // other Keen not dead + } + + junk.tag = 666; + EV_DoDoor(&junk,open); +} + + +// +// ACTION ROUTINES +// + +// +// A_Look +// Stay in state until a player is sighted. +// + +void A_Look(mobj_t *actor) +{ + mobj_t *targ = actor->subsector->sector->soundtarget; + actor->threshold = 0; // any shot will wake up + + /* killough 7/18/98: + * Friendly monsters go after other monsters first, but + * also return to player, without attacking them, if they + * cannot find any targets. A marine's best friend :) + */ + actor->pursuecount = 0; + + if (!(actor->flags & MF_FRIEND && P_LookForTargets(actor, false)) && + !((targ = actor->subsector->sector->soundtarget) && + targ->flags & MF_SHOOTABLE && + (P_SetTarget(&actor->target, targ), + !(actor->flags & MF_AMBUSH) || P_CheckSight(actor, targ))) && + (actor->flags & MF_FRIEND || !P_LookForTargets(actor, false))) + return; + + // go into chase state + + if (actor->info->seesound) + { + int sound; + switch (actor->info->seesound) + { + case sfx_posit1: + case sfx_posit2: + case sfx_posit3: + sound = sfx_posit1+P_Random(pr_see)%3; + break; + + case sfx_bgsit1: + case sfx_bgsit2: + sound = sfx_bgsit1+P_Random(pr_see)%2; + break; + + default: + sound = actor->info->seesound; + break; + } + if (actor->type==MT_SPIDER || actor->type == MT_CYBORG) + S_StartSound(NULL, sound); // full volume + else + S_StartSound(actor, sound); + } + P_SetMobjState(actor, actor->info->seestate); +} + +// +// A_KeepChasing +// +// killough 10/98: +// Allows monsters to continue movement while attacking +// + +static void A_KeepChasing(mobj_t *actor) +{ + if (actor->movecount) + { + actor->movecount--; + if (actor->strafecount) + actor->strafecount--; + P_SmartMove(actor); + } +} + +// +// A_Chase +// Actor has a melee attack, +// so it tries to close as fast as possible +// + +void A_Chase(mobj_t *actor) +{ + if (actor->reactiontime) + actor->reactiontime--; + + if (actor->threshold) { /* modify target threshold */ + if (!actor->target || actor->target->health <= 0) + actor->threshold = 0; + else + actor->threshold--; + } + + /* turn towards movement direction if not there yet + * killough 9/7/98: keep facing towards target if strafing or backing out + */ + + if (actor->strafecount) + A_FaceTarget(actor); + else if (actor->movedir < 8) + { + int delta = (actor->angle &= (7<<29)) - (actor->movedir << 29); + if (delta > 0) + actor->angle -= ANG90/2; + else + if (delta < 0) + actor->angle += ANG90/2; + } + + if (!actor->target || !(actor->target->flags&MF_SHOOTABLE)) + { + if (!P_LookForTargets(actor,true)) // look for a new target + P_SetMobjState(actor, actor->info->spawnstate); // no new target + return; + } + + // do not attack twice in a row + if (actor->flags & MF_JUSTATTACKED) + { + actor->flags &= ~MF_JUSTATTACKED; + if (gameskill != sk_nightmare && !fastparm) + P_NewChaseDir(actor); + return; + } + + // check for melee attack + if (actor->info->meleestate && P_CheckMeleeRange(actor)) + { + if (actor->info->attacksound) + S_StartSound(actor, actor->info->attacksound); + P_SetMobjState(actor, actor->info->meleestate); + /* killough 8/98: remember an attack + * cph - DEMOSYNC? */ + if (!actor->info->missilestate) + actor->flags |= MF_JUSTHIT; + return; + } + + // check for missile attack + if (actor->info->missilestate) + if (!(gameskill < sk_nightmare && !fastparm && actor->movecount)) + if (P_CheckMissileRange(actor)) + { + P_SetMobjState(actor, actor->info->missilestate); + actor->flags |= MF_JUSTATTACKED; + return; + } + + if (!actor->threshold) { + if (!mbf_features) + { /* killough 9/9/98: for backward demo compatibility */ + if (netgame && !P_CheckSight(actor, actor->target) && + P_LookForPlayers(actor, true)) + return; + } + /* killough 7/18/98, 9/9/98: new monster AI */ + else if (help_friends && P_HelpFriend(actor)) + return; /* killough 9/8/98: Help friends in need */ + /* Look for new targets if current one is bad or is out of view */ + else if (actor->pursuecount) + actor->pursuecount--; + else { + /* Our pursuit time has expired. We're going to think about + * changing targets */ + actor->pursuecount = BASETHRESHOLD; + + /* Unless (we have a live target + * and it's not friendly + * and we can see it) + * try to find a new one; return if sucessful */ + + if (!(actor->target && actor->target->health > 0 && + ((comp[comp_pursuit] && !netgame) || + (((actor->target->flags ^ actor->flags) & MF_FRIEND || + (!(actor->flags & MF_FRIEND) && monster_infighting)) && + P_CheckSight(actor, actor->target)))) + && P_LookForTargets(actor, true)) + return; + + /* (Current target was good, or no new target was found.) + * + * If monster is a missile-less friend, give up pursuit and + * return to player, if no attacks have occurred recently. + */ + + if (!actor->info->missilestate && actor->flags & MF_FRIEND) { + if (actor->flags & MF_JUSTHIT) /* if recent action, */ + actor->flags &= ~MF_JUSTHIT; /* keep fighting */ + else if (P_LookForPlayers(actor, true)) /* else return to player */ + return; + } + } + } + + if (actor->strafecount) + actor->strafecount--; + + // chase towards player + if (--actor->movecount<0 || !P_SmartMove(actor)) + P_NewChaseDir(actor); + + // make active sound + if (actor->info->activesound && P_Random(pr_see)<3) + S_StartSound(actor, actor->info->activesound); +} + +// +// A_FaceTarget +// +void A_FaceTarget(mobj_t *actor) +{ + if (!actor->target) + return; + actor->flags &= ~MF_AMBUSH; + actor->angle = R_PointToAngle2(actor->x, actor->y, + actor->target->x, actor->target->y); + if (actor->target->flags & MF_SHADOW) + { // killough 5/5/98: remove dependence on order of evaluation: + int t = P_Random(pr_facetarget); + actor->angle += (t-P_Random(pr_facetarget))<<21; + } +} + +// +// A_PosAttack +// + +void A_PosAttack(mobj_t *actor) +{ + int angle, damage, slope, t; + + if (!actor->target) + return; + A_FaceTarget(actor); + angle = actor->angle; + slope = P_AimLineAttack(actor, angle, MISSILERANGE, 0); /* killough 8/2/98 */ + S_StartSound(actor, sfx_pistol); + + // killough 5/5/98: remove dependence on order of evaluation: + t = P_Random(pr_posattack); + angle += (t - P_Random(pr_posattack))<<20; + damage = (P_Random(pr_posattack)%5 + 1)*3; + P_LineAttack(actor, angle, MISSILERANGE, slope, damage); +} + +void A_SPosAttack(mobj_t* actor) +{ + int i, bangle, slope; + + if (!actor->target) + return; + S_StartSound(actor, sfx_shotgn); + A_FaceTarget(actor); + bangle = actor->angle; + slope = P_AimLineAttack(actor, bangle, MISSILERANGE, 0); /* killough 8/2/98 */ + for (i=0; i<3; i++) + { // killough 5/5/98: remove dependence on order of evaluation: + int t = P_Random(pr_sposattack); + int angle = bangle + ((t - P_Random(pr_sposattack))<<20); + int damage = ((P_Random(pr_sposattack)%5)+1)*3; + P_LineAttack(actor, angle, MISSILERANGE, slope, damage); + } +} + +void A_CPosAttack(mobj_t *actor) +{ + int angle, bangle, damage, slope, t; + + if (!actor->target) + return; + S_StartSound(actor, sfx_shotgn); + A_FaceTarget(actor); + bangle = actor->angle; + slope = P_AimLineAttack(actor, bangle, MISSILERANGE, 0); /* killough 8/2/98 */ + + // killough 5/5/98: remove dependence on order of evaluation: + t = P_Random(pr_cposattack); + angle = bangle + ((t - P_Random(pr_cposattack))<<20); + damage = ((P_Random(pr_cposattack)%5)+1)*3; + P_LineAttack(actor, angle, MISSILERANGE, slope, damage); +} + +void A_CPosRefire(mobj_t *actor) +{ + // keep firing unless target got out of sight + A_FaceTarget(actor); + + /* killough 12/98: Stop firing if a friend has gotten in the way */ + if (P_HitFriend(actor)) + goto stop; + + /* killough 11/98: prevent refiring on friends continuously */ + if (P_Random(pr_cposrefire) < 40) { + if (actor->target && actor->flags & actor->target->flags & MF_FRIEND) + goto stop; + else + return; + } + + if (!actor->target || actor->target->health <= 0 + || !P_CheckSight(actor, actor->target)) +stop: P_SetMobjState(actor, actor->info->seestate); +} + +void A_SpidRefire(mobj_t* actor) +{ + // keep firing unless target got out of sight + A_FaceTarget(actor); + + /* killough 12/98: Stop firing if a friend has gotten in the way */ + if (P_HitFriend(actor)) + goto stop; + + if (P_Random(pr_spidrefire) < 10) + return; + + // killough 11/98: prevent refiring on friends continuously + if (!actor->target || actor->target->health <= 0 + || actor->flags & actor->target->flags & MF_FRIEND + || !P_CheckSight(actor, actor->target)) + stop: P_SetMobjState(actor, actor->info->seestate); +} + +void A_BspiAttack(mobj_t *actor) +{ + if (!actor->target) + return; + A_FaceTarget(actor); + P_SpawnMissile(actor, actor->target, MT_ARACHPLAZ); // launch a missile +} + +// +// A_TroopAttack +// + +void A_TroopAttack(mobj_t *actor) +{ + if (!actor->target) + return; + A_FaceTarget(actor); + if (P_CheckMeleeRange(actor)) + { + int damage; + S_StartSound(actor, sfx_claw); + damage = (P_Random(pr_troopattack)%8+1)*3; + P_DamageMobj(actor->target, actor, actor, damage); + return; + } + P_SpawnMissile(actor, actor->target, MT_TROOPSHOT); // launch a missile +} + +void A_SargAttack(mobj_t *actor) +{ + if (!actor->target) + return; + A_FaceTarget(actor); + if (P_CheckMeleeRange(actor)) + { + int damage = ((P_Random(pr_sargattack)%10)+1)*4; + P_DamageMobj(actor->target, actor, actor, damage); + } +} + +void A_HeadAttack(mobj_t *actor) +{ + if (!actor->target) + return; + A_FaceTarget (actor); + if (P_CheckMeleeRange(actor)) + { + int damage = (P_Random(pr_headattack)%6+1)*10; + P_DamageMobj(actor->target, actor, actor, damage); + return; + } + P_SpawnMissile(actor, actor->target, MT_HEADSHOT); // launch a missile +} + +void A_CyberAttack(mobj_t *actor) +{ + if (!actor->target) + return; + A_FaceTarget(actor); + P_SpawnMissile(actor, actor->target, MT_ROCKET); +} + +void A_BruisAttack(mobj_t *actor) +{ + if (!actor->target) + return; + if (P_CheckMeleeRange(actor)) + { + int damage; + S_StartSound(actor, sfx_claw); + damage = (P_Random(pr_bruisattack)%8+1)*10; + P_DamageMobj(actor->target, actor, actor, damage); + return; + } + P_SpawnMissile(actor, actor->target, MT_BRUISERSHOT); // launch a missile +} + +// +// A_SkelMissile +// + +void A_SkelMissile(mobj_t *actor) +{ + mobj_t *mo; + + if (!actor->target) + return; + + A_FaceTarget (actor); + actor->z += 16*FRACUNIT; // so missile spawns higher + mo = P_SpawnMissile (actor, actor->target, MT_TRACER); + actor->z -= 16*FRACUNIT; // back to normal + + mo->x += mo->momx; + mo->y += mo->momy; + P_SetTarget(&mo->tracer, actor->target); +} + +int TRACEANGLE = 0xc000000; + +void A_Tracer(mobj_t *actor) +{ + angle_t exact; + fixed_t dist; + fixed_t slope; + mobj_t *dest; + mobj_t *th; + + /* killough 1/18/98: this is why some missiles do not have smoke + * and some do. Also, internal demos start at random gametics, thus + * the bug in which revenants cause internal demos to go out of sync. + * + * killough 3/6/98: fix revenant internal demo bug by subtracting + * levelstarttic from gametic. + * + * killough 9/29/98: use new "basetic" so that demos stay in sync + * during pauses and menu activations, while retaining old demo sync. + * + * leveltime would have been better to use to start with in Doom, but + * since old demos were recorded using gametic, we must stick with it, + * and improvise around it (using leveltime causes desync across levels). + */ + + if ((gametic-basetic) & 3) + return; + + // spawn a puff of smoke behind the rocket + P_SpawnPuff(actor->x, actor->y, actor->z); + + th = P_SpawnMobj (actor->x-actor->momx, + actor->y-actor->momy, + actor->z, MT_SMOKE); + + th->momz = FRACUNIT; + th->tics -= P_Random(pr_tracer) & 3; + if (th->tics < 1) + th->tics = 1; + + // adjust direction + dest = actor->tracer; + + if (!dest || dest->health <= 0) + return; + + // change angle + exact = R_PointToAngle2(actor->x, actor->y, dest->x, dest->y); + + if (exact != actor->angle) { + if (exact - actor->angle > 0x80000000) + { + actor->angle -= TRACEANGLE; + if (exact - actor->angle < 0x80000000) + actor->angle = exact; + } + else + { + actor->angle += TRACEANGLE; + if (exact - actor->angle > 0x80000000) + actor->angle = exact; + } + } + + exact = actor->angle>>ANGLETOFINESHIFT; + actor->momx = FixedMul(actor->info->speed, finecosine[exact]); + actor->momy = FixedMul(actor->info->speed, finesine[exact]); + + // change slope + dist = P_AproxDistance(dest->x - actor->x, dest->y - actor->y); + + dist = dist / actor->info->speed; + + if (dist < 1) + dist = 1; + + slope = (dest->z+40*FRACUNIT - actor->z) / dist; + + if (slope < actor->momz) + actor->momz -= FRACUNIT/8; + else + actor->momz += FRACUNIT/8; +} + +void A_SkelWhoosh(mobj_t *actor) +{ + if (!actor->target) + return; + A_FaceTarget(actor); + S_StartSound(actor,sfx_skeswg); +} + +void A_SkelFist(mobj_t *actor) +{ + if (!actor->target) + return; + A_FaceTarget(actor); + if (P_CheckMeleeRange(actor)) + { + int damage = ((P_Random(pr_skelfist)%10)+1)*6; + S_StartSound(actor, sfx_skepch); + P_DamageMobj(actor->target, actor, actor, damage); + } +} + +// +// PIT_VileCheck +// Detect a corpse that could be raised. +// + +mobj_t* corpsehit; +mobj_t* vileobj; +fixed_t viletryx; +fixed_t viletryy; + +static boolean PIT_VileCheck(mobj_t *thing) +{ + int maxdist; + boolean check; + + if (!(thing->flags & MF_CORPSE) ) + return true; // not a monster + + if (thing->tics != -1) + return true; // not lying still yet + + if (thing->info->raisestate == S_NULL) + return true; // monster doesn't have a raise state + + maxdist = thing->info->radius + mobjinfo[MT_VILE].radius; + + if (D_abs(thing->x-viletryx) > maxdist || D_abs(thing->y-viletryy) > maxdist) + return true; // not actually touching + +// Check to see if the radius and height are zero. If they are // phares +// then this is a crushed monster that has been turned into a // | +// gib. One of the options may be to ignore this guy. // V + +// Option 1: the original, buggy method, -> ghost (compatibility) +// Option 2: ressurect the monster, but not as a ghost +// Option 3: ignore the gib + +// if (Option3) // ^ +// if ((thing->height == 0) && (thing->radius == 0)) // | +// return true; // phares + + corpsehit = thing; + corpsehit->momx = corpsehit->momy = 0; + if (comp[comp_vile]) // phares + { // | + corpsehit->height <<= 2; // V + check = P_CheckPosition(corpsehit,corpsehit->x,corpsehit->y); + corpsehit->height >>= 2; + } + else + { + int height,radius; + + height = corpsehit->height; // save temporarily + radius = corpsehit->radius; // save temporarily + corpsehit->height = corpsehit->info->height; + corpsehit->radius = corpsehit->info->radius; + corpsehit->flags |= MF_SOLID; + check = P_CheckPosition(corpsehit,corpsehit->x,corpsehit->y); + corpsehit->height = height; // restore + corpsehit->radius = radius; // restore // ^ + corpsehit->flags &= ~MF_SOLID; + } // | + // phares + if (!check) + return true; // doesn't fit here + return false; // got one, so stop checking +} + +// +// A_VileChase +// Check for ressurecting a body +// + +void A_VileChase(mobj_t* actor) +{ + int xl, xh; + int yl, yh; + int bx, by; + + if (actor->movedir != DI_NODIR) + { + // check for corpses to raise + viletryx = + actor->x + actor->info->speed*xspeed[actor->movedir]; + viletryy = + actor->y + actor->info->speed*yspeed[actor->movedir]; + + xl = (viletryx - bmaporgx - MAXRADIUS*2)>>MAPBLOCKSHIFT; + xh = (viletryx - bmaporgx + MAXRADIUS*2)>>MAPBLOCKSHIFT; + yl = (viletryy - bmaporgy - MAXRADIUS*2)>>MAPBLOCKSHIFT; + yh = (viletryy - bmaporgy + MAXRADIUS*2)>>MAPBLOCKSHIFT; + + vileobj = actor; + for (bx=xl ; bx<=xh ; bx++) + { + for (by=yl ; by<=yh ; by++) + { + // Call PIT_VileCheck to check + // whether object is a corpse + // that canbe raised. + if (!P_BlockThingsIterator(bx,by,PIT_VileCheck)) + { + mobjinfo_t *info; + + // got one! + mobj_t* temp = actor->target; + actor->target = corpsehit; + A_FaceTarget(actor); + actor->target = temp; + + P_SetMobjState(actor, S_VILE_HEAL1); + S_StartSound(corpsehit, sfx_slop); + info = corpsehit->info; + + P_SetMobjState(corpsehit,info->raisestate); + + if (comp[comp_vile]) // phares + corpsehit->height <<= 2; // | + else // V + { + corpsehit->height = info->height; // fix Ghost bug + corpsehit->radius = info->radius; // fix Ghost bug + } // phares + + /* killough 7/18/98: + * friendliness is transferred from AV to raised corpse + */ + corpsehit->flags = + (info->flags & ~MF_FRIEND) | (actor->flags & MF_FRIEND); + + if (!((corpsehit->flags ^ MF_COUNTKILL) & (MF_FRIEND | MF_COUNTKILL))) + totallive++; + + corpsehit->health = info->spawnhealth; + P_SetTarget(&corpsehit->target, NULL); // killough 11/98 + + if (mbf_features) + { /* kilough 9/9/98 */ + P_SetTarget(&corpsehit->lastenemy, NULL); + corpsehit->flags &= ~MF_JUSTHIT; + } + + /* killough 8/29/98: add to appropriate thread */ + P_UpdateThinker(&corpsehit->thinker); + + return; + } + } + } + } + A_Chase(actor); // Return to normal attack. +} + +// +// A_VileStart +// + +void A_VileStart(mobj_t *actor) +{ + S_StartSound(actor, sfx_vilatk); +} + +// +// A_Fire +// Keep fire in front of player unless out of sight +// + +void A_StartFire(mobj_t *actor) +{ + S_StartSound(actor,sfx_flamst); + A_Fire(actor); +} + +void A_FireCrackle(mobj_t* actor) +{ + S_StartSound(actor,sfx_flame); + A_Fire(actor); +} + +void A_Fire(mobj_t *actor) +{ + unsigned an; + mobj_t *dest = actor->tracer; + + if (!dest) + return; + + // don't move it if the vile lost sight + if (!P_CheckSight(actor->target, dest) ) + return; + + an = dest->angle >> ANGLETOFINESHIFT; + + P_UnsetThingPosition(actor); + actor->x = dest->x + FixedMul(24*FRACUNIT, finecosine[an]); + actor->y = dest->y + FixedMul(24*FRACUNIT, finesine[an]); + actor->z = dest->z; + P_SetThingPosition(actor); +} + +// +// A_VileTarget +// Spawn the hellfire +// + +void A_VileTarget(mobj_t *actor) +{ + mobj_t *fog; + + if (!actor->target) + return; + + A_FaceTarget(actor); + + // killough 12/98: fix Vile fog coordinates // CPhipps - compatibility optioned + fog = P_SpawnMobj(actor->target->x, + (compatibility_level < lxdoom_1_compatibility) ? actor->target->x : actor->target->y, + actor->target->z,MT_FIRE); + + P_SetTarget(&actor->tracer, fog); + P_SetTarget(&fog->target, actor); + P_SetTarget(&fog->tracer, actor->target); + A_Fire(fog); +} + +// +// A_VileAttack +// + +void A_VileAttack(mobj_t *actor) +{ + mobj_t *fire; + int an; + + if (!actor->target) + return; + + A_FaceTarget(actor); + + if (!P_CheckSight(actor, actor->target)) + return; + + S_StartSound(actor, sfx_barexp); + P_DamageMobj(actor->target, actor, actor, 20); + actor->target->momz = 1000*FRACUNIT/actor->target->info->mass; + + an = actor->angle >> ANGLETOFINESHIFT; + + fire = actor->tracer; + + if (!fire) + return; + + // move the fire between the vile and the player + fire->x = actor->target->x - FixedMul (24*FRACUNIT, finecosine[an]); + fire->y = actor->target->y - FixedMul (24*FRACUNIT, finesine[an]); + P_RadiusAttack(fire, actor, 70); +} + +// +// Mancubus attack, +// firing three missiles (bruisers) +// in three different directions? +// Doesn't look like it. +// + +#define FATSPREAD (ANG90/8) + +void A_FatRaise(mobj_t *actor) +{ + A_FaceTarget(actor); + S_StartSound(actor, sfx_manatk); +} + +void A_FatAttack1(mobj_t *actor) +{ + mobj_t *mo; + int an; + + if (!actor->target) + return; + + A_FaceTarget(actor); + + // Change direction to ... + actor->angle += FATSPREAD; + + P_SpawnMissile(actor, actor->target, MT_FATSHOT); + + mo = P_SpawnMissile (actor, actor->target, MT_FATSHOT); + mo->angle += FATSPREAD; + an = mo->angle >> ANGLETOFINESHIFT; + mo->momx = FixedMul(mo->info->speed, finecosine[an]); + mo->momy = FixedMul(mo->info->speed, finesine[an]); +} + +void A_FatAttack2(mobj_t *actor) +{ + mobj_t *mo; + int an; + + if (!actor->target) + return; + + A_FaceTarget(actor); + // Now here choose opposite deviation. + actor->angle -= FATSPREAD; + P_SpawnMissile(actor, actor->target, MT_FATSHOT); + + mo = P_SpawnMissile(actor, actor->target, MT_FATSHOT); + mo->angle -= FATSPREAD*2; + an = mo->angle >> ANGLETOFINESHIFT; + mo->momx = FixedMul(mo->info->speed, finecosine[an]); + mo->momy = FixedMul(mo->info->speed, finesine[an]); +} + +void A_FatAttack3(mobj_t *actor) +{ + mobj_t *mo; + int an; + + if (!actor->target) + return; + + A_FaceTarget(actor); + + mo = P_SpawnMissile(actor, actor->target, MT_FATSHOT); + mo->angle -= FATSPREAD/2; + an = mo->angle >> ANGLETOFINESHIFT; + mo->momx = FixedMul(mo->info->speed, finecosine[an]); + mo->momy = FixedMul(mo->info->speed, finesine[an]); + + mo = P_SpawnMissile(actor, actor->target, MT_FATSHOT); + mo->angle += FATSPREAD/2; + an = mo->angle >> ANGLETOFINESHIFT; + mo->momx = FixedMul(mo->info->speed, finecosine[an]); + mo->momy = FixedMul(mo->info->speed, finesine[an]); +} + + +// +// SkullAttack +// Fly at the player like a missile. +// +#define SKULLSPEED (20*FRACUNIT) + +void A_SkullAttack(mobj_t *actor) +{ + mobj_t *dest; + angle_t an; + int dist; + + if (!actor->target) + return; + + dest = actor->target; + actor->flags |= MF_SKULLFLY; + + S_StartSound(actor, actor->info->attacksound); + A_FaceTarget(actor); + an = actor->angle >> ANGLETOFINESHIFT; + actor->momx = FixedMul(SKULLSPEED, finecosine[an]); + actor->momy = FixedMul(SKULLSPEED, finesine[an]); + dist = P_AproxDistance(dest->x - actor->x, dest->y - actor->y); + dist = dist / SKULLSPEED; + + if (dist < 1) + dist = 1; + actor->momz = (dest->z+(dest->height>>1) - actor->z) / dist; +} + +// +// A_PainShootSkull +// Spawn a lost soul and launch it at the target +// + +static void A_PainShootSkull(mobj_t *actor, angle_t angle) +{ + fixed_t x,y,z; + mobj_t *newmobj; + angle_t an; + int prestep; + +// The original code checked for 20 skulls on the level, // phares +// and wouldn't spit another one if there were. If not in // phares +// compatibility mode, we remove the limit. // phares + // phares + if (comp[comp_pain]) /* killough 10/98: compatibility-optioned */ + { + // count total number of skulls currently on the level + int count = 0; + thinker_t *currentthinker = NULL; + while ((currentthinker = P_NextThinker(currentthinker,th_all)) != NULL) + if ((currentthinker->function == P_MobjThinker) + && ((mobj_t *)currentthinker)->type == MT_SKULL) + count++; + if (count > 20) // phares + return; // phares + } + + // okay, there's room for another one + + an = angle >> ANGLETOFINESHIFT; + + prestep = 4*FRACUNIT + 3*(actor->info->radius + mobjinfo[MT_SKULL].radius)/2; + + x = actor->x + FixedMul(prestep, finecosine[an]); + y = actor->y + FixedMul(prestep, finesine[an]); + z = actor->z + 8*FRACUNIT; + + if (comp[comp_skull]) /* killough 10/98: compatibility-optioned */ + newmobj = P_SpawnMobj(x, y, z, MT_SKULL); // phares + else // V + { + // Check whether the Lost Soul is being fired through a 1-sided + // wall or an impassible line, or a "monsters can't cross" line. + // If it is, then we don't allow the spawn. This is a bug fix, but + // it should be considered an enhancement, since it may disturb + // existing demos, so don't do it in compatibility mode. + + if (Check_Sides(actor,x,y)) + return; + + newmobj = P_SpawnMobj(x, y, z, MT_SKULL); + + // Check to see if the new Lost Soul's z value is above the + // ceiling of its new sector, or below the floor. If so, kill it. + + if ((newmobj->z > + (newmobj->subsector->sector->ceilingheight - newmobj->height)) || + (newmobj->z < newmobj->subsector->sector->floorheight)) + { + // kill it immediately + P_DamageMobj(newmobj,actor,actor,10000); + return; // ^ + } // | + } // phares + + /* killough 7/20/98: PEs shoot lost souls with the same friendliness */ + newmobj->flags = (newmobj->flags & ~MF_FRIEND) | (actor->flags & MF_FRIEND); + + /* killough 8/29/98: add to appropriate thread */ + P_UpdateThinker(&newmobj->thinker); + + // Check for movements. + // killough 3/15/98: don't jump over dropoffs: + + if (!P_TryMove(newmobj, newmobj->x, newmobj->y, false)) + { + // kill it immediately + P_DamageMobj(newmobj, actor, actor, 10000); + return; + } + + P_SetTarget(&newmobj->target, actor->target); + A_SkullAttack(newmobj); +} + +// +// A_PainAttack +// Spawn a lost soul and launch it at the target +// + +void A_PainAttack(mobj_t *actor) +{ + if (!actor->target) + return; + A_FaceTarget(actor); + A_PainShootSkull(actor, actor->angle); +} + +void A_PainDie(mobj_t *actor) +{ + A_Fall(actor); + A_PainShootSkull(actor, actor->angle+ANG90); + A_PainShootSkull(actor, actor->angle+ANG180); + A_PainShootSkull(actor, actor->angle+ANG270); +} + +void A_Scream(mobj_t *actor) +{ + int sound; + + switch (actor->info->deathsound) + { + case 0: + return; + + case sfx_podth1: + case sfx_podth2: + case sfx_podth3: + sound = sfx_podth1 + P_Random(pr_scream)%3; + break; + + case sfx_bgdth1: + case sfx_bgdth2: + sound = sfx_bgdth1 + P_Random(pr_scream)%2; + break; + + default: + sound = actor->info->deathsound; + break; + } + + // Check for bosses. + if (actor->type==MT_SPIDER || actor->type == MT_CYBORG) + S_StartSound(NULL, sound); // full volume + else + S_StartSound(actor, sound); +} + +void A_XScream(mobj_t *actor) +{ + S_StartSound(actor, sfx_slop); +} + +void A_Pain(mobj_t *actor) +{ + if (actor->info->painsound) + S_StartSound(actor, actor->info->painsound); +} + +void A_Fall(mobj_t *actor) +{ + // actor is on ground, it can be walked over + actor->flags &= ~MF_SOLID; +} + +// +// A_Explode +// +void A_Explode(mobj_t *thingy) +{ + P_RadiusAttack( thingy, thingy->target, 128 ); +} + +// +// A_BossDeath +// Possibly trigger special effects +// if on first boss level +// + +void A_BossDeath(mobj_t *mo) +{ + thinker_t *th; + line_t junk; + int i; + + if (gamemode == commercial) + { + if (gamemap != 7) + return; + + if ((mo->type != MT_FATSO) + && (mo->type != MT_BABY)) + return; + } + else + { + // e6y + // Additional check of gameepisode is necessary, because + // there is no right or wrong solution for E4M6 in original EXEs, + // there's nothing to emulate. + if (comp[comp_666] && gameepisode < 4) + { + // e6y + // Only following checks are present in doom2.exe ver. 1.666 and 1.9 + // instead of separate checks for each episode in doomult.exe, plutonia.exe and tnt.exe + // There is no more desync on doom.wad\episode3.lmp + // http://www.doomworld.com/idgames/index.php?id=6909 + if (gamemap != 8) + return; + if (mo->type == MT_BRUISER && gameepisode != 1) + return; + } + else + { + switch(gameepisode) + { + case 1: + if (gamemap != 8) + return; + + if (mo->type != MT_BRUISER) + return; + break; + + case 2: + if (gamemap != 8) + return; + + if (mo->type != MT_CYBORG) + return; + break; + + case 3: + if (gamemap != 8) + return; + + if (mo->type != MT_SPIDER) + return; + + break; + + case 4: + switch(gamemap) + { + case 6: + if (mo->type != MT_CYBORG) + return; + break; + + case 8: + if (mo->type != MT_SPIDER) + return; + break; + + default: + return; + break; + } + break; + + default: + if (gamemap != 8) + return; + break; + } + } + + } + + // make sure there is a player alive for victory + for (i=0; i 0) + break; + + if (i==MAXPLAYERS) + return; // no one left alive, so do not end game + + // scan the remaining thinkers to see + // if all bosses are dead + for (th = thinkercap.next ; th != &thinkercap ; th=th->next) + if (th->function == P_MobjThinker) + { + mobj_t *mo2 = (mobj_t *) th; + if (mo2 != mo && mo2->type == mo->type && mo2->health > 0) + return; // other boss not dead + } + + // victory! + if ( gamemode == commercial) + { + if (gamemap == 7) + { + if (mo->type == MT_FATSO) + { + junk.tag = 666; + EV_DoFloor(&junk,lowerFloorToLowest); + return; + } + + if (mo->type == MT_BABY) + { + junk.tag = 667; + EV_DoFloor(&junk,raiseToTexture); + return; + } + } + } + else + { + switch(gameepisode) + { + case 1: + junk.tag = 666; + EV_DoFloor(&junk, lowerFloorToLowest); + return; + break; + + case 4: + switch(gamemap) + { + case 6: + junk.tag = 666; + EV_DoDoor(&junk, blazeOpen); + return; + break; + + case 8: + junk.tag = 666; + EV_DoFloor(&junk, lowerFloorToLowest); + return; + break; + } + } + } + G_ExitLevel(); +} + + +void A_Hoof (mobj_t* mo) +{ + S_StartSound(mo, sfx_hoof); + A_Chase(mo); +} + +void A_Metal(mobj_t *mo) +{ + S_StartSound(mo, sfx_metal); + A_Chase(mo); +} + +void A_BabyMetal(mobj_t *mo) +{ + S_StartSound(mo, sfx_bspwlk); + A_Chase(mo); +} + +void A_OpenShotgun2(player_t *player, pspdef_t *psp) +{ + S_StartSound(player->mo, sfx_dbopn); +} + +void A_LoadShotgun2(player_t *player, pspdef_t *psp) +{ + S_StartSound(player->mo, sfx_dbload); +} + +void A_CloseShotgun2(player_t *player, pspdef_t *psp) +{ + S_StartSound(player->mo, sfx_dbcls); + A_ReFire(player,psp); +} + +// killough 2/7/98: Remove limit on icon landings: +mobj_t **braintargets; +int numbraintargets_alloc; +int numbraintargets; + +struct brain_s brain; // killough 3/26/98: global state of boss brain + +// killough 3/26/98: initialize icon landings at level startup, +// rather than at boss wakeup, to prevent savegame-related crashes + +void P_SpawnBrainTargets(void) // killough 3/26/98: renamed old function +{ + thinker_t *thinker; + + // find all the target spots + numbraintargets = 0; + brain.targeton = 0; + brain.easy = 0; // killough 3/26/98: always init easy to 0 + + for (thinker = thinkercap.next ; + thinker != &thinkercap ; + thinker = thinker->next) + if (thinker->function == P_MobjThinker) + { + mobj_t *m = (mobj_t *) thinker; + + if (m->type == MT_BOSSTARGET ) + { // killough 2/7/98: remove limit on icon landings: + if (numbraintargets >= numbraintargets_alloc) + braintargets = realloc(braintargets, + (numbraintargets_alloc = numbraintargets_alloc ? + numbraintargets_alloc*2 : 32) *sizeof *braintargets); + braintargets[numbraintargets++] = m; + } + } +} + +void A_BrainAwake(mobj_t *mo) +{ + S_StartSound(NULL,sfx_bossit); // killough 3/26/98: only generates sound now +} + +void A_BrainPain(mobj_t *mo) +{ + S_StartSound(NULL,sfx_bospn); +} + +void A_BrainScream(mobj_t *mo) +{ + int x; + for (x=mo->x - 196*FRACUNIT ; x< mo->x + 320*FRACUNIT ; x+= FRACUNIT*8) + { + int y = mo->y - 320*FRACUNIT; + int z = 128 + P_Random(pr_brainscream)*2*FRACUNIT; + mobj_t *th = P_SpawnMobj (x,y,z, MT_ROCKET); + th->momz = P_Random(pr_brainscream)*512; + P_SetMobjState(th, S_BRAINEXPLODE1); + th->tics -= P_Random(pr_brainscream)&7; + if (th->tics < 1) + th->tics = 1; + } + S_StartSound(NULL,sfx_bosdth); +} + +void A_BrainExplode(mobj_t *mo) +{ // killough 5/5/98: remove dependence on order of evaluation: + int t = P_Random(pr_brainexp); + int x = mo->x + (t - P_Random(pr_brainexp))*2048; + int y = mo->y; + int z = 128 + P_Random(pr_brainexp)*2*FRACUNIT; + mobj_t *th = P_SpawnMobj(x,y,z, MT_ROCKET); + th->momz = P_Random(pr_brainexp)*512; + P_SetMobjState(th, S_BRAINEXPLODE1); + th->tics -= P_Random(pr_brainexp)&7; + if (th->tics < 1) + th->tics = 1; +} + +void A_BrainDie(mobj_t *mo) +{ + G_ExitLevel(); +} + +void A_BrainSpit(mobj_t *mo) +{ + mobj_t *targ, *newmobj; + + if (!numbraintargets) // killough 4/1/98: ignore if no targets + return; + + brain.easy ^= 1; // killough 3/26/98: use brain struct + if (gameskill <= sk_easy && !brain.easy) + return; + + // shoot a cube at current target + targ = braintargets[brain.targeton++]; // killough 3/26/98: + brain.targeton %= numbraintargets; // Use brain struct for targets + + // spawn brain missile + newmobj = P_SpawnMissile(mo, targ, MT_SPAWNSHOT); + P_SetTarget(&newmobj->target, targ); + newmobj->reactiontime = (short)(((targ->y-mo->y)/newmobj->momy)/newmobj->state->tics); + + // killough 7/18/98: brain friendliness is transferred + newmobj->flags = (newmobj->flags & ~MF_FRIEND) | (mo->flags & MF_FRIEND); + + // killough 8/29/98: add to appropriate thread + P_UpdateThinker(&newmobj->thinker); + + S_StartSound(NULL, sfx_bospit); +} + +// travelling cube sound +void A_SpawnSound(mobj_t *mo) +{ + S_StartSound(mo,sfx_boscub); + A_SpawnFly(mo); +} + +void A_SpawnFly(mobj_t *mo) +{ + mobj_t *newmobj; + mobj_t *fog; + mobj_t *targ; + int r; + mobjtype_t type; + + if (--mo->reactiontime) + return; // still flying + + targ = mo->target; + + // First spawn teleport fog. + fog = P_SpawnMobj(targ->x, targ->y, targ->z, MT_SPAWNFIRE); + S_StartSound(fog, sfx_telept); + + // Randomly select monster to spawn. + r = P_Random(pr_spawnfly); + + // Probability distribution (kind of :), decreasing likelihood. + if ( r<50 ) + type = MT_TROOP; + else if (r<90) + type = MT_SERGEANT; + else if (r<120) + type = MT_SHADOWS; + else if (r<130) + type = MT_PAIN; + else if (r<160) + type = MT_HEAD; + else if (r<162) + type = MT_VILE; + else if (r<172) + type = MT_UNDEAD; + else if (r<192) + type = MT_BABY; + else if (r<222) + type = MT_FATSO; + else if (r<246) + type = MT_KNIGHT; + else + type = MT_BRUISER; + + newmobj = P_SpawnMobj(targ->x, targ->y, targ->z, type); + + /* killough 7/18/98: brain friendliness is transferred */ + newmobj->flags = (newmobj->flags & ~MF_FRIEND) | (mo->flags & MF_FRIEND); + + /* killough 8/29/98: add to appropriate thread */ + P_UpdateThinker(&newmobj->thinker); + + if (P_LookForTargets(newmobj,true)) /* killough 9/4/98 */ + P_SetMobjState(newmobj, newmobj->info->seestate); + + // telefrag anything in this spot + P_TeleportMove(newmobj, newmobj->x, newmobj->y, true); /* killough 8/9/98 */ + + // remove self (i.e., cube). + P_RemoveMobj(mo); +} + +void A_PlayerScream(mobj_t *mo) +{ + int sound = sfx_pldeth; // Default death sound. + if (gamemode != shareware && mo->health < -50) + sound = sfx_pdiehi; // IF THE PLAYER DIES LESS THAN -50% WITHOUT GIBBING + S_StartSound(mo, sound); +} + +/* cph - MBF-added codepointer functions */ + +// killough 11/98: kill an object +void A_Die(mobj_t *actor) +{ + P_DamageMobj(actor, NULL, NULL, actor->health); +} + +// +// A_Detonate +// killough 8/9/98: same as A_Explode, except that the damage is variable +// + +void A_Detonate(mobj_t *mo) +{ + P_RadiusAttack(mo, mo->target, mo->info->damage); +} + +// +// killough 9/98: a mushroom explosion effect, sorta :) +// Original idea: Linguica +// + +void A_Mushroom(mobj_t *actor) +{ + int i, j, n = actor->info->damage; + + A_Explode(actor); // First make normal explosion + + // Now launch mushroom cloud + for (i = -n; i <= n; i += 8) + for (j = -n; j <= n; j += 8) + { + mobj_t target = *actor, *mo; + target.x += i << FRACBITS; // Aim in many directions from source + target.y += j << FRACBITS; + target.z += P_AproxDistance(i,j) << (FRACBITS+2); // Aim up fairly high + mo = P_SpawnMissile(actor, &target, MT_FATSHOT); // Launch fireball + mo->momx >>= 1; + mo->momy >>= 1; // Slow it down a bit + mo->momz >>= 1; + mo->flags &= ~MF_NOGRAVITY; // Make debris fall under gravity + } +} + +// +// killough 11/98 +// +// The following were inspired by Len Pitre +// +// A small set of highly-sought-after code pointers +// + +void A_Spawn(mobj_t *mo) +{ + if (mo->state->misc1) + { + /* mobj_t *newmobj = */ + P_SpawnMobj(mo->x, mo->y, (mo->state->misc2 << FRACBITS) + mo->z, + mo->state->misc1 - 1); + /* CPhipps - no friendlyness (yet) + newmobj->flags = (newmobj->flags & ~MF_FRIEND) | (mo->flags & MF_FRIEND); + */ + } +} + +void A_Turn(mobj_t *mo) +{ + mo->angle += (unsigned int)(((uint_64_t) mo->state->misc1 << 32) / 360); +} + +void A_Face(mobj_t *mo) +{ + mo->angle = (unsigned int)(((uint_64_t) mo->state->misc1 << 32) / 360); +} + +void A_Scratch(mobj_t *mo) +{ + mo->target && (A_FaceTarget(mo), P_CheckMeleeRange(mo)) ? + mo->state->misc2 ? S_StartSound(mo, mo->state->misc2) : (void) 0, + P_DamageMobj(mo->target, mo, mo, mo->state->misc1) : (void) 0; +} + +void A_PlaySound(mobj_t *mo) +{ + S_StartSound(mo->state->misc2 ? NULL : mo, mo->state->misc1); +} + +void A_RandomJump(mobj_t *mo) +{ + if (P_Random(pr_randomjump) < mo->state->misc2) + P_SetMobjState(mo, mo->state->misc1); +} + +// +// This allows linedef effects to be activated inside deh frames. +// + +void A_LineEffect(mobj_t *mo) +{ + static line_t junk; + player_t player; + player_t *oldplayer; + junk = *lines; + oldplayer = mo->player; + mo->player = &player; + player.health = 100; + junk.special = (short)mo->state->misc1; + if (!junk.special) + return; + junk.tag = (short)mo->state->misc2; + if (!P_UseSpecialLine(mo, &junk, 0)) + P_CrossSpecialLine(&junk, 0, mo); + mo->state->misc1 = junk.special; + mo->player = oldplayer; +} diff --git a/src/p_enemy.h b/src/p_enemy.h new file mode 100644 index 0000000..3c85861 --- /dev/null +++ b/src/p_enemy.h @@ -0,0 +1,118 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Enemy thinking, AI. + * Action Pointer Functions + * that are associated with states/frames. + * + *-----------------------------------------------------------------------------*/ + +#ifndef __P_ENEMY__ +#define __P_ENEMY__ + +#include "p_mobj.h" + +void P_NoiseAlert (mobj_t *target, mobj_t *emmiter); +void P_SpawnBrainTargets(void); /* killough 3/26/98: spawn icon landings */ + +extern struct brain_s { /* killough 3/26/98: global state of boss brain */ + int easy, targeton; +} brain; + +// ******************************************************************** +// Function addresses or Code Pointers +// ******************************************************************** +// These function addresses are the Code Pointers that have been +// modified for years by Dehacked enthusiasts. The new BEX format +// allows more extensive changes (see d_deh.c) + +// Doesn't work with g++, needs actionf_p1 +void A_Explode(); +void A_Pain(); +void A_PlayerScream(); +void A_Fall(); +void A_XScream(); +void A_Look(); +void A_Chase(); +void A_FaceTarget(); +void A_PosAttack(); +void A_Scream(); +void A_SPosAttack(); +void A_VileChase(); +void A_VileStart(); +void A_VileTarget(); +void A_VileAttack(); +void A_StartFire(); +void A_Fire(); +void A_FireCrackle(); +void A_Tracer(); +void A_SkelWhoosh(); +void A_SkelFist(); +void A_SkelMissile(); +void A_FatRaise(); +void A_FatAttack1(); +void A_FatAttack2(); +void A_FatAttack3(); +void A_BossDeath(); +void A_CPosAttack(); +void A_CPosRefire(); +void A_TroopAttack(); +void A_SargAttack(); +void A_HeadAttack(); +void A_BruisAttack(); +void A_SkullAttack(); +void A_Metal(); +void A_SpidRefire(); +void A_BabyMetal(); +void A_BspiAttack(); +void A_Hoof(); +void A_CyberAttack(); +void A_PainAttack(); +void A_PainDie(); +void A_KeenDie(); +void A_BrainPain(); +void A_BrainScream(); +void A_BrainDie(); +void A_BrainAwake(); +void A_BrainSpit(); +void A_SpawnSound(); +void A_SpawnFly(); +void A_BrainExplode(); +void A_Die(); +void A_Detonate(); /* killough 8/9/98: detonate a bomb or other device */ +void A_Mushroom(); /* killough 10/98: mushroom effect */ +void A_Spawn(); // killough 11/98 +void A_Turn(); // killough 11/98 +void A_Face(); // killough 11/98 +void A_Scratch(); // killough 11/98 +void A_PlaySound(); // killough 11/98 +void A_RandomJump(); // killough 11/98 +void A_LineEffect(); // killough 11/98 + +#endif // __P_ENEMY__ diff --git a/src/p_floor.c b/src/p_floor.c new file mode 100644 index 0000000..ba55fdf --- /dev/null +++ b/src/p_floor.c @@ -0,0 +1,1042 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * General plane mover and floor mover action routines + * Floor motion, pure changer types, raising stairs. donuts, elevators + * + *-----------------------------------------------------------------------------*/ + +#include "doomstat.h" +#include "r_main.h" +#include "p_map.h" +#include "p_spec.h" +#include "p_tick.h" +#include "s_sound.h" +#include "sounds.h" + +/////////////////////////////////////////////////////////////////////// +// +// Plane (floor or ceiling), Floor motion and Elevator action routines +// +/////////////////////////////////////////////////////////////////////// + +// +// T_MovePlane() +// +// Move a plane (floor or ceiling) and check for crushing. Called +// every tick by all actions that move floors or ceilings. +// +// Passed the sector to move a plane in, the speed to move it at, +// the dest height it is to achieve, whether it crushes obstacles, +// whether it moves a floor or ceiling, and the direction up or down +// to move. +// +// Returns a result_e: +// ok - plane moved normally, has not achieved destination yet +// pastdest - plane moved normally and is now at destination height +// crushed - plane encountered an obstacle, is holding until removed +// +result_e T_MovePlane +( sector_t* sector, + fixed_t speed, + fixed_t dest, + boolean crush, + int floorOrCeiling, + int direction ) +{ + boolean flag; + fixed_t lastpos; + fixed_t destheight; //jff 02/04/98 used to keep floors/ceilings + // from moving thru each other + + switch(floorOrCeiling) + { + case 0: + // Moving a floor + switch(direction) + { + case -1: + // Moving a floor down + if (sector->floorheight - speed < dest) + { + lastpos = sector->floorheight; + sector->floorheight = dest; + flag = P_CheckSector(sector,crush); //jff 3/19/98 use faster chk + if (flag == true) + { + sector->floorheight =lastpos; + P_CheckSector(sector,crush); //jff 3/19/98 use faster chk + } + return pastdest; + } + else + { + lastpos = sector->floorheight; + sector->floorheight -= speed; + flag = P_CheckSector(sector,crush); //jff 3/19/98 use faster chk + /* cph - make more compatible with original Doom, by + * reintroducing this code. This means floors can't lower + * if objects are stuck in the ceiling */ + if ((flag == true) && comp[comp_floors]) { + sector->floorheight = lastpos; + P_ChangeSector(sector,crush); + return crushed; + } + } + break; + + case 1: + // Moving a floor up + // jff 02/04/98 keep floor from moving thru ceilings + // jff 2/22/98 weaken check to demo_compatibility + destheight = (comp[comp_floors] || destceilingheight)? + dest : sector->ceilingheight; + if (sector->floorheight + speed > destheight) + { + lastpos = sector->floorheight; + sector->floorheight = destheight; + flag = P_CheckSector(sector,crush); //jff 3/19/98 use faster chk + if (flag == true) + { + sector->floorheight = lastpos; + P_CheckSector(sector,crush); //jff 3/19/98 use faster chk + } + return pastdest; + } + else + { + // crushing is possible + lastpos = sector->floorheight; + sector->floorheight += speed; + flag = P_CheckSector(sector,crush); //jff 3/19/98 use faster chk + if (flag == true) + { + /* jff 1/25/98 fix floor crusher */ + if (comp[comp_floors]) { + if (crush == true) + return crushed; + } + sector->floorheight = lastpos; + P_CheckSector(sector,crush); //jff 3/19/98 use faster chk + return crushed; + } + } + break; + } + break; + + case 1: + // moving a ceiling + switch(direction) + { + case -1: + // moving a ceiling down + // jff 02/04/98 keep ceiling from moving thru floors + // jff 2/22/98 weaken check to demo_compatibility + destheight = (comp[comp_floors] || dest>sector->floorheight)? + dest : sector->floorheight; + if (sector->ceilingheight - speed < destheight) + { + lastpos = sector->ceilingheight; + sector->ceilingheight = destheight; + flag = P_CheckSector(sector,crush); //jff 3/19/98 use faster chk + + if (flag == true) + { + sector->ceilingheight = lastpos; + P_CheckSector(sector,crush); //jff 3/19/98 use faster chk + } + return pastdest; + } + else + { + // crushing is possible + lastpos = sector->ceilingheight; + sector->ceilingheight -= speed; + flag = P_CheckSector(sector,crush); //jff 3/19/98 use faster chk + + if (flag == true) + { + if (crush == true) + return crushed; + sector->ceilingheight = lastpos; + P_CheckSector(sector,crush); //jff 3/19/98 use faster chk + return crushed; + } + } + break; + + case 1: + // moving a ceiling up + if (sector->ceilingheight + speed > dest) + { + lastpos = sector->ceilingheight; + sector->ceilingheight = dest; + flag = P_CheckSector(sector,crush); //jff 3/19/98 use faster chk + if (flag == true) + { + sector->ceilingheight = lastpos; + P_CheckSector(sector,crush); //jff 3/19/98 use faster chk + } + return pastdest; + } + else + { + lastpos = sector->ceilingheight; + sector->ceilingheight += speed; + flag = P_CheckSector(sector,crush); //jff 3/19/98 use faster chk + } + break; + } + break; + } + return ok; +} + +// +// T_MoveFloor() +// +// Move a floor to it's destination (up or down). +// Called once per tick for each moving floor. +// +// Passed a floormove_t structure that contains all pertinent info about the +// move. See P_SPEC.H for fields. +// No return. +// +// jff 02/08/98 all cases with labels beginning with gen added to support +// generalized line type behaviors. + +void T_MoveFloor(floormove_t* floor) +{ + result_e res; + + res = T_MovePlane // move the floor + ( + floor->sector, + floor->speed, + floor->floordestheight, + floor->crush, + 0, + floor->direction + ); + + if (!(leveltime&7)) // make the floormove sound + S_StartSound((mobj_t *)&floor->sector->soundorg, sfx_stnmov); + + if (res == pastdest) // if destination height is reached + { + if (floor->direction == 1) // going up + { + switch(floor->type) // handle texture/type changes + { + case donutRaise: + floor->sector->special = floor->newspecial; + floor->sector->floorpic = floor->texture; + break; + case genFloorChgT: + case genFloorChg0: + floor->sector->special = floor->newspecial; + //jff add to fix bug in special transfers from changes + floor->sector->oldspecial = floor->oldspecial; + //fall thru + case genFloorChg: + floor->sector->floorpic = floor->texture; + break; + default: + break; + } + } + else if (floor->direction == -1) // going down + { + switch(floor->type) // handle texture/type changes + { + case lowerAndChange: + floor->sector->special = floor->newspecial; + //jff add to fix bug in special transfers from changes + floor->sector->oldspecial = floor->oldspecial; + floor->sector->floorpic = floor->texture; + break; + case genFloorChgT: + case genFloorChg0: + floor->sector->special = floor->newspecial; + //jff add to fix bug in special transfers from changes + floor->sector->oldspecial = floor->oldspecial; + //fall thru + case genFloorChg: + floor->sector->floorpic = floor->texture; + break; + default: + break; + } + } + + floor->sector->floordata = NULL; //jff 2/22/98 + P_RemoveThinker(&floor->thinker);//remove this floor from list of movers + + //jff 2/26/98 implement stair retrigger lockout while still building + // note this only applies to the retriggerable generalized stairs + + if (floor->sector->stairlock==-2) // if this sector is stairlocked + { + sector_t *sec = floor->sector; + sec->stairlock=-1; // thinker done, promote lock to -1 + + while (sec->prevsec!=-1 && sectors[sec->prevsec].stairlock!=-2) + sec = §ors[sec->prevsec]; // search for a non-done thinker + if (sec->prevsec==-1) // if all thinkers previous are done + { + sec = floor->sector; // search forward + while (sec->nextsec!=-1 && sectors[sec->nextsec].stairlock!=-2) + sec = §ors[sec->nextsec]; + if (sec->nextsec==-1) // if all thinkers ahead are done too + { + while (sec->prevsec!=-1) // clear all locks + { + sec->stairlock = 0; + sec = §ors[sec->prevsec]; + } + sec->stairlock = 0; + } + } + } + + // make floor stop sound + S_StartSound((mobj_t *)&floor->sector->soundorg, sfx_pstop); + } +} + +// +// T_MoveElevator() +// +// Move an elevator to it's destination (up or down) +// Called once per tick for each moving floor. +// +// Passed an elevator_t structure that contains all pertinent info about the +// move. See P_SPEC.H for fields. +// No return. +// +// jff 02/22/98 added to support parallel floor/ceiling motion +// +void T_MoveElevator(elevator_t* elevator) +{ + result_e res; + + if (elevator->direction<0) // moving down + { + res = T_MovePlane //jff 4/7/98 reverse order of ceiling/floor + ( + elevator->sector, + elevator->speed, + elevator->ceilingdestheight, + 0, + 1, // move floor + elevator->direction + ); + if (res==ok || res==pastdest) // jff 4/7/98 don't move ceil if blocked + T_MovePlane + ( + elevator->sector, + elevator->speed, + elevator->floordestheight, + 0, + 0, // move ceiling + elevator->direction + ); + } + else // up + { + res = T_MovePlane //jff 4/7/98 reverse order of ceiling/floor + ( + elevator->sector, + elevator->speed, + elevator->floordestheight, + 0, + 0, // move ceiling + elevator->direction + ); + if (res==ok || res==pastdest) // jff 4/7/98 don't move floor if blocked + T_MovePlane + ( + elevator->sector, + elevator->speed, + elevator->ceilingdestheight, + 0, + 1, // move floor + elevator->direction + ); + } + + // make floor move sound + if (!(leveltime&7)) + S_StartSound((mobj_t *)&elevator->sector->soundorg, sfx_stnmov); + + if (res == pastdest) // if destination height acheived + { + elevator->sector->floordata = NULL; //jff 2/22/98 + elevator->sector->ceilingdata = NULL; //jff 2/22/98 + P_RemoveThinker(&elevator->thinker); // remove elevator from actives + + // make floor stop sound + S_StartSound((mobj_t *)&elevator->sector->soundorg, sfx_pstop); + } +} + +/////////////////////////////////////////////////////////////////////// +// +// Floor motion linedef handlers +// +/////////////////////////////////////////////////////////////////////// + +// +// EV_DoFloor() +// +// Handle regular and extended floor types +// +// Passed the line that activated the floor and the type of floor motion +// Returns true if a thinker was created. +// +int EV_DoFloor +( line_t* line, + floor_e floortype ) +{ + int secnum; + int rtn; + int i; + sector_t* sec; + floormove_t* floor; + + secnum = -1; + rtn = 0; + // move all floors with the same tag as the linedef + while ((secnum = P_FindSectorFromLineTag(line,secnum)) >= 0) + { + sec = §ors[secnum]; + + // Don't start a second thinker on the same floor + if (P_SectorActive(floor_special,sec)) //jff 2/23/98 + continue; + + // new floor thinker + rtn = 1; + floor = Z_Malloc (sizeof(*floor), PU_LEVSPEC, 0); + memset(floor, 0, sizeof(*floor)); + P_AddThinker (&floor->thinker); + sec->floordata = floor; //jff 2/22/98 + floor->thinker.function = T_MoveFloor; + floor->type = floortype; + floor->crush = false; + + // setup the thinker according to the linedef type + switch(floortype) + { + case lowerFloor: + floor->direction = -1; + floor->sector = sec; + floor->speed = FLOORSPEED; + floor->floordestheight = P_FindHighestFloorSurrounding(sec); + break; + + //jff 02/03/30 support lowering floor by 24 absolute + case lowerFloor24: + floor->direction = -1; + floor->sector = sec; + floor->speed = FLOORSPEED; + floor->floordestheight = floor->sector->floorheight + 24 * FRACUNIT; + break; + + //jff 02/03/30 support lowering floor by 32 absolute (fast) + case lowerFloor32Turbo: + floor->direction = -1; + floor->sector = sec; + floor->speed = FLOORSPEED*4; + floor->floordestheight = floor->sector->floorheight + 32 * FRACUNIT; + break; + + case lowerFloorToLowest: + floor->direction = -1; + floor->sector = sec; + floor->speed = FLOORSPEED; + floor->floordestheight = P_FindLowestFloorSurrounding(sec); + break; + + //jff 02/03/30 support lowering floor to next lowest floor + case lowerFloorToNearest: + floor->direction = -1; + floor->sector = sec; + floor->speed = FLOORSPEED; + floor->floordestheight = + P_FindNextLowestFloor(sec,floor->sector->floorheight); + break; + + case turboLower: + floor->direction = -1; + floor->sector = sec; + floor->speed = FLOORSPEED * 4; + floor->floordestheight = P_FindHighestFloorSurrounding(sec); + if (floor->floordestheight != sec->floorheight) + floor->floordestheight += 8*FRACUNIT; + break; + + case raiseFloorCrush: + floor->crush = true; + case raiseFloor: + floor->direction = 1; + floor->sector = sec; + floor->speed = FLOORSPEED; + floor->floordestheight = P_FindLowestCeilingSurrounding(sec); + if (floor->floordestheight > sec->ceilingheight) + floor->floordestheight = sec->ceilingheight; + floor->floordestheight -= (8*FRACUNIT)*(floortype == raiseFloorCrush); + break; + + case raiseFloorTurbo: + floor->direction = 1; + floor->sector = sec; + floor->speed = FLOORSPEED*4; + floor->floordestheight = P_FindNextHighestFloor(sec,sec->floorheight); + break; + + case raiseFloorToNearest: + floor->direction = 1; + floor->sector = sec; + floor->speed = FLOORSPEED; + floor->floordestheight = P_FindNextHighestFloor(sec,sec->floorheight); + break; + + case raiseFloor24: + floor->direction = 1; + floor->sector = sec; + floor->speed = FLOORSPEED; + floor->floordestheight = floor->sector->floorheight + 24 * FRACUNIT; + break; + + // jff 2/03/30 support straight raise by 32 (fast) + case raiseFloor32Turbo: + floor->direction = 1; + floor->sector = sec; + floor->speed = FLOORSPEED*4; + floor->floordestheight = floor->sector->floorheight + 32 * FRACUNIT; + break; + + case raiseFloor512: + floor->direction = 1; + floor->sector = sec; + floor->speed = FLOORSPEED; + floor->floordestheight = floor->sector->floorheight + 512 * FRACUNIT; + break; + + case raiseFloor24AndChange: + floor->direction = 1; + floor->sector = sec; + floor->speed = FLOORSPEED; + floor->floordestheight = floor->sector->floorheight + 24 * FRACUNIT; + sec->floorpic = line->frontsector->floorpic; + sec->special = line->frontsector->special; + //jff 3/14/98 transfer both old and new special + sec->oldspecial = line->frontsector->oldspecial; + break; + + case raiseToTexture: + { + int minsize = INT_MAX; + side_t* side; + + /* jff 3/13/98 no ovf */ + if (!comp[comp_model]) minsize = 32000<direction = 1; + floor->sector = sec; + floor->speed = FLOORSPEED; + for (i = 0; i < sec->linecount; i++) + { + if (twoSided (secnum, i) ) + { + side = getSide(secnum,i,0); + // jff 8/14/98 don't scan texture 0, its not real + if (side->bottomtexture > 0 || + (comp[comp_model] && !side->bottomtexture)) + if (textureheight[side->bottomtexture] < minsize) + minsize = textureheight[side->bottomtexture]; + side = getSide(secnum,i,1); + // jff 8/14/98 don't scan texture 0, its not real + if (side->bottomtexture > 0 || + (comp[comp_model] && !side->bottomtexture)) + if (textureheight[side->bottomtexture] < minsize) + minsize = textureheight[side->bottomtexture]; + } + } + if (comp[comp_model]) + floor->floordestheight = floor->sector->floorheight + minsize; + else + { + floor->floordestheight = + (floor->sector->floorheight>>FRACBITS) + (minsize>>FRACBITS); + if (floor->floordestheight>32000) + floor->floordestheight = 32000; //jff 3/13/98 do not + floor->floordestheight<<=FRACBITS; // allow height overflow + } + } + break; + + case lowerAndChange: + floor->direction = -1; + floor->sector = sec; + floor->speed = FLOORSPEED; + floor->floordestheight = P_FindLowestFloorSurrounding(sec); + floor->texture = sec->floorpic; + + // jff 1/24/98 make sure floor->newspecial gets initialized + // in case no surrounding sector is at floordestheight + // --> should not affect compatibility <-- + floor->newspecial = sec->special; + //jff 3/14/98 transfer both old and new special + floor->oldspecial = sec->oldspecial; + + //jff 5/23/98 use model subroutine to unify fixes and handling + sec = P_FindModelFloorSector(floor->floordestheight,sec-sectors); + if (sec) + { + floor->texture = sec->floorpic; + floor->newspecial = sec->special; + //jff 3/14/98 transfer both old and new special + floor->oldspecial = sec->oldspecial; + } + break; + default: + break; + } + } + return rtn; +} + +// +// EV_DoChange() +// +// Handle pure change types. These change floor texture and sector type +// by trigger or numeric model without moving the floor. +// +// The linedef causing the change and the type of change is passed +// Returns true if any sector changes +// +// jff 3/15/98 added to better support generalized sector types +// +int EV_DoChange +( line_t* line, + change_e changetype ) +{ + int secnum; + int rtn; + sector_t* sec; + sector_t* secm; + + secnum = -1; + rtn = 0; + // change all sectors with the same tag as the linedef + while ((secnum = P_FindSectorFromLineTag(line,secnum)) >= 0) + { + sec = §ors[secnum]; + + rtn = 1; + + // handle trigger or numeric change type + switch(changetype) + { + case trigChangeOnly: + sec->floorpic = line->frontsector->floorpic; + sec->special = line->frontsector->special; + sec->oldspecial = line->frontsector->oldspecial; + break; + case numChangeOnly: + secm = P_FindModelFloorSector(sec->floorheight,secnum); + if (secm) // if no model, no change + { + sec->floorpic = secm->floorpic; + sec->special = secm->special; + sec->oldspecial = secm->oldspecial; + } + break; + default: + break; + } + } + return rtn; +} + +/* + * EV_BuildStairs() + * + * Handles staircase building. A sequence of sectors chosen by algorithm + * rise at a speed indicated to a height that increases by the stepsize + * each step. + * + * Passed the linedef triggering the stairs and the type of stair rise + * Returns true if any thinkers are created + * + * cph 2001/09/21 - compatibility nightmares again + * There are three different ways this function has, during its history, stepped + * through all the stairs to be triggered by the single switch + * - original Doom used a linear P_FindSectorFromLineTag, but failed to preserve + * the index of the previous sector found, so instead it would restart its + * linear search from the last sector of the previous staircase + * - MBF/PrBoom with comp_stairs fail to emulate this, because their + * P_FindSectorFromLineTag is a chained hash table implementation. Instead they + * start following the hash chain from the last sector of the previous + * staircase, which will (probably) have the wrong tag, so they miss any further + * stairs + * - Boom fixed the bug, and MBF/PrBoom without comp_stairs work right + */ +static inline int P_FindSectorFromLineTagWithLowerBound +(line_t* l, int start, int min) +{ + /* Emulate original Doom's linear lower-bounded P_FindSectorFromLineTag + * as needed */ + do { + start = P_FindSectorFromLineTag(l,start); + } while (start >= 0 && start <= min); + return start; +} + +int EV_BuildStairs +( line_t* line, + stair_e type ) +{ + /* cph 2001/09/22 - cleaned up this function to save my sanity. A separate + * outer loop index makes the logic much cleared, and local variables moved + * into the inner blocks helps too */ + int ssec = -1; + int minssec = -1; + int rtn = 0; + + // start a stair at each sector tagged the same as the linedef + while ((ssec = P_FindSectorFromLineTagWithLowerBound(line,ssec,minssec)) >= 0) + { + int secnum = ssec; + sector_t* sec = §ors[secnum]; + + // don't start a stair if the first step's floor is already moving + if (!P_SectorActive(floor_special,sec)) { //jff 2/22/98 + floormove_t* floor; + int texture, height; + fixed_t stairsize; + fixed_t speed; + int ok; + + // create new floor thinker for first step + rtn = 1; + floor = Z_Malloc (sizeof(*floor), PU_LEVSPEC, 0); + memset(floor, 0, sizeof(*floor)); + P_AddThinker (&floor->thinker); + sec->floordata = floor; + floor->thinker.function = T_MoveFloor; + floor->direction = 1; + floor->sector = sec; + floor->type = buildStair; //jff 3/31/98 do not leave uninited + + // set up the speed and stepsize according to the stairs type + switch(type) + { + default: // killough -- prevent compiler warning + case build8: + speed = FLOORSPEED/4; + stairsize = 8*FRACUNIT; + if (!demo_compatibility) + floor->crush = false; //jff 2/27/98 fix uninitialized crush field + break; + case turbo16: + speed = FLOORSPEED*4; + stairsize = 16*FRACUNIT; + if (!demo_compatibility) + floor->crush = true; //jff 2/27/98 fix uninitialized crush field + break; + } + floor->speed = speed; + height = sec->floorheight + stairsize; + floor->floordestheight = height; + + texture = sec->floorpic; + + // Find next sector to raise + // 1. Find 2-sided line with same sector side[0] (lowest numbered) + // 2. Other side is the next sector to raise + // 3. Unless already moving, or different texture, then stop building + do + { + int i; + ok = 0; + + for (i = 0;i < sec->linecount;i++) + { + sector_t* tsec = (sec->lines[i])->frontsector; + int newsecnum; + if ( !((sec->lines[i])->flags & ML_TWOSIDED) ) + continue; + + newsecnum = tsec-sectors; + + if (secnum != newsecnum) + continue; + + tsec = (sec->lines[i])->backsector; + if (!tsec) continue; //jff 5/7/98 if no backside, continue + newsecnum = tsec - sectors; + + // if sector's floor is different texture, look for another + if (tsec->floorpic != texture) + continue; + + /* jff 6/19/98 prevent double stepsize + * killough 10/98: intentionally left this way [MBF comment] + * cph 2001/02/06: stair bug fix should be controlled by comp_stairs, + * except if we're emulating MBF which perversly reverted the fix + */ + if (comp[comp_stairs] || (compatibility_level == mbf_compatibility)) + height += stairsize; // jff 6/28/98 change demo compatibility + + // if sector's floor already moving, look for another + if (P_SectorActive(floor_special,tsec)) //jff 2/22/98 + continue; + + /* cph - see comment above - do this iff we didn't do so above */ + if (!comp[comp_stairs] && (compatibility_level != mbf_compatibility)) + height += stairsize; + + sec = tsec; + secnum = newsecnum; + + // create and initialize a thinker for the next step + floor = Z_Malloc (sizeof(*floor), PU_LEVSPEC, 0); + memset(floor, 0, sizeof(*floor)); + P_AddThinker (&floor->thinker); + + sec->floordata = floor; //jff 2/22/98 + floor->thinker.function = T_MoveFloor; + floor->direction = 1; + floor->sector = sec; + floor->speed = speed; + floor->floordestheight = height; + floor->type = buildStair; //jff 3/31/98 do not leave uninited + //jff 2/27/98 fix uninitialized crush field + if (!demo_compatibility) + floor->crush = type==build8? false : true; + ok = 1; + break; + } + } while(ok); // continue until no next step is found + + } + /* killough 10/98: compatibility option */ + if (comp[comp_stairs]) { + /* cph 2001/09/22 - emulate buggy MBF comp_stairs for demos, with logic + * reversed since we now have a separate outer loop index. + * DEMOSYNC - what about boom_compatibility_compatibility? + */ + if ((compatibility_level >= mbf_compatibility) && (compatibility_level < + prboom_3_compatibility)) ssec = secnum; /* Trash outer loop index */ + else { + /* cph 2001/09/22 - now the correct comp_stairs - Doom used a linear + * search from the last secnum, so we set that as a minimum value and do + * a fresh tag search + */ + ssec = -1; minssec = secnum; + } + } + } + return rtn; +} + +// +// EV_DoDonut() +// +// Handle donut function: lower pillar, raise surrounding pool, both to height, +// texture and type of the sector surrounding the pool. +// +// Passed the linedef that triggered the donut +// Returns whether a thinker was created +// +int EV_DoDonut(line_t* line) +{ + sector_t* s1; + sector_t* s2; + sector_t* s3; + int secnum; + int rtn; + int i; + floormove_t* floor; + + secnum = -1; + rtn = 0; + // do function on all sectors with same tag as linedef + while ((secnum = P_FindSectorFromLineTag(line,secnum)) >= 0) + { + s1 = §ors[secnum]; // s1 is pillar's sector + + // do not start the donut if the pillar is already moving + if (P_SectorActive(floor_special,s1)) //jff 2/22/98 + continue; + + s2 = getNextSector(s1->lines[0],s1); // s2 is pool's sector + if (!s2) continue; // note lowest numbered line around + // pillar must be two-sided + + /* do not start the donut if the pool is already moving + * cph - DEMOSYNC - was !compatibility */ + if (!comp[comp_floors] && P_SectorActive(floor_special,s2)) + continue; //jff 5/7/98 + + // find a two sided line around the pool whose other side isn't the pillar + for (i = 0;i < s2->linecount;i++) + { + //jff 3/29/98 use true two-sidedness, not the flag + // killough 4/5/98: changed demo_compatibility to compatibility + if (comp[comp_model]) + { + if ((!s2->lines[i]->flags & ML_TWOSIDED) || + (s2->lines[i]->backsector == s1)) + continue; + } + else if (!s2->lines[i]->backsector || s2->lines[i]->backsector == s1) + continue; + + rtn = 1; //jff 1/26/98 no donut action - no switch change on return + + s3 = s2->lines[i]->backsector; // s3 is model sector for changes + + // Spawn rising slime + floor = Z_Malloc (sizeof(*floor), PU_LEVSPEC, 0); + memset(floor, 0, sizeof(*floor)); + P_AddThinker (&floor->thinker); + s2->floordata = floor; //jff 2/22/98 + floor->thinker.function = T_MoveFloor; + floor->type = donutRaise; + floor->crush = false; + floor->direction = 1; + floor->sector = s2; + floor->speed = FLOORSPEED / 2; + floor->texture = s3->floorpic; + floor->newspecial = 0; + floor->floordestheight = s3->floorheight; + + // Spawn lowering donut-hole pillar + floor = Z_Malloc (sizeof(*floor), PU_LEVSPEC, 0); + memset(floor, 0, sizeof(*floor)); + P_AddThinker (&floor->thinker); + s1->floordata = floor; //jff 2/22/98 + floor->thinker.function = T_MoveFloor; + floor->type = lowerFloor; + floor->crush = false; + floor->direction = -1; + floor->sector = s1; + floor->speed = FLOORSPEED / 2; + floor->floordestheight = s3->floorheight; + break; + } + } + return rtn; +} + +// +// EV_DoElevator +// +// Handle elevator linedef types +// +// Passed the linedef that triggered the elevator and the elevator action +// +// jff 2/22/98 new type to move floor and ceiling in parallel +// +int EV_DoElevator +( line_t* line, + elevator_e elevtype ) +{ + int secnum; + int rtn; + sector_t* sec; + elevator_t* elevator; + + secnum = -1; + rtn = 0; + // act on all sectors with the same tag as the triggering linedef + while ((secnum = P_FindSectorFromLineTag(line,secnum)) >= 0) + { + sec = §ors[secnum]; + + // If either floor or ceiling is already activated, skip it + if (sec->floordata || sec->ceilingdata) //jff 2/22/98 + continue; + + // create and initialize new elevator thinker + rtn = 1; + elevator = Z_Malloc (sizeof(*elevator), PU_LEVSPEC, 0); + memset(elevator, 0, sizeof(*elevator)); + P_AddThinker (&elevator->thinker); + sec->floordata = elevator; //jff 2/22/98 + sec->ceilingdata = elevator; //jff 2/22/98 + elevator->thinker.function = T_MoveElevator; + elevator->type = elevtype; + + // set up the fields according to the type of elevator action + switch(elevtype) + { + // elevator down to next floor + case elevateDown: + elevator->direction = -1; + elevator->sector = sec; + elevator->speed = ELEVATORSPEED; + elevator->floordestheight = + P_FindNextLowestFloor(sec,sec->floorheight); + elevator->ceilingdestheight = + elevator->floordestheight + sec->ceilingheight - sec->floorheight; + break; + + // elevator up to next floor + case elevateUp: + elevator->direction = 1; + elevator->sector = sec; + elevator->speed = ELEVATORSPEED; + elevator->floordestheight = + P_FindNextHighestFloor(sec,sec->floorheight); + elevator->ceilingdestheight = + elevator->floordestheight + sec->ceilingheight - sec->floorheight; + break; + + // elevator to floor height of activating switch's front sector + case elevateCurrent: + elevator->sector = sec; + elevator->speed = ELEVATORSPEED; + elevator->floordestheight = line->frontsector->floorheight; + elevator->ceilingdestheight = + elevator->floordestheight + sec->ceilingheight - sec->floorheight; + elevator->direction = + elevator->floordestheight>sec->floorheight? 1 : -1; + break; + + default: + break; + } + } + return rtn; +} diff --git a/src/p_genlin.c b/src/p_genlin.c new file mode 100644 index 0000000..873b25b --- /dev/null +++ b/src/p_genlin.c @@ -0,0 +1,1164 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Generalized linedef type handlers + * Floors, Ceilings, Doors, Locked Doors, Lifts, Stairs, Crushers + * + *-----------------------------------------------------------------------------*/ + +#include "doomstat.h" //jff 6/19/98 for demo_compatibility +#include "r_main.h" +#include "p_spec.h" +#include "p_tick.h" +#include "m_random.h" +#include "s_sound.h" +#include "sounds.h" + +////////////////////////////////////////////////////////// +// +// Generalized Linedef Type handlers +// +////////////////////////////////////////////////////////// + +// +// EV_DoGenFloor() +// +// Handle generalized floor types +// +// Passed the line activating the generalized floor function +// Returns true if a thinker is created +// +// jff 02/04/98 Added this routine (and file) to handle generalized +// floor movers using bit fields in the line special type. +// +int EV_DoGenFloor +( line_t* line ) +{ + int secnum; + int rtn; + boolean manual; + sector_t* sec; + floormove_t* floor; + unsigned value = (unsigned)line->special - GenFloorBase; + + // parse the bit fields in the line's special type + + int Crsh = (value & FloorCrush) >> FloorCrushShift; + int ChgT = (value & FloorChange) >> FloorChangeShift; + int Targ = (value & FloorTarget) >> FloorTargetShift; + int Dirn = (value & FloorDirection) >> FloorDirectionShift; + int ChgM = (value & FloorModel) >> FloorModelShift; + int Sped = (value & FloorSpeed) >> FloorSpeedShift; + int Trig = (value & TriggerType) >> TriggerTypeShift; + + rtn = 0; + + // check if a manual trigger, if so do just the sector on the backside + manual = false; + if (Trig==PushOnce || Trig==PushMany) + { + if (!(sec = line->backsector)) + return rtn; + secnum = sec-sectors; + manual = true; + goto manual_floor; + } + + secnum = -1; + // if not manual do all sectors tagged the same as the line + while ((secnum = P_FindSectorFromLineTag(line,secnum)) >= 0) + { + sec = §ors[secnum]; + +manual_floor: + // Do not start another function if floor already moving + if (P_SectorActive(floor_special,sec)) + { + if (!manual) + continue; + else + return rtn; + } + + // new floor thinker + rtn = 1; + floor = Z_Malloc (sizeof(*floor), PU_LEVSPEC, 0); + memset(floor, 0, sizeof(*floor)); + P_AddThinker (&floor->thinker); + sec->floordata = floor; + floor->thinker.function = T_MoveFloor; + floor->crush = Crsh; + floor->direction = Dirn? 1 : -1; + floor->sector = sec; + floor->texture = sec->floorpic; + floor->newspecial = sec->special; + //jff 3/14/98 transfer old special field too + floor->oldspecial = sec->oldspecial; + floor->type = genFloor; + + // set the speed of motion + switch (Sped) + { + case SpeedSlow: + floor->speed = FLOORSPEED; + break; + case SpeedNormal: + floor->speed = FLOORSPEED*2; + break; + case SpeedFast: + floor->speed = FLOORSPEED*4; + break; + case SpeedTurbo: + floor->speed = FLOORSPEED*8; + break; + default: + break; + } + + // set the destination height + switch(Targ) + { + case FtoHnF: + floor->floordestheight = P_FindHighestFloorSurrounding(sec); + break; + case FtoLnF: + floor->floordestheight = P_FindLowestFloorSurrounding(sec); + break; + case FtoNnF: + floor->floordestheight = Dirn? + P_FindNextHighestFloor(sec,sec->floorheight) : + P_FindNextLowestFloor(sec,sec->floorheight); + break; + case FtoLnC: + floor->floordestheight = P_FindLowestCeilingSurrounding(sec); + break; + case FtoC: + floor->floordestheight = sec->ceilingheight; + break; + case FbyST: + floor->floordestheight = (floor->sector->floorheight>>FRACBITS) + + floor->direction * (P_FindShortestTextureAround(secnum)>>FRACBITS); + if (floor->floordestheight>32000) //jff 3/13/98 prevent overflow + floor->floordestheight=32000; // wraparound in floor height + if (floor->floordestheight<-32000) + floor->floordestheight=-32000; + floor->floordestheight<<=FRACBITS; + break; + case Fby24: + floor->floordestheight = floor->sector->floorheight + + floor->direction * 24*FRACUNIT; + break; + case Fby32: + floor->floordestheight = floor->sector->floorheight + + floor->direction * 32*FRACUNIT; + break; + default: + break; + } + + // set texture/type change properties + if (ChgT) // if a texture change is indicated + { + if (ChgM) // if a numeric model change + { + sector_t *sec; + + //jff 5/23/98 find model with ceiling at target height if target + //is a ceiling type + sec = (Targ==FtoLnC || Targ==FtoC)? + P_FindModelCeilingSector(floor->floordestheight,secnum) : + P_FindModelFloorSector(floor->floordestheight,secnum); + if (sec) + { + floor->texture = sec->floorpic; + switch(ChgT) + { + case FChgZero: // zero type + floor->newspecial = 0; + //jff 3/14/98 change old field too + floor->oldspecial = 0; + floor->type = genFloorChg0; + break; + case FChgTyp: // copy type + floor->newspecial = sec->special; + //jff 3/14/98 change old field too + floor->oldspecial = sec->oldspecial; + floor->type = genFloorChgT; + break; + case FChgTxt: // leave type be + floor->type = genFloorChg; + break; + default: + break; + } + } + } + else // else if a trigger model change + { + floor->texture = line->frontsector->floorpic; + switch (ChgT) + { + case FChgZero: // zero type + floor->newspecial = 0; + //jff 3/14/98 change old field too + floor->oldspecial = 0; + floor->type = genFloorChg0; + break; + case FChgTyp: // copy type + floor->newspecial = line->frontsector->special; + //jff 3/14/98 change old field too + floor->oldspecial = line->frontsector->oldspecial; + floor->type = genFloorChgT; + break; + case FChgTxt: // leave type be + floor->type = genFloorChg; + default: + break; + } + } + } + if (manual) return rtn; + } + return rtn; +} + + +// +// EV_DoGenCeiling() +// +// Handle generalized ceiling types +// +// Passed the linedef activating the ceiling function +// Returns true if a thinker created +// +// jff 02/04/98 Added this routine (and file) to handle generalized +// floor movers using bit fields in the line special type. +// +int EV_DoGenCeiling +( line_t* line ) +{ + int secnum; + int rtn; + boolean manual; + fixed_t targheight; + sector_t* sec; + ceiling_t* ceiling; + unsigned value = (unsigned)line->special - GenCeilingBase; + + // parse the bit fields in the line's special type + + int Crsh = (value & CeilingCrush) >> CeilingCrushShift; + int ChgT = (value & CeilingChange) >> CeilingChangeShift; + int Targ = (value & CeilingTarget) >> CeilingTargetShift; + int Dirn = (value & CeilingDirection) >> CeilingDirectionShift; + int ChgM = (value & CeilingModel) >> CeilingModelShift; + int Sped = (value & CeilingSpeed) >> CeilingSpeedShift; + int Trig = (value & TriggerType) >> TriggerTypeShift; + + rtn = 0; + + // check if a manual trigger, if so do just the sector on the backside + manual = false; + if (Trig==PushOnce || Trig==PushMany) + { + if (!(sec = line->backsector)) + return rtn; + secnum = sec-sectors; + manual = true; + goto manual_ceiling; + } + + secnum = -1; + // if not manual do all sectors tagged the same as the line + while ((secnum = P_FindSectorFromLineTag(line,secnum)) >= 0) + { + sec = §ors[secnum]; + +manual_ceiling: + // Do not start another function if ceiling already moving + if (P_SectorActive(ceiling_special,sec)) //jff 2/22/98 + { + if (!manual) + continue; + else + return rtn; + } + + // new ceiling thinker + rtn = 1; + ceiling = Z_Malloc (sizeof(*ceiling), PU_LEVSPEC, 0); + memset(ceiling, 0, sizeof(*ceiling)); + P_AddThinker (&ceiling->thinker); + sec->ceilingdata = ceiling; //jff 2/22/98 + ceiling->thinker.function = T_MoveCeiling; + ceiling->crush = Crsh; + ceiling->direction = Dirn? 1 : -1; + ceiling->sector = sec; + ceiling->texture = sec->ceilingpic; + ceiling->newspecial = sec->special; + //jff 3/14/98 change old field too + ceiling->oldspecial = sec->oldspecial; + ceiling->tag = sec->tag; + ceiling->type = genCeiling; + + // set speed of motion + switch (Sped) + { + case SpeedSlow: + ceiling->speed = CEILSPEED; + break; + case SpeedNormal: + ceiling->speed = CEILSPEED*2; + break; + case SpeedFast: + ceiling->speed = CEILSPEED*4; + break; + case SpeedTurbo: + ceiling->speed = CEILSPEED*8; + break; + default: + break; + } + + // set destination target height + targheight = sec->ceilingheight; + switch(Targ) + { + case CtoHnC: + targheight = P_FindHighestCeilingSurrounding(sec); + break; + case CtoLnC: + targheight = P_FindLowestCeilingSurrounding(sec); + break; + case CtoNnC: + targheight = Dirn? + P_FindNextHighestCeiling(sec,sec->ceilingheight) : + P_FindNextLowestCeiling(sec,sec->ceilingheight); + break; + case CtoHnF: + targheight = P_FindHighestFloorSurrounding(sec); + break; + case CtoF: + targheight = sec->floorheight; + break; + case CbyST: + targheight = (ceiling->sector->ceilingheight>>FRACBITS) + + ceiling->direction * (P_FindShortestUpperAround(secnum)>>FRACBITS); + if (targheight>32000) //jff 3/13/98 prevent overflow + targheight=32000; // wraparound in ceiling height + if (targheight<-32000) + targheight=-32000; + targheight<<=FRACBITS; + break; + case Cby24: + targheight = ceiling->sector->ceilingheight + + ceiling->direction * 24*FRACUNIT; + break; + case Cby32: + targheight = ceiling->sector->ceilingheight + + ceiling->direction * 32*FRACUNIT; + break; + default: + break; + } + if (Dirn) ceiling->topheight = targheight; + else ceiling->bottomheight = targheight; + + // set texture/type change properties + if (ChgT) // if a texture change is indicated + { + if (ChgM) // if a numeric model change + { + sector_t *sec; + + //jff 5/23/98 find model with floor at target height if target + //is a floor type + sec = (Targ==CtoHnF || Targ==CtoF)? + P_FindModelFloorSector(targheight,secnum) : + P_FindModelCeilingSector(targheight,secnum); + if (sec) + { + ceiling->texture = sec->ceilingpic; + switch (ChgT) + { + case CChgZero: // type is zeroed + ceiling->newspecial = 0; + //jff 3/14/98 change old field too + ceiling->oldspecial = 0; + ceiling->type = genCeilingChg0; + break; + case CChgTyp: // type is copied + ceiling->newspecial = sec->special; + //jff 3/14/98 change old field too + ceiling->oldspecial = sec->oldspecial; + ceiling->type = genCeilingChgT; + break; + case CChgTxt: // type is left alone + ceiling->type = genCeilingChg; + break; + default: + break; + } + } + } + else // else if a trigger model change + { + ceiling->texture = line->frontsector->ceilingpic; + switch (ChgT) + { + case CChgZero: // type is zeroed + ceiling->newspecial = 0; + //jff 3/14/98 change old field too + ceiling->oldspecial = 0; + ceiling->type = genCeilingChg0; + break; + case CChgTyp: // type is copied + ceiling->newspecial = line->frontsector->special; + //jff 3/14/98 change old field too + ceiling->oldspecial = line->frontsector->oldspecial; + ceiling->type = genCeilingChgT; + break; + case CChgTxt: // type is left alone + ceiling->type = genCeilingChg; + break; + default: + break; + } + } + } + P_AddActiveCeiling(ceiling); // add this ceiling to the active list + if (manual) return rtn; + } + return rtn; +} + +// +// EV_DoGenLift() +// +// Handle generalized lift types +// +// Passed the linedef activating the lift +// Returns true if a thinker is created +// +int EV_DoGenLift +( line_t* line ) +{ + plat_t* plat; + int secnum; + int rtn; + boolean manual; + sector_t* sec; + unsigned value = (unsigned)line->special - GenLiftBase; + + // parse the bit fields in the line's special type + + int Targ = (value & LiftTarget) >> LiftTargetShift; + int Dely = (value & LiftDelay) >> LiftDelayShift; + int Sped = (value & LiftSpeed) >> LiftSpeedShift; + int Trig = (value & TriggerType) >> TriggerTypeShift; + + secnum = -1; + rtn = 0; + + // Activate all plats that are in_stasis + + if (Targ==LnF2HnF) + P_ActivateInStasis(line->tag); + + // check if a manual trigger, if so do just the sector on the backside + manual = false; + if (Trig==PushOnce || Trig==PushMany) + { + if (!(sec = line->backsector)) + return rtn; + secnum = sec-sectors; + manual = true; + goto manual_lift; + } + + // if not manual do all sectors tagged the same as the line + while ((secnum = P_FindSectorFromLineTag(line,secnum)) >= 0) + { + sec = §ors[secnum]; + +manual_lift: + // Do not start another function if floor already moving + if (P_SectorActive(floor_special,sec)) + { + if (!manual) + continue; + else + return rtn; + } + + // Setup the plat thinker + rtn = 1; + plat = Z_Malloc( sizeof(*plat), PU_LEVSPEC, 0); + memset(plat, 0, sizeof(*plat)); + P_AddThinker(&plat->thinker); + + plat->sector = sec; + plat->sector->floordata = plat; + plat->thinker.function = T_PlatRaise; + plat->crush = false; + plat->tag = line->tag; + + plat->type = genLift; + plat->high = sec->floorheight; + plat->status = down; + + // setup the target destination height + switch(Targ) + { + case F2LnF: + plat->low = P_FindLowestFloorSurrounding(sec); + if (plat->low > sec->floorheight) + plat->low = sec->floorheight; + break; + case F2NnF: + plat->low = P_FindNextLowestFloor(sec,sec->floorheight); + break; + case F2LnC: + plat->low = P_FindLowestCeilingSurrounding(sec); + if (plat->low > sec->floorheight) + plat->low = sec->floorheight; + break; + case LnF2HnF: + plat->type = genPerpetual; + plat->low = P_FindLowestFloorSurrounding(sec); + if (plat->low > sec->floorheight) + plat->low = sec->floorheight; + plat->high = P_FindHighestFloorSurrounding(sec); + if (plat->high < sec->floorheight) + plat->high = sec->floorheight; + plat->status = P_Random(pr_genlift)&1; + break; + default: + break; + } + + // setup the speed of motion + switch(Sped) + { + case SpeedSlow: + plat->speed = PLATSPEED * 2; + break; + case SpeedNormal: + plat->speed = PLATSPEED * 4; + break; + case SpeedFast: + plat->speed = PLATSPEED * 8; + break; + case SpeedTurbo: + plat->speed = PLATSPEED * 16; + break; + default: + break; + } + + // setup the delay time before the floor returns + switch(Dely) + { + case 0: + plat->wait = 1*35; + break; + case 1: + plat->wait = PLATWAIT*35; + break; + case 2: + plat->wait = 5*35; + break; + case 3: + plat->wait = 10*35; + break; + } + + S_StartSound((mobj_t *)&sec->soundorg,sfx_pstart); + P_AddActivePlat(plat); // add this plat to the list of active plats + + if (manual) + return rtn; + } + return rtn; +} + +// +// EV_DoGenStairs() +// +// Handle generalized stair building +// +// Passed the linedef activating the stairs +// Returns true if a thinker is created +// +int EV_DoGenStairs +( line_t* line ) +{ + int secnum; + int osecnum; //jff 3/4/98 preserve loop index + int height; + int i; + int newsecnum; + int texture; + int ok; + int rtn; + boolean manual; + + sector_t* sec; + sector_t* tsec; + + floormove_t* floor; + + fixed_t stairsize; + fixed_t speed; + + unsigned value = (unsigned)line->special - GenStairsBase; + + // parse the bit fields in the line's special type + + int Igno = (value & StairIgnore) >> StairIgnoreShift; + int Dirn = (value & StairDirection) >> StairDirectionShift; + int Step = (value & StairStep) >> StairStepShift; + int Sped = (value & StairSpeed) >> StairSpeedShift; + int Trig = (value & TriggerType) >> TriggerTypeShift; + + rtn = 0; + + // check if a manual trigger, if so do just the sector on the backside + manual = false; + if (Trig==PushOnce || Trig==PushMany) + { + if (!(sec = line->backsector)) + return rtn; + secnum = sec-sectors; + manual = true; + goto manual_stair; + } + + secnum = -1; + // if not manual do all sectors tagged the same as the line + while ((secnum = P_FindSectorFromLineTag(line,secnum)) >= 0) + { + sec = §ors[secnum]; + +manual_stair: + //Do not start another function if floor already moving + //jff 2/26/98 add special lockout condition to wait for entire + //staircase to build before retriggering + if (P_SectorActive(floor_special,sec) || sec->stairlock) + { + if (!manual) + continue; + else + return rtn; + } + + // new floor thinker + rtn = 1; + floor = Z_Malloc (sizeof(*floor), PU_LEVSPEC, 0); + memset(floor, 0, sizeof(*floor)); + P_AddThinker (&floor->thinker); + sec->floordata = floor; + floor->thinker.function = T_MoveFloor; + floor->direction = Dirn? 1 : -1; + floor->sector = sec; + + // setup speed of stair building + switch(Sped) + { + default: + case SpeedSlow: + floor->speed = FLOORSPEED/4; + break; + case SpeedNormal: + floor->speed = FLOORSPEED/2; + break; + case SpeedFast: + floor->speed = FLOORSPEED*2; + break; + case SpeedTurbo: + floor->speed = FLOORSPEED*4; + break; + } + + // setup stepsize for stairs + switch(Step) + { + default: + case 0: + stairsize = 4*FRACUNIT; + break; + case 1: + stairsize = 8*FRACUNIT; + break; + case 2: + stairsize = 16*FRACUNIT; + break; + case 3: + stairsize = 24*FRACUNIT; + break; + } + + speed = floor->speed; + height = sec->floorheight + floor->direction * stairsize; + floor->floordestheight = height; + texture = sec->floorpic; + floor->crush = false; + floor->type = genBuildStair; // jff 3/31/98 do not leave uninited + + sec->stairlock = -2; // jff 2/26/98 set up lock on current sector + sec->nextsec = -1; + sec->prevsec = -1; + + osecnum = secnum; //jff 3/4/98 preserve loop index + // Find next sector to raise + // 1. Find 2-sided line with same sector side[0] + // 2. Other side is the next sector to raise + do + { + ok = 0; + for (i = 0;i < sec->linecount;i++) + { + if ( !((sec->lines[i])->backsector) ) + continue; + + tsec = (sec->lines[i])->frontsector; + newsecnum = tsec-sectors; + + if (secnum != newsecnum) + continue; + + tsec = (sec->lines[i])->backsector; + newsecnum = tsec - sectors; + + if (!Igno && tsec->floorpic != texture) + continue; + + /* jff 6/19/98 prevent double stepsize */ + if (compatibility_level < boom_202_compatibility) + height += floor->direction * stairsize; + + //jff 2/26/98 special lockout condition for retriggering + if (P_SectorActive(floor_special,tsec) || tsec->stairlock) + continue; + + /* jff 6/19/98 increase height AFTER continue */ + if (compatibility_level >= boom_202_compatibility) + height += floor->direction * stairsize; + + // jff 2/26/98 + // link the stair chain in both directions + // lock the stair sector until building complete + sec->nextsec = newsecnum; // link step to next + tsec->prevsec = secnum; // link next back + tsec->nextsec = -1; // set next forward link as end + tsec->stairlock = -2; // lock the step + + sec = tsec; + secnum = newsecnum; + floor = Z_Malloc (sizeof(*floor), PU_LEVSPEC, 0); + + memset(floor, 0, sizeof(*floor)); + P_AddThinker (&floor->thinker); + + sec->floordata = floor; + floor->thinker.function = T_MoveFloor; + floor->direction = Dirn? 1 : -1; + floor->sector = sec; + floor->speed = speed; + floor->floordestheight = height; + floor->crush = false; + floor->type = genBuildStair; // jff 3/31/98 do not leave uninited + + ok = 1; + break; + } + } while(ok); + if (manual) + return rtn; + secnum = osecnum; //jff 3/4/98 restore old loop index + } + // retriggerable generalized stairs build up or down alternately + if (rtn) + line->special ^= StairDirection; // alternate dir on succ activations + return rtn; +} + +// +// EV_DoGenCrusher() +// +// Handle generalized crusher types +// +// Passed the linedef activating the crusher +// Returns true if a thinker created +// +int EV_DoGenCrusher +( line_t* line ) +{ + int secnum; + int rtn; + boolean manual; + sector_t* sec; + ceiling_t* ceiling; + unsigned value = (unsigned)line->special - GenCrusherBase; + + // parse the bit fields in the line's special type + + int Slnt = (value & CrusherSilent) >> CrusherSilentShift; + int Sped = (value & CrusherSpeed) >> CrusherSpeedShift; + int Trig = (value & TriggerType) >> TriggerTypeShift; + + //jff 2/22/98 Reactivate in-stasis ceilings...for certain types. + //jff 4/5/98 return if activated + rtn = P_ActivateInStasisCeiling(line); + + // check if a manual trigger, if so do just the sector on the backside + manual = false; + if (Trig==PushOnce || Trig==PushMany) + { + if (!(sec = line->backsector)) + return rtn; + secnum = sec-sectors; + manual = true; + goto manual_crusher; + } + + secnum = -1; + // if not manual do all sectors tagged the same as the line + while ((secnum = P_FindSectorFromLineTag(line,secnum)) >= 0) + { + sec = §ors[secnum]; + +manual_crusher: + // Do not start another function if ceiling already moving + if (P_SectorActive(ceiling_special,sec)) //jff 2/22/98 + { + if (!manual) + continue; + else + return rtn; + } + + // new ceiling thinker + rtn = 1; + ceiling = Z_Malloc (sizeof(*ceiling), PU_LEVSPEC, 0); + memset(ceiling, 0, sizeof(*ceiling)); + P_AddThinker (&ceiling->thinker); + sec->ceilingdata = ceiling; //jff 2/22/98 + ceiling->thinker.function = T_MoveCeiling; + ceiling->crush = true; + ceiling->direction = -1; + ceiling->sector = sec; + ceiling->texture = sec->ceilingpic; + ceiling->newspecial = sec->special; + ceiling->tag = sec->tag; + ceiling->type = Slnt? genSilentCrusher : genCrusher; + ceiling->topheight = sec->ceilingheight; + ceiling->bottomheight = sec->floorheight + (8*FRACUNIT); + + // setup ceiling motion speed + switch (Sped) + { + case SpeedSlow: + ceiling->speed = CEILSPEED; + break; + case SpeedNormal: + ceiling->speed = CEILSPEED*2; + break; + case SpeedFast: + ceiling->speed = CEILSPEED*4; + break; + case SpeedTurbo: + ceiling->speed = CEILSPEED*8; + break; + default: + break; + } + ceiling->oldspeed=ceiling->speed; + + P_AddActiveCeiling(ceiling); // add to list of active ceilings + if (manual) return rtn; + } + return rtn; +} + +// +// EV_DoGenLockedDoor() +// +// Handle generalized locked door types +// +// Passed the linedef activating the generalized locked door +// Returns true if a thinker created +// +int EV_DoGenLockedDoor +( line_t* line ) +{ + int secnum,rtn; + sector_t* sec; + vldoor_t* door; + boolean manual; + unsigned value = (unsigned)line->special - GenLockedBase; + + // parse the bit fields in the line's special type + + int Kind = (value & LockedKind) >> LockedKindShift; + int Sped = (value & LockedSpeed) >> LockedSpeedShift; + int Trig = (value & TriggerType) >> TriggerTypeShift; + + rtn = 0; + + // check if a manual trigger, if so do just the sector on the backside + manual = false; + if (Trig==PushOnce || Trig==PushMany) + { + if (!(sec = line->backsector)) + return rtn; + secnum = sec-sectors; + manual = true; + goto manual_locked; + } + + secnum = -1; + rtn = 0; + + // if not manual do all sectors tagged the same as the line + while ((secnum = P_FindSectorFromLineTag(line,secnum)) >= 0) + { + sec = §ors[secnum]; +manual_locked: + // Do not start another function if ceiling already moving + if (P_SectorActive(ceiling_special,sec)) //jff 2/22/98 + { + if (!manual) + continue; + else + return rtn; + } + + // new door thinker + rtn = 1; + door = Z_Malloc (sizeof(*door), PU_LEVSPEC, 0); + memset(door, 0, sizeof(*door)); + P_AddThinker (&door->thinker); + sec->ceilingdata = door; //jff 2/22/98 + + door->thinker.function = T_VerticalDoor; + door->sector = sec; + door->topwait = VDOORWAIT; + door->line = line; + door->topheight = P_FindLowestCeilingSurrounding(sec); + door->topheight -= 4*FRACUNIT; + door->direction = 1; + + /* killough 10/98: implement gradual lighting */ + door->lighttag = !comp[comp_doorlight] && + (line->special&6) == 6 && + line->special > GenLockedBase ? line->tag : 0; + + // setup speed of door motion + switch(Sped) + { + default: + case SpeedSlow: + door->type = Kind? genOpen : genRaise; + door->speed = VDOORSPEED; + break; + case SpeedNormal: + door->type = Kind? genOpen : genRaise; + door->speed = VDOORSPEED*2; + break; + case SpeedFast: + door->type = Kind? genBlazeOpen : genBlazeRaise; + door->speed = VDOORSPEED*4; + break; + case SpeedTurbo: + door->type = Kind? genBlazeOpen : genBlazeRaise; + door->speed = VDOORSPEED*8; + + break; + } + + // killough 4/15/98: fix generalized door opening sounds + // (previously they always had the blazing door close sound) + + S_StartSound((mobj_t *)&door->sector->soundorg, // killough 4/15/98 + door->speed >= VDOORSPEED*4 ? sfx_bdopn : sfx_doropn); + + if (manual) + return rtn; + } + return rtn; +} + +// +// EV_DoGenDoor() +// +// Handle generalized door types +// +// Passed the linedef activating the generalized door +// Returns true if a thinker created +// +int EV_DoGenDoor +( line_t* line ) +{ + int secnum,rtn; + sector_t* sec; + boolean manual; + vldoor_t* door; + unsigned value = (unsigned)line->special - GenDoorBase; + + // parse the bit fields in the line's special type + + int Dely = (value & DoorDelay) >> DoorDelayShift; + int Kind = (value & DoorKind) >> DoorKindShift; + int Sped = (value & DoorSpeed) >> DoorSpeedShift; + int Trig = (value & TriggerType) >> TriggerTypeShift; + + rtn = 0; + + // check if a manual trigger, if so do just the sector on the backside + manual = false; + if (Trig==PushOnce || Trig==PushMany) + { + if (!(sec = line->backsector)) + return rtn; + secnum = sec-sectors; + manual = true; + goto manual_door; + } + + + secnum = -1; + rtn = 0; + + // if not manual do all sectors tagged the same as the line + while ((secnum = P_FindSectorFromLineTag(line,secnum)) >= 0) + { + sec = §ors[secnum]; +manual_door: + // Do not start another function if ceiling already moving + if (P_SectorActive(ceiling_special,sec)) //jff 2/22/98 + { + if (!manual) + continue; + else + return rtn; + } + + // new door thinker + rtn = 1; + door = Z_Malloc (sizeof(*door), PU_LEVSPEC, 0); + memset(door, 0, sizeof(*door)); + P_AddThinker (&door->thinker); + sec->ceilingdata = door; //jff 2/22/98 + + door->thinker.function = T_VerticalDoor; + door->sector = sec; + // setup delay for door remaining open/closed + switch(Dely) + { + default: + case 0: + door->topwait = 35; + break; + case 1: + door->topwait = VDOORWAIT; + break; + case 2: + door->topwait = 2*VDOORWAIT; + break; + case 3: + door->topwait = 7*VDOORWAIT; + break; + } + + // setup speed of door motion + switch(Sped) + { + default: + case SpeedSlow: + door->speed = VDOORSPEED; + break; + case SpeedNormal: + door->speed = VDOORSPEED*2; + break; + case SpeedFast: + door->speed = VDOORSPEED*4; + break; + case SpeedTurbo: + door->speed = VDOORSPEED*8; + break; + } + door->line = line; // jff 1/31/98 remember line that triggered us + + /* killough 10/98: implement gradual lighting */ + door->lighttag = !comp[comp_doorlight] && + (line->special&6) == 6 && + line->special > GenLockedBase ? line->tag : 0; + + // set kind of door, whether it opens then close, opens, closes etc. + // assign target heights accordingly + switch(Kind) + { + case OdCDoor: + door->direction = 1; + door->topheight = P_FindLowestCeilingSurrounding(sec); + door->topheight -= 4*FRACUNIT; + if (door->topheight != sec->ceilingheight) + S_StartSound((mobj_t *)&door->sector->soundorg,Sped>=SpeedFast || comp[comp_sound] ? sfx_bdopn : sfx_doropn); + door->type = Sped>=SpeedFast? genBlazeRaise : genRaise; + break; + case ODoor: + door->direction = 1; + door->topheight = P_FindLowestCeilingSurrounding(sec); + door->topheight -= 4*FRACUNIT; + if (door->topheight != sec->ceilingheight) + S_StartSound((mobj_t *)&door->sector->soundorg,Sped>=SpeedFast || comp[comp_sound] ? sfx_bdopn : sfx_doropn); + door->type = Sped>=SpeedFast? genBlazeOpen : genOpen; + break; + case CdODoor: + door->topheight = sec->ceilingheight; + door->direction = -1; + S_StartSound((mobj_t *)&door->sector->soundorg,Sped>=SpeedFast && !comp[comp_sound] ? sfx_bdcls : sfx_dorcls); + door->type = Sped>=SpeedFast? genBlazeCdO : genCdO; + break; + case CDoor: + door->topheight = P_FindLowestCeilingSurrounding(sec); + door->topheight -= 4*FRACUNIT; + door->direction = -1; + S_StartSound((mobj_t *)&door->sector->soundorg,Sped>=SpeedFast && !comp[comp_sound] ? sfx_bdcls : sfx_dorcls); + door->type = Sped>=SpeedFast? genBlazeClose : genClose; + break; + default: + break; + } + if (manual) + return rtn; + } + return rtn; +} diff --git a/src/p_inter.c b/src/p_inter.c new file mode 100644 index 0000000..2688e95 --- /dev/null +++ b/src/p_inter.c @@ -0,0 +1,913 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Handling interactions (i.e., collisions). + * + *-----------------------------------------------------------------------------*/ + +#include "doomstat.h" +#include "dstrings.h" +#include "m_random.h" +#include "am_map.h" +#include "r_main.h" +#include "s_sound.h" +#include "sounds.h" +#include "d_deh.h" // Ty 03/22/98 - externalized strings +#include "p_tick.h" +#include "lprintf.h" + +#include "p_inter.h" +#include "p_enemy.h" + +#ifdef __GNUG__ +#pragma implementation "p_inter.h" +#endif +#include "p_inter.h" + +#define BONUSADD 6 + +// Ty 03/07/98 - add deh externals +// Maximums and such were hardcoded values. Need to externalize those for +// dehacked support (and future flexibility). Most var names came from the key +// strings used in dehacked. + +int initial_health = 100; +int initial_bullets = 50; +int maxhealth = 100; // was MAXHEALTH as a #define, used only in this module +int max_armor = 200; +int green_armor_class = 1; // these are involved with armortype below +int blue_armor_class = 2; +int max_soul = 200; +int soul_health = 100; +int mega_health = 200; +int god_health = 100; // these are used in cheats (see st_stuff.c) +int idfa_armor = 200; +int idfa_armor_class = 2; +// not actually used due to pairing of cheat_k and cheat_fa +int idkfa_armor = 200; +int idkfa_armor_class = 2; + +int bfgcells = 40; // used in p_pspr.c +int monsters_infight = 0; // e6y: Dehacked support - monsters infight +// Ty 03/07/98 - end deh externals + +// a weapon is found with two clip loads, +// a big item has five clip loads +int maxammo[NUMAMMO] = {200, 50, 300, 50}; +int clipammo[NUMAMMO] = { 10, 4, 20, 1}; + +// +// GET STUFF +// + +// +// P_GiveAmmo +// Num is the number of clip loads, +// not the individual count (0= 1/2 clip). +// Returns false if the ammo can't be picked up at all +// + +static boolean P_GiveAmmo(player_t *player, ammotype_t ammo, int num) +{ + int oldammo; + + if (ammo == am_noammo) + return false; + +#ifdef RANGECHECK + if (ammo < 0 || ammo > NUMAMMO) + I_Error ("P_GiveAmmo: bad type %i", ammo); +#endif + + if ( player->ammo[ammo] == player->maxammo[ammo] ) + return false; + + if (num) + num *= clipammo[ammo]; + else + num = clipammo[ammo]/2; + + // give double ammo in trainer mode, you'll need in nightmare + if (gameskill == sk_baby || gameskill == sk_nightmare) + num <<= 1; + + oldammo = player->ammo[ammo]; + player->ammo[ammo] += num; + + if (player->ammo[ammo] > player->maxammo[ammo]) + player->ammo[ammo] = player->maxammo[ammo]; + + // If non zero ammo, don't change up weapons, player was lower on purpose. + if (oldammo) + return true; + + // We were down to zero, so select a new weapon. + // Preferences are not user selectable. + + switch (ammo) + { + case am_clip: + if (player->readyweapon == wp_fist) { + if (player->weaponowned[wp_chaingun]) + player->pendingweapon = wp_chaingun; + else + player->pendingweapon = wp_pistol; + } + break; + + case am_shell: + if (player->readyweapon == wp_fist || player->readyweapon == wp_pistol) + if (player->weaponowned[wp_shotgun]) + player->pendingweapon = wp_shotgun; + break; + + case am_cell: + if (player->readyweapon == wp_fist || player->readyweapon == wp_pistol) + if (player->weaponowned[wp_plasma]) + player->pendingweapon = wp_plasma; + break; + + case am_misl: + if (player->readyweapon == wp_fist) + if (player->weaponowned[wp_missile]) + player->pendingweapon = wp_missile; + default: + break; + } + return true; +} + +// +// P_GiveWeapon +// The weapon name may have a MF_DROPPED flag ored in. +// + +static boolean P_GiveWeapon(player_t *player, weapontype_t weapon, boolean dropped) +{ + boolean gaveammo; + boolean gaveweapon; + + if (netgame && deathmatch!=2 && !dropped) + { + // leave placed weapons forever on net games + if (player->weaponowned[weapon]) + return false; + + player->bonuscount += BONUSADD; + player->weaponowned[weapon] = true; + + P_GiveAmmo(player, weaponinfo[weapon].ammo, deathmatch ? 5 : 2); + + player->pendingweapon = weapon; + /* cph 20028/10 - for old-school DM addicts, allow old behavior + * where only consoleplayer's pickup sounds are heard */ + // displayplayer, not consoleplayer, for viewing multiplayer demos + if (!comp[comp_sound] || player == &players[displayplayer]) + S_StartSound (player->mo, sfx_wpnup|PICKUP_SOUND); // killough 4/25/98 + return false; + } + + if (weaponinfo[weapon].ammo != am_noammo) + { + // give one clip with a dropped weapon, + // two clips with a found weapon + gaveammo = P_GiveAmmo (player, weaponinfo[weapon].ammo, dropped ? 1 : 2); + } + else + gaveammo = false; + + if (player->weaponowned[weapon]) + gaveweapon = false; + else + { + gaveweapon = true; + player->weaponowned[weapon] = true; + player->pendingweapon = weapon; + } + return gaveweapon || gaveammo; +} + +// +// P_GiveBody +// Returns false if the body isn't needed at all +// + +static boolean P_GiveBody(player_t *player, int num) +{ + if (player->health >= maxhealth) + return false; // Ty 03/09/98 externalized MAXHEALTH to maxhealth + player->health += num; + if (player->health > maxhealth) + player->health = maxhealth; + player->mo->health = player->health; + return true; +} + +// +// P_GiveArmor +// Returns false if the armor is worse +// than the current armor. +// + +static boolean P_GiveArmor(player_t *player, int armortype) +{ + int hits = armortype*100; + if (player->armorpoints >= hits) + return false; // don't pick up + player->armortype = armortype; + player->armorpoints = hits; + return true; +} + +// +// P_GiveCard +// + +static void P_GiveCard(player_t *player, card_t card) +{ + if (player->cards[card]) + return; + player->bonuscount = BONUSADD; + player->cards[card] = 1; +} + +// +// P_GivePower +// +// Rewritten by Lee Killough +// + +boolean P_GivePower(player_t *player, int power) +{ + static const int tics[NUMPOWERS] = { + INVULNTICS, 1 /* strength */, INVISTICS, + IRONTICS, 1 /* allmap */, INFRATICS, + }; + + switch (power) + { + case pw_invisibility: + player->mo->flags |= MF_SHADOW; + break; + case pw_allmap: + if (player->powers[pw_allmap]) + return false; + break; + case pw_strength: + P_GiveBody(player,100); + break; + } + + // Unless player has infinite duration cheat, set duration (killough) + + if (player->powers[power] >= 0) + player->powers[power] = tics[power]; + return true; +} + +// +// P_TouchSpecialThing +// + +void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher) +{ + player_t *player; + int i; + int sound; + fixed_t delta = special->z - toucher->z; + + if (delta > toucher->height || delta < -8*FRACUNIT) + return; // out of reach + + sound = sfx_itemup; + player = toucher->player; + + // Dead thing touching. + // Can happen with a sliding player corpse. + if (toucher->health <= 0) + return; + + // Identify by sprite. + switch (special->sprite) + { + // armor + case SPR_ARM1: + if (!P_GiveArmor (player, green_armor_class)) + return; + player->message = s_GOTARMOR; // Ty 03/22/98 - externalized + break; + + case SPR_ARM2: + if (!P_GiveArmor (player, blue_armor_class)) + return; + player->message = s_GOTMEGA; // Ty 03/22/98 - externalized + break; + + // bonus items + case SPR_BON1: + player->health++; // can go over 100% + if (player->health > (maxhealth * 2)) + player->health = (maxhealth * 2); + player->mo->health = player->health; + player->message = s_GOTHTHBONUS; // Ty 03/22/98 - externalized + break; + + case SPR_BON2: + player->armorpoints++; // can go over 100% + if (player->armorpoints > max_armor) + player->armorpoints = max_armor; + if (!player->armortype) + player->armortype = green_armor_class; + player->message = s_GOTARMBONUS; // Ty 03/22/98 - externalized + break; + + case SPR_SOUL: + player->health += soul_health; + if (player->health > max_soul) + player->health = max_soul; + player->mo->health = player->health; + player->message = s_GOTSUPER; // Ty 03/22/98 - externalized + sound = sfx_getpow; + break; + + case SPR_MEGA: + if (gamemode != commercial) + return; + player->health = mega_health; + player->mo->health = player->health; + P_GiveArmor (player,blue_armor_class); + player->message = s_GOTMSPHERE; // Ty 03/22/98 - externalized + sound = sfx_getpow; + break; + + // cards + // leave cards for everyone + case SPR_BKEY: + if (!player->cards[it_bluecard]) + player->message = s_GOTBLUECARD; // Ty 03/22/98 - externalized + P_GiveCard (player, it_bluecard); + if (!netgame) + break; + return; + + case SPR_YKEY: + if (!player->cards[it_yellowcard]) + player->message = s_GOTYELWCARD; // Ty 03/22/98 - externalized + P_GiveCard (player, it_yellowcard); + if (!netgame) + break; + return; + + case SPR_RKEY: + if (!player->cards[it_redcard]) + player->message = s_GOTREDCARD; // Ty 03/22/98 - externalized + P_GiveCard (player, it_redcard); + if (!netgame) + break; + return; + + case SPR_BSKU: + if (!player->cards[it_blueskull]) + player->message = s_GOTBLUESKUL; // Ty 03/22/98 - externalized + P_GiveCard (player, it_blueskull); + if (!netgame) + break; + return; + + case SPR_YSKU: + if (!player->cards[it_yellowskull]) + player->message = s_GOTYELWSKUL; // Ty 03/22/98 - externalized + P_GiveCard (player, it_yellowskull); + if (!netgame) + break; + return; + + case SPR_RSKU: + if (!player->cards[it_redskull]) + player->message = s_GOTREDSKULL; // Ty 03/22/98 - externalized + P_GiveCard (player, it_redskull); + if (!netgame) + break; + return; + + // medikits, heals + case SPR_STIM: + if (!P_GiveBody (player, 10)) + return; + player->message = s_GOTSTIM; // Ty 03/22/98 - externalized + break; + + case SPR_MEDI: + if (!P_GiveBody (player, 25)) + return; + + if (player->health < 50) // cph - 25 + the 25 just added, thanks to Quasar for reporting this bug + player->message = s_GOTMEDINEED; // Ty 03/22/98 - externalized + else + player->message = s_GOTMEDIKIT; // Ty 03/22/98 - externalized + break; + + + // power ups + case SPR_PINV: + if (!P_GivePower (player, pw_invulnerability)) + return; + player->message = s_GOTINVUL; // Ty 03/22/98 - externalized + sound = sfx_getpow; + break; + + case SPR_PSTR: + if (!P_GivePower (player, pw_strength)) + return; + player->message = s_GOTBERSERK; // Ty 03/22/98 - externalized + if (player->readyweapon != wp_fist) + player->pendingweapon = wp_fist; + sound = sfx_getpow; + break; + + case SPR_PINS: + if (!P_GivePower (player, pw_invisibility)) + return; + player->message = s_GOTINVIS; // Ty 03/22/98 - externalized + sound = sfx_getpow; + break; + + case SPR_SUIT: + if (!P_GivePower (player, pw_ironfeet)) + return; + player->message = s_GOTSUIT; // Ty 03/22/98 - externalized + sound = sfx_getpow; + break; + + case SPR_PMAP: + if (!P_GivePower (player, pw_allmap)) + return; + player->message = s_GOTMAP; // Ty 03/22/98 - externalized + sound = sfx_getpow; + break; + + case SPR_PVIS: + if (!P_GivePower (player, pw_infrared)) + return; + player->message = s_GOTVISOR; // Ty 03/22/98 - externalized + sound = sfx_getpow; + break; + + // ammo + case SPR_CLIP: + if (special->flags & MF_DROPPED) + { + if (!P_GiveAmmo (player,am_clip,0)) + return; + } + else + { + if (!P_GiveAmmo (player,am_clip,1)) + return; + } + player->message = s_GOTCLIP; // Ty 03/22/98 - externalized + break; + + case SPR_AMMO: + if (!P_GiveAmmo (player, am_clip,5)) + return; + player->message = s_GOTCLIPBOX; // Ty 03/22/98 - externalized + break; + + case SPR_ROCK: + if (!P_GiveAmmo (player, am_misl,1)) + return; + player->message = s_GOTROCKET; // Ty 03/22/98 - externalized + break; + + case SPR_BROK: + if (!P_GiveAmmo (player, am_misl,5)) + return; + player->message = s_GOTROCKBOX; // Ty 03/22/98 - externalized + break; + + case SPR_CELL: + if (!P_GiveAmmo (player, am_cell,1)) + return; + player->message = s_GOTCELL; // Ty 03/22/98 - externalized + break; + + case SPR_CELP: + if (!P_GiveAmmo (player, am_cell,5)) + return; + player->message = s_GOTCELLBOX; // Ty 03/22/98 - externalized + break; + + case SPR_SHEL: + if (!P_GiveAmmo (player, am_shell,1)) + return; + player->message = s_GOTSHELLS; // Ty 03/22/98 - externalized + break; + + case SPR_SBOX: + if (!P_GiveAmmo (player, am_shell,5)) + return; + player->message = s_GOTSHELLBOX; // Ty 03/22/98 - externalized + break; + + case SPR_BPAK: + if (!player->backpack) + { + for (i=0 ; imaxammo[i] *= 2; + player->backpack = true; + } + for (i=0 ; imessage = s_GOTBACKPACK; // Ty 03/22/98 - externalized + break; + + // weapons + case SPR_BFUG: + if (!P_GiveWeapon (player, wp_bfg, false) ) + return; + player->message = s_GOTBFG9000; // Ty 03/22/98 - externalized + sound = sfx_wpnup; + break; + + case SPR_MGUN: + if (!P_GiveWeapon (player, wp_chaingun, (special->flags&MF_DROPPED)!=0) ) + return; + player->message = s_GOTCHAINGUN; // Ty 03/22/98 - externalized + sound = sfx_wpnup; + break; + + case SPR_CSAW: + if (!P_GiveWeapon (player, wp_chainsaw, false) ) + return; + player->message = s_GOTCHAINSAW; // Ty 03/22/98 - externalized + sound = sfx_wpnup; + break; + + case SPR_LAUN: + if (!P_GiveWeapon (player, wp_missile, false) ) + return; + player->message = s_GOTLAUNCHER; // Ty 03/22/98 - externalized + sound = sfx_wpnup; + break; + + case SPR_PLAS: + if (!P_GiveWeapon (player, wp_plasma, false) ) + return; + player->message = s_GOTPLASMA; // Ty 03/22/98 - externalized + sound = sfx_wpnup; + break; + + case SPR_SHOT: + if (!P_GiveWeapon (player, wp_shotgun, (special->flags&MF_DROPPED)!=0 ) ) + return; + player->message = s_GOTSHOTGUN; // Ty 03/22/98 - externalized + sound = sfx_wpnup; + break; + + case SPR_SGN2: + if (!P_GiveWeapon(player, wp_supershotgun, (special->flags&MF_DROPPED)!=0)) + return; + player->message = s_GOTSHOTGUN2; // Ty 03/22/98 - externalized + sound = sfx_wpnup; + break; + + default: + I_Error ("P_SpecialThing: Unknown gettable thing"); + } + + if (special->flags & MF_COUNTITEM) + player->itemcount++; + P_RemoveMobj (special); + player->bonuscount += BONUSADD; + + /* cph 20028/10 - for old-school DM addicts, allow old behavior + * where only consoleplayer's pickup sounds are heard */ + // displayplayer, not consoleplayer, for viewing multiplayer demos + if (!comp[comp_sound] || player == &players[displayplayer]) + S_StartSound (player->mo, sound | PICKUP_SOUND); // killough 4/25/98 +} + +// +// KillMobj +// +// killough 11/98: make static +static void P_KillMobj(mobj_t *source, mobj_t *target) +{ + mobjtype_t item; + mobj_t *mo; + + target->flags &= ~(MF_SHOOTABLE|MF_FLOAT|MF_SKULLFLY); + + if (target->type != MT_SKULL) + target->flags &= ~MF_NOGRAVITY; + + target->flags |= MF_CORPSE|MF_DROPOFF; + target->height >>= 2; + + if (!((target->flags ^ MF_COUNTKILL) & (MF_FRIEND | MF_COUNTKILL))) + totallive--; + + if (source && source->player) + { + // count for intermission + if (target->flags & MF_COUNTKILL) + source->player->killcount++; + if (target->player) + source->player->frags[target->player-players]++; + } + else + if (target->flags & MF_COUNTKILL) { /* Add to kills tally */ + if ((compatibility_level < lxdoom_1_compatibility) || !netgame) { + if (!netgame) + // count all monster deaths, + // even those caused by other monsters + players[0].killcount++; + } else + if (!deathmatch) { + // try and find a player to give the kill to, otherwise give the + // kill to a random player. this fixes the missing monsters bug + // in coop - rain + // CPhipps - not a bug as such, but certainly an inconsistency. + if (target->lastenemy && target->lastenemy->health > 0 + && target->lastenemy->player) // Fighting a player + target->lastenemy->player->killcount++; + else { + // cph - randomely choose a player in the game to be credited + // and do it uniformly between the active players + unsigned int activeplayers = 0, player, i; + + for (player = 0; playerplayer) + { + // count environment kills against you + if (!source) + target->player->frags[target->player-players]++; + + target->flags &= ~MF_SOLID; + target->player->playerstate = PST_DEAD; + P_DropWeapon (target->player); + + if (target->player == &players[consoleplayer] && (automapmode & am_active)) + AM_Stop(); // don't die in auto map; switch view prior to dying + } + + if (target->health < -target->info->spawnhealth && target->info->xdeathstate) + P_SetMobjState (target, target->info->xdeathstate); + else + P_SetMobjState (target, target->info->deathstate); + + target->tics -= P_Random(pr_killtics)&3; + + if (target->tics < 1) + target->tics = 1; + + // Drop stuff. + // This determines the kind of object spawned + // during the death frame of a thing. + + switch (target->type) + { + case MT_WOLFSS: + case MT_POSSESSED: + item = MT_CLIP; + break; + + case MT_SHOTGUY: + item = MT_SHOTGUN; + break; + + case MT_CHAINGUY: + item = MT_CHAINGUN; + break; + + default: + return; + } + + mo = P_SpawnMobj (target->x,target->y,ONFLOORZ, item); + mo->flags |= MF_DROPPED; // special versions of items +} + +// +// P_DamageMobj +// Damages both enemies and players +// "inflictor" is the thing that caused the damage +// creature or missile, can be NULL (slime, etc) +// "source" is the thing to target after taking damage +// creature or NULL +// Source and inflictor are the same for melee attacks. +// Source can be NULL for slime, barrel explosions +// and other environmental stuff. +// + +void P_DamageMobj(mobj_t *target,mobj_t *inflictor, mobj_t *source, int damage) +{ + player_t *player; + boolean justhit = false; /* killough 11/98 */ + + /* killough 8/31/98: allow bouncers to take damage */ + if (!(target->flags & (MF_SHOOTABLE | MF_BOUNCES))) + return; // shouldn't happen... + + if (target->health <= 0) + return; + + if (target->flags & MF_SKULLFLY) + target->momx = target->momy = target->momz = 0; + + player = target->player; + if (player && gameskill == sk_baby) + damage >>= 1; // take half damage in trainer mode + + // Some close combat weapons should not + // inflict thrust and push the victim out of reach, + // thus kick away unless using the chainsaw. + + if (inflictor && !(target->flags & MF_NOCLIP) && + (!source || !source->player || + source->player->readyweapon != wp_chainsaw)) + { + unsigned ang = R_PointToAngle2 (inflictor->x, inflictor->y, + target->x, target->y); + + fixed_t thrust = damage*(FRACUNIT>>3)*100/target->info->mass; + + // make fall forwards sometimes + if ( damage < 40 && damage > target->health + && target->z - inflictor->z > 64*FRACUNIT + && P_Random(pr_damagemobj) & 1) + { + ang += ANG180; + thrust *= 4; + } + + ang >>= ANGLETOFINESHIFT; + target->momx += FixedMul (thrust, finecosine[ang]); + target->momy += FixedMul (thrust, finesine[ang]); + + /* killough 11/98: thrust objects hanging off ledges */ + if (target->intflags & MIF_FALLING && target->gear >= MAXGEAR) + target->gear = 0; + } + + // player specific + if (player) + { + // end of game hell hack + if (target->subsector->sector->special == 11 && damage >= target->health) + damage = target->health - 1; + + // Below certain threshold, + // ignore damage in GOD mode, or with INVUL power. + // killough 3/26/98: make god mode 100% god mode in non-compat mode + + if ((damage < 1000 || (!comp[comp_god] && (player->cheats&CF_GODMODE))) && + (player->cheats&CF_GODMODE || player->powers[pw_invulnerability])) + return; + + if (player->armortype) + { + int saved = player->armortype == 1 ? damage/3 : damage/2; + if (player->armorpoints <= saved) + { + // armor is used up + saved = player->armorpoints; + player->armortype = 0; + } + player->armorpoints -= saved; + damage -= saved; + } + + player->health -= damage; // mirror mobj health here for Dave + if (player->health < 0) + player->health = 0; + + player->attacker = source; + player->damagecount += damage; // add damage after armor / invuln + + if (player->damagecount > 100) + player->damagecount = 100; // teleport stomp does 10k points... + } + + // do the damage + target->health -= damage; + if (target->health <= 0) + { + P_KillMobj (source, target); + return; + } + + // killough 9/7/98: keep track of targets so that friends can help friends + if (mbf_features) + { + /* If target is a player, set player's target to source, + * so that a friend can tell who's hurting a player + */ + if (player) + P_SetTarget(&target->target, source); + + /* killough 9/8/98: + * If target's health is less than 50%, move it to the front of its list. + * This will slightly increase the chances that enemies will choose to + * "finish it off", but its main purpose is to alert friends of danger. + */ + if (target->health*2 < target->info->spawnhealth) + { + thinker_t *cap = &thinkerclasscap[target->flags & MF_FRIEND ? + th_friends : th_enemies]; + (target->thinker.cprev->cnext = target->thinker.cnext)->cprev = + target->thinker.cprev; + (target->thinker.cnext = cap->cnext)->cprev = &target->thinker; + (target->thinker.cprev = cap)->cnext = &target->thinker; + } + } + + if (P_Random (pr_painchance) < target->info->painchance && + !(target->flags & MF_SKULLFLY)) { //killough 11/98: see below + if (mbf_features) + justhit = true; + else + target->flags |= MF_JUSTHIT; // fight back! + + P_SetMobjState(target, target->info->painstate); + } + + target->reactiontime = 0; // we're awake now... + + /* killough 9/9/98: cleaned up, made more consistent: */ + + if (source && source != target && source->type != MT_VILE && + (!target->threshold || target->type == MT_VILE) && + ((source->flags ^ target->flags) & MF_FRIEND || + monster_infighting || + !mbf_features)) + { + /* if not intent on another player, chase after this one + * + * killough 2/15/98: remember last enemy, to prevent + * sleeping early; 2/21/98: Place priority on players + * killough 9/9/98: cleaned up, made more consistent: + */ + + if (!target->lastenemy || target->lastenemy->health <= 0 || + (!mbf_features ? + !target->lastenemy->player : + !((target->flags ^ target->lastenemy->flags) & MF_FRIEND) && + target->target != source)) // remember last enemy - killough + P_SetTarget(&target->lastenemy, target->target); + + P_SetTarget(&target->target, source); // killough 11/98 + target->threshold = BASETHRESHOLD; + if (target->state == &states[target->info->spawnstate] + && target->info->seestate != S_NULL) + P_SetMobjState (target, target->info->seestate); + } + + /* killough 11/98: Don't attack a friend, unless hit by that friend. + * cph 2006/04/01 - implicitly this is only if mbf_features */ + if (justhit && (target->target == source || !target->target || + !(target->flags & target->target->flags & MF_FRIEND))) + target->flags |= MF_JUSTHIT; // fight back! +} diff --git a/src/p_inter.h b/src/p_inter.h new file mode 100644 index 0000000..53b64a7 --- /dev/null +++ b/src/p_inter.h @@ -0,0 +1,75 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Thing events, and dehacked specified numbers controlling them. + * + *-----------------------------------------------------------------------------*/ + +#ifndef __P_INTER__ +#define __P_INTER__ + +#include "d_player.h" +#include "p_mobj.h" + +#ifdef __GNUG__ +#pragma interface +#endif + +/* Ty 03/09/98 Moved to an int in p_inter.c for deh and externalization */ +#define MAXHEALTH maxhealth + +/* follow a player exlusively for 3 seconds */ +#define BASETHRESHOLD (100) + +boolean P_GivePower(player_t *, int); +void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher); +void P_DamageMobj(mobj_t *target,mobj_t *inflictor,mobj_t *source,int damage); + +/* killough 5/2/98: moved from d_deh.c, g_game.c, m_misc.c, others: */ + +extern int god_health; /* Ty 03/09/98 - deh support, see also p_inter.c */ +extern int idfa_armor; +extern int idfa_armor_class; +extern int idkfa_armor; +extern int idkfa_armor_class; /* Ty - end */ +/* Ty 03/13/98 - externalized initial settings for respawned player */ +extern int initial_health; +extern int initial_bullets; +extern int maxhealth; +extern int max_armor; +extern int green_armor_class; +extern int blue_armor_class; +extern int max_soul; +extern int soul_health; +extern int mega_health; +extern int bfgcells; +extern int monsters_infight; // e6y: Dehacked support - monsters infight +extern int maxammo[], clipammo[]; + +#endif diff --git a/src/p_lights.c b/src/p_lights.c new file mode 100644 index 0000000..84936e0 --- /dev/null +++ b/src/p_lights.c @@ -0,0 +1,443 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Action routines for lighting thinkers + * Spawn sector based lighting effects. + * Handle lighting linedef types + * + *-----------------------------------------------------------------------------*/ + +#include "doomstat.h" //jff 5/18/98 +#include "doomdef.h" +#include "m_random.h" +#include "r_main.h" +#include "p_spec.h" +#include "p_tick.h" + +////////////////////////////////////////////////////////// +// +// Lighting action routines, called once per tick +// +////////////////////////////////////////////////////////// + +// +// T_FireFlicker() +// +// Firelight flicker action routine, called once per tick +// +// Passed a fireflicker_t structure containing light levels and timing +// Returns nothing +// +void T_FireFlicker (fireflicker_t* flick) +{ + int amount; + + if (--flick->count) + return; + + amount = (P_Random(pr_lights)&3)*16; + + if (flick->sector->lightlevel - amount < flick->minlight) + flick->sector->lightlevel = flick->minlight; + else + flick->sector->lightlevel = flick->maxlight - amount; + + flick->count = 4; +} + +// +// T_LightFlash() +// +// Broken light flashing action routine, called once per tick +// +// Passed a lightflash_t structure containing light levels and timing +// Returns nothing +// +void T_LightFlash (lightflash_t* flash) +{ + if (--flash->count) + return; + + if (flash->sector->lightlevel == flash->maxlight) + { + flash-> sector->lightlevel = flash->minlight; + flash->count = (P_Random(pr_lights)&flash->mintime)+1; + } + else + { + flash-> sector->lightlevel = flash->maxlight; + flash->count = (P_Random(pr_lights)&flash->maxtime)+1; + } + +} + +// +// T_StrobeFlash() +// +// Strobe light flashing action routine, called once per tick +// +// Passed a strobe_t structure containing light levels and timing +// Returns nothing +// +void T_StrobeFlash (strobe_t* flash) +{ + if (--flash->count) + return; + + if (flash->sector->lightlevel == flash->minlight) + { + flash-> sector->lightlevel = flash->maxlight; + flash->count = flash->brighttime; + } + else + { + flash-> sector->lightlevel = flash->minlight; + flash->count =flash->darktime; + } +} + +// +// T_Glow() +// +// Glowing light action routine, called once per tick +// +// Passed a glow_t structure containing light levels and timing +// Returns nothing +// + +void T_Glow(glow_t* g) +{ + switch(g->direction) + { + case -1: + // light dims + g->sector->lightlevel -= GLOWSPEED; + if (g->sector->lightlevel <= g->minlight) + { + g->sector->lightlevel += GLOWSPEED; + g->direction = 1; + } + break; + + case 1: + // light brightens + g->sector->lightlevel += GLOWSPEED; + if (g->sector->lightlevel >= g->maxlight) + { + g->sector->lightlevel -= GLOWSPEED; + g->direction = -1; + } + break; + } +} + +////////////////////////////////////////////////////////// +// +// Sector lighting type spawners +// +// After the map has been loaded, each sector is scanned +// for specials that spawn thinkers +// +////////////////////////////////////////////////////////// + +// +// P_SpawnFireFlicker() +// +// Spawns a fire flicker lighting thinker +// +// Passed the sector that spawned the thinker +// Returns nothing +// +void P_SpawnFireFlicker (sector_t* sector) +{ + fireflicker_t* flick; + + // Note that we are resetting sector attributes. + // Nothing special about it during gameplay. + sector->special &= ~31; //jff 3/14/98 clear non-generalized sector type + + flick = Z_Malloc ( sizeof(*flick), PU_LEVSPEC, 0); + + memset(flick, 0, sizeof(*flick)); + P_AddThinker (&flick->thinker); + + flick->thinker.function = T_FireFlicker; + flick->sector = sector; + flick->maxlight = sector->lightlevel; + flick->minlight = P_FindMinSurroundingLight(sector,sector->lightlevel)+16; + flick->count = 4; +} + +// +// P_SpawnLightFlash() +// +// Spawns a broken light flash lighting thinker +// +// Passed the sector that spawned the thinker +// Returns nothing +// +void P_SpawnLightFlash (sector_t* sector) +{ + lightflash_t* flash; + + // nothing special about it during gameplay + sector->special &= ~31; //jff 3/14/98 clear non-generalized sector type + + flash = Z_Malloc ( sizeof(*flash), PU_LEVSPEC, 0); + + memset(flash, 0, sizeof(*flash)); + P_AddThinker (&flash->thinker); + + flash->thinker.function = T_LightFlash; + flash->sector = sector; + flash->maxlight = sector->lightlevel; + + flash->minlight = P_FindMinSurroundingLight(sector,sector->lightlevel); + flash->maxtime = 64; + flash->mintime = 7; + flash->count = (P_Random(pr_lights)&flash->maxtime)+1; +} + +// +// P_SpawnStrobeFlash +// +// Spawns a blinking light thinker +// +// Passed the sector that spawned the thinker, speed of blinking +// and whether blinking is to by syncrhonous with other sectors +// +// Returns nothing +// +void P_SpawnStrobeFlash +( sector_t* sector, + int fastOrSlow, + int inSync ) +{ + strobe_t* flash; + + flash = Z_Malloc ( sizeof(*flash), PU_LEVSPEC, 0); + + memset(flash, 0, sizeof(*flash)); + P_AddThinker (&flash->thinker); + + flash->sector = sector; + flash->darktime = fastOrSlow; + flash->brighttime = STROBEBRIGHT; + flash->thinker.function = T_StrobeFlash; + flash->maxlight = sector->lightlevel; + flash->minlight = P_FindMinSurroundingLight(sector, sector->lightlevel); + + if (flash->minlight == flash->maxlight) + flash->minlight = 0; + + // nothing special about it during gameplay + sector->special &= ~31; //jff 3/14/98 clear non-generalized sector type + + if (!inSync) + flash->count = (P_Random(pr_lights)&7)+1; + else + flash->count = 1; +} + +// +// P_SpawnGlowingLight() +// +// Spawns a glowing light (smooth oscillation from min to max) thinker +// +// Passed the sector that spawned the thinker +// Returns nothing +// +void P_SpawnGlowingLight(sector_t* sector) +{ + glow_t* g; + + g = Z_Malloc( sizeof(*g), PU_LEVSPEC, 0); + + memset(g, 0, sizeof(*g)); + P_AddThinker(&g->thinker); + + g->sector = sector; + g->minlight = P_FindMinSurroundingLight(sector,sector->lightlevel); + g->maxlight = sector->lightlevel; + g->thinker.function = T_Glow; + g->direction = -1; + + sector->special &= ~31; //jff 3/14/98 clear non-generalized sector type +} + +////////////////////////////////////////////////////////// +// +// Linedef lighting function handlers +// +////////////////////////////////////////////////////////// + +// +// EV_StartLightStrobing() +// +// Start strobing lights (usually from a trigger) +// +// Passed the line that activated the strobing +// Returns true +// +// jff 2/12/98 added int return value, fixed return +// +int EV_StartLightStrobing(line_t* line) +{ + int secnum; + sector_t* sec; + + secnum = -1; + // start lights strobing in all sectors tagged same as line + while ((secnum = P_FindSectorFromLineTag(line,secnum)) >= 0) + { + sec = §ors[secnum]; + // if already doing a lighting function, don't start a second + if (P_SectorActive(lighting_special,sec)) //jff 2/22/98 + continue; + + P_SpawnStrobeFlash (sec,SLOWDARK, 0); + } + return 1; +} + +// +// EV_TurnTagLightsOff() +// +// Turn line's tagged sector's lights to min adjacent neighbor level +// +// Passed the line that activated the lights being turned off +// Returns true +// +// jff 2/12/98 added int return value, fixed return +// +int EV_TurnTagLightsOff(line_t* line) +{ + int j; + + // search sectors for those with same tag as activating line + + // killough 10/98: replaced inefficient search with fast search + for (j = -1; (j = P_FindSectorFromLineTag(line,j)) >= 0;) + { + sector_t *sector = sectors + j, *tsec; + int i, min = sector->lightlevel; + // find min neighbor light level + for (i = 0;i < sector->linecount; i++) + if ((tsec = getNextSector(sector->lines[i], sector)) && + tsec->lightlevel < min) + min = tsec->lightlevel; + sector->lightlevel = min; + } + return 1; +} + +// +// EV_LightTurnOn() +// +// Turn sectors tagged to line lights on to specified or max neighbor level +// +// Passed the activating line, and a level to set the light to +// If level passed is 0, the maximum neighbor lighting is used +// Returns true +// +// jff 2/12/98 added int return value, fixed return +// +int EV_LightTurnOn(line_t *line, int bright) +{ + int i; + + // search all sectors for ones with same tag as activating line + + // killough 10/98: replace inefficient search with fast search + for (i = -1; (i = P_FindSectorFromLineTag(line,i)) >= 0;) + { + sector_t *temp, *sector = sectors+i; + int j, tbright = bright; //jff 5/17/98 search for maximum PER sector + + // bright = 0 means to search for highest light level surrounding sector + + if (!bright) + for (j = 0;j < sector->linecount; j++) + if ((temp = getNextSector(sector->lines[j],sector)) && + temp->lightlevel > tbright) + tbright = temp->lightlevel; + + sector->lightlevel = tbright; + + //jff 5/17/98 unless compatibility optioned + //then maximum near ANY tagged sector + if (comp[comp_model]) + bright = tbright; + } + return 1; +} + +/* killough 10/98: + * + * EV_LightTurnOnPartway() + * + * Turn sectors tagged to line lights on to specified or max neighbor level + * + * Passed the activating line, and a light level fraction between 0 and 1. + * Sets the light to min on 0, max on 1, and interpolates in-between. + * Used for doors with gradual lighting effects. + * + * Returns true + */ + +int EV_LightTurnOnPartway(line_t *line, fixed_t level) +{ + int i; + + if (level < 0) // clip at extremes + level = 0; + if (level > FRACUNIT) + level = FRACUNIT; + + // search all sectors for ones with same tag as activating line + for (i = -1; (i = P_FindSectorFromLineTag(line,i)) >= 0;) + { + sector_t *temp, *sector = sectors+i; + int j, bright = 0, min = sector->lightlevel; + + for (j = 0; j < sector->linecount; j++) + if ((temp = getNextSector(sector->lines[j],sector))) + { + if (temp->lightlevel > bright) + bright = temp->lightlevel; + if (temp->lightlevel < min) + min = temp->lightlevel; + } + + sector->lightlevel = // Set level in-between extremes + (level * bright + (FRACUNIT-level) * min) >> FRACBITS; + } + return 1; +} + diff --git a/src/p_map.c b/src/p_map.c new file mode 100644 index 0000000..a8c3a6d --- /dev/null +++ b/src/p_map.c @@ -0,0 +1,2335 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2004 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Movement, collision handling. + * Shooting and aiming. + * + *-----------------------------------------------------------------------------*/ + +#include "doomstat.h" +#include "r_main.h" +#include "p_mobj.h" +#include "p_maputl.h" +#include "p_map.h" +#include "p_setup.h" +#include "p_spec.h" +#include "s_sound.h" +#include "sounds.h" +#include "p_inter.h" +#include "m_random.h" +#include "m_bbox.h" +#include "lprintf.h" + +static mobj_t *tmthing; +static fixed_t tmx; +static fixed_t tmy; +static int pe_x; // Pain Elemental position for Lost Soul checks // phares +static int pe_y; // Pain Elemental position for Lost Soul checks // phares +static int ls_x; // Lost Soul position for Lost Soul checks // phares +static int ls_y; // Lost Soul position for Lost Soul checks // phares + +// If "floatok" true, move would be ok +// if within "tmfloorz - tmceilingz". + +boolean floatok; + +/* killough 11/98: if "felldown" true, object was pushed down ledge */ +boolean felldown; + +// The tm* items are used to hold information globally, usually for +// line or object intersection checking + +fixed_t tmbbox[4]; // bounding box for line intersection checks +fixed_t tmfloorz; // floor you'd hit if free to fall +fixed_t tmceilingz; // ceiling of sector you're in +fixed_t tmdropoffz; // dropoff on other side of line you're crossing + +// keep track of the line that lowers the ceiling, +// so missiles don't explode against sky hack walls + +line_t *ceilingline; +line_t *blockline; /* killough 8/11/98: blocking linedef */ +line_t *floorline; /* killough 8/1/98: Highest touched floor */ +static int tmunstuck; /* killough 8/1/98: whether to allow unsticking */ + +// keep track of special lines as they are hit, +// but don't process them until the move is proven valid + +// 1/11/98 killough: removed limit on special lines crossed +line_t **spechit; // new code -- killough +static int spechit_max; // killough + +int numspechit; + +// Temporary holder for thing_sectorlist threads +msecnode_t* sector_list = NULL; // phares 3/16/98 + +// +// TELEPORT MOVE +// + +// +// PIT_StompThing +// + +static boolean telefrag; /* killough 8/9/98: whether to telefrag at exit */ + +boolean PIT_StompThing (mobj_t* thing) + { + fixed_t blockdist; + + // phares 9/10/98: moved this self-check to start of routine + + // don't clip against self + + if (thing == tmthing) + return true; + + if (!(thing->flags & MF_SHOOTABLE)) // Can't shoot it? Can't stomp it! + return true; + + blockdist = thing->radius + tmthing->radius; + + if (D_abs(thing->x - tmx) >= blockdist || D_abs(thing->y - tmy) >= blockdist) + return true; // didn't hit it + + // monsters don't stomp things except on boss level + if (!telefrag) // killough 8/9/98: make consistent across all levels + return false; + + P_DamageMobj (thing, tmthing, tmthing, 10000); // Stomp! + + return true; + } + + +/* + * killough 8/28/98: + * + * P_GetFriction() + * + * Returns the friction associated with a particular mobj. + */ + +int P_GetFriction(const mobj_t *mo, int *frictionfactor) +{ + int friction = ORIG_FRICTION; + int movefactor = ORIG_FRICTION_FACTOR; + const msecnode_t *m; + const sector_t *sec; + + /* Assign the friction value to objects on the floor, non-floating, + * and clipped. Normally the object's friction value is kept at + * ORIG_FRICTION and this thinker changes it for icy or muddy floors. + * + * When the object is straddling sectors with the same + * floorheight that have different frictions, use the lowest + * friction value (muddy has precedence over icy). + */ + + if (!(mo->flags & (MF_NOCLIP|MF_NOGRAVITY)) + && (mbf_features || (mo->player && !compatibility)) && + variable_friction) + for (m = mo->touching_sectorlist; m; m = m->m_tnext) + if ((sec = m->m_sector)->special & FRICTION_MASK && + (sec->friction < friction || friction == ORIG_FRICTION) && + (mo->z <= sec->floorheight || + (sec->heightsec != -1 && + mo->z <= sectors[sec->heightsec].floorheight && + mbf_features))) + friction = sec->friction, movefactor = sec->movefactor; + + if (frictionfactor) + *frictionfactor = movefactor; + + return friction; +} + +/* phares 3/19/98 + * P_GetMoveFactor() returns the value by which the x,y + * movements are multiplied to add to player movement. + * + * killough 8/28/98: rewritten + */ + +int P_GetMoveFactor(const mobj_t *mo, int *frictionp) +{ + int movefactor, friction; + + //e6y + if (!mbf_features) + { + int momentum; + + movefactor = ORIG_FRICTION_FACTOR; + + if (!compatibility && variable_friction && + !(mo->flags & (MF_NOGRAVITY | MF_NOCLIP))) + { + friction = mo->friction; + if (friction == ORIG_FRICTION) // normal floor + ; + else if (friction > ORIG_FRICTION) // ice + { + movefactor = mo->movefactor; + ((mobj_t*)mo)->movefactor = ORIG_FRICTION_FACTOR; // reset + } + else // sludge + { + + // phares 3/11/98: you start off slowly, then increase as + // you get better footing + + momentum = (P_AproxDistance(mo->momx,mo->momy)); + movefactor = mo->movefactor; + if (momentum > MORE_FRICTION_MOMENTUM<<2) + movefactor <<= 3; + + else if (momentum > MORE_FRICTION_MOMENTUM<<1) + movefactor <<= 2; + + else if (momentum > MORE_FRICTION_MOMENTUM) + movefactor <<= 1; + + ((mobj_t*)mo)->movefactor = ORIG_FRICTION_FACTOR; // reset + } + } // ^ + + return(movefactor); // | + } + + // If the floor is icy or muddy, it's harder to get moving. This is where + // the different friction factors are applied to 'trying to move'. In + // p_mobj.c, the friction factors are applied as you coast and slow down. + + if ((friction = P_GetFriction(mo, &movefactor)) < ORIG_FRICTION) + { + // phares 3/11/98: you start off slowly, then increase as + // you get better footing + + int momentum = P_AproxDistance(mo->momx,mo->momy); + + if (momentum > MORE_FRICTION_MOMENTUM<<2) + movefactor <<= 3; + else if (momentum > MORE_FRICTION_MOMENTUM<<1) + movefactor <<= 2; + else if (momentum > MORE_FRICTION_MOMENTUM) + movefactor <<= 1; + } + + if (frictionp) + *frictionp = friction; + + return movefactor; +} + +// +// P_TeleportMove +// + +boolean P_TeleportMove (mobj_t* thing,fixed_t x,fixed_t y, boolean boss) + { + int xl; + int xh; + int yl; + int yh; + int bx; + int by; + + subsector_t* newsubsec; + + /* killough 8/9/98: make telefragging more consistent, preserve compatibility */ + telefrag = thing->player || + (!comp[comp_telefrag] ? boss : (gamemap==30)); + + // kill anything occupying the position + + tmthing = thing; + + tmx = x; + tmy = y; + + tmbbox[BOXTOP] = y + tmthing->radius; + tmbbox[BOXBOTTOM] = y - tmthing->radius; + tmbbox[BOXRIGHT] = x + tmthing->radius; + tmbbox[BOXLEFT] = x - tmthing->radius; + + newsubsec = R_PointInSubsector (x,y); + ceilingline = NULL; + + // The base floor/ceiling is from the subsector + // that contains the point. + // Any contacted lines the step closer together + // will adjust them. + + tmfloorz = tmdropoffz = newsubsec->sector->floorheight; + tmceilingz = newsubsec->sector->ceilingheight; + + validcount++; + numspechit = 0; + + // stomp on any things contacted + + xl = (tmbbox[BOXLEFT] - bmaporgx - MAXRADIUS)>>MAPBLOCKSHIFT; + xh = (tmbbox[BOXRIGHT] - bmaporgx + MAXRADIUS)>>MAPBLOCKSHIFT; + yl = (tmbbox[BOXBOTTOM] - bmaporgy - MAXRADIUS)>>MAPBLOCKSHIFT; + yh = (tmbbox[BOXTOP] - bmaporgy + MAXRADIUS)>>MAPBLOCKSHIFT; + + for (bx=xl ; bx<=xh ; bx++) + for (by=yl ; by<=yh ; by++) + if (!P_BlockThingsIterator(bx,by,PIT_StompThing)) + return false; + + // the move is ok, + // so unlink from the old position & link into the new position + + P_UnsetThingPosition (thing); + + thing->floorz = tmfloorz; + thing->ceilingz = tmceilingz; + thing->dropoffz = tmdropoffz; // killough 11/98 + + thing->x = x; + thing->y = y; + + P_SetThingPosition (thing); + + thing->PrevX = x; + thing->PrevY = y; + thing->PrevZ = thing->floorz; + + return true; + } + + +// +// MOVEMENT ITERATOR FUNCTIONS +// + +// e6y: Spechits overrun emulation code +static void SpechitOverrun(line_t *ld); + +// // phares +// PIT_CrossLine // | +// Checks to see if a PE->LS trajectory line crosses a blocking // V +// line. Returns false if it does. +// +// tmbbox holds the bounding box of the trajectory. If that box +// does not touch the bounding box of the line in question, +// then the trajectory is not blocked. If the PE is on one side +// of the line and the LS is on the other side, then the +// trajectory is blocked. +// +// Currently this assumes an infinite line, which is not quite +// correct. A more correct solution would be to check for an +// intersection of the trajectory and the line, but that takes +// longer and probably really isn't worth the effort. +// + +static // killough 3/26/98: make static +boolean PIT_CrossLine (line_t* ld) + { + if (!(ld->flags & ML_TWOSIDED) || + (ld->flags & (ML_BLOCKING|ML_BLOCKMONSTERS))) + if (!(tmbbox[BOXLEFT] > ld->bbox[BOXRIGHT] || + tmbbox[BOXRIGHT] < ld->bbox[BOXLEFT] || + tmbbox[BOXTOP] < ld->bbox[BOXBOTTOM] || + tmbbox[BOXBOTTOM] > ld->bbox[BOXTOP])) + if (P_PointOnLineSide(pe_x,pe_y,ld) != P_PointOnLineSide(ls_x,ls_y,ld)) + return(false); // line blocks trajectory // ^ + return(true); // line doesn't block trajectory // | + } // phares + + +/* killough 8/1/98: used to test intersection between thing and line + * assuming NO movement occurs -- used to avoid sticky situations. + */ + +static int untouched(line_t *ld) +{ + fixed_t x, y, tmbbox[4]; + return + (tmbbox[BOXRIGHT] = (x=tmthing->x)+tmthing->radius) <= ld->bbox[BOXLEFT] || + (tmbbox[BOXLEFT] = x-tmthing->radius) >= ld->bbox[BOXRIGHT] || + (tmbbox[BOXTOP] = (y=tmthing->y)+tmthing->radius) <= ld->bbox[BOXBOTTOM] || + (tmbbox[BOXBOTTOM] = y-tmthing->radius) >= ld->bbox[BOXTOP] || + P_BoxOnLineSide(tmbbox, ld) != -1; +} + +// +// PIT_CheckLine +// Adjusts tmfloorz and tmceilingz as lines are contacted +// + +static // killough 3/26/98: make static +boolean PIT_CheckLine (line_t* ld) +{ + if (tmbbox[BOXRIGHT] <= ld->bbox[BOXLEFT] + || tmbbox[BOXLEFT] >= ld->bbox[BOXRIGHT] + || tmbbox[BOXTOP] <= ld->bbox[BOXBOTTOM] + || tmbbox[BOXBOTTOM] >= ld->bbox[BOXTOP] ) + return true; // didn't hit it + + if (P_BoxOnLineSide(tmbbox, ld) != -1) + return true; // didn't hit it + + // A line has been hit + + // The moving thing's destination position will cross the given line. + // If this should not be allowed, return false. + // If the line is special, keep track of it + // to process later if the move is proven ok. + // NOTE: specials are NOT sorted by order, + // so two special lines that are only 8 pixels apart + // could be crossed in either order. + + // killough 7/24/98: allow player to move out of 1s wall, to prevent sticking + if (!ld->backsector) // one sided line + { + blockline = ld; + return tmunstuck && !untouched(ld) && + FixedMul(tmx-tmthing->x,ld->dy) > FixedMul(tmy-tmthing->y,ld->dx); + } + + // killough 8/10/98: allow bouncing objects to pass through as missiles + if (!(tmthing->flags & (MF_MISSILE | MF_BOUNCES))) + { + if (ld->flags & ML_BLOCKING) // explicitly blocking everything + return tmunstuck && !untouched(ld); // killough 8/1/98: allow escape + + // killough 8/9/98: monster-blockers don't affect friends + if (!(tmthing->flags & MF_FRIEND || tmthing->player) + && ld->flags & ML_BLOCKMONSTERS) + return false; // block monsters only + } + + // set openrange, opentop, openbottom + // these define a 'window' from one sector to another across this line + + P_LineOpening (ld); + + // adjust floor & ceiling heights + + if (opentop < tmceilingz) + { + tmceilingz = opentop; + ceilingline = ld; + blockline = ld; + } + + if (openbottom > tmfloorz) + { + tmfloorz = openbottom; + floorline = ld; // killough 8/1/98: remember floor linedef + blockline = ld; + } + + if (lowfloor < tmdropoffz) + tmdropoffz = lowfloor; + + // if contacted a special line, add it to the list + + if (ld->special) + { + // 1/11/98 killough: remove limit on lines hit, by array doubling + if (numspechit >= spechit_max) { + spechit_max = spechit_max ? spechit_max*2 : 8; + spechit = realloc(spechit,sizeof *spechit*spechit_max); // killough + } + spechit[numspechit++] = ld; + // e6y: Spechits overrun emulation code + if (numspechit >= 8 && demo_compatibility) + SpechitOverrun(ld); + } + + return true; +} + +// +// PIT_CheckThing +// + +static boolean PIT_CheckThing(mobj_t *thing) // killough 3/26/98: make static +{ + fixed_t blockdist; + int damage; + + // killough 11/98: add touchy things + if (!(thing->flags & (MF_SOLID|MF_SPECIAL|MF_SHOOTABLE|MF_TOUCHY))) + return true; + + blockdist = thing->radius + tmthing->radius; + + if (D_abs(thing->x - tmx) >= blockdist || D_abs(thing->y - tmy) >= blockdist) + return true; // didn't hit it + + // killough 11/98: + // + // This test has less information content (it's almost always false), so it + // should not be moved up to first, as it adds more overhead than it removes. + + // don't clip against self + + if (thing == tmthing) + return true; + + /* killough 11/98: + * + * TOUCHY flag, for mines or other objects which die on contact with solids. + * If a solid object of a different type comes in contact with a touchy + * thing, and the touchy thing is not the sole one moving relative to fixed + * surroundings such as walls, then the touchy thing dies immediately. + */ + + if (thing->flags & MF_TOUCHY && // touchy object + tmthing->flags & MF_SOLID && // solid object touches it + thing->health > 0 && // touchy object is alive + (thing->intflags & MIF_ARMED || // Thing is an armed mine + sentient(thing)) && // ... or a sentient thing + (thing->type != tmthing->type || // only different species + thing->type == MT_PLAYER) && // ... or different players + thing->z + thing->height >= tmthing->z && // touches vertically + tmthing->z + tmthing->height >= thing->z && + (thing->type ^ MT_PAIN) | // PEs and lost souls + (tmthing->type ^ MT_SKULL) && // are considered same + (thing->type ^ MT_SKULL) | // (but Barons & Knights + (tmthing->type ^ MT_PAIN)) // are intentionally not) + { + P_DamageMobj(thing, NULL, NULL, thing->health); // kill object + return true; + } + + // check for skulls slamming into things + + if (tmthing->flags & MF_SKULLFLY) + { + // A flying skull is smacking something. + // Determine damage amount, and the skull comes to a dead stop. + + int damage = ((P_Random(pr_skullfly)%8)+1)*tmthing->info->damage; + + P_DamageMobj (thing, tmthing, tmthing, damage); + + tmthing->flags &= ~MF_SKULLFLY; + tmthing->momx = tmthing->momy = tmthing->momz = 0; + + P_SetMobjState (tmthing, tmthing->info->spawnstate); + + return false; // stop moving + } + + // missiles can hit other things + // killough 8/10/98: bouncing non-solid things can hit other things too + + if (tmthing->flags & MF_MISSILE || (tmthing->flags & MF_BOUNCES && + !(tmthing->flags & MF_SOLID))) + { + // see if it went over / under + + if (tmthing->z > thing->z + thing->height) + return true; // overhead + + if (tmthing->z+tmthing->height < thing->z) + return true; // underneath + + if (tmthing->target && (tmthing->target->type == thing->type || + (tmthing->target->type == MT_KNIGHT && thing->type == MT_BRUISER)|| + (tmthing->target->type == MT_BRUISER && thing->type == MT_KNIGHT))) + { + if (thing == tmthing->target) + return true; // Don't hit same species as originator. + else + // e6y: Dehacked support - monsters infight + if (thing->type != MT_PLAYER && !monsters_infight) // Explode, but do no damage. + return false; // Let players missile other players. + } + + // killough 8/10/98: if moving thing is not a missile, no damage + // is inflicted, and momentum is reduced if object hit is solid. + + if (!(tmthing->flags & MF_MISSILE)) { + if (!(thing->flags & MF_SOLID)) { + return true; + } else { + tmthing->momx = -tmthing->momx; + tmthing->momy = -tmthing->momy; + if (!(tmthing->flags & MF_NOGRAVITY)) + { + tmthing->momx >>= 2; + tmthing->momy >>= 2; + } + return false; + } + } + + if (!(thing->flags & MF_SHOOTABLE)) + return !(thing->flags & MF_SOLID); // didn't do any damage + + // damage / explode + + damage = ((P_Random(pr_damage)%8)+1)*tmthing->info->damage; + P_DamageMobj (thing, tmthing, tmthing->target, damage); + + // don't traverse any more + return false; + } + + // check for special pickup + + if (thing->flags & MF_SPECIAL) + { + uint_64_t solid = thing->flags & MF_SOLID; + if (tmthing->flags & MF_PICKUP) + P_TouchSpecialThing(thing, tmthing); // can remove thing + return !solid; + } + + // killough 3/16/98: Allow non-solid moving objects to move through solid + // ones, by allowing the moving thing (tmthing) to move if it's non-solid, + // despite another solid thing being in the way. + // killough 4/11/98: Treat no-clipping things as not blocking + // ...but not in demo_compatibility mode + + return !(thing->flags & MF_SOLID) + || (!demo_compatibility + && (thing->flags & MF_NOCLIP || !(tmthing->flags & MF_SOLID))); + + // return !(thing->flags & MF_SOLID); // old code -- killough +} + +// This routine checks for Lost Souls trying to be spawned // phares +// across 1-sided lines, impassible lines, or "monsters can't // | +// cross" lines. Draw an imaginary line between the PE // V +// and the new Lost Soul spawn spot. If that line crosses +// a 'blocking' line, then disallow the spawn. Only search +// lines in the blocks of the blockmap where the bounding box +// of the trajectory line resides. Then check bounding box +// of the trajectory vs. the bounding box of each blocking +// line to see if the trajectory and the blocking line cross. +// Then check the PE and LS to see if they're on different +// sides of the blocking line. If so, return true, otherwise +// false. + +boolean Check_Sides(mobj_t* actor, int x, int y) + { + int bx,by,xl,xh,yl,yh; + + pe_x = actor->x; + pe_y = actor->y; + ls_x = x; + ls_y = y; + + // Here is the bounding box of the trajectory + + tmbbox[BOXLEFT] = pe_x < x ? pe_x : x; + tmbbox[BOXRIGHT] = pe_x > x ? pe_x : x; + tmbbox[BOXTOP] = pe_y > y ? pe_y : y; + tmbbox[BOXBOTTOM] = pe_y < y ? pe_y : y; + + // Determine which blocks to look in for blocking lines + + xl = (tmbbox[BOXLEFT] - bmaporgx)>>MAPBLOCKSHIFT; + xh = (tmbbox[BOXRIGHT] - bmaporgx)>>MAPBLOCKSHIFT; + yl = (tmbbox[BOXBOTTOM] - bmaporgy)>>MAPBLOCKSHIFT; + yh = (tmbbox[BOXTOP] - bmaporgy)>>MAPBLOCKSHIFT; + + // xl->xh, yl->yh determine the mapblock set to search + + validcount++; // prevents checking same line twice + for (bx = xl ; bx <= xh ; bx++) + for (by = yl ; by <= yh ; by++) + if (!P_BlockLinesIterator(bx,by,PIT_CrossLine)) + return true; // ^ + return(false); // | + } // phares + +// +// MOVEMENT CLIPPING +// + +// +// P_CheckPosition +// This is purely informative, nothing is modified +// (except things picked up). +// +// in: +// a mobj_t (can be valid or invalid) +// a position to be checked +// (doesn't need to be related to the mobj_t->x,y) +// +// during: +// special things are touched if MF_PICKUP +// early out on solid lines? +// +// out: +// newsubsec +// floorz +// ceilingz +// tmdropoffz +// the lowest point contacted +// (monsters won't move to a dropoff) +// speciallines[] +// numspeciallines +// + +boolean P_CheckPosition (mobj_t* thing,fixed_t x,fixed_t y) + { + int xl; + int xh; + int yl; + int yh; + int bx; + int by; + subsector_t* newsubsec; + + tmthing = thing; + + tmx = x; + tmy = y; + + tmbbox[BOXTOP] = y + tmthing->radius; + tmbbox[BOXBOTTOM] = y - tmthing->radius; + tmbbox[BOXRIGHT] = x + tmthing->radius; + tmbbox[BOXLEFT] = x - tmthing->radius; + + newsubsec = R_PointInSubsector (x,y); + floorline = blockline = ceilingline = NULL; // killough 8/1/98 + + // Whether object can get out of a sticky situation: + tmunstuck = thing->player && /* only players */ + thing->player->mo == thing && /* not voodoo dolls */ + mbf_features; /* not under old demos */ + + // The base floor / ceiling is from the subsector + // that contains the point. + // Any contacted lines the step closer together + // will adjust them. + + tmfloorz = tmdropoffz = newsubsec->sector->floorheight; + tmceilingz = newsubsec->sector->ceilingheight; + validcount++; + numspechit = 0; + + if ( tmthing->flags & MF_NOCLIP ) + return true; + + // Check things first, possibly picking things up. + // The bounding box is extended by MAXRADIUS + // because mobj_ts are grouped into mapblocks + // based on their origin point, and can overlap + // into adjacent blocks by up to MAXRADIUS units. + + xl = (tmbbox[BOXLEFT] - bmaporgx - MAXRADIUS)>>MAPBLOCKSHIFT; + xh = (tmbbox[BOXRIGHT] - bmaporgx + MAXRADIUS)>>MAPBLOCKSHIFT; + yl = (tmbbox[BOXBOTTOM] - bmaporgy - MAXRADIUS)>>MAPBLOCKSHIFT; + yh = (tmbbox[BOXTOP] - bmaporgy + MAXRADIUS)>>MAPBLOCKSHIFT; + + + for (bx=xl ; bx<=xh ; bx++) + for (by=yl ; by<=yh ; by++) + if (!P_BlockThingsIterator(bx,by,PIT_CheckThing)) + return false; + + // check lines + + xl = (tmbbox[BOXLEFT] - bmaporgx)>>MAPBLOCKSHIFT; + xh = (tmbbox[BOXRIGHT] - bmaporgx)>>MAPBLOCKSHIFT; + yl = (tmbbox[BOXBOTTOM] - bmaporgy)>>MAPBLOCKSHIFT; + yh = (tmbbox[BOXTOP] - bmaporgy)>>MAPBLOCKSHIFT; + + for (bx=xl ; bx<=xh ; bx++) + for (by=yl ; by<=yh ; by++) + if (!P_BlockLinesIterator (bx,by,PIT_CheckLine)) + return false; // doesn't fit + + return true; + } + + +// +// P_TryMove +// Attempt to move to a new position, +// crossing special lines unless MF_TELEPORT is set. +// +boolean P_TryMove(mobj_t* thing,fixed_t x,fixed_t y, + boolean dropoff) // killough 3/15/98: allow dropoff as option + { + fixed_t oldx; + fixed_t oldy; + + felldown = floatok = false; // killough 11/98 + + if (!P_CheckPosition (thing, x, y)) + return false; // solid wall or thing + + if ( !(thing->flags & MF_NOCLIP) ) + { + // killough 7/26/98: reformatted slightly + // killough 8/1/98: Possibly allow escape if otherwise stuck + + if (tmceilingz - tmfloorz < thing->height || // doesn't fit + // mobj must lower to fit + (floatok = true, !(thing->flags & MF_TELEPORT) && + tmceilingz - thing->z < thing->height) || + // too big a step up + (!(thing->flags & MF_TELEPORT) && + tmfloorz - thing->z > 24*FRACUNIT)) + return tmunstuck + && !(ceilingline && untouched(ceilingline)) + && !( floorline && untouched( floorline)); + + /* killough 3/15/98: Allow certain objects to drop off + * killough 7/24/98, 8/1/98: + * Prevent monsters from getting stuck hanging off ledges + * killough 10/98: Allow dropoffs in controlled circumstances + * killough 11/98: Improve symmetry of clipping on stairs + */ + + if (!(thing->flags & (MF_DROPOFF|MF_FLOAT))) { + if (comp[comp_dropoff]) + { + if ((compatibility || !dropoff + // fix demosync bug in mbf compatibility mode + || (mbf_features && compatibility_level <= prboom_2_compatibility)) + && (tmfloorz - tmdropoffz > 24*FRACUNIT)) + return false; // don't stand over a dropoff + } + else + if (!dropoff || (dropoff==2 && // large jump down (e.g. dogs) + (tmfloorz-tmdropoffz > 128*FRACUNIT || + !thing->target || thing->target->z >tmdropoffz))) + { + if (!monkeys || !mbf_features ? + tmfloorz - tmdropoffz > 24*FRACUNIT : + thing->floorz - tmfloorz > 24*FRACUNIT || + thing->dropoffz - tmdropoffz > 24*FRACUNIT) + return false; + } + else { /* dropoff allowed -- check for whether it fell more than 24 */ + felldown = !(thing->flags & MF_NOGRAVITY) && + thing->z - tmfloorz > 24*FRACUNIT; + } + } + + if (thing->flags & MF_BOUNCES && // killough 8/13/98 + !(thing->flags & (MF_MISSILE|MF_NOGRAVITY)) && + !sentient(thing) && tmfloorz - thing->z > 16*FRACUNIT) + return false; // too big a step up for bouncers under gravity + + // killough 11/98: prevent falling objects from going up too many steps + if (thing->intflags & MIF_FALLING && tmfloorz - thing->z > + FixedMul(thing->momx,thing->momx)+FixedMul(thing->momy,thing->momy)) + return false; + } + + // the move is ok, + // so unlink from the old position and link into the new position + + P_UnsetThingPosition (thing); + + oldx = thing->x; + oldy = thing->y; + thing->floorz = tmfloorz; + thing->ceilingz = tmceilingz; + thing->dropoffz = tmdropoffz; // killough 11/98: keep track of dropoffs + thing->x = x; + thing->y = y; + + P_SetThingPosition (thing); + + // if any special lines were hit, do the effect + + if (! (thing->flags&(MF_TELEPORT|MF_NOCLIP)) ) + while (numspechit--) + if (spechit[numspechit]->special) // see if the line was crossed + { + int oldside; + if ((oldside = P_PointOnLineSide(oldx, oldy, spechit[numspechit])) != + P_PointOnLineSide(thing->x, thing->y, spechit[numspechit])) + P_CrossSpecialLine(spechit[numspechit], oldside, thing); + } + + return true; + } + +/* + * killough 9/12/98: + * + * Apply "torque" to objects hanging off of ledges, so that they + * fall off. It's not really torque, since Doom has no concept of + * rotation, but it's a convincing effect which avoids anomalies + * such as lifeless objects hanging more than halfway off of ledges, + * and allows objects to roll off of the edges of moving lifts, or + * to slide up and then back down stairs, or to fall into a ditch. + * If more than one linedef is contacted, the effects are cumulative, + * so balancing is possible. + */ + +static boolean PIT_ApplyTorque(line_t *ld) +{ + if (ld->backsector && // If thing touches two-sided pivot linedef + tmbbox[BOXRIGHT] > ld->bbox[BOXLEFT] && + tmbbox[BOXLEFT] < ld->bbox[BOXRIGHT] && + tmbbox[BOXTOP] > ld->bbox[BOXBOTTOM] && + tmbbox[BOXBOTTOM] < ld->bbox[BOXTOP] && + P_BoxOnLineSide(tmbbox, ld) == -1) + { + mobj_t *mo = tmthing; + + fixed_t dist = // lever arm + + (ld->dx >> FRACBITS) * (mo->y >> FRACBITS) + - (ld->dy >> FRACBITS) * (mo->x >> FRACBITS) + - (ld->dx >> FRACBITS) * (ld->v1->y >> FRACBITS) + + (ld->dy >> FRACBITS) * (ld->v1->x >> FRACBITS); + + if (dist < 0 ? // dropoff direction + ld->frontsector->floorheight < mo->z && + ld->backsector->floorheight >= mo->z : + ld->backsector->floorheight < mo->z && + ld->frontsector->floorheight >= mo->z) + { + /* At this point, we know that the object straddles a two-sided + * linedef, and that the object's center of mass is above-ground. + */ + + fixed_t x = D_abs(ld->dx), y = D_abs(ld->dy); + + if (y > x) + { + fixed_t t = x; + x = y; + y = t; + } + + y = finesine[(tantoangle[FixedDiv(y,x)>>DBITS] + + ANG90) >> ANGLETOFINESHIFT]; + + /* Momentum is proportional to distance between the + * object's center of mass and the pivot linedef. + * + * It is scaled by 2^(OVERDRIVE - gear). When gear is + * increased, the momentum gradually decreases to 0 for + * the same amount of pseudotorque, so that oscillations + * are prevented, yet it has a chance to reach equilibrium. + */ + dist = FixedDiv(FixedMul(dist, (mo->gear < OVERDRIVE) ? + y << -(mo->gear - OVERDRIVE) : + y >> +(mo->gear - OVERDRIVE)), x); + + /* Apply momentum away from the pivot linedef. */ + + x = FixedMul(ld->dy, dist); + y = FixedMul(ld->dx, dist); + + /* Avoid moving too fast all of a sudden (step into "overdrive") */ + + dist = FixedMul(x,x) + FixedMul(y,y); + + while (dist > FRACUNIT*4 && mo->gear < MAXGEAR) + ++mo->gear, x >>= 1, y >>= 1, dist >>= 1; + + mo->momx -= x; + mo->momy += y; + } + } + return true; +} + +/* + * killough 9/12/98 + * + * Applies "torque" to objects, based on all contacted linedefs + */ + +void P_ApplyTorque(mobj_t *mo) +{ + int xl = ((tmbbox[BOXLEFT] = + mo->x - mo->radius) - bmaporgx) >> MAPBLOCKSHIFT; + int xh = ((tmbbox[BOXRIGHT] = + mo->x + mo->radius) - bmaporgx) >> MAPBLOCKSHIFT; + int yl = ((tmbbox[BOXBOTTOM] = + mo->y - mo->radius) - bmaporgy) >> MAPBLOCKSHIFT; + int yh = ((tmbbox[BOXTOP] = + mo->y + mo->radius) - bmaporgy) >> MAPBLOCKSHIFT; + int bx,by,flags = mo->intflags; //Remember the current state, for gear-change + + tmthing = mo; + validcount++; /* prevents checking same line twice */ + + for (bx = xl ; bx <= xh ; bx++) + for (by = yl ; by <= yh ; by++) + P_BlockLinesIterator(bx, by, PIT_ApplyTorque); + + /* If any momentum, mark object as 'falling' using engine-internal flags */ + if (mo->momx | mo->momy) + mo->intflags |= MIF_FALLING; + else // Clear the engine-internal flag indicating falling object. + mo->intflags &= ~MIF_FALLING; + + /* If the object has been moving, step up the gear. + * This helps reach equilibrium and avoid oscillations. + * + * Doom has no concept of potential energy, much less + * of rotation, so we have to creatively simulate these + * systems somehow :) + */ + + if (!((mo->intflags | flags) & MIF_FALLING)) // If not falling for a while, + mo->gear = 0; // Reset it to full strength + else + if (mo->gear < MAXGEAR) // Else if not at max gear, + mo->gear++; // move up a gear +} + +// +// P_ThingHeightClip +// Takes a valid thing and adjusts the thing->floorz, +// thing->ceilingz, and possibly thing->z. +// This is called for all nearby monsters +// whenever a sector changes height. +// If the thing doesn't fit, +// the z will be set to the lowest value +// and false will be returned. +// + +boolean P_ThingHeightClip (mobj_t* thing) +{ + boolean onfloor; + + onfloor = (thing->z == thing->floorz); + + P_CheckPosition (thing, thing->x, thing->y); + + /* what about stranding a monster partially off an edge? + * killough 11/98: Answer: see below (upset balance if hanging off ledge) + */ + + thing->floorz = tmfloorz; + thing->ceilingz = tmceilingz; + thing->dropoffz = tmdropoffz; /* killough 11/98: remember dropoffs */ + + if (onfloor) + { + + // walking monsters rise and fall with the floor + + thing->z = thing->floorz; + + /* killough 11/98: Possibly upset balance of objects hanging off ledges */ + if (thing->intflags & MIF_FALLING && thing->gear >= MAXGEAR) + thing->gear = 0; + } + else + { + + // don't adjust a floating monster unless forced to + + if (thing->z+thing->height > thing->ceilingz) + thing->z = thing->ceilingz - thing->height; + } + + return thing->ceilingz - thing->floorz >= thing->height; +} + + +// +// SLIDE MOVE +// Allows the player to slide along any angled walls. +// + +/* killough 8/2/98: make variables static */ +static fixed_t bestslidefrac; +static line_t* bestslideline; +static mobj_t* slidemo; +static fixed_t tmxmove; +static fixed_t tmymove; + + +// +// P_HitSlideLine +// Adjusts the xmove / ymove +// so that the next move will slide along the wall. +// If the floor is icy, then you can bounce off a wall. // phares +// + +void P_HitSlideLine (line_t* ld) + { + int side; + angle_t lineangle; + angle_t moveangle; + angle_t deltaangle; + fixed_t movelen; + fixed_t newlen; + boolean icyfloor; // is floor icy? // phares + // | + // Under icy conditions, if the angle of approach to the wall // V + // is more than 45 degrees, then you'll bounce and lose half + // your momentum. If less than 45 degrees, you'll slide along + // the wall. 45 is arbitrary and is believable. + + // Check for the special cases of horz or vert walls. + + /* killough 10/98: only bounce if hit hard (prevents wobbling) + * cph - DEMOSYNC - should only affect players in Boom demos? */ + + //e6y + if (mbf_features) + { + icyfloor = + P_AproxDistance(tmxmove, tmymove) > 4*FRACUNIT && + variable_friction && // killough 8/28/98: calc friction on demand + slidemo->z <= slidemo->floorz && + P_GetFriction(slidemo, NULL) > ORIG_FRICTION; + } + else + { + extern boolean onground; + icyfloor = !compatibility && + variable_friction && + slidemo->player && + onground && + slidemo->friction > ORIG_FRICTION; + } + + if (ld->slopetype == ST_HORIZONTAL) + { + if (icyfloor && (D_abs(tmymove) > D_abs(tmxmove))) + { + tmxmove /= 2; // absorb half the momentum + tmymove = -tmymove/2; + S_StartSound(slidemo,sfx_oof); // oooff! + } + else + tmymove = 0; // no more movement in the Y direction + return; + } + + if (ld->slopetype == ST_VERTICAL) + { + if (icyfloor && (D_abs(tmxmove) > D_abs(tmymove))) + { + tmxmove = -tmxmove/2; // absorb half the momentum + tmymove /= 2; + S_StartSound(slidemo,sfx_oof); // oooff! // ^ + } // | + else // phares + tmxmove = 0; // no more movement in the X direction + return; + } + + // The wall is angled. Bounce if the angle of approach is // phares + // less than 45 degrees. // phares + + side = P_PointOnLineSide (slidemo->x, slidemo->y, ld); + + lineangle = R_PointToAngle2 (0,0, ld->dx, ld->dy); + if (side == 1) + lineangle += ANG180; + moveangle = R_PointToAngle2 (0,0, tmxmove, tmymove); + + // killough 3/2/98: + // The moveangle+=10 breaks v1.9 demo compatibility in + // some demos, so it needs demo_compatibility switch. + + if (!demo_compatibility) + moveangle += 10; // prevents sudden path reversal due to // phares + // rounding error // | + deltaangle = moveangle-lineangle; // V + movelen = P_AproxDistance (tmxmove, tmymove); + if (icyfloor && (deltaangle > ANG45) && (deltaangle < ANG90+ANG45)) + { + moveangle = lineangle - deltaangle; + movelen /= 2; // absorb + S_StartSound(slidemo,sfx_oof); // oooff! + moveangle >>= ANGLETOFINESHIFT; + tmxmove = FixedMul (movelen, finecosine[moveangle]); + tmymove = FixedMul (movelen, finesine[moveangle]); + } // ^ + else // | + { // phares + if (deltaangle > ANG180) + deltaangle += ANG180; + + // I_Error ("SlideLine: ang>ANG180"); + + lineangle >>= ANGLETOFINESHIFT; + deltaangle >>= ANGLETOFINESHIFT; + newlen = FixedMul (movelen, finecosine[deltaangle]); + tmxmove = FixedMul (newlen, finecosine[lineangle]); + tmymove = FixedMul (newlen, finesine[lineangle]); + } // phares + } + + +// +// PTR_SlideTraverse +// + +boolean PTR_SlideTraverse (intercept_t* in) + { + line_t* li; + + if (!in->isaline) + I_Error ("PTR_SlideTraverse: not a line?"); + + li = in->d.line; + + if ( ! (li->flags & ML_TWOSIDED) ) + { + if (P_PointOnLineSide (slidemo->x, slidemo->y, li)) + return true; // don't hit the back side + goto isblocking; + } + + // set openrange, opentop, openbottom. + // These define a 'window' from one sector to another across a line + + P_LineOpening (li); + + if (openrange < slidemo->height) + goto isblocking; // doesn't fit + + if (opentop - slidemo->z < slidemo->height) + goto isblocking; // mobj is too high + + if (openbottom - slidemo->z > 24*FRACUNIT ) + goto isblocking; // too big a step up + + // this line doesn't block movement + + return true; + + // the line does block movement, + // see if it is closer than best so far + +isblocking: + + if (in->frac < bestslidefrac) + { + bestslidefrac = in->frac; + bestslideline = li; + } + + return false; // stop + } + + +// +// P_SlideMove +// The momx / momy move is bad, so try to slide +// along a wall. +// Find the first line hit, move flush to it, +// and slide along it +// +// This is a kludgy mess. +// +// killough 11/98: reformatted + +void P_SlideMove(mobj_t *mo) +{ + int hitcount = 3; + + slidemo = mo; // the object that's sliding + + do + { + fixed_t leadx, leady, trailx, traily; + + if (!--hitcount) + goto stairstep; // don't loop forever + + // trace along the three leading corners + + if (mo->momx > 0) + leadx = mo->x + mo->radius, trailx = mo->x - mo->radius; + else + leadx = mo->x - mo->radius, trailx = mo->x + mo->radius; + + if (mo->momy > 0) + leady = mo->y + mo->radius, traily = mo->y - mo->radius; + else + leady = mo->y - mo->radius, traily = mo->y + mo->radius; + + bestslidefrac = FRACUNIT+1; + + P_PathTraverse(leadx, leady, leadx+mo->momx, leady+mo->momy, + PT_ADDLINES, PTR_SlideTraverse); + P_PathTraverse(trailx, leady, trailx+mo->momx, leady+mo->momy, + PT_ADDLINES, PTR_SlideTraverse); + P_PathTraverse(leadx, traily, leadx+mo->momx, traily+mo->momy, + PT_ADDLINES, PTR_SlideTraverse); + + // move up to the wall + + if (bestslidefrac == FRACUNIT+1) + { + // the move must have hit the middle, so stairstep + + stairstep: + + /* killough 3/15/98: Allow objects to drop off ledges + * + * phares 5/4/98: kill momentum if you can't move at all + * This eliminates player bobbing if pressed against a wall + * while on ice. + * + * killough 10/98: keep buggy code around for old Boom demos + * + * cph 2000/09//23: buggy code was only in Boom v2.01 + */ + + if (!P_TryMove(mo, mo->x, mo->y + mo->momy, true)) + if (!P_TryMove(mo, mo->x + mo->momx, mo->y, true)) + if (compatibility_level == boom_201_compatibility) + mo->momx = mo->momy = 0; + + break; + } + + // fudge a bit to make sure it doesn't hit + + if ((bestslidefrac -= 0x800) > 0) + { + fixed_t newx = FixedMul(mo->momx, bestslidefrac); + fixed_t newy = FixedMul(mo->momy, bestslidefrac); + + // killough 3/15/98: Allow objects to drop off ledges + + if (!P_TryMove(mo, mo->x+newx, mo->y+newy, true)) + goto stairstep; + } + + // Now continue along the wall. + // First calculate remainder. + + bestslidefrac = FRACUNIT-(bestslidefrac+0x800); + + if (bestslidefrac > FRACUNIT) + bestslidefrac = FRACUNIT; + + if (bestslidefrac <= 0) + break; + + tmxmove = FixedMul(mo->momx, bestslidefrac); + tmymove = FixedMul(mo->momy, bestslidefrac); + + P_HitSlideLine(bestslideline); // clip the moves + + mo->momx = tmxmove; + mo->momy = tmymove; + + /* killough 10/98: affect the bobbing the same way (but not voodoo dolls) + * cph - DEMOSYNC? */ + if (mo->player && mo->player->mo == mo) + { + if (D_abs(mo->player->momx) > D_abs(tmxmove)) + mo->player->momx = tmxmove; + if (D_abs(mo->player->momy) > D_abs(tmymove)) + mo->player->momy = tmymove; + } + } // killough 3/15/98: Allow objects to drop off ledges: + while (!P_TryMove(mo, mo->x+tmxmove, mo->y+tmymove, true)); +} + +// +// P_LineAttack +// +mobj_t* linetarget; // who got hit (or NULL) +static mobj_t* shootthing; + +/* killough 8/2/98: for more intelligent autoaiming */ +static uint_64_t aim_flags_mask; + +// Height if not aiming up or down +fixed_t shootz; + +int la_damage; +fixed_t attackrange; + +static fixed_t aimslope; + +// slopes to top and bottom of target +// killough 4/20/98: make static instead of using ones in p_sight.c + +static fixed_t topslope; +static fixed_t bottomslope; + + +// +// PTR_AimTraverse +// Sets linetaget and aimslope when a target is aimed at. +// +boolean PTR_AimTraverse (intercept_t* in) + { + line_t* li; + mobj_t* th; + fixed_t slope; + fixed_t thingtopslope; + fixed_t thingbottomslope; + fixed_t dist; + + if (in->isaline) + { + li = in->d.line; + + if ( !(li->flags & ML_TWOSIDED) ) + return false; // stop + + // Crosses a two sided line. + // A two sided line will restrict + // the possible target ranges. + + P_LineOpening (li); + + if (openbottom >= opentop) + return false; // stop + + dist = FixedMul (attackrange, in->frac); + + if (li->frontsector->floorheight != li->backsector->floorheight) + { + slope = FixedDiv (openbottom - shootz , dist); + if (slope > bottomslope) + bottomslope = slope; + } + + if (li->frontsector->ceilingheight != li->backsector->ceilingheight) + { + slope = FixedDiv (opentop - shootz , dist); + if (slope < topslope) + topslope = slope; + } + + if (topslope <= bottomslope) + return false; // stop + + return true; // shot continues + } + + // shoot a thing + + th = in->d.thing; + if (th == shootthing) + return true; // can't shoot self + + if (!(th->flags&MF_SHOOTABLE)) + return true; // corpse or something + + /* killough 7/19/98, 8/2/98: + * friends don't aim at friends (except players), at least not first + */ + if (th->flags & shootthing->flags & aim_flags_mask && !th->player) + return true; + + // check angles to see if the thing can be aimed at + + dist = FixedMul (attackrange, in->frac); + thingtopslope = FixedDiv (th->z+th->height - shootz , dist); + + if (thingtopslope < bottomslope) + return true; // shot over the thing + + thingbottomslope = FixedDiv (th->z - shootz, dist); + + if (thingbottomslope > topslope) + return true; // shot under the thing + + // this thing can be hit! + + if (thingtopslope > topslope) + thingtopslope = topslope; + + if (thingbottomslope < bottomslope) + thingbottomslope = bottomslope; + + aimslope = (thingtopslope+thingbottomslope)/2; + linetarget = th; + + return false; // don't go any farther + } + + +// +// PTR_ShootTraverse +// +boolean PTR_ShootTraverse (intercept_t* in) + { + fixed_t x; + fixed_t y; + fixed_t z; + fixed_t frac; + + mobj_t* th; + + fixed_t slope; + fixed_t dist; + fixed_t thingtopslope; + fixed_t thingbottomslope; + + if (in->isaline) + { + line_t *li = in->d.line; + + if (li->special) + P_ShootSpecialLine (shootthing, li); + + if (li->flags & ML_TWOSIDED) + { // crosses a two sided (really 2s) line + P_LineOpening (li); + dist = FixedMul(attackrange, in->frac); + + // killough 11/98: simplify + + if ((li->frontsector->floorheight==li->backsector->floorheight || + (slope = FixedDiv(openbottom - shootz , dist)) <= aimslope) && + (li->frontsector->ceilingheight==li->backsector->ceilingheight || + (slope = FixedDiv (opentop - shootz , dist)) >= aimslope)) + return true; // shot continues + } + + // hit line + // position a bit closer + + frac = in->frac - FixedDiv (4*FRACUNIT,attackrange); + x = trace.x + FixedMul (trace.dx, frac); + y = trace.y + FixedMul (trace.dy, frac); + z = shootz + FixedMul (aimslope, FixedMul(frac, attackrange)); + + if (li->frontsector->ceilingpic == skyflatnum) + { + // don't shoot the sky! + + if (z > li->frontsector->ceilingheight) + return false; + + // it's a sky hack wall + + if (li->backsector && li->backsector->ceilingpic == skyflatnum) + + // fix bullet-eaters -- killough: + // WARNING: Almost all demos will lose sync without this + // demo_compatibility flag check!!! killough 1/18/98 + if (demo_compatibility || li->backsector->ceilingheight < z) + return false; + } + + // Spawn bullet puffs. + + P_SpawnPuff (x,y,z); + + // don't go any farther + + return false; + } + + // shoot a thing + + th = in->d.thing; + if (th == shootthing) + return true; // can't shoot self + + if (!(th->flags&MF_SHOOTABLE)) + return true; // corpse or something + + // check angles to see if the thing can be aimed at + + dist = FixedMul (attackrange, in->frac); + thingtopslope = FixedDiv (th->z+th->height - shootz , dist); + + if (thingtopslope < aimslope) + return true; // shot over the thing + + thingbottomslope = FixedDiv (th->z - shootz, dist); + + if (thingbottomslope > aimslope) + return true; // shot under the thing + + // hit thing + // position a bit closer + + frac = in->frac - FixedDiv (10*FRACUNIT,attackrange); + + x = trace.x + FixedMul (trace.dx, frac); + y = trace.y + FixedMul (trace.dy, frac); + z = shootz + FixedMul (aimslope, FixedMul(frac, attackrange)); + + // Spawn bullet puffs or blod spots, + // depending on target type. + if (in->d.thing->flags & MF_NOBLOOD) + P_SpawnPuff (x,y,z); + else + P_SpawnBlood (x,y,z, la_damage); + + if (la_damage) + P_DamageMobj (th, shootthing, shootthing, la_damage); + + // don't go any farther + return false; + } + + +// +// P_AimLineAttack +// +fixed_t P_AimLineAttack(mobj_t* t1,angle_t angle,fixed_t distance, uint_64_t mask) + { + fixed_t x2; + fixed_t y2; + + angle >>= ANGLETOFINESHIFT; + shootthing = t1; + + x2 = t1->x + (distance>>FRACBITS)*finecosine[angle]; + y2 = t1->y + (distance>>FRACBITS)*finesine[angle]; + shootz = t1->z + (t1->height>>1) + 8*FRACUNIT; + + // can't shoot outside view angles + + topslope = 100*FRACUNIT/160; + bottomslope = -100*FRACUNIT/160; + + attackrange = distance; + linetarget = NULL; + + /* killough 8/2/98: prevent friends from aiming at friends */ + aim_flags_mask = mask; + + P_PathTraverse(t1->x,t1->y,x2,y2,PT_ADDLINES|PT_ADDTHINGS,PTR_AimTraverse); + + if (linetarget) + return aimslope; + + return 0; + } + + +// +// P_LineAttack +// If damage == 0, it is just a test trace +// that will leave linetarget set. +// + +void P_LineAttack +(mobj_t* t1, + angle_t angle, + fixed_t distance, + fixed_t slope, + int damage) + { + fixed_t x2; + fixed_t y2; + + angle >>= ANGLETOFINESHIFT; + shootthing = t1; + la_damage = damage; + x2 = t1->x + (distance>>FRACBITS)*finecosine[angle]; + y2 = t1->y + (distance>>FRACBITS)*finesine[angle]; + shootz = t1->z + (t1->height>>1) + 8*FRACUNIT; + attackrange = distance; + aimslope = slope; + + P_PathTraverse(t1->x,t1->y,x2,y2,PT_ADDLINES|PT_ADDTHINGS,PTR_ShootTraverse); + } + + +// +// USE LINES +// + +mobj_t* usething; + +boolean PTR_UseTraverse (intercept_t* in) + { + int side; + + if (!in->d.line->special) + { + P_LineOpening (in->d.line); + if (openrange <= 0) + { + S_StartSound (usething, sfx_noway); + + // can't use through a wall + return false; + } + + // not a special line, but keep checking + + return true; + } + + side = 0; + if (P_PointOnLineSide (usething->x, usething->y, in->d.line) == 1) + side = 1; + + // return false; // don't use back side + + P_UseSpecialLine (usething, in->d.line, side); + + //WAS can't use for than one special line in a row + //jff 3/21/98 NOW multiple use allowed with enabling line flag + + return (!demo_compatibility && (in->d.line->flags&ML_PASSUSE))? + true : false; +} + +// Returns false if a "oof" sound should be made because of a blocking +// linedef. Makes 2s middles which are impassable, as well as 2s uppers +// and lowers which block the player, cause the sound effect when the +// player tries to activate them. Specials are excluded, although it is +// assumed that all special linedefs within reach have been considered +// and rejected already (see P_UseLines). +// +// by Lee Killough +// + +boolean PTR_NoWayTraverse(intercept_t* in) + { + line_t *ld = in->d.line; + // This linedef + return ld->special || !( // Ignore specials + ld->flags & ML_BLOCKING || ( // Always blocking + P_LineOpening(ld), // Find openings + openrange <= 0 || // No opening + openbottom > usething->z+24*FRACUNIT || // Too high it blocks + opentop < usething->z+usething->height // Too low it blocks + ) + ); + } + +// +// P_UseLines +// Looks for special lines in front of the player to activate. +// +void P_UseLines (player_t* player) + { + int angle; + fixed_t x1; + fixed_t y1; + fixed_t x2; + fixed_t y2; + + usething = player->mo; + + angle = player->mo->angle >> ANGLETOFINESHIFT; + + x1 = player->mo->x; + y1 = player->mo->y; + x2 = x1 + (USERANGE>>FRACBITS)*finecosine[angle]; + y2 = y1 + (USERANGE>>FRACBITS)*finesine[angle]; + + // old code: + // + // P_PathTraverse ( x1, y1, x2, y2, PT_ADDLINES, PTR_UseTraverse ); + // + // This added test makes the "oof" sound work on 2s lines -- killough: + + if (P_PathTraverse ( x1, y1, x2, y2, PT_ADDLINES, PTR_UseTraverse )) + if (!comp[comp_sound] && !P_PathTraverse ( x1, y1, x2, y2, PT_ADDLINES, PTR_NoWayTraverse )) + S_StartSound (usething, sfx_noway); + } + + +// +// RADIUS ATTACK +// + +static mobj_t *bombsource, *bombspot; +static int bombdamage; + + +// +// PIT_RadiusAttack +// "bombsource" is the creature +// that caused the explosion at "bombspot". +// + +boolean PIT_RadiusAttack (mobj_t* thing) + { + fixed_t dx; + fixed_t dy; + fixed_t dist; + + /* killough 8/20/98: allow bouncers to take damage + * (missile bouncers are already excluded with MF_NOBLOCKMAP) + */ + + if (!(thing->flags & (MF_SHOOTABLE | MF_BOUNCES))) + return true; + + // Boss spider and cyborg + // take no damage from concussion. + + // killough 8/10/98: allow grenades to hurt anyone, unless + // fired by Cyberdemons, in which case it won't hurt Cybers. + + if (bombspot->flags & MF_BOUNCES ? + thing->type == MT_CYBORG && bombsource->type == MT_CYBORG : + thing->type == MT_CYBORG || thing->type == MT_SPIDER) + return true; + + dx = D_abs(thing->x - bombspot->x); + dy = D_abs(thing->y - bombspot->y); + + dist = dx>dy ? dx : dy; + dist = (dist - thing->radius) >> FRACBITS; + + if (dist < 0) + dist = 0; + + if (dist >= bombdamage) + return true; // out of range + + if ( P_CheckSight (thing, bombspot) ) + { + // must be in direct path + P_DamageMobj (thing, bombspot, bombsource, bombdamage - dist); + } + + return true; + } + + +// +// P_RadiusAttack +// Source is the creature that caused the explosion at spot. +// +void P_RadiusAttack(mobj_t* spot,mobj_t* source,int damage) + { + int x; + int y; + + int xl; + int xh; + int yl; + int yh; + + fixed_t dist; + + dist = (damage+MAXRADIUS)<y + dist - bmaporgy)>>MAPBLOCKSHIFT; + yl = (spot->y - dist - bmaporgy)>>MAPBLOCKSHIFT; + xh = (spot->x + dist - bmaporgx)>>MAPBLOCKSHIFT; + xl = (spot->x - dist - bmaporgx)>>MAPBLOCKSHIFT; + bombspot = spot; + bombsource = source; + bombdamage = damage; + + for (y=yl ; y<=yh ; y++) + for (x=xl ; x<=xh ; x++) + P_BlockThingsIterator (x, y, PIT_RadiusAttack ); + } + + + +// +// SECTOR HEIGHT CHANGING +// After modifying a sectors floor or ceiling height, +// call this routine to adjust the positions +// of all things that touch the sector. +// +// If anything doesn't fit anymore, true will be returned. +// If crunch is true, they will take damage +// as they are being crushed. +// If Crunch is false, you should set the sector height back +// the way it was and call P_ChangeSector again +// to undo the changes. +// + +static boolean crushchange, nofit; + +// +// PIT_ChangeSector +// + +boolean PIT_ChangeSector (mobj_t* thing) + { + mobj_t* mo; + + if (P_ThingHeightClip (thing)) + return true; // keep checking + + // crunch bodies to giblets + + if (thing->health <= 0) + { + P_SetMobjState (thing, S_GIBS); + + thing->flags &= ~MF_SOLID; + thing->height = 0; + thing->radius = 0; + return true; // keep checking + } + + // crunch dropped items + + if (thing->flags & MF_DROPPED) + { + P_RemoveMobj (thing); + + // keep checking + return true; + } + + /* killough 11/98: kill touchy things immediately */ + if (thing->flags & MF_TOUCHY && + (thing->intflags & MIF_ARMED || sentient(thing))) + { + P_DamageMobj(thing, NULL, NULL, thing->health); // kill object + return true; // keep checking + } + + if (! (thing->flags & MF_SHOOTABLE) ) + { + // assume it is bloody gibs or something + return true; + } + + nofit = true; + + if (crushchange && !(leveltime&3)) { + int t; + P_DamageMobj(thing,NULL,NULL,10); + + // spray blood in a random direction + mo = P_SpawnMobj (thing->x, + thing->y, + thing->z + thing->height/2, MT_BLOOD); + + /* killough 8/10/98: remove dependence on order of evaluation */ + t = P_Random(pr_crush); + mo->momx = (t - P_Random (pr_crush))<<12; + t = P_Random(pr_crush); + mo->momy = (t - P_Random (pr_crush))<<12; + } + + // keep checking (crush other things) + return true; + } + + +// +// P_ChangeSector +// +boolean P_ChangeSector(sector_t* sector,boolean crunch) + { + int x; + int y; + + nofit = false; + crushchange = crunch; + + // ARRGGHHH!!!! + // This is horrendously slow!!! + // killough 3/14/98 + + // re-check heights for all things near the moving sector + + for (x=sector->blockbox[BOXLEFT] ; x<= sector->blockbox[BOXRIGHT] ; x++) + for (y=sector->blockbox[BOXBOTTOM];y<= sector->blockbox[BOXTOP] ; y++) + P_BlockThingsIterator (x, y, PIT_ChangeSector); + + return nofit; + } + +// +// P_CheckSector +// jff 3/19/98 added to just check monsters on the periphery +// of a moving sector instead of all in bounding box of the +// sector. Both more accurate and faster. +// + +boolean P_CheckSector(sector_t* sector,boolean crunch) + { + msecnode_t *n; + + if (comp[comp_floors]) /* use the old routine for old demos though */ + return P_ChangeSector(sector,crunch); + + nofit = false; + crushchange = crunch; + + // killough 4/4/98: scan list front-to-back until empty or exhausted, + // restarting from beginning after each thing is processed. Avoids + // crashes, and is sure to examine all things in the sector, and only + // the things which are in the sector, until a steady-state is reached. + // Things can arbitrarily be inserted and removed and it won't mess up. + // + // killough 4/7/98: simplified to avoid using complicated counter + + // Mark all things invalid + + for (n=sector->touching_thinglist; n; n=n->m_snext) + n->visited = false; + + do + for (n=sector->touching_thinglist; n; n=n->m_snext) // go through list + if (!n->visited) // unprocessed thing found + { + n->visited = true; // mark thing as processed + if (!(n->m_thing->flags & MF_NOBLOCKMAP)) //jff 4/7/98 don't do these + PIT_ChangeSector(n->m_thing); // process it + break; // exit and start over + } + while (n); // repeat from scratch until all things left are marked valid + + return nofit; + } + + +// CPhipps - +// Use block memory allocator here + +#include "z_bmalloc.h" + +IMPLEMENT_BLOCK_MEMORY_ALLOC_ZONE(secnodezone, sizeof(msecnode_t), PU_LEVEL, 32, "SecNodes"); + +inline static msecnode_t* P_GetSecnode(void) +{ + return (msecnode_t*)Z_BMalloc(&secnodezone); +} + +// P_PutSecnode() returns a node to the freelist. + +inline static void P_PutSecnode(msecnode_t* node) +{ + Z_BFree(&secnodezone, node); +} + +// phares 3/16/98 +// +// P_AddSecnode() searches the current list to see if this sector is +// already there. If not, it adds a sector node at the head of the list of +// sectors this object appears in. This is called when creating a list of +// nodes that will get linked in later. Returns a pointer to the new node. + +msecnode_t* P_AddSecnode(sector_t* s, mobj_t* thing, msecnode_t* nextnode) + { + msecnode_t* node; + + node = nextnode; + while (node) + { + if (node->m_sector == s) // Already have a node for this sector? + { + node->m_thing = thing; // Yes. Setting m_thing says 'keep it'. + return(nextnode); + } + node = node->m_tnext; + } + + // Couldn't find an existing node for this sector. Add one at the head + // of the list. + + node = P_GetSecnode(); + + // killough 4/4/98, 4/7/98: mark new nodes unvisited. + node->visited = 0; + + node->m_sector = s; // sector + node->m_thing = thing; // mobj + node->m_tprev = NULL; // prev node on Thing thread + node->m_tnext = nextnode; // next node on Thing thread + if (nextnode) + nextnode->m_tprev = node; // set back link on Thing + + // Add new node at head of sector thread starting at s->touching_thinglist + + node->m_sprev = NULL; // prev node on sector thread + node->m_snext = s->touching_thinglist; // next node on sector thread + if (s->touching_thinglist) + node->m_snext->m_sprev = node; + s->touching_thinglist = node; + return(node); + } + + +// P_DelSecnode() deletes a sector node from the list of +// sectors this object appears in. Returns a pointer to the next node +// on the linked list, or NULL. + +msecnode_t* P_DelSecnode(msecnode_t* node) + { + msecnode_t* tp; // prev node on thing thread + msecnode_t* tn; // next node on thing thread + msecnode_t* sp; // prev node on sector thread + msecnode_t* sn; // next node on sector thread + + if (node) + { + + // Unlink from the Thing thread. The Thing thread begins at + // sector_list and not from mobj_t->touching_sectorlist. + + tp = node->m_tprev; + tn = node->m_tnext; + if (tp) + tp->m_tnext = tn; + if (tn) + tn->m_tprev = tp; + + // Unlink from the sector thread. This thread begins at + // sector_t->touching_thinglist. + + sp = node->m_sprev; + sn = node->m_snext; + if (sp) + sp->m_snext = sn; + else + node->m_sector->touching_thinglist = sn; + if (sn) + sn->m_sprev = sp; + + // Return this node to the freelist + + P_PutSecnode(node); + return(tn); + } + return(NULL); + } // phares 3/13/98 + +// Delete an entire sector list + +void P_DelSeclist(msecnode_t* node) + + { + while (node) + node = P_DelSecnode(node); + } + + +// phares 3/14/98 +// +// PIT_GetSectors +// Locates all the sectors the object is in by looking at the lines that +// cross through it. You have already decided that the object is allowed +// at this location, so don't bother with checking impassable or +// blocking lines. + +boolean PIT_GetSectors(line_t* ld) + { + if (tmbbox[BOXRIGHT] <= ld->bbox[BOXLEFT] || + tmbbox[BOXLEFT] >= ld->bbox[BOXRIGHT] || + tmbbox[BOXTOP] <= ld->bbox[BOXBOTTOM] || + tmbbox[BOXBOTTOM] >= ld->bbox[BOXTOP]) + return true; + + if (P_BoxOnLineSide(tmbbox, ld) != -1) + return true; + + // This line crosses through the object. + + // Collect the sector(s) from the line and add to the + // sector_list you're examining. If the Thing ends up being + // allowed to move to this position, then the sector_list + // will be attached to the Thing's mobj_t at touching_sectorlist. + + sector_list = P_AddSecnode(ld->frontsector,tmthing,sector_list); + + /* Don't assume all lines are 2-sided, since some Things + * like MT_TFOG are allowed regardless of whether their radius takes + * them beyond an impassable linedef. + * + * killough 3/27/98, 4/4/98: + * Use sidedefs instead of 2s flag to determine two-sidedness. + * killough 8/1/98: avoid duplicate if same sector on both sides + * cph - DEMOSYNC? */ + + if (ld->backsector && ld->backsector != ld->frontsector) + sector_list = P_AddSecnode(ld->backsector, tmthing, sector_list); + + return true; + } + + +// phares 3/14/98 +// +// P_CreateSecNodeList alters/creates the sector_list that shows what sectors +// the object resides in. + +void P_CreateSecNodeList(mobj_t* thing,fixed_t x,fixed_t y) +{ + int xl; + int xh; + int yl; + int yh; + int bx; + int by; + msecnode_t* node; + mobj_t* saved_tmthing = tmthing; /* cph - see comment at func end */ + fixed_t saved_tmx = tmx, saved_tmy = tmy; /* ditto */ + + // First, clear out the existing m_thing fields. As each node is + // added or verified as needed, m_thing will be set properly. When + // finished, delete all nodes where m_thing is still NULL. These + // represent the sectors the Thing has vacated. + + node = sector_list; + while (node) + { + node->m_thing = NULL; + node = node->m_tnext; + } + + tmthing = thing; + + tmx = x; + tmy = y; + + tmbbox[BOXTOP] = y + tmthing->radius; + tmbbox[BOXBOTTOM] = y - tmthing->radius; + tmbbox[BOXRIGHT] = x + tmthing->radius; + tmbbox[BOXLEFT] = x - tmthing->radius; + + validcount++; // used to make sure we only process a line once + + xl = (tmbbox[BOXLEFT] - bmaporgx)>>MAPBLOCKSHIFT; + xh = (tmbbox[BOXRIGHT] - bmaporgx)>>MAPBLOCKSHIFT; + yl = (tmbbox[BOXBOTTOM] - bmaporgy)>>MAPBLOCKSHIFT; + yh = (tmbbox[BOXTOP] - bmaporgy)>>MAPBLOCKSHIFT; + + for (bx=xl ; bx<=xh ; bx++) + for (by=yl ; by<=yh ; by++) + P_BlockLinesIterator(bx,by,PIT_GetSectors); + + // Add the sector of the (x,y) point to sector_list. + + sector_list = P_AddSecnode(thing->subsector->sector,thing,sector_list); + + // Now delete any nodes that won't be used. These are the ones where + // m_thing is still NULL. + + node = sector_list; + while (node) + { + if (node->m_thing == NULL) + { + if (node == sector_list) + sector_list = node->m_tnext; + node = P_DelSecnode(node); + } + else + node = node->m_tnext; + } + + /* cph - + * This is the strife we get into for using global variables. tmthing + * is being used by several different functions calling + * P_BlockThingIterator, including functions that can be called *from* + * P_BlockThingIterator. Using a global tmthing is not reentrant. + * OTOH for Boom/MBF demos we have to preserve the buggy behavior. + * Fun. We restore its previous value unless we're in a Boom/MBF demo. + */ + if ((compatibility_level < boom_compatibility_compatibility) || + (compatibility_level >= prboom_3_compatibility)) + tmthing = saved_tmthing; + /* And, duh, the same for tmx/y - cph 2002/09/22 + * And for tmbbox - cph 2003/08/10 */ + if ((compatibility_level < boom_compatibility_compatibility) /* || + (compatibility_level >= prboom_4_compatibility) */) { + tmx = saved_tmx, tmy = saved_tmy; + if (tmthing) { + tmbbox[BOXTOP] = tmy + tmthing->radius; + tmbbox[BOXBOTTOM] = tmy - tmthing->radius; + tmbbox[BOXRIGHT] = tmx + tmthing->radius; + tmbbox[BOXLEFT] = tmx - tmthing->radius; + } + } +} + +/* cphipps 2004/08/30 - + * Must clear tmthing at tic end, as it might contain a pointer to a removed thinker, or the level might have ended/been ended and we clear the objects it was pointing too. Hopefully we don't need to carry this between tics for sync. */ +void P_MapStart(void) { + if (tmthing) I_Error("P_MapStart: tmthing set!"); +} +void P_MapEnd(void) { + tmthing = NULL; +} + +// e6y +// Code to emulate the behavior of Vanilla Doom when encountering an overrun +// of the spechit array. +// No more desyncs on compet-n\hr.wad\hr18*.lmp, all strain.wad\map07 demos etc. +// http://www.doomworld.com/vb/showthread.php?s=&threadid=35214 +static void SpechitOverrun(line_t *ld) +{ + //int addr = 0x01C09C98 + (ld - lines) * 0x3E; + int addr = 0x00C09C98 + (ld - lines) * 0x3E; + + if (compatibility_level == dosdoom_compatibility || compatibility_level == tasdoom_compatibility) + { + // e6y + // There are no more desyncs in the following dosdoom demos: + // flsofdth.wad\fod3uv.lmp - http://www.doomworld.com/sda/flsofdth.htm + // hr.wad\hf181430.lmp - http://www.doomworld.com/tas/hf181430.zip + // hr.wad\hr181329.lmp - http://www.doomworld.com/tas/hr181329.zip + // icarus.wad\ic09uv.lmp - http://competn.doom2.net/pub/sda/i-o/icuvlmps.zip + + switch(numspechit) + { + case 8: break; /* strange cph's code */ + case 9: + tmfloorz = addr; + break; + case 10: + tmceilingz = addr; + break; + + default: + lprintf(LO_ERROR, "SpechitOverrun: Warning: unable to emulate" + " an overrun where numspechit=%i\n", + numspechit); + break; + } + } + else + { + switch(numspechit) + { + case 8: break; /* numspechit, not significant it seems - cph */ + case 9: + case 10: + case 11: + case 12: + tmbbox[numspechit-9] = addr; + break; + case 13: + nofit = addr; + break; + case 14: + crushchange = addr; + break; + default: + lprintf(LO_ERROR, "SpechitOverrun: Warning: unable to emulate" + " an overrun where numspechit=%i\n", + numspechit); + break; + } + } +} diff --git a/src/p_map.h b/src/p_map.h new file mode 100644 index 0000000..8da2f62 --- /dev/null +++ b/src/p_map.h @@ -0,0 +1,92 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Map functions + * + *-----------------------------------------------------------------------------*/ + +#ifndef __P_MAP__ +#define __P_MAP__ + +#include "r_defs.h" +#include "d_player.h" + +#define USERANGE (64*FRACUNIT) +#define MELEERANGE (64*FRACUNIT) +#define MISSILERANGE (32*64*FRACUNIT) + +// MAXRADIUS is for precalculated sector block boxes the spider demon +// is larger, but we do not have any moving sectors nearby +#define MAXRADIUS (32*FRACUNIT) + +// killough 3/15/98: add fourth argument to P_TryMove +boolean P_TryMove(mobj_t *thing, fixed_t x, fixed_t y, boolean dropoff); + +// killough 8/9/98: extra argument for telefragging +boolean P_TeleportMove(mobj_t *thing, fixed_t x, fixed_t y,boolean boss); +void P_SlideMove(mobj_t *mo); +boolean P_CheckSight(mobj_t *t1, mobj_t *t2); +void P_UseLines(player_t *player); + +// killough 8/2/98: add 'mask' argument to prevent friends autoaiming at others +fixed_t P_AimLineAttack(mobj_t *t1,angle_t angle,fixed_t distance, uint_64_t mask); + +void P_LineAttack(mobj_t *t1, angle_t angle, fixed_t distance, + fixed_t slope, int damage ); +void P_RadiusAttack(mobj_t *spot, mobj_t *source, int damage); +boolean P_CheckPosition(mobj_t *thing, fixed_t x, fixed_t y); + +//jff 3/19/98 P_CheckSector(): new routine to replace P_ChangeSector() +boolean P_ChangeSector(sector_t* sector,boolean crunch); +boolean P_CheckSector(sector_t *sector, boolean crunch); +void P_DelSeclist(msecnode_t*); // phares 3/16/98 +void P_CreateSecNodeList(mobj_t*,fixed_t,fixed_t); // phares 3/14/98 +boolean Check_Sides(mobj_t *, int, int); // phares + +int P_GetMoveFactor(const mobj_t *mo, int *friction); // killough 8/28/98 +int P_GetFriction(const mobj_t *mo, int *factor); // killough 8/28/98 +void P_ApplyTorque(mobj_t *mo); // killough 9/12/98 + +/* cphipps 2004/08/30 */ +void P_MapStart(void); +void P_MapEnd(void); + +// If "floatok" true, move would be ok if within "tmfloorz - tmceilingz". +extern boolean floatok; +extern boolean felldown; // killough 11/98: indicates object pushed off ledge +extern fixed_t tmfloorz; +extern fixed_t tmceilingz; +extern line_t *ceilingline; +extern line_t *floorline; // killough 8/23/98 +extern mobj_t *linetarget; // who got hit (or NULL) +extern msecnode_t *sector_list; // phares 3/16/98 +extern fixed_t tmbbox[4]; // phares 3/20/98 +extern line_t *blockline; // killough 8/11/98 + +#endif // __P_MAP__ diff --git a/src/p_maputl.c b/src/p_maputl.c new file mode 100644 index 0000000..62ce91c --- /dev/null +++ b/src/p_maputl.c @@ -0,0 +1,683 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Movement/collision utility functions, + * as used by function in p_map.c. + * BLOCKMAP Iterator functions, + * and some PIT_* functions to use for iteration. + * + *-----------------------------------------------------------------------------*/ + +#include "doomstat.h" +#include "m_bbox.h" +#include "r_main.h" +#include "p_maputl.h" +#include "p_map.h" +#include "p_setup.h" + +// +// P_AproxDistance +// Gives an estimation of distance (not exact) +// + +fixed_t CONSTFUNC P_AproxDistance(fixed_t dx, fixed_t dy) +{ + dx = D_abs(dx); + dy = D_abs(dy); + if (dx < dy) + return dx+dy-(dx>>1); + return dx+dy-(dy>>1); +} + +// +// P_PointOnLineSide +// Returns 0 or 1 +// +// killough 5/3/98: reformatted, cleaned up + +int PUREFUNC P_PointOnLineSide(fixed_t x, fixed_t y, const line_t *line) +{ + return + !line->dx ? x <= line->v1->x ? line->dy > 0 : line->dy < 0 : + !line->dy ? y <= line->v1->y ? line->dx < 0 : line->dx > 0 : + FixedMul(y-line->v1->y, line->dx>>FRACBITS) >= + FixedMul(line->dy>>FRACBITS, x-line->v1->x); +} + +// +// P_BoxOnLineSide +// Considers the line to be infinite +// Returns side 0 or 1, -1 if box crosses the line. +// +// killough 5/3/98: reformatted, cleaned up + +int PUREFUNC P_BoxOnLineSide(const fixed_t *tmbox, const line_t *ld) +{ + switch (ld->slopetype) + { + int p; + default: // shut up compiler warnings -- killough + case ST_HORIZONTAL: + return + (tmbox[BOXBOTTOM] > ld->v1->y) == (p = tmbox[BOXTOP] > ld->v1->y) ? + p ^ (ld->dx < 0) : -1; + case ST_VERTICAL: + return + (tmbox[BOXLEFT] < ld->v1->x) == (p = tmbox[BOXRIGHT] < ld->v1->x) ? + p ^ (ld->dy < 0) : -1; + case ST_POSITIVE: + return + P_PointOnLineSide(tmbox[BOXRIGHT], tmbox[BOXBOTTOM], ld) == + (p = P_PointOnLineSide(tmbox[BOXLEFT], tmbox[BOXTOP], ld)) ? p : -1; + case ST_NEGATIVE: + return + (P_PointOnLineSide(tmbox[BOXLEFT], tmbox[BOXBOTTOM], ld)) == + (p = P_PointOnLineSide(tmbox[BOXRIGHT], tmbox[BOXTOP], ld)) ? p : -1; + } +} + +// +// P_PointOnDivlineSide +// Returns 0 or 1. +// +// killough 5/3/98: reformatted, cleaned up + +static int PUREFUNC P_PointOnDivlineSide(fixed_t x, fixed_t y, const divline_t *line) +{ + return + !line->dx ? x <= line->x ? line->dy > 0 : line->dy < 0 : + !line->dy ? y <= line->y ? line->dx < 0 : line->dx > 0 : + (line->dy^line->dx^(x -= line->x)^(y -= line->y)) < 0 ? (line->dy^x) < 0 : + FixedMul(y>>8, line->dx>>8) >= FixedMul(line->dy>>8, x>>8); +} + +// +// P_MakeDivline +// + +static void P_MakeDivline(const line_t *li, divline_t *dl) +{ + dl->x = li->v1->x; + dl->y = li->v1->y; + dl->dx = li->dx; + dl->dy = li->dy; +} + +// +// P_InterceptVector +// Returns the fractional intercept point +// along the first divline. +// This is only called by the addthings +// and addlines traversers. +// + +/* cph - this is killough's 4/19/98 version of P_InterceptVector and + * P_InterceptVector2 (which were interchangeable). We still use this + * in compatibility mode. */ +fixed_t PUREFUNC P_InterceptVector2(const divline_t *v2, const divline_t *v1) +{ + fixed_t den; + return (den = FixedMul(v1->dy>>8, v2->dx) - FixedMul(v1->dx>>8, v2->dy)) ? + FixedDiv(FixedMul((v1->x - v2->x)>>8, v1->dy) + + FixedMul((v2->y - v1->y)>>8, v1->dx), den) : 0; +} + +fixed_t PUREFUNC P_InterceptVector(const divline_t *v2, const divline_t *v1) +{ + if (compatibility_level < prboom_4_compatibility) + return P_InterceptVector2(v2, v1); + else { + /* cph - This was introduced at prboom_4_compatibility - no precision/overflow problems */ + int_64_t den = (int_64_t)v1->dy * v2->dx - (int_64_t)v1->dx * v2->dy; + den >>= 16; + if (!den) + return 0; + return (fixed_t)(((int_64_t)(v1->x - v2->x) * v1->dy - (int_64_t)(v1->y - v2->y) * v1->dx) / den); + } +} + +// +// P_LineOpening +// Sets opentop and openbottom to the window +// through a two sided line. +// OPTIMIZE: keep this precalculated +// + +fixed_t opentop; +fixed_t openbottom; +fixed_t openrange; +fixed_t lowfloor; + +// moved front and back outside P-LineOpening and changed // phares 3/7/98 +// them to these so we can pick up the new friction value +// in PIT_CheckLine() +sector_t *openfrontsector; // made global // phares +sector_t *openbacksector; // made global + +void P_LineOpening(const line_t *linedef) +{ + if (linedef->sidenum[1] == NO_INDEX) // single sided line + { + openrange = 0; + return; + } + + openfrontsector = linedef->frontsector; + openbacksector = linedef->backsector; + + if (openfrontsector->ceilingheight < openbacksector->ceilingheight) + opentop = openfrontsector->ceilingheight; + else + opentop = openbacksector->ceilingheight; + + if (openfrontsector->floorheight > openbacksector->floorheight) + { + openbottom = openfrontsector->floorheight; + lowfloor = openbacksector->floorheight; + } + else + { + openbottom = openbacksector->floorheight; + lowfloor = openfrontsector->floorheight; + } + openrange = opentop - openbottom; +} + +// +// THING POSITION SETTING +// + +// +// P_UnsetThingPosition +// Unlinks a thing from block map and sectors. +// On each position change, BLOCKMAP and other +// lookups maintaining lists ot things inside +// these structures need to be updated. +// + +void P_UnsetThingPosition (mobj_t *thing) +{ + if (!(thing->flags & MF_NOSECTOR)) + { + /* invisible things don't need to be in sector list + * unlink from subsector + * + * killough 8/11/98: simpler scheme using pointers-to-pointers for prev + * pointers, allows head node pointers to be treated like everything else + */ + + mobj_t **sprev = thing->sprev; + mobj_t *snext = thing->snext; + if ((*sprev = snext)) // unlink from sector list + snext->sprev = sprev; + + // phares 3/14/98 + // + // Save the sector list pointed to by touching_sectorlist. + // In P_SetThingPosition, we'll keep any nodes that represent + // sectors the Thing still touches. We'll add new ones then, and + // delete any nodes for sectors the Thing has vacated. Then we'll + // put it back into touching_sectorlist. It's done this way to + // avoid a lot of deleting/creating for nodes, when most of the + // time you just get back what you deleted anyway. + // + // If this Thing is being removed entirely, then the calling + // routine will clear out the nodes in sector_list. + + sector_list = thing->touching_sectorlist; + thing->touching_sectorlist = NULL; //to be restored by P_SetThingPosition + } + + if (!(thing->flags & MF_NOBLOCKMAP)) + { + /* inert things don't need to be in blockmap + * + * killough 8/11/98: simpler scheme using pointers-to-pointers for prev + * pointers, allows head node pointers to be treated like everything else + * + * Also more robust, since it doesn't depend on current position for + * unlinking. Old method required computing head node based on position + * at time of unlinking, assuming it was the same position as during + * linking. + */ + + mobj_t *bnext, **bprev = thing->bprev; + if (bprev && (*bprev = bnext = thing->bnext)) // unlink from block map + bnext->bprev = bprev; + } +} + +// +// P_SetThingPosition +// Links a thing into both a block and a subsector +// based on it's x y. +// Sets thing->subsector properly +// +// killough 5/3/98: reformatted, cleaned up + +void P_SetThingPosition(mobj_t *thing) +{ // link into subsector + subsector_t *ss = thing->subsector = R_PointInSubsector(thing->x, thing->y); + if (!(thing->flags & MF_NOSECTOR)) + { + // invisible things don't go into the sector links + + // killough 8/11/98: simpler scheme using pointer-to-pointer prev + // pointers, allows head nodes to be treated like everything else + + mobj_t **link = &ss->sector->thinglist; + mobj_t *snext = *link; + if ((thing->snext = snext)) + snext->sprev = &thing->snext; + thing->sprev = link; + *link = thing; + + // phares 3/16/98 + // + // If sector_list isn't NULL, it has a collection of sector + // nodes that were just removed from this Thing. + + // Collect the sectors the object will live in by looking at + // the existing sector_list and adding new nodes and deleting + // obsolete ones. + + // When a node is deleted, its sector links (the links starting + // at sector_t->touching_thinglist) are broken. When a node is + // added, new sector links are created. + + P_CreateSecNodeList(thing,thing->x,thing->y); + thing->touching_sectorlist = sector_list; // Attach to Thing's mobj_t + sector_list = NULL; // clear for next time + } + + // link into blockmap + if (!(thing->flags & MF_NOBLOCKMAP)) + { + // inert things don't need to be in blockmap + int blockx = (thing->x - bmaporgx)>>MAPBLOCKSHIFT; + int blocky = (thing->y - bmaporgy)>>MAPBLOCKSHIFT; + if (blockx>=0 && blockx < bmapwidth && blocky>=0 && blocky < bmapheight) + { + // killough 8/11/98: simpler scheme using pointer-to-pointer prev + // pointers, allows head nodes to be treated like everything else + + mobj_t **link = &blocklinks[blocky*bmapwidth+blockx]; + mobj_t *bnext = *link; + if ((thing->bnext = bnext)) + bnext->bprev = &thing->bnext; + thing->bprev = link; + *link = thing; + } + else // thing is off the map + thing->bnext = NULL, thing->bprev = NULL; + } +} + +// +// BLOCK MAP ITERATORS +// For each line/thing in the given mapblock, +// call the passed PIT_* function. +// If the function returns false, +// exit with false without checking anything else. +// + +// +// P_BlockLinesIterator +// The validcount flags are used to avoid checking lines +// that are marked in multiple mapblocks, +// so increment validcount before the first call +// to P_BlockLinesIterator, then make one or more calls +// to it. +// +// killough 5/3/98: reformatted, cleaned up + +boolean P_BlockLinesIterator(int x, int y, boolean func(line_t*)) +{ + int offset; + const long *list; // killough 3/1/98: for removal of blockmap limit + + if (x<0 || y<0 || x>=bmapwidth || y>=bmapheight) + return true; + offset = y*bmapwidth+x; + offset = *(blockmap+offset); + list = blockmaplump+offset; // original was reading // phares + // delmiting 0 as linedef 0 // phares + + // killough 1/31/98: for compatibility we need to use the old method. + // Most demos go out of sync, and maybe other problems happen, if we + // don't consider linedef 0. For safety this should be qualified. + + if (!demo_compatibility) // killough 2/22/98: demo_compatibility check + list++; // skip 0 starting delimiter // phares + for ( ; *list != -1 ; list++) // phares + { + line_t *ld = &lines[*list]; + if (ld->validcount == validcount) + continue; // line has already been checked + ld->validcount = validcount; + if (!func(ld)) + return false; + } + return true; // everything was checked +} + +// +// P_BlockThingsIterator +// +// killough 5/3/98: reformatted, cleaned up + +boolean P_BlockThingsIterator(int x, int y, boolean func(mobj_t*)) +{ + mobj_t *mobj; + if (!(x<0 || y<0 || x>=bmapwidth || y>=bmapheight)) + for (mobj = blocklinks[y*bmapwidth+x]; mobj; mobj = mobj->bnext) + if (!func(mobj)) + return false; + return true; +} + +// +// INTERCEPT ROUTINES +// + +// 1/11/98 killough: Intercept limit removed +static intercept_t *intercepts, *intercept_p; + +// Check for limit and double size if necessary -- killough +static void check_intercept(void) +{ + static size_t num_intercepts; + size_t offset = intercept_p - intercepts; + if (offset >= num_intercepts) + { + num_intercepts = num_intercepts ? num_intercepts*2 : 128; + intercepts = realloc(intercepts, sizeof(*intercepts)*num_intercepts); + intercept_p = intercepts + offset; + } +} + +divline_t trace; + +// PIT_AddLineIntercepts. +// Looks for lines in the given block +// that intercept the given trace +// to add to the intercepts list. +// +// A line is crossed if its endpoints +// are on opposite sides of the trace. +// +// killough 5/3/98: reformatted, cleaned up + +boolean PIT_AddLineIntercepts(line_t *ld) +{ + int s1; + int s2; + fixed_t frac; + divline_t dl; + + // avoid precision problems with two routines + if (trace.dx > FRACUNIT*16 || trace.dy > FRACUNIT*16 || + trace.dx < -FRACUNIT*16 || trace.dy < -FRACUNIT*16) + { + s1 = P_PointOnDivlineSide (ld->v1->x, ld->v1->y, &trace); + s2 = P_PointOnDivlineSide (ld->v2->x, ld->v2->y, &trace); + } + else + { + s1 = P_PointOnLineSide (trace.x, trace.y, ld); + s2 = P_PointOnLineSide (trace.x+trace.dx, trace.y+trace.dy, ld); + } + + if (s1 == s2) + return true; // line isn't crossed + + // hit the line + P_MakeDivline(ld, &dl); + frac = P_InterceptVector(&trace, &dl); + + if (frac < 0) + return true; // behind source + + check_intercept(); // killough + + intercept_p->frac = frac; + intercept_p->isaline = true; + intercept_p->d.line = ld; + intercept_p++; + + return true; // continue +} + +// +// PIT_AddThingIntercepts +// +// killough 5/3/98: reformatted, cleaned up + +boolean PIT_AddThingIntercepts(mobj_t *thing) +{ + fixed_t x1, y1; + fixed_t x2, y2; + int s1, s2; + divline_t dl; + fixed_t frac; + + // check a corner to corner crossection for hit + if ((trace.dx ^ trace.dy) > 0) + { + x1 = thing->x - thing->radius; + y1 = thing->y + thing->radius; + x2 = thing->x + thing->radius; + y2 = thing->y - thing->radius; + } + else + { + x1 = thing->x - thing->radius; + y1 = thing->y - thing->radius; + x2 = thing->x + thing->radius; + y2 = thing->y + thing->radius; + } + + s1 = P_PointOnDivlineSide (x1, y1, &trace); + s2 = P_PointOnDivlineSide (x2, y2, &trace); + + if (s1 == s2) + return true; // line isn't crossed + + dl.x = x1; + dl.y = y1; + dl.dx = x2-x1; + dl.dy = y2-y1; + + frac = P_InterceptVector (&trace, &dl); + + if (frac < 0) + return true; // behind source + + check_intercept(); // killough + + intercept_p->frac = frac; + intercept_p->isaline = false; + intercept_p->d.thing = thing; + intercept_p++; + + return true; // keep going +} + +// +// P_TraverseIntercepts +// Returns true if the traverser function returns true +// for all lines. +// +// killough 5/3/98: reformatted, cleaned up + +boolean P_TraverseIntercepts(traverser_t func, fixed_t maxfrac) +{ + intercept_t *in = NULL; + int count = intercept_p - intercepts; + while (count--) + { + fixed_t dist = INT_MAX; + intercept_t *scan; + for (scan = intercepts; scan < intercept_p; scan++) + if (scan->frac < dist) + dist = (in=scan)->frac; + if (dist > maxfrac) + return true; // checked everything in range + if (!func(in)) + return false; // don't bother going farther + in->frac = INT_MAX; + } + return true; // everything was traversed +} + +// +// P_PathTraverse +// Traces a line from x1,y1 to x2,y2, +// calling the traverser function for each. +// Returns true if the traverser function returns true +// for all lines. +// +// killough 5/3/98: reformatted, cleaned up + +boolean P_PathTraverse(fixed_t x1, fixed_t y1, fixed_t x2, fixed_t y2, + int flags, boolean trav(intercept_t *)) +{ + fixed_t xt1, yt1; + fixed_t xt2, yt2; + fixed_t xstep, ystep; + fixed_t partial; + fixed_t xintercept, yintercept; + int mapx, mapy; + int mapxstep, mapystep; + int count; + + validcount++; + intercept_p = intercepts; + + if (!((x1-bmaporgx)&(MAPBLOCKSIZE-1))) + x1 += FRACUNIT; // don't side exactly on a line + + if (!((y1-bmaporgy)&(MAPBLOCKSIZE-1))) + y1 += FRACUNIT; // don't side exactly on a line + + trace.x = x1; + trace.y = y1; + trace.dx = x2 - x1; + trace.dy = y2 - y1; + + x1 -= bmaporgx; + y1 -= bmaporgy; + xt1 = x1>>MAPBLOCKSHIFT; + yt1 = y1>>MAPBLOCKSHIFT; + + x2 -= bmaporgx; + y2 -= bmaporgy; + xt2 = x2>>MAPBLOCKSHIFT; + yt2 = y2>>MAPBLOCKSHIFT; + + if (xt2 > xt1) + { + mapxstep = 1; + partial = FRACUNIT - ((x1>>MAPBTOFRAC)&(FRACUNIT-1)); + ystep = FixedDiv (y2-y1,D_abs(x2-x1)); + } + else + if (xt2 < xt1) + { + mapxstep = -1; + partial = (x1>>MAPBTOFRAC)&(FRACUNIT-1); + ystep = FixedDiv (y2-y1,D_abs(x2-x1)); + } + else + { + mapxstep = 0; + partial = FRACUNIT; + ystep = 256*FRACUNIT; + } + + yintercept = (y1>>MAPBTOFRAC) + FixedMul(partial, ystep); + + if (yt2 > yt1) + { + mapystep = 1; + partial = FRACUNIT - ((y1>>MAPBTOFRAC)&(FRACUNIT-1)); + xstep = FixedDiv (x2-x1,D_abs(y2-y1)); + } + else + if (yt2 < yt1) + { + mapystep = -1; + partial = (y1>>MAPBTOFRAC)&(FRACUNIT-1); + xstep = FixedDiv (x2-x1,D_abs(y2-y1)); + } + else + { + mapystep = 0; + partial = FRACUNIT; + xstep = 256*FRACUNIT; + } + + xintercept = (x1>>MAPBTOFRAC) + FixedMul (partial, xstep); + + // Step through map blocks. + // Count is present to prevent a round off error + // from skipping the break. + + mapx = xt1; + mapy = yt1; + + for (count = 0; count < 64; count++) + { + if (flags & PT_ADDLINES) + if (!P_BlockLinesIterator(mapx, mapy,PIT_AddLineIntercepts)) + return false; // early out + + if (flags & PT_ADDTHINGS) + if (!P_BlockThingsIterator(mapx, mapy,PIT_AddThingIntercepts)) + return false; // early out + + if (mapx == xt2 && mapy == yt2) + break; + + if ((yintercept >> FRACBITS) == mapy) + { + yintercept += ystep; + mapx += mapxstep; + } + else + if ((xintercept >> FRACBITS) == mapx) + { + xintercept += xstep; + mapy += mapystep; + } + } + + // go through the sorted list + return P_TraverseIntercepts(trav, FRACUNIT); +} diff --git a/src/p_maputl.h b/src/p_maputl.h new file mode 100644 index 0000000..8a70ba0 --- /dev/null +++ b/src/p_maputl.h @@ -0,0 +1,89 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Map utility functions + * + *-----------------------------------------------------------------------------*/ + +#ifndef __P_MAPUTL__ +#define __P_MAPUTL__ + +#include "r_defs.h" + +/* mapblocks are used to check movement against lines and things */ +#define MAPBLOCKUNITS 128 +#define MAPBLOCKSIZE (MAPBLOCKUNITS*FRACUNIT) +#define MAPBLOCKSHIFT (FRACBITS+7) +#define MAPBMASK (MAPBLOCKSIZE-1) +#define MAPBTOFRAC (MAPBLOCKSHIFT-FRACBITS) + +#define PT_ADDLINES 1 +#define PT_ADDTHINGS 2 +#define PT_EARLYOUT 4 + +typedef struct { + fixed_t x; + fixed_t y; + fixed_t dx; + fixed_t dy; +} divline_t; + +typedef struct { + fixed_t frac; /* along trace line */ + boolean isaline; + union { + mobj_t* thing; + line_t* line; + } d; +} intercept_t; + +typedef boolean (*traverser_t)(intercept_t *in); + +fixed_t CONSTFUNC P_AproxDistance (fixed_t dx, fixed_t dy); +int PUREFUNC P_PointOnLineSide (fixed_t x, fixed_t y, const line_t *line); +int PUREFUNC P_BoxOnLineSide (const fixed_t *tmbox, const line_t *ld); +fixed_t PUREFUNC P_InterceptVector (const divline_t *v2, const divline_t *v1); +/* cph - old compatibility version below */ +fixed_t PUREFUNC P_InterceptVector2(const divline_t *v2, const divline_t *v1); + +void P_LineOpening (const line_t *linedef); +void P_UnsetThingPosition(mobj_t *thing); +void P_SetThingPosition(mobj_t *thing); +boolean P_BlockLinesIterator (int x, int y, boolean func(line_t *)); +boolean P_BlockThingsIterator(int x, int y, boolean func(mobj_t *)); +boolean P_PathTraverse(fixed_t x1, fixed_t y1, fixed_t x2, fixed_t y2, + int flags, boolean trav(intercept_t *)); + +extern fixed_t opentop; +extern fixed_t openbottom; +extern fixed_t openrange; +extern fixed_t lowfloor; +extern divline_t trace; + +#endif /* __P_MAPUTL__ */ diff --git a/src/p_mobj.c b/src/p_mobj.c new file mode 100644 index 0000000..0f8c8e3 --- /dev/null +++ b/src/p_mobj.c @@ -0,0 +1,1526 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2004 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Moving object handling. Spawn functions. + * + *-----------------------------------------------------------------------------*/ + +#include "doomdef.h" +#include "doomstat.h" +#include "m_random.h" +#include "r_main.h" +#include "p_maputl.h" +#include "p_map.h" +#include "p_tick.h" +#include "sounds.h" +#include "st_stuff.h" +#include "hu_stuff.h" +#include "s_sound.h" +#include "info.h" +#include "g_game.h" +#include "p_inter.h" +#include "lprintf.h" +#include "r_demo.h" + +// +// P_SetMobjState +// Returns true if the mobj is still present. +// + +boolean P_SetMobjState(mobj_t* mobj,statenum_t state) + { + state_t* st; + + // killough 4/9/98: remember states seen, to detect cycles: + + static statenum_t seenstate_tab[NUMSTATES]; // fast transition table + statenum_t *seenstate = seenstate_tab; // pointer to table + static int recursion; // detects recursion + statenum_t i = state; // initial state + boolean ret = true; // return value + statenum_t tempstate[NUMSTATES]; // for use with recursion + + if (recursion++) // if recursion detected, + memset(seenstate=tempstate,0,sizeof tempstate); // clear state table + + do + { + if (state == S_NULL) + { + mobj->state = (state_t *) S_NULL; + P_RemoveMobj (mobj); + ret = false; + break; // killough 4/9/98 + } + + st = &states[state]; + mobj->state = st; + mobj->tics = st->tics; + mobj->sprite = st->sprite; + mobj->frame = st->frame; + + // Modified handling. + // Call action functions when the state is set + + if (st->action) + st->action(mobj); + + seenstate[state] = 1 + st->nextstate; // killough 4/9/98 + + state = st->nextstate; + } while (!mobj->tics && !seenstate[state]); // killough 4/9/98 + + if (ret && !mobj->tics) // killough 4/9/98: detect state cycles + doom_printf("Warning: State Cycle Detected"); + + if (!--recursion) + for (;(state=seenstate[i]);i=state-1) + seenstate[i] = 0; // killough 4/9/98: erase memory of states + + return ret; + } + + +// +// P_ExplodeMissile +// + +void P_ExplodeMissile (mobj_t* mo) + { + mo->momx = mo->momy = mo->momz = 0; + + P_SetMobjState (mo, mobjinfo[mo->type].deathstate); + + mo->tics -= P_Random(pr_explode)&3; + + if (mo->tics < 1) + mo->tics = 1; + + mo->flags &= ~MF_MISSILE; + + if (mo->info->deathsound) + S_StartSound (mo, mo->info->deathsound); + } + + +// +// P_XYMovement +// +// Attempts to move something if it has momentum. +// + +static void P_XYMovement (mobj_t* mo) + { + player_t *player; + fixed_t xmove, ymove; + + //e6y + fixed_t oldx,oldy; // phares 9/10/98: reducing bobbing/momentum on ice + +#if 0 + fixed_t ptryx; + fixed_t ptryy; + fixed_t xmove; + fixed_t ymove; + fixed_t oldx,oldy; // phares 9/10/98: reducing bobbing/momentum on ice + // when up against walls +#endif + if (!(mo->momx | mo->momy)) // Any momentum? + { + if (mo->flags & MF_SKULLFLY) + { + + // the skull slammed into something + + mo->flags &= ~MF_SKULLFLY; + mo->momz = 0; + + P_SetMobjState (mo, mo->info->spawnstate); + } + return; + } + + player = mo->player; + + if (mo->momx > MAXMOVE) + mo->momx = MAXMOVE; + else if (mo->momx < -MAXMOVE) + mo->momx = -MAXMOVE; + + if (mo->momy > MAXMOVE) + mo->momy = MAXMOVE; + else if (mo->momy < -MAXMOVE) + mo->momy = -MAXMOVE; + + xmove = mo->momx; + ymove = mo->momy; + + oldx = mo->x; // phares 9/10/98: new code to reduce bobbing/momentum + oldy = mo->y; // when on ice & up against wall. These will be compared + // to your x,y values later to see if you were able to move + + do + { + fixed_t ptryx, ptryy; + // killough 8/9/98: fix bug in original Doom source: + // Large negative displacements were never considered. + // This explains the tendency for Mancubus fireballs + // to pass through walls. + // CPhipps - compatibility optioned + + if (xmove > MAXMOVE/2 || ymove > MAXMOVE/2 || + (!comp[comp_moveblock] + && (xmove < -MAXMOVE/2 || ymove < -MAXMOVE/2))) + { + ptryx = mo->x + xmove/2; + ptryy = mo->y + ymove/2; + xmove >>= 1; + ymove >>= 1; + } + else + { + ptryx = mo->x + xmove; + ptryy = mo->y + ymove; + xmove = ymove = 0; + } + + // killough 3/15/98: Allow objects to drop off + + if (!P_TryMove (mo, ptryx, ptryy, true)) + { + // blocked move + + // killough 8/11/98: bouncing off walls + // killough 10/98: + // Add ability for objects other than players to bounce on ice + + if (!(mo->flags & MF_MISSILE) && + mbf_features && + (mo->flags & MF_BOUNCES || + (!player && blockline && + variable_friction && mo->z <= mo->floorz && + P_GetFriction(mo, NULL) > ORIG_FRICTION))) + { + if (blockline) + { + fixed_t r = ((blockline->dx >> FRACBITS) * mo->momx + + (blockline->dy >> FRACBITS) * mo->momy) / + ((blockline->dx >> FRACBITS)*(blockline->dx >> FRACBITS)+ + (blockline->dy >> FRACBITS)*(blockline->dy >> FRACBITS)); + fixed_t x = FixedMul(r, blockline->dx); + fixed_t y = FixedMul(r, blockline->dy); + + // reflect momentum away from wall + + mo->momx = x*2 - mo->momx; + mo->momy = y*2 - mo->momy; + + // if under gravity, slow down in + // direction perpendicular to wall. + + if (!(mo->flags & MF_NOGRAVITY)) + { + mo->momx = (mo->momx + x)/2; + mo->momy = (mo->momy + y)/2; + } + } + else + mo->momx = mo->momy = 0; + } + else + if (player) // try to slide along it + P_SlideMove (mo); + else + if (mo->flags & MF_MISSILE) + { + // explode a missile + + if (ceilingline && + ceilingline->backsector && + ceilingline->backsector->ceilingpic == skyflatnum) + if (demo_compatibility || // killough + mo->z > ceilingline->backsector->ceilingheight) + { + // Hack to prevent missiles exploding + // against the sky. + // Does not handle sky floors. + + P_RemoveMobj (mo); + return; + } + P_ExplodeMissile (mo); + } + else // whatever else it is, it is now standing still in (x,y) + mo->momx = mo->momy = 0; + } + } while (xmove || ymove); + + // slow down + +#if 0 /* killough 10/98: this is unused code (except maybe in .deh files?) */ + if (player && player->cheats & CF_NOMOMENTUM) + { + // debug option for no sliding at all + mo->momx = mo->momy = 0; + player->momx = player->momy = 0; /* killough 10/98 */ + return; + } +#endif + + /* no friction for missiles or skulls ever, no friction when airborne */ + if (mo->flags & (MF_MISSILE | MF_SKULLFLY) || mo->z > mo->floorz) + return; + + /* killough 8/11/98: add bouncers + * killough 9/15/98: add objects falling off ledges + * killough 11/98: only include bouncers hanging off ledges + */ + if (((mo->flags & MF_BOUNCES && mo->z > mo->dropoffz) || + mo->flags & MF_CORPSE || mo->intflags & MIF_FALLING) && + (mo->momx > FRACUNIT/4 || mo->momx < -FRACUNIT/4 || + mo->momy > FRACUNIT/4 || mo->momy < -FRACUNIT/4) && + mo->floorz != mo->subsector->sector->floorheight) + return; // do not stop sliding if halfway off a step with some momentum + + // killough 11/98: + // Stop voodoo dolls that have come to rest, despite any + // moving corresponding player, except in old demos: + + if (mo->momx > -STOPSPEED && mo->momx < STOPSPEED && + mo->momy > -STOPSPEED && mo->momy < STOPSPEED && + (!player || !(player->cmd.forwardmove | player->cmd.sidemove) || + (player->mo != mo && compatibility_level >= lxdoom_1_compatibility))) + { + // if in a walking frame, stop moving + + // killough 10/98: + // Don't affect main player when voodoo dolls stop, except in old demos: + +// if ( player&&(unsigned)((player->mo->state - states)- S_PLAY_RUN1) < 4) +// P_SetMobjState (player->mo, S_PLAY); + if (player && (unsigned)(player->mo->state - states - S_PLAY_RUN1) < 4 + && (player->mo == mo || compatibility_level >= lxdoom_1_compatibility)) + P_SetMobjState(player->mo, S_PLAY); + + mo->momx = mo->momy = 0; + + /* killough 10/98: kill any bobbing momentum too (except in voodoo dolls) + * cph - DEMOSYNC - needs compatibility check? + */ + if (player && player->mo == mo) + player->momx = player->momy = 0; + } + else + { + /* phares 3/17/98 + * + * Friction will have been adjusted by friction thinkers for + * icy or muddy floors. Otherwise it was never touched and + * remained set at ORIG_FRICTION + * + * killough 8/28/98: removed inefficient thinker algorithm, + * instead using touching_sectorlist in P_GetFriction() to + * determine friction (and thus only when it is needed). + * + * killough 10/98: changed to work with new bobbing method. + * Reducing player momentum is no longer needed to reduce + * bobbing, so ice works much better now. + * + * cph - DEMOSYNC - need old code for Boom demos? + */ + + //e6y + if (compatibility_level <= boom_201_compatibility) + { + // phares 3/17/98 + // Friction will have been adjusted by friction thinkers for icy + // or muddy floors. Otherwise it was never touched and + // remained set at ORIG_FRICTION + mo->momx = FixedMul(mo->momx,mo->friction); + mo->momy = FixedMul(mo->momy,mo->friction); + mo->friction = ORIG_FRICTION; // reset to normal for next tic + } + else if (compatibility_level <= lxdoom_1_compatibility) + { + // phares 9/10/98: reduce bobbing/momentum when on ice & up against wall + + if ((oldx == mo->x) && (oldy == mo->y)) // Did you go anywhere? + { // No. Use original friction. This allows you to not bob so much + // if you're on ice, but keeps enough momentum around to break free + // when you're mildly stuck in a wall. + mo->momx = FixedMul(mo->momx,ORIG_FRICTION); + mo->momy = FixedMul(mo->momy,ORIG_FRICTION); + } + else + { // Yes. Use stored friction. + mo->momx = FixedMul(mo->momx,mo->friction); + mo->momy = FixedMul(mo->momy,mo->friction); + } + mo->friction = ORIG_FRICTION; // reset to normal for next tic + } + else + { + + fixed_t friction = P_GetFriction(mo, NULL); + + mo->momx = FixedMul(mo->momx, friction); + mo->momy = FixedMul(mo->momy, friction); + + /* killough 10/98: Always decrease player bobbing by ORIG_FRICTION. + * This prevents problems with bobbing on ice, where it was not being + * reduced fast enough, leading to all sorts of kludges being developed. + */ + + if (player && player->mo == mo) /* Not voodoo dolls */ + { + player->momx = FixedMul(player->momx, ORIG_FRICTION); + player->momy = FixedMul(player->momy, ORIG_FRICTION); + } + + } + + } + } + + +// +// P_ZMovement +// +// Attempt vertical movement. + +static void P_ZMovement (mobj_t* mo) +{ + /* killough 7/11/98: + * BFG fireballs bounced on floors and ceilings in Pre-Beta Doom + * killough 8/9/98: added support for non-missile objects bouncing + * (e.g. grenade, mine, pipebomb) + */ + + if (mo->flags & MF_BOUNCES && mo->momz) { + mo->z += mo->momz; + if (mo->z <= mo->floorz) { /* bounce off floors */ + mo->z = mo->floorz; + if (mo->momz < 0) { + mo->momz = -mo->momz; + if (!(mo->flags & MF_NOGRAVITY)) { /* bounce back with decay */ + mo->momz = mo->flags & MF_FLOAT ? // floaters fall slowly + mo->flags & MF_DROPOFF ? // DROPOFF indicates rate + FixedMul(mo->momz, (fixed_t)(FRACUNIT*.85)) : + FixedMul(mo->momz, (fixed_t)(FRACUNIT*.70)) : + FixedMul(mo->momz, (fixed_t)(FRACUNIT*.45)) ; + + /* Bring it to rest below a certain speed */ + if (D_abs(mo->momz) <= mo->info->mass*(GRAVITY*4/256)) + mo->momz = 0; + } + + /* killough 11/98: touchy objects explode on impact */ + if (mo->flags & MF_TOUCHY && mo->intflags & MIF_ARMED + && mo->health > 0) + P_DamageMobj(mo, NULL, NULL, mo->health); + else if (mo->flags & MF_FLOAT && sentient(mo)) + goto floater; + return; + } + } else if (mo->z >= mo->ceilingz - mo->height) { + /* bounce off ceilings */ + mo->z = mo->ceilingz - mo->height; + if (mo->momz > 0) { + if (mo->subsector->sector->ceilingpic != skyflatnum) + mo->momz = -mo->momz; /* always bounce off non-sky ceiling */ + else if (mo->flags & MF_MISSILE) + P_RemoveMobj(mo); /* missiles don't bounce off skies */ + else if (mo->flags & MF_NOGRAVITY) + mo->momz = -mo->momz; // bounce unless under gravity + + if (mo->flags & MF_FLOAT && sentient(mo)) + goto floater; + + return; + } + } else { + if (!(mo->flags & MF_NOGRAVITY)) /* free-fall under gravity */ + mo->momz -= mo->info->mass*(GRAVITY/256); + + if (mo->flags & MF_FLOAT && sentient(mo)) goto floater; + return; + } + + /* came to a stop */ + mo->momz = 0; + + if (mo->flags & MF_MISSILE) { + if (ceilingline && + ceilingline->backsector && + ceilingline->backsector->ceilingpic == skyflatnum && + mo->z > ceilingline->backsector->ceilingheight) + P_RemoveMobj(mo); /* don't explode on skies */ + else + P_ExplodeMissile(mo); + } + + if (mo->flags & MF_FLOAT && sentient(mo)) goto floater; + return; + } + + /* killough 8/9/98: end bouncing object code */ + + // check for smooth step up + + if (mo->player && + mo->player->mo == mo && // killough 5/12/98: exclude voodoo dolls + mo->z < mo->floorz) + { + mo->player->viewheight -= mo->floorz-mo->z; + mo->player->deltaviewheight = (VIEWHEIGHT - mo->player->viewheight)>>3; + } + + // adjust altitude + + mo->z += mo->momz; + +floater: + if ((mo->flags & MF_FLOAT) && mo->target) + + // float down towards target if too close + + if (!((mo->flags ^ MF_FLOAT) & (MF_FLOAT | MF_SKULLFLY | MF_INFLOAT)) && + mo->target) /* killough 11/98: simplify */ + { + fixed_t delta; + if (P_AproxDistance(mo->x - mo->target->x, mo->y - mo->target->y) < + D_abs(delta = mo->target->z + (mo->height>>1) - mo->z)*3) + mo->z += delta < 0 ? -FLOATSPEED : FLOATSPEED; + } + + // clip movement + + if (mo->z <= mo->floorz) + { + // hit the floor + + /* Note (id): + * somebody left this after the setting momz to 0, + * kinda useless there. + * cph - This was the a bug in the linuxdoom-1.10 source which + * caused it not to sync Doom 2 v1.9 demos. Someone + * added the above comment and moved up the following code. So + * demos would desync in close lost soul fights. + * cph - revised 2001/04/15 - + * This was a bug in the Doom/Doom 2 source; the following code + * is meant to make charging lost souls bounce off of floors, but it + * was incorrectly placed after momz was set to 0. + * However, this bug was fixed in Doom95, Final/Ultimate Doom, and + * the v1.10 source release (which is one reason why it failed to sync + * some Doom2 v1.9 demos) + * I've added a comp_soul compatibility option to make this behavior + * selectable for PrBoom v2.3+. For older demos, we do this here only + * if we're in a compatibility level above Doom 2 v1.9 (in which case we + * mimic the bug and do it further down instead) + */ + + if (mo->flags & MF_SKULLFLY && + (!comp[comp_soul] || + (compatibility_level > doom2_19_compatibility && + compatibility_level < prboom_4_compatibility) + )) + mo->momz = -mo->momz; // the skull slammed into something + + if (mo->momz < 0) + { + /* killough 11/98: touchy objects explode on impact */ + if (mo->flags & MF_TOUCHY && mo->intflags & MIF_ARMED && mo->health > 0) + P_DamageMobj(mo, NULL, NULL, mo->health); + else + if (mo->player && /* killough 5/12/98: exclude voodoo dolls */ + mo->player->mo == mo && mo->momz < -GRAVITY*8) + { + // Squat down. + // Decrease viewheight for a moment + // after hitting the ground (hard), + // and utter appropriate sound. + + mo->player->deltaviewheight = mo->momz>>3; + if (mo->health > 0) /* cph - prevent "oof" when dead */ + S_StartSound (mo, sfx_oof); + } + mo->momz = 0; + } + mo->z = mo->floorz; + + /* cph 2001/04/15 - + * This is the buggy lost-soul bouncing code referenced above. + * We've already set momz = 0 normally by this point, so it's useless. + * However we might still have upward momentum, in which case this will + * incorrectly reverse it, so we might still need this for demo sync + */ + if (mo->flags & MF_SKULLFLY && + compatibility_level <= doom2_19_compatibility) + mo->momz = -mo->momz; // the skull slammed into something + + if ( (mo->flags & MF_MISSILE) && !(mo->flags & MF_NOCLIP) ) + { + P_ExplodeMissile (mo); + return; + } + } + else // still above the floor // phares + if (!(mo->flags & MF_NOGRAVITY)) + { + if (!mo->momz) + mo->momz = -GRAVITY; + mo->momz -= GRAVITY; + } + + if (mo->z + mo->height > mo->ceilingz) + { + /* cph 2001/04/15 - + * Lost souls were meant to bounce off of ceilings; + * new comp_soul compatibility option added + */ + if (!comp[comp_soul] && mo->flags & MF_SKULLFLY) + mo->momz = -mo->momz; // the skull slammed into something + + // hit the ceiling + + if (mo->momz > 0) + mo->momz = 0; + + mo->z = mo->ceilingz - mo->height; + + /* cph 2001/04/15 - + * We might have hit a ceiling but had downward momentum (e.g. ceiling is + * lowering on us), so for old demos we must still do the buggy + * momentum reversal here + */ + if (comp[comp_soul] && mo->flags & MF_SKULLFLY) + mo->momz = -mo->momz; // the skull slammed into something + + if ( (mo->flags & MF_MISSILE) && !(mo->flags & MF_NOCLIP) ) + { + P_ExplodeMissile (mo); + return; + } + } + } + +// +// P_NightmareRespawn +// + +static void P_NightmareRespawn(mobj_t* mobj) + { + fixed_t x; + fixed_t y; + fixed_t z; + subsector_t* ss; + mobj_t* mo; + mapthing_t* mthing; + + x = mobj->spawnpoint.x << FRACBITS; + y = mobj->spawnpoint.y << FRACBITS; + + /* haleyjd: stupid nightmare respawning bug fix + * + * 08/09/00: compatibility added, time to ramble :) + * This fixes the notorious nightmare respawning bug that causes monsters + * that didn't spawn at level startup to respawn at the point (0,0) + * regardless of that point's nature. SMMU and Eternity need this for + * script-spawned things like Halif Swordsmythe, as well. + * + * cph - copied from eternity, except comp_respawnfix becomes comp_respawn + * and the logic is reversed (i.e. like the rest of comp_ it *disables* + * the fix) + */ + if(!comp[comp_respawn] && !x && !y) + { + // spawnpoint was zeroed out, so use point of death instead + x = mobj->x; + y = mobj->y; + } + + // something is occupying its position? + + if (!P_CheckPosition (mobj, x, y) ) + return; // no respwan + + // spawn a teleport fog at old spot + // because of removal of the body? + + mo = P_SpawnMobj (mobj->x, + mobj->y, + mobj->subsector->sector->floorheight, + MT_TFOG); + + // initiate teleport sound + + S_StartSound (mo, sfx_telept); + + // spawn a teleport fog at the new spot + + ss = R_PointInSubsector (x,y); + + mo = P_SpawnMobj (x, y, ss->sector->floorheight , MT_TFOG); + + S_StartSound (mo, sfx_telept); + + // spawn the new monster + + mthing = &mobj->spawnpoint; + if (mobj->info->flags & MF_SPAWNCEILING) + z = ONCEILINGZ; + else + z = ONFLOORZ; + + // inherit attributes from deceased one + + mo = P_SpawnMobj (x,y,z, mobj->type); + mo->spawnpoint = mobj->spawnpoint; + mo->angle = ANG45 * (mthing->angle/45); + + if (mthing->options & MTF_AMBUSH) + mo->flags |= MF_AMBUSH; + + /* killough 11/98: transfer friendliness from deceased */ + mo->flags = (mo->flags & ~MF_FRIEND) | (mobj->flags & MF_FRIEND); + + mo->reactiontime = 18; + + // remove the old monster, + + P_RemoveMobj (mobj); + } + + +// +// P_MobjThinker +// + +void P_MobjThinker (mobj_t* mobj) + { + // killough 11/98: + // removed old code which looked at target references + // (we use pointer reference counting now) + + mobj->PrevX = mobj->x; + mobj->PrevY = mobj->y; + mobj->PrevZ = mobj->z; + + // momentum movement + if (mobj->momx | mobj->momy || mobj->flags & MF_SKULLFLY) + { + P_XYMovement(mobj); + if (mobj->thinker.function != P_MobjThinker) // cph - Must've been removed + return; // killough - mobj was removed + } + + if (mobj->z != mobj->floorz || mobj->momz) + { + P_ZMovement(mobj); + if (mobj->thinker.function != P_MobjThinker) // cph - Must've been removed + return; // killough - mobj was removed + } + else + if (!(mobj->momx | mobj->momy) && !sentient(mobj)) + { // non-sentient objects at rest + mobj->intflags |= MIF_ARMED; // arm a mine which has come to rest + + // killough 9/12/98: objects fall off ledges if they are hanging off + // slightly push off of ledge if hanging more than halfway off + + if (mobj->z > mobj->dropoffz && // Only objects contacting dropoff + !(mobj->flags & MF_NOGRAVITY) && // Only objects which fall + !comp[comp_falloff]) // Not in old demos + P_ApplyTorque(mobj); // Apply torque + else + mobj->intflags &= ~MIF_FALLING, mobj->gear = 0; // Reset torque + } + + // cycle through states, + // calling action functions at transitions + + if (mobj->tics != -1) + { + mobj->tics--; + + // you can cycle through multiple states in a tic + + if (!mobj->tics) + if (!P_SetMobjState (mobj, mobj->state->nextstate) ) + return; // freed itself + } + else + { + + // check for nightmare respawn + + if (! (mobj->flags & MF_COUNTKILL) ) + return; + + if (!respawnmonsters) + return; + + mobj->movecount++; + + if (mobj->movecount < 12*35) + return; + + if (leveltime & 31) + return; + + if (P_Random (pr_respawn) > 4) + return; + + P_NightmareRespawn (mobj); + } + + } + + +// +// P_SpawnMobj +// +mobj_t* P_SpawnMobj(fixed_t x,fixed_t y,fixed_t z,mobjtype_t type) + { + mobj_t* mobj; + state_t* st; + mobjinfo_t* info; + + mobj = Z_Malloc (sizeof(*mobj), PU_LEVEL, NULL); + memset (mobj, 0, sizeof (*mobj)); + info = &mobjinfo[type]; + mobj->type = type; + mobj->info = info; + mobj->x = x; + mobj->y = y; + mobj->radius = info->radius; + mobj->height = info->height; // phares + mobj->flags = info->flags; + + /* killough 8/23/98: no friends, bouncers, or touchy things in old demos */ + if (!mbf_features) + mobj->flags &= ~(MF_BOUNCES | MF_FRIEND | MF_TOUCHY); + else + if (type == MT_PLAYER) // Except in old demos, players + mobj->flags |= MF_FRIEND; // are always friends. + + mobj->health = info->spawnhealth; + + if (gameskill != sk_nightmare) + mobj->reactiontime = info->reactiontime; + + mobj->lastlook = P_Random (pr_lastlook) % MAXPLAYERS; + + // do not set the state with P_SetMobjState, + // because action routines can not be called yet + + st = &states[info->spawnstate]; + + mobj->state = st; + mobj->tics = st->tics; + mobj->sprite = st->sprite; + mobj->frame = st->frame; + mobj->touching_sectorlist = NULL; // NULL head of sector list // phares 3/13/98 + + // set subsector and/or block links + + P_SetThingPosition (mobj); + + mobj->dropoffz = /* killough 11/98: for tracking dropoffs */ + mobj->floorz = mobj->subsector->sector->floorheight; + mobj->ceilingz = mobj->subsector->sector->ceilingheight; + + mobj->z = z == ONFLOORZ ? mobj->floorz : z == ONCEILINGZ ? + mobj->ceilingz - mobj->height : z; + + mobj->PrevX = mobj->x; + mobj->PrevY = mobj->y; + mobj->PrevZ = mobj->z; + + mobj->thinker.function = P_MobjThinker; + + //e6y + mobj->friction = ORIG_FRICTION; // phares 3/17/98 + + mobj->target = mobj->tracer = mobj->lastenemy = NULL; + P_AddThinker (&mobj->thinker); + if (!((mobj->flags ^ MF_COUNTKILL) & (MF_FRIEND | MF_COUNTKILL))) + totallive++; + return mobj; + } + + +static mapthing_t itemrespawnque[ITEMQUESIZE]; +static int itemrespawntime[ITEMQUESIZE]; +int iquehead; +int iquetail; + + +// +// P_RemoveMobj +// + +void P_RemoveMobj (mobj_t* mobj) +{ + if ((mobj->flags & MF_SPECIAL) + && !(mobj->flags & MF_DROPPED) + && (mobj->type != MT_INV) + && (mobj->type != MT_INS)) + { + itemrespawnque[iquehead] = mobj->spawnpoint; + itemrespawntime[iquehead] = leveltime; + iquehead = (iquehead+1)&(ITEMQUESIZE-1); + + // lose one off the end? + + if (iquehead == iquetail) + iquetail = (iquetail+1)&(ITEMQUESIZE-1); + } + + // unlink from sector and block lists + + P_UnsetThingPosition (mobj); + + // Delete all nodes on the current sector_list phares 3/16/98 + + if (sector_list) + { + P_DelSeclist(sector_list); + sector_list = NULL; + } + + // stop any playing sound + + S_StopSound (mobj); + + // killough 11/98: + // + // Remove any references to other mobjs. + // + // Older demos might depend on the fields being left alone, however, + // if multiple thinkers reference each other indirectly before the + // end of the current tic. + // CPhipps - only leave dead references in old demos; I hope lxdoom_1 level + // demos are rare and don't rely on this. I hope. + + if ((compatibility_level >= lxdoom_1_compatibility) || + (!demorecording && !demoplayback)) { + P_SetTarget(&mobj->target, NULL); + P_SetTarget(&mobj->tracer, NULL); + P_SetTarget(&mobj->lastenemy, NULL); + } + // free block + + P_RemoveThinker (&mobj->thinker); +} + + +/* + * P_FindDoomedNum + * + * Finds a mobj type with a matching doomednum + * + * killough 8/24/98: rewrote to use hashing + */ + +static PUREFUNC int P_FindDoomedNum(unsigned type) +{ + static struct { int first, next; } *hash; + register int i; + + if (!hash) + { + hash = Z_Malloc(sizeof *hash * NUMMOBJTYPES, PU_CACHE, (void **) &hash); + for (i=0; ix << FRACBITS; + y = mthing->y << FRACBITS; + + // spawn a teleport fog at the new spot + + ss = R_PointInSubsector (x,y); + mo = P_SpawnMobj (x, y, ss->sector->floorheight , MT_IFOG); + S_StartSound (mo, sfx_itmbk); + + // find which type to spawn + + /* killough 8/23/98: use table for faster lookup */ + i = P_FindDoomedNum(mthing->type); + + // spawn it + + if (mobjinfo[i].flags & MF_SPAWNCEILING) + z = ONCEILINGZ; + else + z = ONFLOORZ; + + mo = P_SpawnMobj (x,y,z, i); + mo->spawnpoint = *mthing; + mo->angle = ANG45 * (mthing->angle/45); + + // pull it from the queue + + iquetail = (iquetail+1)&(ITEMQUESIZE-1); + } + +// +// P_SpawnPlayer +// Called when a player is spawned on the level. +// Most of the player structure stays unchanged +// between levels. +// + +extern byte playernumtotrans[MAXPLAYERS]; + +void P_SpawnPlayer (int n, const mapthing_t* mthing) + { + player_t* p; + fixed_t x; + fixed_t y; + fixed_t z; + mobj_t* mobj; + int i; + + // not playing? + + if (!playeringame[n]) + return; + + p = &players[n]; + + if (p->playerstate == PST_REBORN) + G_PlayerReborn (mthing->type-1); + + /* cph 2001/08/14 - use the options field of memorised player starts to + * indicate whether the start really exists in the level. + */ + if (!mthing->options) + I_Error("P_SpawnPlayer: attempt to spawn player at unavailable start point"); + + x = mthing->x << FRACBITS; + y = mthing->y << FRACBITS; + z = ONFLOORZ; + mobj = P_SpawnMobj (x,y,z, MT_PLAYER); + + // set color translations for player sprites + + mobj->flags |= playernumtotrans[n]<angle = ANG45 * (mthing->angle/45); + mobj->player = p; + mobj->health = p->health; + + p->mo = mobj; + p->playerstate = PST_LIVE; + p->refire = 0; + p->message = NULL; + p->damagecount = 0; + p->bonuscount = 0; + p->extralight = 0; + p->fixedcolormap = 0; + p->viewheight = VIEWHEIGHT; + + p->momx = p->momy = 0; // killough 10/98: initialize bobbing to 0. + + // setup gun psprite + + P_SetupPsprites (p); + + // give all cards in death match mode + + if (deathmatch) + for (i = 0 ; i < NUMCARDS ; i++) + p->cards[i] = true; + + if (mthing->type-1 == consoleplayer) + { + ST_Start(); // wake up the status bar + HU_Start(); // wake up the heads up text + } + R_SmoothPlaying_Reset(p); // e6y + } + +/* + * P_IsDoomnumAllowed() + * Based on code taken from P_LoadThings() in src/p_setup.c Return TRUE + * if the thing in question is expected to be available in the gamemode used. + */ + +boolean P_IsDoomnumAllowed(int doomnum) +{ + // Do not spawn cool, new monsters if !commercial + if (gamemode != commercial) + switch(doomnum) + { + case 64: // Archvile + case 65: // Former Human Commando + case 66: // Revenant + case 67: // Mancubus + case 68: // Arachnotron + case 69: // Hell Knight + case 71: // Pain Elemental + case 84: // Wolf SS + case 88: // Boss Brain + case 89: // Boss Shooter + return false; + } + + return true; +} + +// +// P_SpawnMapThing +// The fields of the mapthing should +// already be in host byte order. +// + +void P_SpawnMapThing (const mapthing_t* mthing) + { + int i; + //int bit; + mobj_t* mobj; + fixed_t x; + fixed_t y; + fixed_t z; + int options = mthing->options; /* cph 2001/07/07 - make writable copy */ + + // killough 2/26/98: Ignore type-0 things as NOPs + // phares 5/14/98: Ignore Player 5-8 starts (for now) + + switch(mthing->type) + { + case 0: + case DEN_PLAYER5: + case DEN_PLAYER6: + case DEN_PLAYER7: + case DEN_PLAYER8: + return; + } + + // killough 11/98: clear flags unused by Doom + // + // We clear the flags unused in Doom if we see flag mask 256 set, since + // it is reserved to be 0 under the new scheme. A 1 in this reserved bit + // indicates it's a Doom wad made by a Doom editor which puts 1's in + // bits that weren't used in Doom (such as HellMaker wads). So we should + // then simply ignore all upper bits. + + if (demo_compatibility || + (compatibility_level >= lxdoom_1_compatibility && + options & MTF_RESERVED)) { + if (!demo_compatibility) // cph - Add warning about bad thing flags + lprintf(LO_WARN, "P_SpawnMapThing: correcting bad flags (%u) (thing type %d)\n", + options, mthing->type); + options &= MTF_EASY|MTF_NORMAL|MTF_HARD|MTF_AMBUSH|MTF_NOTSINGLE; + } + + // count deathmatch start positions + + // doom2.exe has at most 10 deathmatch starts + if (mthing->type == 11) + { + if (!(!compatibility || deathmatch_p-deathmatchstarts < 10)) { + return; + } else { + // 1/11/98 killough -- new code removes limit on deathmatch starts: + + size_t offset = deathmatch_p - deathmatchstarts; + + if (offset >= num_deathmatchstarts) + { + num_deathmatchstarts = num_deathmatchstarts ? + num_deathmatchstarts*2 : 16; + deathmatchstarts = realloc(deathmatchstarts, + num_deathmatchstarts * + sizeof(*deathmatchstarts)); + deathmatch_p = deathmatchstarts + offset; + } + memcpy(deathmatch_p++, mthing, sizeof(*mthing)); + (deathmatch_p-1)->options = 1; + return; + } + } + + // check for players specially + + if (mthing->type <= 4 && mthing->type > 0) // killough 2/26/98 -- fix crashes + { +#ifdef DOGS + // killough 7/19/98: Marine's best friend :) + if (!netgame && mthing->type > 1 && mthing->type <= dogs+1 && + !players[mthing->type-1].secretcount) + { // use secretcount to avoid multiple dogs in case of multiple starts + players[mthing->type-1].secretcount = 1; + + // killough 10/98: force it to be a friend + options |= MTF_FRIEND; + if(HelperThing != -1) // haleyjd 9/22/99: deh substitution + { + int type = HelperThing - 1; + if(type >= 0 && type < NUMMOBJTYPES) + { + i = type; + } + else + { + doom_printf("Invalid value %i for helper, ignored.", HelperThing); + i = MT_DOGS; + } + } + else { + i = MT_DOGS; + } + goto spawnit; + } +#endif + + // save spots for respawning in coop games + playerstarts[mthing->type-1] = *mthing; + /* cph 2006/07/24 - use the otherwise-unused options field to flag that + * this start is present (so we know which elements of the array are filled + * in, in effect). Also note that the call below to P_SpawnPlayer must use + * the playerstarts version with this field set */ + playerstarts[mthing->type-1].options = 1; + + if (!deathmatch) + P_SpawnPlayer (mthing->type-1, &playerstarts[mthing->type-1]); + return; + } + + // check for apropriate skill level + + /* jff "not single" thing flag */ + if (!netgame && options & MTF_NOTSINGLE) + return; + + //jff 3/30/98 implement "not deathmatch" thing flag + + if (netgame && deathmatch && options & MTF_NOTDM) + return; + + //jff 3/30/98 implement "not cooperative" thing flag + + if (netgame && !deathmatch && options & MTF_NOTCOOP) + return; + + // killough 11/98: simplify + if (gameskill == sk_baby || gameskill == sk_easy ? + !(options & MTF_EASY) : + gameskill == sk_hard || gameskill == sk_nightmare ? + !(options & MTF_HARD) : !(options & MTF_NORMAL)) + return; + + // find which type to spawn + + // killough 8/23/98: use table for faster lookup + i = P_FindDoomedNum(mthing->type); + + // phares 5/16/98: + // Do not abort because of an unknown thing. Ignore it, but post a + // warning message for the player. + + if (i == NUMMOBJTYPES) + { + doom_printf("Unknown Thing type %i at (%i, %i)",mthing->type,mthing->x,mthing->y); + return; + } + + // don't spawn keycards and players in deathmatch + + if (deathmatch && mobjinfo[i].flags & MF_NOTDMATCH) + return; + + // don't spawn any monsters if -nomonsters + + if (nomonsters && (i == MT_SKULL || (mobjinfo[i].flags & MF_COUNTKILL))) + return; + + // spawn it +#ifdef DOGS +spawnit: +#endif + + x = mthing->x << FRACBITS; + y = mthing->y << FRACBITS; + + if (mobjinfo[i].flags & MF_SPAWNCEILING) + z = ONCEILINGZ; + else + z = ONFLOORZ; + + mobj = P_SpawnMobj (x,y,z, i); + mobj->spawnpoint = *mthing; + + if (mobj->tics > 0) + mobj->tics = 1 + (P_Random (pr_spawnthing) % mobj->tics); + + if (!(mobj->flags & MF_FRIEND) && + options & MTF_FRIEND && + mbf_features) + { + mobj->flags |= MF_FRIEND; // killough 10/98: + P_UpdateThinker(&mobj->thinker); // transfer friendliness flag + } + + /* killough 7/20/98: exclude friends */ + if (!((mobj->flags ^ MF_COUNTKILL) & (MF_FRIEND | MF_COUNTKILL))) + totalkills++; + + if (mobj->flags & MF_COUNTITEM) + totalitems++; + + mobj->angle = ANG45 * (mthing->angle/45); + if (options & MTF_AMBUSH) + mobj->flags |= MF_AMBUSH; + } + + +// +// GAME SPAWN FUNCTIONS +// + +// +// P_SpawnPuff +// + +extern fixed_t attackrange; + +void P_SpawnPuff(fixed_t x,fixed_t y,fixed_t z) + { + mobj_t* th; + // killough 5/5/98: remove dependence on order of evaluation: + int t = P_Random(pr_spawnpuff); + z += (t - P_Random(pr_spawnpuff))<<10; + + th = P_SpawnMobj (x,y,z, MT_PUFF); + th->momz = FRACUNIT; + th->tics -= P_Random(pr_spawnpuff)&3; + + if (th->tics < 1) + th->tics = 1; + + // don't make punches spark on the wall + + if (attackrange == MELEERANGE) + P_SetMobjState (th, S_PUFF3); + } + + +// +// P_SpawnBlood +// +void P_SpawnBlood(fixed_t x,fixed_t y,fixed_t z,int damage) + { + mobj_t* th; + // killough 5/5/98: remove dependence on order of evaluation: + int t = P_Random(pr_spawnblood); + z += (t - P_Random(pr_spawnblood))<<10; + th = P_SpawnMobj(x,y,z, MT_BLOOD); + th->momz = FRACUNIT*2; + th->tics -= P_Random(pr_spawnblood)&3; + + if (th->tics < 1) + th->tics = 1; + + if (damage <= 12 && damage >= 9) + P_SetMobjState (th,S_BLOOD2); + else if (damage < 9) + P_SetMobjState (th,S_BLOOD3); + } + + +// +// P_CheckMissileSpawn +// Moves the missile forward a bit +// and possibly explodes it right there. +// + +void P_CheckMissileSpawn (mobj_t* th) + { + th->tics -= P_Random(pr_missile)&3; + if (th->tics < 1) + th->tics = 1; + + // move a little forward so an angle can + // be computed if it immediately explodes + + th->x += (th->momx>>1); + th->y += (th->momy>>1); + th->z += (th->momz>>1); + + // killough 8/12/98: for non-missile objects (e.g. grenades) + if (!(th->flags & MF_MISSILE) && mbf_features) + return; + + // killough 3/15/98: no dropoff (really = don't care for missiles) + + if (!P_TryMove (th, th->x, th->y, false)) + P_ExplodeMissile (th); + } + + +// +// P_SpawnMissile +// + +mobj_t* P_SpawnMissile(mobj_t* source,mobj_t* dest,mobjtype_t type) + { + mobj_t* th; + angle_t an; + int dist; + + th = P_SpawnMobj (source->x,source->y,source->z + 4*8*FRACUNIT,type); + + if (th->info->seesound) + S_StartSound (th, th->info->seesound); + + P_SetTarget(&th->target, source); // where it came from + an = R_PointToAngle2 (source->x, source->y, dest->x, dest->y); + + // fuzzy player + + if (dest->flags & MF_SHADOW) + { // killough 5/5/98: remove dependence on order of evaluation: + int t = P_Random(pr_shadow); + an += (t - P_Random(pr_shadow))<<20; + } + + th->angle = an; + an >>= ANGLETOFINESHIFT; + th->momx = FixedMul (th->info->speed, finecosine[an]); + th->momy = FixedMul (th->info->speed, finesine[an]); + + dist = P_AproxDistance (dest->x - source->x, dest->y - source->y); + dist = dist / th->info->speed; + + if (dist < 1) + dist = 1; + + th->momz = (dest->z - source->z) / dist; + P_CheckMissileSpawn (th); + + return th; + } + + +// +// P_SpawnPlayerMissile +// Tries to aim at a nearby monster +// + +void P_SpawnPlayerMissile(mobj_t* source,mobjtype_t type) +{ + mobj_t *th; + fixed_t x, y, z, slope = 0; + + // see which target is to be aimed at + + angle_t an = source->angle; + + // killough 7/19/98: autoaiming was not in original beta + { + // killough 8/2/98: prefer autoaiming at enemies + uint_64_t mask = mbf_features ? MF_FRIEND : 0; + + do + { + slope = P_AimLineAttack(source, an, 16*64*FRACUNIT, mask); + if (!linetarget) + slope = P_AimLineAttack(source, an += 1<<26, 16*64*FRACUNIT, mask); + if (!linetarget) + slope = P_AimLineAttack(source, an -= 2<<26, 16*64*FRACUNIT, mask); + if (!linetarget) + an = source->angle, slope = 0; + } + while (mask && (mask=0, !linetarget)); // killough 8/2/98 + } + + x = source->x; + y = source->y; + z = source->z + 4*8*FRACUNIT; + + th = P_SpawnMobj (x,y,z, type); + + if (th->info->seesound) + S_StartSound (th, th->info->seesound); + + P_SetTarget(&th->target, source); + th->angle = an; + th->momx = FixedMul(th->info->speed,finecosine[an>>ANGLETOFINESHIFT]); + th->momy = FixedMul(th->info->speed,finesine[an>>ANGLETOFINESHIFT]); + th->momz = FixedMul(th->info->speed,slope); + + P_CheckMissileSpawn(th); + } diff --git a/src/p_mobj.h b/src/p_mobj.h new file mode 100644 index 0000000..ccd66f3 --- /dev/null +++ b/src/p_mobj.h @@ -0,0 +1,403 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Map Objects, MObj, definition and handling. + * + *-----------------------------------------------------------------------------*/ + +#ifndef __P_MOBJ__ +#define __P_MOBJ__ + +// Basics. +#include "tables.h" +#include "m_fixed.h" + +// We need the thinker_t stuff. +#include "d_think.h" + +// We need the WAD data structure for Map things, +// from the THINGS lump. +#include "doomdata.h" + +// States are tied to finite states are +// tied to animation frames. +// Needs precompiled tables/data structures. +#include "info.h" + +// +// NOTES: mobj_t +// +// mobj_ts are used to tell the refresh where to draw an image, +// tell the world simulation when objects are contacted, +// and tell the sound driver how to position a sound. +// +// The refresh uses the next and prev links to follow +// lists of things in sectors as they are being drawn. +// The sprite, frame, and angle elements determine which patch_t +// is used to draw the sprite if it is visible. +// The sprite and frame values are allmost allways set +// from state_t structures. +// The statescr.exe utility generates the states.h and states.c +// files that contain the sprite/frame numbers from the +// statescr.txt source file. +// The xyz origin point represents a point at the bottom middle +// of the sprite (between the feet of a biped). +// This is the default origin position for patch_ts grabbed +// with lumpy.exe. +// A walking creature will have its z equal to the floor +// it is standing on. +// +// The sound code uses the x,y, and subsector fields +// to do stereo positioning of any sound effited by the mobj_t. +// +// The play simulation uses the blocklinks, x,y,z, radius, height +// to determine when mobj_ts are touching each other, +// touching lines in the map, or hit by trace lines (gunshots, +// lines of sight, etc). +// The mobj_t->flags element has various bit flags +// used by the simulation. +// +// Every mobj_t is linked into a single sector +// based on its origin coordinates. +// The subsector_t is found with R_PointInSubsector(x,y), +// and the sector_t can be found with subsector->sector. +// The sector links are only used by the rendering code, +// the play simulation does not care about them at all. +// +// Any mobj_t that needs to be acted upon by something else +// in the play world (block movement, be shot, etc) will also +// need to be linked into the blockmap. +// If the thing has the MF_NOBLOCK flag set, it will not use +// the block links. It can still interact with other things, +// but only as the instigator (missiles will run into other +// things, but nothing can run into a missile). +// Each block in the grid is 128*128 units, and knows about +// every line_t that it contains a piece of, and every +// interactable mobj_t that has its origin contained. +// +// A valid mobj_t is a mobj_t that has the proper subsector_t +// filled in for its xy coordinates and is linked into the +// sector from which the subsector was made, or has the +// MF_NOSECTOR flag set (the subsector_t needs to be valid +// even if MF_NOSECTOR is set), and is linked into a blockmap +// block or has the MF_NOBLOCKMAP flag set. +// Links should only be modified by the P_[Un]SetThingPosition() +// functions. +// Do not change the MF_NO? flags while a thing is valid. +// +// Any questions? +// + +// +// Misc. mobj flags +// + +// Call P_SpecialThing when touched. +#define MF_SPECIAL (uint_64_t)(0x0000000000000001) +// Blocks. +#define MF_SOLID (uint_64_t)(0x0000000000000002) +// Can be hit. +#define MF_SHOOTABLE (uint_64_t)(0x0000000000000004) +// Don't use the sector links (invisible but touchable). +#define MF_NOSECTOR (uint_64_t)(0x0000000000000008) +// Don't use the blocklinks (inert but displayable) +#define MF_NOBLOCKMAP (uint_64_t)(0x0000000000000010) + +// Not to be activated by sound, deaf monster. +#define MF_AMBUSH (uint_64_t)(0x0000000000000020) +// Will try to attack right back. +#define MF_JUSTHIT (uint_64_t)(0x0000000000000040) +// Will take at least one step before attacking. +#define MF_JUSTATTACKED (uint_64_t)(0x0000000000000080) +// On level spawning (initial position), +// hang from ceiling instead of stand on floor. +#define MF_SPAWNCEILING (uint_64_t)(0x0000000000000100) +// Don't apply gravity (every tic), +// that is, object will float, keeping current height +// or changing it actively. +#define MF_NOGRAVITY (uint_64_t)(0x0000000000000200) + +// Movement flags. +// This allows jumps from high places. +#define MF_DROPOFF (uint_64_t)(0x0000000000000400) +// For players, will pick up items. +#define MF_PICKUP (uint_64_t)(0x0000000000000800) +// Player cheat. ??? +#define MF_NOCLIP (uint_64_t)(0x0000000000001000) +// Player: keep info about sliding along walls. +#define MF_SLIDE (uint_64_t)(0x0000000000002000) +// Allow moves to any height, no gravity. +// For active floaters, e.g. cacodemons, pain elementals. +#define MF_FLOAT (uint_64_t)(0x0000000000004000) +// Don't cross lines +// ??? or look at heights on teleport. +#define MF_TELEPORT (uint_64_t)(0x0000000000008000) +// Don't hit same species, explode on block. +// Player missiles as well as fireballs of various kinds. +#define MF_MISSILE (uint_64_t)(0x0000000000010000) +// Dropped by a demon, not level spawned. +// E.g. ammo clips dropped by dying former humans. +#define MF_DROPPED (uint_64_t)(0x0000000000020000) +// Use fuzzy draw (shadow demons or spectres), +// temporary player invisibility powerup. +#define MF_SHADOW (uint_64_t)(0x0000000000040000) +// Flag: don't bleed when shot (use puff), +// barrels and shootable furniture shall not bleed. +#define MF_NOBLOOD (uint_64_t)(0x0000000000080000) +// Don't stop moving halfway off a step, +// that is, have dead bodies slide down all the way. +#define MF_CORPSE (uint_64_t)(0x0000000000100000) +// Floating to a height for a move, ??? +// don't auto float to target's height. +#define MF_INFLOAT (uint_64_t)(0x0000000000200000) + +// On kill, count this enemy object +// towards intermission kill total. +// Happy gathering. +#define MF_COUNTKILL (uint_64_t)(0x0000000000400000) + +// On picking up, count this item object +// towards intermission item total. +#define MF_COUNTITEM (uint_64_t)(0x0000000000800000) + +// Special handling: skull in flight. +// Neither a cacodemon nor a missile. +#define MF_SKULLFLY (uint_64_t)(0x0000000001000000) + +// Don't spawn this object +// in death match mode (e.g. key cards). +#define MF_NOTDMATCH (uint_64_t)(0x0000000002000000) + +// Player sprites in multiplayer modes are modified +// using an internal color lookup table for re-indexing. +// If 0x4 0x8 or 0xc, +// use a translation table for player colormaps +#define MF_TRANSLATION (uint_64_t)(0x000000000c000000) +#define MF_TRANSLATION1 (uint_64_t)(0x0000000004000000) +#define MF_TRANSLATION2 (uint_64_t)(0x0000000008000000) +// Hmm ???. +#define MF_TRANSSHIFT 26 + +#define MF_UNUSED2 (uint_64_t)(0x0000000010000000) +#define MF_UNUSED3 (uint_64_t)(0x0000000020000000) + + // Translucent sprite? // phares +#define MF_TRANSLUCENT (uint_64_t)(0x0000000040000000) + +// this is free LONGLONG(0x0000000100000000) + +// these are greater than an int. That's why the flags below are now uint_64_t + +#define MF_TOUCHY LONGLONG(0x0000000100000000) +#define MF_BOUNCES LONGLONG(0x0000000200000000) +#define MF_FRIEND LONGLONG(0x0000000400000000) + +// killough 9/15/98: Same, but internal flags, not intended for .deh +// (some degree of opaqueness is good, to avoid compatibility woes) + +enum { + MIF_FALLING = 1, // Object is falling + MIF_ARMED = 2, // Object is armed (for MF_TOUCHY objects) +}; + +// Map Object definition. +// +// +// killough 2/20/98: +// +// WARNING: Special steps must be taken in p_saveg.c if C pointers are added to +// this mobj_s struct, or else savegames will crash when loaded. See p_saveg.c. +// Do not add "struct mobj_s *fooptr" without adding code to p_saveg.c to +// convert the pointers to ordinals and back for savegames. This was the whole +// reason behind monsters going to sleep when loading savegames (the "target" +// pointer was simply nullified after loading, to prevent Doom from crashing), +// and the whole reason behind loadgames crashing on savegames of AV attacks. +// + +// killough 9/8/98: changed some fields to shorts, +// for better memory usage (if only for cache). +/* cph 2006/08/28 - move Prev[XYZ] fields to the end of the struct. Add any + * other new fields to the end, and make sure you don't break savegames! */ + +typedef struct mobj_s +{ + // List: thinker links. + thinker_t thinker; + + // Info for drawing: position. + fixed_t x; + fixed_t y; + fixed_t z; + + // More list: links in sector (if needed) + struct mobj_s* snext; + struct mobj_s** sprev; // killough 8/10/98: change to ptr-to-ptr + + //More drawing info: to determine current sprite. + angle_t angle; // orientation + spritenum_t sprite; // used to find patch_t and flip value + int frame; // might be ORed with FF_FULLBRIGHT + + // Interaction info, by BLOCKMAP. + // Links in blocks (if needed). + struct mobj_s* bnext; + struct mobj_s** bprev; // killough 8/11/98: change to ptr-to-ptr + + struct subsector_s* subsector; + + // The closest interval over all contacted Sectors. + fixed_t floorz; + fixed_t ceilingz; + + // killough 11/98: the lowest floor over all contacted Sectors. + fixed_t dropoffz; + + // For movement checking. + fixed_t radius; + fixed_t height; + + // Momentums, used to update position. + fixed_t momx; + fixed_t momy; + fixed_t momz; + + // If == validcount, already checked. + int validcount; + + mobjtype_t type; + mobjinfo_t* info; // &mobjinfo[mobj->type] + + int tics; // state tic counter + state_t* state; + uint_64_t flags; + int intflags; // killough 9/15/98: internal flags + int health; + + // Movement direction, movement generation (zig-zagging). + short movedir; // 0-7 + short movecount; // when 0, select a new dir + short strafecount; // killough 9/8/98: monster strafing + + // Thing being chased/attacked (or NULL), + // also the originator for missiles. + struct mobj_s* target; + + // Reaction time: if non 0, don't attack yet. + // Used by player to freeze a bit after teleporting. + short reactiontime; + + // If >0, the current target will be chased no + // matter what (even if shot by another object) + short threshold; + + // killough 9/9/98: How long a monster pursues a target. + short pursuecount; + + short gear; // killough 11/98: used in torque simulation + + // Additional info record for player avatars only. + // Only valid if type == MT_PLAYER + struct player_s* player; + + // Player number last looked for. + short lastlook; + + // For nightmare respawn. + mapthing_t spawnpoint; + + // Thing being chased/attacked for tracers. + struct mobj_s* tracer; + + // new field: last known enemy -- killough 2/15/98 + struct mobj_s* lastenemy; + + // killough 8/2/98: friction properties part of sectors, + // not objects -- removed friction properties from here + // e6y: restored friction properties here + // Friction values for the sector the object is in + int friction; // phares 3/17/98 + int movefactor; + + // a linked list of sectors where this object appears + struct msecnode_s* touching_sectorlist; // phares 3/14/98 + + fixed_t PrevX; + fixed_t PrevY; + fixed_t PrevZ; + + fixed_t pad; // cph - needed so I can get the size unambiguously on amd64 + + // SEE WARNING ABOVE ABOUT POINTER FIELDS!!! +} mobj_t; + +// External declarations (fomerly in p_local.h) -- killough 5/2/98 + +#define VIEWHEIGHT (41*FRACUNIT) + +#define GRAVITY FRACUNIT +#define MAXMOVE (30*FRACUNIT) + +#define ONFLOORZ INT_MIN +#define ONCEILINGZ INT_MAX + +// Time interval for item respawning. +#define ITEMQUESIZE 128 + +#define FLOATSPEED (FRACUNIT*4) +#define STOPSPEED (FRACUNIT/16) + +// killough 11/98: +// For torque simulation: + +#define OVERDRIVE 6 +#define MAXGEAR (OVERDRIVE+16) + +// killough 11/98: +// Whether an object is "sentient" or not. Used for environmental influences. +#define sentient(mobj) ((mobj)->health > 0 && (mobj)->info->seestate) + +extern int iquehead; +extern int iquetail; + +void P_RespawnSpecials(void); +mobj_t *P_SpawnMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype_t type); +void P_RemoveMobj(mobj_t *th); +boolean P_SetMobjState(mobj_t *mobj, statenum_t state); +void P_MobjThinker(mobj_t *mobj); +void P_SpawnPuff(fixed_t x, fixed_t y, fixed_t z); +void P_SpawnBlood(fixed_t x, fixed_t y, fixed_t z, int damage); +mobj_t *P_SpawnMissile(mobj_t *source, mobj_t *dest, mobjtype_t type); +void P_SpawnPlayerMissile(mobj_t *source, mobjtype_t type); +boolean P_IsDoomnumAllowed(int doomnum); +void P_SpawnMapThing (const mapthing_t* mthing); +void P_SpawnPlayer(int n, const mapthing_t *mthing); +void P_CheckMissileSpawn(mobj_t*); // killough 8/2/98 +void P_ExplodeMissile(mobj_t*); // killough +#endif + diff --git a/src/p_plats.c b/src/p_plats.c new file mode 100644 index 0000000..9bea240 --- /dev/null +++ b/src/p_plats.c @@ -0,0 +1,437 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Plats (i.e. elevator platforms) code, raising/lowering. + * + *-----------------------------------------------------------------------------*/ + +#include "doomstat.h" +#include "m_random.h" +#include "r_main.h" +#include "p_spec.h" +#include "p_tick.h" +#include "s_sound.h" +#include "sounds.h" + +platlist_t *activeplats; // killough 2/14/98: made global again + +// +// T_PlatRaise() +// +// Action routine to move a plat up and down +// +// Passed a plat structure containing all pertinent information about the move +// No return +// +// jff 02/08/98 all cases with labels beginning with gen added to support +// generalized line type behaviors. + +void T_PlatRaise(plat_t* plat) +{ + result_e res; + + // handle plat moving, up, down, waiting, or in stasis, + switch(plat->status) + { + case up: // plat moving up + res = T_MovePlane(plat->sector,plat->speed,plat->high,plat->crush,0,1); + + // if a pure raise type, make the plat moving sound + if (plat->type == raiseAndChange + || plat->type == raiseToNearestAndChange) + { + if (!(leveltime&7)) + S_StartSound((mobj_t *)&plat->sector->soundorg, sfx_stnmov); + } + + // if encountered an obstacle, and not a crush type, reverse direction + if (res == crushed && (!plat->crush)) + { + plat->count = plat->wait; + plat->status = down; + S_StartSound((mobj_t *)&plat->sector->soundorg, sfx_pstart); + } + else // else handle reaching end of up stroke + { + if (res == pastdest) // end of stroke + { + // if not an instant toggle type, wait, make plat stop sound + if (plat->type!=toggleUpDn) + { + plat->count = plat->wait; + plat->status = waiting; + S_StartSound((mobj_t *)&plat->sector->soundorg, sfx_pstop); + } + else // else go into stasis awaiting next toggle activation + { + plat->oldstatus = plat->status;//jff 3/14/98 after action wait + plat->status = in_stasis; //for reactivation of toggle + } + + // lift types and pure raise types are done at end of up stroke + // only the perpetual type waits then goes back up + switch(plat->type) + { + case blazeDWUS: + case downWaitUpStay: + case raiseAndChange: + case raiseToNearestAndChange: + case genLift: + P_RemoveActivePlat(plat); // killough + default: + break; + } + } + } + break; + + case down: // plat moving down + res = T_MovePlane(plat->sector,plat->speed,plat->low,false,0,-1); + + // handle reaching end of down stroke + if (res == pastdest) + { + // if not an instant toggle, start waiting, make plat stop sound + if (plat->type!=toggleUpDn) //jff 3/14/98 toggle up down + { // is silent, instant, no waiting + plat->count = plat->wait; + plat->status = waiting; + S_StartSound((mobj_t *)&plat->sector->soundorg,sfx_pstop); + } + else // instant toggles go into stasis awaiting next activation + { + plat->oldstatus = plat->status;//jff 3/14/98 after action wait + plat->status = in_stasis; //for reactivation of toggle + } + + //jff 1/26/98 remove the plat if it bounced so it can be tried again + //only affects plats that raise and bounce + //killough 1/31/98: relax compatibility to demo_compatibility + + // remove the plat if its a pure raise type + if (!comp[comp_floors]) + { + switch(plat->type) + { + case raiseAndChange: + case raiseToNearestAndChange: + P_RemoveActivePlat(plat); + default: + break; + } + } + } + break; + + case waiting: // plat is waiting + if (!--plat->count) // downcount and check for delay elapsed + { + if (plat->sector->floorheight == plat->low) + plat->status = up; // if at bottom, start up + else + plat->status = down; // if at top, start down + + // make plat start sound + S_StartSound((mobj_t *)&plat->sector->soundorg,sfx_pstart); + } + break; //jff 1/27/98 don't pickup code added later to in_stasis + + case in_stasis: // do nothing if in stasis + break; + } +} + + +// +// EV_DoPlat +// +// Handle Plat linedef types +// +// Passed the linedef that activated the plat, the type of plat action, +// and for some plat types, an amount to raise +// Returns true if a thinker is started, or restarted from stasis +// +int EV_DoPlat +( line_t* line, + plattype_e type, + int amount ) +{ + plat_t* plat; + int secnum; + int rtn; + sector_t* sec; + + secnum = -1; + rtn = 0; + + + // Activate all plats that are in_stasis + switch(type) + { + case perpetualRaise: + P_ActivateInStasis(line->tag); + break; + + case toggleUpDn: + P_ActivateInStasis(line->tag); + rtn=1; + break; + + default: + break; + } + + // act on all sectors tagged the same as the activating linedef + while ((secnum = P_FindSectorFromLineTag(line,secnum)) >= 0) + { + sec = §ors[secnum]; + + // don't start a second floor function if already moving + if (P_SectorActive(floor_special,sec)) //jff 2/23/98 multiple thinkers + continue; + + // Create a thinker + rtn = 1; + plat = Z_Malloc( sizeof(*plat), PU_LEVSPEC, 0); + memset(plat, 0, sizeof(*plat)); + P_AddThinker(&plat->thinker); + + plat->type = type; + plat->sector = sec; + plat->sector->floordata = plat; //jff 2/23/98 multiple thinkers + plat->thinker.function = T_PlatRaise; + plat->crush = false; + plat->tag = line->tag; + + //jff 1/26/98 Avoid raise plat bouncing a head off a ceiling and then + //going down forever -- default low to plat height when triggered + plat->low = sec->floorheight; + + // set up plat according to type + switch(type) + { + case raiseToNearestAndChange: + plat->speed = PLATSPEED/2; + sec->floorpic = sides[line->sidenum[0]].sector->floorpic; + plat->high = P_FindNextHighestFloor(sec,sec->floorheight); + plat->wait = 0; + plat->status = up; + sec->special = 0; + //jff 3/14/98 clear old field as well + sec->oldspecial = 0; + + S_StartSound((mobj_t *)&sec->soundorg,sfx_stnmov); + break; + + case raiseAndChange: + plat->speed = PLATSPEED/2; + sec->floorpic = sides[line->sidenum[0]].sector->floorpic; + plat->high = sec->floorheight + amount*FRACUNIT; + plat->wait = 0; + plat->status = up; + + S_StartSound((mobj_t *)&sec->soundorg,sfx_stnmov); + break; + + case downWaitUpStay: + plat->speed = PLATSPEED * 4; + plat->low = P_FindLowestFloorSurrounding(sec); + + if (plat->low > sec->floorheight) + plat->low = sec->floorheight; + + plat->high = sec->floorheight; + plat->wait = 35*PLATWAIT; + plat->status = down; + S_StartSound((mobj_t *)&sec->soundorg,sfx_pstart); + break; + + case blazeDWUS: + plat->speed = PLATSPEED * 8; + plat->low = P_FindLowestFloorSurrounding(sec); + + if (plat->low > sec->floorheight) + plat->low = sec->floorheight; + + plat->high = sec->floorheight; + plat->wait = 35*PLATWAIT; + plat->status = down; + S_StartSound((mobj_t *)&sec->soundorg,sfx_pstart); + break; + + case perpetualRaise: + plat->speed = PLATSPEED; + plat->low = P_FindLowestFloorSurrounding(sec); + + if (plat->low > sec->floorheight) + plat->low = sec->floorheight; + + plat->high = P_FindHighestFloorSurrounding(sec); + + if (plat->high < sec->floorheight) + plat->high = sec->floorheight; + + plat->wait = 35*PLATWAIT; + plat->status = P_Random(pr_plats)&1; + + S_StartSound((mobj_t *)&sec->soundorg,sfx_pstart); + break; + + case toggleUpDn: //jff 3/14/98 add new type to support instant toggle + plat->speed = PLATSPEED; //not used + plat->wait = 35*PLATWAIT; //not used + plat->crush = true; //jff 3/14/98 crush anything in the way + + // set up toggling between ceiling, floor inclusive + plat->low = sec->ceilingheight; + plat->high = sec->floorheight; + plat->status = down; + break; + + default: + break; + } + P_AddActivePlat(plat); // add plat to list of active plats + } + return rtn; +} + +// The following were all rewritten by Lee Killough +// to use the new structure which places no limits +// on active plats. It also avoids spending as much +// time searching for active plats. Previously a +// fixed-size array was used, with NULL indicating +// empty entries, while now a doubly-linked list +// is used. + +// +// P_ActivateInStasis() +// +// Activate a plat that has been put in stasis +// (stopped perpetual floor, instant floor/ceil toggle) +// +// Passed the tag of the plat that should be reactivated +// Returns nothing +// +void P_ActivateInStasis(int tag) +{ + platlist_t *pl; + for (pl=activeplats; pl; pl=pl->next) // search the active plats + { + plat_t *plat = pl->plat; // for one in stasis with right tag + if (plat->tag == tag && plat->status == in_stasis) + { + if (plat->type==toggleUpDn) //jff 3/14/98 reactivate toggle type + plat->status = plat->oldstatus==up? down : up; + else + plat->status = plat->oldstatus; + plat->thinker.function = T_PlatRaise; + } + } +} + +// +// EV_StopPlat() +// +// Handler for "stop perpetual floor" linedef type +// +// Passed the linedef that stopped the plat +// Returns true if a plat was put in stasis +// +// jff 2/12/98 added int return value, fixed return +// +int EV_StopPlat(line_t* line) +{ + platlist_t *pl; + for (pl=activeplats; pl; pl=pl->next) // search the active plats + { + plat_t *plat = pl->plat; // for one with the tag not in stasis + if (plat->status != in_stasis && plat->tag == line->tag) + { + plat->oldstatus = plat->status; // put it in stasis + plat->status = in_stasis; + plat->thinker.function = NULL; + } + } + return 1; +} + +// +// P_AddActivePlat() +// +// Add a plat to the head of the active plat list +// +// Passed a pointer to the plat to add +// Returns nothing +// +void P_AddActivePlat(plat_t* plat) +{ + platlist_t *list = malloc(sizeof *list); + list->plat = plat; + plat->list = list; + if ((list->next = activeplats)) + list->next->prev = &list->next; + list->prev = &activeplats; + activeplats = list; +} + +// +// P_RemoveActivePlat() +// +// Remove a plat from the active plat list +// +// Passed a pointer to the plat to remove +// Returns nothing +// +void P_RemoveActivePlat(plat_t* plat) +{ + platlist_t *list = plat->list; + plat->sector->floordata = NULL; //jff 2/23/98 multiple thinkers + P_RemoveThinker(&plat->thinker); + if ((*list->prev = list->next)) + list->next->prev = list->prev; + free(list); +} + +// +// P_RemoveAllActivePlats() +// +// Remove all plats from the active plat list +// +// Passed nothing, returns nothing +// +void P_RemoveAllActivePlats(void) +{ + while (activeplats) + { + platlist_t *next = activeplats->next; + free(activeplats); + activeplats = next; + } +} diff --git a/src/p_pspr.c b/src/p_pspr.c new file mode 100644 index 0000000..afac1fc --- /dev/null +++ b/src/p_pspr.c @@ -0,0 +1,829 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Weapon sprite animation, weapon objects. + * Action functions for weapons. + * + *-----------------------------------------------------------------------------*/ + +#include "doomstat.h" +#include "r_main.h" +#include "p_map.h" +#include "p_inter.h" +#include "p_pspr.h" +#include "p_enemy.h" +#include "m_random.h" +#include "s_sound.h" +#include "sounds.h" +#include "d_event.h" +#include "r_demo.h" + +#define LOWERSPEED (FRACUNIT*6) +#define RAISESPEED (FRACUNIT*6) +#define WEAPONBOTTOM (FRACUNIT*128) +#define WEAPONTOP (FRACUNIT*32) + +#define BFGCELLS bfgcells /* Ty 03/09/98 externalized in p_inter.c */ + +extern void P_Thrust(player_t *, angle_t, fixed_t); + +// The following array holds the recoil values // phares + +static const int recoil_values[] = { // phares + 10, // wp_fist + 10, // wp_pistol + 30, // wp_shotgun + 10, // wp_chaingun + 100,// wp_missile + 20, // wp_plasma + 100,// wp_bfg + 0, // wp_chainsaw + 80 // wp_supershotgun +}; + +// +// P_SetPsprite +// + +static void P_SetPsprite(player_t *player, int position, statenum_t stnum) +{ + pspdef_t *psp = &player->psprites[position]; + + do + { + state_t *state; + + if (!stnum) + { + // object removed itself + psp->state = NULL; + break; + } + + state = &states[stnum]; + psp->state = state; + psp->tics = state->tics; // could be 0 + + if (state->misc1) + { + // coordinate set + psp->sx = state->misc1 << FRACBITS; + psp->sy = state->misc2 << FRACBITS; + } + + // Call action routine. + // Modified handling. + if (state->action) + { + state->action(player, psp); + if (!psp->state) + break; + } + stnum = psp->state->nextstate; + } + while (!psp->tics); // an initial state of 0 could cycle through +} + +// +// P_BringUpWeapon +// Starts bringing the pending weapon up +// from the bottom of the screen. +// Uses player +// + +static void P_BringUpWeapon(player_t *player) +{ + statenum_t newstate; + + if (player->pendingweapon == wp_nochange) + player->pendingweapon = player->readyweapon; + + if (player->pendingweapon == wp_chainsaw) + S_StartSound (player->mo, sfx_sawup); + + newstate = weaponinfo[player->pendingweapon].upstate; + + player->pendingweapon = wp_nochange; + // killough 12/98: prevent pistol from starting visibly at bottom of screen: + player->psprites[ps_weapon].sy = + mbf_features ? WEAPONBOTTOM+FRACUNIT*2 : WEAPONBOTTOM; + + P_SetPsprite(player, ps_weapon, newstate); +} + +// The first set is where the weapon preferences from // killough, +// default.cfg are stored. These values represent the keys used // phares +// in DOOM2 to bring up the weapon, i.e. 6 = plasma gun. These // | +// are NOT the wp_* constants. // V + +int weapon_preferences[2][NUMWEAPONS+1] = { + {6, 9, 4, 3, 2, 8, 5, 7, 1, 0}, // !compatibility preferences + {6, 9, 4, 3, 2, 8, 5, 7, 1, 0}, // compatibility preferences +}; + +// P_SwitchWeapon checks current ammo levels and gives you the +// most preferred weapon with ammo. It will not pick the currently +// raised weapon. When called from P_CheckAmmo this won't matter, +// because the raised weapon has no ammo anyway. When called from +// G_BuildTiccmd you want to toggle to a different weapon regardless. + +int P_SwitchWeapon(player_t *player) +{ + int *prefer = weapon_preferences[demo_compatibility!=0]; // killough 3/22/98 + int currentweapon = player->readyweapon; + int newweapon = currentweapon; + int i = NUMWEAPONS+1; // killough 5/2/98 + + // killough 2/8/98: follow preferences and fix BFG/SSG bugs + + do + switch (*prefer++) + { + case 1: + if (!player->powers[pw_strength]) // allow chainsaw override + break; + case 0: + newweapon = wp_fist; + break; + case 2: + if (player->ammo[am_clip]) + newweapon = wp_pistol; + break; + case 3: + if (player->weaponowned[wp_shotgun] && player->ammo[am_shell]) + newweapon = wp_shotgun; + break; + case 4: + if (player->weaponowned[wp_chaingun] && player->ammo[am_clip]) + newweapon = wp_chaingun; + break; + case 5: + if (player->weaponowned[wp_missile] && player->ammo[am_misl]) + newweapon = wp_missile; + break; + case 6: + if (player->weaponowned[wp_plasma] && player->ammo[am_cell] && + gamemode != shareware) + newweapon = wp_plasma; + break; + case 7: + if (player->weaponowned[wp_bfg] && gamemode != shareware && + player->ammo[am_cell] >= (demo_compatibility ? 41 : 40)) + newweapon = wp_bfg; + break; + case 8: + if (player->weaponowned[wp_chainsaw]) + newweapon = wp_chainsaw; + break; + case 9: + if (player->weaponowned[wp_supershotgun] && gamemode == commercial && + player->ammo[am_shell] >= (demo_compatibility ? 3 : 2)) + newweapon = wp_supershotgun; + break; + } + while (newweapon==currentweapon && --i); // killough 5/2/98 + return newweapon; +} + +// killough 5/2/98: whether consoleplayer prefers weapon w1 over weapon w2. +int P_WeaponPreferred(int w1, int w2) +{ + return + (weapon_preferences[0][0] != ++w2 && (weapon_preferences[0][0] == ++w1 || + (weapon_preferences[0][1] != w2 && (weapon_preferences[0][1] == w1 || + (weapon_preferences[0][2] != w2 && (weapon_preferences[0][2] == w1 || + (weapon_preferences[0][3] != w2 && (weapon_preferences[0][3] == w1 || + (weapon_preferences[0][4] != w2 && (weapon_preferences[0][4] == w1 || + (weapon_preferences[0][5] != w2 && (weapon_preferences[0][5] == w1 || + (weapon_preferences[0][6] != w2 && (weapon_preferences[0][6] == w1 || + (weapon_preferences[0][7] != w2 && (weapon_preferences[0][7] == w1 + )))))))))))))))); +} + +// +// P_CheckAmmo +// Returns true if there is enough ammo to shoot. +// If not, selects the next weapon to use. +// (only in demo_compatibility mode -- killough 3/22/98) +// + +boolean P_CheckAmmo(player_t *player) +{ + ammotype_t ammo = weaponinfo[player->readyweapon].ammo; + int count = 1; // Regular + + if (player->readyweapon == wp_bfg) // Minimal amount for one shot varies. + count = BFGCELLS; + else + if (player->readyweapon == wp_supershotgun) // Double barrel. + count = 2; + + // Some do not need ammunition anyway. + // Return if current ammunition sufficient. + + if (ammo == am_noammo || player->ammo[ammo] >= count) + return true; + + // Out of ammo, pick a weapon to change to. + // + // killough 3/22/98: for old demos we do the switch here and now; + // for Boom games we cannot do this, and have different player + // preferences across demos or networks, so we have to use the + // G_BuildTiccmd() interface instead of making the switch here. + + if (demo_compatibility) + { + player->pendingweapon = P_SwitchWeapon(player); // phares + // Now set appropriate weapon overlay. + P_SetPsprite(player,ps_weapon,weaponinfo[player->readyweapon].downstate); + } + + return false; +} + +// +// P_FireWeapon. +// + +static void P_FireWeapon(player_t *player) +{ + statenum_t newstate; + + if (!P_CheckAmmo(player)) + return; + + P_SetMobjState(player->mo, S_PLAY_ATK1); + newstate = weaponinfo[player->readyweapon].atkstate; + P_SetPsprite(player, ps_weapon, newstate); + P_NoiseAlert(player->mo, player->mo); +} + +// +// P_DropWeapon +// Player died, so put the weapon away. +// + +void P_DropWeapon(player_t *player) +{ + P_SetPsprite(player, ps_weapon, weaponinfo[player->readyweapon].downstate); +} + +// +// A_WeaponReady +// The player can fire the weapon +// or change to another weapon at this time. +// Follows after getting weapon up, +// or after previous attack/fire sequence. +// + +void A_WeaponReady(player_t *player, pspdef_t *psp) +{ + // get out of attack state + if (player->mo->state == &states[S_PLAY_ATK1] + || player->mo->state == &states[S_PLAY_ATK2] ) + P_SetMobjState(player->mo, S_PLAY); + + if (player->readyweapon == wp_chainsaw && psp->state == &states[S_SAW]) + S_StartSound(player->mo, sfx_sawidl); + + // check for change + // if player is dead, put the weapon away + + if (player->pendingweapon != wp_nochange || !player->health) + { + // change weapon (pending weapon should already be validated) + statenum_t newstate = weaponinfo[player->readyweapon].downstate; + P_SetPsprite(player, ps_weapon, newstate); + return; + } + + // check for fire + // the missile launcher and bfg do not auto fire + + if (player->cmd.buttons & BT_ATTACK) + { + if (!player->attackdown || (player->readyweapon != wp_missile && + player->readyweapon != wp_bfg)) + { + player->attackdown = true; + P_FireWeapon(player); + return; + } + } + else + player->attackdown = false; + + // bob the weapon based on movement speed + { + int angle = (128*leveltime) & FINEMASK; + psp->sx = FRACUNIT + FixedMul(player->bob, finecosine[angle]); + angle &= FINEANGLES/2-1; + psp->sy = WEAPONTOP + FixedMul(player->bob, finesine[angle]); + } +} + +// +// A_ReFire +// The player can re-fire the weapon +// without lowering it entirely. +// + +void A_ReFire(player_t *player, pspdef_t *psp) +{ + // check for fire + // (if a weaponchange is pending, let it go through instead) + + if ( (player->cmd.buttons & BT_ATTACK) + && player->pendingweapon == wp_nochange && player->health) + { + player->refire++; + P_FireWeapon(player); + } + else + { + player->refire = 0; + P_CheckAmmo(player); + } +} + +void A_CheckReload(player_t *player, pspdef_t *psp) +{ + if (!P_CheckAmmo(player) && compatibility_level >= prboom_4_compatibility) { + /* cph 2002/08/08 - In old Doom, P_CheckAmmo would start the weapon lowering + * immediately. This was lost in Boom when the weapon switching logic was + * rewritten. But we must tell Doom that we don't need to complete the + * reload frames for the weapon here. G_BuildTiccmd will set ->pendingweapon + * for us later on. */ + P_SetPsprite(player,ps_weapon,weaponinfo[player->readyweapon].downstate); + } +} + +// +// A_Lower +// Lowers current weapon, +// and changes weapon at bottom. +// + +void A_Lower(player_t *player, pspdef_t *psp) +{ + psp->sy += LOWERSPEED; + + // Is already down. + if (psp->sy < WEAPONBOTTOM) + return; + + // Player is dead. + if (player->playerstate == PST_DEAD) + { + psp->sy = WEAPONBOTTOM; + return; // don't bring weapon back up + } + + // The old weapon has been lowered off the screen, + // so change the weapon and start raising it + + if (!player->health) + { // Player is dead, so keep the weapon off screen. + P_SetPsprite(player, ps_weapon, S_NULL); + return; + } + + player->readyweapon = player->pendingweapon; + + P_BringUpWeapon(player); +} + +// +// A_Raise +// + +void A_Raise(player_t *player, pspdef_t *psp) +{ + statenum_t newstate; + + psp->sy -= RAISESPEED; + + if (psp->sy > WEAPONTOP) + return; + + psp->sy = WEAPONTOP; + + // The weapon has been raised all the way, + // so change to the ready state. + + newstate = weaponinfo[player->readyweapon].readystate; + + P_SetPsprite(player, ps_weapon, newstate); +} + + +// Weapons now recoil, amount depending on the weapon. // phares +// // | +// The P_SetPsprite call in each of the weapon firing routines // V +// was moved here so the recoil could be synched with the +// muzzle flash, rather than the pressing of the trigger. +// The BFG delay caused this to be necessary. + +static void A_FireSomething(player_t* player,int adder) +{ + P_SetPsprite(player, ps_flash, + weaponinfo[player->readyweapon].flashstate+adder); + + // killough 3/27/98: prevent recoil in no-clipping mode + if (!(player->mo->flags & MF_NOCLIP)) + if (!compatibility && weapon_recoil) + P_Thrust(player, + ANG180+player->mo->angle, // ^ + 2048*recoil_values[player->readyweapon]); // | +} // phares + +// +// A_GunFlash +// + +void A_GunFlash(player_t *player, pspdef_t *psp) +{ + P_SetMobjState(player->mo, S_PLAY_ATK2); + + A_FireSomething(player,0); // phares +} + +// +// WEAPON ATTACKS +// + +// +// A_Punch +// + +void A_Punch(player_t *player, pspdef_t *psp) +{ + angle_t angle; + int t, slope, damage = (P_Random(pr_punch)%10+1)<<1; + + if (player->powers[pw_strength]) + damage *= 10; + + angle = player->mo->angle; + + // killough 5/5/98: remove dependence on order of evaluation: + t = P_Random(pr_punchangle); + angle += (t - P_Random(pr_punchangle))<<18; + + /* killough 8/2/98: make autoaiming prefer enemies */ + if (!mbf_features || + (slope = P_AimLineAttack(player->mo, angle, MELEERANGE, MF_FRIEND), + !linetarget)) + slope = P_AimLineAttack(player->mo, angle, MELEERANGE, 0); + + P_LineAttack(player->mo, angle, MELEERANGE, slope, damage); + + if (!linetarget) + return; + + S_StartSound(player->mo, sfx_punch); + + // turn to face target + + player->mo->angle = R_PointToAngle2(player->mo->x, player->mo->y, + linetarget->x, linetarget->y); + R_SmoothPlaying_Reset(player); // e6y +} + +// +// A_Saw +// + +void A_Saw(player_t *player, pspdef_t *psp) +{ + int slope, damage = 2*(P_Random(pr_saw)%10+1); + angle_t angle = player->mo->angle; + // killough 5/5/98: remove dependence on order of evaluation: + int t = P_Random(pr_saw); + angle += (t - P_Random(pr_saw))<<18; + + /* Use meleerange + 1 so that the puff doesn't skip the flash + * killough 8/2/98: make autoaiming prefer enemies */ + if (!mbf_features || + (slope = P_AimLineAttack(player->mo, angle, MELEERANGE+1, MF_FRIEND), + !linetarget)) + slope = P_AimLineAttack(player->mo, angle, MELEERANGE+1, 0); + + P_LineAttack(player->mo, angle, MELEERANGE+1, slope, damage); + + if (!linetarget) + { + S_StartSound(player->mo, sfx_sawful); + return; + } + + S_StartSound(player->mo, sfx_sawhit); + + // turn to face target + angle = R_PointToAngle2(player->mo->x, player->mo->y, + linetarget->x, linetarget->y); + + if (angle - player->mo->angle > ANG180) { + if (angle - player->mo->angle < -ANG90/20) + player->mo->angle = angle + ANG90/21; + else + player->mo->angle -= ANG90/20; + } else { + if (angle - player->mo->angle > ANG90/20) + player->mo->angle = angle - ANG90/21; + else + player->mo->angle += ANG90/20; + } + + player->mo->flags |= MF_JUSTATTACKED; + R_SmoothPlaying_Reset(player); // e6y +} + +// +// A_FireMissile +// + +void A_FireMissile(player_t *player, pspdef_t *psp) +{ + player->ammo[weaponinfo[player->readyweapon].ammo]--; + P_SpawnPlayerMissile(player->mo, MT_ROCKET); +} + +// +// A_FireBFG +// + +void A_FireBFG(player_t *player, pspdef_t *psp) +{ + player->ammo[weaponinfo[player->readyweapon].ammo] -= BFGCELLS; + P_SpawnPlayerMissile(player->mo, MT_BFG); +} + +// +// A_FirePlasma +// + +void A_FirePlasma(player_t *player, pspdef_t *psp) +{ + player->ammo[weaponinfo[player->readyweapon].ammo]--; + + A_FireSomething(player,P_Random(pr_plasma)&1); // phares + P_SpawnPlayerMissile(player->mo, MT_PLASMA); +} + +// +// P_BulletSlope +// Sets a slope so a near miss is at aproximately +// the height of the intended target +// + +static fixed_t bulletslope; + +static void P_BulletSlope(mobj_t *mo) +{ + angle_t an = mo->angle; // see which target is to be aimed at + + /* killough 8/2/98: make autoaiming prefer enemies */ + uint_64_t mask = mbf_features ? MF_FRIEND : 0; + + do + { + bulletslope = P_AimLineAttack(mo, an, 16*64*FRACUNIT, mask); + if (!linetarget) + bulletslope = P_AimLineAttack(mo, an += 1<<26, 16*64*FRACUNIT, mask); + if (!linetarget) + bulletslope = P_AimLineAttack(mo, an -= 2<<26, 16*64*FRACUNIT, mask); + } + while (mask && (mask=0, !linetarget)); /* killough 8/2/98 */ +} + +// +// P_GunShot +// + +static void P_GunShot(mobj_t *mo, boolean accurate) +{ + int damage = 5*(P_Random(pr_gunshot)%3+1); + angle_t angle = mo->angle; + + if (!accurate) + { // killough 5/5/98: remove dependence on order of evaluation: + int t = P_Random(pr_misfire); + angle += (t - P_Random(pr_misfire))<<18; + } + + P_LineAttack(mo, angle, MISSILERANGE, bulletslope, damage); +} + +// +// A_FirePistol +// + +void A_FirePistol(player_t *player, pspdef_t *psp) +{ + S_StartSound(player->mo, sfx_pistol); + + P_SetMobjState(player->mo, S_PLAY_ATK2); + player->ammo[weaponinfo[player->readyweapon].ammo]--; + + A_FireSomething(player,0); // phares + P_BulletSlope(player->mo); + P_GunShot(player->mo, !player->refire); +} + +// +// A_FireShotgun +// + +void A_FireShotgun(player_t *player, pspdef_t *psp) +{ + int i; + + S_StartSound(player->mo, sfx_shotgn); + P_SetMobjState(player->mo, S_PLAY_ATK2); + + player->ammo[weaponinfo[player->readyweapon].ammo]--; + + A_FireSomething(player,0); // phares + + P_BulletSlope(player->mo); + + for (i=0; i<7; i++) + P_GunShot(player->mo, false); +} + +// +// A_FireShotgun2 +// + +void A_FireShotgun2(player_t *player, pspdef_t *psp) +{ + int i; + + S_StartSound(player->mo, sfx_dshtgn); + P_SetMobjState(player->mo, S_PLAY_ATK2); + player->ammo[weaponinfo[player->readyweapon].ammo] -= 2; + + A_FireSomething(player,0); // phares + + P_BulletSlope(player->mo); + + for (i=0; i<20; i++) + { + int damage = 5*(P_Random(pr_shotgun)%3+1); + angle_t angle = player->mo->angle; + // killough 5/5/98: remove dependence on order of evaluation: + int t = P_Random(pr_shotgun); + angle += (t - P_Random(pr_shotgun))<<19; + t = P_Random(pr_shotgun); + P_LineAttack(player->mo, angle, MISSILERANGE, bulletslope + + ((t - P_Random(pr_shotgun))<<5), damage); + } +} + +// +// A_FireCGun +// + +void A_FireCGun(player_t *player, pspdef_t *psp) +{ + if (player->ammo[weaponinfo[player->readyweapon].ammo] || comp[comp_sound]) + S_StartSound(player->mo, sfx_pistol); + + if (!player->ammo[weaponinfo[player->readyweapon].ammo]) + return; + + P_SetMobjState(player->mo, S_PLAY_ATK2); + player->ammo[weaponinfo[player->readyweapon].ammo]--; + + A_FireSomething(player,psp->state - &states[S_CHAIN1]); // phares + + P_BulletSlope(player->mo); + + P_GunShot(player->mo, !player->refire); +} + +void A_Light0(player_t *player, pspdef_t *psp) +{ + player->extralight = 0; +} + +void A_Light1 (player_t *player, pspdef_t *psp) +{ + player->extralight = 1; +} + +void A_Light2 (player_t *player, pspdef_t *psp) +{ + player->extralight = 2; +} + +// +// A_BFGSpray +// Spawn a BFG explosion on every monster in view +// + +void A_BFGSpray(mobj_t *mo) +{ + int i; + + for (i=0 ; i<40 ; i++) // offset angles from its attack angle + { + int j, damage; + angle_t an = mo->angle - ANG90/2 + ANG90/40*i; + + // mo->target is the originator (player) of the missile + + // killough 8/2/98: make autoaiming prefer enemies + if (!mbf_features || + (P_AimLineAttack(mo->target, an, 16*64*FRACUNIT, MF_FRIEND), + !linetarget)) + P_AimLineAttack(mo->target, an, 16*64*FRACUNIT, 0); + + if (!linetarget) + continue; + + P_SpawnMobj(linetarget->x, linetarget->y, + linetarget->z + (linetarget->height>>2), MT_EXTRABFG); + + for (damage=j=0; j<15; j++) + damage += (P_Random(pr_bfg)&7) + 1; + + P_DamageMobj(linetarget, mo->target, mo->target, damage); + } +} + +// +// A_BFGsound +// + +void A_BFGsound(player_t *player, pspdef_t *psp) +{ + S_StartSound(player->mo, sfx_bfg); +} + +// +// P_SetupPsprites +// Called at start of level for each player. +// + +void P_SetupPsprites(player_t *player) +{ + int i; + + // remove all psprites + for (i=0; ipsprites[i].state = NULL; + + // spawn the gun + player->pendingweapon = player->readyweapon; + P_BringUpWeapon(player); +} + +// +// P_MovePsprites +// Called every tic by player thinking routine. +// + +void P_MovePsprites(player_t *player) +{ + pspdef_t *psp = player->psprites; + int i; + + // a null state means not active + // drop tic count and possibly change state + // a -1 tic count never changes + + for (i=0; istate && psp->tics != -1 && !--psp->tics) + P_SetPsprite(player, i, psp->state->nextstate); + + player->psprites[ps_flash].sx = player->psprites[ps_weapon].sx; + player->psprites[ps_flash].sy = player->psprites[ps_weapon].sy; +} diff --git a/src/p_pspr.h b/src/p_pspr.h new file mode 100644 index 0000000..46d9652 --- /dev/null +++ b/src/p_pspr.h @@ -0,0 +1,119 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Sprite animation. + * + *-----------------------------------------------------------------------------*/ + +#ifndef __P_PSPR__ +#define __P_PSPR__ + +/* Basic data types. + * Needs fixed point, and BAM angles. */ + +#include "m_fixed.h" +#include "tables.h" + +/* Needs to include the precompiled sprite animation tables. + * + * Header generated by multigen utility. + * This includes all the data for thing animation, + * i.e. the Thing Atrributes table and the Frame Sequence table. + */ + +#include "info.h" + +#ifdef __GNUG__ +#pragma interface +#endif + +/* + * Frame flags: + * handles maximum brightness (torches, muzzle flare, light sources) + */ + +#define FF_FULLBRIGHT 0x8000 /* flag in thing->frame */ +#define FF_FRAMEMASK 0x7fff + +/* + * Overlay psprites are scaled shapes + * drawn directly on the view screen, + * coordinates are given for a 320*200 view screen. + */ + +typedef enum +{ + ps_weapon, + ps_flash, + NUMPSPRITES +} psprnum_t; + +typedef struct +{ + state_t *state; /* a NULL state means not active */ + int tics; + fixed_t sx; + fixed_t sy; +} pspdef_t; + +extern int weapon_preferences[2][NUMWEAPONS+1]; /* killough 5/2/98 */ +int P_WeaponPreferred(int w1, int w2); + +struct player_s; +int P_SwitchWeapon(struct player_s *player); +boolean P_CheckAmmo(struct player_s *player); +void P_SetupPsprites(struct player_s *curplayer); +void P_MovePsprites(struct player_s *curplayer); +void P_DropWeapon(struct player_s *player); + +void A_Light0(); +void A_WeaponReady(); +void A_Lower(); +void A_Raise(); +void A_Punch(); +void A_ReFire(); +void A_FirePistol(); +void A_Light1(); +void A_FireShotgun(); +void A_Light2(); +void A_FireShotgun2(); +void A_CheckReload(); +void A_OpenShotgun2(); +void A_LoadShotgun2(); +void A_CloseShotgun2(); +void A_FireCGun(); +void A_GunFlash(); +void A_FireMissile(); +void A_Saw(); +void A_FirePlasma(); +void A_BFGsound(); +void A_FireBFG(); +void A_BFGSpray(); + +#endif diff --git a/src/p_saveg.c b/src/p_saveg.c new file mode 100644 index 0000000..7e3d63b --- /dev/null +++ b/src/p_saveg.c @@ -0,0 +1,1029 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Archiving: SaveGame I/O. + * + *-----------------------------------------------------------------------------*/ + +#include "doomstat.h" +#include "r_main.h" +#include "p_maputl.h" +#include "p_spec.h" +#include "p_tick.h" +#include "p_saveg.h" +#include "m_random.h" +#include "am_map.h" +#include "p_enemy.h" +#include "lprintf.h" + +byte *save_p; + +// Pads save_p to a 4-byte boundary +// so that the load/save works on SGI&Gecko. +#define PADSAVEP() do { save_p += (4 - ((int) save_p & 3)) & 3; } while (0) +// +// P_ArchivePlayers +// +void P_ArchivePlayers (void) +{ + int i; + + CheckSaveGame(sizeof(player_t) * MAXPLAYERS); // killough + for (i=0 ; ipsprites[j].state) + dest->psprites[j].state = + (state_t *)(dest->psprites[j].state-states); + } +} + +// +// P_UnArchivePlayers +// +void P_UnArchivePlayers (void) +{ + int i; + + for (i=0 ; ifloorheight + sizeof sec->ceilingheight) + * numsectors + sizeof(short)*3*numlines + 4; + + for (i=0; itextureoffset + sizeof si->rowoffset; + if (lines[i].sidenum[1] != NO_INDEX) + size += + sizeof(short)*3 + sizeof si->textureoffset + sizeof si->rowoffset; + } + + CheckSaveGame(size); // killough + + PADSAVEP(); // killough 3/22/98 + + put = (short *)save_p; + + // do sectors + for (i=0, sec = sectors ; ifloorheight, sizeof sec->floorheight); + put = (void *)((char *) put + sizeof sec->floorheight); + memcpy(put, &sec->ceilingheight, sizeof sec->ceilingheight); + put = (void *)((char *) put + sizeof sec->ceilingheight); + + *put++ = sec->floorpic; + *put++ = sec->ceilingpic; + *put++ = sec->lightlevel; + *put++ = sec->special; // needed? yes -- transfer types + *put++ = sec->tag; // needed? need them -- killough + } + + // do lines + for (i=0, li = lines ; iflags; + *put++ = li->special; + *put++ = li->tag; + + for (j=0; j<2; j++) + if (li->sidenum[j] != NO_INDEX) + { + si = &sides[li->sidenum[j]]; + + // killough 10/98: save full sidedef offsets, + // preserving fractional scroll offsets + + memcpy(put, &si->textureoffset, sizeof si->textureoffset); + put = (void *)((char *) put + sizeof si->textureoffset); + memcpy(put, &si->rowoffset, sizeof si->rowoffset); + put = (void *)((char *) put + sizeof si->rowoffset); + + *put++ = si->toptexture; + *put++ = si->bottomtexture; + *put++ = si->midtexture; + } + } + save_p = (byte *) put; +} + + + +// +// P_UnArchiveWorld +// +void P_UnArchiveWorld (void) +{ + int i; + sector_t *sec; + line_t *li; + const short *get; + + PADSAVEP(); // killough 3/22/98 + + get = (short *) save_p; + + // do sectors + for (i=0, sec = sectors ; ifloorheight, get, sizeof sec->floorheight); + get = (void *)((char *) get + sizeof sec->floorheight); + memcpy(&sec->ceilingheight, get, sizeof sec->ceilingheight); + get = (void *)((char *) get + sizeof sec->ceilingheight); + + sec->floorpic = *get++; + sec->ceilingpic = *get++; + sec->lightlevel = *get++; + sec->special = *get++; + sec->tag = *get++; + sec->ceilingdata = 0; //jff 2/22/98 now three thinker fields, not two + sec->floordata = 0; + sec->lightingdata = 0; + sec->soundtarget = 0; + } + + // do lines + for (i=0, li = lines ; iflags = *get++; + li->special = *get++; + li->tag = *get++; + for (j=0 ; j<2 ; j++) + if (li->sidenum[j] != NO_INDEX) + { + side_t *si = &sides[li->sidenum[j]]; + + // killough 10/98: load full sidedef offsets, including fractions + + memcpy(&si->textureoffset, get, sizeof si->textureoffset); + get = (void *)((char *) get + sizeof si->textureoffset); + memcpy(&si->rowoffset, get, sizeof si->rowoffset); + get = (void *)((char *) get + sizeof si->rowoffset); + + si->toptexture = *get++; + si->bottomtexture = *get++; + si->midtexture = *get++; + } + } + save_p = (byte *) get; +} + +// +// Thinkers +// + +typedef enum { + tc_end, + tc_mobj +} thinkerclass_t; + +// phares 9/13/98: Moved this code outside of P_ArchiveThinkers so the +// thinker indices could be used by the code that saves sector info. + +static int number_of_thinkers; + +void P_ThinkerToIndex(void) + { + thinker_t *th; + + // killough 2/14/98: + // count the number of thinkers, and mark each one with its index, using + // the prev field as a placeholder, since it can be restored later. + + number_of_thinkers = 0; + for (th = thinkercap.next ; th != &thinkercap ; th=th->next) + if (th->function == P_MobjThinker) + th->prev = (thinker_t *) ++number_of_thinkers; + } + +// phares 9/13/98: Moved this code outside of P_ArchiveThinkers so the +// thinker indices could be used by the code that saves sector info. + +void P_IndexToThinker(void) + { + // killough 2/14/98: restore prev pointers + thinker_t *th; + thinker_t *prev = &thinkercap; + + for (th = thinkercap.next ; th != &thinkercap ; prev=th, th=th->next) + th->prev = prev; + } + +// +// P_ArchiveThinkers +// +// 2/14/98 killough: substantially modified to fix savegame bugs + +void P_ArchiveThinkers (void) +{ + thinker_t *th; + + CheckSaveGame(sizeof brain); // killough 3/26/98: Save boss brain state + memcpy(save_p, &brain, sizeof brain); + save_p += sizeof brain; + + /* check that enough room is available in savegame buffer + * - killough 2/14/98 + * cph - use number_of_thinkers saved by P_ThinkerToIndex above + * size per object is sizeof(mobj_t) - 2*sizeof(void*) - 4*sizeof(fixed_t) plus + * padded type (4) plus 5*sizeof(void*), i.e. sizeof(mobj_t) + 4 + + * 3*sizeof(void*) + * cph - +1 for the tc_end + */ + CheckSaveGame(number_of_thinkers*(sizeof(mobj_t)-3*sizeof(fixed_t)+4+3*sizeof(void*)) +1); + + // save off the current thinkers + for (th = thinkercap.next ; th != &thinkercap ; th=th->next) + if (th->function == P_MobjThinker) + { + mobj_t *mobj; + + *save_p++ = tc_mobj; + PADSAVEP(); + mobj = (mobj_t *)save_p; + /* cph 2006/07/30 - + * The end of mobj_t changed from + * boolean invisible; + * mobj_t* lastenemy; + * mobj_t* above_monster; + * mobj_t* below_monster; + * void* touching_sectorlist; + * to + * mobj_t* lastenemy; + * void* touching_sectorlist; + * fixed_t PrevX, PrevY, PrevZ, padding; + * at prboom 2.4.4. There is code here to preserve the savegame format. + * + * touching_sectorlist is reconstructed anyway, so we now leave off the + * last 2 words of mobj_t, write 5 words of 0 and then write lastenemy + * into the second of these. + */ + memcpy (mobj, th, sizeof(*mobj) - 2*sizeof(void*)); + save_p += sizeof(*mobj) - 2*sizeof(void*) - 4*sizeof(fixed_t); + memset (save_p, 0, 5*sizeof(void*)); + mobj->state = (state_t *)(mobj->state - states); + + // killough 2/14/98: convert pointers into indices. + // Fixes many savegame problems, by properly saving + // target and tracer fields. Note: we store NULL if + // the thinker pointed to by these fields is not a + // mobj thinker. + + if (mobj->target) + mobj->target = mobj->target->thinker.function == + P_MobjThinker ? + (mobj_t *) mobj->target->thinker.prev : NULL; + + if (mobj->tracer) + mobj->tracer = mobj->tracer->thinker.function == + P_MobjThinker ? + (mobj_t *) mobj->tracer->thinker.prev : NULL; + + // killough 2/14/98: new field: save last known enemy. Prevents + // monsters from going to sleep after killing monsters and not + // seeing player anymore. + + if (((mobj_t*)th)->lastenemy && ((mobj_t*)th)->lastenemy->thinker.function == P_MobjThinker) { + memcpy (save_p + sizeof(void*), &(((mobj_t*)th)->lastenemy->thinker.prev), sizeof(void*)); + } + + // killough 2/14/98: end changes + + save_p += 5*sizeof(void*); + + if (mobj->player) + mobj->player = (player_t *)((mobj->player-players) + 1); + } + + // add a terminating marker + *save_p++ = tc_end; + + // killough 9/14/98: save soundtargets + { + int i; + CheckSaveGame(numsectors * sizeof(mobj_t *)); // killough 9/14/98 + for (i = 0; i < numsectors; i++) + { + mobj_t *target = sectors[i].soundtarget; + // Fix crash on reload when a soundtarget points to a removed corpse + // (prboom bug #1590350) + if (target && target->thinker.function == P_MobjThinker) + target = (mobj_t *) target->thinker.prev; + else + target = NULL; + memcpy(save_p, &target, sizeof target); + save_p += sizeof target; + } + } +} + +/* + * killough 11/98 + * + * Same as P_SetTarget() in p_tick.c, except that the target is nullified + * first, so that no old target's reference count is decreased (when loading + * savegames, old targets are indices, not really pointers to targets). + */ + +static void P_SetNewTarget(mobj_t **mop, mobj_t *targ) +{ + *mop = NULL; + P_SetTarget(mop, targ); +} + +// +// P_UnArchiveThinkers +// +// 2/14/98 killough: substantially modified to fix savegame bugs +// + +// savegame file stores ints in the corresponding * field; this function +// safely casts them back to int. +static int P_GetMobj(mobj_t* mi, size_t s) +{ + size_t i = (size_t)mi; + if (i >= s) I_Error("Corrupt savegame"); + return i; +} + +void P_UnArchiveThinkers (void) +{ + thinker_t *th; + mobj_t **mobj_p; // killough 2/14/98: Translation table + size_t size; // killough 2/14/98: size of or index into table + + totallive = 0; + // killough 3/26/98: Load boss brain state + memcpy(&brain, save_p, sizeof brain); + save_p += sizeof brain; + + // remove all the current thinkers + for (th = thinkercap.next; th != &thinkercap; ) + { + thinker_t *next = th->next; + if (th->function == P_MobjThinker) + P_RemoveMobj ((mobj_t *) th); + else + Z_Free (th); + th = next; + } + P_InitThinkers (); + + // killough 2/14/98: count number of thinkers by skipping through them + { + byte *sp = save_p; // save pointer and skip header + for (size = 1; *save_p++ == tc_mobj; size++) // killough 2/14/98 + { // skip all entries, adding up count + PADSAVEP(); + /* cph 2006/07/30 - see comment below for change in layout of mobj_t */ + save_p += sizeof(mobj_t)+3*sizeof(void*)-4*sizeof(fixed_t); + } + + if (*--save_p != tc_end) + I_Error ("P_UnArchiveThinkers: Unknown tclass %i in savegame", *save_p); + + // first table entry special: 0 maps to NULL + *(mobj_p = malloc(size * sizeof *mobj_p)) = 0; // table of pointers + save_p = sp; // restore save pointer + } + + // read in saved thinkers + for (size = 1; *save_p++ == tc_mobj; size++) // killough 2/14/98 + { + mobj_t *mobj = Z_Malloc(sizeof(mobj_t), PU_LEVEL, NULL); + + // killough 2/14/98 -- insert pointers to thinkers into table, in order: + mobj_p[size] = mobj; + + PADSAVEP(); + /* cph 2006/07/30 - + * The end of mobj_t changed from + * boolean invisible; + * mobj_t* lastenemy; + * mobj_t* above_monster; + * mobj_t* below_monster; + * void* touching_sectorlist; + * to + * mobj_t* lastenemy; + * void* touching_sectorlist; + * fixed_t PrevX, PrevY, PrevZ; + * at prboom 2.4.4. There is code here to preserve the savegame format. + * + * touching_sectorlist is reconstructed anyway, so we now read in all + * but the last 5 words from the savegame (filling all but the last 2 + * fields of our current mobj_t. We then pull lastenemy from the 2nd of + * the 5 leftover words, and skip the others. + */ + memcpy (mobj, save_p, sizeof(mobj_t)-2*sizeof(void*)-4*sizeof(fixed_t)); + save_p += sizeof(mobj_t)-sizeof(void*)-4*sizeof(fixed_t); + memcpy (&(mobj->lastenemy), save_p, sizeof(void*)); + save_p += 4*sizeof(void*); + mobj->state = states + (int) mobj->state; + + if (mobj->player) + (mobj->player = &players[(int) mobj->player - 1]) -> mo = mobj; + + P_SetThingPosition (mobj); + mobj->info = &mobjinfo[mobj->type]; + + // killough 2/28/98: + // Fix for falling down into a wall after savegame loaded: + // mobj->floorz = mobj->subsector->sector->floorheight; + // mobj->ceilingz = mobj->subsector->sector->ceilingheight; + + mobj->thinker.function = P_MobjThinker; + P_AddThinker (&mobj->thinker); + + if (!((mobj->flags ^ MF_COUNTKILL) & (MF_FRIEND | MF_COUNTKILL | MF_CORPSE))) + totallive++; + } + + // killough 2/14/98: adjust target and tracer fields, plus + // lastenemy field, to correctly point to mobj thinkers. + // NULL entries automatically handled by first table entry. + // + // killough 11/98: use P_SetNewTarget() to set fields + + for (th = thinkercap.next ; th != &thinkercap ; th=th->next) + { + P_SetNewTarget(&((mobj_t *) th)->target, + mobj_p[P_GetMobj(((mobj_t *)th)->target,size)]); + + P_SetNewTarget(&((mobj_t *) th)->tracer, + mobj_p[P_GetMobj(((mobj_t *)th)->tracer,size)]); + + P_SetNewTarget(&((mobj_t *) th)->lastenemy, + mobj_p[P_GetMobj(((mobj_t *)th)->lastenemy,size)]); + } + + { // killough 9/14/98: restore soundtargets + int i; + for (i = 0; i < numsectors; i++) + { + mobj_t *target; + memcpy(&target, save_p, sizeof target); + save_p += sizeof target; + // Must verify soundtarget. See P_ArchiveThinkers. + P_SetNewTarget(§ors[i].soundtarget, mobj_p[P_GetMobj(target,size)]); + } + } + + free(mobj_p); // free translation table + + // killough 3/26/98: Spawn icon landings: + if (gamemode == commercial) + P_SpawnBrainTargets(); +} + +// +// P_ArchiveSpecials +// +enum { + tc_ceiling, + tc_door, + tc_floor, + tc_plat, + tc_flash, + tc_strobe, + tc_glow, + tc_elevator, //jff 2/22/98 new elevator type thinker + tc_scroll, // killough 3/7/98: new scroll effect thinker + tc_pusher, // phares 3/22/98: new push/pull effect thinker + tc_flicker, // killough 10/4/98 + tc_endspecials +} specials_e; + +// +// Things to handle: +// +// T_MoveCeiling, (ceiling_t: sector_t * swizzle), - active list +// T_VerticalDoor, (vldoor_t: sector_t * swizzle), +// T_MoveFloor, (floormove_t: sector_t * swizzle), +// T_LightFlash, (lightflash_t: sector_t * swizzle), +// T_StrobeFlash, (strobe_t: sector_t *), +// T_Glow, (glow_t: sector_t *), +// T_PlatRaise, (plat_t: sector_t *), - active list +// T_MoveElevator, (plat_t: sector_t *), - active list // jff 2/22/98 +// T_Scroll // killough 3/7/98 +// T_Pusher // phares 3/22/98 +// T_FireFlicker // killough 10/4/98 +// + +void P_ArchiveSpecials (void) +{ + thinker_t *th; + size_t size = 0; // killough + + // save off the current thinkers (memory size calculation -- killough) + + for (th = thinkercap.next ; th != &thinkercap ; th=th->next) + if (!th->function) + { + platlist_t *pl; + ceilinglist_t *cl; //jff 2/22/98 need this for ceilings too now + for (pl=activeplats; pl; pl=pl->next) + if (pl->plat == (plat_t *) th) // killough 2/14/98 + { + size += 4+sizeof(plat_t); + goto end; + } + for (cl=activeceilings; cl; cl=cl->next) // search for activeceiling + if (cl->ceiling == (ceiling_t *) th) //jff 2/22/98 + { + size += 4+sizeof(ceiling_t); + goto end; + } + end:; + } + else + size += + th->function==T_MoveCeiling ? 4+sizeof(ceiling_t) : + th->function==T_VerticalDoor ? 4+sizeof(vldoor_t) : + th->function==T_MoveFloor ? 4+sizeof(floormove_t): + th->function==T_PlatRaise ? 4+sizeof(plat_t) : + th->function==T_LightFlash ? 4+sizeof(lightflash_t): + th->function==T_StrobeFlash ? 4+sizeof(strobe_t) : + th->function==T_Glow ? 4+sizeof(glow_t) : + th->function==T_MoveElevator ? 4+sizeof(elevator_t): + th->function==T_Scroll ? 4+sizeof(scroll_t) : + th->function==T_Pusher ? 4+sizeof(pusher_t) : + th->function==T_FireFlicker? 4+sizeof(fireflicker_t) : + 0; + + CheckSaveGame(size + 1); // killough; cph: +1 for the tc_endspecials + + // save off the current thinkers + for (th=thinkercap.next; th!=&thinkercap; th=th->next) + { + if (!th->function) + { + platlist_t *pl; + ceilinglist_t *cl; //jff 2/22/98 add iter variable for ceilings + + // killough 2/8/98: fix plat original height bug. + // Since acv==NULL, this could be a plat in stasis. + // so check the active plats list, and save this + // plat (jff: or ceiling) even if it is in stasis. + + for (pl=activeplats; pl; pl=pl->next) + if (pl->plat == (plat_t *) th) // killough 2/14/98 + goto plat; + + for (cl=activeceilings; cl; cl=cl->next) + if (cl->ceiling == (ceiling_t *) th) //jff 2/22/98 + goto ceiling; + + continue; + } + + if (th->function == T_MoveCeiling) + { + ceiling_t *ceiling; + ceiling: // killough 2/14/98 + *save_p++ = tc_ceiling; + PADSAVEP(); + ceiling = (ceiling_t *)save_p; + memcpy (ceiling, th, sizeof(*ceiling)); + save_p += sizeof(*ceiling); + ceiling->sector = (sector_t *)(ceiling->sector - sectors); + continue; + } + + if (th->function == T_VerticalDoor) + { + vldoor_t *door; + *save_p++ = tc_door; + PADSAVEP(); + door = (vldoor_t *) save_p; + memcpy (door, th, sizeof *door); + save_p += sizeof(*door); + door->sector = (sector_t *)(door->sector - sectors); + //jff 1/31/98 archive line remembered by door as well + door->line = (line_t *) (door->line ? door->line-lines : -1); + continue; + } + + if (th->function == T_MoveFloor) + { + floormove_t *floor; + *save_p++ = tc_floor; + PADSAVEP(); + floor = (floormove_t *)save_p; + memcpy (floor, th, sizeof(*floor)); + save_p += sizeof(*floor); + floor->sector = (sector_t *)(floor->sector - sectors); + continue; + } + + if (th->function == T_PlatRaise) + { + plat_t *plat; + plat: // killough 2/14/98: added fix for original plat height above + *save_p++ = tc_plat; + PADSAVEP(); + plat = (plat_t *)save_p; + memcpy (plat, th, sizeof(*plat)); + save_p += sizeof(*plat); + plat->sector = (sector_t *)(plat->sector - sectors); + continue; + } + + if (th->function == T_LightFlash) + { + lightflash_t *flash; + *save_p++ = tc_flash; + PADSAVEP(); + flash = (lightflash_t *)save_p; + memcpy (flash, th, sizeof(*flash)); + save_p += sizeof(*flash); + flash->sector = (sector_t *)(flash->sector - sectors); + continue; + } + + if (th->function == T_StrobeFlash) + { + strobe_t *strobe; + *save_p++ = tc_strobe; + PADSAVEP(); + strobe = (strobe_t *)save_p; + memcpy (strobe, th, sizeof(*strobe)); + save_p += sizeof(*strobe); + strobe->sector = (sector_t *)(strobe->sector - sectors); + continue; + } + + if (th->function == T_Glow) + { + glow_t *glow; + *save_p++ = tc_glow; + PADSAVEP(); + glow = (glow_t *)save_p; + memcpy (glow, th, sizeof(*glow)); + save_p += sizeof(*glow); + glow->sector = (sector_t *)(glow->sector - sectors); + continue; + } + + // killough 10/4/98: save flickers + if (th->function == T_FireFlicker) + { + fireflicker_t *flicker; + *save_p++ = tc_flicker; + PADSAVEP(); + flicker = (fireflicker_t *)save_p; + memcpy (flicker, th, sizeof(*flicker)); + save_p += sizeof(*flicker); + flicker->sector = (sector_t *)(flicker->sector - sectors); + continue; + } + + //jff 2/22/98 new case for elevators + if (th->function == T_MoveElevator) + { + elevator_t *elevator; //jff 2/22/98 + *save_p++ = tc_elevator; + PADSAVEP(); + elevator = (elevator_t *)save_p; + memcpy (elevator, th, sizeof(*elevator)); + save_p += sizeof(*elevator); + elevator->sector = (sector_t *)(elevator->sector - sectors); + continue; + } + + // killough 3/7/98: Scroll effect thinkers + if (th->function == T_Scroll) + { + *save_p++ = tc_scroll; + memcpy (save_p, th, sizeof(scroll_t)); + save_p += sizeof(scroll_t); + continue; + } + + // phares 3/22/98: Push/Pull effect thinkers + + if (th->function == T_Pusher) + { + *save_p++ = tc_pusher; + memcpy (save_p, th, sizeof(pusher_t)); + save_p += sizeof(pusher_t); + continue; + } + } + + // add a terminating marker + *save_p++ = tc_endspecials; +} + + +// +// P_UnArchiveSpecials +// +void P_UnArchiveSpecials (void) +{ + byte tclass; + + // read in saved thinkers + while ((tclass = *save_p++) != tc_endspecials) // killough 2/14/98 + switch (tclass) + { + case tc_ceiling: + PADSAVEP(); + { + ceiling_t *ceiling = Z_Malloc (sizeof(*ceiling), PU_LEVEL, NULL); + memcpy (ceiling, save_p, sizeof(*ceiling)); + save_p += sizeof(*ceiling); + ceiling->sector = §ors[(int)ceiling->sector]; + ceiling->sector->ceilingdata = ceiling; //jff 2/22/98 + + if (ceiling->thinker.function) + ceiling->thinker.function = T_MoveCeiling; + + P_AddThinker (&ceiling->thinker); + P_AddActiveCeiling(ceiling); + break; + } + + case tc_door: + PADSAVEP(); + { + vldoor_t *door = Z_Malloc (sizeof(*door), PU_LEVEL, NULL); + memcpy (door, save_p, sizeof(*door)); + save_p += sizeof(*door); + door->sector = §ors[(int)door->sector]; + + //jff 1/31/98 unarchive line remembered by door as well + door->line = (int)door->line!=-1? &lines[(int)door->line] : NULL; + + door->sector->ceilingdata = door; //jff 2/22/98 + door->thinker.function = T_VerticalDoor; + P_AddThinker (&door->thinker); + break; + } + + case tc_floor: + PADSAVEP(); + { + floormove_t *floor = Z_Malloc (sizeof(*floor), PU_LEVEL, NULL); + memcpy (floor, save_p, sizeof(*floor)); + save_p += sizeof(*floor); + floor->sector = §ors[(int)floor->sector]; + floor->sector->floordata = floor; //jff 2/22/98 + floor->thinker.function = T_MoveFloor; + P_AddThinker (&floor->thinker); + break; + } + + case tc_plat: + PADSAVEP(); + { + plat_t *plat = Z_Malloc (sizeof(*plat), PU_LEVEL, NULL); + memcpy (plat, save_p, sizeof(*plat)); + save_p += sizeof(*plat); + plat->sector = §ors[(int)plat->sector]; + plat->sector->floordata = plat; //jff 2/22/98 + + if (plat->thinker.function) + plat->thinker.function = T_PlatRaise; + + P_AddThinker (&plat->thinker); + P_AddActivePlat(plat); + break; + } + + case tc_flash: + PADSAVEP(); + { + lightflash_t *flash = Z_Malloc (sizeof(*flash), PU_LEVEL, NULL); + memcpy (flash, save_p, sizeof(*flash)); + save_p += sizeof(*flash); + flash->sector = §ors[(int)flash->sector]; + flash->thinker.function = T_LightFlash; + P_AddThinker (&flash->thinker); + break; + } + + case tc_strobe: + PADSAVEP(); + { + strobe_t *strobe = Z_Malloc (sizeof(*strobe), PU_LEVEL, NULL); + memcpy (strobe, save_p, sizeof(*strobe)); + save_p += sizeof(*strobe); + strobe->sector = §ors[(int)strobe->sector]; + strobe->thinker.function = T_StrobeFlash; + P_AddThinker (&strobe->thinker); + break; + } + + case tc_glow: + PADSAVEP(); + { + glow_t *glow = Z_Malloc (sizeof(*glow), PU_LEVEL, NULL); + memcpy (glow, save_p, sizeof(*glow)); + save_p += sizeof(*glow); + glow->sector = §ors[(int)glow->sector]; + glow->thinker.function = T_Glow; + P_AddThinker (&glow->thinker); + break; + } + + case tc_flicker: // killough 10/4/98 + PADSAVEP(); + { + fireflicker_t *flicker = Z_Malloc (sizeof(*flicker), PU_LEVEL, NULL); + memcpy (flicker, save_p, sizeof(*flicker)); + save_p += sizeof(*flicker); + flicker->sector = §ors[(int)flicker->sector]; + flicker->thinker.function = T_FireFlicker; + P_AddThinker (&flicker->thinker); + break; + } + + //jff 2/22/98 new case for elevators + case tc_elevator: + PADSAVEP(); + { + elevator_t *elevator = Z_Malloc (sizeof(*elevator), PU_LEVEL, NULL); + memcpy (elevator, save_p, sizeof(*elevator)); + save_p += sizeof(*elevator); + elevator->sector = §ors[(int)elevator->sector]; + elevator->sector->floordata = elevator; //jff 2/22/98 + elevator->sector->ceilingdata = elevator; //jff 2/22/98 + elevator->thinker.function = T_MoveElevator; + P_AddThinker (&elevator->thinker); + break; + } + + case tc_scroll: // killough 3/7/98: scroll effect thinkers + { + scroll_t *scroll = Z_Malloc (sizeof(scroll_t), PU_LEVEL, NULL); + memcpy (scroll, save_p, sizeof(scroll_t)); + save_p += sizeof(scroll_t); + scroll->thinker.function = T_Scroll; + P_AddThinker(&scroll->thinker); + break; + } + + case tc_pusher: // phares 3/22/98: new Push/Pull effect thinkers + { + pusher_t *pusher = Z_Malloc (sizeof(pusher_t), PU_LEVEL, NULL); + memcpy (pusher, save_p, sizeof(pusher_t)); + save_p += sizeof(pusher_t); + pusher->thinker.function = T_Pusher; + pusher->source = P_GetPushThing(pusher->affectee); + P_AddThinker(&pusher->thinker); + break; + } + + default: + I_Error("P_UnarchiveSpecials: Unknown tclass %i in savegame", tclass); + } +} + +// killough 2/16/98: save/restore random number generator state information + +void P_ArchiveRNG(void) +{ + CheckSaveGame(sizeof rng); + memcpy(save_p, &rng, sizeof rng); + save_p += sizeof rng; +} + +void P_UnArchiveRNG(void) +{ + memcpy(&rng, save_p, sizeof rng); + save_p += sizeof rng; +} + +// killough 2/22/98: Save/restore automap state +// killough 2/22/98: Save/restore automap state +void P_ArchiveMap(void) +{ + int zero = 0, one = 1; + CheckSaveGame(2 * sizeof zero + sizeof markpointnum + + markpointnum * sizeof *markpoints + + sizeof automapmode + sizeof one); + + memcpy(save_p, &automapmode, sizeof automapmode); + save_p += sizeof automapmode; + memcpy(save_p, &one, sizeof one); // CPhipps - used to be viewactive, now + save_p += sizeof one; // that's worked out locally by D_Display + memcpy(save_p, &zero, sizeof zero); // CPhipps - used to be followplayer + save_p += sizeof zero; // that is now part of automapmode + memcpy(save_p, &zero, sizeof zero); // CPhipps - used to be automap_grid, ditto + save_p += sizeof zero; + memcpy(save_p, &markpointnum, sizeof markpointnum); + save_p += sizeof markpointnum; + + if (markpointnum) + { + memcpy(save_p, markpoints, sizeof *markpoints * markpointnum); + save_p += markpointnum * sizeof *markpoints; + } +} + +void P_UnArchiveMap(void) +{ + int unused; + memcpy(&automapmode, save_p, sizeof automapmode); + save_p += sizeof automapmode; + memcpy(&unused, save_p, sizeof unused); + save_p += sizeof unused; + memcpy(&unused, save_p, sizeof unused); + save_p += sizeof unused; + memcpy(&unused, save_p, sizeof unused); + save_p += sizeof unused; + + if (automapmode & am_active) + AM_Start(); + + memcpy(&markpointnum, save_p, sizeof markpointnum); + save_p += sizeof markpointnum; + + if (markpointnum) + { + while (markpointnum >= markpointnum_max) + markpoints = realloc(markpoints, sizeof *markpoints * + (markpointnum_max = markpointnum_max ? markpointnum_max*2 : 16)); + memcpy(markpoints, save_p, markpointnum * sizeof *markpoints); + save_p += markpointnum * sizeof *markpoints; + } +} + diff --git a/src/p_saveg.h b/src/p_saveg.h new file mode 100644 index 0000000..dd986cf --- /dev/null +++ b/src/p_saveg.h @@ -0,0 +1,66 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Savegame I/O, archiving, persistence. + * + *-----------------------------------------------------------------------------*/ + +#ifndef __P_SAVEG__ +#define __P_SAVEG__ + +#ifdef __GNUG__ +#pragma interface +#endif + +/* Persistent storage/archiving. + * These are the load / save game routines. */ +void P_ArchivePlayers(void); +void P_UnArchivePlayers(void); +void P_ArchiveWorld(void); +void P_UnArchiveWorld(void); +void P_ArchiveThinkers(void); +void P_UnArchiveThinkers(void); +void P_ArchiveSpecials(void); +void P_UnArchiveSpecials(void); +void P_ThinkerToIndex(void); /* phares 9/13/98: save soundtarget in savegame */ +void P_IndexToThinker(void); /* phares 9/13/98: save soundtarget in savegame */ + +/* 1/18/98 killough: add RNG info to savegame */ +void P_ArchiveRNG(void); +void P_UnArchiveRNG(void); + +/* 2/21/98 killough: add automap info to savegame */ +void P_ArchiveMap(void); +void P_UnArchiveMap(void); + +extern byte *save_p; +void CheckSaveGame(size_t,const char*, int); /* killough */ +#define CheckSaveGame(a) (CheckSaveGame)(a, __FILE__, __LINE__) + +#endif diff --git a/src/p_setup.c b/src/p_setup.c new file mode 100644 index 0000000..5247cb9 --- /dev/null +++ b/src/p_setup.c @@ -0,0 +1,1688 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Do all the WAD I/O, get map description, + * set up initial state and misc. LUTs. + * + *-----------------------------------------------------------------------------*/ + +#include + +#include "doomstat.h" +#include "m_bbox.h" +#include "m_argv.h" +#include "g_game.h" +#include "w_wad.h" +#include "r_main.h" +#include "r_things.h" +#include "p_maputl.h" +#include "p_map.h" +#include "p_setup.h" +#include "p_spec.h" +#include "p_tick.h" +#include "p_enemy.h" +#include "s_sound.h" +#include "lprintf.h" //jff 10/6/98 for debug outputs +#include "v_video.h" +#include "r_demo.h" +#include "r_fps.h" + +// +// MAP related Lookup tables. +// Store VERTEXES, LINEDEFS, SIDEDEFS, etc. +// + +int numvertexes; +vertex_t *vertexes; + +int numsegs; +seg_t *segs; + +int numsectors; +sector_t *sectors; + +int numsubsectors; +subsector_t *subsectors; + +int numnodes; +node_t *nodes; + +int numlines; +line_t *lines; + +int numsides; +side_t *sides; + + +//////////////////////////////////////////////////////////////////////////////////////////// +// figgi 08/21/00 -- constants and globals for glBsp support +#define gNd2 0x32644E67 // figgi -- suppport for new GL_VERT format v2.0 +#define gNd3 0x33644E67 +#define gNd4 0x34644E67 +#define gNd5 0x35644E67 +#define ZNOD 0x444F4E5A +#define ZGLN 0x4E4C475A +#define GL_VERT_OFFSET 4 + +int firstglvertex = 0; +int nodesVersion = 0; +boolean forceOldBsp = false; + +// figgi 08/21/00 -- glSegs +typedef struct +{ + unsigned short v1; // start vertex (16 bit) + unsigned short v2; // end vertex (16 bit) + unsigned short linedef; // linedef, or -1 for minisegs + short side; // side on linedef: 0 for right, 1 for left + unsigned short partner; // corresponding partner seg, or -1 on one-sided walls +} glseg_t; + +// fixed 32 bit gl_vert format v2.0+ (glBsp 1.91) +typedef struct +{ + fixed_t x,y; +} mapglvertex_t; + +enum +{ + ML_GL_LABEL=0, // A separator name, GL_ExMx or GL_MAPxx + ML_GL_VERTS, // Extra Vertices + ML_GL_SEGS, // Segs, from linedefs & minisegs + ML_GL_SSECT, // SubSectors, list of segs + ML_GL_NODES // GL BSP nodes +}; +//////////////////////////////////////////////////////////////////////////////////////////// + + +// BLOCKMAP +// Created from axis aligned bounding box +// of the map, a rectangular array of +// blocks of size ... +// Used to speed up collision detection +// by spatial subdivision in 2D. +// +// Blockmap size. + +int bmapwidth, bmapheight; // size in mapblocks + +// killough 3/1/98: remove blockmap limit internally: +long *blockmap; // was short -- killough + +// offsets in blockmap are from here +long *blockmaplump; // was short -- killough + +fixed_t bmaporgx, bmaporgy; // origin of block map + +mobj_t **blocklinks; // for thing chains + +// +// REJECT +// For fast sight rejection. +// Speeds up enemy AI by skipping detailed +// LineOf Sight calculation. +// Without the special effect, this could +// be used as a PVS lookup as well. +// + +static int rejectlump = -1;// cph - store reject lump num if cached +const byte *rejectmatrix; // cph - const* + +// Maintain single and multi player starting spots. + +// 1/11/98 killough: Remove limit on deathmatch starts +mapthing_t *deathmatchstarts; // killough +size_t num_deathmatchstarts; // killough + +mapthing_t *deathmatch_p; +mapthing_t playerstarts[MAXPLAYERS]; + +// +// P_CheckForZDoomNodes +// + +static boolean P_CheckForZDoomNodes(int lumpnum, int gl_lumpnum) +{ + const void *data; + + data = W_CacheLumpNum(lumpnum + ML_NODES); + if (*(const int *)data == ZNOD) + I_Error("P_CheckForZDoomNodes: ZDoom nodes not supported yet"); + + data = W_CacheLumpNum(lumpnum + ML_SSECTORS); + if (*(const int *)data == ZGLN) + I_Error("P_CheckForZDoomNodes: ZDoom GL nodes not supported yet"); + + return false; +} + +// +// P_GetNodesVersion +// + +static void P_GetNodesVersion(int lumpnum, int gl_lumpnum) +{ + const void *data; + + data = W_CacheLumpNum(gl_lumpnum+ML_GL_VERTS); + if ( (gl_lumpnum > lumpnum) && (forceOldBsp == false) && (compatibility_level >= prboom_2_compatibility) ) { + if (*(const int *)data == gNd2) { + data = W_CacheLumpNum(gl_lumpnum+ML_GL_SEGS); + if (*(const int *)data == gNd3) { + nodesVersion = gNd3; + lprintf(LO_DEBUG, "P_GetNodesVersion: found version 3 nodes\n"); + I_Error("P_GetNodesVersion: version 3 nodes not supported\n"); + } else { + nodesVersion = gNd2; + lprintf(LO_DEBUG, "P_GetNodesVersion: found version 2 nodes\n"); + } + } + if (*(const int *)data == gNd4) { + nodesVersion = gNd4; + lprintf(LO_DEBUG, "P_GetNodesVersion: found version 4 nodes\n"); + I_Error("P_GetNodesVersion: version 4 nodes not supported\n"); + } + if (*(const int *)data == gNd5) { + nodesVersion = gNd5; + lprintf(LO_DEBUG, "P_GetNodesVersion: found version 5 nodes\n"); + I_Error("P_GetNodesVersion: version 5 nodes not supported\n"); + } + } else { + nodesVersion = 0; + lprintf(LO_DEBUG,"P_GetNodesVersion: using normal BSP nodes\n"); + if (P_CheckForZDoomNodes(lumpnum, gl_lumpnum)) + I_Error("P_GetNodesVersion: ZDoom nodes not supported yet"); + } +} + +// +// P_LoadVertexes +// +// killough 5/3/98: reformatted, cleaned up +// +static void P_LoadVertexes (int lump) +{ + const mapvertex_t *data; // cph - const + int i; + + // Determine number of lumps: + // total lump length / vertex record length. + numvertexes = W_LumpLength(lump) / sizeof(mapvertex_t); + + // Allocate zone memory for buffer. + vertexes = Z_Malloc(numvertexes*sizeof(vertex_t),PU_LEVEL,0); + + // Load data into cache. + // cph 2006/07/29 - cast to mapvertex_t here, making the loop below much neater + data = (const mapvertex_t *)W_CacheLumpNum(lump); + + // Copy and convert vertex coordinates, + // internal representation as fixed. + for (i=0; i= 0) // check for glVertices + { + gldata = W_CacheLumpNum(gllump); + + if (nodesVersion == gNd2) // 32 bit GL_VERT format (16.16 fixed) + { + const mapglvertex_t* mgl; + + numvertexes += (W_LumpLength(gllump) - GL_VERT_OFFSET)/sizeof(mapglvertex_t); + vertexes = Z_Malloc (numvertexes*sizeof(vertex_t),PU_LEVEL,0); + mgl = (const mapglvertex_t *) (gldata + GL_VERT_OFFSET); + + for (i = firstglvertex; i < numvertexes; i++) + { + vertexes[i].x = mgl->x; + vertexes[i].y = mgl->y; + mgl++; + } + } + else + { + numvertexes += W_LumpLength(gllump)/sizeof(mapvertex_t); + vertexes = Z_Malloc (numvertexes*sizeof(vertex_t),PU_LEVEL,0); + ml = (const mapvertex_t *)gldata; + + for (i = firstglvertex; i < numvertexes; i++) + { + vertexes[i].x = SHORT(ml->x)<y)<x)<y)<x - v2->x) / (float)FRACUNIT; + b = (float)(v1->y - v2->y) / (float)FRACUNIT; + r = (int)(sqrt(a*a+b*b) * (float)FRACUNIT); + return r; +} + + + +// +// P_LoadSegs +// +// killough 5/3/98: reformatted, cleaned up + +static void P_LoadSegs (int lump) +{ + int i; + const mapseg_t *data; // cph - const + + numsegs = W_LumpLength(lump) / sizeof(mapseg_t); + segs = Z_Calloc(numsegs,sizeof(seg_t),PU_LEVEL,0); + data = (const mapseg_t *)W_CacheLumpNum(lump); // cph - wad lump handling updated + + if ((!data) || (!numsegs)) + I_Error("P_LoadSegs: no segs in level"); + + for (i=0; iiSegID = i; // proff 11/05/2000: needed for OpenGL + + v1 = (unsigned short)SHORT(ml->v1); + v2 = (unsigned short)SHORT(ml->v2); + li->v1 = &vertexes[v1]; + li->v2 = &vertexes[v2]; + + li->miniseg = false; // figgi -- there are no minisegs in classic BSP nodes + li->length = GetDistance(li->v2->x - li->v1->x, li->v2->y - li->v1->y); + li->angle = (SHORT(ml->angle))<<16; + li->offset =(SHORT(ml->offset))<<16; + linedef = (unsigned short)SHORT(ml->linedef); + ldef = &lines[linedef]; + li->linedef = ldef; + side = SHORT(ml->side); + li->sidedef = &sides[ldef->sidenum[side]]; + + /* cph 2006/09/30 - our frontsector can be the second side of the + * linedef, so must check for NO_INDEX in case we are incorrectly + * referencing the back of a 1S line */ + if (ldef->sidenum[side] != NO_INDEX) + li->frontsector = sides[ldef->sidenum[side]].sector; + else { + li->frontsector = 0; + lprintf(LO_WARN, "P_LoadSegs: front of seg %i has no sidedef\n", i); + } + + if (ldef->flags & ML_TWOSIDED && ldef->sidenum[side^1]!=NO_INDEX) + li->backsector = sides[ldef->sidenum[side^1]].sector; + else + li->backsector = 0; + } + + W_UnlockLumpNum(lump); // cph - release the data +} + + + +/******************************************* + * Name : P_LoadGLSegs * + * created : 08/13/00 * + * modified : 09/18/00, adapted for PrBoom * + * author : figgi * + * what : support for gl nodes * + *******************************************/ +static void P_LoadGLSegs(int lump) +{ + int i; + const glseg_t *ml; + line_t *ldef; + + numsegs = W_LumpLength(lump) / sizeof(glseg_t); + segs = Z_Malloc(numsegs * sizeof(seg_t), PU_LEVEL, 0); + memset(segs, 0, numsegs * sizeof(seg_t)); + ml = (const glseg_t*)W_CacheLumpNum(lump); + + if ((!ml) || (!numsegs)) + I_Error("P_LoadGLSegs: no glsegs in level"); + + for(i = 0; i < numsegs; i++) + { // check for gl-vertices + segs[i].v1 = &vertexes[checkGLVertex(SHORT(ml->v1))]; + segs[i].v2 = &vertexes[checkGLVertex(SHORT(ml->v2))]; + segs[i].iSegID = i; + + if(ml->linedef != (unsigned short)-1) // skip minisegs + { + ldef = &lines[ml->linedef]; + segs[i].linedef = ldef; + segs[i].miniseg = false; + segs[i].angle = R_PointToAngle2(segs[i].v1->x,segs[i].v1->y,segs[i].v2->x,segs[i].v2->y); + + segs[i].sidedef = &sides[ldef->sidenum[ml->side]]; + segs[i].length = GetDistance(segs[i].v2->x - segs[i].v1->x, segs[i].v2->y - segs[i].v1->y); + segs[i].frontsector = sides[ldef->sidenum[ml->side]].sector; + if (ldef->flags & ML_TWOSIDED) + segs[i].backsector = sides[ldef->sidenum[ml->side^1]].sector; + else + segs[i].backsector = 0; + + if (ml->side) + segs[i].offset = GetOffset(segs[i].v1, ldef->v2); + else + segs[i].offset = GetOffset(segs[i].v1, ldef->v1); + } + else + { + segs[i].miniseg = true; + segs[i].angle = 0; + segs[i].offset = 0; + segs[i].length = 0; + segs[i].linedef = NULL; + segs[i].sidedef = NULL; + segs[i].frontsector = NULL; + segs[i].backsector = NULL; + } + ml++; + } + W_UnlockLumpNum(lump); +} + +// +// P_LoadSubsectors +// +// killough 5/3/98: reformatted, cleaned up + +static void P_LoadSubsectors (int lump) +{ + /* cph 2006/07/29 - make data a const mapsubsector_t *, so the loop below is simpler & gives no constness warnings */ + const mapsubsector_t *data; + int i; + + numsubsectors = W_LumpLength (lump) / sizeof(mapsubsector_t); + subsectors = Z_Calloc(numsubsectors,sizeof(subsector_t),PU_LEVEL,0); + data = (const mapsubsector_t *)W_CacheLumpNum(lump); + + if ((!data) || (!numsubsectors)) + I_Error("P_LoadSubsectors: no subsectors in level"); + + for (i=0; iiSectorID=i; // proff 04/05/2000: needed for OpenGL + ss->floorheight = SHORT(ms->floorheight)<ceilingheight = SHORT(ms->ceilingheight)<floorpic = R_FlatNumForName(ms->floorpic); + ss->ceilingpic = R_FlatNumForName(ms->ceilingpic); + ss->lightlevel = SHORT(ms->lightlevel); + ss->special = SHORT(ms->special); + ss->oldspecial = SHORT(ms->special); + ss->tag = SHORT(ms->tag); + ss->thinglist = NULL; + ss->touching_thinglist = NULL; // phares 3/14/98 + + ss->nextsec = -1; //jff 2/26/98 add fields to support locking out + ss->prevsec = -1; // stair retriggering until build completes + + // killough 3/7/98: + ss->floor_xoffs = 0; + ss->floor_yoffs = 0; // floor and ceiling flats offsets + ss->ceiling_xoffs = 0; + ss->ceiling_yoffs = 0; + ss->heightsec = -1; // sector used to get floor and ceiling height + ss->floorlightsec = -1; // sector used to get floor lighting + // killough 3/7/98: end changes + + // killough 4/11/98 sector used to get ceiling lighting: + ss->ceilinglightsec = -1; + + // killough 4/4/98: colormaps: + ss->bottommap = ss->midmap = ss->topmap = 0; + + // killough 10/98: sky textures coming from sidedefs: + ss->sky = 0; + } + + W_UnlockLumpNum(lump); // cph - release the data +} + + +// +// P_LoadNodes +// +// killough 5/3/98: reformatted, cleaned up + +static void P_LoadNodes (int lump) +{ + const byte *data; // cph - const* + int i; + + numnodes = W_LumpLength (lump) / sizeof(mapnode_t); + nodes = Z_Malloc (numnodes*sizeof(node_t),PU_LEVEL,0); + data = W_CacheLumpNum (lump); // cph - wad lump handling updated + + if ((!data) || (!numnodes)) + { + // allow trivial maps + if (numsubsectors == 1) + lprintf(LO_INFO, + "P_LoadNodes: trivial map (no nodes, one subsector)\n"); + else + I_Error("P_LoadNodes: no nodes in level"); + } + + for (i=0; ix = SHORT(mn->x)<y = SHORT(mn->y)<dx = SHORT(mn->dx)<dy = SHORT(mn->dy)<children[j] = SHORT(mn->children[j]); + for (k=0 ; k<4 ; k++) + no->bbox[j][k] = SHORT(mn->bbox[j][k])<flags = (unsigned short)SHORT(mld->flags); + ld->special = SHORT(mld->special); + ld->tag = SHORT(mld->tag); + v1 = ld->v1 = &vertexes[(unsigned short)SHORT(mld->v1)]; + v2 = ld->v2 = &vertexes[(unsigned short)SHORT(mld->v2)]; + ld->dx = v2->x - v1->x; + ld->dy = v2->y - v1->y; + + ld->tranlump = -1; // killough 4/11/98: no translucency by default + + ld->slopetype = !ld->dx ? ST_VERTICAL : !ld->dy ? ST_HORIZONTAL : + FixedDiv(ld->dy, ld->dx) > 0 ? ST_POSITIVE : ST_NEGATIVE; + + if (v1->x < v2->x) + { + ld->bbox[BOXLEFT] = v1->x; + ld->bbox[BOXRIGHT] = v2->x; + } + else + { + ld->bbox[BOXLEFT] = v2->x; + ld->bbox[BOXRIGHT] = v1->x; + } + if (v1->y < v2->y) + { + ld->bbox[BOXBOTTOM] = v1->y; + ld->bbox[BOXTOP] = v2->y; + } + else + { + ld->bbox[BOXBOTTOM] = v2->y; + ld->bbox[BOXTOP] = v1->y; + } + + /* calculate sound origin of line to be its midpoint */ + //e6y: fix sound origin for large levels + // no need for comp_sound test, these are only used when comp_sound = 0 + ld->soundorg.x = ld->bbox[BOXLEFT] / 2 + ld->bbox[BOXRIGHT] / 2; + ld->soundorg.y = ld->bbox[BOXTOP] / 2 + ld->bbox[BOXBOTTOM] / 2; + + ld->iLineID=i; // proff 04/05/2000: needed for OpenGL + ld->sidenum[0] = SHORT(mld->sidenum[0]); + ld->sidenum[1] = SHORT(mld->sidenum[1]); + + { + /* cph 2006/09/30 - fix sidedef errors right away. + * cph 2002/07/20 - these errors are fatal if not fixed, so apply them + * in compatibility mode - a desync is better than a crash! */ + int j; + + for (j=0; j < 2; j++) + { + if (ld->sidenum[j] != NO_INDEX && ld->sidenum[j] >= numsides) { + ld->sidenum[j] = NO_INDEX; + lprintf(LO_WARN, "P_LoadLineDefs: linedef %d has out-of-range sidedef number\n",numlines-i-1); + } + } + + // killough 11/98: fix common wad errors (missing sidedefs): + + if (ld->sidenum[0] == NO_INDEX) { + ld->sidenum[0] = 0; // Substitute dummy sidedef for missing right side + // cph - print a warning about the bug + lprintf(LO_WARN, "P_LoadLineDefs: linedef %d missing first sidedef\n",numlines-i-1); + } + + if ((ld->sidenum[1] == NO_INDEX) && (ld->flags & ML_TWOSIDED)) { + ld->flags &= ~ML_TWOSIDED; // Clear 2s flag for missing left side + // cph - print a warning about the bug + lprintf(LO_WARN, "P_LoadLineDefs: linedef %d has two-sided flag set, but no second sidedef\n",numlines-i-1); + } + } + + // killough 4/4/98: support special sidedef interpretation below + if (ld->sidenum[0] != NO_INDEX && ld->special) + sides[*ld->sidenum].special = ld->special; + } + + W_UnlockLumpNum(lump); // cph - release the lump +} + +// killough 4/4/98: delay using sidedefs until they are loaded +// killough 5/3/98: reformatted, cleaned up + +static void P_LoadLineDefs2(int lump) +{ + int i = numlines; + register line_t *ld = lines; + for (;i--;ld++) + { + ld->frontsector = sides[ld->sidenum[0]].sector; //e6y: Can't be NO_INDEX here + ld->backsector = ld->sidenum[1]!=NO_INDEX ? sides[ld->sidenum[1]].sector : 0; + switch (ld->special) + { // killough 4/11/98: handle special types + int lump, j; + + case 260: // killough 4/11/98: translucent 2s textures + lump = sides[*ld->sidenum].special; // translucency from sidedef + if (!ld->tag) // if tag==0, + ld->tranlump = lump; // affect this linedef only + else + for (j=0;jtag) // affect all matching linedefs + lines[j].tranlump = lump; + break; + } + } +} + +// +// P_LoadSideDefs +// +// killough 4/4/98: split into two functions + +static void P_LoadSideDefs (int lump) +{ + numsides = W_LumpLength(lump) / sizeof(mapsidedef_t); + sides = Z_Calloc(numsides,sizeof(side_t),PU_LEVEL,0); +} + +// killough 4/4/98: delay using texture names until +// after linedefs are loaded, to allow overloading. +// killough 5/3/98: reformatted, cleaned up + +static void P_LoadSideDefs2(int lump) +{ + const byte *data = W_CacheLumpNum(lump); // cph - const*, wad lump handling updated + int i; + + for (i=0; itextureoffset = SHORT(msd->textureoffset)<rowoffset = SHORT(msd->rowoffset)<sector); + if (sector_num >= numsectors) { + lprintf(LO_WARN,"P_LoadSideDefs2: sidedef %i has out-of-range sector num %u\n", i, sector_num); + sector_num = 0; + } + sd->sector = sec = §ors[sector_num]; + } + + // killough 4/4/98: allow sidedef texture names to be overloaded + // killough 4/11/98: refined to allow colormaps to work as wall + // textures if invalid as colormaps but valid as textures. + switch (sd->special) + { + case 242: // variable colormap via 242 linedef + sd->bottomtexture = + (sec->bottommap = R_ColormapNumForName(msd->bottomtexture)) < 0 ? + sec->bottommap = 0, R_TextureNumForName(msd->bottomtexture): 0 ; + sd->midtexture = + (sec->midmap = R_ColormapNumForName(msd->midtexture)) < 0 ? + sec->midmap = 0, R_TextureNumForName(msd->midtexture) : 0 ; + sd->toptexture = + (sec->topmap = R_ColormapNumForName(msd->toptexture)) < 0 ? + sec->topmap = 0, R_TextureNumForName(msd->toptexture) : 0 ; + break; + + case 260: // killough 4/11/98: apply translucency to 2s normal texture + sd->midtexture = strncasecmp("TRANMAP", msd->midtexture, 8) ? + (sd->special = W_CheckNumForName(msd->midtexture)) < 0 || + W_LumpLength(sd->special) != 65536 ? + sd->special=0, R_TextureNumForName(msd->midtexture) : + (sd->special++, 0) : (sd->special=0); + sd->toptexture = R_TextureNumForName(msd->toptexture); + sd->bottomtexture = R_TextureNumForName(msd->bottomtexture); + break; + + default: // normal cases + sd->midtexture = R_SafeTextureNumForName(msd->midtexture, i); + sd->toptexture = R_SafeTextureNumForName(msd->toptexture, i); + sd->bottomtexture = R_SafeTextureNumForName(msd->bottomtexture, i); + break; + } + } + + W_UnlockLumpNum(lump); // cph - release the lump +} + +// +// jff 10/6/98 +// New code added to speed up calculation of internal blockmap +// Algorithm is order of nlines*(ncols+nrows) not nlines*ncols*nrows +// + +#define blkshift 7 /* places to shift rel position for cell num */ +#define blkmask ((1<0 + // jff 10/12/98 0 ok with + 1 in rows,cols + +typedef struct linelist_t // type used to list lines in each block +{ + long num; + struct linelist_t *next; +} linelist_t; + +// +// Subroutine to add a line number to a block list +// It simply returns if the line is already in the block +// + +static void AddBlockLine +( + linelist_t **lists, + int *count, + int *done, + int blockno, + long lineno +) +{ + linelist_t *l; + + if (done[blockno]) + return; + + l = malloc(sizeof(linelist_t)); + l->num = lineno; + l->next = lists[blockno]; + lists[blockno] = l; + count[blockno]++; + done[blockno] = 1; +} + +// +// Actually construct the blockmap lump from the level data +// +// This finds the intersection of each linedef with the column and +// row lines at the left and bottom of each blockmap cell. It then +// adds the line to all block lists touching the intersection. +// + +static void P_CreateBlockMap(void) +{ + int xorg,yorg; // blockmap origin (lower left) + int nrows,ncols; // blockmap dimensions + linelist_t **blocklists=NULL; // array of pointers to lists of lines + int *blockcount=NULL; // array of counters of line lists + int *blockdone=NULL; // array keeping track of blocks/line + int NBlocks; // number of cells = nrows*ncols + long linetotal=0; // total length of all blocklists + int i,j; + int map_minx=INT_MAX; // init for map limits search + int map_miny=INT_MAX; + int map_maxx=INT_MIN; + int map_maxy=INT_MIN; + + // scan for map limits, which the blockmap must enclose + + for (i=0;i map_maxx) + map_maxx = t; + if ((t=vertexes[i].y) < map_miny) + map_miny = t; + else if (t > map_maxy) + map_maxy = t; + } + map_minx >>= FRACBITS; // work in map coords, not fixed_t + map_maxx >>= FRACBITS; + map_miny >>= FRACBITS; + map_maxy >>= FRACBITS; + + // set up blockmap area to enclose level plus margin + + xorg = map_minx-blkmargin; + yorg = map_miny-blkmargin; + ncols = (map_maxx+blkmargin-xorg+1+blkmask)>>blkshift; //jff 10/12/98 + nrows = (map_maxy+blkmargin-yorg+1+blkmask)>>blkshift; //+1 needed for + NBlocks = ncols*nrows; //map exactly 1 cell + + // create the array of pointers on NBlocks to blocklists + // also create an array of linelist counts on NBlocks + // finally make an array in which we can mark blocks done per line + + // CPhipps - calloc's + blocklists = calloc(NBlocks,sizeof(linelist_t *)); + blockcount = calloc(NBlocks,sizeof(int)); + blockdone = malloc(NBlocks*sizeof(int)); + + // initialize each blocklist, and enter the trailing -1 in all blocklists + // note the linked list of lines grows backwards + + for (i=0;inum = -1; + blocklists[i]->next = NULL; + blockcount[i]++; + } + + // For each linedef in the wad, determine all blockmap blocks it touches, + // and add the linedef number to the blocklists for those blocks + + for (i=0;ix>>FRACBITS; // lines[i] map coords + int y1 = lines[i].v1->y>>FRACBITS; + int x2 = lines[i].v2->x>>FRACBITS; + int y2 = lines[i].v2->y>>FRACBITS; + int dx = x2-x1; + int dy = y2-y1; + int vert = !dx; // lines[i] slopetype + int horiz = !dy; + int spos = (dx^dy) > 0; + int sneg = (dx^dy) < 0; + int bx,by; // block cell coords + int minx = x1>x2? x2 : x1; // extremal lines[i] coords + int maxx = x1>x2? x1 : x2; + int miny = y1>y2? y2 : y1; + int maxy = y1>y2? y1 : y2; + + // no blocks done for this linedef yet + + memset(blockdone,0,NBlocks*sizeof(int)); + + // The line always belongs to the blocks containing its endpoints + + bx = (x1-xorg)>>blkshift; + by = (y1-yorg)>>blkshift; + AddBlockLine(blocklists,blockcount,blockdone,by*ncols+bx,i); + bx = (x2-xorg)>>blkshift; + by = (y2-yorg)>>blkshift; + AddBlockLine(blocklists,blockcount,blockdone,by*ncols+bx,i); + + + // For each column, see where the line along its left edge, which + // it contains, intersects the Linedef i. Add i to each corresponding + // blocklist. + + if (!vert) // don't interesect vertical lines with columns + { + for (j=0;j>blkshift; // block row number + int yp = (y-yorg)&blkmask; // y position within block + + if (yb<0 || yb>nrows-1) // outside blockmap, continue + continue; + + if (xmaxx) // line doesn't touch column + continue; + + // The cell that contains the intersection point is always added + + AddBlockLine(blocklists,blockcount,blockdone,ncols*yb+j,i); + + // if the intersection is at a corner it depends on the slope + // (and whether the line extends past the intersection) which + // blocks are hit + + if (yp==0) // intersection at a corner + { + if (sneg) // \ - blocks x,y-, x-,y + { + if (yb>0 && miny0 && minx0 && j>0 && minx0 && minx0 && minx>blkshift; // block column number + int xp = (x-xorg)&blkmask; // x position within block + + if (xb<0 || xb>ncols-1) // outside blockmap, continue + continue; + + if (ymaxy) // line doesn't touch row + continue; + + // The cell that contains the intersection point is always added + + AddBlockLine(blocklists,blockcount,blockdone,ncols*j+xb,i); + + // if the intersection is at a corner it depends on the slope + // (and whether the line extends past the intersection) which + // blocks are hit + + if (xp==0) // intersection at a corner + { + if (sneg) // \ - blocks x,y-, x-,y + { + if (j>0 && miny0 && minx0 && miny0 && j>0 && miny0 && minynext; + blockmaplump[offs++] = bl->num; + free(bl); + bl = tmp; + } + } + + // free all temporary storage + + free (blocklists); + free (blockcount); + free (blockdone); +} + +// jff 10/6/98 +// End new code added to speed up calculation of internal blockmap + +// +// P_LoadBlockMap +// +// killough 3/1/98: substantially modified to work +// towards removing blockmap limit (a wad limitation) +// +// killough 3/30/98: Rewritten to remove blockmap limit, +// though current algorithm is brute-force and unoptimal. +// + +static void P_LoadBlockMap (int lump) +{ + long count; + + if (M_CheckParm("-blockmap") || W_LumpLength(lump)<8 || (count = W_LumpLength(lump)/2) >= 0x10000) //e6y + P_CreateBlockMap(); + else + { + long i; + // cph - const*, wad lump handling updated + const short *wadblockmaplump = W_CacheLumpNum(lump); + blockmaplump = Z_Malloc(sizeof(*blockmaplump) * count, PU_LEVEL, 0); + + // killough 3/1/98: Expand wad blockmap into larger internal one, + // by treating all offsets except -1 as unsigned and zero-extending + // them. This potentially doubles the size of blockmaps allowed, + // because Doom originally considered the offsets as always signed. + + blockmaplump[0] = SHORT(wadblockmaplump[0]); + blockmaplump[1] = SHORT(wadblockmaplump[1]); + blockmaplump[2] = (long)(SHORT(wadblockmaplump[2])) & 0xffff; + blockmaplump[3] = (long)(SHORT(wadblockmaplump[3])) & 0xffff; + + for (i=4 ; i= required) + return; // nothing to do + + // allocate a new block and copy the reject table into it; zero the rest + // PU_LEVEL => will be freed on level exit + newreject = Z_Malloc(required, PU_LEVEL, NULL); + rejectmatrix = (const byte *)memmove(newreject, rejectmatrix, length); + memset(newreject + length, 0, required - length); + // unlock the original lump, it is no longer needed + W_UnlockLumpNum(rejectlump); + rejectlump = -1; + + if (demo_compatibility) + { + // merged in RejectOverrunAddInt(), and the 4 calls to it, here + unsigned int rejectpad[4] = { + 0, // size, will be filled in using totallines + 0, // part of the header of a doom.exe z_zone block + 50, // DOOM_CONST_PU_LEVEL + 0x1d4a11 // DOOM_CONST_ZONEID + }; + unsigned int i, pad = 0, *src = rejectpad; + byte *dest = newreject + length; + + rejectpad[0] = ((totallines*4+3)&~3)+24; // doom.exe zone header size + + // copy at most 16 bytes from rejectpad + // emulating a 32-bit, little-endian architecture (can't memmove) + for (i = 0; i < required - length && i < 16; i++) { // 16 hard-coded + if (!(i&3)) // get the next 4 bytes to copy when i=0,4,8,12 + pad = *src++; + *dest++ = pad & 0xff; // store lowest-significant byte + pad >>= 8; // rotate the next byte down + } + } + lprintf(LO_WARN, "P_LoadReject: REJECT too short (%u<%u) - padded\n", + length, required); +} + +// +// P_GroupLines +// Builds sector line lists and subsector sector numbers. +// Finds block bounding boxes for sectors. +// +// killough 5/3/98: reformatted, cleaned up +// cph 18/8/99: rewritten to avoid O(numlines * numsectors) section +// It makes things more complicated, but saves seconds on big levels +// figgi 09/18/00 -- adapted for gl-nodes + +// cph - convenient sub-function +static void P_AddLineToSector(line_t* li, sector_t* sector) +{ + fixed_t *bbox = (void*)sector->blockbox; + + sector->lines[sector->linecount++] = li; + M_AddToBox (bbox, li->v1->x, li->v1->y); + M_AddToBox (bbox, li->v2->x, li->v2->y); +} + +// modified to return totallines (needed by P_LoadReject) +static int P_GroupLines (void) +{ + register line_t *li; + register sector_t *sector; + int i,j, total = numlines; + + // figgi + for (i=0 ; isidedef) + { + subsectors[i].sector = seg->sidedef->sector; + break; + } + seg++; + } + if(subsectors[i].sector == NULL) + I_Error("P_GroupLines: Subsector a part of no sector!\n"); + } + + // count number of lines in each sector + for (i=0,li=lines; ifrontsector->linecount++; + if (li->backsector && li->backsector != li->frontsector) + { + li->backsector->linecount++; + total++; + } + } + + { // allocate line tables for each sector + line_t **linebuffer = Z_Malloc(total*sizeof(line_t *), PU_LEVEL, 0); + + // e6y: REJECT overrun emulation code + // moved to P_LoadReject + + for (i=0, sector = sectors; ilines = linebuffer; + linebuffer += sector->linecount; + sector->linecount = 0; + M_ClearBox(sector->blockbox); + } + } + + // Enter those lines + for (i=0,li=lines; ifrontsector); + if (li->backsector && li->backsector != li->frontsector) + P_AddLineToSector(li, li->backsector); + } + + for (i=0, sector = sectors; iblockbox; // cph - For convenience, so + // I can sue the old code unchanged + int block; + + // set the degenmobj_t to the middle of the bounding box + if (comp[comp_sound]) + { + sector->soundorg.x = (bbox[BOXRIGHT]+bbox[BOXLEFT])/2; + sector->soundorg.y = (bbox[BOXTOP]+bbox[BOXBOTTOM])/2; + } + else + { + //e6y: fix sound origin for large levels + sector->soundorg.x = bbox[BOXRIGHT]/2+bbox[BOXLEFT]/2; + sector->soundorg.y = bbox[BOXTOP]/2+bbox[BOXBOTTOM]/2; + } + + // adjust bounding box to map blocks + block = (bbox[BOXTOP]-bmaporgy+MAXRADIUS)>>MAPBLOCKSHIFT; + block = block >= bmapheight ? bmapheight-1 : block; + sector->blockbox[BOXTOP]=block; + + block = (bbox[BOXBOTTOM]-bmaporgy-MAXRADIUS)>>MAPBLOCKSHIFT; + block = block < 0 ? 0 : block; + sector->blockbox[BOXBOTTOM]=block; + + block = (bbox[BOXRIGHT]-bmaporgx+MAXRADIUS)>>MAPBLOCKSHIFT; + block = block >= bmapwidth ? bmapwidth-1 : block; + sector->blockbox[BOXRIGHT]=block; + + block = (bbox[BOXLEFT]-bmaporgx-MAXRADIUS)>>MAPBLOCKSHIFT; + block = block < 0 ? 0 : block; + sector->blockbox[BOXLEFT]=block; + } + + return total; // this value is needed by the reject overrun emulation code +} + +// +// killough 10/98 +// +// Remove slime trails. +// +// Slime trails are inherent to Doom's coordinate system -- i.e. there is +// nothing that a node builder can do to prevent slime trails ALL of the time, +// because it's a product of the integer coodinate system, and just because +// two lines pass through exact integer coordinates, doesn't necessarily mean +// that they will intersect at integer coordinates. Thus we must allow for +// fractional coordinates if we are to be able to split segs with node lines, +// as a node builder must do when creating a BSP tree. +// +// A wad file does not allow fractional coordinates, so node builders are out +// of luck except that they can try to limit the number of splits (they might +// also be able to detect the degree of roundoff error and try to avoid splits +// with a high degree of roundoff error). But we can use fractional coordinates +// here, inside the engine. It's like the difference between square inches and +// square miles, in terms of granularity. +// +// For each vertex of every seg, check to see whether it's also a vertex of +// the linedef associated with the seg (i.e, it's an endpoint). If it's not +// an endpoint, and it wasn't already moved, move the vertex towards the +// linedef by projecting it using the law of cosines. Formula: +// +// 2 2 2 2 +// dx x0 + dy x1 + dx dy (y0 - y1) dy y0 + dx y1 + dx dy (x0 - x1) +// {---------------------------------, ---------------------------------} +// 2 2 2 2 +// dx + dy dx + dy +// +// (x0,y0) is the vertex being moved, and (x1,y1)-(x1+dx,y1+dy) is the +// reference linedef. +// +// Segs corresponding to orthogonal linedefs (exactly vertical or horizontal +// linedefs), which comprise at least half of all linedefs in most wads, don't +// need to be considered, because they almost never contribute to slime trails +// (because then any roundoff error is parallel to the linedef, which doesn't +// cause slime). Skipping simple orthogonal lines lets the code finish quicker. +// +// Please note: This section of code is not interchangable with TeamTNT's +// code which attempts to fix the same problem. +// +// Firelines (TM) is a Rezistered Trademark of MBF Productions +// + +static void P_RemoveSlimeTrails(void) // killough 10/98 +{ + byte *hit = calloc(1, numvertexes); // Hitlist for vertices + int i; + for (i=0; idx && l->dy) // We can ignore orthogonal lines + { + vertex_t *v = segs[i].v1; + do + if (!hit[v - vertexes]) // If we haven't processed vertex + { + hit[v - vertexes] = 1; // Mark this vertex as processed + if (v != l->v1 && v != l->v2) // Exclude endpoints of linedefs + { // Project the vertex back onto the parent linedef + int_64_t dx2 = (l->dx >> FRACBITS) * (l->dx >> FRACBITS); + int_64_t dy2 = (l->dy >> FRACBITS) * (l->dy >> FRACBITS); + int_64_t dxy = (l->dx >> FRACBITS) * (l->dy >> FRACBITS); + int_64_t s = dx2 + dy2; + int x0 = v->x, y0 = v->y, x1 = l->v1->x, y1 = l->v1->y; + v->x = (int)((dx2 * x0 + dy2 * x1 + dxy * (y0 - y1)) / s); + v->y = (int)((dy2 * y0 + dx2 * y1 + dxy * (x0 - x1)) / s); + } + } // Obsfucated C contest entry: :) + while ((v != segs[i].v2) && (v = segs[i].v2)); + } + } + free(hit); +} + +// +// P_SetupLevel +// +// killough 5/3/98: reformatted, cleaned up + +void P_SetupLevel(int episode, int map, int playermask, skill_t skill) +{ + int i; + char lumpname[9]; + int lumpnum; + + char gl_lumpname[9]; + int gl_lumpnum; + + R_StopAllInterpolations(); + + totallive = totalkills = totalitems = totalsecret = wminfo.maxfrags = 0; + wminfo.partime = 180; + + for (i=0; i 0) + P_LoadVertexes2 (lumpnum+ML_VERTEXES,gl_lumpnum+ML_GL_VERTS); + else + P_LoadVertexes (lumpnum+ML_VERTEXES); + P_LoadSectors (lumpnum+ML_SECTORS); + P_LoadSideDefs (lumpnum+ML_SIDEDEFS); + P_LoadLineDefs (lumpnum+ML_LINEDEFS); + P_LoadSideDefs2 (lumpnum+ML_SIDEDEFS); + P_LoadLineDefs2 (lumpnum+ML_LINEDEFS); + P_LoadBlockMap (lumpnum+ML_BLOCKMAP); + + if (nodesVersion > 0) + { + P_LoadSubsectors(gl_lumpnum + ML_GL_SSECT); + P_LoadNodes(gl_lumpnum + ML_GL_NODES); + P_LoadGLSegs(gl_lumpnum + ML_GL_SEGS); + } + else + { + P_LoadSubsectors(lumpnum + ML_SSECTORS); + P_LoadNodes(lumpnum + ML_NODES); + P_LoadSegs(lumpnum + ML_SEGS); + } + +#else + + P_LoadVertexes (lumpnum+ML_VERTEXES); + P_LoadSectors (lumpnum+ML_SECTORS); + P_LoadSideDefs (lumpnum+ML_SIDEDEFS); // killough 4/4/98 + P_LoadLineDefs (lumpnum+ML_LINEDEFS); // | + P_LoadSideDefs2 (lumpnum+ML_SIDEDEFS); // | + P_LoadLineDefs2 (lumpnum+ML_LINEDEFS); // killough 4/4/98 + P_LoadBlockMap (lumpnum+ML_BLOCKMAP); // killough 3/1/98 + P_LoadSubsectors(lumpnum+ML_SSECTORS); + P_LoadNodes (lumpnum+ML_NODES); + P_LoadSegs (lumpnum+ML_SEGS); + +#endif + + // reject loading and underflow padding separated out into new function + // P_GroupLines modified to return a number the underflow padding needs + P_LoadReject(lumpnum, P_GroupLines()); + + // e6y + // Correction of desync on dv04-423.lmp/dv.wad + // http://www.doomworld.com/vb/showthread.php?s=&postid=627257#post627257 + if (compatibility_level>=lxdoom_1_compatibility || M_CheckParm("-force_remove_slime_trails") > 0) + P_RemoveSlimeTrails(); // killough 10/98: remove slime trails from wad + + // Note: you don't need to clear player queue slots -- + // a much simpler fix is in g_game.c -- killough 10/98 + + bodyqueslot = 0; + + /* cph - reset all multiplayer starts */ + memset(playerstarts,0,sizeof(playerstarts)); + deathmatch_p = deathmatchstarts; + for (i = 0; i < MAXPLAYERS; i++) + players[i].mo = NULL; + + P_MapStart(); + + P_LoadThings(lumpnum+ML_THINGS); + + // if deathmatch, randomly spawn the active players + if (deathmatch) + { + for (i=0; idx ? x == node->x ? 2 : x <= node->x ? node->dy > 0 : node->dy < 0 : + !node->dy ? ( compatibility_level < prboom_4_compatibility ? x : y) == node->y ? 2 : y <= node->y ? node->dx < 0 : node->dx > 0 : + (right = ((y - node->y) >> FRACBITS) * (node->dx >> FRACBITS)) < + (left = ((x - node->x) >> FRACBITS) * (node->dy >> FRACBITS)) ? 0 : + right == left ? 2 : 1; +} + +// +// P_CrossSubsector +// Returns true +// if strace crosses the given subsector successfully. +// +// killough 4/19/98: made static and cleaned up + +static boolean P_CrossSubsector(int num) +{ + seg_t *seg = segs + subsectors[num].firstline; + int count; + fixed_t opentop = 0, openbottom = 0; + const sector_t *front = NULL, *back = NULL; + +#ifdef RANGECHECK + if (num >= numsubsectors) + I_Error("P_CrossSubsector: ss %i with numss = %i", num, numsubsectors); +#endif + + for (count = subsectors[num].numlines; --count >= 0; seg++) { // check lines + line_t *line = seg->linedef; + divline_t divl; + + if(!line) // figgi -- skip minisegs + continue; + + // allready checked other side? + if (line->validcount == validcount) + continue; + + line->validcount = validcount; + + /* OPTIMIZE: killough 4/20/98: Added quick bounding-box rejection test + * cph - this is causing demo desyncs on original Doom demos. + * Who knows why. Exclude test for those. + */ + if (!demo_compatibility) + if (line->bbox[BOXLEFT ] > los.bbox[BOXRIGHT ] || + line->bbox[BOXRIGHT ] < los.bbox[BOXLEFT ] || + line->bbox[BOXBOTTOM] > los.bbox[BOXTOP ] || + line->bbox[BOXTOP] < los.bbox[BOXBOTTOM]) + continue; + + // cph - do what we can before forced to check intersection + if (line->flags & ML_TWOSIDED) { + + // no wall to block sight with? + if ((front = seg->frontsector)->floorheight == + (back = seg->backsector)->floorheight && + front->ceilingheight == back->ceilingheight) + continue; + + // possible occluder + // because of ceiling height differences + opentop = front->ceilingheight < back->ceilingheight ? + front->ceilingheight : back->ceilingheight ; + + // because of floor height differences + openbottom = front->floorheight > back->floorheight ? + front->floorheight : back->floorheight ; + + // cph - reject if does not intrude in the z-space of the possible LOS + if ((opentop >= los.maxz) && (openbottom <= los.minz)) + continue; + } + + { // Forget this line if it doesn't cross the line of sight + const vertex_t *v1,*v2; + + v1 = line->v1; + v2 = line->v2; + + if (P_DivlineSide(v1->x, v1->y, &los.strace) == + P_DivlineSide(v2->x, v2->y, &los.strace)) + continue; + + divl.dx = v2->x - (divl.x = v1->x); + divl.dy = v2->y - (divl.y = v1->y); + + // line isn't crossed? + if (P_DivlineSide(los.strace.x, los.strace.y, &divl) == + P_DivlineSide(los.t2x, los.t2y, &divl)) + continue; + } + + // cph - if bottom >= top or top < minz or bottom > maxz then it must be + // solid wrt this LOS + if (!(line->flags & ML_TWOSIDED) || (openbottom >= opentop) || + (opentop < los.minz) || (openbottom > los.maxz)) + return false; + + { // crosses a two sided line + /* cph 2006/07/15 - oops, we missed this in 2.4.0 & .1; + * use P_InterceptVector2 for those compat levels only. */ + fixed_t frac = (compatibility_level == prboom_5_compatibility || compatibility_level == prboom_6_compatibility) ? + P_InterceptVector2(&los.strace, &divl) : + P_InterceptVector(&los.strace, &divl); + + if (front->floorheight != back->floorheight) { + fixed_t slope = FixedDiv(openbottom - los.sightzstart , frac); + if (slope > los.bottomslope) + los.bottomslope = slope; + } + + if (front->ceilingheight != back->ceilingheight) + { + fixed_t slope = FixedDiv(opentop - los.sightzstart , frac); + if (slope < los.topslope) + los.topslope = slope; + } + + if (los.topslope <= los.bottomslope) + return false; // stop + } + } + // passed the subsector ok + return true; +} + +// +// P_CrossBSPNode +// Returns true +// if strace crosses the given node successfully. +// +// killough 4/20/98: rewritten to remove tail recursion, clean up, and optimize +// cph - Made to use R_PointOnSide instead of P_DivlineSide, since the latter +// could return 2 which was ambigous, and the former is +// better optimised; also removes two casts :-) + +static boolean P_CrossBSPNode_LxDoom(int bspnum) +{ + while (!(bspnum & NF_SUBSECTOR)) + { + register const node_t *bsp = nodes + bspnum; + int side,side2; + side = R_PointOnSide(los.strace.x, los.strace.y, bsp); + side2 = R_PointOnSide(los.t2x, los.t2y, bsp); + if (side == side2) + bspnum = bsp->children[side]; // doesn't touch the other side + else // the partition plane is crossed here + if (!P_CrossBSPNode_LxDoom(bsp->children[side])) + return 0; // cross the starting side + else + bspnum = bsp->children[side^1]; // cross the ending side + } + return P_CrossSubsector(bspnum == -1 ? 0 : bspnum & ~NF_SUBSECTOR); +} + +static boolean P_CrossBSPNode_PrBoom(int bspnum) +{ + while (!(bspnum & NF_SUBSECTOR)) + { + register const node_t *bsp = nodes + bspnum; + int side,side2; + side = P_DivlineSide(los.strace.x,los.strace.y,(const divline_t *)bsp)&1; + side2= P_DivlineSide(los.t2x, los.t2y, (const divline_t *) bsp); + if (side == side2) + bspnum = bsp->children[side]; // doesn't touch the other side + else // the partition plane is crossed here + if (!P_CrossBSPNode_PrBoom(bsp->children[side])) + return 0; // cross the starting side + else + bspnum = bsp->children[side^1]; // cross the ending side + } + return P_CrossSubsector(bspnum == -1 ? 0 : bspnum & ~NF_SUBSECTOR); +} + +/* proff - Moved the compatibility check outside the functions + * this gives a slight speedup + */ +static boolean P_CrossBSPNode(int bspnum) +{ + /* cph - LxDoom used some R_* funcs here */ + if (compatibility_level == lxdoom_1_compatibility) + return P_CrossBSPNode_LxDoom(bspnum); + else + return P_CrossBSPNode_PrBoom(bspnum); +} + +// +// P_CheckSight +// Returns true +// if a straight line between t1 and t2 is unobstructed. +// Uses REJECT. +// +// killough 4/20/98: cleaned up, made to use new LOS struct + +boolean P_CheckSight(mobj_t *t1, mobj_t *t2) +{ + const sector_t *s1 = t1->subsector->sector; + const sector_t *s2 = t2->subsector->sector; + int pnum = (s1-sectors)*numsectors + (s2-sectors); + + // First check for trivial rejection. + // Determine subsector entries in REJECT table. + // + // Check in REJECT table. + + if (rejectmatrix[pnum>>3] & (1 << (pnum&7))) // can't possibly be connected + return false; + + // killough 4/19/98: make fake floors and ceilings block monster view + + if ((s1->heightsec != -1 && + ((t1->z + t1->height <= sectors[s1->heightsec].floorheight && + t2->z >= sectors[s1->heightsec].floorheight) || + (t1->z >= sectors[s1->heightsec].ceilingheight && + t2->z + t1->height <= sectors[s1->heightsec].ceilingheight))) + || + (s2->heightsec != -1 && + ((t2->z + t2->height <= sectors[s2->heightsec].floorheight && + t1->z >= sectors[s2->heightsec].floorheight) || + (t2->z >= sectors[s2->heightsec].ceilingheight && + t1->z + t2->height <= sectors[s2->heightsec].ceilingheight)))) + return false; + + /* killough 11/98: shortcut for melee situations + * same subsector? obviously visible + * cph - compatibility optioned for demo sync, cf HR06-UV.LMP */ + if ((t1->subsector == t2->subsector) && + (compatibility_level >= mbf_compatibility)) + return true; + + // An unobstructed LOS is possible. + // Now look from eyes of t1 to any part of t2. + + validcount++; + + los.topslope = (los.bottomslope = t2->z - (los.sightzstart = + t1->z + t1->height - + (t1->height>>2))) + t2->height; + los.strace.dx = (los.t2x = t2->x) - (los.strace.x = t1->x); + los.strace.dy = (los.t2y = t2->y) - (los.strace.y = t1->y); + + if (t1->x > t2->x) + los.bbox[BOXRIGHT] = t1->x, los.bbox[BOXLEFT] = t2->x; + else + los.bbox[BOXRIGHT] = t2->x, los.bbox[BOXLEFT] = t1->x; + + if (t1->y > t2->y) + los.bbox[BOXTOP] = t1->y, los.bbox[BOXBOTTOM] = t2->y; + else + los.bbox[BOXTOP] = t2->y, los.bbox[BOXBOTTOM] = t1->y; + + /* cph - calculate min and max z of the potential line of sight + * For old demos, we disable this optimisation by setting them to + * the extremes */ + switch (compatibility_level) { + case lxdoom_1_compatibility: + if (los.sightzstart < t2->z) { + los.maxz = t2->z + t2->height; los.minz = los.sightzstart; + } else if (los.sightzstart > t2->z + t2->height) { + los.maxz = los.sightzstart; los.minz = t2->z; + } else { + los.maxz = t2->z + t2->height; los.minz = t2->z; + } + break; + default: + los.maxz = INT_MAX; los.minz = INT_MIN; + } + + // the head node is the last node output + return P_CrossBSPNode(numnodes-1); +} diff --git a/src/p_spec.c b/src/p_spec.c new file mode 100644 index 0000000..738eec2 --- /dev/null +++ b/src/p_spec.c @@ -0,0 +1,3353 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * -Loads and initializes texture and flat animation sequences + * -Implements utility functions for all linedef/sector special handlers + * -Dispatches walkover and gun line triggers + * -Initializes and implements special sector types + * -Implements donut linedef triggers + * -Initializes and implements BOOM linedef triggers for + * Scrollers/Conveyors + * Friction + * Wind/Current + * + *-----------------------------------------------------------------------------*/ + +#include "doomstat.h" +#include "p_spec.h" +#include "p_tick.h" +#include "p_setup.h" +#include "m_random.h" +#include "d_englsh.h" +#include "m_argv.h" +#include "w_wad.h" +#include "r_main.h" +#include "p_maputl.h" +#include "p_map.h" +#include "g_game.h" +#include "p_inter.h" +#include "s_sound.h" +#include "sounds.h" +#include "m_bbox.h" // phares 3/20/98 +#include "d_deh.h" +#include "r_plane.h" +#include "lprintf.h" + +// +// Animating textures and planes +// There is another anim_t used in wi_stuff, unrelated. +// +typedef struct +{ + boolean istexture; + int picnum; + int basepic; + int numpics; + int speed; + +} anim_t; + +// +// source animation definition +// +// +#ifdef _MSC_VER // proff: This is the same as __attribute__ ((packed)) in GNUC +#pragma pack(push) +#pragma pack(1) +#endif //_MSC_VER + +#if defined(__MWERKS__) +#pragma options align=packed +#endif + +typedef struct +{ + signed char istexture; //jff 3/23/98 make char for comparison // cph - make signed + char endname[9]; // if false, it is a flat + char startname[9]; + int speed; +} PACKEDATTR animdef_t; //jff 3/23/98 pack to read from memory + +#if defined(__MWERKS__) +#pragma options align=reset +#endif + +#ifdef _MSC_VER +#pragma pack(pop) +#endif //_MSC_VER + +#define MAXANIMS 32 // no longer a strict limit -- killough + +static anim_t* lastanim; +static anim_t* anims; // new structure w/o limits -- killough +static size_t maxanims; + +// killough 3/7/98: Initialize generalized scrolling +static void P_SpawnScrollers(void); + +static void P_SpawnFriction(void); // phares 3/16/98 +static void P_SpawnPushers(void); // phares 3/20/98 + +// +// P_InitPicAnims +// +// Load the table of animation definitions, checking for existence of +// the start and end of each frame. If the start doesn't exist the sequence +// is skipped, if the last doesn't exist, BOOM exits. +// +// Wall/Flat animation sequences, defined by name of first and last frame, +// The full animation sequence is given using all lumps between the start +// and end entry, in the order found in the WAD file. +// +// This routine modified to read its data from a predefined lump or +// PWAD lump called ANIMATED rather than a static table in this module to +// allow wad designers to insert or modify animation sequences. +// +// Lump format is an array of byte packed animdef_t structures, terminated +// by a structure with istexture == -1. The lump can be generated from a +// text source file using SWANTBLS.EXE, distributed with the BOOM utils. +// The standard list of switches and animations is contained in the example +// source text file DEFSWANI.DAT also in the BOOM util distribution. +// +// +void P_InitPicAnims (void) +{ + int i; + const animdef_t *animdefs; //jff 3/23/98 pointer to animation lump + int lump = W_GetNumForName("ANIMATED"); // cph - new wad lump handling + // Init animation + + //jff 3/23/98 read from predefined or wad lump instead of table + animdefs = (const animdef_t *)W_CacheLumpNum(lump); + + lastanim = anims; + for (i=0 ; animdefs[i].istexture != -1 ; i++) + { + // 1/11/98 killough -- removed limit by array-doubling + if (lastanim >= anims + maxanims) + { + size_t newmax = maxanims ? maxanims*2 : MAXANIMS; + anims = realloc(anims, newmax*sizeof(*anims)); // killough + lastanim = anims + maxanims; + maxanims = newmax; + } + + if (animdefs[i].istexture) + { + // different episode ? + if (R_CheckTextureNumForName(animdefs[i].startname) == -1) + continue; + + lastanim->picnum = R_TextureNumForName (animdefs[i].endname); + lastanim->basepic = R_TextureNumForName (animdefs[i].startname); + } + else + { + if ((W_CheckNumForName)(animdefs[i].startname, ns_flats) == -1) // killough 4/17/98 + continue; + + lastanim->picnum = R_FlatNumForName (animdefs[i].endname); + lastanim->basepic = R_FlatNumForName (animdefs[i].startname); + } + + lastanim->istexture = animdefs[i].istexture; + lastanim->numpics = lastanim->picnum - lastanim->basepic + 1; + + if (lastanim->numpics < 2) + I_Error ("P_InitPicAnims: bad cycle from %s to %s", + animdefs[i].startname, + animdefs[i].endname); + + lastanim->speed = LONG(animdefs[i].speed); // killough 5/5/98: add LONG() + lastanim++; + } + W_UnlockLumpNum(lump); +} + +/////////////////////////////////////////////////////////////// +// +// Linedef and Sector Special Implementation Utility Functions +// +/////////////////////////////////////////////////////////////// + +// +// getSide() +// +// Will return a side_t* +// given the number of the current sector, +// the line number, and the side (0/1) that you want. +// +// Note: if side=1 is specified, it must exist or results undefined +// +side_t* getSide +( int currentSector, + int line, + int side ) +{ + return &sides[ (sectors[currentSector].lines[line])->sidenum[side] ]; +} + + +// +// getSector() +// +// Will return a sector_t* +// given the number of the current sector, +// the line number and the side (0/1) that you want. +// +// Note: if side=1 is specified, it must exist or results undefined +// +sector_t* getSector +( int currentSector, + int line, + int side ) +{ + return sides[ (sectors[currentSector].lines[line])->sidenum[side] ].sector; +} + + +// +// twoSided() +// +// Given the sector number and the line number, +// it will tell you whether the line is two-sided or not. +// +// modified to return actual two-sidedness rather than presence +// of 2S flag unless compatibility optioned +// +int twoSided +( int sector, + int line ) +{ + //jff 1/26/98 return what is actually needed, whether the line + //has two sidedefs, rather than whether the 2S flag is set + + return comp[comp_model] ? + (sectors[sector].lines[line])->flags & ML_TWOSIDED + : + (sectors[sector].lines[line])->sidenum[1] != NO_INDEX; +} + + +// +// getNextSector() +// +// Return sector_t * of sector next to current across line. +// +// Note: returns NULL if not two-sided line, or both sides refer to sector +// +sector_t* getNextSector +( line_t* line, + sector_t* sec ) +{ + //jff 1/26/98 check unneeded since line->backsector already + //returns NULL if the line is not two sided, and does so from + //the actual two-sidedness of the line, rather than its 2S flag + + if (comp[comp_model]) + { + if (!(line->flags & ML_TWOSIDED)) + return NULL; + } + + if (line->frontsector == sec) { + if (comp[comp_model] || line->backsector!=sec) + return line->backsector; //jff 5/3/98 don't retn sec unless compatibility + else // fixes an intra-sector line breaking functions + return NULL; // like floor->highest floor + } + return line->frontsector; +} + + +// +// P_FindLowestFloorSurrounding() +// +// Returns the fixed point value of the lowest floor height +// in the sector passed or its surrounding sectors. +// +fixed_t P_FindLowestFloorSurrounding(sector_t* sec) +{ + int i; + line_t* check; + sector_t* other; + fixed_t floor = sec->floorheight; + + for (i=0 ;i < sec->linecount ; i++) + { + check = sec->lines[i]; + other = getNextSector(check,sec); + + if (!other) + continue; + + if (other->floorheight < floor) + floor = other->floorheight; + } + return floor; +} + + +// +// P_FindHighestFloorSurrounding() +// +// Passed a sector, returns the fixed point value of the largest +// floor height in the surrounding sectors, not including that passed +// +// NOTE: if no surrounding sector exists -32000*FRACUINT is returned +// if compatibility then -500*FRACUNIT is the smallest return possible +// +fixed_t P_FindHighestFloorSurrounding(sector_t *sec) +{ + int i; + line_t* check; + sector_t* other; + fixed_t floor = -500*FRACUNIT; + + //jff 1/26/98 Fix initial value for floor to not act differently + //in sections of wad that are below -500 units + if (!comp[comp_model]) /* jff 3/12/98 avoid ovf */ + floor = -32000*FRACUNIT; // in height calculations + + for (i=0 ;i < sec->linecount ; i++) + { + check = sec->lines[i]; + other = getNextSector(check,sec); + + if (!other) + continue; + + if (other->floorheight > floor) + floor = other->floorheight; + } + return floor; +} + + +// +// P_FindNextHighestFloor() +// +// Passed a sector and a floor height, returns the fixed point value +// of the smallest floor height in a surrounding sector larger than +// the floor height passed. If no such height exists the floorheight +// passed is returned. +// +// Rewritten by Lee Killough to avoid fixed array and to be faster +// +fixed_t P_FindNextHighestFloor(sector_t *sec, int currentheight) +{ + sector_t *other; + int i; + + for (i=0 ;i < sec->linecount ; i++) + if ((other = getNextSector(sec->lines[i],sec)) && + other->floorheight > currentheight) + { + int height = other->floorheight; + while (++i < sec->linecount) + if ((other = getNextSector(sec->lines[i],sec)) && + other->floorheight < height && + other->floorheight > currentheight) + height = other->floorheight; + return height; + } + /* cph - my guess at doom v1.2 - 1.4beta compatibility here. + * If there are no higher neighbouring sectors, Heretic just returned + * heightlist[0] (local variable), i.e. noise off the stack. 0 is right for + * RETURN01 E1M2, so let's take that. */ + return (compatibility_level < doom_1666_compatibility ? 0 : currentheight); +} + + +// +// P_FindNextLowestFloor() +// +// Passed a sector and a floor height, returns the fixed point value +// of the largest floor height in a surrounding sector smaller than +// the floor height passed. If no such height exists the floorheight +// passed is returned. +// +// jff 02/03/98 Twiddled Lee's P_FindNextHighestFloor to make this +// +fixed_t P_FindNextLowestFloor(sector_t *sec, int currentheight) +{ + sector_t *other; + int i; + + for (i=0 ;i < sec->linecount ; i++) + if ((other = getNextSector(sec->lines[i],sec)) && + other->floorheight < currentheight) + { + int height = other->floorheight; + while (++i < sec->linecount) + if ((other = getNextSector(sec->lines[i],sec)) && + other->floorheight > height && + other->floorheight < currentheight) + height = other->floorheight; + return height; + } + return currentheight; +} + + +// +// P_FindNextLowestCeiling() +// +// Passed a sector and a ceiling height, returns the fixed point value +// of the largest ceiling height in a surrounding sector smaller than +// the ceiling height passed. If no such height exists the ceiling height +// passed is returned. +// +// jff 02/03/98 Twiddled Lee's P_FindNextHighestFloor to make this +// +fixed_t P_FindNextLowestCeiling(sector_t *sec, int currentheight) +{ + sector_t *other; + int i; + + for (i=0 ;i < sec->linecount ; i++) + if ((other = getNextSector(sec->lines[i],sec)) && + other->ceilingheight < currentheight) + { + int height = other->ceilingheight; + while (++i < sec->linecount) + if ((other = getNextSector(sec->lines[i],sec)) && + other->ceilingheight > height && + other->ceilingheight < currentheight) + height = other->ceilingheight; + return height; + } + return currentheight; +} + + +// +// P_FindNextHighestCeiling() +// +// Passed a sector and a ceiling height, returns the fixed point value +// of the smallest ceiling height in a surrounding sector larger than +// the ceiling height passed. If no such height exists the ceiling height +// passed is returned. +// +// jff 02/03/98 Twiddled Lee's P_FindNextHighestFloor to make this +// +fixed_t P_FindNextHighestCeiling(sector_t *sec, int currentheight) +{ + sector_t *other; + int i; + + for (i=0 ;i < sec->linecount ; i++) + if ((other = getNextSector(sec->lines[i],sec)) && + other->ceilingheight > currentheight) + { + int height = other->ceilingheight; + while (++i < sec->linecount) + if ((other = getNextSector(sec->lines[i],sec)) && + other->ceilingheight < height && + other->ceilingheight > currentheight) + height = other->ceilingheight; + return height; + } + return currentheight; +} + + +// +// P_FindLowestCeilingSurrounding() +// +// Passed a sector, returns the fixed point value of the smallest +// ceiling height in the surrounding sectors, not including that passed +// +// NOTE: if no surrounding sector exists 32000*FRACUINT is returned +// but if compatibility then INT_MAX is the return +// +fixed_t P_FindLowestCeilingSurrounding(sector_t* sec) +{ + int i; + line_t* check; + sector_t* other; + fixed_t height = INT_MAX; + + /* jff 3/12/98 avoid ovf in height calculations */ + if (!comp[comp_model]) height = 32000*FRACUNIT; + + for (i=0 ;i < sec->linecount ; i++) + { + check = sec->lines[i]; + other = getNextSector(check,sec); + + if (!other) + continue; + + if (other->ceilingheight < height) + height = other->ceilingheight; + } + return height; +} + + +// +// P_FindHighestCeilingSurrounding() +// +// Passed a sector, returns the fixed point value of the largest +// ceiling height in the surrounding sectors, not including that passed +// +// NOTE: if no surrounding sector exists -32000*FRACUINT is returned +// but if compatibility then 0 is the smallest return possible +// +fixed_t P_FindHighestCeilingSurrounding(sector_t* sec) +{ + int i; + line_t* check; + sector_t* other; + fixed_t height = 0; + + /* jff 1/26/98 Fix initial value for floor to not act differently + * in sections of wad that are below 0 units + * jff 3/12/98 avoid ovf in height calculations */ + if (!comp[comp_model]) height = -32000*FRACUNIT; + + for (i=0 ;i < sec->linecount ; i++) + { + check = sec->lines[i]; + other = getNextSector(check,sec); + + if (!other) + continue; + + if (other->ceilingheight > height) + height = other->ceilingheight; + } + return height; +} + + +// +// P_FindShortestTextureAround() +// +// Passed a sector number, returns the shortest lower texture on a +// linedef bounding the sector. +// +// Note: If no lower texture exists 32000*FRACUNIT is returned. +// but if compatibility then INT_MAX is returned +// +// jff 02/03/98 Add routine to find shortest lower texture +// +fixed_t P_FindShortestTextureAround(int secnum) +{ + int minsize = INT_MAX; + side_t* side; + int i; + sector_t *sec = §ors[secnum]; + + if (!comp[comp_model]) + minsize = 32000<linecount; i++) + { + if (twoSided(secnum, i)) + { + side = getSide(secnum,i,0); + if (side->bottomtexture > 0) //jff 8/14/98 texture 0 is a placeholder + if (textureheight[side->bottomtexture] < minsize) + minsize = textureheight[side->bottomtexture]; + side = getSide(secnum,i,1); + if (side->bottomtexture > 0) //jff 8/14/98 texture 0 is a placeholder + if (textureheight[side->bottomtexture] < minsize) + minsize = textureheight[side->bottomtexture]; + } + } + return minsize; +} + + +// +// P_FindShortestUpperAround() +// +// Passed a sector number, returns the shortest upper texture on a +// linedef bounding the sector. +// +// Note: If no upper texture exists 32000*FRACUNIT is returned. +// but if compatibility then INT_MAX is returned +// +// jff 03/20/98 Add routine to find shortest upper texture +// +fixed_t P_FindShortestUpperAround(int secnum) +{ + int minsize = INT_MAX; + side_t* side; + int i; + sector_t *sec = §ors[secnum]; + + if (!comp[comp_model]) + minsize = 32000<linecount; i++) + { + if (twoSided(secnum, i)) + { + side = getSide(secnum,i,0); + if (side->toptexture > 0) //jff 8/14/98 texture 0 is a placeholder + if (textureheight[side->toptexture] < minsize) + minsize = textureheight[side->toptexture]; + side = getSide(secnum,i,1); + if (side->toptexture > 0) //jff 8/14/98 texture 0 is a placeholder + if (textureheight[side->toptexture] < minsize) + minsize = textureheight[side->toptexture]; + } + } + return minsize; +} + + +// +// P_FindModelFloorSector() +// +// Passed a floor height and a sector number, return a pointer to a +// a sector with that floor height across the lowest numbered two sided +// line surrounding the sector. +// +// Note: If no sector at that height bounds the sector passed, return NULL +// +// jff 02/03/98 Add routine to find numeric model floor +// around a sector specified by sector number +// jff 3/14/98 change first parameter to plain height to allow call +// from routine not using floormove_t +// +sector_t *P_FindModelFloorSector(fixed_t floordestheight,int secnum) +{ + int i; + sector_t *sec=NULL; + int linecount; + + sec = §ors[secnum]; //jff 3/2/98 woops! better do this + //jff 5/23/98 don't disturb sec->linecount while searching + // but allow early exit in old demos + linecount = sec->linecount; + for (i = 0; i < (demo_compatibility && sec->linecountlinecount : linecount); i++) + { + if ( twoSided(secnum, i) ) + { + if (getSide(secnum,i,0)->sector-sectors == secnum) + sec = getSector(secnum,i,1); + else + sec = getSector(secnum,i,0); + + if (sec->floorheight == floordestheight) + return sec; + } + } + return NULL; +} + + +// +// P_FindModelCeilingSector() +// +// Passed a ceiling height and a sector number, return a pointer to a +// a sector with that ceiling height across the lowest numbered two sided +// line surrounding the sector. +// +// Note: If no sector at that height bounds the sector passed, return NULL +// +// jff 02/03/98 Add routine to find numeric model ceiling +// around a sector specified by sector number +// used only from generalized ceiling types +// jff 3/14/98 change first parameter to plain height to allow call +// from routine not using ceiling_t +// +sector_t *P_FindModelCeilingSector(fixed_t ceildestheight,int secnum) +{ + int i; + sector_t *sec=NULL; + int linecount; + + sec = §ors[secnum]; //jff 3/2/98 woops! better do this + //jff 5/23/98 don't disturb sec->linecount while searching + // but allow early exit in old demos + linecount = sec->linecount; + for (i = 0; i < (demo_compatibility && sec->linecountlinecount : linecount); i++) + { + if ( twoSided(secnum, i) ) + { + if (getSide(secnum,i,0)->sector-sectors == secnum) + sec = getSector(secnum,i,1); + else + sec = getSector(secnum,i,0); + + if (sec->ceilingheight == ceildestheight) + return sec; + } + } + return NULL; +} + +// +// RETURN NEXT SECTOR # THAT LINE TAG REFERS TO +// + +// Find the next sector with the same tag as a linedef. +// Rewritten by Lee Killough to use chained hashing to improve speed + +int P_FindSectorFromLineTag(const line_t *line, int start) +{ + start = start >= 0 ? sectors[start].nexttag : + sectors[(unsigned) line->tag % (unsigned) numsectors].firsttag; + while (start >= 0 && sectors[start].tag != line->tag) + start = sectors[start].nexttag; + return start; +} + +// killough 4/16/98: Same thing, only for linedefs + +int P_FindLineFromLineTag(const line_t *line, int start) +{ + start = start >= 0 ? lines[start].nexttag : + lines[(unsigned) line->tag % (unsigned) numlines].firsttag; + while (start >= 0 && lines[start].tag != line->tag) + start = lines[start].nexttag; + return start; +} + +// Hash the sector tags across the sectors and linedefs. +static void P_InitTagLists(void) +{ + register int i; + + for (i=numsectors; --i>=0; ) // Initially make all slots empty. + sectors[i].firsttag = -1; + for (i=numsectors; --i>=0; ) // Proceed from last to first sector + { // so that lower sectors appear first + int j = (unsigned) sectors[i].tag % (unsigned) numsectors; // Hash func + sectors[i].nexttag = sectors[j].firsttag; // Prepend sector to chain + sectors[j].firsttag = i; + } + + // killough 4/17/98: same thing, only for linedefs + + for (i=numlines; --i>=0; ) // Initially make all slots empty. + lines[i].firsttag = -1; + for (i=numlines; --i>=0; ) // Proceed from last to first linedef + { // so that lower linedefs appear first + int j = (unsigned) lines[i].tag % (unsigned) numlines; // Hash func + lines[i].nexttag = lines[j].firsttag; // Prepend linedef to chain + lines[j].firsttag = i; + } +} + +// +// P_FindMinSurroundingLight() +// +// Passed a sector and a light level, returns the smallest light level +// in a surrounding sector less than that passed. If no smaller light +// level exists, the light level passed is returned. +// +int P_FindMinSurroundingLight +( sector_t* sector, + int max ) +{ + int i; + int min; + line_t* line; + sector_t* check; + + min = max; + for (i=0 ; i < sector->linecount ; i++) + { + line = sector->lines[i]; + check = getNextSector(line,sector); + + if (!check) + continue; + + if (check->lightlevel < min) + min = check->lightlevel; + } + return min; +} + + +// +// P_CanUnlockGenDoor() +// +// Passed a generalized locked door linedef and a player, returns whether +// the player has the keys necessary to unlock that door. +// +// Note: The linedef passed MUST be a generalized locked door type +// or results are undefined. +// +// jff 02/05/98 routine added to test for unlockability of +// generalized locked doors +// +boolean P_CanUnlockGenDoor +( line_t* line, + player_t* player) +{ + // does this line special distinguish between skulls and keys? + int skulliscard = (line->special & LockedNKeys)>>LockedNKeysShift; + + // determine for each case of lock type if player's keys are adequate + switch((line->special & LockedKey)>>LockedKeyShift) + { + case AnyKey: + if + ( + !player->cards[it_redcard] && + !player->cards[it_redskull] && + !player->cards[it_bluecard] && + !player->cards[it_blueskull] && + !player->cards[it_yellowcard] && + !player->cards[it_yellowskull] + ) + { + player->message = s_PD_ANY; // Ty 03/27/98 - externalized + S_StartSound(player->mo,sfx_oof); // killough 3/20/98 + return false; + } + break; + case RCard: + if + ( + !player->cards[it_redcard] && + (!skulliscard || !player->cards[it_redskull]) + ) + { + player->message = skulliscard? s_PD_REDK : s_PD_REDC; // Ty 03/27/98 - externalized + S_StartSound(player->mo,sfx_oof); // killough 3/20/98 + return false; + } + break; + case BCard: + if + ( + !player->cards[it_bluecard] && + (!skulliscard || !player->cards[it_blueskull]) + ) + { + player->message = skulliscard? s_PD_BLUEK : s_PD_BLUEC; // Ty 03/27/98 - externalized + S_StartSound(player->mo,sfx_oof); // killough 3/20/98 + return false; + } + break; + case YCard: + if + ( + !player->cards[it_yellowcard] && + (!skulliscard || !player->cards[it_yellowskull]) + ) + { + player->message = skulliscard? s_PD_YELLOWK : s_PD_YELLOWC; // Ty 03/27/98 - externalized + S_StartSound(player->mo,sfx_oof); // killough 3/20/98 + return false; + } + break; + case RSkull: + if + ( + !player->cards[it_redskull] && + (!skulliscard || !player->cards[it_redcard]) + ) + { + player->message = skulliscard? s_PD_REDK : s_PD_REDS; // Ty 03/27/98 - externalized + S_StartSound(player->mo,sfx_oof); // killough 3/20/98 + return false; + } + break; + case BSkull: + if + ( + !player->cards[it_blueskull] && + (!skulliscard || !player->cards[it_bluecard]) + ) + { + player->message = skulliscard? s_PD_BLUEK : s_PD_BLUES; // Ty 03/27/98 - externalized + S_StartSound(player->mo,sfx_oof); // killough 3/20/98 + return false; + } + break; + case YSkull: + if + ( + !player->cards[it_yellowskull] && + (!skulliscard || !player->cards[it_yellowcard]) + ) + { + player->message = skulliscard? s_PD_YELLOWK : s_PD_YELLOWS; // Ty 03/27/98 - externalized + S_StartSound(player->mo,sfx_oof); // killough 3/20/98 + return false; + } + break; + case AllKeys: + if + ( + !skulliscard && + ( + !player->cards[it_redcard] || + !player->cards[it_redskull] || + !player->cards[it_bluecard] || + !player->cards[it_blueskull] || + !player->cards[it_yellowcard] || + !player->cards[it_yellowskull] + ) + ) + { + player->message = s_PD_ALL6; // Ty 03/27/98 - externalized + S_StartSound(player->mo,sfx_oof); // killough 3/20/98 + return false; + } + if + ( + skulliscard && + ( + (!player->cards[it_redcard] && + !player->cards[it_redskull]) || + (!player->cards[it_bluecard] && + !player->cards[it_blueskull]) || + (!player->cards[it_yellowcard] && + !player->cards[it_yellowskull]) + ) + ) + { + player->message = s_PD_ALL3; // Ty 03/27/98 - externalized + S_StartSound(player->mo,sfx_oof); // killough 3/20/98 + return false; + } + break; + } + return true; +} + + +// +// P_SectorActive() +// +// Passed a linedef special class (floor, ceiling, lighting) and a sector +// returns whether the sector is already busy with a linedef special of the +// same class. If old demo compatibility true, all linedef special classes +// are the same. +// +// jff 2/23/98 added to prevent old demos from +// succeeding in starting multiple specials on one sector +// +boolean PUREFUNC P_SectorActive(special_e t, const sector_t *sec) +{ + if (demo_compatibility) // return whether any thinker is active + return sec->floordata != NULL || sec->ceilingdata != NULL || sec->lightingdata != NULL; + else + switch (t) // return whether thinker of same type is active + { + case floor_special: + return sec->floordata != NULL; + case ceiling_special: + return sec->ceilingdata != NULL; + case lighting_special: + return sec->lightingdata != NULL; + } + return true; // don't know which special, must be active, shouldn't be here +} + + +// +// P_CheckTag() +// +// Passed a line, returns true if the tag is non-zero or the line special +// allows no tag without harm. If compatibility, all linedef specials are +// allowed to have zero tag. +// +// Note: Only line specials activated by walkover, pushing, or shooting are +// checked by this routine. +// +// jff 2/27/98 Added to check for zero tag allowed for regular special types +// +int P_CheckTag(line_t *line) +{ + /* tag not zero, allowed, or + * killough 11/98: compatibility option */ + if (comp[comp_zerotags] || line->tag) + return 1; + + switch(line->special) + { + case 1: // Manual door specials + case 26: + case 27: + case 28: + case 31: + case 32: + case 33: + case 34: + case 117: + case 118: + + case 139: // Lighting specials + case 170: + case 79: + case 35: + case 138: + case 171: + case 81: + case 13: + case 192: + case 169: + case 80: + case 12: + case 194: + case 173: + case 157: + case 104: + case 193: + case 172: + case 156: + case 17: + + case 195: // Thing teleporters + case 174: + case 97: + case 39: + case 126: + case 125: + case 210: + case 209: + case 208: + case 207: + + case 11: // Exits + case 52: + case 197: + case 51: + case 124: + case 198: + + case 48: // Scrolling walls + case 85: + return 1; // zero tag allowed + + default: + break; + } + return 0; // zero tag not allowed +} + + +// +// P_IsSecret() +// +// Passed a sector, returns if the sector secret type is still active, i.e. +// secret type is set and the secret has not yet been obtained. +// +// jff 3/14/98 added to simplify checks for whether sector is secret +// in automap and other places +// +boolean PUREFUNC P_IsSecret(const sector_t *sec) +{ + return (sec->special==9 || (sec->special&SECRET_MASK)); +} + + +// +// P_WasSecret() +// +// Passed a sector, returns if the sector secret type is was active, i.e. +// secret type was set and the secret has been obtained already. +// +// jff 3/14/98 added to simplify checks for whether sector is secret +// in automap and other places +// +boolean PUREFUNC P_WasSecret(const sector_t *sec) +{ + return (sec->oldspecial==9 || (sec->oldspecial&SECRET_MASK)); +} + + +////////////////////////////////////////////////////////////////////////// +// +// Events +// +// Events are operations triggered by using, crossing, +// or shooting special lines, or by timed thinkers. +// +///////////////////////////////////////////////////////////////////////// + +// +// P_CrossSpecialLine - Walkover Trigger Dispatcher +// +// Called every time a thing origin is about +// to cross a line with a non 0 special, whether a walkover type or not. +// +// jff 02/12/98 all W1 lines were fixed to check the result from the EV_ +// function before clearing the special. This avoids losing the function +// of the line, should the sector already be active when the line is +// crossed. Change is qualified by demo_compatibility. +// +// CPhipps - take a line_t pointer instead of a line number, as in MBF +void P_CrossSpecialLine(line_t *line, int side, mobj_t *thing) +{ + int ok; + + // Things that should never trigger lines + if (!thing->player) + { + // Things that should NOT trigger specials... + switch(thing->type) + { + case MT_ROCKET: + case MT_PLASMA: + case MT_BFG: + case MT_TROOPSHOT: + case MT_HEADSHOT: + case MT_BRUISERSHOT: + return; + break; + + default: break; + } + } + + //jff 02/04/98 add check here for generalized lindef types + if (!demo_compatibility) // generalized types not recognized if old demo + { + // pointer to line function is NULL by default, set non-null if + // line special is walkover generalized linedef type + int (*linefunc)(line_t *line)=NULL; + + // check each range of generalized linedefs + if ((unsigned)line->special >= GenEnd) + { + // Out of range for GenFloors + } + else if ((unsigned)line->special >= GenFloorBase) + { + if (!thing->player) + if ((line->special & FloorChange) || !(line->special & FloorModel)) + return; // FloorModel is "Allow Monsters" if FloorChange is 0 + if (!line->tag) //jff 2/27/98 all walk generalized types require tag + return; + linefunc = EV_DoGenFloor; + } + else if ((unsigned)line->special >= GenCeilingBase) + { + if (!thing->player) + if ((line->special & CeilingChange) || !(line->special & CeilingModel)) + return; // CeilingModel is "Allow Monsters" if CeilingChange is 0 + if (!line->tag) //jff 2/27/98 all walk generalized types require tag + return; + linefunc = EV_DoGenCeiling; + } + else if ((unsigned)line->special >= GenDoorBase) + { + if (!thing->player) + { + if (!(line->special & DoorMonster)) + return; // monsters disallowed from this door + if (line->flags & ML_SECRET) // they can't open secret doors either + return; + } + if (!line->tag) //3/2/98 move outside the monster check + return; + linefunc = EV_DoGenDoor; + } + else if ((unsigned)line->special >= GenLockedBase) + { + if (!thing->player) + return; // monsters disallowed from unlocking doors + if (((line->special&TriggerType)==WalkOnce) || ((line->special&TriggerType)==WalkMany)) + { //jff 4/1/98 check for being a walk type before reporting door type + if (!P_CanUnlockGenDoor(line,thing->player)) + return; + } + else + return; + linefunc = EV_DoGenLockedDoor; + } + else if ((unsigned)line->special >= GenLiftBase) + { + if (!thing->player) + if (!(line->special & LiftMonster)) + return; // monsters disallowed + if (!line->tag) //jff 2/27/98 all walk generalized types require tag + return; + linefunc = EV_DoGenLift; + } + else if ((unsigned)line->special >= GenStairsBase) + { + if (!thing->player) + if (!(line->special & StairMonster)) + return; // monsters disallowed + if (!line->tag) //jff 2/27/98 all walk generalized types require tag + return; + linefunc = EV_DoGenStairs; + } + + if (linefunc) // if it was a valid generalized type + switch((line->special & TriggerType) >> TriggerTypeShift) + { + case WalkOnce: + if (linefunc(line)) + line->special = 0; // clear special if a walk once type + return; + case WalkMany: + linefunc(line); + return; + default: // if not a walk type, do nothing here + return; + } + } + + if (!thing->player) + { + ok = 0; + switch(line->special) + { + case 39: // teleport trigger + case 97: // teleport retrigger + case 125: // teleport monsteronly trigger + case 126: // teleport monsteronly retrigger + case 4: // raise door + case 10: // plat down-wait-up-stay trigger + case 88: // plat down-wait-up-stay retrigger + //jff 3/5/98 add ability of monsters etc. to use teleporters + case 208: //silent thing teleporters + case 207: + case 243: //silent line-line teleporter + case 244: //jff 3/6/98 make fit within DCK's 256 linedef types + case 262: //jff 4/14/98 add monster only + case 263: //jff 4/14/98 silent thing,line,line rev types + case 264: //jff 4/14/98 plus player/monster silent line + case 265: // reversed types + case 266: + case 267: + case 268: + case 269: + ok = 1; + break; + } + if (!ok) + return; + } + + if (!P_CheckTag(line)) //jff 2/27/98 disallow zero tag on some types + return; + + // Dispatch on the line special value to the line's action routine + // If a once only function, and successful, clear the line special + + switch (line->special) + { + // Regular walk once triggers + + case 2: + // Open Door + if (EV_DoDoor(line,open) || demo_compatibility) + line->special = 0; + break; + + case 3: + // Close Door + if (EV_DoDoor(line,close) || demo_compatibility) + line->special = 0; + break; + + case 4: + // Raise Door + if (EV_DoDoor(line,normal) || demo_compatibility) + line->special = 0; + break; + + case 5: + // Raise Floor + if (EV_DoFloor(line,raiseFloor) || demo_compatibility) + line->special = 0; + break; + + case 6: + // Fast Ceiling Crush & Raise + if (EV_DoCeiling(line,fastCrushAndRaise) || demo_compatibility) + line->special = 0; + break; + + case 8: + // Build Stairs + if (EV_BuildStairs(line,build8) || demo_compatibility) + line->special = 0; + break; + + case 10: + // PlatDownWaitUp + if (EV_DoPlat(line,downWaitUpStay,0) || demo_compatibility) + line->special = 0; + break; + + case 12: + // Light Turn On - brightest near + if (EV_LightTurnOn(line,0) || demo_compatibility) + line->special = 0; + break; + + case 13: + // Light Turn On 255 + if (EV_LightTurnOn(line,255) || demo_compatibility) + line->special = 0; + break; + + case 16: + // Close Door 30 + if (EV_DoDoor(line,close30ThenOpen) || demo_compatibility) + line->special = 0; + break; + + case 17: + // Start Light Strobing + if (EV_StartLightStrobing(line) || demo_compatibility) + line->special = 0; + break; + + case 19: + // Lower Floor + if (EV_DoFloor(line,lowerFloor) || demo_compatibility) + line->special = 0; + break; + + case 22: + // Raise floor to nearest height and change texture + if (EV_DoPlat(line,raiseToNearestAndChange,0) || demo_compatibility) + line->special = 0; + break; + + case 25: + // Ceiling Crush and Raise + if (EV_DoCeiling(line,crushAndRaise) || demo_compatibility) + line->special = 0; + break; + + case 30: + // Raise floor to shortest texture height + // on either side of lines. + if (EV_DoFloor(line,raiseToTexture) || demo_compatibility) + line->special = 0; + break; + + case 35: + // Lights Very Dark + if (EV_LightTurnOn(line,35) || demo_compatibility) + line->special = 0; + break; + + case 36: + // Lower Floor (TURBO) + if (EV_DoFloor(line,turboLower) || demo_compatibility) + line->special = 0; + break; + + case 37: + // LowerAndChange + if (EV_DoFloor(line,lowerAndChange) || demo_compatibility) + line->special = 0; + break; + + case 38: + // Lower Floor To Lowest + if (EV_DoFloor(line, lowerFloorToLowest) || demo_compatibility) + line->special = 0; + break; + + case 39: + // TELEPORT! //jff 02/09/98 fix using up with wrong side crossing + if (EV_Teleport(line, side, thing) || demo_compatibility) + line->special = 0; + break; + + case 40: + // RaiseCeilingLowerFloor + if (demo_compatibility) + { + EV_DoCeiling( line, raiseToHighest ); + EV_DoFloor( line, lowerFloorToLowest ); //jff 02/12/98 doesn't work + line->special = 0; + } + else + if (EV_DoCeiling(line, raiseToHighest)) + line->special = 0; + break; + + case 44: + // Ceiling Crush + if (EV_DoCeiling(line, lowerAndCrush) || demo_compatibility) + line->special = 0; + break; + + case 52: + // EXIT! + // killough 10/98: prevent zombies from exiting levels + if (!(thing->player && thing->player->health <= 0 && !comp[comp_zombie])) + G_ExitLevel (); + break; + + case 53: + // Perpetual Platform Raise + if (EV_DoPlat(line,perpetualRaise,0) || demo_compatibility) + line->special = 0; + break; + + case 54: + // Platform Stop + if (EV_StopPlat(line) || demo_compatibility) + line->special = 0; + break; + + case 56: + // Raise Floor Crush + if (EV_DoFloor(line,raiseFloorCrush) || demo_compatibility) + line->special = 0; + break; + + case 57: + // Ceiling Crush Stop + if (EV_CeilingCrushStop(line) || demo_compatibility) + line->special = 0; + break; + + case 58: + // Raise Floor 24 + if (EV_DoFloor(line,raiseFloor24) || demo_compatibility) + line->special = 0; + break; + + case 59: + // Raise Floor 24 And Change + if (EV_DoFloor(line,raiseFloor24AndChange) || demo_compatibility) + line->special = 0; + break; + + case 100: + // Build Stairs Turbo 16 + if (EV_BuildStairs(line,turbo16) || demo_compatibility) + line->special = 0; + break; + + case 104: + // Turn lights off in sector(tag) + if (EV_TurnTagLightsOff(line) || demo_compatibility) + line->special = 0; + break; + + case 108: + // Blazing Door Raise (faster than TURBO!) + if (EV_DoDoor(line,blazeRaise) || demo_compatibility) + line->special = 0; + break; + + case 109: + // Blazing Door Open (faster than TURBO!) + if (EV_DoDoor (line,blazeOpen) || demo_compatibility) + line->special = 0; + break; + + case 110: + // Blazing Door Close (faster than TURBO!) + if (EV_DoDoor (line,blazeClose) || demo_compatibility) + line->special = 0; + break; + + case 119: + // Raise floor to nearest surr. floor + if (EV_DoFloor(line,raiseFloorToNearest) || demo_compatibility) + line->special = 0; + break; + + case 121: + // Blazing PlatDownWaitUpStay + if (EV_DoPlat(line,blazeDWUS,0) || demo_compatibility) + line->special = 0; + break; + + case 124: + // Secret EXIT + // killough 10/98: prevent zombies from exiting levels + // CPhipps - change for lxdoom's compatibility handling + if (!(thing->player && thing->player->health <= 0 && !comp[comp_zombie])) + G_SecretExitLevel (); + break; + + case 125: + // TELEPORT MonsterONLY + if (!thing->player && + (EV_Teleport(line, side, thing) || demo_compatibility)) + line->special = 0; + break; + + case 130: + // Raise Floor Turbo + if (EV_DoFloor(line,raiseFloorTurbo) || demo_compatibility) + line->special = 0; + break; + + case 141: + // Silent Ceiling Crush & Raise + if (EV_DoCeiling(line,silentCrushAndRaise) || demo_compatibility) + line->special = 0; + break; + + // Regular walk many retriggerable + + case 72: + // Ceiling Crush + EV_DoCeiling( line, lowerAndCrush ); + break; + + case 73: + // Ceiling Crush and Raise + EV_DoCeiling(line,crushAndRaise); + break; + + case 74: + // Ceiling Crush Stop + EV_CeilingCrushStop(line); + break; + + case 75: + // Close Door + EV_DoDoor(line,close); + break; + + case 76: + // Close Door 30 + EV_DoDoor(line,close30ThenOpen); + break; + + case 77: + // Fast Ceiling Crush & Raise + EV_DoCeiling(line,fastCrushAndRaise); + break; + + case 79: + // Lights Very Dark + EV_LightTurnOn(line,35); + break; + + case 80: + // Light Turn On - brightest near + EV_LightTurnOn(line,0); + break; + + case 81: + // Light Turn On 255 + EV_LightTurnOn(line,255); + break; + + case 82: + // Lower Floor To Lowest + EV_DoFloor( line, lowerFloorToLowest ); + break; + + case 83: + // Lower Floor + EV_DoFloor(line,lowerFloor); + break; + + case 84: + // LowerAndChange + EV_DoFloor(line,lowerAndChange); + break; + + case 86: + // Open Door + EV_DoDoor(line,open); + break; + + case 87: + // Perpetual Platform Raise + EV_DoPlat(line,perpetualRaise,0); + break; + + case 88: + // PlatDownWaitUp + EV_DoPlat(line,downWaitUpStay,0); + break; + + case 89: + // Platform Stop + EV_StopPlat(line); + break; + + case 90: + // Raise Door + EV_DoDoor(line,normal); + break; + + case 91: + // Raise Floor + EV_DoFloor(line,raiseFloor); + break; + + case 92: + // Raise Floor 24 + EV_DoFloor(line,raiseFloor24); + break; + + case 93: + // Raise Floor 24 And Change + EV_DoFloor(line,raiseFloor24AndChange); + break; + + case 94: + // Raise Floor Crush + EV_DoFloor(line,raiseFloorCrush); + break; + + case 95: + // Raise floor to nearest height + // and change texture. + EV_DoPlat(line,raiseToNearestAndChange,0); + break; + + case 96: + // Raise floor to shortest texture height + // on either side of lines. + EV_DoFloor(line,raiseToTexture); + break; + + case 97: + // TELEPORT! + EV_Teleport( line, side, thing ); + break; + + case 98: + // Lower Floor (TURBO) + EV_DoFloor(line,turboLower); + break; + + case 105: + // Blazing Door Raise (faster than TURBO!) + EV_DoDoor (line,blazeRaise); + break; + + case 106: + // Blazing Door Open (faster than TURBO!) + EV_DoDoor (line,blazeOpen); + break; + + case 107: + // Blazing Door Close (faster than TURBO!) + EV_DoDoor (line,blazeClose); + break; + + case 120: + // Blazing PlatDownWaitUpStay. + EV_DoPlat(line,blazeDWUS,0); + break; + + case 126: + // TELEPORT MonsterONLY. + if (!thing->player) + EV_Teleport( line, side, thing ); + break; + + case 128: + // Raise To Nearest Floor + EV_DoFloor(line,raiseFloorToNearest); + break; + + case 129: + // Raise Floor Turbo + EV_DoFloor(line,raiseFloorTurbo); + break; + + // Extended walk triggers + + // jff 1/29/98 added new linedef types to fill all functions out so that + // all have varieties SR, S1, WR, W1 + + // killough 1/31/98: "factor out" compatibility test, by + // adding inner switch qualified by compatibility flag. + // relax test to demo_compatibility + + // killough 2/16/98: Fix problems with W1 types being cleared too early + + default: + if (!demo_compatibility) + switch (line->special) + { + // Extended walk once triggers + + case 142: + // Raise Floor 512 + // 142 W1 EV_DoFloor(raiseFloor512) + if (EV_DoFloor(line,raiseFloor512)) + line->special = 0; + break; + + case 143: + // Raise Floor 24 and change + // 143 W1 EV_DoPlat(raiseAndChange,24) + if (EV_DoPlat(line,raiseAndChange,24)) + line->special = 0; + break; + + case 144: + // Raise Floor 32 and change + // 144 W1 EV_DoPlat(raiseAndChange,32) + if (EV_DoPlat(line,raiseAndChange,32)) + line->special = 0; + break; + + case 145: + // Lower Ceiling to Floor + // 145 W1 EV_DoCeiling(lowerToFloor) + if (EV_DoCeiling( line, lowerToFloor )) + line->special = 0; + break; + + case 146: + // Lower Pillar, Raise Donut + // 146 W1 EV_DoDonut() + if (EV_DoDonut(line)) + line->special = 0; + break; + + case 199: + // Lower ceiling to lowest surrounding ceiling + // 199 W1 EV_DoCeiling(lowerToLowest) + if (EV_DoCeiling(line,lowerToLowest)) + line->special = 0; + break; + + case 200: + // Lower ceiling to highest surrounding floor + // 200 W1 EV_DoCeiling(lowerToMaxFloor) + if (EV_DoCeiling(line,lowerToMaxFloor)) + line->special = 0; + break; + + case 207: + // killough 2/16/98: W1 silent teleporter (normal kind) + if (EV_SilentTeleport(line, side, thing)) + line->special = 0; + break; + + //jff 3/16/98 renumber 215->153 + case 153: //jff 3/15/98 create texture change no motion type + // Texture/Type Change Only (Trig) + // 153 W1 Change Texture/Type Only + if (EV_DoChange(line,trigChangeOnly)) + line->special = 0; + break; + + case 239: //jff 3/15/98 create texture change no motion type + // Texture/Type Change Only (Numeric) + // 239 W1 Change Texture/Type Only + if (EV_DoChange(line,numChangeOnly)) + line->special = 0; + break; + + case 219: + // Lower floor to next lower neighbor + // 219 W1 Lower Floor Next Lower Neighbor + if (EV_DoFloor(line,lowerFloorToNearest)) + line->special = 0; + break; + + case 227: + // Raise elevator next floor + // 227 W1 Raise Elevator next floor + if (EV_DoElevator(line,elevateUp)) + line->special = 0; + break; + + case 231: + // Lower elevator next floor + // 231 W1 Lower Elevator next floor + if (EV_DoElevator(line,elevateDown)) + line->special = 0; + break; + + case 235: + // Elevator to current floor + // 235 W1 Elevator to current floor + if (EV_DoElevator(line,elevateCurrent)) + line->special = 0; + break; + + case 243: //jff 3/6/98 make fit within DCK's 256 linedef types + // killough 2/16/98: W1 silent teleporter (linedef-linedef kind) + if (EV_SilentLineTeleport(line, side, thing, false)) + line->special = 0; + break; + + case 262: //jff 4/14/98 add silent line-line reversed + if (EV_SilentLineTeleport(line, side, thing, true)) + line->special = 0; + break; + + case 264: //jff 4/14/98 add monster-only silent line-line reversed + if (!thing->player && + EV_SilentLineTeleport(line, side, thing, true)) + line->special = 0; + break; + + case 266: //jff 4/14/98 add monster-only silent line-line + if (!thing->player && + EV_SilentLineTeleport(line, side, thing, false)) + line->special = 0; + break; + + case 268: //jff 4/14/98 add monster-only silent + if (!thing->player && EV_SilentTeleport(line, side, thing)) + line->special = 0; + break; + + //jff 1/29/98 end of added W1 linedef types + + // Extended walk many retriggerable + + //jff 1/29/98 added new linedef types to fill all functions + //out so that all have varieties SR, S1, WR, W1 + + case 147: + // Raise Floor 512 + // 147 WR EV_DoFloor(raiseFloor512) + EV_DoFloor(line,raiseFloor512); + break; + + case 148: + // Raise Floor 24 and Change + // 148 WR EV_DoPlat(raiseAndChange,24) + EV_DoPlat(line,raiseAndChange,24); + break; + + case 149: + // Raise Floor 32 and Change + // 149 WR EV_DoPlat(raiseAndChange,32) + EV_DoPlat(line,raiseAndChange,32); + break; + + case 150: + // Start slow silent crusher + // 150 WR EV_DoCeiling(silentCrushAndRaise) + EV_DoCeiling(line,silentCrushAndRaise); + break; + + case 151: + // RaiseCeilingLowerFloor + // 151 WR EV_DoCeiling(raiseToHighest), + // EV_DoFloor(lowerFloortoLowest) + EV_DoCeiling( line, raiseToHighest ); + EV_DoFloor( line, lowerFloorToLowest ); + break; + + case 152: + // Lower Ceiling to Floor + // 152 WR EV_DoCeiling(lowerToFloor) + EV_DoCeiling( line, lowerToFloor ); + break; + + //jff 3/16/98 renumber 153->256 + case 256: + // Build stairs, step 8 + // 256 WR EV_BuildStairs(build8) + EV_BuildStairs(line,build8); + break; + + //jff 3/16/98 renumber 154->257 + case 257: + // Build stairs, step 16 + // 257 WR EV_BuildStairs(turbo16) + EV_BuildStairs(line,turbo16); + break; + + case 155: + // Lower Pillar, Raise Donut + // 155 WR EV_DoDonut() + EV_DoDonut(line); + break; + + case 156: + // Start lights strobing + // 156 WR Lights EV_StartLightStrobing() + EV_StartLightStrobing(line); + break; + + case 157: + // Lights to dimmest near + // 157 WR Lights EV_TurnTagLightsOff() + EV_TurnTagLightsOff(line); + break; + + case 201: + // Lower ceiling to lowest surrounding ceiling + // 201 WR EV_DoCeiling(lowerToLowest) + EV_DoCeiling(line,lowerToLowest); + break; + + case 202: + // Lower ceiling to highest surrounding floor + // 202 WR EV_DoCeiling(lowerToMaxFloor) + EV_DoCeiling(line,lowerToMaxFloor); + break; + + case 208: + // killough 2/16/98: WR silent teleporter (normal kind) + EV_SilentTeleport(line, side, thing); + break; + + case 212: //jff 3/14/98 create instant toggle floor type + // Toggle floor between C and F instantly + // 212 WR Instant Toggle Floor + EV_DoPlat(line,toggleUpDn,0); + break; + + //jff 3/16/98 renumber 216->154 + case 154: //jff 3/15/98 create texture change no motion type + // Texture/Type Change Only (Trigger) + // 154 WR Change Texture/Type Only + EV_DoChange(line,trigChangeOnly); + break; + + case 240: //jff 3/15/98 create texture change no motion type + // Texture/Type Change Only (Numeric) + // 240 WR Change Texture/Type Only + EV_DoChange(line,numChangeOnly); + break; + + case 220: + // Lower floor to next lower neighbor + // 220 WR Lower Floor Next Lower Neighbor + EV_DoFloor(line,lowerFloorToNearest); + break; + + case 228: + // Raise elevator next floor + // 228 WR Raise Elevator next floor + EV_DoElevator(line,elevateUp); + break; + + case 232: + // Lower elevator next floor + // 232 WR Lower Elevator next floor + EV_DoElevator(line,elevateDown); + break; + + case 236: + // Elevator to current floor + // 236 WR Elevator to current floor + EV_DoElevator(line,elevateCurrent); + break; + + case 244: //jff 3/6/98 make fit within DCK's 256 linedef types + // killough 2/16/98: WR silent teleporter (linedef-linedef kind) + EV_SilentLineTeleport(line, side, thing, false); + break; + + case 263: //jff 4/14/98 add silent line-line reversed + EV_SilentLineTeleport(line, side, thing, true); + break; + + case 265: //jff 4/14/98 add monster-only silent line-line reversed + if (!thing->player) + EV_SilentLineTeleport(line, side, thing, true); + break; + + case 267: //jff 4/14/98 add monster-only silent line-line + if (!thing->player) + EV_SilentLineTeleport(line, side, thing, false); + break; + + case 269: //jff 4/14/98 add monster-only silent + if (!thing->player) + EV_SilentTeleport(line, side, thing); + break; + + //jff 1/29/98 end of added WR linedef types + } + break; + } +} + +// +// P_ShootSpecialLine - Gun trigger special dispatcher +// +// Called when a thing shoots a special line with bullet, shell, saw, or fist. +// +// jff 02/12/98 all G1 lines were fixed to check the result from the EV_ +// function before clearing the special. This avoids losing the function +// of the line, should the sector already be in motion when the line is +// impacted. Change is qualified by demo_compatibility. +// +void P_ShootSpecialLine +( mobj_t* thing, + line_t* line ) +{ + //jff 02/04/98 add check here for generalized linedef + if (!demo_compatibility) + { + // pointer to line function is NULL by default, set non-null if + // line special is gun triggered generalized linedef type + int (*linefunc)(line_t *line)=NULL; + + // check each range of generalized linedefs + if ((unsigned)line->special >= GenEnd) + { + // Out of range for GenFloors + } + else if ((unsigned)line->special >= GenFloorBase) + { + if (!thing->player) + if ((line->special & FloorChange) || !(line->special & FloorModel)) + return; // FloorModel is "Allow Monsters" if FloorChange is 0 + if (!line->tag) //jff 2/27/98 all gun generalized types require tag + return; + + linefunc = EV_DoGenFloor; + } + else if ((unsigned)line->special >= GenCeilingBase) + { + if (!thing->player) + if ((line->special & CeilingChange) || !(line->special & CeilingModel)) + return; // CeilingModel is "Allow Monsters" if CeilingChange is 0 + if (!line->tag) //jff 2/27/98 all gun generalized types require tag + return; + linefunc = EV_DoGenCeiling; + } + else if ((unsigned)line->special >= GenDoorBase) + { + if (!thing->player) + { + if (!(line->special & DoorMonster)) + return; // monsters disallowed from this door + if (line->flags & ML_SECRET) // they can't open secret doors either + return; + } + if (!line->tag) //jff 3/2/98 all gun generalized types require tag + return; + linefunc = EV_DoGenDoor; + } + else if ((unsigned)line->special >= GenLockedBase) + { + if (!thing->player) + return; // monsters disallowed from unlocking doors + if (((line->special&TriggerType)==GunOnce) || ((line->special&TriggerType)==GunMany)) + { //jff 4/1/98 check for being a gun type before reporting door type + if (!P_CanUnlockGenDoor(line,thing->player)) + return; + } + else + return; + if (!line->tag) //jff 2/27/98 all gun generalized types require tag + return; + + linefunc = EV_DoGenLockedDoor; + } + else if ((unsigned)line->special >= GenLiftBase) + { + if (!thing->player) + if (!(line->special & LiftMonster)) + return; // monsters disallowed + linefunc = EV_DoGenLift; + } + else if ((unsigned)line->special >= GenStairsBase) + { + if (!thing->player) + if (!(line->special & StairMonster)) + return; // monsters disallowed + if (!line->tag) //jff 2/27/98 all gun generalized types require tag + return; + linefunc = EV_DoGenStairs; + } + else if ((unsigned)line->special >= GenCrusherBase) + { + if (!thing->player) + if (!(line->special & StairMonster)) + return; // monsters disallowed + if (!line->tag) //jff 2/27/98 all gun generalized types require tag + return; + linefunc = EV_DoGenCrusher; + } + + if (linefunc) + switch((line->special & TriggerType) >> TriggerTypeShift) + { + case GunOnce: + if (linefunc(line)) + P_ChangeSwitchTexture(line,0); + return; + case GunMany: + if (linefunc(line)) + P_ChangeSwitchTexture(line,1); + return; + default: // if not a gun type, do nothing here + return; + } + } + + // Impacts that other things can activate. + if (!thing->player) + { + int ok = 0; + switch(line->special) + { + case 46: + // 46 GR Open door on impact weapon is monster activatable + ok = 1; + break; + } + if (!ok) + return; + } + + if (!P_CheckTag(line)) //jff 2/27/98 disallow zero tag on some types + return; + + switch(line->special) + { + case 24: + // 24 G1 raise floor to highest adjacent + if (EV_DoFloor(line,raiseFloor) || demo_compatibility) + P_ChangeSwitchTexture(line,0); + break; + + case 46: + // 46 GR open door, stay open + EV_DoDoor(line,open); + P_ChangeSwitchTexture(line,1); + break; + + case 47: + // 47 G1 raise floor to nearest and change texture and type + if (EV_DoPlat(line,raiseToNearestAndChange,0) || demo_compatibility) + P_ChangeSwitchTexture(line,0); + break; + + //jff 1/30/98 added new gun linedefs here + // killough 1/31/98: added demo_compatibility check, added inner switch + + default: + if (!demo_compatibility) + switch (line->special) + { + case 197: + // Exit to next level + // killough 10/98: prevent zombies from exiting levels + if(thing->player && thing->player->health<=0 && !comp[comp_zombie]) + break; + P_ChangeSwitchTexture(line,0); + G_ExitLevel(); + break; + + case 198: + // Exit to secret level + // killough 10/98: prevent zombies from exiting levels + if(thing->player && thing->player->health<=0 && !comp[comp_zombie]) + break; + P_ChangeSwitchTexture(line,0); + G_SecretExitLevel(); + break; + //jff end addition of new gun linedefs + } + break; + } +} + + +// +// P_PlayerInSpecialSector() +// +// Called every tick frame +// that the player origin is in a special sector +// +// Changed to ignore sector types the engine does not recognize +// +void P_PlayerInSpecialSector (player_t* player) +{ + sector_t* sector; + + sector = player->mo->subsector->sector; + + // Falling, not all the way down yet? + // Sector specials don't apply in mid-air + if (player->mo->z != sector->floorheight) + return; + + // Has hit ground. + //jff add if to handle old vs generalized types + if (sector->special<32) // regular sector specials + { + switch (sector->special) + { + case 5: + // 5/10 unit damage per 31 ticks + if (!player->powers[pw_ironfeet]) + if (!(leveltime&0x1f)) + P_DamageMobj (player->mo, NULL, NULL, 10); + break; + + case 7: + // 2/5 unit damage per 31 ticks + if (!player->powers[pw_ironfeet]) + if (!(leveltime&0x1f)) + P_DamageMobj (player->mo, NULL, NULL, 5); + break; + + case 16: + // 10/20 unit damage per 31 ticks + case 4: + // 10/20 unit damage plus blinking light (light already spawned) + if (!player->powers[pw_ironfeet] + || (P_Random(pr_slimehurt)<5) ) // even with suit, take damage + { + if (!(leveltime&0x1f)) + P_DamageMobj (player->mo, NULL, NULL, 20); + } + break; + + case 9: + // Tally player in secret sector, clear secret special + player->secretcount++; + sector->special = 0; + break; + + case 11: + // Exit on health < 11, take 10/20 damage per 31 ticks + if (comp[comp_god]) /* killough 2/21/98: add compatibility switch */ + player->cheats &= ~CF_GODMODE; // on godmode cheat clearing + // does not affect invulnerability + if (!(leveltime&0x1f)) + P_DamageMobj (player->mo, NULL, NULL, 20); + + if (player->health <= 10) + G_ExitLevel(); + break; + + default: + //jff 1/24/98 Don't exit as DOOM2 did, just ignore + break; + }; + } + else //jff 3/14/98 handle extended sector types for secrets and damage + { + switch ((sector->special&DAMAGE_MASK)>>DAMAGE_SHIFT) + { + case 0: // no damage + break; + case 1: // 2/5 damage per 31 ticks + if (!player->powers[pw_ironfeet]) + if (!(leveltime&0x1f)) + P_DamageMobj (player->mo, NULL, NULL, 5); + break; + case 2: // 5/10 damage per 31 ticks + if (!player->powers[pw_ironfeet]) + if (!(leveltime&0x1f)) + P_DamageMobj (player->mo, NULL, NULL, 10); + break; + case 3: // 10/20 damage per 31 ticks + if (!player->powers[pw_ironfeet] + || (P_Random(pr_slimehurt)<5)) // take damage even with suit + { + if (!(leveltime&0x1f)) + P_DamageMobj (player->mo, NULL, NULL, 20); + } + break; + } + if (sector->special&SECRET_MASK) + { + player->secretcount++; + sector->special &= ~SECRET_MASK; + if (sector->special<32) // if all extended bits clear, + sector->special=0; // sector is not special anymore + } + + // phares 3/19/98: + // + // If FRICTION_MASK or PUSH_MASK is set, we don't care at this + // point, since the code to deal with those situations is + // handled by Thinkers. + + } +} + +// +// P_UpdateSpecials() +// +// Check level timer, frag counter, +// animate flats, scroll walls, +// change button textures +// +// Reads and modifies globals: +// levelTimer, levelTimeCount, +// levelFragLimit, levelFragLimitCount +// + +static boolean levelTimer; +static int levelTimeCount; +boolean levelFragLimit; // Ty 03/18/98 Added -frags support +int levelFragLimitCount; // Ty 03/18/98 Added -frags support + +void P_UpdateSpecials (void) +{ + anim_t* anim; + int pic; + int i; + + // Downcount level timer, exit level if elapsed + if (levelTimer == true) + { + levelTimeCount--; + if (!levelTimeCount) + G_ExitLevel(); + } + + // Check frag counters, if frag limit reached, exit level // Ty 03/18/98 + // Seems like the total frags should be kept in a simple + // array somewhere, but until they are... + if (levelFragLimit == true) // we used -frags so compare count + { + int k,m,fragcount,exitflag=false; + for (k=0;k= levelFragLimitCount) exitflag = true; + if (exitflag == true) break; // skip out of the loop--we're done + } + if (exitflag == true) + G_ExitLevel(); + } + + // Animate flats and textures globally + for (anim = anims ; anim < lastanim ; anim++) + { + for (i=anim->basepic ; ibasepic+anim->numpics ; i++) + { + pic = anim->basepic + ( (leveltime/anim->speed + i)%anim->numpics ); + if (anim->istexture) + texturetranslation[i] = pic; + else + flattranslation[i] = pic; + } + } + + // Check buttons (retriggerable switches) and change texture on timeout + for (i = 0; i < MAXBUTTONS; i++) + if (buttonlist[i].btimer) + { + buttonlist[i].btimer--; + if (!buttonlist[i].btimer) + { + switch(buttonlist[i].where) + { + case top: + sides[buttonlist[i].line->sidenum[0]].toptexture = + buttonlist[i].btexture; + break; + + case middle: + sides[buttonlist[i].line->sidenum[0]].midtexture = + buttonlist[i].btexture; + break; + + case bottom: + sides[buttonlist[i].line->sidenum[0]].bottomtexture = + buttonlist[i].btexture; + break; + } + { + /* don't take the address of the switch's sound origin, + * unless in a compatibility mode. */ + mobj_t *so = (mobj_t *)buttonlist[i].soundorg; + if (comp[comp_sound] || compatibility_level < prboom_6_compatibility) + /* since the buttonlist array is usually zeroed out, + * button popouts generally appear to come from (0,0) */ + so = (mobj_t *)&buttonlist[i].soundorg; + S_StartSound(so, sfx_swtchn); + } + memset(&buttonlist[i],0,sizeof(button_t)); + } + } +} + +////////////////////////////////////////////////////////////////////// +// +// Sector and Line special thinker spawning at level startup +// +////////////////////////////////////////////////////////////////////// + +// +// P_SpawnSpecials +// After the map has been loaded, +// scan for specials that spawn thinkers +// + +// Parses command line parameters. +void P_SpawnSpecials (void) +{ + sector_t* sector; + int i; + int episode; + + episode = 1; + if (W_CheckNumForName("texture2") >= 0) + episode = 2; + + // See if -timer needs to be used. + levelTimer = false; + + i = M_CheckParm("-avg"); // Austin Virtual Gaming 20 min timer on DM play + if (i && deathmatch) + { + levelTimer = true; + levelTimeCount = 20 * 60 * TICRATE; + } + + i = M_CheckParm("-timer"); // user defined timer on game play + if (i && deathmatch) + { + int time; + time = atoi(myargv[i+1]) * 60 * TICRATE; + levelTimer = true; + levelTimeCount = time; + } + + // See if -frags has been used + levelFragLimit = false; + i = M_CheckParm("-frags"); // Ty 03/18/98 Added -frags support + if (i && deathmatch) + { + int frags; + frags = atoi(myargv[i+1]); + if (frags <= 0) frags = 10; // default 10 if no count provided + levelFragLimit = true; + levelFragLimitCount = frags; + } + + + // Init special sectors. + sector = sectors; + for (i=0 ; ispecial) + continue; + + if (sector->special&SECRET_MASK) //jff 3/15/98 count extended + totalsecret++; // secret sectors too + + switch (sector->special&31) + { + case 1: + // random off + P_SpawnLightFlash (sector); + break; + + case 2: + // strobe fast + P_SpawnStrobeFlash(sector,FASTDARK,0); + break; + + case 3: + // strobe slow + P_SpawnStrobeFlash(sector,SLOWDARK,0); + break; + + case 4: + // strobe fast/death slime + P_SpawnStrobeFlash(sector,FASTDARK,0); + sector->special |= 3<special<32) //jff 3/14/98 bits don't count unless not + totalsecret++; // a generalized sector type + break; + + case 10: + // door close in 30 seconds + P_SpawnDoorCloseIn30 (sector); + break; + + case 12: + // sync strobe slow + P_SpawnStrobeFlash (sector, SLOWDARK, 1); + break; + + case 13: + // sync strobe fast + P_SpawnStrobeFlash (sector, FASTDARK, 1); + break; + + case 14: + // door raise in 5 minutes + P_SpawnDoorRaiseIn5Mins (sector, i); + break; + + case 17: + // fire flickering + P_SpawnFireFlicker(sector); + break; + } + } + + P_RemoveAllActiveCeilings(); // jff 2/22/98 use killough's scheme + + P_RemoveAllActivePlats(); // killough + + for (i = 0;i < MAXBUTTONS;i++) + memset(&buttonlist[i],0,sizeof(button_t)); + + // P_InitTagLists() must be called before P_FindSectorFromLineTag() + // or P_FindLineFromLineTag() can be called. + + P_InitTagLists(); // killough 1/30/98: Create xref tables for tags + + P_SpawnScrollers(); // killough 3/7/98: Add generalized scrollers + + P_SpawnFriction(); // phares 3/12/98: New friction model using linedefs + + P_SpawnPushers(); // phares 3/20/98: New pusher model using linedefs + + for (i=0; i= 0;) + sectors[s].heightsec = sec; + break; + + // killough 3/16/98: Add support for setting + // floor lighting independently (e.g. lava) + case 213: + sec = sides[*lines[i].sidenum].sector-sectors; + for (s = -1; (s = P_FindSectorFromLineTag(lines+i,s)) >= 0;) + sectors[s].floorlightsec = sec; + break; + + // killough 4/11/98: Add support for setting + // ceiling lighting independently + case 261: + sec = sides[*lines[i].sidenum].sector-sectors; + for (s = -1; (s = P_FindSectorFromLineTag(lines+i,s)) >= 0;) + sectors[s].ceilinglightsec = sec; + break; + + // killough 10/98: + // + // Support for sky textures being transferred from sidedefs. + // Allows scrolling and other effects (but if scrolling is + // used, then the same sector tag needs to be used for the + // sky sector, the sky-transfer linedef, and the scroll-effect + // linedef). Still requires user to use F_SKY1 for the floor + // or ceiling texture, to distinguish floor and ceiling sky. + + case 271: // Regular sky + case 272: // Same, only flipped + for (s = -1; (s = P_FindSectorFromLineTag(lines+i,s)) >= 0;) + sectors[s].sky = i | PL_SKYFLAT; + break; + } +} + +// killough 2/28/98: +// +// This function, with the help of r_plane.c and r_bsp.c, supports generalized +// scrolling floors and walls, with optional mobj-carrying properties, e.g. +// conveyor belts, rivers, etc. A linedef with a special type affects all +// tagged sectors the same way, by creating scrolling and/or object-carrying +// properties. Multiple linedefs may be used on the same sector and are +// cumulative, although the special case of scrolling a floor and carrying +// things on it, requires only one linedef. The linedef's direction determines +// the scrolling direction, and the linedef's length determines the scrolling +// speed. This was designed so that an edge around the sector could be used to +// control the direction of the sector's scrolling, which is usually what is +// desired. +// +// Process the active scrollers. +// +// This is the main scrolling code +// killough 3/7/98 + +void T_Scroll(scroll_t *s) +{ + fixed_t dx = s->dx, dy = s->dy; + + if (s->control != -1) + { // compute scroll amounts based on a sector's height changes + fixed_t height = sectors[s->control].floorheight + + sectors[s->control].ceilingheight; + fixed_t delta = height - s->last_height; + s->last_height = height; + dx = FixedMul(dx, delta); + dy = FixedMul(dy, delta); + } + + // killough 3/14/98: Add acceleration + if (s->accel) + { + s->vdx = dx += s->vdx; + s->vdy = dy += s->vdy; + } + + if (!(dx | dy)) // no-op if both (x,y) offsets 0 + return; + + switch (s->type) + { + side_t *side; + sector_t *sec; + fixed_t height, waterheight; // killough 4/4/98: add waterheight + msecnode_t *node; + mobj_t *thing; + + case sc_side: // killough 3/7/98: Scroll wall texture + side = sides + s->affectee; + side->textureoffset += dx; + side->rowoffset += dy; + break; + + case sc_floor: // killough 3/7/98: Scroll floor texture + sec = sectors + s->affectee; + sec->floor_xoffs += dx; + sec->floor_yoffs += dy; + break; + + case sc_ceiling: // killough 3/7/98: Scroll ceiling texture + sec = sectors + s->affectee; + sec->ceiling_xoffs += dx; + sec->ceiling_yoffs += dy; + break; + + case sc_carry: + + // killough 3/7/98: Carry things on floor + // killough 3/20/98: use new sector list which reflects true members + // killough 3/27/98: fix carrier bug + // killough 4/4/98: Underwater, carry things even w/o gravity + + sec = sectors + s->affectee; + height = sec->floorheight; + waterheight = sec->heightsec != -1 && + sectors[sec->heightsec].floorheight > height ? + sectors[sec->heightsec].floorheight : INT_MIN; + + for (node = sec->touching_thinglist; node; node = node->m_snext) + if (!((thing = node->m_thing)->flags & MF_NOCLIP) && + (!(thing->flags & MF_NOGRAVITY || thing->z > height) || + thing->z < waterheight)) + { + // Move objects only if on floor or underwater, + // non-floating, and clipped. + thing->momx += dx; + thing->momy += dy; + } + break; + + case sc_carry_ceiling: // to be added later + break; + } +} + +// +// Add_Scroller() +// +// Add a generalized scroller to the thinker list. +// +// type: the enumerated type of scrolling: floor, ceiling, floor carrier, +// wall, floor carrier & scroller +// +// (dx,dy): the direction and speed of the scrolling or its acceleration +// +// control: the sector whose heights control this scroller's effect +// remotely, or -1 if no control sector +// +// affectee: the index of the affected object (sector or sidedef) +// +// accel: non-zero if this is an accelerative effect +// + +static void Add_Scroller(int type, fixed_t dx, fixed_t dy, + int control, int affectee, int accel) +{ + scroll_t *s = Z_Malloc(sizeof *s, PU_LEVSPEC, 0); + s->thinker.function = T_Scroll; + s->type = type; + s->dx = dx; + s->dy = dy; + s->accel = accel; + s->vdx = s->vdy = 0; + if ((s->control = control) != -1) + s->last_height = + sectors[control].floorheight + sectors[control].ceilingheight; + s->affectee = affectee; + P_AddThinker(&s->thinker); +} + +// Adds wall scroller. Scroll amount is rotated with respect to wall's +// linedef first, so that scrolling towards the wall in a perpendicular +// direction is translated into vertical motion, while scrolling along +// the wall in a parallel direction is translated into horizontal motion. +// +// killough 5/25/98: cleaned up arithmetic to avoid drift due to roundoff +// +// killough 10/98: +// fix scrolling aliasing problems, caused by long linedefs causing overflowing + +static void Add_WallScroller(fixed_t dx, fixed_t dy, const line_t *l, + int control, int accel) +{ + fixed_t x = D_abs(l->dx), y = D_abs(l->dy), d; + if (y > x) + d = x, x = y, y = d; + d = FixedDiv(x, finesine[(tantoangle[FixedDiv(y,x) >> DBITS] + ANG90) + >> ANGLETOFINESHIFT]); + + // CPhipps - Import scroller calc overflow fix, compatibility optioned + if (compatibility_level >= lxdoom_1_compatibility) { + x = (fixed_t)(((int_64_t)dy * -(int_64_t)l->dy - (int_64_t)dx * (int_64_t)l->dx) / (int_64_t)d); // killough 10/98: + y = (fixed_t)(((int_64_t)dy * (int_64_t)l->dx - (int_64_t)dx * (int_64_t)l->dy) / (int_64_t)d); // Use long long arithmetic + } else { + x = -FixedDiv(FixedMul(dy, l->dy) + FixedMul(dx, l->dx), d); + y = -FixedDiv(FixedMul(dx, l->dy) - FixedMul(dy, l->dx), d); + } + Add_Scroller(sc_side, x, y, control, *l->sidenum, accel); +} + +// Amount (dx,dy) vector linedef is shifted right to get scroll amount +#define SCROLL_SHIFT 5 + +// Factor to scale scrolling effect into mobj-carrying properties = 3/32. +// (This is so scrolling floors and objects on them can move at same speed.) +#define CARRYFACTOR ((fixed_t)(FRACUNIT*.09375)) + +// Initialize the scrollers +static void P_SpawnScrollers(void) +{ + int i; + line_t *l = lines; + + for (i=0;idx >> SCROLL_SHIFT; // direction and speed of scrolling + fixed_t dy = l->dy >> SCROLL_SHIFT; + int control = -1, accel = 0; // no control sector or acceleration + int special = l->special; + + // killough 3/7/98: Types 245-249 are same as 250-254 except that the + // first side's sector's heights cause scrolling when they change, and + // this linedef controls the direction and speed of the scrolling. The + // most complicated linedef since donuts, but powerful :) + // + // killough 3/15/98: Add acceleration. Types 214-218 are the same but + // are accelerative. + + if (special >= 245 && special <= 249) // displacement scrollers + { + special += 250-245; + control = sides[*l->sidenum].sector - sectors; + } + else + if (special >= 214 && special <= 218) // accelerative scrollers + { + accel = 1; + special += 250-214; + control = sides[*l->sidenum].sector - sectors; + } + + switch (special) + { + register int s; + + case 250: // scroll effect ceiling + for (s=-1; (s = P_FindSectorFromLineTag(l,s)) >= 0;) + Add_Scroller(sc_ceiling, -dx, dy, control, s, accel); + break; + + case 251: // scroll effect floor + case 253: // scroll and carry objects on floor + for (s=-1; (s = P_FindSectorFromLineTag(l,s)) >= 0;) + Add_Scroller(sc_floor, -dx, dy, control, s, accel); + if (special != 253) + break; + + case 252: // carry objects on floor + dx = FixedMul(dx,CARRYFACTOR); + dy = FixedMul(dy,CARRYFACTOR); + for (s=-1; (s = P_FindSectorFromLineTag(l,s)) >= 0;) + Add_Scroller(sc_carry, dx, dy, control, s, accel); + break; + + // killough 3/1/98: scroll wall according to linedef + // (same direction and speed as scrolling floors) + case 254: + for (s=-1; (s = P_FindLineFromLineTag(l,s)) >= 0;) + if (s != i) + Add_WallScroller(dx, dy, lines+s, control, accel); + break; + + case 255: // killough 3/2/98: scroll according to sidedef offsets + s = lines[i].sidenum[0]; + Add_Scroller(sc_side, -sides[s].textureoffset, + sides[s].rowoffset, -1, s, accel); + break; + + case 48: // scroll first side + Add_Scroller(sc_side, FRACUNIT, 0, -1, lines[i].sidenum[0], accel); + break; + + case 85: // jff 1/30/98 2-way scroll + Add_Scroller(sc_side, -FRACUNIT, 0, -1, lines[i].sidenum[0], accel); + break; + } + } +} + +// e6y +// restored boom's friction code + +///////////////////////////// +// +// Add a friction thinker to the thinker list +// +// Add_Friction adds a new friction thinker to the list of active thinkers. +// + +static void Add_Friction(int friction, int movefactor, int affectee) + { + friction_t *f = Z_Malloc(sizeof *f, PU_LEVSPEC, 0); + + f->thinker.function/*.acp1*/ = /*(actionf_p1) */T_Friction; + f->friction = friction; + f->movefactor = movefactor; + f->affectee = affectee; + P_AddThinker(&f->thinker); + } + +///////////////////////////// +// +// This is where abnormal friction is applied to objects in the sectors. +// A friction thinker has been spawned for each sector where less or +// more friction should be applied. The amount applied is proportional to +// the length of the controlling linedef. + +void T_Friction(friction_t *f) + { + sector_t *sec; + mobj_t *thing; + msecnode_t* node; + + if (compatibility || !variable_friction) + return; + + sec = sectors + f->affectee; + + // Be sure the special sector type is still turned on. If so, proceed. + // Else, bail out; the sector type has been changed on us. + + if (!(sec->special & FRICTION_MASK)) + return; + + // Assign the friction value to players on the floor, non-floating, + // and clipped. Normally the object's friction value is kept at + // ORIG_FRICTION and this thinker changes it for icy or muddy floors. + + // In Phase II, you can apply friction to Things other than players. + + // When the object is straddling sectors with the same + // floorheight that have different frictions, use the lowest + // friction value (muddy has precedence over icy). + + node = sec->touching_thinglist; // things touching this sector + while (node) + { + thing = node->m_thing; + if (thing->player && + !(thing->flags & (MF_NOGRAVITY | MF_NOCLIP)) && + thing->z <= sec->floorheight) + { + if ((thing->friction == ORIG_FRICTION) || // normal friction? + (f->friction < thing->friction)) + { + thing->friction = f->friction; + thing->movefactor = f->movefactor; + } + } + node = node->m_snext; + } + } + + +// killough 3/7/98 -- end generalized scroll effects + +//////////////////////////////////////////////////////////////////////////// +// +// FRICTION EFFECTS +// +// phares 3/12/98: Start of friction effects +// +// As the player moves, friction is applied by decreasing the x and y +// momentum values on each tic. By varying the percentage of decrease, +// we can simulate muddy or icy conditions. In mud, the player slows +// down faster. In ice, the player slows down more slowly. +// +// The amount of friction change is controlled by the length of a linedef +// with type 223. A length < 100 gives you mud. A length > 100 gives you ice. +// +// Also, each sector where these effects are to take place is given a +// new special type _______. Changing the type value at runtime allows +// these effects to be turned on or off. +// +// Sector boundaries present problems. The player should experience these +// friction changes only when his feet are touching the sector floor. At +// sector boundaries where floor height changes, the player can find +// himself still 'in' one sector, but with his feet at the floor level +// of the next sector (steps up or down). To handle this, Thinkers are used +// in icy/muddy sectors. These thinkers examine each object that is touching +// their sectors, looking for players whose feet are at the same level as +// their floors. Players satisfying this condition are given new friction +// values that are applied by the player movement code later. +// +// killough 8/28/98: +// +// Completely redid code, which did not need thinkers, and which put a heavy +// drag on CPU. Friction is now a property of sectors, NOT objects inside +// them. All objects, not just players, are affected by it, if they touch +// the sector's floor. Code simpler and faster, only calling on friction +// calculations when an object needs friction considered, instead of doing +// friction calculations on every sector during every tic. +// +// Although this -might- ruin Boom demo sync involving friction, it's the only +// way, short of code explosion, to fix the original design bug. Fixing the +// design bug in Boom's original friction code, while maintaining demo sync +// under every conceivable circumstance, would double or triple code size, and +// would require maintenance of buggy legacy code which is only useful for old +// demos. Doom demos, which are more important IMO, are not affected by this +// change. +// +///////////////////////////// +// +// Initialize the sectors where friction is increased or decreased + +static void P_SpawnFriction(void) +{ + int i; + line_t *l = lines; + + // killough 8/28/98: initialize all sectors to normal friction first + for (i = 0; i < numsectors; i++) + { + sectors[i].friction = ORIG_FRICTION; + sectors[i].movefactor = ORIG_FRICTION_FACTOR; + } + + for (i = 0 ; i < numlines ; i++,l++) + if (l->special == 223) + { + int length = P_AproxDistance(l->dx,l->dy)>>FRACBITS; + int friction = (0x1EB8*length)/0x80 + 0xD000; + int movefactor, s; + + // The following check might seem odd. At the time of movement, + // the move distance is multiplied by 'friction/0x10000', so a + // higher friction value actually means 'less friction'. + + if (friction > ORIG_FRICTION) // ice + movefactor = ((0x10092 - friction)*(0x70))/0x158; + else + movefactor = ((friction - 0xDB34)*(0xA))/0x80; + + if (mbf_features) + { // killough 8/28/98: prevent odd situations + if (friction > FRACUNIT) + friction = FRACUNIT; + if (friction < 0) + friction = 0; + if (movefactor < 32) + movefactor = 32; + } + + for (s = -1; (s = P_FindSectorFromLineTag(l,s)) >= 0 ; ) + { + // killough 8/28/98: + // + // Instead of spawning thinkers, which are slow and expensive, + // modify the sector's own friction values. Friction should be + // a property of sectors, not objects which reside inside them. + // Original code scanned every object in every friction sector + // on every tic, adjusting its friction, putting unnecessary + // drag on CPU. New code adjusts friction of sector only once + // at level startup, and then uses this friction value. + + //e6y: boom's friction code for boom compatibility + if (!demo_compatibility && !mbf_features) + Add_Friction(friction,movefactor,s); + + sectors[s].friction = friction; + sectors[s].movefactor = movefactor; + } + } +} + +// +// phares 3/12/98: End of friction effects +// +//////////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////////// +// +// PUSH/PULL EFFECT +// +// phares 3/20/98: Start of push/pull effects +// +// This is where push/pull effects are applied to objects in the sectors. +// +// There are four kinds of push effects +// +// 1) Pushing Away +// +// Pushes you away from a point source defined by the location of an +// MT_PUSH Thing. The force decreases linearly with distance from the +// source. This force crosses sector boundaries and is felt w/in a circle +// whose center is at the MT_PUSH. The force is felt only if the point +// MT_PUSH can see the target object. +// +// 2) Pulling toward +// +// Same as Pushing Away except you're pulled toward an MT_PULL point +// source. This force crosses sector boundaries and is felt w/in a circle +// whose center is at the MT_PULL. The force is felt only if the point +// MT_PULL can see the target object. +// +// 3) Wind +// +// Pushes you in a constant direction. Full force above ground, half +// force on the ground, nothing if you're below it (water). +// +// 4) Current +// +// Pushes you in a constant direction. No force above ground, full +// force if on the ground or below it (water). +// +// The magnitude of the force is controlled by the length of a controlling +// linedef. The force vector for types 3 & 4 is determined by the angle +// of the linedef, and is constant. +// +// For each sector where these effects occur, the sector special type has +// to have the PUSH_MASK bit set. If this bit is turned off by a switch +// at run-time, the effect will not occur. The controlling sector for +// types 1 & 2 is the sector containing the MT_PUSH/MT_PULL Thing. + + +#define PUSH_FACTOR 7 + +///////////////////////////// +// +// Add a push thinker to the thinker list + +static void Add_Pusher(int type, int x_mag, int y_mag, mobj_t* source, int affectee) + { + pusher_t *p = Z_Malloc(sizeof *p, PU_LEVSPEC, 0); + + p->thinker.function = T_Pusher; + p->source = source; + p->type = type; + p->x_mag = x_mag>>FRACBITS; + p->y_mag = y_mag>>FRACBITS; + p->magnitude = P_AproxDistance(p->x_mag,p->y_mag); + if (source) // point source exist? + { + p->radius = (p->magnitude)<<(FRACBITS+1); // where force goes to zero + p->x = p->source->x; + p->y = p->source->y; + } + p->affectee = affectee; + P_AddThinker(&p->thinker); + } + +///////////////////////////// +// +// PIT_PushThing determines the angle and magnitude of the effect. +// The object's x and y momentum values are changed. +// +// tmpusher belongs to the point source (MT_PUSH/MT_PULL). +// +// killough 10/98: allow to affect things besides players + +pusher_t* tmpusher; // pusher structure for blockmap searches + +static boolean PIT_PushThing(mobj_t* thing) +{ + /* killough 10/98: made more general */ + if (!mbf_features ? + thing->player && !(thing->flags & (MF_NOCLIP | MF_NOGRAVITY)) : + (sentient(thing) || thing->flags & MF_SHOOTABLE) && + !(thing->flags & MF_NOCLIP)) + { + angle_t pushangle; + fixed_t speed; + fixed_t sx = tmpusher->x; + fixed_t sy = tmpusher->y; + + speed = (tmpusher->magnitude - + ((P_AproxDistance(thing->x - sx,thing->y - sy) + >>FRACBITS)>>1))<<(FRACBITS-PUSH_FACTOR-1); + + // killough 10/98: make magnitude decrease with square + // of distance, making it more in line with real nature, + // so long as it's still in range with original formula. + // + // Removes angular distortion, and makes effort required + // to stay close to source, grow increasingly hard as you + // get closer, as expected. Still, it doesn't consider z :( + + if (speed > 0 && mbf_features) + { + int x = (thing->x-sx) >> FRACBITS; + int y = (thing->y-sy) >> FRACBITS; + speed = (int)(((uint_64_t) tmpusher->magnitude << 23) / (x*x+y*y+1)); + } + + // If speed <= 0, you're outside the effective radius. You also have + // to be able to see the push/pull source point. + + if (speed > 0 && P_CheckSight(thing,tmpusher->source)) + { + pushangle = R_PointToAngle2(thing->x,thing->y,sx,sy); + if (tmpusher->source->type == MT_PUSH) + pushangle += ANG180; // away + pushangle >>= ANGLETOFINESHIFT; + thing->momx += FixedMul(speed,finecosine[pushangle]); + thing->momy += FixedMul(speed,finesine[pushangle]); + } + } + return true; +} + +///////////////////////////// +// +// T_Pusher looks for all objects that are inside the radius of +// the effect. +// + +void T_Pusher(pusher_t *p) + { + sector_t *sec; + mobj_t *thing; + msecnode_t* node; + int xspeed,yspeed; + int xl,xh,yl,yh,bx,by; + int radius; + int ht = 0; + + if (!allow_pushers) + return; + + sec = sectors + p->affectee; + + // Be sure the special sector type is still turned on. If so, proceed. + // Else, bail out; the sector type has been changed on us. + + if (!(sec->special & PUSH_MASK)) + return; + + // For constant pushers (wind/current) there are 3 situations: + // + // 1) Affected Thing is above the floor. + // + // Apply the full force if wind, no force if current. + // + // 2) Affected Thing is on the ground. + // + // Apply half force if wind, full force if current. + // + // 3) Affected Thing is below the ground (underwater effect). + // + // Apply no force if wind, full force if current. + + if (p->type == p_push) + { + + // Seek out all pushable things within the force radius of this + // point pusher. Crosses sectors, so use blockmap. + + tmpusher = p; // MT_PUSH/MT_PULL point source + radius = p->radius; // where force goes to zero + tmbbox[BOXTOP] = p->y + radius; + tmbbox[BOXBOTTOM] = p->y - radius; + tmbbox[BOXRIGHT] = p->x + radius; + tmbbox[BOXLEFT] = p->x - radius; + + xl = (tmbbox[BOXLEFT] - bmaporgx - MAXRADIUS)>>MAPBLOCKSHIFT; + xh = (tmbbox[BOXRIGHT] - bmaporgx + MAXRADIUS)>>MAPBLOCKSHIFT; + yl = (tmbbox[BOXBOTTOM] - bmaporgy - MAXRADIUS)>>MAPBLOCKSHIFT; + yh = (tmbbox[BOXTOP] - bmaporgy + MAXRADIUS)>>MAPBLOCKSHIFT; + for (bx=xl ; bx<=xh ; bx++) + for (by=yl ; by<=yh ; by++) + P_BlockThingsIterator(bx,by,PIT_PushThing); + return; + } + + // constant pushers p_wind and p_current + + if (sec->heightsec != -1) // special water sector? + ht = sectors[sec->heightsec].floorheight; + node = sec->touching_thinglist; // things touching this sector + for ( ; node ; node = node->m_snext) + { + thing = node->m_thing; + if (!thing->player || (thing->flags & (MF_NOGRAVITY | MF_NOCLIP))) + continue; + if (p->type == p_wind) + { + if (sec->heightsec == -1) // NOT special water sector + if (thing->z > thing->floorz) // above ground + { + xspeed = p->x_mag; // full force + yspeed = p->y_mag; + } + else // on ground + { + xspeed = (p->x_mag)>>1; // half force + yspeed = (p->y_mag)>>1; + } + else // special water sector + { + if (thing->z > ht) // above ground + { + xspeed = p->x_mag; // full force + yspeed = p->y_mag; + } + else if (thing->player->viewz < ht) // underwater + xspeed = yspeed = 0; // no force + else // wading in water + { + xspeed = (p->x_mag)>>1; // half force + yspeed = (p->y_mag)>>1; + } + } + } + else // p_current + { + if (sec->heightsec == -1) // NOT special water sector + if (thing->z > sec->floorheight) // above ground + xspeed = yspeed = 0; // no force + else // on ground + { + xspeed = p->x_mag; // full force + yspeed = p->y_mag; + } + else // special water sector + if (thing->z > ht) // above ground + xspeed = yspeed = 0; // no force + else // underwater + { + xspeed = p->x_mag; // full force + yspeed = p->y_mag; + } + } + thing->momx += xspeed<<(FRACBITS-PUSH_FACTOR); + thing->momy += yspeed<<(FRACBITS-PUSH_FACTOR); + } + } + +///////////////////////////// +// +// P_GetPushThing() returns a pointer to an MT_PUSH or MT_PULL thing, +// NULL otherwise. + +mobj_t* P_GetPushThing(int s) + { + mobj_t* thing; + sector_t* sec; + + sec = sectors + s; + thing = sec->thinglist; + while (thing) + { + switch(thing->type) + { + case MT_PUSH: + case MT_PULL: + return thing; + default: + break; + } + thing = thing->snext; + } + return NULL; + } + +///////////////////////////// +// +// Initialize the sectors where pushers are present +// + +static void P_SpawnPushers(void) + { + int i; + line_t *l = lines; + register int s; + mobj_t* thing; + + for (i = 0 ; i < numlines ; i++,l++) + switch(l->special) + { + case 224: // wind + for (s = -1; (s = P_FindSectorFromLineTag(l,s)) >= 0 ; ) + Add_Pusher(p_wind,l->dx,l->dy,NULL,s); + break; + case 225: // current + for (s = -1; (s = P_FindSectorFromLineTag(l,s)) >= 0 ; ) + Add_Pusher(p_current,l->dx,l->dy,NULL,s); + break; + case 226: // push/pull + for (s = -1; (s = P_FindSectorFromLineTag(l,s)) >= 0 ; ) + { + thing = P_GetPushThing(s); + if (thing) // No MT_P* means no effect + Add_Pusher(p_push,l->dx,l->dy,thing,s); + } + break; + } + } + +// +// phares 3/20/98: End of Pusher effects +// +//////////////////////////////////////////////////////////////////////////// diff --git a/src/p_spec.h b/src/p_spec.h new file mode 100644 index 0000000..1d5aa2b --- /dev/null +++ b/src/p_spec.h @@ -0,0 +1,1141 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: definitions, declarations and prototypes for specials + * + *-----------------------------------------------------------------------------*/ + +#ifndef __P_SPEC__ +#define __P_SPEC__ + +#include "r_defs.h" +#include "d_player.h" + +// Define values for map objects +#define MO_TELEPORTMAN 14 + +// p_floor + +#define ELEVATORSPEED (FRACUNIT*4) +#define FLOORSPEED FRACUNIT + +// p_ceilng + +#define CEILSPEED FRACUNIT +#define CEILWAIT 150 + +// p_doors + +#define VDOORSPEED (FRACUNIT*2) +#define VDOORWAIT 150 + +// p_plats + +#define PLATWAIT 3 +#define PLATSPEED FRACUNIT + +// p_switch + +// 4 players, 4 buttons each at once, max. +// killough 2/14/98: redefine in terms of MAXPLAYERS +#define MAXBUTTONS (MAXPLAYERS*4) + +// 1 second, in ticks. +#define BUTTONTIME TICRATE + +// p_lights + +#define GLOWSPEED 8 +#define STROBEBRIGHT 5 +#define FASTDARK 15 +#define SLOWDARK 35 + +//jff 3/14/98 add bits and shifts for generalized sector types + +#define DAMAGE_MASK 0x60 +#define DAMAGE_SHIFT 5 +#define SECRET_MASK 0x80 +#define SECRET_SHIFT 7 +#define FRICTION_MASK 0x100 +#define FRICTION_SHIFT 8 +#define PUSH_MASK 0x200 +#define PUSH_SHIFT 9 + +//jff 02/04/98 Define masks, shifts, for fields in +// generalized linedef types + +#define GenEnd 0x8000 +#define GenFloorBase 0x6000 +#define GenCeilingBase 0x4000 +#define GenDoorBase 0x3c00 +#define GenLockedBase 0x3800 +#define GenLiftBase 0x3400 +#define GenStairsBase 0x3000 +#define GenCrusherBase 0x2F80 + +#define TriggerType 0x0007 +#define TriggerTypeShift 0 + +// define masks and shifts for the floor type fields + +#define FloorCrush 0x1000 +#define FloorChange 0x0c00 +#define FloorTarget 0x0380 +#define FloorDirection 0x0040 +#define FloorModel 0x0020 +#define FloorSpeed 0x0018 + +#define FloorCrushShift 12 +#define FloorChangeShift 10 +#define FloorTargetShift 7 +#define FloorDirectionShift 6 +#define FloorModelShift 5 +#define FloorSpeedShift 3 + +// define masks and shifts for the ceiling type fields + +#define CeilingCrush 0x1000 +#define CeilingChange 0x0c00 +#define CeilingTarget 0x0380 +#define CeilingDirection 0x0040 +#define CeilingModel 0x0020 +#define CeilingSpeed 0x0018 + +#define CeilingCrushShift 12 +#define CeilingChangeShift 10 +#define CeilingTargetShift 7 +#define CeilingDirectionShift 6 +#define CeilingModelShift 5 +#define CeilingSpeedShift 3 + +// define masks and shifts for the lift type fields + +#define LiftTarget 0x0300 +#define LiftDelay 0x00c0 +#define LiftMonster 0x0020 +#define LiftSpeed 0x0018 + +#define LiftTargetShift 8 +#define LiftDelayShift 6 +#define LiftMonsterShift 5 +#define LiftSpeedShift 3 + +// define masks and shifts for the stairs type fields + +#define StairIgnore 0x0200 +#define StairDirection 0x0100 +#define StairStep 0x00c0 +#define StairMonster 0x0020 +#define StairSpeed 0x0018 + +#define StairIgnoreShift 9 +#define StairDirectionShift 8 +#define StairStepShift 6 +#define StairMonsterShift 5 +#define StairSpeedShift 3 + +// define masks and shifts for the crusher type fields + +#define CrusherSilent 0x0040 +#define CrusherMonster 0x0020 +#define CrusherSpeed 0x0018 + +#define CrusherSilentShift 6 +#define CrusherMonsterShift 5 +#define CrusherSpeedShift 3 + +// define masks and shifts for the door type fields + +#define DoorDelay 0x0300 +#define DoorMonster 0x0080 +#define DoorKind 0x0060 +#define DoorSpeed 0x0018 + +#define DoorDelayShift 8 +#define DoorMonsterShift 7 +#define DoorKindShift 5 +#define DoorSpeedShift 3 + +// define masks and shifts for the locked door type fields + +#define LockedNKeys 0x0200 +#define LockedKey 0x01c0 +#define LockedKind 0x0020 +#define LockedSpeed 0x0018 + +#define LockedNKeysShift 9 +#define LockedKeyShift 6 +#define LockedKindShift 5 +#define LockedSpeedShift 3 + +// define names for the TriggerType field of the general linedefs + +typedef enum +{ + WalkOnce, + WalkMany, + SwitchOnce, + SwitchMany, + GunOnce, + GunMany, + PushOnce, + PushMany, +} triggertype_e; + +// define names for the Speed field of the general linedefs + +typedef enum +{ + SpeedSlow, + SpeedNormal, + SpeedFast, + SpeedTurbo, +} motionspeed_e; + +// define names for the Target field of the general floor + +typedef enum +{ + FtoHnF, + FtoLnF, + FtoNnF, + FtoLnC, + FtoC, + FbyST, + Fby24, + Fby32, +} floortarget_e; + +// define names for the Changer Type field of the general floor + +typedef enum +{ + FNoChg, + FChgZero, + FChgTxt, + FChgTyp, +} floorchange_e; + +// define names for the Change Model field of the general floor + +typedef enum +{ + FTriggerModel, + FNumericModel, +} floormodel_t; + +// define names for the Target field of the general ceiling + +typedef enum +{ + CtoHnC, + CtoLnC, + CtoNnC, + CtoHnF, + CtoF, + CbyST, + Cby24, + Cby32, +} ceilingtarget_e; + +// define names for the Changer Type field of the general ceiling + +typedef enum +{ + CNoChg, + CChgZero, + CChgTxt, + CChgTyp, +} ceilingchange_e; + +// define names for the Change Model field of the general ceiling + +typedef enum +{ + CTriggerModel, + CNumericModel, +} ceilingmodel_t; + +// define names for the Target field of the general lift + +typedef enum +{ + F2LnF, + F2NnF, + F2LnC, + LnF2HnF, +} lifttarget_e; + +// define names for the door Kind field of the general ceiling + +typedef enum +{ + OdCDoor, + ODoor, + CdODoor, + CDoor, +} doorkind_e; + +// define names for the locked door Kind field of the general ceiling + +typedef enum +{ + AnyKey, + RCard, + BCard, + YCard, + RSkull, + BSkull, + YSkull, + AllKeys, +} keykind_e; + +////////////////////////////////////////////////////////////////// +// +// enums for classes of linedef triggers +// +////////////////////////////////////////////////////////////////// + +//jff 2/23/98 identify the special classes that can share sectors + +typedef enum +{ + floor_special, + ceiling_special, + lighting_special, +} special_e; + +//jff 3/15/98 pure texture/type change for better generalized support +typedef enum +{ + trigChangeOnly, + numChangeOnly, +} change_e; + +// p_plats + +typedef enum +{ + up, + down, + waiting, + in_stasis +} plat_e; + +typedef enum +{ + perpetualRaise, + downWaitUpStay, + raiseAndChange, + raiseToNearestAndChange, + blazeDWUS, + genLift, //jff added to support generalized Plat types + genPerpetual, + toggleUpDn, //jff 3/14/98 added to support instant toggle type + +} plattype_e; + +// p_doors + +typedef enum +{ + normal, + close30ThenOpen, + close, + open, + raiseIn5Mins, + blazeRaise, + blazeOpen, + blazeClose, + + //jff 02/05/98 add generalize door types + genRaise, + genBlazeRaise, + genOpen, + genBlazeOpen, + genClose, + genBlazeClose, + genCdO, + genBlazeCdO, +} vldoor_e; + +// p_ceilng + +typedef enum +{ + lowerToFloor, + raiseToHighest, + lowerToLowest, + lowerToMaxFloor, + lowerAndCrush, + crushAndRaise, + fastCrushAndRaise, + silentCrushAndRaise, + + //jff 02/04/98 add types for generalized ceiling mover + genCeiling, + genCeilingChg, + genCeilingChg0, + genCeilingChgT, + + //jff 02/05/98 add types for generalized ceiling mover + genCrusher, + genSilentCrusher, + +} ceiling_e; + +// p_floor + +typedef enum +{ + // lower floor to highest surrounding floor + lowerFloor, + + // lower floor to lowest surrounding floor + lowerFloorToLowest, + + // lower floor to highest surrounding floor VERY FAST + turboLower, + + // raise floor to lowest surrounding CEILING + raiseFloor, + + // raise floor to next highest surrounding floor + raiseFloorToNearest, + + //jff 02/03/98 lower floor to next lowest neighbor + lowerFloorToNearest, + + //jff 02/03/98 lower floor 24 absolute + lowerFloor24, + + //jff 02/03/98 lower floor 32 absolute + lowerFloor32Turbo, + + // raise floor to shortest height texture around it + raiseToTexture, + + // lower floor to lowest surrounding floor + // and change floorpic + lowerAndChange, + + raiseFloor24, + + //jff 02/03/98 raise floor 32 absolute + raiseFloor32Turbo, + + raiseFloor24AndChange, + raiseFloorCrush, + + // raise to next highest floor, turbo-speed + raiseFloorTurbo, + donutRaise, + raiseFloor512, + + //jff 02/04/98 add types for generalized floor mover + genFloor, + genFloorChg, + genFloorChg0, + genFloorChgT, + + //new types for stair builders + buildStair, + genBuildStair, +} floor_e; + +typedef enum +{ + build8, // slowly build by 8 + turbo16 // quickly build by 16 + +} stair_e; + +typedef enum +{ + elevateUp, + elevateDown, + elevateCurrent, +} elevator_e; + +////////////////////////////////////////////////////////////////// +// +// general enums +// +////////////////////////////////////////////////////////////////// + +// texture type enum +typedef enum +{ + top, + middle, + bottom + +} bwhere_e; + +// crush check returns +typedef enum +{ + ok, + crushed, + pastdest +} result_e; + +////////////////////////////////////////////////////////////////// +// +// linedef and sector special data types +// +////////////////////////////////////////////////////////////////// + +// p_switch + +// switch animation structure type + +#if defined(__MWERKS__) +#pragma options align=packed +#endif + +typedef struct +{ + char name1[9]; + char name2[9]; + short episode; +} PACKEDATTR switchlist_t; //jff 3/23/98 pack to read from memory + +#if defined(__MWERKS__) +#pragma options align=reset +#endif + +typedef struct +{ + line_t* line; + bwhere_e where; + int btexture; + int btimer; + mobj_t* soundorg; + +} button_t; + +// p_lights + +typedef struct +{ + thinker_t thinker; + sector_t* sector; + int count; + int maxlight; + int minlight; + +} fireflicker_t; + +typedef struct +{ + thinker_t thinker; + sector_t* sector; + int count; + int maxlight; + int minlight; + int maxtime; + int mintime; + +} lightflash_t; + +typedef struct +{ + thinker_t thinker; + sector_t* sector; + int count; + int minlight; + int maxlight; + int darktime; + int brighttime; + +} strobe_t; + +typedef struct +{ + thinker_t thinker; + sector_t* sector; + int minlight; + int maxlight; + int direction; + +} glow_t; + +// p_plats + +typedef struct +{ + thinker_t thinker; + sector_t* sector; + fixed_t speed; + fixed_t low; + fixed_t high; + int wait; + int count; + plat_e status; + plat_e oldstatus; + boolean crush; + int tag; + plattype_e type; + + struct platlist *list; // killough +} plat_t; + +// New limit-free plat structure -- killough + +typedef struct platlist { + plat_t *plat; + struct platlist *next,**prev; +} platlist_t; + +// p_ceilng + +typedef struct +{ + thinker_t thinker; + vldoor_e type; + sector_t* sector; + fixed_t topheight; + fixed_t speed; + + // 1 = up, 0 = waiting at top, -1 = down + int direction; + + // tics to wait at the top + int topwait; + // (keep in case a door going down is reset) + // when it reaches 0, start going down + int topcountdown; + + //jff 1/31/98 keep track of line door is triggered by + line_t *line; + + /* killough 10/98: sector tag for gradual lighting effects */ + int lighttag; +} vldoor_t; + +// p_doors + +typedef struct +{ + thinker_t thinker; + ceiling_e type; + sector_t* sector; + fixed_t bottomheight; + fixed_t topheight; + fixed_t speed; + fixed_t oldspeed; + boolean crush; + + //jff 02/04/98 add these to support ceiling changers + int newspecial; + int oldspecial; //jff 3/14/98 add to fix bug in change transfers + short texture; + + // 1 = up, 0 = waiting, -1 = down + int direction; + + // ID + int tag; + int olddirection; + struct ceilinglist *list; // jff 2/22/98 copied from killough's plats +} ceiling_t; + +typedef struct ceilinglist { + ceiling_t *ceiling; + struct ceilinglist *next,**prev; +} ceilinglist_t; + +// p_floor + +typedef struct +{ + thinker_t thinker; + floor_e type; + boolean crush; + sector_t* sector; + int direction; + int newspecial; + int oldspecial; //jff 3/14/98 add to fix bug in change transfers + short texture; + fixed_t floordestheight; + fixed_t speed; + +} floormove_t; + +typedef struct +{ + thinker_t thinker; + elevator_e type; + sector_t* sector; + int direction; + fixed_t floordestheight; + fixed_t ceilingdestheight; + fixed_t speed; +} elevator_t; + +// p_spec + +// killough 3/7/98: Add generalized scroll effects + +typedef struct { + thinker_t thinker; // Thinker structure for scrolling + fixed_t dx, dy; // (dx,dy) scroll speeds + int affectee; // Number of affected sidedef, sector, tag, or whatever + int control; // Control sector (-1 if none) used to control scrolling + fixed_t last_height; // Last known height of control sector + fixed_t vdx, vdy; // Accumulated velocity if accelerative + int accel; // Whether it's accelerative + enum + { + sc_side, + sc_floor, + sc_ceiling, + sc_carry, + sc_carry_ceiling, // killough 4/11/98: carry objects hanging on ceilings + } type; // Type of scroll effect +} scroll_t; + +// phares 3/12/98: added new model of friction for ice/sludge effects + +typedef struct { + thinker_t thinker; // Thinker structure for friction + int friction; // friction value (E800 = normal) + int movefactor; // inertia factor when adding to momentum + int affectee; // Number of affected sector +} friction_t; + +// phares 3/20/98: added new model of Pushers for push/pull effects + +typedef struct { + thinker_t thinker; // Thinker structure for Pusher + enum + { + p_push, + p_pull, + p_wind, + p_current, + } type; + mobj_t* source; // Point source if point pusher + int x_mag; // X Strength + int y_mag; // Y Strength + int magnitude; // Vector strength for point pusher + int radius; // Effective radius for point pusher + int x; // X of point source if point pusher + int y; // Y of point source if point pusher + int affectee; // Number of affected sector +} pusher_t; + +////////////////////////////////////////////////////////////////// +// +// external data declarations +// +////////////////////////////////////////////////////////////////// + +// list of retriggerable buttons active +extern button_t buttonlist[MAXBUTTONS]; + +extern platlist_t *activeplats; // killough 2/14/98 + +extern ceilinglist_t *activeceilings; // jff 2/22/98 + +//////////////////////////////////////////////////////////////// +// +// Linedef and sector special utility function prototypes +// +//////////////////////////////////////////////////////////////// + +int twoSided +( int sector, + int line ); + +sector_t* getSector +( int currentSector, + int line, + int side ); + +side_t* getSide +( int currentSector, + int line, + int side ); + +fixed_t P_FindLowestFloorSurrounding +( sector_t* sec ); + +fixed_t P_FindHighestFloorSurrounding +( sector_t* sec ); + +fixed_t P_FindNextHighestFloor +( sector_t* sec, + int currentheight ); + +fixed_t P_FindNextLowestFloor +( sector_t* sec, + int currentheight ); + +fixed_t P_FindLowestCeilingSurrounding +( sector_t* sec ); // jff 2/04/98 + +fixed_t P_FindHighestCeilingSurrounding +( sector_t* sec ); // jff 2/04/98 + +fixed_t P_FindNextLowestCeiling +( sector_t *sec, + int currentheight ); // jff 2/04/98 + +fixed_t P_FindNextHighestCeiling +( sector_t *sec, + int currentheight ); // jff 2/04/98 + +fixed_t P_FindShortestTextureAround +( int secnum ); // jff 2/04/98 + +fixed_t P_FindShortestUpperAround +( int secnum ); // jff 2/04/98 + +sector_t* P_FindModelFloorSector +( fixed_t floordestheight, + int secnum ); //jff 02/04/98 + +sector_t* P_FindModelCeilingSector +( fixed_t ceildestheight, + int secnum ); //jff 02/04/98 + +int P_FindSectorFromLineTag +( const line_t *line, + int start ); // killough 4/17/98 + +int P_FindLineFromLineTag +( const line_t *line, + int start ); // killough 4/17/98 + +int P_FindMinSurroundingLight +( sector_t* sector, + int max ); + +sector_t* getNextSector +( line_t* line, + sector_t* sec ); + +int P_CheckTag +(line_t *line); // jff 2/27/98 + +boolean P_CanUnlockGenDoor +( line_t* line, + player_t* player); + +boolean PUREFUNC P_SectorActive +( special_e t, + const sector_t* s ); + +boolean PUREFUNC P_IsSecret +( const sector_t *sec ); + +boolean PUREFUNC P_WasSecret +( const sector_t *sec ); + +void P_ChangeSwitchTexture +( line_t* line, + int useAgain ); + +//////////////////////////////////////////////////////////////// +// +// Linedef and sector special action function prototypes +// +//////////////////////////////////////////////////////////////// + +// p_lights + +void T_LightFlash +( lightflash_t* flash ); + +void T_StrobeFlash +( strobe_t* flash ); + +// jff 8/8/98 add missing thinker for flicker +void T_FireFlicker +( fireflicker_t* flick ); + +void T_Glow +( glow_t* g ); + +// p_plats + +void T_PlatRaise +( plat_t* plat ); + +// p_doors + +void T_VerticalDoor +( vldoor_t* door ); + +// p_ceilng + +void T_MoveCeiling +( ceiling_t* ceiling ); + +// p_floor + +result_e T_MovePlane +( sector_t* sector, + fixed_t speed, + fixed_t dest, + boolean crush, + int floorOrCeiling, + int direction ); + +void T_MoveFloor +( floormove_t* floor ); + +void T_MoveElevator +( elevator_t* elevator ); + +// p_spec + +void T_Scroll +( scroll_t * ); // killough 3/7/98: scroll effect thinker + +void T_Friction +( friction_t * ); // phares 3/12/98: friction thinker + +void T_Pusher +( pusher_t * ); // phares 3/20/98: Push thinker + +//////////////////////////////////////////////////////////////// +// +// Linedef and sector special handler prototypes +// +//////////////////////////////////////////////////////////////// + +// p_telept + +int EV_Teleport +( line_t* line, + int side, + mobj_t* thing ); + +// killough 2/14/98: Add silent teleporter +int EV_SilentTeleport +( line_t* line, + int side, + mobj_t* thing ); + +// killough 1/31/98: Add silent line teleporter +int EV_SilentLineTeleport +( line_t* line, + int side, + mobj_t* thing, + boolean reverse); + +// p_floor + +int +EV_DoElevator +( line_t* line, + elevator_e type ); + +int EV_BuildStairs +( line_t* line, + stair_e type ); + +int EV_DoFloor +( line_t* line, + floor_e floortype ); + +// p_ceilng + +int EV_DoCeiling +( line_t* line, + ceiling_e type ); + +int EV_CeilingCrushStop +( line_t* line ); + +// p_doors + +int EV_VerticalDoor +( line_t* line, + mobj_t* thing ); + +int EV_DoDoor +( line_t* line, + vldoor_e type ); + +int EV_DoLockedDoor +( line_t* line, + vldoor_e type, + mobj_t* thing ); + +// p_lights + +int EV_StartLightStrobing +( line_t* line ); + +int EV_TurnTagLightsOff +( line_t* line ); + +int EV_LightTurnOn +( line_t* line, + int bright ); + +int EV_LightTurnOnPartway(line_t* line, fixed_t level); // killough 10/10/98 + +// p_floor + +int EV_DoChange +( line_t* line, + change_e changetype ); + +int EV_DoDonut +( line_t* line ); + +// p_plats + +int EV_DoPlat +( line_t* line, + plattype_e type, + int amount ); + +int EV_StopPlat +( line_t* line ); + +// p_genlin + +int EV_DoGenFloor +( line_t* line ); + +int EV_DoGenCeiling +( line_t* line ); + +int EV_DoGenLift +( line_t* line ); + +int EV_DoGenStairs +( line_t* line ); + +int EV_DoGenCrusher +( line_t* line ); + +int EV_DoGenDoor +( line_t* line ); + +int EV_DoGenLockedDoor +( line_t* line ); + +//////////////////////////////////////////////////////////////// +// +// Linedef and sector special thinker spawning +// +//////////////////////////////////////////////////////////////// + +// at game start +void P_InitPicAnims +( void ); + +void P_InitSwitchList +( void ); + +// at map load +void P_SpawnSpecials +( void ); + +// every tic +void P_UpdateSpecials +( void ); + +// when needed +boolean P_UseSpecialLine +( mobj_t* thing, + line_t* line, + int side ); + +void P_ShootSpecialLine +( mobj_t* thing, + line_t* line ); + +void P_CrossSpecialLine(line_t *line, int side, mobj_t *thing); + +void P_PlayerInSpecialSector +( player_t* player ); + +// p_lights + +void P_SpawnFireFlicker +( sector_t* sector ); + +void P_SpawnLightFlash +( sector_t* sector ); + +void P_SpawnStrobeFlash +( sector_t* sector, + int fastOrSlow, + int inSync ); + +void P_SpawnGlowingLight +( sector_t* sector ); + +// p_plats + +void P_AddActivePlat +( plat_t* plat ); + +void P_RemoveActivePlat +( plat_t* plat ); + +void P_RemoveAllActivePlats +( void ); // killough + +void P_ActivateInStasis +( int tag ); + +// p_doors + +void P_SpawnDoorCloseIn30 +( sector_t* sec ); + +void P_SpawnDoorRaiseIn5Mins +( sector_t* sec, + int secnum ); + +// p_ceilng + +void P_RemoveActiveCeiling +( ceiling_t* ceiling ); //jff 2/22/98 + +void P_RemoveAllActiveCeilings +( void ); //jff 2/22/98 + +void P_AddActiveCeiling +( ceiling_t* c ); + +int P_ActivateInStasisCeiling +( line_t* line ); + +mobj_t* P_GetPushThing(int); // phares 3/23/98 + +#endif diff --git a/src/p_switch.c b/src/p_switch.c new file mode 100644 index 0000000..7dfb2f9 --- /dev/null +++ b/src/p_switch.c @@ -0,0 +1,1150 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Switches, buttons. Two-state animation. Exits. + * + *-----------------------------------------------------------------------------*/ + +#include "doomstat.h" +#include "w_wad.h" +#include "r_main.h" +#include "p_spec.h" +#include "g_game.h" +#include "s_sound.h" +#include "sounds.h" +#include "lprintf.h" + +// killough 2/8/98: Remove switch limit + +static int *switchlist; // killough +static int max_numswitches; // killough +static int numswitches; // killough + +button_t buttonlist[MAXBUTTONS]; + +// +// P_InitSwitchList() +// +// Only called at game initialization in order to list the set of switches +// and buttons known to the engine. This enables their texture to change +// when activated, and in the case of buttons, change back after a timeout. +// +// This routine modified to read its data from a predefined lump or +// PWAD lump called SWITCHES rather than a static table in this module to +// allow wad designers to insert or modify switches. +// +// Lump format is an array of byte packed switchlist_t structures, terminated +// by a structure with episode == -0. The lump can be generated from a +// text source file using SWANTBLS.EXE, distributed with the BOOM utils. +// The standard list of switches and animations is contained in the example +// source text file DEFSWANI.DAT also in the BOOM util distribution. +// +// Rewritten by Lee Killough to remove limit 2/8/98 +// +void P_InitSwitchList(void) +{ + int i, index = 0; + int episode = (gamemode == registered || gamemode==retail) ? + 2 : gamemode == commercial ? 3 : 1; + const switchlist_t *alphSwitchList; //jff 3/23/98 pointer to switch table + int lump = W_GetNumForName("SWITCHES"); // cph - new wad lump handling + + //jff 3/23/98 read the switch table from a predefined lump + alphSwitchList = (const switchlist_t *)W_CacheLumpNum(lump); + + for (i=0;;i++) + { + if (index+1 >= max_numswitches) + switchlist = realloc(switchlist, sizeof *switchlist * + (max_numswitches = max_numswitches ? max_numswitches*2 : 8)); + if (SHORT(alphSwitchList[i].episode) <= episode) //jff 5/11/98 endianess + { + int texture1, texture2; + + if (!SHORT(alphSwitchList[i].episode)) + break; + + // Ignore switches referencing unknown texture names, instead of exiting. + // Warn if either one is missing, but only add if both are valid. + texture1 = R_CheckTextureNumForName(alphSwitchList[i].name1); + if (texture1 == -1) + lprintf(LO_WARN, "P_InitSwitchList: unknown texture %s\n", + alphSwitchList[i].name1); + texture2 = R_CheckTextureNumForName(alphSwitchList[i].name2); + if (texture2 == -1) + lprintf(LO_WARN, "P_InitSwitchList: unknown texture %s\n", + alphSwitchList[i].name2); + if (texture1 != -1 && texture2 != -1) { + switchlist[index++] = texture1; + switchlist[index++] = texture2; + } + } + } + + numswitches = index/2; + switchlist[index] = -1; + W_UnlockLumpNum(lump); +} + +// +// P_StartButton() +// +// Start a button (retriggerable switch) counting down till it turns off. +// +// Passed the linedef the button is on, which texture on the sidedef contains +// the button, the texture number of the button, and the time the button is +// to remain active in gametics. +// No return. +// +static void P_StartButton +( line_t* line, + bwhere_e w, + int texture, + int time ) +{ + int i; + + // See if button is already pressed + for (i = 0;i < MAXBUTTONS;i++) + if (buttonlist[i].btimer && buttonlist[i].line == line) + return; + + for (i = 0;i < MAXBUTTONS;i++) + if (!buttonlist[i].btimer) // use first unused element of list + { + buttonlist[i].line = line; + buttonlist[i].where = w; + buttonlist[i].btexture = texture; + buttonlist[i].btimer = time; + /* use sound origin of line itself - no need to compatibility-wrap + * as the popout code gets it wrong whatever its value */ + buttonlist[i].soundorg = (mobj_t *)&line->soundorg; + return; + } + + I_Error("P_StartButton: no button slots left!"); +} + +// +// P_ChangeSwitchTexture() +// +// Function that changes switch wall texture on activation. +// +// Passed the line which the switch is on, and whether its retriggerable. +// If not retriggerable, this function clears the line special to insure that +// +// No return +// +void P_ChangeSwitchTexture +( line_t* line, + int useAgain ) +{ + /* Rearranged a bit to avoid too much code duplication */ + mobj_t *soundorg; + int i, sound; + short *texture, *ttop, *tmid, *tbot; + bwhere_e position; + + ttop = &sides[line->sidenum[0]].toptexture; + tmid = &sides[line->sidenum[0]].midtexture; + tbot = &sides[line->sidenum[0]].bottomtexture; + + sound = sfx_swtchn; + /* use the sound origin of the linedef (its midpoint) + * unless in a compatibility mode */ + soundorg = (mobj_t *)&line->soundorg; + if (comp[comp_sound] || compatibility_level < prboom_6_compatibility) { + /* usually NULL, unless there is another button already pressed in, + * in which case it's the sound origin of that button press... */ + soundorg = buttonlist->soundorg; + } else { + // EXIT SWITCH? + /* don't do this unless you're in a compatibility mode */ + // proff - this works as advertised, but I don't like the sound + // if (line->special == 11 || line->special == 51) // exit or secret exit + // sound = sfx_swtchx; + } + + /* don't zero line->special until after exit switch test */ + if (!useAgain) + line->special = 0; + + /* search for a texture to change */ + texture = NULL; position = 0; + for (i = 0;i < numswitches*2;i++) { /* this could be more efficient... */ + if (switchlist[i] == *ttop) { + texture = ttop; position = top; break; + } else if (switchlist[i] == *tmid) { + texture = tmid; position = middle; break; + } else if (switchlist[i] == *tbot) { + texture = tbot; position = bottom; break; + } + } + if (texture == NULL) + return; /* no switch texture was found to change */ + *texture = switchlist[i^1]; + + S_StartSound(soundorg, sound); + + if (useAgain) + P_StartButton(line, position, switchlist[i], BUTTONTIME); +} + + +// +// P_UseSpecialLine +// +// +// Called when a thing uses (pushes) a special line. +// Only the front sides of lines are usable. +// Dispatches to the appropriate linedef function handler. +// +// Passed the thing using the line, the line being used, and the side used +// Returns true if a thinker was created +// +boolean +P_UseSpecialLine +( mobj_t* thing, + line_t* line, + int side ) +{ + + // e6y + // b.m. side test was broken in boom201 + if ((demoplayback ? (demover != 201) : (compatibility_level != boom_201_compatibility))) + if (side) //jff 6/1/98 fix inadvertent deletion of side test + return false; + + //jff 02/04/98 add check here for generalized floor/ceil mover + if (!demo_compatibility) + { + // pointer to line function is NULL by default, set non-null if + // line special is push or switch generalized linedef type + int (*linefunc)(line_t *line)=NULL; + + // check each range of generalized linedefs + if ((unsigned)line->special >= GenEnd) + { + // Out of range for GenFloors + } + else if ((unsigned)line->special >= GenFloorBase) + { + if (!thing->player) + if ((line->special & FloorChange) || !(line->special & FloorModel)) + return false; // FloorModel is "Allow Monsters" if FloorChange is 0 + if (!line->tag && ((line->special&6)!=6)) //jff 2/27/98 all non-manual + return false; // generalized types require tag + linefunc = EV_DoGenFloor; + } + else if ((unsigned)line->special >= GenCeilingBase) + { + if (!thing->player) + if ((line->special & CeilingChange) || !(line->special & CeilingModel)) + return false; // CeilingModel is "Allow Monsters" if CeilingChange is 0 + if (!line->tag && ((line->special&6)!=6)) //jff 2/27/98 all non-manual + return false; // generalized types require tag + linefunc = EV_DoGenCeiling; + } + else if ((unsigned)line->special >= GenDoorBase) + { + if (!thing->player) + { + if (!(line->special & DoorMonster)) + return false; // monsters disallowed from this door + if (line->flags & ML_SECRET) // they can't open secret doors either + return false; + } + if (!line->tag && ((line->special&6)!=6)) //jff 3/2/98 all non-manual + return false; // generalized types require tag + linefunc = EV_DoGenDoor; + } + else if ((unsigned)line->special >= GenLockedBase) + { + if (!thing->player) + return false; // monsters disallowed from unlocking doors + if (!P_CanUnlockGenDoor(line,thing->player)) + return false; + if (!line->tag && ((line->special&6)!=6)) //jff 2/27/98 all non-manual + return false; // generalized types require tag + + linefunc = EV_DoGenLockedDoor; + } + else if ((unsigned)line->special >= GenLiftBase) + { + if (!thing->player) + if (!(line->special & LiftMonster)) + return false; // monsters disallowed + if (!line->tag && ((line->special&6)!=6)) //jff 2/27/98 all non-manual + return false; // generalized types require tag + linefunc = EV_DoGenLift; + } + else if ((unsigned)line->special >= GenStairsBase) + { + if (!thing->player) + if (!(line->special & StairMonster)) + return false; // monsters disallowed + if (!line->tag && ((line->special&6)!=6)) //jff 2/27/98 all non-manual + return false; // generalized types require tag + linefunc = EV_DoGenStairs; + } + else if ((unsigned)line->special >= GenCrusherBase) + { + if (!thing->player) + if (!(line->special & CrusherMonster)) + return false; // monsters disallowed + if (!line->tag && ((line->special&6)!=6)) //jff 2/27/98 all non-manual + return false; // generalized types require tag + linefunc = EV_DoGenCrusher; + } + + if (linefunc) + switch((line->special & TriggerType) >> TriggerTypeShift) + { + case PushOnce: + if (!side) + if (linefunc(line)) + line->special = 0; + return true; + case PushMany: + if (!side) + linefunc(line); + return true; + case SwitchOnce: + if (linefunc(line)) + P_ChangeSwitchTexture(line,0); + return true; + case SwitchMany: + if (linefunc(line)) + P_ChangeSwitchTexture(line,1); + return true; + default: // if not a switch/push type, do nothing here + return false; + } + } + + // Switches that other things can activate. + if (!thing->player) + { + // never open secret doors + if (line->flags & ML_SECRET) + return false; + + switch(line->special) + { + case 1: // MANUAL DOOR RAISE + case 32: // MANUAL BLUE + case 33: // MANUAL RED + case 34: // MANUAL YELLOW + //jff 3/5/98 add ability to use teleporters for monsters + case 195: // switch teleporters + case 174: + case 210: // silent switch teleporters + case 209: + break; + + default: + return false; + break; + } + } + + if (!P_CheckTag(line)) //jff 2/27/98 disallow zero tag on some types + return false; + + // Dispatch to handler according to linedef type + switch (line->special) + { + // Manual doors, push type with no tag + case 1: // Vertical Door + case 26: // Blue Door/Locked + case 27: // Yellow Door /Locked + case 28: // Red Door /Locked + + case 31: // Manual door open + case 32: // Blue locked door open + case 33: // Red locked door open + case 34: // Yellow locked door open + + case 117: // Blazing door raise + case 118: // Blazing door open + EV_VerticalDoor (line, thing); + break; + + // Switches (non-retriggerable) + case 7: + // Build Stairs + if (EV_BuildStairs(line,build8)) + P_ChangeSwitchTexture(line,0); + break; + + case 9: + // Change Donut + if (EV_DoDonut(line)) + P_ChangeSwitchTexture(line,0); + break; + + case 11: + /* Exit level + * killough 10/98: prevent zombies from exiting levels + */ + if (thing->player && thing->player->health <= 0 && !comp[comp_zombie]) + { + S_StartSound(thing, sfx_noway); + return false; + } + + P_ChangeSwitchTexture(line,0); + G_ExitLevel (); + break; + + case 14: + // Raise Floor 32 and change texture + if (EV_DoPlat(line,raiseAndChange,32)) + P_ChangeSwitchTexture(line,0); + break; + + case 15: + // Raise Floor 24 and change texture + if (EV_DoPlat(line,raiseAndChange,24)) + P_ChangeSwitchTexture(line,0); + break; + + case 18: + // Raise Floor to next highest floor + if (EV_DoFloor(line, raiseFloorToNearest)) + P_ChangeSwitchTexture(line,0); + break; + + case 20: + // Raise Plat next highest floor and change texture + if (EV_DoPlat(line,raiseToNearestAndChange,0)) + P_ChangeSwitchTexture(line,0); + break; + + case 21: + // PlatDownWaitUpStay + if (EV_DoPlat(line,downWaitUpStay,0)) + P_ChangeSwitchTexture(line,0); + break; + + case 23: + // Lower Floor to Lowest + if (EV_DoFloor(line,lowerFloorToLowest)) + P_ChangeSwitchTexture(line,0); + break; + + case 29: + // Raise Door + if (EV_DoDoor(line,normal)) + P_ChangeSwitchTexture(line,0); + break; + + case 41: + // Lower Ceiling to Floor + if (EV_DoCeiling(line,lowerToFloor)) + P_ChangeSwitchTexture(line,0); + break; + + case 71: + // Turbo Lower Floor + if (EV_DoFloor(line,turboLower)) + P_ChangeSwitchTexture(line,0); + break; + + case 49: + // Ceiling Crush And Raise + if (EV_DoCeiling(line,crushAndRaise)) + P_ChangeSwitchTexture(line,0); + break; + + case 50: + // Close Door + if (EV_DoDoor(line,close)) + P_ChangeSwitchTexture(line,0); + break; + + case 51: + /* Secret EXIT + * killough 10/98: prevent zombies from exiting levels + */ + if (thing->player && thing->player->health <= 0 && !comp[comp_zombie]) + { + S_StartSound(thing, sfx_noway); + return false; + } + + P_ChangeSwitchTexture(line,0); + G_SecretExitLevel (); + break; + + case 55: + // Raise Floor Crush + if (EV_DoFloor(line,raiseFloorCrush)) + P_ChangeSwitchTexture(line,0); + break; + + case 101: + // Raise Floor + if (EV_DoFloor(line,raiseFloor)) + P_ChangeSwitchTexture(line,0); + break; + + case 102: + // Lower Floor to Surrounding floor height + if (EV_DoFloor(line,lowerFloor)) + P_ChangeSwitchTexture(line,0); + break; + + case 103: + // Open Door + if (EV_DoDoor(line,open)) + P_ChangeSwitchTexture(line,0); + break; + + case 111: + // Blazing Door Raise (faster than TURBO!) + if (EV_DoDoor (line,blazeRaise)) + P_ChangeSwitchTexture(line,0); + break; + + case 112: + // Blazing Door Open (faster than TURBO!) + if (EV_DoDoor (line,blazeOpen)) + P_ChangeSwitchTexture(line,0); + break; + + case 113: + // Blazing Door Close (faster than TURBO!) + if (EV_DoDoor (line,blazeClose)) + P_ChangeSwitchTexture(line,0); + break; + + case 122: + // Blazing PlatDownWaitUpStay + if (EV_DoPlat(line,blazeDWUS,0)) + P_ChangeSwitchTexture(line,0); + break; + + case 127: + // Build Stairs Turbo 16 + if (EV_BuildStairs(line,turbo16)) + P_ChangeSwitchTexture(line,0); + break; + + case 131: + // Raise Floor Turbo + if (EV_DoFloor(line,raiseFloorTurbo)) + P_ChangeSwitchTexture(line,0); + break; + + case 133: + // BlzOpenDoor BLUE + case 135: + // BlzOpenDoor RED + case 137: + // BlzOpenDoor YELLOW + if (EV_DoLockedDoor (line,blazeOpen,thing)) + P_ChangeSwitchTexture(line,0); + break; + + case 140: + // Raise Floor 512 + if (EV_DoFloor(line,raiseFloor512)) + P_ChangeSwitchTexture(line,0); + break; + + // killough 1/31/98: factored out compatibility check; + // added inner switch, relaxed check to demo_compatibility + + default: + if (!demo_compatibility) + switch (line->special) + { + //jff 1/29/98 added linedef types to fill all functions out so that + // all possess SR, S1, WR, W1 types + + case 158: + // Raise Floor to shortest lower texture + // 158 S1 EV_DoFloor(raiseToTexture), CSW(0) + if (EV_DoFloor(line,raiseToTexture)) + P_ChangeSwitchTexture(line,0); + break; + + case 159: + // Raise Floor to shortest lower texture + // 159 S1 EV_DoFloor(lowerAndChange) + if (EV_DoFloor(line,lowerAndChange)) + P_ChangeSwitchTexture(line,0); + break; + + case 160: + // Raise Floor 24 and change + // 160 S1 EV_DoFloor(raiseFloor24AndChange) + if (EV_DoFloor(line,raiseFloor24AndChange)) + P_ChangeSwitchTexture(line,0); + break; + + case 161: + // Raise Floor 24 + // 161 S1 EV_DoFloor(raiseFloor24) + if (EV_DoFloor(line,raiseFloor24)) + P_ChangeSwitchTexture(line,0); + break; + + case 162: + // Moving floor min n to max n + // 162 S1 EV_DoPlat(perpetualRaise,0) + if (EV_DoPlat(line,perpetualRaise,0)) + P_ChangeSwitchTexture(line,0); + break; + + case 163: + // Stop Moving floor + // 163 S1 EV_DoPlat(perpetualRaise,0) + EV_StopPlat(line); + P_ChangeSwitchTexture(line,0); + break; + + case 164: + // Start fast crusher + // 164 S1 EV_DoCeiling(fastCrushAndRaise) + if (EV_DoCeiling(line,fastCrushAndRaise)) + P_ChangeSwitchTexture(line,0); + break; + + case 165: + // Start slow silent crusher + // 165 S1 EV_DoCeiling(silentCrushAndRaise) + if (EV_DoCeiling(line,silentCrushAndRaise)) + P_ChangeSwitchTexture(line,0); + break; + + case 166: + // Raise ceiling, Lower floor + // 166 S1 EV_DoCeiling(raiseToHighest), EV_DoFloor(lowerFloortoLowest) + if (EV_DoCeiling(line, raiseToHighest) || + EV_DoFloor(line, lowerFloorToLowest)) + P_ChangeSwitchTexture(line,0); + break; + + case 167: + // Lower floor and Crush + // 167 S1 EV_DoCeiling(lowerAndCrush) + if (EV_DoCeiling(line, lowerAndCrush)) + P_ChangeSwitchTexture(line,0); + break; + + case 168: + // Stop crusher + // 168 S1 EV_CeilingCrushStop() + if (EV_CeilingCrushStop(line)) + P_ChangeSwitchTexture(line,0); + break; + + case 169: + // Lights to brightest neighbor sector + // 169 S1 EV_LightTurnOn(0) + EV_LightTurnOn(line,0); + P_ChangeSwitchTexture(line,0); + break; + + case 170: + // Lights to near dark + // 170 S1 EV_LightTurnOn(35) + EV_LightTurnOn(line,35); + P_ChangeSwitchTexture(line,0); + break; + + case 171: + // Lights on full + // 171 S1 EV_LightTurnOn(255) + EV_LightTurnOn(line,255); + P_ChangeSwitchTexture(line,0); + break; + + case 172: + // Start Lights Strobing + // 172 S1 EV_StartLightStrobing() + EV_StartLightStrobing(line); + P_ChangeSwitchTexture(line,0); + break; + + case 173: + // Lights to Dimmest Near + // 173 S1 EV_TurnTagLightsOff() + EV_TurnTagLightsOff(line); + P_ChangeSwitchTexture(line,0); + break; + + case 174: + // Teleport + // 174 S1 EV_Teleport(side,thing) + if (EV_Teleport(line,side,thing)) + P_ChangeSwitchTexture(line,0); + break; + + case 175: + // Close Door, Open in 30 secs + // 175 S1 EV_DoDoor(close30ThenOpen) + if (EV_DoDoor(line,close30ThenOpen)) + P_ChangeSwitchTexture(line,0); + break; + + case 189: //jff 3/15/98 create texture change no motion type + // Texture Change Only (Trigger) + // 189 S1 Change Texture/Type Only + if (EV_DoChange(line,trigChangeOnly)) + P_ChangeSwitchTexture(line,0); + break; + + case 203: + // Lower ceiling to lowest surrounding ceiling + // 203 S1 EV_DoCeiling(lowerToLowest) + if (EV_DoCeiling(line,lowerToLowest)) + P_ChangeSwitchTexture(line,0); + break; + + case 204: + // Lower ceiling to highest surrounding floor + // 204 S1 EV_DoCeiling(lowerToMaxFloor) + if (EV_DoCeiling(line,lowerToMaxFloor)) + P_ChangeSwitchTexture(line,0); + break; + + case 209: + // killough 1/31/98: silent teleporter + //jff 209 S1 SilentTeleport + if (EV_SilentTeleport(line, side, thing)) + P_ChangeSwitchTexture(line,0); + break; + + case 241: //jff 3/15/98 create texture change no motion type + // Texture Change Only (Numeric) + // 241 S1 Change Texture/Type Only + if (EV_DoChange(line,numChangeOnly)) + P_ChangeSwitchTexture(line,0); + break; + + case 221: + // Lower floor to next lowest floor + // 221 S1 Lower Floor To Nearest Floor + if (EV_DoFloor(line,lowerFloorToNearest)) + P_ChangeSwitchTexture(line,0); + break; + + case 229: + // Raise elevator next floor + // 229 S1 Raise Elevator next floor + if (EV_DoElevator(line,elevateUp)) + P_ChangeSwitchTexture(line,0); + break; + + case 233: + // Lower elevator next floor + // 233 S1 Lower Elevator next floor + if (EV_DoElevator(line,elevateDown)) + P_ChangeSwitchTexture(line,0); + break; + + case 237: + // Elevator to current floor + // 237 S1 Elevator to current floor + if (EV_DoElevator(line,elevateCurrent)) + P_ChangeSwitchTexture(line,0); + break; + + + // jff 1/29/98 end of added S1 linedef types + + //jff 1/29/98 added linedef types to fill all functions out so that + // all possess SR, S1, WR, W1 types + + case 78: //jff 3/15/98 create texture change no motion type + // Texture Change Only (Numeric) + // 78 SR Change Texture/Type Only + if (EV_DoChange(line,numChangeOnly)) + P_ChangeSwitchTexture(line,1); + break; + + case 176: + // Raise Floor to shortest lower texture + // 176 SR EV_DoFloor(raiseToTexture), CSW(1) + if (EV_DoFloor(line,raiseToTexture)) + P_ChangeSwitchTexture(line,1); + break; + + case 177: + // Raise Floor to shortest lower texture + // 177 SR EV_DoFloor(lowerAndChange) + if (EV_DoFloor(line,lowerAndChange)) + P_ChangeSwitchTexture(line,1); + break; + + case 178: + // Raise Floor 512 + // 178 SR EV_DoFloor(raiseFloor512) + if (EV_DoFloor(line,raiseFloor512)) + P_ChangeSwitchTexture(line,1); + break; + + case 179: + // Raise Floor 24 and change + // 179 SR EV_DoFloor(raiseFloor24AndChange) + if (EV_DoFloor(line,raiseFloor24AndChange)) + P_ChangeSwitchTexture(line,1); + break; + + case 180: + // Raise Floor 24 + // 180 SR EV_DoFloor(raiseFloor24) + if (EV_DoFloor(line,raiseFloor24)) + P_ChangeSwitchTexture(line,1); + break; + + case 181: + // Moving floor min n to max n + // 181 SR EV_DoPlat(perpetualRaise,0) + + EV_DoPlat(line,perpetualRaise,0); + P_ChangeSwitchTexture(line,1); + break; + + case 182: + // Stop Moving floor + // 182 SR EV_DoPlat(perpetualRaise,0) + EV_StopPlat(line); + P_ChangeSwitchTexture(line,1); + break; + + case 183: + // Start fast crusher + // 183 SR EV_DoCeiling(fastCrushAndRaise) + if (EV_DoCeiling(line,fastCrushAndRaise)) + P_ChangeSwitchTexture(line,1); + break; + + case 184: + // Start slow crusher + // 184 SR EV_DoCeiling(crushAndRaise) + if (EV_DoCeiling(line,crushAndRaise)) + P_ChangeSwitchTexture(line,1); + break; + + case 185: + // Start slow silent crusher + // 185 SR EV_DoCeiling(silentCrushAndRaise) + if (EV_DoCeiling(line,silentCrushAndRaise)) + P_ChangeSwitchTexture(line,1); + break; + + case 186: + // Raise ceiling, Lower floor + // 186 SR EV_DoCeiling(raiseToHighest), EV_DoFloor(lowerFloortoLowest) + if (EV_DoCeiling(line, raiseToHighest) || + EV_DoFloor(line, lowerFloorToLowest)) + P_ChangeSwitchTexture(line,1); + break; + + case 187: + // Lower floor and Crush + // 187 SR EV_DoCeiling(lowerAndCrush) + if (EV_DoCeiling(line, lowerAndCrush)) + P_ChangeSwitchTexture(line,1); + break; + + case 188: + // Stop crusher + // 188 SR EV_CeilingCrushStop() + if (EV_CeilingCrushStop(line)) + P_ChangeSwitchTexture(line,1); + break; + + case 190: //jff 3/15/98 create texture change no motion type + // Texture Change Only (Trigger) + // 190 SR Change Texture/Type Only + if (EV_DoChange(line,trigChangeOnly)) + P_ChangeSwitchTexture(line,1); + break; + + case 191: + // Lower Pillar, Raise Donut + // 191 SR EV_DoDonut() + if (EV_DoDonut(line)) + P_ChangeSwitchTexture(line,1); + break; + + case 192: + // Lights to brightest neighbor sector + // 192 SR EV_LightTurnOn(0) + EV_LightTurnOn(line,0); + P_ChangeSwitchTexture(line,1); + break; + + case 193: + // Start Lights Strobing + // 193 SR EV_StartLightStrobing() + EV_StartLightStrobing(line); + P_ChangeSwitchTexture(line,1); + break; + + case 194: + // Lights to Dimmest Near + // 194 SR EV_TurnTagLightsOff() + EV_TurnTagLightsOff(line); + P_ChangeSwitchTexture(line,1); + break; + + case 195: + // Teleport + // 195 SR EV_Teleport(side,thing) + if (EV_Teleport(line,side,thing)) + P_ChangeSwitchTexture(line,1); + break; + + case 196: + // Close Door, Open in 30 secs + // 196 SR EV_DoDoor(close30ThenOpen) + if (EV_DoDoor(line,close30ThenOpen)) + P_ChangeSwitchTexture(line,1); + break; + + case 205: + // Lower ceiling to lowest surrounding ceiling + // 205 SR EV_DoCeiling(lowerToLowest) + if (EV_DoCeiling(line,lowerToLowest)) + P_ChangeSwitchTexture(line,1); + break; + + case 206: + // Lower ceiling to highest surrounding floor + // 206 SR EV_DoCeiling(lowerToMaxFloor) + if (EV_DoCeiling(line,lowerToMaxFloor)) + P_ChangeSwitchTexture(line,1); + break; + + case 210: + // killough 1/31/98: silent teleporter + //jff 210 SR SilentTeleport + if (EV_SilentTeleport(line, side, thing)) + P_ChangeSwitchTexture(line,1); + break; + + case 211: //jff 3/14/98 create instant toggle floor type + // Toggle Floor Between C and F Instantly + // 211 SR Toggle Floor Instant + if (EV_DoPlat(line,toggleUpDn,0)) + P_ChangeSwitchTexture(line,1); + break; + + case 222: + // Lower floor to next lowest floor + // 222 SR Lower Floor To Nearest Floor + if (EV_DoFloor(line,lowerFloorToNearest)) + P_ChangeSwitchTexture(line,1); + break; + + case 230: + // Raise elevator next floor + // 230 SR Raise Elevator next floor + if (EV_DoElevator(line,elevateUp)) + P_ChangeSwitchTexture(line,1); + break; + + case 234: + // Lower elevator next floor + // 234 SR Lower Elevator next floor + if (EV_DoElevator(line,elevateDown)) + P_ChangeSwitchTexture(line,1); + break; + + case 238: + // Elevator to current floor + // 238 SR Elevator to current floor + if (EV_DoElevator(line,elevateCurrent)) + P_ChangeSwitchTexture(line,1); + break; + + case 258: + // Build stairs, step 8 + // 258 SR EV_BuildStairs(build8) + if (EV_BuildStairs(line,build8)) + P_ChangeSwitchTexture(line,1); + break; + + case 259: + // Build stairs, step 16 + // 259 SR EV_BuildStairs(turbo16) + if (EV_BuildStairs(line,turbo16)) + P_ChangeSwitchTexture(line,1); + break; + + // 1/29/98 jff end of added SR linedef types + + } + break; + + // Buttons (retriggerable switches) + case 42: + // Close Door + if (EV_DoDoor(line,close)) + P_ChangeSwitchTexture(line,1); + break; + + case 43: + // Lower Ceiling to Floor + if (EV_DoCeiling(line,lowerToFloor)) + P_ChangeSwitchTexture(line,1); + break; + + case 45: + // Lower Floor to Surrounding floor height + if (EV_DoFloor(line,lowerFloor)) + P_ChangeSwitchTexture(line,1); + break; + + case 60: + // Lower Floor to Lowest + if (EV_DoFloor(line,lowerFloorToLowest)) + P_ChangeSwitchTexture(line,1); + break; + + case 61: + // Open Door + if (EV_DoDoor(line,open)) + P_ChangeSwitchTexture(line,1); + break; + + case 62: + // PlatDownWaitUpStay + if (EV_DoPlat(line,downWaitUpStay,1)) + P_ChangeSwitchTexture(line,1); + break; + + case 63: + // Raise Door + if (EV_DoDoor(line,normal)) + P_ChangeSwitchTexture(line,1); + break; + + case 64: + // Raise Floor to ceiling + if (EV_DoFloor(line,raiseFloor)) + P_ChangeSwitchTexture(line,1); + break; + + case 66: + // Raise Floor 24 and change texture + if (EV_DoPlat(line,raiseAndChange,24)) + P_ChangeSwitchTexture(line,1); + break; + + case 67: + // Raise Floor 32 and change texture + if (EV_DoPlat(line,raiseAndChange,32)) + P_ChangeSwitchTexture(line,1); + break; + + case 65: + // Raise Floor Crush + if (EV_DoFloor(line,raiseFloorCrush)) + P_ChangeSwitchTexture(line,1); + break; + + case 68: + // Raise Plat to next highest floor and change texture + if (EV_DoPlat(line,raiseToNearestAndChange,0)) + P_ChangeSwitchTexture(line,1); + break; + + case 69: + // Raise Floor to next highest floor + if (EV_DoFloor(line, raiseFloorToNearest)) + P_ChangeSwitchTexture(line,1); + break; + + case 70: + // Turbo Lower Floor + if (EV_DoFloor(line,turboLower)) + P_ChangeSwitchTexture(line,1); + break; + + case 114: + // Blazing Door Raise (faster than TURBO!) + if (EV_DoDoor (line,blazeRaise)) + P_ChangeSwitchTexture(line,1); + break; + + case 115: + // Blazing Door Open (faster than TURBO!) + if (EV_DoDoor (line,blazeOpen)) + P_ChangeSwitchTexture(line,1); + break; + + case 116: + // Blazing Door Close (faster than TURBO!) + if (EV_DoDoor (line,blazeClose)) + P_ChangeSwitchTexture(line,1); + break; + + case 123: + // Blazing PlatDownWaitUpStay + if (EV_DoPlat(line,blazeDWUS,0)) + P_ChangeSwitchTexture(line,1); + break; + + case 132: + // Raise Floor Turbo + if (EV_DoFloor(line,raiseFloorTurbo)) + P_ChangeSwitchTexture(line,1); + break; + + case 99: + // BlzOpenDoor BLUE + case 134: + // BlzOpenDoor RED + case 136: + // BlzOpenDoor YELLOW + if (EV_DoLockedDoor (line,blazeOpen,thing)) + P_ChangeSwitchTexture(line,1); + break; + + case 138: + // Light Turn On + EV_LightTurnOn(line,255); + P_ChangeSwitchTexture(line,1); + break; + + case 139: + // Light Turn Off + EV_LightTurnOn(line,35); + P_ChangeSwitchTexture(line,1); + break; + } + return true; +} diff --git a/src/p_telept.c b/src/p_telept.c new file mode 100644 index 0000000..744e901 --- /dev/null +++ b/src/p_telept.c @@ -0,0 +1,345 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2002 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Teleportation. + * + *-----------------------------------------------------------------------------*/ + +#include "doomdef.h" +#include "doomstat.h" +#include "p_spec.h" +#include "p_maputl.h" +#include "p_map.h" +#include "r_main.h" +#include "p_tick.h" +#include "s_sound.h" +#include "sounds.h" +#include "p_user.h" +#include "r_demo.h" + +static mobj_t* P_TeleportDestination(line_t* line) +{ + int i; + for (i = -1; (i = P_FindSectorFromLineTag(line, i)) >= 0;) { + register thinker_t* th = NULL; + while ((th = P_NextThinker(th,th_misc)) != NULL) + if (th->function == P_MobjThinker) { + register mobj_t* m = (mobj_t*)th; + if (m->type == MT_TELEPORTMAN && + m->subsector->sector-sectors == i) + return m; + } + } + return NULL; +} +// +// TELEPORTATION +// +// killough 5/3/98: reformatted, cleaned up + +int EV_Teleport(line_t *line, int side, mobj_t *thing) +{ + mobj_t *m; + + // don't teleport missiles + // Don't teleport if hit back of line, + // so you can get out of teleporter. + if (side || thing->flags & MF_MISSILE) + return 0; + + // killough 1/31/98: improve performance by using + // P_FindSectorFromLineTag instead of simple linear search. + + if ((m = P_TeleportDestination(line)) != NULL) + { + fixed_t oldx = thing->x, oldy = thing->y, oldz = thing->z; + player_t *player = thing->player; + + // killough 5/12/98: exclude voodoo dolls: + if (player && player->mo != thing) + player = NULL; + + if (!P_TeleportMove(thing, m->x, m->y, false)) /* killough 8/9/98 */ + return 0; + + if (compatibility_level != finaldoom_compatibility) + thing->z = thing->floorz; + + if (player) + player->viewz = thing->z + player->viewheight; + + // spawn teleport fog and emit sound at source + S_StartSound(P_SpawnMobj(oldx, oldy, oldz, MT_TFOG), sfx_telept); + + // spawn teleport fog and emit sound at destination + S_StartSound(P_SpawnMobj(m->x + + 20*finecosine[m->angle>>ANGLETOFINESHIFT], + m->y + + 20*finesine[m->angle>>ANGLETOFINESHIFT], + thing->z, MT_TFOG), + sfx_telept); + + /* don't move for a bit + * cph - DEMOSYNC - BOOM had (player) here? */ + if (thing->player) + thing->reactiontime = 18; + + thing->angle = m->angle; + + thing->momx = thing->momy = thing->momz = 0; + + /* killough 10/98: kill all bobbing momentum too */ + if (player) + player->momx = player->momy = 0; + + // e6y + if (player && player->mo == thing) + R_ResetAfterTeleport(player); + + return 1; + } + return 0; +} + +// +// Silent TELEPORTATION, by Lee Killough +// Primarily for rooms-over-rooms etc. +// + +int EV_SilentTeleport(line_t *line, int side, mobj_t *thing) +{ + mobj_t *m; + + // don't teleport missiles + // Don't teleport if hit back of line, + // so you can get out of teleporter. + + if (side || thing->flags & MF_MISSILE) + return 0; + + if ((m = P_TeleportDestination(line)) != NULL) + { + // Height of thing above ground, in case of mid-air teleports: + fixed_t z = thing->z - thing->floorz; + + // Get the angle between the exit thing and source linedef. + // Rotate 90 degrees, so that walking perpendicularly across + // teleporter linedef causes thing to exit in the direction + // indicated by the exit thing. + angle_t angle = + R_PointToAngle2(0, 0, line->dx, line->dy) - m->angle + ANG90; + + // Sine, cosine of angle adjustment + fixed_t s = finesine[angle>>ANGLETOFINESHIFT]; + fixed_t c = finecosine[angle>>ANGLETOFINESHIFT]; + + // Momentum of thing crossing teleporter linedef + fixed_t momx = thing->momx; + fixed_t momy = thing->momy; + + // Whether this is a player, and if so, a pointer to its player_t + player_t *player = thing->player; + + // Attempt to teleport, aborting if blocked + if (!P_TeleportMove(thing, m->x, m->y, false)) /* killough 8/9/98 */ + return 0; + + // Rotate thing according to difference in angles + thing->angle += angle; + + // Adjust z position to be same height above ground as before + thing->z = z + thing->floorz; + + // Rotate thing's momentum to come out of exit just like it entered + thing->momx = FixedMul(momx, c) - FixedMul(momy, s); + thing->momy = FixedMul(momy, c) + FixedMul(momx, s); + + // Adjust player's view, in case there has been a height change + // Voodoo dolls are excluded by making sure player->mo == thing. + if (player && player->mo == thing) + { + // Save the current deltaviewheight, used in stepping + fixed_t deltaviewheight = player->deltaviewheight; + + // Clear deltaviewheight, since we don't want any changes + player->deltaviewheight = 0; + + // Set player's view according to the newly set parameters + P_CalcHeight(player); + + // Reset the delta to have the same dynamics as before + player->deltaviewheight = deltaviewheight; + } + + // e6y + if (player && player->mo == thing) + R_ResetAfterTeleport(player); + + return 1; + } + return 0; +} + +// +// Silent linedef-based TELEPORTATION, by Lee Killough +// Primarily for rooms-over-rooms etc. +// This is the complete player-preserving kind of teleporter. +// It has advantages over the teleporter with thing exits. +// + +// maximum fixed_t units to move object to avoid hiccups +#define FUDGEFACTOR 10 + +int EV_SilentLineTeleport(line_t *line, int side, mobj_t *thing, + boolean reverse) +{ + int i; + line_t *l; + + if (side || thing->flags & MF_MISSILE) + return 0; + + for (i = -1; (i = P_FindLineFromLineTag(line, i)) >= 0;) + if ((l=lines+i) != line && l->backsector) + { + // Get the thing's position along the source linedef + fixed_t pos = D_abs(line->dx) > D_abs(line->dy) ? + FixedDiv(thing->x - line->v1->x, line->dx) : + FixedDiv(thing->y - line->v1->y, line->dy) ; + + // Get the angle between the two linedefs, for rotating + // orientation and momentum. Rotate 180 degrees, and flip + // the position across the exit linedef, if reversed. + angle_t angle = (reverse ? pos = FRACUNIT-pos, 0 : ANG180) + + R_PointToAngle2(0, 0, l->dx, l->dy) - + R_PointToAngle2(0, 0, line->dx, line->dy); + + // Interpolate position across the exit linedef + fixed_t x = l->v2->x - FixedMul(pos, l->dx); + fixed_t y = l->v2->y - FixedMul(pos, l->dy); + + // Sine, cosine of angle adjustment + fixed_t s = finesine[angle>>ANGLETOFINESHIFT]; + fixed_t c = finecosine[angle>>ANGLETOFINESHIFT]; + + // Maximum distance thing can be moved away from interpolated + // exit, to ensure that it is on the correct side of exit linedef + int fudge = FUDGEFACTOR; + + // Whether this is a player, and if so, a pointer to its player_t. + // Voodoo dolls are excluded by making sure thing->player->mo==thing. + player_t *player = thing->player && thing->player->mo == thing ? + thing->player : NULL; + + // Whether walking towards first side of exit linedef steps down + int stepdown = + l->frontsector->floorheight < l->backsector->floorheight; + + // Height of thing above ground + fixed_t z = thing->z - thing->floorz; + + // Side to exit the linedef on positionally. + // + // Notes: + // + // This flag concerns exit position, not momentum. Due to + // roundoff error, the thing can land on either the left or + // the right side of the exit linedef, and steps must be + // taken to make sure it does not end up on the wrong side. + // + // Exit momentum is always towards side 1 in a reversed + // teleporter, and always towards side 0 otherwise. + // + // Exiting positionally on side 1 is always safe, as far + // as avoiding oscillations and stuck-in-wall problems, + // but may not be optimum for non-reversed teleporters. + // + // Exiting on side 0 can cause oscillations if momentum + // is towards side 1, as it is with reversed teleporters. + // + // Exiting on side 1 slightly improves player viewing + // when going down a step on a non-reversed teleporter. + + int side = reverse || (player && stepdown); + + // Make sure we are on correct side of exit linedef. + while (P_PointOnLineSide(x, y, l) != side && --fudge>=0) + if (D_abs(l->dx) > D_abs(l->dy)) + y -= l->dx < 0 != side ? -1 : 1; + else + x += l->dy < 0 != side ? -1 : 1; + + // Attempt to teleport, aborting if blocked + if (!P_TeleportMove(thing, x, y, false)) /* killough 8/9/98 */ + return 0; + + // e6y + if (player && player->mo == thing) + R_ResetAfterTeleport(player); + + // Adjust z position to be same height above ground as before. + // Ground level at the exit is measured as the higher of the + // two floor heights at the exit linedef. + thing->z = z + sides[l->sidenum[stepdown]].sector->floorheight; + + // Rotate thing's orientation according to difference in linedef angles + thing->angle += angle; + + // Momentum of thing crossing teleporter linedef + x = thing->momx; + y = thing->momy; + + // Rotate thing's momentum to come out of exit just like it entered + thing->momx = FixedMul(x, c) - FixedMul(y, s); + thing->momy = FixedMul(y, c) + FixedMul(x, s); + + // Adjust a player's view, in case there has been a height change + if (player) + { + // Save the current deltaviewheight, used in stepping + fixed_t deltaviewheight = player->deltaviewheight; + + // Clear deltaviewheight, since we don't want any changes now + player->deltaviewheight = 0; + + // Set player's view according to the newly set parameters + P_CalcHeight(player); + + // Reset the delta to have the same dynamics as before + player->deltaviewheight = deltaviewheight; + } + + // e6y + if (player && player->mo == thing) + R_ResetAfterTeleport(player); + + return 1; + } + return 0; +} diff --git a/src/p_tick.c b/src/p_tick.c new file mode 100644 index 0000000..6046046 --- /dev/null +++ b/src/p_tick.c @@ -0,0 +1,291 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000,2002 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Thinker, Ticker. + * + *-----------------------------------------------------------------------------*/ + +#include "doomstat.h" +#include "p_user.h" +#include "p_spec.h" +#include "p_tick.h" +#include "p_map.h" +#include "r_fps.h" + +int leveltime; + +static boolean newthinkerpresent; + +// +// THINKERS +// All thinkers should be allocated by Z_Malloc +// so they can be operated on uniformly. +// The actual structures will vary in size, +// but the first element must be thinker_t. +// + +// killough 8/29/98: we maintain several separate threads, each containing +// a special class of thinkers, to allow more efficient searches. +thinker_t thinkerclasscap[th_all+1]; + +// +// P_InitThinkers +// + +void P_InitThinkers(void) +{ + int i; + + for (i=0; ifunction == P_RemoveThinkerDelayed ? th_delete : + thinker->function == P_MobjThinker && + ((mobj_t *) thinker)->health > 0 && + (((mobj_t *) thinker)->flags & MF_COUNTKILL || + ((mobj_t *) thinker)->type == MT_SKULL) ? + ((mobj_t *) thinker)->flags & MF_FRIEND ? + th_friends : th_enemies : th_misc; + + { + /* Remove from current thread, if in one */ + if ((th = thinker->cnext)!= NULL) + (th->cprev = thinker->cprev)->cnext = th; + } + + // Add to appropriate thread + th = &thinkerclasscap[class]; + th->cprev->cnext = thinker; + thinker->cnext = th; + thinker->cprev = th->cprev; + th->cprev = thinker; +} + +// +// P_AddThinker +// Adds a new thinker at the end of the list. +// + +void P_AddThinker(thinker_t* thinker) +{ + thinkercap.prev->next = thinker; + thinker->next = &thinkercap; + thinker->prev = thinkercap.prev; + thinkercap.prev = thinker; + + thinker->references = 0; // killough 11/98: init reference counter to 0 + + // killough 8/29/98: set sentinel pointers, and then add to appropriate list + thinker->cnext = thinker->cprev = NULL; + P_UpdateThinker(thinker); + newthinkerpresent = true; +} + +// +// killough 11/98: +// +// Make currentthinker external, so that P_RemoveThinkerDelayed +// can adjust currentthinker when thinkers self-remove. + +static thinker_t *currentthinker; + +// +// P_RemoveThinkerDelayed() +// +// Called automatically as part of the thinker loop in P_RunThinkers(), +// on nodes which are pending deletion. +// +// If this thinker has no more pointers referencing it indirectly, +// remove it, and set currentthinker to one node preceeding it, so +// that the next step in P_RunThinkers() will get its successor. +// + +void P_RemoveThinkerDelayed(thinker_t *thinker) +{ + if (!thinker->references) + { + { /* Remove from main thinker list */ + thinker_t *next = thinker->next; + /* Note that currentthinker is guaranteed to point to us, + * and since we're freeing our memory, we had better change that. So + * point it to thinker->prev, so the iterator will correctly move on to + * thinker->prev->next = thinker->next */ + (next->prev = currentthinker = thinker->prev)->next = next; + } + { + /* Remove from current thinker class list */ + thinker_t *th = thinker->cnext; + (th->cprev = thinker->cprev)->cnext = th; + } + Z_Free(thinker); + } +} + +// +// P_RemoveThinker +// +// Deallocation is lazy -- it will not actually be freed +// until its thinking turn comes up. +// +// killough 4/25/98: +// +// Instead of marking the function with -1 value cast to a function pointer, +// set the function to P_RemoveThinkerDelayed(), so that later, it will be +// removed automatically as part of the thinker process. +// + +void P_RemoveThinker(thinker_t *thinker) +{ + R_StopInterpolationIfNeeded(thinker); + thinker->function = P_RemoveThinkerDelayed; + + P_UpdateThinker(thinker); +} + +/* cph 2002/01/13 - iterator for thinker list + * WARNING: Do not modify thinkers between calls to this functin + */ +thinker_t* P_NextThinker(thinker_t* th, th_class cl) +{ + thinker_t* top = &thinkerclasscap[cl]; + if (!th) th = top; + th = cl == th_all ? th->next : th->cnext; + return th == top ? NULL : th; +} + +/* + * P_SetTarget + * + * This function is used to keep track of pointer references to mobj thinkers. + * In Doom, objects such as lost souls could sometimes be removed despite + * their still being referenced. In Boom, 'target' mobj fields were tested + * during each gametic, and any objects pointed to by them would be prevented + * from being removed. But this was incomplete, and was slow (every mobj was + * checked during every gametic). Now, we keep a count of the number of + * references, and delay removal until the count is 0. + */ + +void P_SetTarget(mobj_t **mop, mobj_t *targ) +{ + if (*mop) // If there was a target already, decrease its refcount + (*mop)->thinker.references--; + if ((*mop = targ)) // Set new target and if non-NULL, increase its counter + targ->thinker.references++; +} + +// +// P_RunThinkers +// +// killough 4/25/98: +// +// Fix deallocator to stop using "next" pointer after node has been freed +// (a Doom bug). +// +// Process each thinker. For thinkers which are marked deleted, we must +// load the "next" pointer prior to freeing the node. In Doom, the "next" +// pointer was loaded AFTER the thinker was freed, which could have caused +// crashes. +// +// But if we are not deleting the thinker, we should reload the "next" +// pointer after calling the function, in case additional thinkers are +// added at the end of the list. +// +// killough 11/98: +// +// Rewritten to delete nodes implicitly, by making currentthinker +// external and using P_RemoveThinkerDelayed() implicitly. +// + +static void P_RunThinkers (void) +{ + for (currentthinker = thinkercap.next; + currentthinker != &thinkercap; + currentthinker = currentthinker->next) + { + if (newthinkerpresent) + R_ActivateThinkerInterpolations(currentthinker); + if (currentthinker->function) + currentthinker->function(currentthinker); + } + newthinkerpresent = false; +} + +// +// P_Ticker +// + +void P_Ticker (void) +{ + int i; + + /* pause if in menu and at least one tic has been run + * + * killough 9/29/98: note that this ties in with basetic, + * since G_Ticker does the pausing during recording or + * playback, and compenates by incrementing basetic. + * + * All of this complicated mess is used to preserve demo sync. + */ + + if (paused || (menuactive && !demoplayback && !netgame && + players[consoleplayer].viewz != 1)) + return; + + R_UpdateInterpolations (); + + P_MapStart(); + // not if this is an intermission screen + if(gamestate==GS_LEVEL) + for (i=0; i>= ANGLETOFINESHIFT; + player->mo->momx += FixedMul(move,finecosine[angle]); + player->mo->momy += FixedMul(move,finesine[angle]); + } + + +/* + * P_Bob + * Same as P_Thrust, but only affects bobbing. + * + * killough 10/98: We apply thrust separately between the real physical player + * and the part which affects bobbing. This way, bobbing only comes from player + * motion, nothing external, avoiding many problems, e.g. bobbing should not + * occur on conveyors, unless the player walks on one, and bobbing should be + * reduced at a regular rate, even on ice (where the player coasts). + */ + +static void P_Bob(player_t *player, angle_t angle, fixed_t move) +{ + //e6y + if (!mbf_features) + return; + + player->momx += FixedMul(move,finecosine[angle >>= ANGLETOFINESHIFT]); + player->momy += FixedMul(move,finesine[angle]); +} + +// +// P_CalcHeight +// Calculate the walking / running height adjustment +// + +void P_CalcHeight (player_t* player) + { + int angle; + fixed_t bob; + + // Regular movement bobbing + // (needs to be calculated for gun swing + // even if not on ground) + // OPTIMIZE: tablify angle + // Note: a LUT allows for effects + // like a ramp with low health. + + + /* killough 10/98: Make bobbing depend only on player-applied motion. + * + * Note: don't reduce bobbing here if on ice: if you reduce bobbing here, + * it causes bobbing jerkiness when the player moves from ice to non-ice, + * and vice-versa. + */ + player->bob = !mbf_features ? + (FixedMul (player->mo->momx, player->mo->momx) + + FixedMul (player->mo->momy,player->mo->momy))>>2 : + player_bobbing ? (FixedMul(player->momx,player->momx) + + FixedMul(player->momy,player->momy))>>2 : 0; + + //e6y + if (compatibility_level >= boom_202_compatibility && + compatibility_level <= lxdoom_1_compatibility && + player->mo->friction > ORIG_FRICTION) // ice? + { + if (player->bob > (MAXBOB>>2)) + player->bob = MAXBOB>>2; + } + else + + if (player->bob > MAXBOB) + player->bob = MAXBOB; + + if (!onground || player->cheats & CF_NOMOMENTUM) + { + player->viewz = player->mo->z + VIEWHEIGHT; + + if (player->viewz > player->mo->ceilingz-4*FRACUNIT) + player->viewz = player->mo->ceilingz-4*FRACUNIT; + +// The following line was in the Id source and appears // phares 2/25/98 +// to be a bug. player->viewz is checked in a similar +// manner at a different exit below. + +// player->viewz = player->mo->z + player->viewheight; + return; + } + + angle = (FINEANGLES/20*leveltime)&FINEMASK; + bob = FixedMul(player->bob/2,finesine[angle]); + + // move viewheight + + if (player->playerstate == PST_LIVE) + { + player->viewheight += player->deltaviewheight; + + if (player->viewheight > VIEWHEIGHT) + { + player->viewheight = VIEWHEIGHT; + player->deltaviewheight = 0; + } + + if (player->viewheight < VIEWHEIGHT/2) + { + player->viewheight = VIEWHEIGHT/2; + if (player->deltaviewheight <= 0) + player->deltaviewheight = 1; + } + + if (player->deltaviewheight) + { + player->deltaviewheight += FRACUNIT/4; + if (!player->deltaviewheight) + player->deltaviewheight = 1; + } + } + + player->viewz = player->mo->z + player->viewheight + bob; + + if (player->viewz > player->mo->ceilingz-4*FRACUNIT) + player->viewz = player->mo->ceilingz-4*FRACUNIT; + } + + +// +// P_MovePlayer +// +// Adds momentum if the player is not in the air +// +// killough 10/98: simplified + +void P_MovePlayer (player_t* player) +{ + ticcmd_t *cmd = &player->cmd; + mobj_t *mo = player->mo; + + mo->angle += cmd->angleturn << 16; + onground = mo->z <= mo->floorz; + + // e6y + if (demo_smoothturns && player == &players[displayplayer]) + R_SmoothPlaying_Add(cmd->angleturn << 16); + + // killough 10/98: + // + // We must apply thrust to the player and bobbing separately, to avoid + // anomalies. The thrust applied to bobbing is always the same strength on + // ice, because the player still "works just as hard" to move, while the + // thrust applied to the movement varies with 'movefactor'. + + //e6y + if ((!demo_compatibility && !mbf_features) || (cmd->forwardmove | cmd->sidemove)) // killough 10/98 + { + if (onground || mo->flags & MF_BOUNCES) // killough 8/9/98 + { + int friction, movefactor = P_GetMoveFactor(mo, &friction); + + // killough 11/98: + // On sludge, make bobbing depend on efficiency. + // On ice, make it depend on effort. + + int bobfactor = + friction < ORIG_FRICTION ? movefactor : ORIG_FRICTION_FACTOR; + + if (cmd->forwardmove) + { + P_Bob(player,mo->angle,cmd->forwardmove*bobfactor); + P_Thrust(player,mo->angle,cmd->forwardmove*movefactor); + } + + if (cmd->sidemove) + { + P_Bob(player,mo->angle-ANG90,cmd->sidemove*bobfactor); + P_Thrust(player,mo->angle-ANG90,cmd->sidemove*movefactor); + } + } + if (mo->state == states+S_PLAY) + P_SetMobjState(mo,S_PLAY_RUN1); + } +} + +#define ANG5 (ANG90/18) + +// +// P_DeathThink +// Fall on your face when dying. +// Decrease POV height to floor height. +// + +void P_DeathThink (player_t* player) + { + angle_t angle; + angle_t delta; + + P_MovePsprites (player); + + // fall to the ground + + if (player->viewheight > 6*FRACUNIT) + player->viewheight -= FRACUNIT; + + if (player->viewheight < 6*FRACUNIT) + player->viewheight = 6*FRACUNIT; + + player->deltaviewheight = 0; + onground = (player->mo->z <= player->mo->floorz); + P_CalcHeight (player); + + if (player->attacker && player->attacker != player->mo) + { + angle = R_PointToAngle2 (player->mo->x, + player->mo->y, + player->attacker->x, + player->attacker->y); + + delta = angle - player->mo->angle; + + if (delta < ANG5 || delta > (unsigned)-ANG5) + { + // Looking at killer, + // so fade damage flash down. + + player->mo->angle = angle; + + if (player->damagecount) + player->damagecount--; + } + else if (delta < ANG180) + player->mo->angle += ANG5; + else + player->mo->angle -= ANG5; + } + else if (player->damagecount) + player->damagecount--; + + if (player->cmd.buttons & BT_USE) + player->playerstate = PST_REBORN; + R_SmoothPlaying_Reset(player); // e6y + } + + +// +// P_PlayerThink +// + +void P_PlayerThink (player_t* player) + { + ticcmd_t* cmd; + weapontype_t newweapon; + + if (movement_smooth && players && &players[displayplayer] == player) + { + original_view_vars.viewx = player->mo->x; + original_view_vars.viewy = player->mo->y; + original_view_vars.viewz = player->viewz; + original_view_vars.viewangle = R_SmoothPlaying_Get(player->mo->angle) + viewangleoffset; + } + + // killough 2/8/98, 3/21/98: + if (player->cheats & CF_NOCLIP) + player->mo->flags |= MF_NOCLIP; + else + player->mo->flags &= ~MF_NOCLIP; + + // chain saw run forward + + cmd = &player->cmd; + if (player->mo->flags & MF_JUSTATTACKED) + { + cmd->angleturn = 0; + cmd->forwardmove = 0xc800/512; + cmd->sidemove = 0; + player->mo->flags &= ~MF_JUSTATTACKED; + } + + if (player->playerstate == PST_DEAD) + { + P_DeathThink (player); + return; + } + + // Move around. + // Reactiontime is used to prevent movement + // for a bit after a teleport. + + if (player->mo->reactiontime) + player->mo->reactiontime--; + else + P_MovePlayer (player); + + P_CalcHeight (player); // Determines view height and bobbing + + // Determine if there's anything about the sector you're in that's + // going to affect you, like painful floors. + + if (player->mo->subsector->sector->special) + P_PlayerInSpecialSector (player); + + // Check for weapon change. + + if (cmd->buttons & BT_CHANGE) + { + // The actual changing of the weapon is done + // when the weapon psprite can do it + // (read: not in the middle of an attack). + + newweapon = (cmd->buttons & BT_WEAPONMASK)>>BT_WEAPONSHIFT; + + // killough 3/22/98: For demo compatibility we must perform the fist + // and SSG weapons switches here, rather than in G_BuildTiccmd(). For + // other games which rely on user preferences, we must use the latter. + + if (demo_compatibility) + { // compatibility mode -- required for old demos -- killough + if (newweapon == wp_fist && player->weaponowned[wp_chainsaw] && + (player->readyweapon != wp_chainsaw || + !player->powers[pw_strength])) + newweapon = wp_chainsaw; + if (gamemode == commercial && + newweapon == wp_shotgun && + player->weaponowned[wp_supershotgun] && + player->readyweapon != wp_supershotgun) + newweapon = wp_supershotgun; + } + + // killough 2/8/98, 3/22/98 -- end of weapon selection changes + + if (player->weaponowned[newweapon] && newweapon != player->readyweapon) + + // Do not go to plasma or BFG in shareware, + // even if cheated. + + if ((newweapon != wp_plasma && newweapon != wp_bfg) + || (gamemode != shareware) ) + player->pendingweapon = newweapon; + } + + // check for use + + if (cmd->buttons & BT_USE) + { + if (!player->usedown) + { + P_UseLines (player); + player->usedown = true; + } + } + else + player->usedown = false; + + // cycle psprites + + P_MovePsprites (player); + + // Counters, time dependent power ups. + + // Strength counts up to diminish fade. + + if (player->powers[pw_strength]) + player->powers[pw_strength]++; + + // killough 1/98: Make idbeholdx toggle: + + if (player->powers[pw_invulnerability] > 0) // killough + player->powers[pw_invulnerability]--; + + if (player->powers[pw_invisibility] > 0) // killough + if (! --player->powers[pw_invisibility] ) + player->mo->flags &= ~MF_SHADOW; + + if (player->powers[pw_infrared] > 0) // killough + player->powers[pw_infrared]--; + + if (player->powers[pw_ironfeet] > 0) // killough + player->powers[pw_ironfeet]--; + + if (player->damagecount) + player->damagecount--; + + if (player->bonuscount) + player->bonuscount--; + + // Handling colormaps. + // killough 3/20/98: reformat to terse C syntax + + player->fixedcolormap = player->powers[pw_invulnerability] > 4*32 || + player->powers[pw_invulnerability] & 8 ? INVERSECOLORMAP : + player->powers[pw_infrared] > 4*32 || player->powers[pw_infrared] & 8; + } diff --git a/src/p_user.h b/src/p_user.h new file mode 100644 index 0000000..b0540e8 --- /dev/null +++ b/src/p_user.h @@ -0,0 +1,47 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Player related stuff. + * Bobbing POV/weapon, movement. + * Pending weapon. + * + *-----------------------------------------------------------------------------*/ + +#ifndef __P_USER__ +#define __P_USER__ + +#include "d_player.h" + +void P_PlayerThink(player_t *player); +void P_CalcHeight(player_t *player); +void P_DeathThink(player_t *player); +void P_MovePlayer(player_t *player); +void P_Thrust(player_t *player, angle_t angle, fixed_t move); + +#endif /* __P_USER__ */ diff --git a/src/protocol.h b/src/protocol.h new file mode 100644 index 0000000..c2af10d --- /dev/null +++ b/src/protocol.h @@ -0,0 +1,96 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Doom Network protocol packet definitions. + *-----------------------------------------------------------------------------*/ + +#include "doomtype.h" +#include "d_ticcmd.h" +#include "m_swap.h" + +enum packet_type_e { + PKT_INIT, // initial packet to server + PKT_SETUP, // game information packet + PKT_GO, // game has started + PKT_TICC, // tics from client + PKT_TICS, // tics from server + PKT_RETRANS, // Request for retransmission + PKT_EXTRA, // Extra info packet + PKT_QUIT, // Player quit game + PKT_DOWN, // Server downed + PKT_WAD, // Wad file request + PKT_BACKOFF, // Request for client back-off +}; + +typedef struct { + byte checksum; // Simple checksum of the entire packet + byte type; /* Type of packet */ + byte reserved[2]; /* Was random in prboom <=2.2.4, now 0 */ + unsigned tic; // Timestamp +} PACKEDATTR packet_header_t; + +static inline void packet_set(packet_header_t* p, enum packet_type_e t, unsigned long tic) +{ p->tic = doom_htonl(tic); p->type = t; p->reserved[0] = 0; p->reserved[1] = 0; } + +#ifndef GAME_OPTIONS_SIZE +// From g_game.h +#define GAME_OPTIONS_SIZE 64 +#endif + +struct setup_packet_s { + byte players, yourplayer, skill, episode, level, deathmatch, complevel, ticdup, extratic; + byte game_options[GAME_OPTIONS_SIZE]; + byte numwads; + byte wadnames[1]; // Actually longer +}; + +/* cph - convert network byte stream to usable ticcmd_t and visa-versa + * - the functions are functionally identical apart from parameters + * - the void* param can be unaligned. By using void* as the parameter + * it means gcc won't assume alignment so won't make false assumptions + * when optimising. So I'm told. + */ +inline static void RawToTic(ticcmd_t* dst, const void* src) +{ + memcpy(dst,src,sizeof *dst); + dst->angleturn = doom_ntohs(dst->angleturn); + dst->consistancy = doom_ntohs(dst->consistancy); +} + +inline static void TicToRaw(void* dst, const ticcmd_t* src) +{ + /* We have to make a copy of the source struct, then do byte swaps, + * and fnially copy to the destination (can't do the swaps in the + * destination, because it might not be aligned). + */ + ticcmd_t tmp = *src; + tmp.angleturn = doom_ntohs(tmp.angleturn); + tmp.consistancy = doom_ntohs(tmp.consistancy); + memcpy(dst,&tmp,sizeof tmp); +} diff --git a/src/r_bsp.c b/src/r_bsp.c new file mode 100644 index 0000000..e9ce6c5 --- /dev/null +++ b/src/r_bsp.c @@ -0,0 +1,664 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * BSP traversal, handling of LineSegs for rendering. + * + *-----------------------------------------------------------------------------*/ + +#include "doomstat.h" +#include "m_bbox.h" +#include "r_main.h" +#include "r_segs.h" +#include "r_plane.h" +#include "r_things.h" +#include "r_bsp.h" // cph - sanity checking +#include "v_video.h" +#include "lprintf.h" + +seg_t *curline; +side_t *sidedef; +line_t *linedef; +sector_t *frontsector; +sector_t *backsector; +drawseg_t *ds_p; + +// killough 4/7/98: indicates doors closed wrt automap bugfix: +// cph - replaced by linedef rendering flags - int doorclosed; + +// killough: New code which removes 2s linedef limit +drawseg_t *drawsegs; +unsigned maxdrawsegs; +// drawseg_t drawsegs[MAXDRAWSEGS]; // old code -- killough + +// +// R_ClearDrawSegs +// + +void R_ClearDrawSegs(void) +{ + ds_p = drawsegs; +} + +// CPhipps - +// Instead of clipsegs, let's try using an array with one entry for each column, +// indicating whether it's blocked by a solid wall yet or not. + +byte solidcol[MAX_SCREENWIDTH]; + +// CPhipps - +// R_ClipWallSegment +// +// Replaces the old R_Clip*WallSegment functions. It draws bits of walls in those +// columns which aren't solid, and updates the solidcol[] array appropriately + +static void R_ClipWallSegment(int first, int last, boolean solid) +{ + byte *p; + while (first < last) { + if (solidcol[first]) { + if (!(p = memchr(solidcol+first, 0, last-first))) return; // All solid + first = p - solidcol; + } else { + int to; + if (!(p = memchr(solidcol+first, 1, last-first))) to = last; + else to = p - solidcol; + R_StoreWallRange(first, to-1); + if (solid) { + memset(solidcol+first,1,to-first); + } + first = to; + } + } +} + +// +// R_ClearClipSegs +// + +void R_ClearClipSegs (void) +{ + memset(solidcol, 0, SCREENWIDTH); +} + +// killough 1/18/98 -- This function is used to fix the automap bug which +// showed lines behind closed doors simply because the door had a dropoff. +// +// cph - converted to R_RecalcLineFlags. This recalculates all the flags for +// a line, including closure and texture tiling. + +static void R_RecalcLineFlags(void) +{ + linedef->r_validcount = gametic; + + /* First decide if the line is closed, normal, or invisible */ + if (!(linedef->flags & ML_TWOSIDED) + || backsector->ceilingheight <= frontsector->floorheight + || backsector->floorheight >= frontsector->ceilingheight + || ( + // if door is closed because back is shut: + backsector->ceilingheight <= backsector->floorheight + + // preserve a kind of transparent door/lift special effect: + && (backsector->ceilingheight >= frontsector->ceilingheight || + curline->sidedef->toptexture) + + && (backsector->floorheight <= frontsector->floorheight || + curline->sidedef->bottomtexture) + + // properly render skies (consider door "open" if both ceilings are sky): + && (backsector->ceilingpic !=skyflatnum || + frontsector->ceilingpic!=skyflatnum) + ) + ) + linedef->r_flags = RF_CLOSED; + else { + // Reject empty lines used for triggers + // and special events. + // Identical floor and ceiling on both sides, + // identical light levels on both sides, + // and no middle texture. + // CPhipps - recode for speed, not certain if this is portable though + if (backsector->ceilingheight != frontsector->ceilingheight + || backsector->floorheight != frontsector->floorheight + || curline->sidedef->midtexture + || memcmp(&backsector->floor_xoffs, &frontsector->floor_xoffs, + sizeof(frontsector->floor_xoffs) + sizeof(frontsector->floor_yoffs) + + sizeof(frontsector->ceiling_xoffs) + sizeof(frontsector->ceiling_yoffs) + + sizeof(frontsector->ceilingpic) + sizeof(frontsector->floorpic) + + sizeof(frontsector->lightlevel) + sizeof(frontsector->floorlightsec) + + sizeof(frontsector->ceilinglightsec))) { + linedef->r_flags = 0; return; + } else + linedef->r_flags = RF_IGNORE; + } + + /* cph - I'm too lazy to try and work with offsets in this */ + if (curline->sidedef->rowoffset) return; + + /* Now decide on texture tiling */ + if (linedef->flags & ML_TWOSIDED) { + int c; + + /* Does top texture need tiling */ + if ((c = frontsector->ceilingheight - backsector->ceilingheight) > 0 && + (textureheight[texturetranslation[curline->sidedef->toptexture]] > c)) + linedef->r_flags |= RF_TOP_TILE; + + /* Does bottom texture need tiling */ + if ((c = frontsector->floorheight - backsector->floorheight) > 0 && + (textureheight[texturetranslation[curline->sidedef->bottomtexture]] > c)) + linedef->r_flags |= RF_BOT_TILE; + } else { + int c; + /* Does middle texture need tiling */ + if ((c = frontsector->ceilingheight - frontsector->floorheight) > 0 && + (textureheight[texturetranslation[curline->sidedef->midtexture]] > c)) + linedef->r_flags |= RF_MID_TILE; + } +} + +// +// killough 3/7/98: Hack floor/ceiling heights for deep water etc. +// +// If player's view height is underneath fake floor, lower the +// drawn ceiling to be just under the floor height, and replace +// the drawn floor and ceiling textures, and light level, with +// the control sector's. +// +// Similar for ceiling, only reflected. +// +// killough 4/11/98, 4/13/98: fix bugs, add 'back' parameter +// + +sector_t *R_FakeFlat(sector_t *sec, sector_t *tempsec, + int *floorlightlevel, int *ceilinglightlevel, + boolean back) +{ + if (floorlightlevel) + *floorlightlevel = sec->floorlightsec == -1 ? + sec->lightlevel : sectors[sec->floorlightsec].lightlevel; + + if (ceilinglightlevel) + *ceilinglightlevel = sec->ceilinglightsec == -1 ? // killough 4/11/98 + sec->lightlevel : sectors[sec->ceilinglightsec].lightlevel; + + if (sec->heightsec != -1) + { + const sector_t *s = §ors[sec->heightsec]; + int heightsec = viewplayer->mo->subsector->sector->heightsec; + int underwater = heightsec!=-1 && viewz<=sectors[heightsec].floorheight; + + // Replace sector being drawn, with a copy to be hacked + *tempsec = *sec; + + // Replace floor and ceiling height with other sector's heights. + tempsec->floorheight = s->floorheight; + tempsec->ceilingheight = s->ceilingheight; + + // killough 11/98: prevent sudden light changes from non-water sectors: + if (underwater && (tempsec-> floorheight = sec->floorheight, + tempsec->ceilingheight = s->floorheight-1, !back)) + { // head-below-floor hack + tempsec->floorpic = s->floorpic; + tempsec->floor_xoffs = s->floor_xoffs; + tempsec->floor_yoffs = s->floor_yoffs; + + if (underwater) { + if (s->ceilingpic == skyflatnum) { + tempsec->floorheight = tempsec->ceilingheight+1; + tempsec->ceilingpic = tempsec->floorpic; + tempsec->ceiling_xoffs = tempsec->floor_xoffs; + tempsec->ceiling_yoffs = tempsec->floor_yoffs; + } else { + tempsec->ceilingpic = s->ceilingpic; + tempsec->ceiling_xoffs = s->ceiling_xoffs; + tempsec->ceiling_yoffs = s->ceiling_yoffs; + } + } + + tempsec->lightlevel = s->lightlevel; + + if (floorlightlevel) + *floorlightlevel = s->floorlightsec == -1 ? s->lightlevel : + sectors[s->floorlightsec].lightlevel; // killough 3/16/98 + + if (ceilinglightlevel) + *ceilinglightlevel = s->ceilinglightsec == -1 ? s->lightlevel : + sectors[s->ceilinglightsec].lightlevel; // killough 4/11/98 + } + else + if (heightsec != -1 && viewz >= sectors[heightsec].ceilingheight && + sec->ceilingheight > s->ceilingheight) + { // Above-ceiling hack + tempsec->ceilingheight = s->ceilingheight; + tempsec->floorheight = s->ceilingheight + 1; + + tempsec->floorpic = tempsec->ceilingpic = s->ceilingpic; + tempsec->floor_xoffs = tempsec->ceiling_xoffs = s->ceiling_xoffs; + tempsec->floor_yoffs = tempsec->ceiling_yoffs = s->ceiling_yoffs; + + if (s->floorpic != skyflatnum) + { + tempsec->ceilingheight = sec->ceilingheight; + tempsec->floorpic = s->floorpic; + tempsec->floor_xoffs = s->floor_xoffs; + tempsec->floor_yoffs = s->floor_yoffs; + } + + tempsec->lightlevel = s->lightlevel; + + if (floorlightlevel) + *floorlightlevel = s->floorlightsec == -1 ? s->lightlevel : + sectors[s->floorlightsec].lightlevel; // killough 3/16/98 + + if (ceilinglightlevel) + *ceilinglightlevel = s->ceilinglightsec == -1 ? s->lightlevel : + sectors[s->ceilinglightsec].lightlevel; // killough 4/11/98 + } + sec = tempsec; // Use other sector + } + return sec; +} + +// +// R_AddLine +// Clips the given segment +// and adds any visible pieces to the line list. +// + +static void R_AddLine (seg_t *line) +{ + int x1; + int x2; + angle_t angle1; + angle_t angle2; + angle_t span; + angle_t tspan; + static sector_t tempsec; // killough 3/8/98: ceiling/water hack + + curline = line; + + angle1 = R_PointToAngle (line->v1->x, line->v1->y); + angle2 = R_PointToAngle (line->v2->x, line->v2->y); + + // Clip to view edges. + span = angle1 - angle2; + + // Back side, i.e. backface culling + if (span >= ANG180) + return; + + // Global angle needed by segcalc. + rw_angle1 = angle1; + angle1 -= viewangle; + angle2 -= viewangle; + + tspan = angle1 + clipangle; + if (tspan > 2*clipangle) + { + tspan -= 2*clipangle; + + // Totally off the left edge? + if (tspan >= span) + return; + + angle1 = clipangle; + } + + tspan = clipangle - angle2; + if (tspan > 2*clipangle) + { + tspan -= 2*clipangle; + + // Totally off the left edge? + if (tspan >= span) + return; + angle2 = 0-clipangle; + } + + // The seg is in the view range, + // but not necessarily visible. + + angle1 = (angle1+ANG90)>>ANGLETOFINESHIFT; + angle2 = (angle2+ANG90)>>ANGLETOFINESHIFT; + + // killough 1/31/98: Here is where "slime trails" can SOMETIMES occur: + x1 = viewangletox[angle1]; + x2 = viewangletox[angle2]; + +#ifdef GL_DOOM + // proff 11/99: we have to add these segs to avoid gaps in OpenGL + if (x1 >= x2) // killough 1/31/98 -- change == to >= for robustness + { + if (V_GetMode() == VID_MODEGL) + { + if (ds_p == drawsegs+maxdrawsegs) // killough 1/98 -- fix 2s line HOM + { + unsigned pos = ds_p - drawsegs; // jff 8/9/98 fix from ZDOOM1.14a + unsigned newmax = maxdrawsegs ? maxdrawsegs*2 : 128; // killough + drawsegs = realloc(drawsegs,newmax*sizeof(*drawsegs)); + //ds_p = drawsegs+maxdrawsegs; + ds_p = drawsegs + pos; // jff 8/9/98 fix from ZDOOM1.14a + maxdrawsegs = newmax; + } + ds_p->curline = curline; + ds_p++; + gld_AddWall(curline); + return; + } + else + return; + } +#else + // Does not cross a pixel? + if (x1 >= x2) // killough 1/31/98 -- change == to >= for robustness + return; +#endif + + backsector = line->backsector; + + // Single sided line? + if (backsector) + // killough 3/8/98, 4/4/98: hack for invisible ceilings / deep water + backsector = R_FakeFlat(backsector, &tempsec, NULL, NULL, true); + + /* cph - roll up linedef properties in flags */ + if ((linedef = curline->linedef)->r_validcount != gametic) + R_RecalcLineFlags(); + + if (linedef->r_flags & RF_IGNORE) + { + return; + } + else + R_ClipWallSegment (x1, x2, linedef->r_flags & RF_CLOSED); +} + +// +// R_CheckBBox +// Checks BSP node/subtree bounding box. +// Returns true +// if some part of the bbox might be visible. +// + +static const int checkcoord[12][4] = // killough -- static const +{ + {3,0,2,1}, + {3,0,2,0}, + {3,1,2,0}, + {0}, + {2,0,2,1}, + {0,0,0,0}, + {3,1,3,0}, + {0}, + {2,0,3,1}, + {2,1,3,1}, + {2,1,3,0} +}; + +// killough 1/28/98: static // CPhipps - const parameter, reformatted +static boolean R_CheckBBox(const fixed_t *bspcoord) +{ + angle_t angle1, angle2; + + { + int boxpos; + const int* check; + + // Find the corners of the box + // that define the edges from current viewpoint. + boxpos = (viewx <= bspcoord[BOXLEFT] ? 0 : viewx < bspcoord[BOXRIGHT ] ? 1 : 2) + + (viewy >= bspcoord[BOXTOP ] ? 0 : viewy > bspcoord[BOXBOTTOM] ? 4 : 8); + + if (boxpos == 5) + return true; + + check = checkcoord[boxpos]; + angle1 = R_PointToAngle (bspcoord[check[0]], bspcoord[check[1]]) - viewangle; + angle2 = R_PointToAngle (bspcoord[check[2]], bspcoord[check[3]]) - viewangle; + } + + // cph - replaced old code, which was unclear and badly commented + // Much more efficient code now + if ((signed)angle1 < (signed)angle2) { /* it's "behind" us */ + /* Either angle1 or angle2 is behind us, so it doesn't matter if we + * change it to the corect sign + */ + if ((angle1 >= ANG180) && (angle1 < ANG270)) + angle1 = INT_MAX; /* which is ANG180-1 */ + else + angle2 = INT_MIN; + } + + if ((signed)angle2 >= (signed)clipangle) return false; // Both off left edge + if ((signed)angle1 <= -(signed)clipangle) return false; // Both off right edge + if ((signed)angle1 >= (signed)clipangle) angle1 = clipangle; // Clip at left edge + if ((signed)angle2 <= -(signed)clipangle) angle2 = 0-clipangle; // Clip at right edge + + // Find the first clippost + // that touches the source post + // (adjacent pixels are touching). + angle1 = (angle1+ANG90)>>ANGLETOFINESHIFT; + angle2 = (angle2+ANG90)>>ANGLETOFINESHIFT; + { + int sx1 = viewangletox[angle1]; + int sx2 = viewangletox[angle2]; + // const cliprange_t *start; + + // Does not cross a pixel. + if (sx1 == sx2) + return false; + + if (!memchr(solidcol+sx1, 0, sx2-sx1)) return false; + // All columns it covers are already solidly covered + } + + return true; +} + +// +// R_Subsector +// Determine floor/ceiling planes. +// Add sprites of things in sector. +// Draw one or more line segments. +// +// killough 1/31/98 -- made static, polished + +static void R_Subsector(int num) +{ + int count; + seg_t *line; + subsector_t *sub; + sector_t tempsec; // killough 3/7/98: deep water hack + int floorlightlevel; // killough 3/16/98: set floor lightlevel + int ceilinglightlevel; // killough 4/11/98 +#ifdef GL_DOOM + visplane_t dummyfloorplane; + visplane_t dummyceilingplane; +#endif + +#ifdef RANGECHECK + if (num>=numsubsectors) + I_Error ("R_Subsector: ss %i with numss = %i", num, numsubsectors); +#endif + + sub = &subsectors[num]; + frontsector = sub->sector; + count = sub->numlines; + line = &segs[sub->firstline]; + + // killough 3/8/98, 4/4/98: Deep water / fake ceiling effect + frontsector = R_FakeFlat(frontsector, &tempsec, &floorlightlevel, + &ceilinglightlevel, false); // killough 4/11/98 + + // killough 3/7/98: Add (x,y) offsets to flats, add deep water check + // killough 3/16/98: add floorlightlevel + // killough 10/98: add support for skies transferred from sidedefs + + floorplane = frontsector->floorheight < viewz || // killough 3/7/98 + (frontsector->heightsec != -1 && + sectors[frontsector->heightsec].ceilingpic == skyflatnum) ? + R_FindPlane(frontsector->floorheight, + frontsector->floorpic == skyflatnum && // kilough 10/98 + frontsector->sky & PL_SKYFLAT ? frontsector->sky : + frontsector->floorpic, + floorlightlevel, // killough 3/16/98 + frontsector->floor_xoffs, // killough 3/7/98 + frontsector->floor_yoffs + ) : NULL; + + ceilingplane = frontsector->ceilingheight > viewz || + frontsector->ceilingpic == skyflatnum || + (frontsector->heightsec != -1 && + sectors[frontsector->heightsec].floorpic == skyflatnum) ? + R_FindPlane(frontsector->ceilingheight, // killough 3/8/98 + frontsector->ceilingpic == skyflatnum && // kilough 10/98 + frontsector->sky & PL_SKYFLAT ? frontsector->sky : + frontsector->ceilingpic, + ceilinglightlevel, // killough 4/11/98 + frontsector->ceiling_xoffs, // killough 3/7/98 + frontsector->ceiling_yoffs + ) : NULL; +#ifdef GL_DOOM + // check if the sector is faked + if ((frontsector==sub->sector) && (V_GetMode() == VID_MODEGL)) + { + // if the sector has bottomtextures, then the floorheight will be set to the + // highest surounding floorheight + if ((frontsector->no_bottomtextures) || (!floorplane)) + { + int i=frontsector->linecount; + + dummyfloorplane.height=INT_MIN; + while (i--) + { + line_t *tmpline=frontsector->lines[i]; + if (tmpline->backsector) + if (tmpline->backsector != frontsector) + if (tmpline->backsector->floorheight>dummyfloorplane.height) + { + dummyfloorplane.height=tmpline->backsector->floorheight; + dummyfloorplane.lightlevel=tmpline->backsector->lightlevel; + } + if (tmpline->frontsector) + if (tmpline->frontsector != frontsector) + if (tmpline->frontsector->floorheight>dummyfloorplane.height) + { + dummyfloorplane.height=tmpline->frontsector->floorheight; + dummyfloorplane.lightlevel=tmpline->frontsector->lightlevel; + } + } + if (dummyfloorplane.height!=INT_MIN) + floorplane=&dummyfloorplane; + } + // the same for ceilings. they will be set to the lowest ceilingheight + if ((frontsector->no_toptextures) || (!ceilingplane)) + { + int i=frontsector->linecount; + + dummyceilingplane.height=INT_MAX; + while (i--) + { + line_t *tmpline=frontsector->lines[i]; + if (tmpline->backsector) + if (tmpline->backsector != frontsector) + if (tmpline->backsector->ceilingheightbacksector->ceilingheight; + dummyceilingplane.lightlevel=tmpline->backsector->lightlevel; + } + if (tmpline->frontsector) + if (tmpline->frontsector != frontsector) + if (tmpline->frontsector->ceilingheightfrontsector->ceilingheight; + dummyceilingplane.lightlevel=tmpline->frontsector->lightlevel; + } + } + if (dummyceilingplane.height!=INT_MAX) + ceilingplane=&dummyceilingplane; + } + } +#endif + + // killough 9/18/98: Fix underwater slowdown, by passing real sector + // instead of fake one. Improve sprite lighting by basing sprite + // lightlevels on floor & ceiling lightlevels in the surrounding area. + // + // 10/98 killough: + // + // NOTE: TeamTNT fixed this bug incorrectly, messing up sprite lighting!!! + // That is part of the 242 effect!!! If you simply pass sub->sector to + // the old code you will not get correct lighting for underwater sprites!!! + // Either you must pass the fake sector and handle validcount here, on the + // real sector, or you must account for the lighting in some other way, + // like passing it as an argument. + + R_AddSprites(sub, (floorlightlevel+ceilinglightlevel)/2); + while (count--) + { + if (line->miniseg == false) + R_AddLine (line); + line++; + curline = NULL; /* cph 2001/11/18 - must clear curline now we're done with it, so R_ColourMap doesn't try using it for other things */ + } +#ifdef GL_DOOM + if (V_GetMode() == VID_MODEGL) + gld_AddPlane(num, floorplane, ceilingplane); +#endif +} + +// +// RenderBSPNode +// Renders all subsectors below a given node, +// traversing subtree recursively. +// Just call with BSP root. +// +// killough 5/2/98: reformatted, removed tail recursion + +void R_RenderBSPNode(int bspnum) +{ + while (!(bspnum & NF_SUBSECTOR)) // Found a subsector? + { + const node_t *bsp = &nodes[bspnum]; + + // Decide which side the view point is on. + int side = R_PointOnSide(viewx, viewy, bsp); + // Recursively divide front space. + R_RenderBSPNode(bsp->children[side]); + + // Possibly divide back space. + + if (!R_CheckBBox(bsp->bbox[side^1])) + return; + + bspnum = bsp->children[side^1]; + } + R_Subsector(bspnum == -1 ? 0 : bspnum & ~NF_SUBSECTOR); +} diff --git a/src/r_bsp.h b/src/r_bsp.h new file mode 100644 index 0000000..4ba9190 --- /dev/null +++ b/src/r_bsp.h @@ -0,0 +1,64 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Refresh module, BSP traversal and handling. + * + *-----------------------------------------------------------------------------*/ + +#ifndef __R_BSP__ +#define __R_BSP__ + +#ifdef __GNUG__ +#pragma interface +#endif + +extern seg_t *curline; +extern side_t *sidedef; +extern line_t *linedef; +extern sector_t *frontsector; +extern sector_t *backsector; + +/* old code -- killough: + * extern drawseg_t drawsegs[MAXDRAWSEGS]; + * new code -- killough: */ +extern drawseg_t *drawsegs; +extern unsigned maxdrawsegs; + +extern byte solidcol[MAX_SCREENWIDTH]; + +extern drawseg_t *ds_p; + +void R_ClearClipSegs(void); +void R_ClearDrawSegs(void); +void R_RenderBSPNode(int bspnum); + +/* killough 4/13/98: fake floors/ceilings for deep water / fake ceilings: */ +sector_t *R_FakeFlat(sector_t *, sector_t *, int *, int *, boolean); + +#endif diff --git a/src/r_data.c b/src/r_data.c new file mode 100644 index 0000000..bf2c6c7 --- /dev/null +++ b/src/r_data.c @@ -0,0 +1,745 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2002 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Preparation of data for rendering, + * generation of lookups, caching, retrieval by name. + * + *-----------------------------------------------------------------------------*/ + +#include "doomstat.h" +#include "w_wad.h" +#include "r_draw.h" +#include "r_main.h" +#include "r_sky.h" +#include "i_system.h" +#include "r_bsp.h" +#include "r_things.h" +#include "p_tick.h" +#include "lprintf.h" // jff 08/03/98 - declaration of lprintf +#include "p_tick.h" + +// +// Graphics. +// DOOM graphics for walls and sprites +// is stored in vertical runs of opaque pixels (posts). +// A column is composed of zero or more posts, +// a patch or sprite is composed of zero or more columns. +// + +// +// Texture definition. +// Each texture is composed of one or more patches, +// with patches being lumps stored in the WAD. +// The lumps are referenced by number, and patched +// into the rectangular texture space using origin +// and possibly other attributes. +// + +typedef struct +{ + short originx; + short originy; + short patch; + short stepdir; // unused in Doom but might be used in Phase 2 Boom + short colormap; // unused in Doom but might be used in Phase 2 Boom +} PACKEDATTR mappatch_t; + + +typedef struct +{ + char name[8]; + char pad2[4]; // unused + short width; + short height; + char pad[4]; // unused in Doom but might be used in Boom Phase 2 + short patchcount; + mappatch_t patches[1]; +} PACKEDATTR maptexture_t; + +// A maptexturedef_t describes a rectangular texture, which is composed +// of one or more mappatch_t structures that arrange graphic patches. + +// killough 4/17/98: make firstcolormaplump,lastcolormaplump external +int firstcolormaplump, lastcolormaplump; // killough 4/17/98 + +int firstflat, lastflat, numflats; +int firstspritelump, lastspritelump, numspritelumps; +int numtextures; +texture_t **textures; // proff - 04/05/2000 removed static for OpenGL +fixed_t *textureheight; //needed for texture pegging (and TFE fix - killough) +int *flattranslation; // for global animation +int *texturetranslation; + +// +// R_GetTextureColumn +// + +const byte *R_GetTextureColumn(const rpatch_t *texpatch, int col) { + while (col < 0) + col += texpatch->width; + col &= texpatch->widthmask; + + return texpatch->columns[col].pixels; +} + +// +// R_InitTextures +// Initializes the texture list +// with the textures from the world map. +// + +static void R_InitTextures (void) +{ + const maptexture_t *mtexture; + texture_t *texture; + const mappatch_t *mpatch; + texpatch_t *patch; + int i, j; + int maptex_lump[2] = {-1, -1}; + const int *maptex; + const int *maptex1, *maptex2; + char name[9]; + int names_lump; // cph - new wad lump handling + const char *names; // cph - + const char *name_p;// const*'s + int *patchlookup; + int totalwidth; + int nummappatches; + int offset; + int maxoff, maxoff2; + int numtextures1, numtextures2; + const int *directory; + int errors = 0; + + // Load the patch names from pnames.lmp. + name[8] = 0; + names = W_CacheLumpNum(names_lump = W_GetNumForName("PNAMES")); + nummappatches = LONG(*((const int *)names)); + name_p = names+4; + patchlookup = malloc(nummappatches*sizeof(*patchlookup)); // killough + + for (i=0 ; i maxoff) + I_Error("R_InitTextures: Bad texture directory"); + + mtexture = (const maptexture_t *) ( (const byte *)maptex + offset); + + texture = textures[i] = + Z_Malloc(sizeof(texture_t) + + sizeof(texpatch_t)*(SHORT(mtexture->patchcount)-1), + PU_STATIC, 0); + + texture->width = SHORT(mtexture->width); + texture->height = SHORT(mtexture->height); + texture->patchcount = SHORT(mtexture->patchcount); + + /* Mattias Engdegård emailed me of the following explenation of + * why memcpy doesnt work on some systems: + * "I suppose it is the mad unaligned allocation + * going on (and which gcc in some way manages to cope with + * through the __attribute__ ((packed))), and which it forgets + * when optimizing memcpy (to a single word move) since it appears + * to be aligned. Technically a gcc bug, but I can't blame it when + * it's stressed with that amount of + * non-standard nonsense." + * So in short the unaligned struct confuses gcc's optimizer so + * i took the memcpy out alltogether to avoid future problems-Jess + */ + /* The above was #ifndef SPARC, but i got a mail from + * Putera Joseph F NPRI containing: + * I had to use the memcpy function on a sparc machine. The + * other one would give me a core dump. + * cph - I find it hard to believe that sparc memcpy is broken, + * but I don't believe the pointers to memcpy have to be aligned + * either. Use fast memcpy on other machines anyway. + */ +/* + proff - I took this out, because Oli Kraus (olikraus@yahoo.com) told + me the memcpy produced a buserror. Since this function isn't time- + critical I'm using the for loop now. +*/ +/* +#ifndef GCC + memcpy(texture->name, mtexture->name, sizeof(texture->name)); +#else +*/ + { + int j; + for(j=0;jname);j++) + texture->name[j]=mtexture->name[j]; + } +/* #endif */ + + mpatch = mtexture->patches; + patch = texture->patches; + + for (j=0 ; jpatchcount ; j++, mpatch++, patch++) + { + patch->originx = SHORT(mpatch->originx); + patch->originy = SHORT(mpatch->originy); + patch->patch = patchlookup[SHORT(mpatch->patch)]; + if (patch->patch == -1) + { + //jff 8/3/98 use logical output routine + lprintf(LO_ERROR,"\nR_InitTextures: Missing patch %d in texture %.8s", + SHORT(mpatch->patch), texture->name); // killough 4/17/98 + ++errors; + } + } + + for (j=1; j*2 <= texture->width; j<<=1) + ; + texture->widthmask = j-1; + textureheight[i] = texture->height<width; + } + + free(patchlookup); // killough + + for (i=0; i<2; i++) // cph - release the TEXTUREx lumps + if (maptex_lump[i] != -1) + W_UnlockLumpNum(maptex_lump[i]); + + if (errors) + I_Error("R_InitTextures: %d errors", errors); + + // Precalculate whatever possible. + if (devparm) // cph - If in development mode, generate now so all errors are found at once + for (i=0 ; iindex = -1; + while (--i >= 0) + { + int j = W_LumpNameHash(textures[i]->name) % (unsigned) numtextures; + textures[i]->next = textures[j]->index; // Prepend to chain + textures[j]->index = i; + } +} + +// +// R_InitFlats +// +static void R_InitFlats(void) +{ + int i; + + firstflat = W_GetNumForName("F_START") + 1; + lastflat = W_GetNumForName("F_END") - 1; + numflats = lastflat - firstflat + 1; + + // Create translation table for global animation. + // killough 4/9/98: make column offsets 32-bit; + // clean up malloc-ing to use sizeof + + flattranslation = + Z_Malloc((numflats+1)*sizeof(*flattranslation), PU_STATIC, 0); + + for (i=0 ; i x ? l : x > u ? u : x); } + +const lighttable_t* R_ColourMap(int lightlevel, fixed_t spryscale) +{ + if (fixedcolormap) return fixedcolormap; + else { + if (curline) + if (curline->v1->y == curline->v2->y) + lightlevel -= 1 << LIGHTSEGSHIFT; + else + if (curline->v1->x == curline->v2->x) + lightlevel += 1 << LIGHTSEGSHIFT; + + lightlevel += extralight << LIGHTSEGSHIFT; + + /* cph 2001/11/17 - + * Work out what colour map to use, remembering to clamp it to the number of + * colour maps we actually have. This formula is basically the one from the + * original source, just brought into one place. The main difference is it + * throws away less precision in the lightlevel half, so it supports 32 + * light levels in WADs compared to Doom's 16. + * + * Note we can make it more accurate if we want - we should keep all the + * precision until the final step, so slight scale differences can count + * against slight light level variations. + */ + return fullcolormap + between(0,NUMCOLORMAPS-1, + ((256-lightlevel)*2*NUMCOLORMAPS/256) - 4 + - (FixedMul(spryscale,pspriteiscale)/2 >> LIGHTSCALESHIFT) + )*256; + } +} + +// +// R_InitTranMap +// +// Initialize translucency filter map +// +// By Lee Killough 2/21/98 +// + +int tran_filter_pct = 66; // filter percent + +#define TSC 12 /* number of fixed point digits in filter percent */ + +void R_InitTranMap(int progress) +{ + int lump = W_CheckNumForName("TRANMAP"); + + // If a tranlucency filter map lump is present, use it + + if (lump != -1) // Set a pointer to the translucency filter maps. + main_tranmap = W_CacheLumpNum(lump); // killough 4/11/98 + else if (W_CheckNumForName("PLAYPAL")!=-1) // can be called before WAD loaded + { // Compose a default transparent filter map based on PLAYPAL. + const byte *playpal = W_CacheLumpName("PLAYPAL"); + byte *my_tranmap; + + char fname[PATH_MAX+1]; + struct { + unsigned char pct; + unsigned char playpal[256]; + } cache; + FILE *cachefp = fopen(strcat(strcpy(fname, I_DoomExeDir()), "/tranmap.dat"),"rb"); + + main_tranmap = my_tranmap = Z_Malloc(256*256, PU_STATIC, 0); // killough 4/11/98 + + // Use cached translucency filter if it's available + + if (!cachefp || + fread(&cache, 1, sizeof cache, cachefp) != sizeof cache || + cache.pct != tran_filter_pct || + memcmp(cache.playpal, playpal, sizeof cache.playpal) || + fread(my_tranmap, 256, 256, cachefp) != 256 ) // killough 4/11/98 + { + long pal[3][256], tot[256], pal_w1[3][256]; + long w1 = ((unsigned long) tran_filter_pct<=0); + } + + // Next, compute all entries using minimum arithmetic. + + { + int i,j; + byte *tp = my_tranmap; + for (i=0;i<256;i++) + { + long r1 = pal[0][i] * w2; + long g1 = pal[1][i] * w2; + long b1 = pal[2][i] * w2; + if (!(i & 31) && progress) + //jff 8/3/98 use logical output routine + lprintf(LO_INFO,"."); + for (j=0;j<256;j++,tp++) + { + register int color = 255; + register long err; + long r = pal_w1[0][j] + r1; + long g = pal_w1[1][j] + g1; + long b = pal_w1[2][j] + b1; + long best = LONG_MAX; + do + if ((err = tot[color] - pal[0][color]*r + - pal[1][color]*g - pal[2][color]*b) < best) + best = err, *tp = color; + while (--color >= 0); + } + } + } + if ((cachefp = fopen(fname,"wb")) != NULL) // write out the cached translucency map + { + cache.pct = tran_filter_pct; + memcpy(cache.playpal, playpal, 256); + fseek(cachefp, 0, SEEK_SET); + fwrite(&cache, 1, sizeof cache, cachefp); + fwrite(main_tranmap, 256, 256, cachefp); + // CPhipps - leave close for a few lines... + } + } + + if (cachefp) // killough 11/98: fix filehandle leak + fclose(cachefp); + + W_UnlockLumpName("PLAYPAL"); + } +} + +// +// R_InitData +// Locates all the lumps +// that will be used by all views +// Must be called after W_Init. +// + +void R_InitData(void) +{ + lprintf(LO_INFO, "Textures "); + R_InitTextures(); + lprintf(LO_INFO, "Flats "); + R_InitFlats(); + lprintf(LO_INFO, "Sprites "); + R_InitSpriteLumps(); + if (default_translucency) // killough 3/1/98 + R_InitTranMap(1); // killough 2/21/98, 3/6/98 + R_InitColormaps(); // killough 3/20/98 +} + +// +// R_FlatNumForName +// Retrieval, get a flat number for a flat name. +// +// killough 4/17/98: changed to use ns_flats namespace +// + +int R_FlatNumForName(const char *name) // killough -- const added +{ + int i = (W_CheckNumForName)(name, ns_flats); + if (i == -1) + I_Error("R_FlatNumForName: %.8s not found", name); + return i - firstflat; +} + +// +// R_CheckTextureNumForName +// Check whether texture is available. +// Filter out NoTexture indicator. +// +// Rewritten by Lee Killough to use hash table for fast lookup. Considerably +// reduces the time needed to start new levels. See w_wad.c for comments on +// the hashing algorithm, which is also used for lump searches. +// +// killough 1/21/98, 1/31/98 +// + +int PUREFUNC R_CheckTextureNumForName(const char *name) +{ + int i = NO_TEXTURE; + if (*name != '-') // "NoTexture" marker. + { + i = textures[W_LumpNameHash(name) % (unsigned) numtextures]->index; + while (i >= 0 && strncasecmp(textures[i]->name,name,8)) + i = textures[i]->next; + } + return i; +} + +// +// R_TextureNumForName +// Calls R_CheckTextureNumForName, +// aborts with error message. +// + +int PUREFUNC R_TextureNumForName(const char *name) // const added -- killough +{ + int i = R_CheckTextureNumForName(name); + if (i == -1) + I_Error("R_TextureNumForName: %.8s not found", name); + return i; +} + +// +// R_SafeTextureNumForName +// Calls R_CheckTextureNumForName, and changes any error to NO_TEXTURE +int PUREFUNC R_SafeTextureNumForName(const char *name, int snum) +{ + int i = R_CheckTextureNumForName(name); + if (i == -1) { + i = NO_TEXTURE; // e6y - return "no texture" + lprintf(LO_DEBUG,"bad texture '%s' in sidedef %d\n",name,snum); + } + return i; +} + +// +// R_PrecacheLevel +// Preloads all relevant graphics for the level. +// +// Totally rewritten by Lee Killough to use less memory, +// to avoid using alloca(), and to improve performance. +// cph - new wad lump handling, calls cache functions but acquires no locks + +static inline void precache_lump(int l) +{ + W_CacheLumpNum(l); W_UnlockLumpNum(l); +} + +void R_PrecacheLevel(void) +{ + register int i; + register byte *hitlist; + + if (demoplayback) + return; + + { + size_t size = numflats > numsprites ? numflats : numsprites; + hitlist = malloc((size_t)numtextures > size ? numtextures : size); + } + + // Precache flats. + + memset(hitlist, 0, numflats); + + for (i = numsectors; --i >= 0; ) + hitlist[sectors[i].floorpic] = hitlist[sectors[i].ceilingpic] = 1; + + for (i = numflats; --i >= 0; ) + if (hitlist[i]) + precache_lump(firstflat + i); + + // Precache textures. + + memset(hitlist, 0, numtextures); + + for (i = numsides; --i >= 0;) + hitlist[sides[i].bottomtexture] = + hitlist[sides[i].toptexture] = + hitlist[sides[i].midtexture] = 1; + + // Sky texture is always present. + // Note that F_SKY1 is the name used to + // indicate a sky floor/ceiling as a flat, + // while the sky texture is stored like + // a wall texture, with an episode dependend + // name. + + hitlist[skytexture] = 1; + + for (i = numtextures; --i >= 0; ) + if (hitlist[i]) + { + texture_t *texture = textures[i]; + int j = texture->patchcount; + while (--j >= 0) + precache_lump(texture->patches[j].patch); + } + + // Precache sprites. + memset(hitlist, 0, numsprites); + + { + thinker_t *th = NULL; + while ((th = P_NextThinker(th,th_all)) != NULL) + if (th->function == P_MobjThinker) + hitlist[((mobj_t *)th)->sprite] = 1; + } + + for (i=numsprites; --i >= 0;) + if (hitlist[i]) + { + int j = sprites[i].numframes; + while (--j >= 0) + { + short *sflump = sprites[i].spriteframes[j].lump; + int k = 7; + do + precache_lump(firstspritelump + sflump[k]); + while (--k >= 0); + } + } + free(hitlist); +} + +// Proff - Added for OpenGL +void R_SetPatchNum(patchnum_t *patchnum, const char *name) +{ + const rpatch_t *patch = R_CachePatchName(name); + patchnum->width = patch->width; + patchnum->height = patch->height; + patchnum->leftoffset = patch->leftoffset; + patchnum->topoffset = patch->topoffset; + patchnum->lumpnum = W_GetNumForName(name); + R_UnlockPatchName(name); +} diff --git a/src/r_data.h b/src/r_data.h new file mode 100644 index 0000000..98f15b4 --- /dev/null +++ b/src/r_data.h @@ -0,0 +1,109 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Refresh module, data I/O, caching, retrieval of graphics + * by name. + * + *-----------------------------------------------------------------------------*/ + + +#ifndef __R_DATA__ +#define __R_DATA__ + +#include "r_defs.h" +#include "r_state.h" +#include "r_patch.h" + +#ifdef __GNUG__ +#pragma interface +#endif + +// A single patch from a texture definition, basically +// a rectangular area within the texture rectangle. +typedef struct +{ + int originx, originy; // Block origin, which has already accounted + int patch; // for the internal origin of the patch. +} texpatch_t; + +// +// Texture definition. +// A DOOM wall texture is a list of patches +// which are to be combined in a predefined order. +// + +typedef struct +{ + char name[8]; // Keep name for switch changing, etc. + int next, index; // killough 1/31/98: used in hashing algorithm + // CPhipps - moved arrays with per-texture entries to elements here + unsigned widthmask; + // CPhipps - end of additions + short width, height; + short patchcount; // All the patches[patchcount] are drawn + texpatch_t patches[1]; // back-to-front into the cached texture. +} texture_t; + +extern int numtextures; +extern texture_t **textures; + + +const byte *R_GetTextureColumn(const rpatch_t *texpatch, int col); + + +// I/O, setting up the stuff. +void R_InitData (void); +void R_PrecacheLevel (void); + + +// Retrieval. +// Floor/ceiling opaque texture tiles, +// lookup by name. For animation? +int R_FlatNumForName (const char* name); // killough -- const added + + +// R_*TextureNumForName returns the texture number for the texture name, or NO_TEXTURE if +// there is no texture (i.e. "-") specified. +/* cph 2006/07/23 - defined value for no-texture marker (texture "-" in the WAD file) */ +#define NO_TEXTURE 0 +int PUREFUNC R_TextureNumForName (const char *name); // killough -- const added; cph - now PUREFUNC +int PUREFUNC R_SafeTextureNumForName (const char *name, int snum); +int PUREFUNC R_CheckTextureNumForName (const char *name); + +void R_InitTranMap(int); // killough 3/6/98: translucency initialization +int R_ColormapNumForName(const char *name); // killough 4/4/98 +/* cph 2001/11/17 - new func to do lighting calcs and get suitable colour map */ +const lighttable_t* R_ColourMap(int lightlevel, fixed_t spryscale); + +extern const byte *main_tranmap, *tranmap; + +/* Proff - Added for OpenGL - cph - const char* param */ +void R_SetPatchNum(patchnum_t *patchnum, const char *name); + +#endif diff --git a/src/r_defs.h b/src/r_defs.h new file mode 100644 index 0000000..07057bb --- /dev/null +++ b/src/r_defs.h @@ -0,0 +1,428 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Refresh/rendering module, shared data struct definitions. + * + *-----------------------------------------------------------------------------*/ + +#ifndef __R_DEFS__ +#define __R_DEFS__ + +// Screenwidth. +#include "doomdef.h" + +// Some more or less basic data types +// we depend on. +#include "m_fixed.h" + +// We rely on the thinker data struct +// to handle sound origins in sectors. +#include "d_think.h" + +// SECTORS do store MObjs anyway. +#include "p_mobj.h" + +#ifdef __GNUG__ +#pragma interface +#endif + +// Silhouette, needed for clipping Segs (mainly) +// and sprites representing things. +#define SIL_NONE 0 +#define SIL_BOTTOM 1 +#define SIL_TOP 2 +#define SIL_BOTH 3 + +#define MAXDRAWSEGS 256 + +// +// INTERNAL MAP TYPES +// used by play and refresh +// + +// +// Your plain vanilla vertex. +// Note: transformed values not buffered locally, +// like some DOOM-alikes ("wt", "WebView") do. +// +typedef struct +{ + fixed_t x, y; +} vertex_t; + +// Each sector has a degenmobj_t in its center for sound origin purposes. +typedef struct +{ + thinker_t thinker; // not used for anything + fixed_t x, y, z; +} degenmobj_t; + +// +// The SECTORS record, at runtime. +// Stores things/mobjs. +// + +typedef struct +{ + int iSectorID; // proff 04/05/2000: needed for OpenGL and used in debugmode by the HUD to draw sectornum + boolean no_toptextures; + boolean no_bottomtextures; + fixed_t floorheight; + fixed_t ceilingheight; + int nexttag,firsttag; // killough 1/30/98: improves searches for tags. + int soundtraversed; // 0 = untraversed, 1,2 = sndlines-1 + mobj_t *soundtarget; // thing that made a sound (or null) + int blockbox[4]; // mapblock bounding box for height changes + degenmobj_t soundorg; // origin for any sounds played by the sector + int validcount; // if == validcount, already checked + mobj_t *thinglist; // list of mobjs in sector + + /* killough 8/28/98: friction is a sector property, not an mobj property. + * these fields used to be in mobj_t, but presented performance problems + * when processed as mobj properties. Fix is to make them sector properties. + */ + int friction,movefactor; + + // thinker_t for reversable actions + void *floordata; // jff 2/22/98 make thinkers on + void *ceilingdata; // floors, ceilings, lighting, + void *lightingdata; // independent of one another + + // jff 2/26/98 lockout machinery for stairbuilding + int stairlock; // -2 on first locked -1 after thinker done 0 normally + int prevsec; // -1 or number of sector for previous step + int nextsec; // -1 or number of next step sector + + // killough 3/7/98: support flat heights drawn at another sector's heights + int heightsec; // other sector, or -1 if no other sector + + int bottommap, midmap, topmap; // killough 4/4/98: dynamic colormaps + + // list of mobjs that are at least partially in the sector + // thinglist is a subset of touching_thinglist + struct msecnode_s *touching_thinglist; // phares 3/14/98 + + int linecount; + struct line_s **lines; + + // killough 10/98: support skies coming from sidedefs. Allows scrolling + // skies and other effects. No "level info" kind of lump is needed, + // because you can use an arbitrary number of skies per level with this + // method. This field only applies when skyflatnum is used for floorpic + // or ceilingpic, because the rest of Doom needs to know which is sky + // and which isn't, etc. + + int sky; + + // killough 3/7/98: floor and ceiling texture offsets + fixed_t floor_xoffs, floor_yoffs; + fixed_t ceiling_xoffs, ceiling_yoffs; + + // killough 4/11/98: support for lightlevels coming from another sector + int floorlightsec, ceilinglightsec; + + short floorpic; + short ceilingpic; + short lightlevel; + short special; + short oldspecial; //jff 2/16/98 remembers if sector WAS secret (automap) + short tag; +} sector_t; + +// +// The SideDef. +// + +typedef struct +{ + fixed_t textureoffset; // add this to the calculated texture column + fixed_t rowoffset; // add this to the calculated texture top + short toptexture; // Texture indices. We do not maintain names here. + short bottomtexture; + short midtexture; + sector_t* sector; // Sector the SideDef is facing. + + // killough 4/4/98, 4/11/98: highest referencing special linedef's type, + // or lump number of special effect. Allows texture names to be overloaded + // for other functions. + + int special; + +} side_t; + +// +// Move clipping aid for LineDefs. +// +typedef enum +{ + ST_HORIZONTAL, + ST_VERTICAL, + ST_POSITIVE, + ST_NEGATIVE +} slopetype_t; + +typedef struct line_s +{ + int iLineID; // proff 04/05/2000: needed for OpenGL + vertex_t *v1, *v2; // Vertices, from v1 to v2. + fixed_t dx, dy; // Precalculated v2 - v1 for side checking. + unsigned short flags; // Animation related. + short special; + short tag; + unsigned short sidenum[2]; // Visual appearance: SideDefs. + fixed_t bbox[4]; // A bounding box, for the linedef's extent + slopetype_t slopetype; // To aid move clipping. + sector_t *frontsector; // Front and back sector. + sector_t *backsector; + int validcount; // if == validcount, already checked + void *specialdata; // thinker_t for reversable actions + int tranlump; // killough 4/11/98: translucency filter, -1 == none + int firsttag,nexttag; // killough 4/17/98: improves searches for tags. + int r_validcount; // cph: if == gametic, r_flags already done + enum { // cph: + RF_TOP_TILE = 1, // Upper texture needs tiling + RF_MID_TILE = 2, // Mid texture needs tiling + RF_BOT_TILE = 4, // Lower texture needs tiling + RF_IGNORE = 8, // Renderer can skip this line + RF_CLOSED =16, // Line blocks view + } r_flags; + degenmobj_t soundorg; // sound origin for switches/buttons +} line_t; + + +// phares 3/14/98 +// +// Sector list node showing all sectors an object appears in. +// +// There are two threads that flow through these nodes. The first thread +// starts at touching_thinglist in a sector_t and flows through the m_snext +// links to find all mobjs that are entirely or partially in the sector. +// The second thread starts at touching_sectorlist in an mobj_t and flows +// through the m_tnext links to find all sectors a thing touches. This is +// useful when applying friction or push effects to sectors. These effects +// can be done as thinkers that act upon all objects touching their sectors. +// As an mobj moves through the world, these nodes are created and +// destroyed, with the links changed appropriately. +// +// For the links, NULL means top or end of list. + +typedef struct msecnode_s +{ + sector_t *m_sector; // a sector containing this object + struct mobj_s *m_thing; // this object + struct msecnode_s *m_tprev; // prev msecnode_t for this thing + struct msecnode_s *m_tnext; // next msecnode_t for this thing + struct msecnode_s *m_sprev; // prev msecnode_t for this sector + struct msecnode_s *m_snext; // next msecnode_t for this sector + boolean visited; // killough 4/4/98, 4/7/98: used in search algorithms +} msecnode_t; + +// +// The LineSeg. +// +typedef struct +{ + vertex_t *v1, *v2; + fixed_t offset; + angle_t angle; + side_t* sidedef; + line_t* linedef; + + int iSegID; // proff 11/05/2000: needed for OpenGL + // figgi -- needed for glnodes + float length; + boolean miniseg; + + + // Sector references. + // Could be retrieved from linedef, too + // (but that would be slower -- killough) + // backsector is NULL for one sided lines + + sector_t *frontsector, *backsector; +} seg_t; + + +// +// A SubSector. +// References a Sector. +// Basically, this is a list of LineSegs, +// indicating the visible walls that define +// (all or some) sides of a convex BSP leaf. +// + +typedef struct subsector_s +{ + sector_t *sector; + unsigned short numlines, firstline; +} subsector_t; + + +// +// BSP node. +// +typedef struct +{ + fixed_t x, y, dx, dy; // Partition line. + fixed_t bbox[2][4]; // Bounding box for each child. + unsigned short children[2]; // If NF_SUBSECTOR its a subsector. +} node_t; + +// +// OTHER TYPES +// + +// This could be wider for >8 bit display. +// Indeed, true color support is posibble +// precalculating 24bpp lightmap/colormap LUT. +// from darkening PLAYPAL to all black. +// Could use even more than 32 levels. + +typedef byte lighttable_t; + +// +// Masked 2s linedefs +// + +typedef struct drawseg_s +{ + seg_t *curline; + int x1, x2; + fixed_t scale1, scale2, scalestep; + int silhouette; // 0=none, 1=bottom, 2=top, 3=both + fixed_t bsilheight; // do not clip sprites above this + fixed_t tsilheight; // do not clip sprites below this + + // Added for filtering (fractional texture u coord) support - POPE + fixed_t rw_offset, rw_distance, rw_centerangle; + + // Pointers to lists for sprite clipping, + // all three adjusted so [x1] is first value. + + int *sprtopclip, *sprbottomclip, *maskedtexturecol; // dropoff overflow +} drawseg_t; + +// proff: Added for OpenGL +typedef struct +{ + int width,height; + int leftoffset,topoffset; + int lumpnum; +} patchnum_t; + +// +// A vissprite_t is a thing that will be drawn during a refresh. +// i.e. a sprite object that is partly visible. +// + +typedef struct vissprite_s +{ + mobj_t *thing; + boolean flip; + int x1, x2; + fixed_t gx, gy; // for line side calculation + fixed_t gz, gzt; // global bottom / top for silhouette clipping + fixed_t startfrac; // horizontal position of x1 + fixed_t scale; + fixed_t xiscale; // negative if flipped + fixed_t texturemid; + int patch; + uint_64_t mobjflags; + + // for color translation and shadow draw, maxbright frames as well + const lighttable_t *colormap; + + // killough 3/27/98: height sector for underwater/fake ceiling support + int heightsec; + + boolean isplayersprite; +} vissprite_t; + +// +// Sprites are patches with a special naming convention +// so they can be recognized by R_InitSprites. +// The base name is NNNNFx or NNNNFxFx, with +// x indicating the rotation, x = 0, 1-7. +// The sprite and frame specified by a thing_t +// is range checked at run time. +// A sprite is a patch_t that is assumed to represent +// a three dimensional object and may have multiple +// rotations pre drawn. +// Horizontal flipping is used to save space, +// thus NNNNF2F5 defines a mirrored patch. +// Some sprites will only have one picture used +// for all views: NNNNF0 +// + +typedef struct +{ + // If false use 0 for any position. + // Note: as eight entries are available, + // we might as well insert the same name eight times. + boolean rotate; + + // Lump to use for view angles 0-7. + short lump[8]; + + // Flip bit (1 = flip) to use for view angles 0-7. + byte flip[8]; + +} spriteframe_t; + +// +// A sprite definition: +// a number of animation frames. +// + +typedef struct +{ + int numframes; + spriteframe_t *spriteframes; +} spritedef_t; + +// +// Now what is a visplane, anyway? +// +// Go to http://classicgaming.com/doom/editing/ to find out -- killough +// + +typedef struct visplane +{ + struct visplane *next; // Next visplane in hash chain -- killough + int picnum, lightlevel, minx, maxx; + fixed_t height; + fixed_t xoffs, yoffs; // killough 2/28/98: Support scrolling flats + unsigned int pad1; // leave pads for [minx-1]/[maxx+1] + unsigned int top[MAX_SCREENWIDTH]; + unsigned int pad2, pad3; // killough 2/8/98, 4/25/98 + unsigned int bottom[MAX_SCREENWIDTH]; + unsigned int pad4; // dropoff overflow +} visplane_t; + +#endif diff --git a/src/r_demo.c b/src/r_demo.c new file mode 100644 index 0000000..0f9e6f1 --- /dev/null +++ b/src/r_demo.c @@ -0,0 +1,88 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze, Andrey Budko + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Demo stuff + * + *--------------------------------------------------------------------- + */ + +#include "doomstat.h" +#include "r_demo.h" +#include "r_fps.h" + +int demo_smoothturns = false; +int demo_smoothturnsfactor = 6; + +static int smooth_playing_turns[SMOOTH_PLAYING_MAXFACTOR]; +static int_64_t smooth_playing_sum; +static int smooth_playing_index; +static angle_t smooth_playing_angle; + +void R_SmoothPlaying_Reset(player_t *player) +{ + if (demo_smoothturns && demoplayback && players) + { + if (!player) + player = &players[displayplayer]; + + if (player==&players[displayplayer]) + { + smooth_playing_angle = players[displayplayer].mo->angle; + memset(smooth_playing_turns, 0, sizeof(smooth_playing_turns[0]) * SMOOTH_PLAYING_MAXFACTOR); + smooth_playing_sum = 0; + smooth_playing_index = 0; + } + } +} + +void R_SmoothPlaying_Add(int delta) +{ + if (demo_smoothturns && demoplayback) + { + smooth_playing_sum -= smooth_playing_turns[smooth_playing_index]; + smooth_playing_turns[smooth_playing_index] = delta; + smooth_playing_index = (smooth_playing_index + 1)%(demo_smoothturnsfactor); + smooth_playing_sum += delta; + smooth_playing_angle += (int)(smooth_playing_sum/(demo_smoothturnsfactor)); + } +} + +angle_t R_SmoothPlaying_Get(angle_t defangle) +{ + if (demo_smoothturns && demoplayback) + return smooth_playing_angle; + else + return defangle; +} + +void R_ResetAfterTeleport(player_t *player) +{ + R_ResetViewInterpolation(); + R_SmoothPlaying_Reset(player); +} diff --git a/src/r_demo.h b/src/r_demo.h new file mode 100644 index 0000000..e5be4cb --- /dev/null +++ b/src/r_demo.h @@ -0,0 +1,45 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze, Andrey Budko + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Demo stuff + * + *--------------------------------------------------------------------- + */ + +#include "doomstat.h" + +#define SMOOTH_PLAYING_MAXFACTOR 16 + +extern int demo_smoothturns; +extern int demo_smoothturnsfactor; + +void R_SmoothPlaying_Reset(player_t *player); +void R_SmoothPlaying_Add(int delta); +angle_t R_SmoothPlaying_Get(angle_t defangle); +void R_ResetAfterTeleport(player_t *player); diff --git a/src/r_draw.c b/src/r_draw.c new file mode 100644 index 0000000..388bd76 --- /dev/null +++ b/src/r_draw.c @@ -0,0 +1,1128 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * The actual span/column drawing functions. + * Here find the main potential for optimization, + * e.g. inline assembly, different algorithms. + * + *-----------------------------------------------------------------------------*/ + +#include "doomstat.h" +#include "w_wad.h" +#include "r_main.h" +#include "r_draw.h" +#include "r_filter.h" +#include "v_video.h" +#include "st_stuff.h" +#include "g_game.h" +#include "am_map.h" +#include "lprintf.h" + +// +// All drawing to the view buffer is accomplished in this file. +// The other refresh files only know about ccordinates, +// not the architecture of the frame buffer. +// Conveniently, the frame buffer is a linear one, +// and we need only the base address, +// and the total size == width*height*depth/8., +// + +byte *viewimage; +int viewwidth; +int scaledviewwidth; +int viewheight; +int viewwindowx; +int viewwindowy; + +// Color tables for different players, +// translate a limited part to another +// (color ramps used for suit colors). +// + +// CPhipps - made const*'s +const byte *tranmap; // translucency filter maps 256x256 // phares +const byte *main_tranmap; // killough 4/11/98 + +// +// R_DrawColumn +// Source is the top of the column to scale. +// + +// SoM: OPTIMIZE for ANYRES +typedef enum +{ + COL_NONE, + COL_OPAQUE, + COL_TRANS, + COL_FLEXTRANS, + COL_FUZZ, + COL_FLEXADD +} columntype_e; + +static int temp_x = 0; +static int tempyl[4], tempyh[4]; +static byte byte_tempbuf[MAX_SCREENHEIGHT * 4]; +static unsigned short short_tempbuf[MAX_SCREENHEIGHT * 4]; +static unsigned int int_tempbuf[MAX_SCREENHEIGHT * 4]; +static int startx = 0; +static int temptype = COL_NONE; +static int commontop, commonbot; +static const byte *temptranmap = NULL; +// SoM 7-28-04: Fix the fuzz problem. +static const byte *tempfuzzmap; + +// +// Spectre/Invisibility. +// + +#define FUZZTABLE 50 +// proff 08/17/98: Changed for high-res +//#define FUZZOFF (SCREENWIDTH) +#define FUZZOFF 1 + +static const int fuzzoffset_org[FUZZTABLE] = { + FUZZOFF,-FUZZOFF,FUZZOFF,-FUZZOFF,FUZZOFF,FUZZOFF,-FUZZOFF, + FUZZOFF,FUZZOFF,-FUZZOFF,FUZZOFF,FUZZOFF,FUZZOFF,-FUZZOFF, + FUZZOFF,FUZZOFF,FUZZOFF,-FUZZOFF,-FUZZOFF,-FUZZOFF,-FUZZOFF, + FUZZOFF,-FUZZOFF,-FUZZOFF,FUZZOFF,FUZZOFF,FUZZOFF,FUZZOFF,-FUZZOFF, + FUZZOFF,-FUZZOFF,FUZZOFF,FUZZOFF,-FUZZOFF,-FUZZOFF,FUZZOFF, + FUZZOFF,-FUZZOFF,-FUZZOFF,-FUZZOFF,-FUZZOFF,FUZZOFF,FUZZOFF, + FUZZOFF,FUZZOFF,-FUZZOFF,FUZZOFF,FUZZOFF,-FUZZOFF,FUZZOFF +}; + +static int fuzzoffset[FUZZTABLE]; + +static int fuzzpos = 0; + +// render pipelines +#define RDC_STANDARD 1 +#define RDC_TRANSLUCENT 2 +#define RDC_TRANSLATED 4 +#define RDC_FUZZ 8 +// no color mapping +#define RDC_NOCOLMAP 16 +// filter modes +#define RDC_DITHERZ 32 +#define RDC_BILINEAR 64 +#define RDC_ROUNDED 128 + +draw_vars_t drawvars = { + NULL, // byte_topleft + NULL, // short_topleft + NULL, // int_topleft + 0, // byte_pitch + 0, // short_pitch + 0, // int_pitch + RDRAW_FILTER_POINT, // filterwall + RDRAW_FILTER_POINT, // filterfloor + RDRAW_FILTER_POINT, // filtersprite + RDRAW_FILTER_POINT, // filterz + RDRAW_FILTER_POINT, // filterpatch + + RDRAW_MASKEDCOLUMNEDGE_SQUARE, // sprite_edges + RDRAW_MASKEDCOLUMNEDGE_SQUARE, // patch_edges + + // 49152 = FRACUNIT * 0.75 + // 81920 = FRACUNIT * 1.25 + 49152 // mag_threshold +}; + +// +// Error functions that will abort if R_FlushColumns tries to flush +// columns without a column type. +// + +static void R_FlushWholeError(void) +{ + I_Error("R_FlushWholeColumns called without being initialized.\n"); +} + +static void R_FlushHTError(void) +{ + I_Error("R_FlushHTColumns called without being initialized.\n"); +} + +static void R_QuadFlushError(void) +{ + I_Error("R_FlushQuadColumn called without being initialized.\n"); +} + +static void (*R_FlushWholeColumns)(void) = R_FlushWholeError; +static void (*R_FlushHTColumns)(void) = R_FlushHTError; +static void (*R_FlushQuadColumn)(void) = R_QuadFlushError; + +static void R_FlushColumns(void) +{ + if(temp_x != 4 || commontop >= commonbot) + R_FlushWholeColumns(); + else + { + R_FlushHTColumns(); + R_FlushQuadColumn(); + } + temp_x = 0; +} + +// +// R_ResetColumnBuffer +// +// haleyjd 09/13/04: new function to call from main rendering loop +// which gets rid of the unnecessary reset of various variables during +// column drawing. +// +void R_ResetColumnBuffer(void) +{ + // haleyjd 10/06/05: this must not be done if temp_x == 0! + if(temp_x) + R_FlushColumns(); + temptype = COL_NONE; + R_FlushWholeColumns = R_FlushWholeError; + R_FlushHTColumns = R_FlushHTError; + R_FlushQuadColumn = R_QuadFlushError; +} + +#define R_DRAWCOLUMN_PIPELINE RDC_STANDARD +#define R_DRAWCOLUMN_PIPELINE_BITS 8 +#define R_FLUSHWHOLE_FUNCNAME R_FlushWhole8 +#define R_FLUSHHEADTAIL_FUNCNAME R_FlushHT8 +#define R_FLUSHQUAD_FUNCNAME R_FlushQuad8 +#include "r_drawflush.inl" + +#define R_DRAWCOLUMN_PIPELINE RDC_TRANSLUCENT +#define R_DRAWCOLUMN_PIPELINE_BITS 8 +#define R_FLUSHWHOLE_FUNCNAME R_FlushWholeTL8 +#define R_FLUSHHEADTAIL_FUNCNAME R_FlushHTTL8 +#define R_FLUSHQUAD_FUNCNAME R_FlushQuadTL8 +#include "r_drawflush.inl" + +#define R_DRAWCOLUMN_PIPELINE RDC_FUZZ +#define R_DRAWCOLUMN_PIPELINE_BITS 8 +#define R_FLUSHWHOLE_FUNCNAME R_FlushWholeFuzz8 +#define R_FLUSHHEADTAIL_FUNCNAME R_FlushHTFuzz8 +#define R_FLUSHQUAD_FUNCNAME R_FlushQuadFuzz8 +#include "r_drawflush.inl" + +#define R_DRAWCOLUMN_PIPELINE RDC_STANDARD +#define R_DRAWCOLUMN_PIPELINE_BITS 15 +#define R_FLUSHWHOLE_FUNCNAME R_FlushWhole15 +#define R_FLUSHHEADTAIL_FUNCNAME R_FlushHT15 +#define R_FLUSHQUAD_FUNCNAME R_FlushQuad15 +#include "r_drawflush.inl" + +#define R_DRAWCOLUMN_PIPELINE RDC_TRANSLUCENT +#define R_DRAWCOLUMN_PIPELINE_BITS 15 +#define R_FLUSHWHOLE_FUNCNAME R_FlushWholeTL15 +#define R_FLUSHHEADTAIL_FUNCNAME R_FlushHTTL15 +#define R_FLUSHQUAD_FUNCNAME R_FlushQuadTL15 +#include "r_drawflush.inl" + +#define R_DRAWCOLUMN_PIPELINE RDC_FUZZ +#define R_DRAWCOLUMN_PIPELINE_BITS 15 +#define R_FLUSHWHOLE_FUNCNAME R_FlushWholeFuzz15 +#define R_FLUSHHEADTAIL_FUNCNAME R_FlushHTFuzz15 +#define R_FLUSHQUAD_FUNCNAME R_FlushQuadFuzz15 +#include "r_drawflush.inl" + +#define R_DRAWCOLUMN_PIPELINE RDC_STANDARD +#define R_DRAWCOLUMN_PIPELINE_BITS 16 +#define R_FLUSHWHOLE_FUNCNAME R_FlushWhole16 +#define R_FLUSHHEADTAIL_FUNCNAME R_FlushHT16 +#define R_FLUSHQUAD_FUNCNAME R_FlushQuad16 +#include "r_drawflush.inl" + +#define R_DRAWCOLUMN_PIPELINE RDC_TRANSLUCENT +#define R_DRAWCOLUMN_PIPELINE_BITS 16 +#define R_FLUSHWHOLE_FUNCNAME R_FlushWholeTL16 +#define R_FLUSHHEADTAIL_FUNCNAME R_FlushHTTL16 +#define R_FLUSHQUAD_FUNCNAME R_FlushQuadTL16 +#include "r_drawflush.inl" + +#define R_DRAWCOLUMN_PIPELINE RDC_FUZZ +#define R_DRAWCOLUMN_PIPELINE_BITS 16 +#define R_FLUSHWHOLE_FUNCNAME R_FlushWholeFuzz16 +#define R_FLUSHHEADTAIL_FUNCNAME R_FlushHTFuzz16 +#define R_FLUSHQUAD_FUNCNAME R_FlushQuadFuzz16 +#include "r_drawflush.inl" + +#define R_DRAWCOLUMN_PIPELINE RDC_STANDARD +#define R_DRAWCOLUMN_PIPELINE_BITS 32 +#define R_FLUSHWHOLE_FUNCNAME R_FlushWhole32 +#define R_FLUSHHEADTAIL_FUNCNAME R_FlushHT32 +#define R_FLUSHQUAD_FUNCNAME R_FlushQuad32 +#include "r_drawflush.inl" + +#define R_DRAWCOLUMN_PIPELINE RDC_TRANSLUCENT +#define R_DRAWCOLUMN_PIPELINE_BITS 32 +#define R_FLUSHWHOLE_FUNCNAME R_FlushWholeTL32 +#define R_FLUSHHEADTAIL_FUNCNAME R_FlushHTTL32 +#define R_FLUSHQUAD_FUNCNAME R_FlushQuadTL32 +#include "r_drawflush.inl" + +#define R_DRAWCOLUMN_PIPELINE RDC_FUZZ +#define R_DRAWCOLUMN_PIPELINE_BITS 32 +#define R_FLUSHWHOLE_FUNCNAME R_FlushWholeFuzz32 +#define R_FLUSHHEADTAIL_FUNCNAME R_FlushHTFuzz32 +#define R_FLUSHQUAD_FUNCNAME R_FlushQuadFuzz32 +#include "r_drawflush.inl" + +// +// R_DrawColumn +// + +// +// A column is a vertical slice/span from a wall texture that, +// given the DOOM style restrictions on the view orientation, +// will always have constant z depth. +// Thus a special case loop for very fast rendering can +// be used. It has also been used with Wolfenstein 3D. +// + +byte *translationtables; + +#define R_DRAWCOLUMN_PIPELINE_TYPE RDC_PIPELINE_STANDARD +#define R_DRAWCOLUMN_PIPELINE_BASE RDC_STANDARD + +#define R_DRAWCOLUMN_PIPELINE_BITS 8 +#define R_DRAWCOLUMN_FUNCNAME_COMPOSITE(postfix) R_DrawColumn8 ## postfix +#define R_FLUSHWHOLE_FUNCNAME R_FlushWhole8 +#define R_FLUSHHEADTAIL_FUNCNAME R_FlushHT8 +#define R_FLUSHQUAD_FUNCNAME R_FlushQuad8 +#include "r_drawcolpipeline.inl" + +#define R_DRAWCOLUMN_PIPELINE_BITS 15 +#define R_DRAWCOLUMN_FUNCNAME_COMPOSITE(postfix) R_DrawColumn15 ## postfix +#define R_FLUSHWHOLE_FUNCNAME R_FlushWhole15 +#define R_FLUSHHEADTAIL_FUNCNAME R_FlushHT15 +#define R_FLUSHQUAD_FUNCNAME R_FlushQuad15 +#include "r_drawcolpipeline.inl" + +#define R_DRAWCOLUMN_PIPELINE_BITS 16 +#define R_DRAWCOLUMN_FUNCNAME_COMPOSITE(postfix) R_DrawColumn16 ## postfix +#define R_FLUSHWHOLE_FUNCNAME R_FlushWhole16 +#define R_FLUSHHEADTAIL_FUNCNAME R_FlushHT16 +#define R_FLUSHQUAD_FUNCNAME R_FlushQuad16 +#include "r_drawcolpipeline.inl" + +#define R_DRAWCOLUMN_PIPELINE_BITS 32 +#define R_DRAWCOLUMN_FUNCNAME_COMPOSITE(postfix) R_DrawColumn32 ## postfix +#define R_FLUSHWHOLE_FUNCNAME R_FlushWhole32 +#define R_FLUSHHEADTAIL_FUNCNAME R_FlushHT32 +#define R_FLUSHQUAD_FUNCNAME R_FlushQuad32 +#include "r_drawcolpipeline.inl" + +#undef R_DRAWCOLUMN_PIPELINE_BASE +#undef R_DRAWCOLUMN_PIPELINE_TYPE + +// Here is the version of R_DrawColumn that deals with translucent // phares +// textures and sprites. It's identical to R_DrawColumn except // | +// for the spot where the color index is stuffed into *dest. At // V +// that point, the existing color index and the new color index +// are mapped through the TRANMAP lump filters to get a new color +// index whose RGB values are the average of the existing and new +// colors. +// +// Since we're concerned about performance, the 'translucent or +// opaque' decision is made outside this routine, not down where the +// actual code differences are. + +#define R_DRAWCOLUMN_PIPELINE_TYPE RDC_PIPELINE_TRANSLUCENT +#define R_DRAWCOLUMN_PIPELINE_BASE RDC_TRANSLUCENT + +#define R_DRAWCOLUMN_PIPELINE_BITS 8 +#define R_DRAWCOLUMN_FUNCNAME_COMPOSITE(postfix) R_DrawTLColumn8 ## postfix +#define R_FLUSHWHOLE_FUNCNAME R_FlushWholeTL8 +#define R_FLUSHHEADTAIL_FUNCNAME R_FlushHTTL8 +#define R_FLUSHQUAD_FUNCNAME R_FlushQuadTL8 +#include "r_drawcolpipeline.inl" + +#define R_DRAWCOLUMN_PIPELINE_BITS 15 +#define R_DRAWCOLUMN_FUNCNAME_COMPOSITE(postfix) R_DrawTLColumn15 ## postfix +#define R_FLUSHWHOLE_FUNCNAME R_FlushWholeTL15 +#define R_FLUSHHEADTAIL_FUNCNAME R_FlushHTTL15 +#define R_FLUSHQUAD_FUNCNAME R_FlushQuadTL15 +#include "r_drawcolpipeline.inl" + +#define R_DRAWCOLUMN_PIPELINE_BITS 16 +#define R_DRAWCOLUMN_FUNCNAME_COMPOSITE(postfix) R_DrawTLColumn16 ## postfix +#define R_FLUSHWHOLE_FUNCNAME R_FlushWholeTL16 +#define R_FLUSHHEADTAIL_FUNCNAME R_FlushHTTL16 +#define R_FLUSHQUAD_FUNCNAME R_FlushQuadTL16 +#include "r_drawcolpipeline.inl" + +#define R_DRAWCOLUMN_PIPELINE_BITS 32 +#define R_DRAWCOLUMN_FUNCNAME_COMPOSITE(postfix) R_DrawTLColumn32 ## postfix +#define R_FLUSHWHOLE_FUNCNAME R_FlushWholeTL32 +#define R_FLUSHHEADTAIL_FUNCNAME R_FlushHTTL32 +#define R_FLUSHQUAD_FUNCNAME R_FlushQuadTL32 +#include "r_drawcolpipeline.inl" + +#undef R_DRAWCOLUMN_PIPELINE_BASE +#undef R_DRAWCOLUMN_PIPELINE_TYPE + +// +// R_DrawTranslatedColumn +// Used to draw player sprites +// with the green colorramp mapped to others. +// Could be used with different translation +// tables, e.g. the lighter colored version +// of the BaronOfHell, the HellKnight, uses +// identical sprites, kinda brightened up. +// + +#define R_DRAWCOLUMN_PIPELINE_TYPE RDC_PIPELINE_TRANSLATED +#define R_DRAWCOLUMN_PIPELINE_BASE RDC_TRANSLATED + +#define R_DRAWCOLUMN_PIPELINE_BITS 8 +#define R_DRAWCOLUMN_FUNCNAME_COMPOSITE(postfix) R_DrawTranslatedColumn8 ## postfix +#define R_FLUSHWHOLE_FUNCNAME R_FlushWhole8 +#define R_FLUSHHEADTAIL_FUNCNAME R_FlushHT8 +#define R_FLUSHQUAD_FUNCNAME R_FlushQuad8 +#include "r_drawcolpipeline.inl" + +#define R_DRAWCOLUMN_PIPELINE_BITS 15 +#define R_DRAWCOLUMN_FUNCNAME_COMPOSITE(postfix) R_DrawTranslatedColumn15 ## postfix +#define R_FLUSHWHOLE_FUNCNAME R_FlushWhole15 +#define R_FLUSHHEADTAIL_FUNCNAME R_FlushHT15 +#define R_FLUSHQUAD_FUNCNAME R_FlushQuad15 +#include "r_drawcolpipeline.inl" + +#define R_DRAWCOLUMN_PIPELINE_BITS 16 +#define R_DRAWCOLUMN_FUNCNAME_COMPOSITE(postfix) R_DrawTranslatedColumn16 ## postfix +#define R_FLUSHWHOLE_FUNCNAME R_FlushWhole16 +#define R_FLUSHHEADTAIL_FUNCNAME R_FlushHT16 +#define R_FLUSHQUAD_FUNCNAME R_FlushQuad16 +#include "r_drawcolpipeline.inl" + +#define R_DRAWCOLUMN_PIPELINE_BITS 32 +#define R_DRAWCOLUMN_FUNCNAME_COMPOSITE(postfix) R_DrawTranslatedColumn32 ## postfix +#define R_FLUSHWHOLE_FUNCNAME R_FlushWhole32 +#define R_FLUSHHEADTAIL_FUNCNAME R_FlushHT32 +#define R_FLUSHQUAD_FUNCNAME R_FlushQuad32 +#include "r_drawcolpipeline.inl" + +#undef R_DRAWCOLUMN_PIPELINE_BASE +#undef R_DRAWCOLUMN_PIPELINE_TYPE + +// +// Framebuffer postprocessing. +// Creates a fuzzy image by copying pixels +// from adjacent ones to left and right. +// Used with an all black colormap, this +// could create the SHADOW effect, +// i.e. spectres and invisible players. +// + +#define R_DRAWCOLUMN_PIPELINE_TYPE RDC_PIPELINE_FUZZ +#define R_DRAWCOLUMN_PIPELINE_BASE RDC_FUZZ + +#define R_DRAWCOLUMN_PIPELINE_BITS 8 +#define R_DRAWCOLUMN_FUNCNAME_COMPOSITE(postfix) R_DrawFuzzColumn8 ## postfix +#define R_FLUSHWHOLE_FUNCNAME R_FlushWholeFuzz8 +#define R_FLUSHHEADTAIL_FUNCNAME R_FlushHTFuzz8 +#define R_FLUSHQUAD_FUNCNAME R_FlushQuadFuzz8 +#include "r_drawcolpipeline.inl" + +#define R_DRAWCOLUMN_PIPELINE_BITS 15 +#define R_DRAWCOLUMN_FUNCNAME_COMPOSITE(postfix) R_DrawFuzzColumn15 ## postfix +#define R_FLUSHWHOLE_FUNCNAME R_FlushWholeFuzz15 +#define R_FLUSHHEADTAIL_FUNCNAME R_FlushHTFuzz15 +#define R_FLUSHQUAD_FUNCNAME R_FlushQuadFuzz15 +#include "r_drawcolpipeline.inl" + +#define R_DRAWCOLUMN_PIPELINE_BITS 16 +#define R_DRAWCOLUMN_FUNCNAME_COMPOSITE(postfix) R_DrawFuzzColumn16 ## postfix +#define R_FLUSHWHOLE_FUNCNAME R_FlushWholeFuzz16 +#define R_FLUSHHEADTAIL_FUNCNAME R_FlushHTFuzz16 +#define R_FLUSHQUAD_FUNCNAME R_FlushQuadFuzz16 +#include "r_drawcolpipeline.inl" + +#define R_DRAWCOLUMN_PIPELINE_BITS 32 +#define R_DRAWCOLUMN_FUNCNAME_COMPOSITE(postfix) R_DrawFuzzColumn32 ## postfix +#define R_FLUSHWHOLE_FUNCNAME R_FlushWholeFuzz32 +#define R_FLUSHHEADTAIL_FUNCNAME R_FlushHTFuzz32 +#define R_FLUSHQUAD_FUNCNAME R_FlushQuadFuzz32 +#include "r_drawcolpipeline.inl" + +#undef R_DRAWCOLUMN_PIPELINE_BASE +#undef R_DRAWCOLUMN_PIPELINE_TYPE + +static R_DrawColumn_f drawcolumnfuncs[VID_MODEMAX][RDRAW_FILTER_MAXFILTERS][RDRAW_FILTER_MAXFILTERS][RDC_PIPELINE_MAXPIPELINES] = { + { + { + {NULL, NULL, NULL, NULL,}, + {R_DrawColumn8_PointUV, + R_DrawTLColumn8_PointUV, + R_DrawTranslatedColumn8_PointUV, + R_DrawFuzzColumn8_PointUV,}, + {R_DrawColumn8_LinearUV, + R_DrawTLColumn8_LinearUV, + R_DrawTranslatedColumn8_LinearUV, + R_DrawFuzzColumn8_LinearUV,}, + {R_DrawColumn8_RoundedUV, + R_DrawTLColumn8_RoundedUV, + R_DrawTranslatedColumn8_RoundedUV, + R_DrawFuzzColumn8_RoundedUV,}, + }, + { + {NULL, NULL, NULL, NULL,}, + {R_DrawColumn8_PointUV_PointZ, + R_DrawTLColumn8_PointUV_PointZ, + R_DrawTranslatedColumn8_PointUV_PointZ, + R_DrawFuzzColumn8_PointUV_PointZ,}, + {R_DrawColumn8_LinearUV_PointZ, + R_DrawTLColumn8_LinearUV_PointZ, + R_DrawTranslatedColumn8_LinearUV_PointZ, + R_DrawFuzzColumn8_LinearUV_PointZ,}, + {R_DrawColumn8_RoundedUV_PointZ, + R_DrawTLColumn8_RoundedUV_PointZ, + R_DrawTranslatedColumn8_RoundedUV_PointZ, + R_DrawFuzzColumn8_RoundedUV_PointZ,}, + }, + { + {NULL, NULL, NULL, NULL,}, + {R_DrawColumn8_PointUV_LinearZ, + R_DrawTLColumn8_PointUV_LinearZ, + R_DrawTranslatedColumn8_PointUV_LinearZ, + R_DrawFuzzColumn8_PointUV_LinearZ,}, + {R_DrawColumn8_LinearUV_LinearZ, + R_DrawTLColumn8_LinearUV_LinearZ, + R_DrawTranslatedColumn8_LinearUV_LinearZ, + R_DrawFuzzColumn8_LinearUV_LinearZ,}, + {R_DrawColumn8_RoundedUV_LinearZ, + R_DrawTLColumn8_RoundedUV_LinearZ, + R_DrawTranslatedColumn8_RoundedUV_LinearZ, + R_DrawFuzzColumn8_RoundedUV_LinearZ,}, + }, + }, + { + { + {NULL, NULL, NULL, NULL,}, + {R_DrawColumn15_PointUV, + R_DrawTLColumn15_PointUV, + R_DrawTranslatedColumn15_PointUV, + R_DrawFuzzColumn15_PointUV,}, + {R_DrawColumn15_LinearUV, + R_DrawTLColumn15_LinearUV, + R_DrawTranslatedColumn15_LinearUV, + R_DrawFuzzColumn15_LinearUV,}, + {R_DrawColumn15_RoundedUV, + R_DrawTLColumn15_RoundedUV, + R_DrawTranslatedColumn15_RoundedUV, + R_DrawFuzzColumn15_RoundedUV,}, + }, + { + {NULL, NULL, NULL, NULL,}, + {R_DrawColumn15_PointUV_PointZ, + R_DrawTLColumn15_PointUV_PointZ, + R_DrawTranslatedColumn15_PointUV_PointZ, + R_DrawFuzzColumn15_PointUV_PointZ,}, + {R_DrawColumn15_LinearUV_PointZ, + R_DrawTLColumn15_LinearUV_PointZ, + R_DrawTranslatedColumn15_LinearUV_PointZ, + R_DrawFuzzColumn15_LinearUV_PointZ,}, + {R_DrawColumn15_RoundedUV_PointZ, + R_DrawTLColumn15_RoundedUV_PointZ, + R_DrawTranslatedColumn15_RoundedUV_PointZ, + R_DrawFuzzColumn15_RoundedUV_PointZ,}, + }, + { + {NULL, NULL, NULL, NULL,}, + {R_DrawColumn15_PointUV_LinearZ, + R_DrawTLColumn15_PointUV_LinearZ, + R_DrawTranslatedColumn15_PointUV_LinearZ, + R_DrawFuzzColumn15_PointUV_LinearZ,}, + {R_DrawColumn15_LinearUV_LinearZ, + R_DrawTLColumn15_LinearUV_LinearZ, + R_DrawTranslatedColumn15_LinearUV_LinearZ, + R_DrawFuzzColumn15_LinearUV_LinearZ,}, + {R_DrawColumn15_RoundedUV_LinearZ, + R_DrawTLColumn15_RoundedUV_LinearZ, + R_DrawTranslatedColumn15_RoundedUV_LinearZ, + R_DrawFuzzColumn15_RoundedUV_LinearZ,}, + }, + }, + { + { + {NULL, NULL, NULL, NULL,}, + {R_DrawColumn16_PointUV, + R_DrawTLColumn16_PointUV, + R_DrawTranslatedColumn16_PointUV, + R_DrawFuzzColumn16_PointUV,}, + {R_DrawColumn16_LinearUV, + R_DrawTLColumn16_LinearUV, + R_DrawTranslatedColumn16_LinearUV, + R_DrawFuzzColumn16_LinearUV,}, + {R_DrawColumn16_RoundedUV, + R_DrawTLColumn16_RoundedUV, + R_DrawTranslatedColumn16_RoundedUV, + R_DrawFuzzColumn16_RoundedUV,}, + }, + { + {NULL, NULL, NULL, NULL,}, + {R_DrawColumn16_PointUV_PointZ, + R_DrawTLColumn16_PointUV_PointZ, + R_DrawTranslatedColumn16_PointUV_PointZ, + R_DrawFuzzColumn16_PointUV_PointZ,}, + {R_DrawColumn16_LinearUV_PointZ, + R_DrawTLColumn16_LinearUV_PointZ, + R_DrawTranslatedColumn16_LinearUV_PointZ, + R_DrawFuzzColumn16_LinearUV_PointZ,}, + {R_DrawColumn16_RoundedUV_PointZ, + R_DrawTLColumn16_RoundedUV_PointZ, + R_DrawTranslatedColumn16_RoundedUV_PointZ, + R_DrawFuzzColumn16_RoundedUV_PointZ,}, + }, + { + {NULL, NULL, NULL, NULL,}, + {R_DrawColumn16_PointUV_LinearZ, + R_DrawTLColumn16_PointUV_LinearZ, + R_DrawTranslatedColumn16_PointUV_LinearZ, + R_DrawFuzzColumn16_PointUV_LinearZ,}, + {R_DrawColumn16_LinearUV_LinearZ, + R_DrawTLColumn16_LinearUV_LinearZ, + R_DrawTranslatedColumn16_LinearUV_LinearZ, + R_DrawFuzzColumn16_LinearUV_LinearZ,}, + {R_DrawColumn16_RoundedUV_LinearZ, + R_DrawTLColumn16_RoundedUV_LinearZ, + R_DrawTranslatedColumn16_RoundedUV_LinearZ, + R_DrawFuzzColumn16_RoundedUV_LinearZ,}, + }, + }, + { + { + {NULL, NULL, NULL, NULL,}, + {R_DrawColumn32_PointUV, + R_DrawTLColumn32_PointUV, + R_DrawTranslatedColumn32_PointUV, + R_DrawFuzzColumn32_PointUV,}, + {R_DrawColumn32_LinearUV, + R_DrawTLColumn32_LinearUV, + R_DrawTranslatedColumn32_LinearUV, + R_DrawFuzzColumn32_LinearUV,}, + {R_DrawColumn32_RoundedUV, + R_DrawTLColumn32_RoundedUV, + R_DrawTranslatedColumn32_RoundedUV, + R_DrawFuzzColumn32_RoundedUV,}, + }, + { + {NULL, NULL, NULL, NULL,}, + {R_DrawColumn32_PointUV_PointZ, + R_DrawTLColumn32_PointUV_PointZ, + R_DrawTranslatedColumn32_PointUV_PointZ, + R_DrawFuzzColumn32_PointUV_PointZ,}, + {R_DrawColumn32_LinearUV_PointZ, + R_DrawTLColumn32_LinearUV_PointZ, + R_DrawTranslatedColumn32_LinearUV_PointZ, + R_DrawFuzzColumn32_LinearUV_PointZ,}, + {R_DrawColumn32_RoundedUV_PointZ, + R_DrawTLColumn32_RoundedUV_PointZ, + R_DrawTranslatedColumn32_RoundedUV_PointZ, + R_DrawFuzzColumn32_RoundedUV_PointZ,}, + }, + { + {NULL, NULL, NULL, NULL,}, + {R_DrawColumn32_PointUV_LinearZ, + R_DrawTLColumn32_PointUV_LinearZ, + R_DrawTranslatedColumn32_PointUV_LinearZ, + R_DrawFuzzColumn32_PointUV_LinearZ,}, + {R_DrawColumn32_LinearUV_LinearZ, + R_DrawTLColumn32_LinearUV_LinearZ, + R_DrawTranslatedColumn32_LinearUV_LinearZ, + R_DrawFuzzColumn32_LinearUV_LinearZ,}, + {R_DrawColumn32_RoundedUV_LinearZ, + R_DrawTLColumn32_RoundedUV_LinearZ, + R_DrawTranslatedColumn32_RoundedUV_LinearZ, + R_DrawFuzzColumn32_RoundedUV_LinearZ,}, + }, + }, +}; + +R_DrawColumn_f R_GetDrawColumnFunc(enum column_pipeline_e type, + enum draw_filter_type_e filter, + enum draw_filter_type_e filterz) { + R_DrawColumn_f result = drawcolumnfuncs[V_GetMode()][filterz][filter][type]; + if (result == NULL) + I_Error("R_GetDrawColumnFunc: undefined function (%d, %d, %d)", + type, filter, filterz); + return result; +} + +void R_SetDefaultDrawColumnVars(draw_column_vars_t *dcvars) { + dcvars->x = dcvars->yl = dcvars->yh = dcvars->z = 0; + dcvars->iscale = dcvars->texturemid = dcvars->texheight = dcvars->texu = 0; + dcvars->source = dcvars->prevsource = dcvars->nextsource = NULL; + dcvars->colormap = dcvars->nextcolormap = colormaps[0]; + dcvars->translation = NULL; + dcvars->edgeslope = dcvars->drawingmasked = 0; + dcvars->edgetype = drawvars.sprite_edges; +} + +// +// R_InitTranslationTables +// Creates the translation tables to map +// the green color ramp to gray, brown, red. +// Assumes a given structure of the PLAYPAL. +// Could be read from a lump instead. +// + +byte playernumtotrans[MAXPLAYERS]; +extern lighttable_t *(*c_zlight)[LIGHTLEVELS][MAXLIGHTZ]; + +void R_InitTranslationTables (void) +{ + int i, j; +#define MAXTRANS 3 + byte transtocolour[MAXTRANS]; + + // killough 5/2/98: + // Remove dependency of colormaps aligned on 256-byte boundary + + if (translationtables == NULL) // CPhipps - allow multiple calls + translationtables = Z_Malloc(256*MAXTRANS, PU_STATIC, 0); + + for (i=0; i= 0x70 && i<= 0x7f) + { + // CPhipps - configurable player colours + translationtables[i] = colormaps[0][((i&0xf)<<9) + transtocolour[0]]; + translationtables[i+256] = colormaps[0][((i&0xf)<<9) + transtocolour[1]]; + translationtables[i+512] = colormaps[0][((i&0xf)<<9) + transtocolour[2]]; + } + else // Keep all other colors as is. + translationtables[i]=translationtables[i+256]=translationtables[i+512]=i; +} + +// +// R_DrawSpan +// With DOOM style restrictions on view orientation, +// the floors and ceilings consist of horizontal slices +// or spans with constant z depth. +// However, rotation around the world z axis is possible, +// thus this mapping, while simpler and faster than +// perspective correct texture mapping, has to traverse +// the texture at an angle in all but a few cases. +// In consequence, flats are not stored by column (like walls), +// and the inner loop has to step in texture space u and v. +// + +#define R_DRAWSPAN_FUNCNAME R_DrawSpan8_PointUV_PointZ +#define R_DRAWSPAN_PIPELINE_BITS 8 +#define R_DRAWSPAN_PIPELINE (RDC_STANDARD) +#include "r_drawspan.inl" + +#define R_DRAWSPAN_FUNCNAME R_DrawSpan8_PointUV_LinearZ +#define R_DRAWSPAN_PIPELINE_BITS 8 +#define R_DRAWSPAN_PIPELINE (RDC_STANDARD | RDC_DITHERZ) +#include "r_drawspan.inl" + +#define R_DRAWSPAN_FUNCNAME R_DrawSpan8_LinearUV_PointZ +#define R_DRAWSPAN_PIPELINE_BITS 8 +#define R_DRAWSPAN_PIPELINE (RDC_STANDARD | RDC_BILINEAR) +#include "r_drawspan.inl" + +#define R_DRAWSPAN_FUNCNAME R_DrawSpan8_LinearUV_LinearZ +#define R_DRAWSPAN_PIPELINE_BITS 8 +#define R_DRAWSPAN_PIPELINE (RDC_STANDARD | RDC_BILINEAR | RDC_DITHERZ) +#include "r_drawspan.inl" + +#define R_DRAWSPAN_FUNCNAME R_DrawSpan8_RoundedUV_PointZ +#define R_DRAWSPAN_PIPELINE_BITS 8 +#define R_DRAWSPAN_PIPELINE (RDC_STANDARD | RDC_ROUNDED) +#include "r_drawspan.inl" + +#define R_DRAWSPAN_FUNCNAME R_DrawSpan8_RoundedUV_LinearZ +#define R_DRAWSPAN_PIPELINE_BITS 8 +#define R_DRAWSPAN_PIPELINE (RDC_STANDARD | RDC_ROUNDED | RDC_DITHERZ) +#include "r_drawspan.inl" + +#define R_DRAWSPAN_FUNCNAME R_DrawSpan15_PointUV_PointZ +#define R_DRAWSPAN_PIPELINE_BITS 15 +#define R_DRAWSPAN_PIPELINE (RDC_STANDARD) +#include "r_drawspan.inl" + +#define R_DRAWSPAN_FUNCNAME R_DrawSpan15_PointUV_LinearZ +#define R_DRAWSPAN_PIPELINE_BITS 15 +#define R_DRAWSPAN_PIPELINE (RDC_STANDARD | RDC_DITHERZ) +#include "r_drawspan.inl" + +#define R_DRAWSPAN_FUNCNAME R_DrawSpan15_LinearUV_PointZ +#define R_DRAWSPAN_PIPELINE_BITS 15 +#define R_DRAWSPAN_PIPELINE (RDC_STANDARD | RDC_BILINEAR) +#include "r_drawspan.inl" + +#define R_DRAWSPAN_FUNCNAME R_DrawSpan15_LinearUV_LinearZ +#define R_DRAWSPAN_PIPELINE_BITS 15 +#define R_DRAWSPAN_PIPELINE (RDC_STANDARD | RDC_BILINEAR | RDC_DITHERZ) +#include "r_drawspan.inl" + +#define R_DRAWSPAN_FUNCNAME R_DrawSpan15_RoundedUV_PointZ +#define R_DRAWSPAN_PIPELINE_BITS 15 +#define R_DRAWSPAN_PIPELINE (RDC_STANDARD | RDC_ROUNDED) +#include "r_drawspan.inl" + +#define R_DRAWSPAN_FUNCNAME R_DrawSpan15_RoundedUV_LinearZ +#define R_DRAWSPAN_PIPELINE_BITS 15 +#define R_DRAWSPAN_PIPELINE (RDC_STANDARD | RDC_ROUNDED | RDC_DITHERZ) +#include "r_drawspan.inl" + +#define R_DRAWSPAN_FUNCNAME R_DrawSpan16_PointUV_PointZ +#define R_DRAWSPAN_PIPELINE_BITS 16 +#define R_DRAWSPAN_PIPELINE (RDC_STANDARD) +#include "r_drawspan.inl" + +#define R_DRAWSPAN_FUNCNAME R_DrawSpan16_PointUV_LinearZ +#define R_DRAWSPAN_PIPELINE_BITS 16 +#define R_DRAWSPAN_PIPELINE (RDC_STANDARD | RDC_DITHERZ) +#include "r_drawspan.inl" + +#define R_DRAWSPAN_FUNCNAME R_DrawSpan16_LinearUV_PointZ +#define R_DRAWSPAN_PIPELINE_BITS 16 +#define R_DRAWSPAN_PIPELINE (RDC_STANDARD | RDC_BILINEAR) +#include "r_drawspan.inl" + +#define R_DRAWSPAN_FUNCNAME R_DrawSpan16_LinearUV_LinearZ +#define R_DRAWSPAN_PIPELINE_BITS 16 +#define R_DRAWSPAN_PIPELINE (RDC_STANDARD | RDC_BILINEAR | RDC_DITHERZ) +#include "r_drawspan.inl" + +#define R_DRAWSPAN_FUNCNAME R_DrawSpan16_RoundedUV_PointZ +#define R_DRAWSPAN_PIPELINE_BITS 16 +#define R_DRAWSPAN_PIPELINE (RDC_STANDARD | RDC_ROUNDED) +#include "r_drawspan.inl" + +#define R_DRAWSPAN_FUNCNAME R_DrawSpan16_RoundedUV_LinearZ +#define R_DRAWSPAN_PIPELINE_BITS 16 +#define R_DRAWSPAN_PIPELINE (RDC_STANDARD | RDC_ROUNDED | RDC_DITHERZ) +#include "r_drawspan.inl" + +#define R_DRAWSPAN_FUNCNAME R_DrawSpan32_PointUV_PointZ +#define R_DRAWSPAN_PIPELINE_BITS 32 +#define R_DRAWSPAN_PIPELINE (RDC_STANDARD) +#include "r_drawspan.inl" + +#define R_DRAWSPAN_FUNCNAME R_DrawSpan32_PointUV_LinearZ +#define R_DRAWSPAN_PIPELINE_BITS 32 +#define R_DRAWSPAN_PIPELINE (RDC_STANDARD | RDC_DITHERZ) +#include "r_drawspan.inl" + +#define R_DRAWSPAN_FUNCNAME R_DrawSpan32_LinearUV_PointZ +#define R_DRAWSPAN_PIPELINE_BITS 32 +#define R_DRAWSPAN_PIPELINE (RDC_STANDARD | RDC_BILINEAR) +#include "r_drawspan.inl" + +#define R_DRAWSPAN_FUNCNAME R_DrawSpan32_LinearUV_LinearZ +#define R_DRAWSPAN_PIPELINE_BITS 32 +#define R_DRAWSPAN_PIPELINE (RDC_STANDARD | RDC_BILINEAR | RDC_DITHERZ) +#include "r_drawspan.inl" + +#define R_DRAWSPAN_FUNCNAME R_DrawSpan32_RoundedUV_PointZ +#define R_DRAWSPAN_PIPELINE_BITS 32 +#define R_DRAWSPAN_PIPELINE (RDC_STANDARD | RDC_ROUNDED) +#include "r_drawspan.inl" + +#define R_DRAWSPAN_FUNCNAME R_DrawSpan32_RoundedUV_LinearZ +#define R_DRAWSPAN_PIPELINE_BITS 32 +#define R_DRAWSPAN_PIPELINE (RDC_STANDARD | RDC_ROUNDED | RDC_DITHERZ) +#include "r_drawspan.inl" + +static R_DrawSpan_f drawspanfuncs[VID_MODEMAX][RDRAW_FILTER_MAXFILTERS][RDRAW_FILTER_MAXFILTERS] = { + { + { + NULL, + NULL, + NULL, + NULL, + }, + { + NULL, + R_DrawSpan8_PointUV_PointZ, + R_DrawSpan8_LinearUV_PointZ, + R_DrawSpan8_RoundedUV_PointZ, + }, + { + NULL, + R_DrawSpan8_PointUV_LinearZ, + R_DrawSpan8_LinearUV_LinearZ, + R_DrawSpan8_RoundedUV_LinearZ, + }, + { + NULL, + NULL, + NULL, + NULL, + }, + }, + { + { + NULL, + NULL, + NULL, + NULL, + }, + { + NULL, + R_DrawSpan15_PointUV_PointZ, + R_DrawSpan15_LinearUV_PointZ, + R_DrawSpan15_RoundedUV_PointZ, + }, + { + NULL, + R_DrawSpan15_PointUV_LinearZ, + R_DrawSpan15_LinearUV_LinearZ, + R_DrawSpan15_RoundedUV_LinearZ, + }, + { + NULL, + NULL, + NULL, + NULL, + }, + }, + { + { + NULL, + NULL, + NULL, + NULL, + }, + { + NULL, + R_DrawSpan16_PointUV_PointZ, + R_DrawSpan16_LinearUV_PointZ, + R_DrawSpan16_RoundedUV_PointZ, + }, + { + NULL, + R_DrawSpan16_PointUV_LinearZ, + R_DrawSpan16_LinearUV_LinearZ, + R_DrawSpan16_RoundedUV_LinearZ, + }, + { + NULL, + NULL, + NULL, + NULL, + }, + }, + { + { + NULL, + NULL, + NULL, + NULL, + }, + { + NULL, + R_DrawSpan32_PointUV_PointZ, + R_DrawSpan32_LinearUV_PointZ, + R_DrawSpan32_RoundedUV_PointZ, + }, + { + NULL, + R_DrawSpan32_PointUV_LinearZ, + R_DrawSpan32_LinearUV_LinearZ, + R_DrawSpan32_RoundedUV_LinearZ, + }, + { + NULL, + NULL, + NULL, + NULL, + }, + }, +}; + +R_DrawSpan_f R_GetDrawSpanFunc(enum draw_filter_type_e filter, + enum draw_filter_type_e filterz) { + R_DrawSpan_f result = drawspanfuncs[V_GetMode()][filterz][filter]; + if (result == NULL) + I_Error("R_GetDrawSpanFunc: undefined function (%d, %d)", + filter, filterz); + return result; +} + +void R_DrawSpan(draw_span_vars_t *dsvars) { + R_GetDrawSpanFunc(drawvars.filterfloor, drawvars.filterz)(dsvars); +} + +// +// R_InitBuffer +// Creats lookup tables that avoid +// multiplies and other hazzles +// for getting the framebuffer address +// of a pixel to draw. +// + +void R_InitBuffer(int width, int height) +{ + int i=0; + // Handle resize, + // e.g. smaller view windows + // with border and/or status bar. + + viewwindowx = (SCREENWIDTH-width) >> 1; + + // Same with base row offset. + + viewwindowy = width==SCREENWIDTH ? 0 : (SCREENHEIGHT-(ST_SCALED_HEIGHT-1)-height)>>1; + + drawvars.byte_topleft = screens[0].data + viewwindowy*screens[0].byte_pitch + viewwindowx; + drawvars.short_topleft = (unsigned short *)(screens[0].data) + viewwindowy*screens[0].short_pitch + viewwindowx; + drawvars.int_topleft = (unsigned int *)(screens[0].data) + viewwindowy*screens[0].int_pitch + viewwindowx; + drawvars.byte_pitch = screens[0].byte_pitch; + drawvars.short_pitch = screens[0].short_pitch; + drawvars.int_pitch = screens[0].int_pitch; + + if (V_GetMode() == VID_MODE8) { + for (i=0; i 0) { + for (i = (SCREENHEIGHT - ST_SCALED_HEIGHT); i < SCREENHEIGHT; i++) + { + R_VideoErase (0, i, side); + R_VideoErase (ST_SCALED_WIDTH+side, i, side); + } + } + } + + if ( viewheight >= ( SCREENHEIGHT - ST_SCALED_HEIGHT )) + return; // if high-res, don´t go any further! + + top = ((SCREENHEIGHT-ST_SCALED_HEIGHT)-viewheight)/2; + side = (SCREENWIDTH-scaledviewwidth)/2; + + // copy top + for (i = 0; i < top; i++) + R_VideoErase (0, i, SCREENWIDTH); + + // copy sides + for (i = top; i < (top+viewheight); i++) { + R_VideoErase (0, i, side); + R_VideoErase (viewwidth+side, i, side); + } + + // copy bottom + for (i = top+viewheight; i < (SCREENHEIGHT - ST_SCALED_HEIGHT); i++) + R_VideoErase (0, i, SCREENWIDTH); +} diff --git a/src/r_draw.h b/src/r_draw.h new file mode 100644 index 0000000..1b9bc62 --- /dev/null +++ b/src/r_draw.h @@ -0,0 +1,163 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * System specific interface stuff. + * + *-----------------------------------------------------------------------------*/ + +#ifndef __R_DRAW__ +#define __R_DRAW__ + +#include "r_defs.h" + +#ifdef __GNUG__ +#pragma interface +#endif + +enum column_pipeline_e { + RDC_PIPELINE_STANDARD, + RDC_PIPELINE_TRANSLUCENT, + RDC_PIPELINE_TRANSLATED, + RDC_PIPELINE_FUZZ, + RDC_PIPELINE_MAXPIPELINES, +}; + +// Used to specify what kind of filering you want +enum draw_filter_type_e { + RDRAW_FILTER_NONE, + RDRAW_FILTER_POINT, + RDRAW_FILTER_LINEAR, + RDRAW_FILTER_ROUNDED, + RDRAW_FILTER_MAXFILTERS +}; + +// Used to specify what kind of column edge rendering to use on masked +// columns. SQUARE = standard, SLOPED = slope the column edge up or down +// based on neighboring columns +enum sloped_edge_type_e { + RDRAW_MASKEDCOLUMNEDGE_SQUARE, + RDRAW_MASKEDCOLUMNEDGE_SLOPED +}; + +// Packaged into a struct - POPE +typedef struct { + int x; + int yl; + int yh; + fixed_t z; // the current column z coord + fixed_t iscale; + fixed_t texturemid; + int texheight; // killough + fixed_t texu; // the current column u coord + const byte *source; // first pixel in a column + const byte *prevsource; // first pixel in previous column + const byte *nextsource; // first pixel in next column + const lighttable_t *colormap; + const lighttable_t *nextcolormap; + const byte *translation; + int edgeslope; // OR'ed RDRAW_EDGESLOPE_* + // 1 if R_DrawColumn* is currently drawing a masked column, otherwise 0 + int drawingmasked; + enum sloped_edge_type_e edgetype; +} draw_column_vars_t; + +void R_SetDefaultDrawColumnVars(draw_column_vars_t *dcvars); + +void R_VideoErase(int x, int y, int count); + +typedef struct { + int y; + int x1; + int x2; + fixed_t z; // the current span z coord + fixed_t xfrac; + fixed_t yfrac; + fixed_t xstep; + fixed_t ystep; + const byte *source; // start of a 64*64 tile image + const lighttable_t *colormap; + const lighttable_t *nextcolormap; +} draw_span_vars_t; + +typedef struct { + byte *byte_topleft; + unsigned short *short_topleft; + unsigned int *int_topleft; + int byte_pitch; + int short_pitch; + int int_pitch; + + enum draw_filter_type_e filterwall; + enum draw_filter_type_e filterfloor; + enum draw_filter_type_e filtersprite; + enum draw_filter_type_e filterz; + enum draw_filter_type_e filterpatch; + + enum sloped_edge_type_e sprite_edges; + enum sloped_edge_type_e patch_edges; + + // Used to specify an early-out magnification threshold for filtering. + // If a texture is being minified (dcvars.iscale > rdraw_magThresh), then it + // drops back to point filtering. + fixed_t mag_threshold; +} draw_vars_t; + +extern draw_vars_t drawvars; + +extern byte playernumtotrans[MAXPLAYERS]; // CPhipps - what translation table for what player +extern byte *translationtables; + +typedef void (*R_DrawColumn_f)(draw_column_vars_t *dcvars); +R_DrawColumn_f R_GetDrawColumnFunc(enum column_pipeline_e type, + enum draw_filter_type_e filter, + enum draw_filter_type_e filterz); + +// Span blitting for rows, floor/ceiling. No Spectre effect needed. +typedef void (*R_DrawSpan_f)(draw_span_vars_t *dsvars); +R_DrawSpan_f R_GetDrawSpanFunc(enum draw_filter_type_e filter, + enum draw_filter_type_e filterz); +void R_DrawSpan(draw_span_vars_t *dsvars); + +void R_InitBuffer(int width, int height); + +// Initialize color translation tables, for player rendering etc. +void R_InitTranslationTables(void); + +// Rendering function. +void R_FillBackScreen(void); + +// If the view size is not full screen, draws a border around it. +void R_DrawViewBorder(void); + +// haleyjd 09/13/04: new function to call from main rendering loop +// which gets rid of the unnecessary reset of various variables during +// column drawing. +void R_ResetColumnBuffer(void); + +#endif diff --git a/src/r_drawcolpipeline.inl b/src/r_drawcolpipeline.inl new file mode 100644 index 0000000..8e122cd --- /dev/null +++ b/src/r_drawcolpipeline.inl @@ -0,0 +1,51 @@ + +// no color mapping +#define R_DRAWCOLUMN_FUNCNAME R_DRAWCOLUMN_FUNCNAME_COMPOSITE(_PointUV) +#define R_DRAWCOLUMN_PIPELINE (R_DRAWCOLUMN_PIPELINE_BASE | RDC_NOCOLMAP) +#include "r_drawcolumn.inl" + +// simple depth color mapping +#define R_DRAWCOLUMN_FUNCNAME R_DRAWCOLUMN_FUNCNAME_COMPOSITE(_PointUV_PointZ) +#define R_DRAWCOLUMN_PIPELINE R_DRAWCOLUMN_PIPELINE_BASE +#include "r_drawcolumn.inl" + +// z-dither +#define R_DRAWCOLUMN_FUNCNAME R_DRAWCOLUMN_FUNCNAME_COMPOSITE(_PointUV_LinearZ) +#define R_DRAWCOLUMN_PIPELINE (R_DRAWCOLUMN_PIPELINE_BASE | RDC_DITHERZ) +#include "r_drawcolumn.inl" + +// bilinear with no color mapping +#define R_DRAWCOLUMN_FUNCNAME R_DRAWCOLUMN_FUNCNAME_COMPOSITE(_LinearUV) +#define R_DRAWCOLUMN_PIPELINE (R_DRAWCOLUMN_PIPELINE_BASE | RDC_BILINEAR | RDC_NOCOLMAP) +#include "r_drawcolumn.inl" + +// bilinear with simple depth color mapping +#define R_DRAWCOLUMN_FUNCNAME R_DRAWCOLUMN_FUNCNAME_COMPOSITE(_LinearUV_PointZ) +#define R_DRAWCOLUMN_PIPELINE (R_DRAWCOLUMN_PIPELINE_BASE | RDC_BILINEAR) +#include "r_drawcolumn.inl" + +// bilinear + z-dither +#define R_DRAWCOLUMN_FUNCNAME R_DRAWCOLUMN_FUNCNAME_COMPOSITE(_LinearUV_LinearZ) +#define R_DRAWCOLUMN_PIPELINE (R_DRAWCOLUMN_PIPELINE_BASE | RDC_BILINEAR | RDC_DITHERZ) +#include "r_drawcolumn.inl" + +// rounded with no color mapping +#define R_DRAWCOLUMN_FUNCNAME R_DRAWCOLUMN_FUNCNAME_COMPOSITE(_RoundedUV) +#define R_DRAWCOLUMN_PIPELINE (R_DRAWCOLUMN_PIPELINE_BASE | RDC_ROUNDED | RDC_NOCOLMAP) +#include "r_drawcolumn.inl" + +// rounded with simple depth color mapping +#define R_DRAWCOLUMN_FUNCNAME R_DRAWCOLUMN_FUNCNAME_COMPOSITE(_RoundedUV_PointZ) +#define R_DRAWCOLUMN_PIPELINE (R_DRAWCOLUMN_PIPELINE_BASE | RDC_ROUNDED) +#include "r_drawcolumn.inl" + +// rounded + z-dither +#define R_DRAWCOLUMN_FUNCNAME R_DRAWCOLUMN_FUNCNAME_COMPOSITE(_RoundedUV_LinearZ) +#define R_DRAWCOLUMN_PIPELINE (R_DRAWCOLUMN_PIPELINE_BASE | RDC_ROUNDED | RDC_DITHERZ) +#include "r_drawcolumn.inl" + +#undef R_FLUSHWHOLE_FUNCNAME +#undef R_FLUSHHEADTAIL_FUNCNAME +#undef R_FLUSHQUAD_FUNCNAME +#undef R_DRAWCOLUMN_FUNCNAME_COMPOSITE +#undef R_DRAWCOLUMN_PIPELINE_BITS diff --git a/src/r_drawcolumn.inl b/src/r_drawcolumn.inl new file mode 100644 index 0000000..199a24e --- /dev/null +++ b/src/r_drawcolumn.inl @@ -0,0 +1,378 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + *-----------------------------------------------------------------------------*/ + + +#if (R_DRAWCOLUMN_PIPELINE_BITS == 8) +#define SCREENTYPE byte +#define TEMPBUF byte_tempbuf +#elif (R_DRAWCOLUMN_PIPELINE_BITS == 15) +#define SCREENTYPE unsigned short +#define TEMPBUF short_tempbuf +#elif (R_DRAWCOLUMN_PIPELINE_BITS == 16) +#define SCREENTYPE unsigned short +#define TEMPBUF short_tempbuf +#elif (R_DRAWCOLUMN_PIPELINE_BITS == 32) +#define SCREENTYPE unsigned int +#define TEMPBUF int_tempbuf +#endif + +#define GETDESTCOLOR8(col) (col) +#define GETDESTCOLOR15(col) (col) +#define GETDESTCOLOR16(col) (col) +#define GETDESTCOLOR32(col) (col) + +#if (R_DRAWCOLUMN_PIPELINE & RDC_TRANSLATED) +#define GETCOL8_MAPPED(col) (translation[(col)]) +#else +#define GETCOL8_MAPPED(col) (col) +#endif + +#if (R_DRAWCOLUMN_PIPELINE & RDC_NOCOLMAP) + #define GETCOL8_DEPTH(col) GETCOL8_MAPPED(col) +#else + #if (R_DRAWCOLUMN_PIPELINE & RDC_DITHERZ) + #define GETCOL8_DEPTH(col) (dither_colormaps[filter_getDitheredPixelLevel(x, y, fracz)][GETCOL8_MAPPED(col)]) + #else + #define GETCOL8_DEPTH(col) colormap[GETCOL8_MAPPED(col)] + #endif +#endif + +#if (R_DRAWCOLUMN_PIPELINE & RDC_BILINEAR) + #define GETCOL8(frac, nextfrac) GETCOL8_DEPTH(filter_getDitheredForColumn(x,y,frac,nextfrac)) + #define GETCOL15(frac, nextfrac) filter_getFilteredForColumn15(GETCOL8_DEPTH,frac,nextfrac) + #define GETCOL16(frac, nextfrac) filter_getFilteredForColumn16(GETCOL8_DEPTH,frac,nextfrac) + #define GETCOL32(frac, nextfrac) filter_getFilteredForColumn32(GETCOL8_DEPTH,frac,nextfrac) +#elif (R_DRAWCOLUMN_PIPELINE & RDC_ROUNDED) + #define GETCOL8(frac, nextfrac) GETCOL8_DEPTH(filter_getRoundedForColumn(frac,nextfrac)) + #define GETCOL15(frac, nextfrac) VID_PAL15(GETCOL8_DEPTH(filter_getRoundedForColumn(frac,nextfrac)), VID_COLORWEIGHTMASK) + #define GETCOL16(frac, nextfrac) VID_PAL16(GETCOL8_DEPTH(filter_getRoundedForColumn(frac,nextfrac)), VID_COLORWEIGHTMASK) + #define GETCOL32(frac, nextfrac) VID_PAL32(GETCOL8_DEPTH(filter_getRoundedForColumn(frac,nextfrac)), VID_COLORWEIGHTMASK) +#else + #define GETCOL8(frac, nextfrac) GETCOL8_DEPTH(source[(frac)>>FRACBITS]) + #define GETCOL15(frac, nextfrac) VID_PAL15(GETCOL8_DEPTH(source[(frac)>>FRACBITS]), VID_COLORWEIGHTMASK) + #define GETCOL16(frac, nextfrac) VID_PAL16(GETCOL8_DEPTH(source[(frac)>>FRACBITS]), VID_COLORWEIGHTMASK) + #define GETCOL32(frac, nextfrac) VID_PAL32(GETCOL8_DEPTH(source[(frac)>>FRACBITS]), VID_COLORWEIGHTMASK) +#endif + +#if (R_DRAWCOLUMN_PIPELINE & (RDC_BILINEAR|RDC_ROUNDED|RDC_DITHERZ)) + #define INCY(y) (y++) +#else + #define INCY(y) +#endif + +#if (R_DRAWCOLUMN_PIPELINE & RDC_TRANSLUCENT) +#define COLTYPE (COL_TRANS) +#elif (R_DRAWCOLUMN_PIPELINE & RDC_FUZZ) +#define COLTYPE (COL_FUZZ) +#else +#define COLTYPE (COL_OPAQUE) +#endif + +#if (R_DRAWCOLUMN_PIPELINE_BITS == 8) + #define GETCOL(frac, nextfrac) GETCOL8(frac, nextfrac) + #define GETDESTCOLOR(col) GETDESTCOLOR8(col) +#elif (R_DRAWCOLUMN_PIPELINE_BITS == 15) + #define GETCOL(frac, nextfrac) GETCOL15(frac, nextfrac) + #define GETDESTCOLOR(col) GETDESTCOLOR15(col) +#elif (R_DRAWCOLUMN_PIPELINE_BITS == 16) + #define GETCOL(frac, nextfrac) GETCOL16(frac, nextfrac) + #define GETDESTCOLOR(col) GETDESTCOLOR16(col) +#elif (R_DRAWCOLUMN_PIPELINE_BITS == 32) + #define GETCOL(frac, nextfrac) GETCOL32(frac, nextfrac) + #define GETDESTCOLOR(col) GETDESTCOLOR32(col) +#endif + +static void R_DRAWCOLUMN_FUNCNAME(draw_column_vars_t *dcvars) +{ + int count; + SCREENTYPE *dest; // killough + fixed_t frac; + const fixed_t fracstep = dcvars->iscale; +#if ((R_DRAWCOLUMN_PIPELINE & RDC_BILINEAR) && (R_DRAWCOLUMN_PIPELINE_BITS != 8)) + const fixed_t slope_texu = (dcvars->source == dcvars->nextsource) ? 0 : dcvars->texu & 0xffff; +#else + const fixed_t slope_texu = dcvars->texu; +#endif + + // drop back to point filtering if we're minifying +#if (R_DRAWCOLUMN_PIPELINE & (RDC_BILINEAR|RDC_ROUNDED)) + if (dcvars->iscale > drawvars.mag_threshold) { + R_GetDrawColumnFunc(R_DRAWCOLUMN_PIPELINE_TYPE, + RDRAW_FILTER_POINT, + drawvars.filterz)(dcvars); + return; + } +#endif + +#if (R_DRAWCOLUMN_PIPELINE & RDC_FUZZ) + // Adjust borders. Low... + if (!dcvars->yl) + dcvars->yl = 1; + + // .. and high. + if (dcvars->yh == viewheight-1) + dcvars->yh = viewheight - 2; +#endif + + // leban 1/17/99: + // removed the + 1 here, adjusted the if test, and added an increment + // later. this helps a compiler pipeline a bit better. the x86 + // assembler also does this. + + count = dcvars->yh - dcvars->yl; + + // leban 1/17/99: + // this case isn't executed too often. depending on how many instructions + // there are between here and the second if test below, this case could + // be moved down and might save instructions overall. since there are + // probably different wads that favor one way or the other, i'll leave + // this alone for now. + if (count < 0) // Zero length, column does not exceed a pixel. + return; + +#ifdef RANGECHECK + if (dcvars->x >= SCREENWIDTH + || dcvars->yl < 0 + || dcvars->yh >= SCREENHEIGHT) + I_Error("R_DrawColumn: %i to %i at %i", dcvars->yl, dcvars->yh, dcvars->x); +#endif + + // Determine scaling, which is the only mapping to be done. + #if (R_DRAWCOLUMN_PIPELINE & RDC_BILINEAR) + frac = dcvars->texturemid - (FRACUNIT>>1) + (dcvars->yl-centery)*fracstep; + #else + frac = dcvars->texturemid + (dcvars->yl-centery)*fracstep; + #endif + + if (dcvars->drawingmasked && dcvars->edgetype == RDRAW_MASKEDCOLUMNEDGE_SLOPED) { + // slope the top and bottom column edge based on the fractional u coordinate + // and dcvars->edgeslope, which were set in R_DrawMaskedColumn + // in r_things.c + if (dcvars->yl != 0) { + if (dcvars->edgeslope & RDRAW_EDGESLOPE_TOP_UP) { + // [/#] + int shift = ((0xffff-(slope_texu & 0xffff))/dcvars->iscale); + dcvars->yl += shift; + count -= shift; + frac += 0xffff-(slope_texu & 0xffff); + } + else if (dcvars->edgeslope & RDRAW_EDGESLOPE_TOP_DOWN) { + // [#\] + int shift = ((slope_texu & 0xffff)/dcvars->iscale); + dcvars->yl += shift; + count -= shift; + frac += slope_texu & 0xffff; + } + } + if (dcvars->yh != viewheight-1) { + if (dcvars->edgeslope & RDRAW_EDGESLOPE_BOT_UP) { + // [#/] + int shift = ((0xffff-(slope_texu & 0xffff))/dcvars->iscale); + dcvars->yh -= shift; + count -= shift; + } + else if (dcvars->edgeslope & RDRAW_EDGESLOPE_BOT_DOWN) { + // [\#] + int shift = ((slope_texu & 0xffff)/dcvars->iscale); + dcvars->yh -= shift; + count -= shift; + } + } + if (count <= 0) return; + } + + // Framebuffer destination address. + // SoM: MAGIC + { + // haleyjd: reordered predicates + if(temp_x == 4 || + (temp_x && (temptype != COLTYPE || temp_x + startx != dcvars->x))) + R_FlushColumns(); + + if(!temp_x) + { + startx = dcvars->x; + tempyl[0] = commontop = dcvars->yl; + tempyh[0] = commonbot = dcvars->yh; + temptype = COLTYPE; +#if (R_DRAWCOLUMN_PIPELINE & RDC_TRANSLUCENT) + temptranmap = tranmap; +#elif (R_DRAWCOLUMN_PIPELINE & RDC_FUZZ) + tempfuzzmap = fullcolormap; // SoM 7-28-04: Fix the fuzz problem. +#endif + R_FlushWholeColumns = R_FLUSHWHOLE_FUNCNAME; + R_FlushHTColumns = R_FLUSHHEADTAIL_FUNCNAME; + R_FlushQuadColumn = R_FLUSHQUAD_FUNCNAME; + dest = &TEMPBUF[dcvars->yl << 2]; + } else { + tempyl[temp_x] = dcvars->yl; + tempyh[temp_x] = dcvars->yh; + + if(dcvars->yl > commontop) + commontop = dcvars->yl; + if(dcvars->yh < commonbot) + commonbot = dcvars->yh; + + dest = &TEMPBUF[(dcvars->yl << 2) + temp_x]; + } + temp_x += 1; + } + +// do nothing else when drawin fuzz columns +#if (!(R_DRAWCOLUMN_PIPELINE & RDC_FUZZ)) + { + const byte *source = dcvars->source; + const lighttable_t *colormap = dcvars->colormap; + const byte *translation = dcvars->translation; +#if (R_DRAWCOLUMN_PIPELINE & (RDC_BILINEAR|RDC_ROUNDED|RDC_DITHERZ)) + int y = dcvars->yl; + const int x = dcvars->x; +#endif +#if (R_DRAWCOLUMN_PIPELINE & RDC_DITHERZ) + const int fracz = (dcvars->z >> 6) & 255; + const byte *dither_colormaps[2] = { dcvars->colormap, dcvars->nextcolormap }; +#endif +#if (R_DRAWCOLUMN_PIPELINE & RDC_BILINEAR) + #if (R_DRAWCOLUMN_PIPELINE_BITS == 8) + const int yl = dcvars->yl; + const byte *dither_sources[2] = { dcvars->source, dcvars->nextsource }; + const unsigned int filter_fracu = (dcvars->source == dcvars->nextsource) ? 0 : (dcvars->texu>>8) & 0xff; + #else + const byte *nextsource = dcvars->nextsource; + const unsigned int filter_fracu = (dcvars->source == dcvars->nextsource) ? 0 : dcvars->texu & 0xffff; + #endif +#endif +#if (R_DRAWCOLUMN_PIPELINE & RDC_ROUNDED) + const byte *prevsource = dcvars->prevsource; + const byte *nextsource = dcvars->nextsource; + const unsigned int filter_fracu = (dcvars->source == dcvars->nextsource) ? 0 : (dcvars->texu>>8) & 0xff; +#endif + + count++; + + // Inner loop that does the actual texture mapping, + // e.g. a DDA-lile scaling. + // This is as fast as it gets. (Yeah, right!!! -- killough) + // + // killough 2/1/98: more performance tuning + + if (dcvars->texheight == 128) { + #define FIXEDT_128MASK ((127<texheight == 0) { + /* cph - another special case */ + while (count--) { + *dest = GETDESTCOLOR(GETCOL(frac, (frac+FRACUNIT))); + INCY(y); + dest += 4; + frac += fracstep; + } + } else { + unsigned heightmask = dcvars->texheight-1; // CPhipps - specify type + if (! (dcvars->texheight & heightmask) ) { // power of 2 -- killough + fixed_t fixedt_heightmask = (heightmask<=0) { // texture height is a power of 2 -- killough + *dest = GETDESTCOLOR(GETCOL(frac & fixedt_heightmask, (frac+FRACUNIT) & fixedt_heightmask)); + INCY(y); + dest += 4; + frac += fracstep; + *dest = GETDESTCOLOR(GETCOL(frac & fixedt_heightmask, (frac+FRACUNIT) & fixedt_heightmask)); + INCY(y); + dest += 4; + frac += fracstep; + } + if (count & 1) + *dest = GETDESTCOLOR(GETCOL(frac & fixedt_heightmask, (frac+FRACUNIT) & fixedt_heightmask)); + INCY(y); + } else { + fixed_t nextfrac = 0; + + heightmask++; + heightmask <<= FRACBITS; + + if (frac < 0) + while ((frac += heightmask) < 0); + else + while (frac >= (int)heightmask) + frac -= heightmask; + +#if (R_DRAWCOLUMN_PIPELINE & (RDC_BILINEAR|RDC_ROUNDED)) + nextfrac = frac + FRACUNIT; + while (nextfrac >= (int)heightmask) + nextfrac -= heightmask; +#endif + +#define INCFRAC(f) if ((f += fracstep) >= (int)heightmask) f -= heightmask; + + while (count--) { + // Re-map color indices from wall texture column + // using a lighting/special effects LUT. + + // heightmask is the Tutti-Frutti fix -- killough + + *dest = GETDESTCOLOR(GETCOL(frac, nextfrac)); + INCY(y); + dest += 4; + INCFRAC(frac); +#if (R_DRAWCOLUMN_PIPELINE & (RDC_BILINEAR|RDC_ROUNDED)) + INCFRAC(nextfrac); +#endif + } + } + } + } +#endif // (!(R_DRAWCOLUMN_PIPELINE & RDC_FUZZ)) +} + +#undef GETDESTCOLOR32 +#undef GETDESTCOLOR16 +#undef GETDESTCOLOR15 +#undef GETDESTCOLOR8 +#undef GETDESTCOLOR +#undef GETCOL8_MAPPED +#undef GETCOL8_DEPTH +#undef GETCOL32 +#undef GETCOL16 +#undef GETCOL15 +#undef GETCOL8 +#undef GETCOL +#undef INCY +#undef INCFRAC +#undef COLTYPE +#undef TEMPBUF +#undef SCREENTYPE + +#undef R_DRAWCOLUMN_FUNCNAME +#undef R_DRAWCOLUMN_PIPELINE diff --git a/src/r_drawflush.inl b/src/r_drawflush.inl new file mode 100644 index 0000000..ab8ce61 --- /dev/null +++ b/src/r_drawflush.inl @@ -0,0 +1,300 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + *-----------------------------------------------------------------------------*/ + +#if (R_DRAWCOLUMN_PIPELINE_BITS == 8) +#define SCREENTYPE byte +#define TOPLEFT byte_topleft +#define PITCH byte_pitch +#define TEMPBUF byte_tempbuf +#elif (R_DRAWCOLUMN_PIPELINE_BITS == 15) +#define SCREENTYPE unsigned short +#define TOPLEFT short_topleft +#define PITCH short_pitch +#define TEMPBUF short_tempbuf +#elif (R_DRAWCOLUMN_PIPELINE_BITS == 16) +#define SCREENTYPE unsigned short +#define TOPLEFT short_topleft +#define PITCH short_pitch +#define TEMPBUF short_tempbuf +#elif (R_DRAWCOLUMN_PIPELINE_BITS == 32) +#define SCREENTYPE unsigned int +#define TOPLEFT int_topleft +#define PITCH int_pitch +#define TEMPBUF int_tempbuf +#endif + +#if (R_DRAWCOLUMN_PIPELINE & RDC_TRANSLUCENT) +#define GETDESTCOLOR8(col1, col2) (temptranmap[((col1)<<8)+(col2)]) +#define GETDESTCOLOR15(col1, col2) (GETBLENDED15_3268((col1), (col2))) +#define GETDESTCOLOR16(col1, col2) (GETBLENDED16_3268((col1), (col2))) +#define GETDESTCOLOR32(col1, col2) (GETBLENDED32_3268((col1), (col2))) +#elif (R_DRAWCOLUMN_PIPELINE & RDC_FUZZ) +#define GETDESTCOLOR8(col) (tempfuzzmap[6*256+(col)]) +#define GETDESTCOLOR15(col) GETBLENDED15_9406(col, 0) +#define GETDESTCOLOR16(col) GETBLENDED16_9406(col, 0) +#define GETDESTCOLOR32(col) GETBLENDED32_9406(col, 0) +#else +#define GETDESTCOLOR8(col) (col) +#define GETDESTCOLOR15(col) (col) +#define GETDESTCOLOR16(col) (col) +#define GETDESTCOLOR32(col) (col) +#endif + +#if (R_DRAWCOLUMN_PIPELINE & RDC_TRANSLUCENT) + #if (R_DRAWCOLUMN_PIPELINE_BITS == 8) + #define GETDESTCOLOR(col1, col2) GETDESTCOLOR8(col1, col2) + #elif (R_DRAWCOLUMN_PIPELINE_BITS == 15) + #define GETDESTCOLOR(col1, col2) GETDESTCOLOR15(col1, col2) + #elif (R_DRAWCOLUMN_PIPELINE_BITS == 16) + #define GETDESTCOLOR(col1, col2) GETDESTCOLOR16(col1, col2) + #elif (R_DRAWCOLUMN_PIPELINE_BITS == 32) + #define GETDESTCOLOR(col1, col2) GETDESTCOLOR32(col1, col2) + #endif +#else + #if (R_DRAWCOLUMN_PIPELINE_BITS == 8) + #define GETDESTCOLOR(col) GETDESTCOLOR8(col) + #elif (R_DRAWCOLUMN_PIPELINE_BITS == 15) + #define GETDESTCOLOR(col) GETDESTCOLOR15(col) + #elif (R_DRAWCOLUMN_PIPELINE_BITS == 16) + #define GETDESTCOLOR(col) GETDESTCOLOR16(col) + #elif (R_DRAWCOLUMN_PIPELINE_BITS == 32) + #define GETDESTCOLOR(col) GETDESTCOLOR32(col) + #endif +#endif + +// +// R_FlushWholeOpaque +// +// Flushes the entire columns in the buffer, one at a time. +// This is used when a quad flush isn't possible. +// Opaque version -- no remapping whatsoever. +// +static void R_FLUSHWHOLE_FUNCNAME(void) +{ + SCREENTYPE *source; + SCREENTYPE *dest; + int count, yl; + + while(--temp_x >= 0) + { + yl = tempyl[temp_x]; + source = &TEMPBUF[temp_x + (yl << 2)]; + dest = drawvars.TOPLEFT + yl*drawvars.PITCH + startx + temp_x; + count = tempyh[temp_x] - yl + 1; + + while(--count >= 0) + { +#if (R_DRAWCOLUMN_PIPELINE & RDC_TRANSLUCENT) + *dest = GETDESTCOLOR(*dest, *source); +#elif (R_DRAWCOLUMN_PIPELINE & RDC_FUZZ) + // SoM 7-28-04: Fix the fuzz problem. + *dest = GETDESTCOLOR(dest[fuzzoffset[fuzzpos]]); + + // Clamp table lookup index. + if(++fuzzpos == FUZZTABLE) + fuzzpos = 0; +#else + *dest = *source; +#endif + + source += 4; + dest += drawvars.PITCH; + } + } +} + +// +// R_FlushHTOpaque +// +// Flushes the head and tail of columns in the buffer in +// preparation for a quad flush. +// Opaque version -- no remapping whatsoever. +// +static void R_FLUSHHEADTAIL_FUNCNAME(void) +{ + SCREENTYPE *source; + SCREENTYPE *dest; + int count, colnum = 0; + int yl, yh; + + while(colnum < 4) + { + yl = tempyl[colnum]; + yh = tempyh[colnum]; + + // flush column head + if(yl < commontop) + { + source = &TEMPBUF[colnum + (yl << 2)]; + dest = drawvars.TOPLEFT + yl*drawvars.PITCH + startx + colnum; + count = commontop - yl; + + while(--count >= 0) + { +#if (R_DRAWCOLUMN_PIPELINE & RDC_TRANSLUCENT) + // haleyjd 09/11/04: use temptranmap here + *dest = GETDESTCOLOR(*dest, *source); +#elif (R_DRAWCOLUMN_PIPELINE & RDC_FUZZ) + // SoM 7-28-04: Fix the fuzz problem. + *dest = GETDESTCOLOR(dest[fuzzoffset[fuzzpos]]); + + // Clamp table lookup index. + if(++fuzzpos == FUZZTABLE) + fuzzpos = 0; +#else + *dest = *source; +#endif + + source += 4; + dest += drawvars.PITCH; + } + } + + // flush column tail + if(yh > commonbot) + { + source = &TEMPBUF[colnum + ((commonbot + 1) << 2)]; + dest = drawvars.TOPLEFT + (commonbot + 1)*drawvars.PITCH + startx + colnum; + count = yh - commonbot; + + while(--count >= 0) + { +#if (R_DRAWCOLUMN_PIPELINE & RDC_TRANSLUCENT) + // haleyjd 09/11/04: use temptranmap here + *dest = GETDESTCOLOR(*dest, *source); +#elif (R_DRAWCOLUMN_PIPELINE & RDC_FUZZ) + // SoM 7-28-04: Fix the fuzz problem. + *dest = GETDESTCOLOR(dest[fuzzoffset[fuzzpos]]); + + // Clamp table lookup index. + if(++fuzzpos == FUZZTABLE) + fuzzpos = 0; +#else + *dest = *source; +#endif + + source += 4; + dest += drawvars.PITCH; + } + } + ++colnum; + } +} + +static void R_FLUSHQUAD_FUNCNAME(void) +{ + SCREENTYPE *source = &TEMPBUF[commontop << 2]; + SCREENTYPE *dest = drawvars.TOPLEFT + commontop*drawvars.PITCH + startx; + int count; +#if (R_DRAWCOLUMN_PIPELINE & RDC_FUZZ) + int fuzz1, fuzz2, fuzz3, fuzz4; + + fuzz1 = fuzzpos; + fuzz2 = (fuzz1 + tempyl[1]) % FUZZTABLE; + fuzz3 = (fuzz2 + tempyl[2]) % FUZZTABLE; + fuzz4 = (fuzz3 + tempyl[3]) % FUZZTABLE; +#endif + + count = commonbot - commontop + 1; + +#if (R_DRAWCOLUMN_PIPELINE & RDC_TRANSLUCENT) + while(--count >= 0) + { + dest[0] = GETDESTCOLOR(dest[0], source[0]); + dest[1] = GETDESTCOLOR(dest[1], source[1]); + dest[2] = GETDESTCOLOR(dest[2], source[2]); + dest[3] = GETDESTCOLOR(dest[3], source[3]); + source += 4 * sizeof(byte); + dest += drawvars.PITCH * sizeof(byte); + } +#elif (R_DRAWCOLUMN_PIPELINE & RDC_FUZZ) + while(--count >= 0) + { + dest[0] = GETDESTCOLOR(dest[0 + fuzzoffset[fuzz1]]); + dest[1] = GETDESTCOLOR(dest[1 + fuzzoffset[fuzz2]]); + dest[2] = GETDESTCOLOR(dest[2 + fuzzoffset[fuzz3]]); + dest[3] = GETDESTCOLOR(dest[3 + fuzzoffset[fuzz4]]); + fuzz1 = (fuzz1 + 1) % FUZZTABLE; + fuzz2 = (fuzz2 + 1) % FUZZTABLE; + fuzz3 = (fuzz3 + 1) % FUZZTABLE; + fuzz4 = (fuzz4 + 1) % FUZZTABLE; + source += 4 * sizeof(byte); + dest += drawvars.PITCH * sizeof(byte); + } +#else + #if (R_DRAWCOLUMN_PIPELINE_BITS == 8) + if ((sizeof(int) == 4) && (((int)source % 4) == 0) && (((int)dest % 4) == 0)) { + while(--count >= 0) + { + *(int *)dest = *(int *)source; + source += 4 * sizeof(byte); + dest += drawvars.PITCH * sizeof(byte); + } + } else { + while(--count >= 0) + { + dest[0] = source[0]; + dest[1] = source[1]; + dest[2] = source[2]; + dest[3] = source[3]; + source += 4 * sizeof(byte); + dest += drawvars.PITCH * sizeof(byte); + } + } + #else + while(--count >= 0) + { + dest[0] = source[0]; + dest[1] = source[1]; + dest[2] = source[2]; + dest[3] = source[3]; + source += 4; + dest += drawvars.PITCH; + } + #endif +#endif +} + +#undef GETDESTCOLOR32 +#undef GETDESTCOLOR16 +#undef GETDESTCOLOR15 +#undef GETDESTCOLOR8 +#undef GETDESTCOLOR + +#undef TEMPBUF +#undef PITCH +#undef TOPLEFT +#undef SCREENTYPE + +#undef R_DRAWCOLUMN_PIPELINE_BITS +#undef R_DRAWCOLUMN_PIPELINE +#undef R_FLUSHWHOLE_FUNCNAME +#undef R_FLUSHHEADTAIL_FUNCNAME +#undef R_FLUSHQUAD_FUNCNAME diff --git a/src/r_drawspan.inl b/src/r_drawspan.inl new file mode 100644 index 0000000..84cc95d --- /dev/null +++ b/src/r_drawspan.inl @@ -0,0 +1,160 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + *-----------------------------------------------------------------------------*/ + +// +// R_DrawSpan +// + +#if (R_DRAWSPAN_PIPELINE_BITS == 8) +#define SCREENTYPE byte +#define TOPLEFT byte_topleft +#define PITCH byte_pitch +#elif (R_DRAWSPAN_PIPELINE_BITS == 15) +#define SCREENTYPE unsigned short +#define TOPLEFT short_topleft +#define PITCH short_pitch +#elif (R_DRAWSPAN_PIPELINE_BITS == 16) +#define SCREENTYPE unsigned short +#define TOPLEFT short_topleft +#define PITCH short_pitch +#elif (R_DRAWSPAN_PIPELINE_BITS == 32) +#define SCREENTYPE unsigned int +#define TOPLEFT int_topleft +#define PITCH int_pitch +#endif + +#if (R_DRAWSPAN_PIPELINE & RDC_DITHERZ) + #define GETDEPTHMAP(col) dither_colormaps[filter_getDitheredPixelLevel(x1, y, fracz)][(col)] +#else + #define GETDEPTHMAP(col) colormap[(col)] +#endif + +#if (R_DRAWSPAN_PIPELINE_BITS == 8) + #define GETCOL_POINT(col) GETDEPTHMAP(col) + #define GETCOL_LINEAR(col) GETDEPTHMAP(col) +#elif (R_DRAWSPAN_PIPELINE_BITS == 15) + #define GETCOL_POINT(col) VID_PAL15(GETDEPTHMAP(col), VID_COLORWEIGHTMASK) + #define GETCOL_LINEAR(col) filter_getFilteredForSpan15(GETDEPTHMAP, xfrac, yfrac) +#elif (R_DRAWSPAN_PIPELINE_BITS == 16) + #define GETCOL_POINT(col) VID_PAL16(GETDEPTHMAP(col), VID_COLORWEIGHTMASK) + #define GETCOL_LINEAR(col) filter_getFilteredForSpan16(GETDEPTHMAP, xfrac, yfrac) +#elif (R_DRAWSPAN_PIPELINE_BITS == 32) + #define GETCOL_POINT(col) VID_PAL32(GETDEPTHMAP(col), VID_COLORWEIGHTMASK) + #define GETCOL_LINEAR(col) filter_getFilteredForSpan32(GETDEPTHMAP, xfrac, yfrac) +#endif + +#if (R_DRAWSPAN_PIPELINE & RDC_BILINEAR) + #define GETCOL(col) GETCOL_LINEAR(col) +#else + #define GETCOL(col) GETCOL_POINT(col) +#endif + +static void R_DRAWSPAN_FUNCNAME(draw_span_vars_t *dsvars) +{ +#if (R_DRAWSPAN_PIPELINE & (RDC_ROUNDED|RDC_BILINEAR)) + // drop back to point filtering if we're minifying + // 49152 = FRACUNIT * 0.75 + if ((D_abs(dsvars->xstep) > drawvars.mag_threshold) + || (D_abs(dsvars->ystep) > drawvars.mag_threshold)) + { + R_GetDrawSpanFunc(RDRAW_FILTER_POINT, + drawvars.filterz)(dsvars); + return; + } +#endif + { + unsigned count = dsvars->x2 - dsvars->x1 + 1; + fixed_t xfrac = dsvars->xfrac; + fixed_t yfrac = dsvars->yfrac; + const fixed_t xstep = dsvars->xstep; + const fixed_t ystep = dsvars->ystep; + const byte *source = dsvars->source; + const byte *colormap = dsvars->colormap; + SCREENTYPE *dest = drawvars.TOPLEFT + dsvars->y*drawvars.PITCH + dsvars->x1; +#if (R_DRAWSPAN_PIPELINE & (RDC_DITHERZ|RDC_BILINEAR)) + const int y = dsvars->y; + int x1 = dsvars->x1; +#endif +#if (R_DRAWSPAN_PIPELINE & RDC_DITHERZ) + const int fracz = (dsvars->z >> 12) & 255; + const byte *dither_colormaps[2] = { dsvars->colormap, dsvars->nextcolormap }; +#endif + + while (count) { +#if ((R_DRAWSPAN_PIPELINE_BITS != 8) && (R_DRAWSPAN_PIPELINE & RDC_BILINEAR)) + // truecolor bilinear filtered + *dest++ = GETCOL(0); + xfrac += xstep; + yfrac += ystep; + count--; + #if (R_DRAWSPAN_PIPELINE & RDC_DITHERZ) + x1--; + #endif +#elif (R_DRAWSPAN_PIPELINE & RDC_ROUNDED) + *dest++ = GETCOL(filter_getRoundedForSpan(xfrac, yfrac)); + xfrac += xstep; + yfrac += ystep; + count--; + #if (R_DRAWSPAN_PIPELINE & RDC_DITHERZ) + x1--; + #endif +#else + #if (R_DRAWSPAN_PIPELINE & RDC_BILINEAR) + // 8 bit bilinear + const fixed_t xtemp = ((xfrac >> 16) + (filter_getDitheredPixelLevel(x1, y, ((xfrac>>8)&0xff)))) & 63; + const fixed_t ytemp = ((yfrac >> 10) + 64*(filter_getDitheredPixelLevel(x1, y, ((yfrac>>8)&0xff)))) & 4032; + #else + const fixed_t xtemp = (xfrac >> 16) & 63; + const fixed_t ytemp = (yfrac >> 10) & 4032; + #endif + const fixed_t spot = xtemp | ytemp; + xfrac += xstep; + yfrac += ystep; + *dest++ = GETCOL(source[spot]); + count--; + #if (R_DRAWSPAN_PIPELINE & (RDC_DITHERZ|RDC_BILINEAR)) + x1--; + #endif +#endif + } + } +} + +#undef GETDEPTHMAP +#undef GETCOL_LINEAR +#undef GETCOL_POINT +#undef GETCOL +#undef PITCH +#undef TOPLEFT +#undef SCREENTYPE + +#undef R_DRAWSPAN_PIPELINE_BITS +#undef R_DRAWSPAN_PIPELINE +#undef R_DRAWSPAN_FUNCNAME diff --git a/src/r_filter.c b/src/r_filter.c new file mode 100644 index 0000000..4b44f14 --- /dev/null +++ b/src/r_filter.c @@ -0,0 +1,119 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + *-----------------------------------------------------------------------------*/ + +#include "doomtype.h" +#include "r_filter.h" + +#define DMR 16 +byte filter_ditherMatrix[DITHER_DIM][DITHER_DIM] = { + 0*DMR, 14*DMR, 3*DMR, 13*DMR, 11*DMR, 5*DMR, 8*DMR, 6*DMR, + 12*DMR, 2*DMR, 15*DMR, 1*DMR, 7*DMR, 9*DMR, 4*DMR, 10*DMR +}; + +byte filter_roundedUVMap[FILTER_UVDIM*FILTER_UVDIM]; +byte filter_roundedRowMap[4*16]; + +void R_FilterInit(void) { + int i,j,s,t; + + // scale2x takes the following source: + // A B C + // D E F + // G H I + // + // and doubles the size of E to produce: + // E0 E1 + // E2 E3 + // + // E0 = D == B && B != F && D != H ? D : E; + // E1 = B == F && B != D && F != H ? F : E; + // E2 = D == H && D != B && H != F ? D : E; + // E3 = H == F && D != H && B != F ? F : E; + // + // to make this comparison regimen faster, we encode source color + // equivalency into a single byte with the getCode() macro + // + // #define getCode(b,f,h,d) ( (b == f)<<0 | (f == h)<<1 | (h == d)<<2 | (d == b)<<3 ) + + // encode the scale2x conditionals into a lookup code + for (i=0; i<16; i++) { + // E0 = D == B && B != F && D != H ? D : E; // 10-0 => 1000 or 1010 => 8 or A + filter_roundedRowMap[0*16+i] = (i == 0x8 || i == 0xA) ? 0 : 1; + // E1 = B == F && B != D && F != H ? F : E; // 0-01 => 0101 or 0001 => 5 or 1 + filter_roundedRowMap[1*16+i] = (i == 0x5 || i == 0x1) ? 2 : 1; + // E2 = D == H && D != B && H != F ? D : E; // 010- => 0101 or 0100 => 5 or 4 + filter_roundedRowMap[2*16+i] = (i == 0x4 || i == 0x5) ? 0 : 1; + // E3 = H == F && D != H && B != F ? F : E; // -010 => 1010 or 0010 => A or 2 + filter_roundedRowMap[3*16+i] = (i == 0xA || i == 0x2) ? 2 : 1; + } + + // fill the uvMap. this will return: + // 0/\1 + // /4 \ + // \ / + // 2\/3 + // .. based on the uv coordinates + for (i=0; i=0 && t>=0) filter_roundedUVMap[i*FILTER_UVDIM+j] = (s+t > FILTER_UVDIM/2) ? 0 : 4; + else if (s>=0 && t<=0) filter_roundedUVMap[i*FILTER_UVDIM+j] = (s-t > FILTER_UVDIM/2) ? 2 : 4; + else if (s<=0 && t>=0) filter_roundedUVMap[i*FILTER_UVDIM+j] = (-s+t > FILTER_UVDIM/2) ? 1 : 4; + else if (s<=0 && t<=0) filter_roundedUVMap[i*FILTER_UVDIM+j] = (-s-t > FILTER_UVDIM/2) ? 3 : 4; + else filter_roundedUVMap[i*FILTER_UVDIM+j] = 4; + } + } +} + +byte *filter_getScale2xQuadColors(byte e, byte b, byte f, byte h, byte d) { + // A B C + // D E F + // G H I + // perform the Scale2x algorithm (quickly) to get the new quad to represent E + static byte quad[5]; + static byte rowColors[3]; + int code; + + rowColors[0] = d; + rowColors[1] = e; + rowColors[2] = f; + + #define getCode(b,f,h,d) ( (b == f)<<0 | (f == h)<<1 | (h == d)<<2 | (d == b)<<3 ) + + code = getCode(b,f,h,d); + quad[0] = rowColors[filter_roundedRowMap[0*16+code]]; + quad[1] = rowColors[filter_roundedRowMap[1*16+code]]; + quad[2] = rowColors[filter_roundedRowMap[2*16+code]]; + quad[3] = rowColors[filter_roundedRowMap[3*16+code]]; + quad[4] = e; + + return quad; +} diff --git a/src/r_filter.h b/src/r_filter.h new file mode 100644 index 0000000..8151eac --- /dev/null +++ b/src/r_filter.h @@ -0,0 +1,174 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + *-----------------------------------------------------------------------------*/ + +#ifndef R_FILTER_H +#define R_FILTER_H + +#define DITHER_DIM 4 + +extern byte filter_ditherMatrix[DITHER_DIM][DITHER_DIM]; +#define FILTER_UVBITS 6 +#define FILTER_UVDIM (1<> 8), which was empirically +// derived. the "-dcvars.yl" is apparently required to offset some minor +// shaking in coordinate y-axis and prevents dithering seams +#define FILTER_GETV(x,y,texV,nextRowTexV) \ + (filter_getDitheredPixelLevel(x, y, (((texV) - yl) >> 8)&0xff) ? ((nextRowTexV)>>FRACBITS) : ((texV)>>FRACBITS)) + +// Choose current column or next column to the right based on dither of the +// fractional texture U coord +#define filter_getDitheredForColumn(x, y, texV, nextRowTexV) \ + dither_sources[(filter_getDitheredPixelLevel(x, y, filter_fracu))][FILTER_GETV(x,y,texV,nextRowTexV)] + +#define filter_getRoundedForColumn(texV, nextRowTexV) \ + filter_getScale2xQuadColors( \ + source[ ((texV)>>FRACBITS) ], \ + source[ (MAX(0, ((texV)>>FRACBITS)-1)) ], \ + nextsource[ ((texV)>>FRACBITS) ], \ + source[ ((nextRowTexV)>>FRACBITS) ], \ + prevsource[ ((texV)>>FRACBITS) ] \ + ) \ + [ filter_roundedUVMap[ \ + ((filter_fracu>>(8-FILTER_UVBITS))<>8) & 0xff)>>(8-FILTER_UVBITS)) \ + ] ] + +#define filter_getRoundedForSpan(texU, texV) \ + filter_getScale2xQuadColors( \ + source[ (((texU)>>16)&0x3f) | (((texV)>>10)&0xfc0) ], \ + source[ (((texU)>>16)&0x3f) | ((((texV)-FRACUNIT)>>10)&0xfc0) ], \ + source[ ((((texU)+FRACUNIT)>>16)&0x3f) | (((texV)>>10)&0xfc0) ], \ + source[ (((texU)>>16)&0x3f) | ((((texV)+FRACUNIT)>>10)&0xfc0) ], \ + source[ ((((texU)-FRACUNIT)>>16)&0x3f) | (((texV)>>10)&0xfc0) ] \ + ) \ + [ filter_roundedUVMap[ \ + (((((texU)>>8) & 0xff)>>(8-FILTER_UVBITS))<>8) & 0xff)>>(8-FILTER_UVBITS)) \ + ] ] + +byte *filter_getScale2xQuadColors(byte e, byte b, byte f, byte h, byte d); + +// This is the horrendous macro version of the function commented out of +// r_filter.c. It does a bilinear blend on the four source texels for a +// given u and v +#define filter_getFilteredForColumn32(depthmap, texV, nextRowTexV) ( \ + VID_PAL32( depthmap(nextsource[(nextRowTexV)>>FRACBITS]), (filter_fracu*((texV)&0xffff))>>(32-VID_COLORWEIGHTBITS) ) + \ + VID_PAL32( depthmap(source[(nextRowTexV)>>FRACBITS]), ((0xffff-filter_fracu)*((texV)&0xffff))>>(32-VID_COLORWEIGHTBITS) ) + \ + VID_PAL32( depthmap(source[(texV)>>FRACBITS]), ((0xffff-filter_fracu)*(0xffff-((texV)&0xffff)))>>(32-VID_COLORWEIGHTBITS) ) + \ + VID_PAL32( depthmap(nextsource[(texV)>>FRACBITS]), (filter_fracu*(0xffff-((texV)&0xffff)))>>(32-VID_COLORWEIGHTBITS) )) + +// The 16 bit method of the filtering doesn't really maintain enough +// accuracy for discerning viewers, but the alternative requires converting +// from 32 bit, which is slow and requires both the intPalette and the +// shortPalette to be in memory at the same time. +#define filter_getFilteredForColumn16(depthmap, texV, nextRowTexV) ( \ + VID_PAL16( depthmap(nextsource[(nextRowTexV)>>FRACBITS]), (filter_fracu*((texV)&0xffff))>>(32-VID_COLORWEIGHTBITS) ) + \ + VID_PAL16( depthmap(source[(nextRowTexV)>>FRACBITS]), ((0xffff-filter_fracu)*((texV)&0xffff))>>(32-VID_COLORWEIGHTBITS) ) + \ + VID_PAL16( depthmap(source[(texV)>>FRACBITS]), ((0xffff-filter_fracu)*(0xffff-((texV)&0xffff)))>>(32-VID_COLORWEIGHTBITS) ) + \ + VID_PAL16( depthmap(nextsource[(texV)>>FRACBITS]), (filter_fracu*(0xffff-((texV)&0xffff)))>>(32-VID_COLORWEIGHTBITS) )) + +#define filter_getFilteredForColumn15(depthmap, texV, nextRowTexV) ( \ + VID_PAL15( depthmap(nextsource[(nextRowTexV)>>FRACBITS]), (filter_fracu*((texV)&0xffff))>>(32-VID_COLORWEIGHTBITS) ) + \ + VID_PAL15( depthmap(source[(nextRowTexV)>>FRACBITS]), ((0xffff-filter_fracu)*((texV)&0xffff))>>(32-VID_COLORWEIGHTBITS) ) + \ + VID_PAL15( depthmap(source[(texV)>>FRACBITS]), ((0xffff-filter_fracu)*(0xffff-((texV)&0xffff)))>>(32-VID_COLORWEIGHTBITS) ) + \ + VID_PAL15( depthmap(nextsource[(texV)>>FRACBITS]), (filter_fracu*(0xffff-((texV)&0xffff)))>>(32-VID_COLORWEIGHTBITS) )) + +// Same as for column but wrapping at 64 +#define filter_getFilteredForSpan32(depthmap, texU, texV) ( \ + VID_PAL32( depthmap(source[ ((((texU)+FRACUNIT)>>16)&0x3f) | ((((texV)+FRACUNIT)>>10)&0xfc0)]), (unsigned int)(((texU)&0xffff)*((texV)&0xffff))>>(32-VID_COLORWEIGHTBITS)) + \ + VID_PAL32( depthmap(source[ (((texU)>>16)&0x3f) | ((((texV)+FRACUNIT)>>10)&0xfc0)]), (unsigned int)((0xffff-((texU)&0xffff))*((texV)&0xffff))>>(32-VID_COLORWEIGHTBITS)) + \ + VID_PAL32( depthmap(source[ (((texU)>>16)&0x3f) | (((texV)>>10)&0xfc0)]), (unsigned int)((0xffff-((texU)&0xffff))*(0xffff-((texV)&0xffff)))>>(32-VID_COLORWEIGHTBITS)) + \ + VID_PAL32( depthmap(source[ ((((texU)+FRACUNIT)>>16)&0x3f) | (((texV)>>10)&0xfc0)]), (unsigned int)(((texU)&0xffff)*(0xffff-((texV)&0xffff)))>>(32-VID_COLORWEIGHTBITS))) + +// Use 16 bit addition here since it's a little faster and the defects from +// such low-accuracy blending are less visible on spans +#define filter_getFilteredForSpan16(depthmap, texU, texV) ( \ + VID_PAL16( depthmap(source[ ((((texU)+FRACUNIT)>>16)&0x3f) | ((((texV)+FRACUNIT)>>10)&0xfc0)]), (unsigned int)(((texU)&0xffff)*((texV)&0xffff))>>(32-VID_COLORWEIGHTBITS)) + \ + VID_PAL16( depthmap(source[ (((texU)>>16)&0x3f) | ((((texV)+FRACUNIT)>>10)&0xfc0)]), (unsigned int)((0xffff-((texU)&0xffff))*((texV)&0xffff))>>(32-VID_COLORWEIGHTBITS)) + \ + VID_PAL16( depthmap(source[ (((texU)>>16)&0x3f) | (((texV)>>10)&0xfc0)]), (unsigned int)((0xffff-((texU)&0xffff))*(0xffff-((texV)&0xffff)))>>(32-VID_COLORWEIGHTBITS)) + \ + VID_PAL16( depthmap(source[ ((((texU)+FRACUNIT)>>16)&0x3f) | (((texV)>>10)&0xfc0)]), (unsigned int)(((texU)&0xffff)*(0xffff-((texV)&0xffff)))>>(32-VID_COLORWEIGHTBITS))) + +#define filter_getFilteredForSpan15(depthmap, texU, texV) ( \ + VID_PAL15( depthmap(source[ ((((texU)+FRACUNIT)>>16)&0x3f) | ((((texV)+FRACUNIT)>>10)&0xfc0)]), (unsigned int)(((texU)&0xffff)*((texV)&0xffff))>>(32-VID_COLORWEIGHTBITS)) + \ + VID_PAL15( depthmap(source[ (((texU)>>16)&0x3f) | ((((texV)+FRACUNIT)>>10)&0xfc0)]), (unsigned int)((0xffff-((texU)&0xffff))*((texV)&0xffff))>>(32-VID_COLORWEIGHTBITS)) + \ + VID_PAL15( depthmap(source[ (((texU)>>16)&0x3f) | (((texV)>>10)&0xfc0)]), (unsigned int)((0xffff-((texU)&0xffff))*(0xffff-((texV)&0xffff)))>>(32-VID_COLORWEIGHTBITS)) + \ + VID_PAL15( depthmap(source[ ((((texU)+FRACUNIT)>>16)&0x3f) | (((texV)>>10)&0xfc0)]), (unsigned int)(((texU)&0xffff)*(0xffff-((texV)&0xffff)))>>(32-VID_COLORWEIGHTBITS))) + +// do red and blue at once for slight speedup + +#define GETBLENDED15_5050(col1, col2) \ + ((((col1&0x7c1f)+(col2&0x7c1f))>>1)&0x7c1f) | \ + ((((col1&0x03e0)+(col2&0x03e0))>>1)&0x03e0) + +#define GETBLENDED16_5050(col1, col2) \ + ((((col1&0xf81f)+(col2&0xf81f))>>1)&0xf81f) | \ + ((((col1&0x07e0)+(col2&0x07e0))>>1)&0x07e0) + +#define GETBLENDED32_5050(col1, col2) \ + ((((col1&0xff00ff)+(col2&0xff00ff))>>1)&0xff00ff) | \ + ((((col1&0x00ff00)+(col2&0x00ff00))>>1)&0x00ff00) + +#define GETBLENDED15_3268(col1, col2) \ + ((((col1&0x7c1f)*5+(col2&0x7c1f)*11)>>4)&0x7c1f) | \ + ((((col1&0x03e0)*5+(col2&0x03e0)*11)>>4)&0x03e0) + +#define GETBLENDED16_3268(col1, col2) \ + ((((col1&0xf81f)*5+(col2&0xf81f)*11)>>4)&0xf81f) | \ + ((((col1&0x07e0)*5+(col2&0x07e0)*11)>>4)&0x07e0) + +#define GETBLENDED32_3268(col1, col2) \ + ((((col1&0xff00ff)*5+(col2&0xff00ff)*11)>>4)&0xff00ff) | \ + ((((col1&0x00ff00)*5+(col2&0x00ff00)*11)>>4)&0x00ff00) + +#define GETBLENDED15_9406(col1, col2) \ + ((((col1&0x7c1f)*15+(col2&0x7c1f))>>4)&0x7c1f) | \ + ((((col1&0x03e0)*15+(col2&0x03e0))>>4)&0x03e0) + +#define GETBLENDED16_9406(col1, col2) \ + ((((col1&0xf81f)*15+(col2&0xf81f))>>4)&0xf81f) | \ + ((((col1&0x07e0)*15+(col2&0x07e0))>>4)&0x07e0) + +#define GETBLENDED32_9406(col1, col2) \ + ((((col1&0xff00ff)*15+(col2&0xff00ff))>>4)&0xff00ff) | \ + ((((col1&0x00ff00)*15+(col2&0x00ff00))>>4)&0x00ff00) + +#endif diff --git a/src/r_fps.c b/src/r_fps.c new file mode 100644 index 0000000..09b7bc0 --- /dev/null +++ b/src/r_fps.c @@ -0,0 +1,450 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze, Andrey Budko + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Uncapped framerate stuff + * + *--------------------------------------------------------------------- + */ + +#include "doomstat.h" +#include "r_defs.h" +#include "r_state.h" +#include "p_spec.h" +#include "r_demo.h" +#include "r_fps.h" + +int movement_smooth = false; + +typedef enum +{ + INTERP_SectorFloor, + INTERP_SectorCeiling, + INTERP_Vertex, + INTERP_WallPanning, + INTERP_FloorPanning, + INTERP_CeilingPanning +} interpolation_type_e; + +typedef struct +{ + interpolation_type_e type; + void *address; +} interpolation_t; + +static int numinterpolations = 0; + +tic_vars_t tic_vars; + +view_vars_t original_view_vars; + +extern int realtic_clock_rate; +void D_Display(void); + +void R_InitInterpolation(void) +{ + tic_vars.msec = realtic_clock_rate * TICRATE / 100000.0f; +} + +typedef fixed_t fixed2_t[2]; +static fixed2_t *oldipos; +static fixed2_t *bakipos; +static interpolation_t *curipos; + +static boolean NoInterpolateView; +static boolean didInterp; +boolean WasRenderedInTryRunTics; + +void R_InterpolateView (player_t *player, fixed_t frac) +{ + if (movement_smooth) + { + if (NoInterpolateView) + { + NoInterpolateView = false; + original_view_vars.viewx = player->mo->x; + original_view_vars.viewy = player->mo->y; + original_view_vars.viewz = player->viewz; + + original_view_vars.viewangle = player->mo->angle + viewangleoffset; + } + + viewx = original_view_vars.viewx + FixedMul (frac, player->mo->x - original_view_vars.viewx); + viewy = original_view_vars.viewy + FixedMul (frac, player->mo->y - original_view_vars.viewy); + viewz = original_view_vars.viewz + FixedMul (frac, player->viewz - original_view_vars.viewz); + + viewangle = original_view_vars.viewangle + FixedMul (frac, R_SmoothPlaying_Get(player->mo->angle) + viewangleoffset - original_view_vars.viewangle); + } + else + { + viewx = player->mo->x; + viewy = player->mo->y; + viewz = player->viewz; + viewangle = R_SmoothPlaying_Get(player->mo->angle); + } +} + +void R_ResetViewInterpolation () +{ + NoInterpolateView = true; +} + +static void R_CopyInterpToOld (int i) +{ + switch (curipos[i].type) + { + case INTERP_SectorFloor: + oldipos[i][0] = ((sector_t*)curipos[i].address)->floorheight; + break; + case INTERP_SectorCeiling: + oldipos[i][0] = ((sector_t*)curipos[i].address)->ceilingheight; + break; + case INTERP_Vertex: + oldipos[i][0] = ((vertex_t*)curipos[i].address)->x; + oldipos[i][1] = ((vertex_t*)curipos[i].address)->y; + break; + case INTERP_WallPanning: + oldipos[i][0] = ((side_t*)curipos[i].address)->rowoffset; + oldipos[i][1] = ((side_t*)curipos[i].address)->textureoffset; + break; + case INTERP_FloorPanning: + oldipos[i][0] = ((sector_t*)curipos[i].address)->floor_xoffs; + oldipos[i][1] = ((sector_t*)curipos[i].address)->floor_yoffs; + break; + case INTERP_CeilingPanning: + oldipos[i][0] = ((sector_t*)curipos[i].address)->ceiling_xoffs; + oldipos[i][1] = ((sector_t*)curipos[i].address)->ceiling_yoffs; + break; + } +} + +static void R_CopyBakToInterp (int i) +{ + switch (curipos[i].type) + { + case INTERP_SectorFloor: + ((sector_t*)curipos[i].address)->floorheight = bakipos[i][0]; + break; + case INTERP_SectorCeiling: + ((sector_t*)curipos[i].address)->ceilingheight = bakipos[i][0]; + break; + case INTERP_Vertex: + ((vertex_t*)curipos[i].address)->x = bakipos[i][0]; + ((vertex_t*)curipos[i].address)->y = bakipos[i][1]; + break; + case INTERP_WallPanning: + ((side_t*)curipos[i].address)->rowoffset = bakipos[i][0]; + ((side_t*)curipos[i].address)->textureoffset = bakipos[i][1]; + break; + case INTERP_FloorPanning: + ((sector_t*)curipos[i].address)->floor_xoffs = bakipos[i][0]; + ((sector_t*)curipos[i].address)->floor_yoffs = bakipos[i][1]; + break; + case INTERP_CeilingPanning: + ((sector_t*)curipos[i].address)->ceiling_xoffs = bakipos[i][0]; + ((sector_t*)curipos[i].address)->ceiling_yoffs = bakipos[i][1]; + break; + } +} + +static void R_DoAnInterpolation (int i, fixed_t smoothratio) +{ + fixed_t pos; + fixed_t *adr1 = NULL; + fixed_t *adr2 = NULL; + + switch (curipos[i].type) + { + case INTERP_SectorFloor: + adr1 = &((sector_t*)curipos[i].address)->floorheight; + break; + case INTERP_SectorCeiling: + adr1 = &((sector_t*)curipos[i].address)->ceilingheight; + break; + case INTERP_Vertex: + adr1 = &((vertex_t*)curipos[i].address)->x; +//// adr2 = &((vertex_t*)curipos[i].Address)->y; + break; + case INTERP_WallPanning: + adr1 = &((side_t*)curipos[i].address)->rowoffset; + adr2 = &((side_t*)curipos[i].address)->textureoffset; + break; + case INTERP_FloorPanning: + adr1 = &((sector_t*)curipos[i].address)->floor_xoffs; + adr2 = &((sector_t*)curipos[i].address)->floor_yoffs; + break; + case INTERP_CeilingPanning: + adr1 = &((sector_t*)curipos[i].address)->ceiling_xoffs; + adr2 = &((sector_t*)curipos[i].address)->ceiling_yoffs; + break; + + default: + return; + } + + if (adr1) + { + pos = bakipos[i][0] = *adr1; + *adr1 = oldipos[i][0] + FixedMul (pos - oldipos[i][0], smoothratio); + } + + if (adr2) + { + pos = bakipos[i][1] = *adr2; + *adr2 = oldipos[i][1] + FixedMul (pos - oldipos[i][1], smoothratio); + } +} + +void R_UpdateInterpolations() +{ + int i; + if (!movement_smooth) + return; + for (i = numinterpolations-1; i >= 0; --i) + R_CopyInterpToOld (i); +} + +int interpolations_max = 0; + +static void R_SetInterpolation(interpolation_type_e type, void *posptr) +{ + int i; + if (!movement_smooth) + return; + + if (numinterpolations >= interpolations_max) { + interpolations_max = interpolations_max ? interpolations_max * 2 : 256; + + oldipos = (fixed2_t*)realloc(oldipos, sizeof(*oldipos) * interpolations_max); + bakipos = (fixed2_t*)realloc(bakipos, sizeof(*bakipos) * interpolations_max); + curipos = (interpolation_t*)realloc(curipos, sizeof(*curipos) * interpolations_max); + } + + for(i = numinterpolations-1; i >= 0; i--) + if (curipos[i].address == posptr && curipos[i].type == type) + return; + + curipos[numinterpolations].address = posptr; + curipos[numinterpolations].type = type; + R_CopyInterpToOld (numinterpolations); + numinterpolations++; +} + +static void R_StopInterpolation(interpolation_type_e type, void *posptr) +{ + int i; + + if (!movement_smooth) + return; + + for(i=numinterpolations-1; i>= 0; --i) + { + if (curipos[i].address == posptr && curipos[i].type == type) + { + numinterpolations--; + oldipos[i][0] = oldipos[numinterpolations][0]; + oldipos[i][1] = oldipos[numinterpolations][1]; + bakipos[i][0] = bakipos[numinterpolations][0]; + bakipos[i][1] = bakipos[numinterpolations][1]; + curipos[i] = curipos[numinterpolations]; + break; + } + } +} + +void R_StopAllInterpolations(void) +{ + int i; + + if (!movement_smooth) + return; + + for(i=numinterpolations-1; i>= 0; --i) + { + numinterpolations--; + oldipos[i][0] = oldipos[numinterpolations][0]; + oldipos[i][1] = oldipos[numinterpolations][1]; + bakipos[i][0] = bakipos[numinterpolations][0]; + bakipos[i][1] = bakipos[numinterpolations][1]; + curipos[i] = curipos[numinterpolations]; + } +} + +void R_DoInterpolations(fixed_t smoothratio) +{ + int i; + if (!movement_smooth) + return; + + if (smoothratio == FRACUNIT) + { + didInterp = false; + return; + } + + didInterp = true; + + for (i = numinterpolations-1; i >= 0; --i) + { + R_DoAnInterpolation (i, smoothratio); + } +} + +void R_RestoreInterpolations() +{ + int i; + + if (!movement_smooth) + return; + + if (didInterp) + { + didInterp = false; + for (i = numinterpolations-1; i >= 0; --i) + { + R_CopyBakToInterp (i); + } + } +} + +void R_ActivateSectorInterpolations() +{ + int i; + sector_t *sec; + + if (!movement_smooth) + return; + + for (i=0, sec = sectors ; ifloordata) + R_SetInterpolation (INTERP_SectorFloor, sec); + if (sec->ceilingdata) + R_SetInterpolation (INTERP_SectorCeiling, sec); + } +} + +static void R_InterpolationGetData(thinker_t *th, + interpolation_type_e *type1, interpolation_type_e *type2, + void **posptr1, void **posptr2) +{ + *posptr1 = NULL; + *posptr2 = NULL; + + if (th->function == T_MoveFloor) + { + *type1 = INTERP_SectorFloor; + *posptr1 = ((floormove_t *)th)->sector; + } + else + if (th->function == T_PlatRaise) + { + *type1 = INTERP_SectorFloor; + *posptr1 = ((plat_t *)th)->sector; + } + else + if (th->function == T_MoveCeiling) + { + *type1 = INTERP_SectorCeiling; + *posptr1 = ((ceiling_t *)th)->sector; + } + else + if (th->function == T_VerticalDoor) + { + *type1 = INTERP_SectorCeiling; + *posptr1 = ((vldoor_t *)th)->sector; + } + else + if (th->function == T_MoveElevator) + { + *type1 = INTERP_SectorFloor; + *posptr1 = ((elevator_t *)th)->sector; + *type2 = INTERP_SectorCeiling; + *posptr2 = ((elevator_t *)th)->sector; + } + else + if (th->function == T_Scroll) + { + switch (((scroll_t *)th)->type) + { + case sc_side: + *type1 = INTERP_WallPanning; + *posptr1 = sides + ((scroll_t *)th)->affectee; + break; + case sc_floor: + *type1 = INTERP_FloorPanning; + *posptr1 = sectors + ((scroll_t *)th)->affectee; + break; + case sc_ceiling: + *type1 = INTERP_CeilingPanning; + *posptr1 = sectors + ((scroll_t *)th)->affectee; + break; + default: ; + } + } +} + +void R_ActivateThinkerInterpolations(thinker_t *th) +{ + void *posptr1; + void *posptr2; + interpolation_type_e type1, type2; + + if (!movement_smooth) + return; + + R_InterpolationGetData(th, &type1, &type2, &posptr1, &posptr2); + + if(posptr1) + { + R_SetInterpolation (type1, posptr1); + + if(posptr2) + R_SetInterpolation (type2, posptr2); + } +} + +void R_StopInterpolationIfNeeded(thinker_t *th) +{ + void *posptr1; + void *posptr2; + interpolation_type_e type1, type2; + + if (!movement_smooth) + return; + + R_InterpolationGetData(th, &type1, &type2, &posptr1, &posptr2); + + if(posptr1) + { + R_StopInterpolation (type1, posptr1); + if(posptr2) + R_StopInterpolation (type2, posptr2); + } +} + diff --git a/src/r_fps.h b/src/r_fps.h new file mode 100644 index 0000000..bfbeab0 --- /dev/null +++ b/src/r_fps.h @@ -0,0 +1,76 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze, Andrey Budko + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Uncapped framerate stuff + * + *--------------------------------------------------------------------- + */ + +#ifndef __R_FPS__ +#define __R_FPS__ + +#include "doomstat.h" + +extern int movement_smooth; + +typedef struct { + fixed_t viewx; + fixed_t viewy; + fixed_t viewz; + angle_t viewangle; + angle_t viewpitch; +} view_vars_t; + +extern view_vars_t original_view_vars; + +typedef struct { + unsigned int start; + unsigned int next; + unsigned int step; + fixed_t frac; + float msec; +} tic_vars_t; + +extern tic_vars_t tic_vars; + +void R_InitInterpolation(void); +void R_InterpolateView (player_t *player, fixed_t frac); + +extern boolean WasRenderedInTryRunTics; + +void R_ResetViewInterpolation (); +void R_UpdateInterpolations(); +void R_StopAllInterpolations(void); +void R_DoInterpolations(fixed_t smoothratio); +void R_RestoreInterpolations(); +void R_ActivateSectorInterpolations(); +void R_ActivateThinkerInterpolations(thinker_t *th); +void R_StopInterpolationIfNeeded(thinker_t *th); + +#endif diff --git a/src/r_main.c b/src/r_main.c new file mode 100644 index 0000000..cab5997 --- /dev/null +++ b/src/r_main.c @@ -0,0 +1,649 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Rendering main loop and setup functions, + * utility functions (BSP, geometry, trigonometry). + * See tables.c, too. + * + *-----------------------------------------------------------------------------*/ + + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#ifdef USE_SDL +#include "SDL.h" +#endif +#include "doomstat.h" +#include "d_net.h" +#include "w_wad.h" +#include "r_main.h" +#include "r_things.h" +#include "r_plane.h" +#include "r_bsp.h" +#include "r_draw.h" +#include "m_bbox.h" +#include "r_sky.h" +#include "v_video.h" +#include "lprintf.h" +#include "st_stuff.h" +#include "i_main.h" +#include "i_system.h" +#include "g_game.h" +#include "r_demo.h" +#include "r_fps.h" + +// Fineangles in the SCREENWIDTH wide window. +#define FIELDOFVIEW 2048 + +// killough: viewangleoffset is a legacy from the pre-v1.2 days, when Doom +// had Left/Mid/Right viewing. +/-ANG90 offsets were placed here on each +// node, by d_net.c, to set up a L/M/R session. + +int viewangleoffset; +int validcount = 1; // increment every time a check is made +const lighttable_t *fixedcolormap; +int centerx, centery; +fixed_t centerxfrac, centeryfrac; +fixed_t viewheightfrac; //e6y: for correct clipping of things +fixed_t projection; +// proff 11/06/98: Added for high-res +fixed_t projectiony; +fixed_t viewx, viewy, viewz; +angle_t viewangle; +fixed_t viewcos, viewsin; +player_t *viewplayer; +extern lighttable_t **walllights; + +static mobj_t *oviewer; + +// +// precalculated math tables +// + +angle_t clipangle; + +// The viewangletox[viewangle + FINEANGLES/4] lookup +// maps the visible view angles to screen X coordinates, +// flattening the arc to a flat projection plane. +// There will be many angles mapped to the same X. + +int viewangletox[FINEANGLES/2]; + +// The xtoviewangleangle[] table maps a screen pixel +// to the lowest viewangle that maps back to x ranges +// from clipangle to -clipangle. + +angle_t xtoviewangle[MAX_SCREENWIDTH+1]; // killough 2/8/98 + +// killough 3/20/98: Support dynamic colormaps, e.g. deep water +// killough 4/4/98: support dynamic number of them as well + +int numcolormaps; +const lighttable_t *(*c_zlight)[LIGHTLEVELS][MAXLIGHTZ]; +const lighttable_t *(*zlight)[MAXLIGHTZ]; +const lighttable_t *fullcolormap; +const lighttable_t **colormaps; + +// killough 3/20/98, 4/4/98: end dynamic colormaps + +int extralight; // bumped light from gun blasts + +// +// R_PointOnSide +// Traverse BSP (sub) tree, +// check point against partition plane. +// Returns side 0 (front) or 1 (back). +// +// killough 5/2/98: reformatted +// + +PUREFUNC int R_PointOnSide(fixed_t x, fixed_t y, const node_t *node) +{ + if (!node->dx) + return x <= node->x ? node->dy > 0 : node->dy < 0; + + if (!node->dy) + return y <= node->y ? node->dx < 0 : node->dx > 0; + + x -= node->x; + y -= node->y; + + // Try to quickly decide by looking at sign bits. + if ((node->dy ^ node->dx ^ x ^ y) < 0) + return (node->dy ^ x) < 0; // (left is negative) + return FixedMul(y, node->dx>>FRACBITS) >= FixedMul(node->dy>>FRACBITS, x); +} + +// killough 5/2/98: reformatted + +PUREFUNC int R_PointOnSegSide(fixed_t x, fixed_t y, const seg_t *line) +{ + fixed_t lx = line->v1->x; + fixed_t ly = line->v1->y; + fixed_t ldx = line->v2->x - lx; + fixed_t ldy = line->v2->y - ly; + + if (!ldx) + return x <= lx ? ldy > 0 : ldy < 0; + + if (!ldy) + return y <= ly ? ldx < 0 : ldx > 0; + + x -= lx; + y -= ly; + + // Try to quickly decide by looking at sign bits. + if ((ldy ^ ldx ^ x ^ y) < 0) + return (ldy ^ x) < 0; // (left is negative) + return FixedMul(y, ldx>>FRACBITS) >= FixedMul(ldy>>FRACBITS, x); +} + +// +// R_PointToAngle +// To get a global angle from cartesian coordinates, +// the coordinates are flipped until they are in +// the first octant of the coordinate system, then +// the y (<=x) is scaled and divided by x to get a +// tangent (slope) value which is looked up in the +// tantoangle[] table. The +1 size of tantoangle[] +// is to handle the case when x==y without additional +// checking. +// +// killough 5/2/98: reformatted, cleaned up + +#include + +angle_t R_PointToAngle(fixed_t x, fixed_t y) +{ + static fixed_t oldx, oldy; + static angle_t oldresult; + + x -= viewx; y -= viewy; + + if ( /* !render_precise && */ + // e6y: here is where "slime trails" can SOMETIMES occur +#ifdef GL_DOOM + (V_GetMode() != VID_MODEGL) && +#endif + (x < INT_MAX/4 && x > -INT_MAX/4 && y < INT_MAX/4 && y > -INT_MAX/4) + ) + { + // old R_PointToAngle + return (x || y) ? + x >= 0 ? + y >= 0 ? + (x > y) ? tantoangle[SlopeDiv(y,x)] : // octant 0 + ANG90-1-tantoangle[SlopeDiv(x,y)] : // octant 1 + x > (y = -y) ? 0-tantoangle[SlopeDiv(y,x)] : // octant 8 + ANG270+tantoangle[SlopeDiv(x,y)] : // octant 7 + y >= 0 ? (x = -x) > y ? ANG180-1-tantoangle[SlopeDiv(y,x)] : // octant 3 + ANG90 + tantoangle[SlopeDiv(x,y)] : // octant 2 + (x = -x) > (y = -y) ? ANG180+tantoangle[ SlopeDiv(y,x)] : // octant 4 + ANG270-1-tantoangle[SlopeDiv(x,y)] : // octant 5 + 0; + } + + // R_PointToAngleEx merged into R_PointToAngle + // e6y: The precision of the code above is abysmal so use the CRT atan2 function instead! + if (oldx != x || oldy != y) + { + oldx = x; + oldy = y; + oldresult = (int)(atan2(y, x) * ANG180/M_PI); + } + return oldresult; +} + +angle_t R_PointToAngle2(fixed_t viewx, fixed_t viewy, fixed_t x, fixed_t y) +{ + return (y -= viewy, (x -= viewx) || y) ? + x >= 0 ? + y >= 0 ? + (x > y) ? tantoangle[SlopeDiv(y,x)] : // octant 0 + ANG90-1-tantoangle[SlopeDiv(x,y)] : // octant 1 + x > (y = -y) ? 0-tantoangle[SlopeDiv(y,x)] : // octant 8 + ANG270+tantoangle[SlopeDiv(x,y)] : // octant 7 + y >= 0 ? (x = -x) > y ? ANG180-1-tantoangle[SlopeDiv(y,x)] : // octant 3 + ANG90 + tantoangle[SlopeDiv(x,y)] : // octant 2 + (x = -x) > (y = -y) ? ANG180+tantoangle[ SlopeDiv(y,x)] : // octant 4 + ANG270-1-tantoangle[SlopeDiv(x,y)] : // octant 5 + 0; +} + +// +// R_InitTextureMapping +// +// killough 5/2/98: reformatted + +static void R_InitTextureMapping (void) +{ + register int i,x; + fixed_t focallength; + + // Use tangent table to generate viewangletox: + // viewangletox will give the next greatest x + // after the view angle. + // + // Calc focallength + // so FIELDOFVIEW angles covers SCREENWIDTH. + + focallength = FixedDiv(centerxfrac, finetangent[FINEANGLES/4+FIELDOFVIEW/2]); + + for (i=0 ; i FRACUNIT*2) + t = -1; + else + if (finetangent[i] < -FRACUNIT*2) + t = viewwidth+1; + else + { + t = FixedMul(finetangent[i], focallength); + t = (centerxfrac - t + FRACUNIT-1) >> FRACBITS; + if (t < -1) + t = -1; + else + if (t > viewwidth+1) + t = viewwidth+1; + } + viewangletox[i] = t; + } + + // Scan viewangletox[] to generate xtoviewangle[]: + // xtoviewangle will give the smallest view angle + // that maps to x. + + for (x=0; x<=viewwidth; x++) + { + for (i=0; viewangletox[i] > x; i++) + ; + xtoviewangle[x] = (i<>= LIGHTSCALESHIFT)/DISTMAP; + + if (level < 0) + level = 0; + else + if (level >= NUMCOLORMAPS) + level = NUMCOLORMAPS-1; + + // killough 3/20/98: Initialize multiple colormaps + level *= 256; + for (t=0; t>ANGLETOFINESHIFT]); + distscale[i] = FixedDiv(FRACUNIT,cosadj); + } + +} + +// +// R_Init +// + +extern int screenblocks; + +void R_Init (void) +{ + // CPhipps - R_DrawColumn isn't constant anymore, so must + // initialise in code + // current column draw function + lprintf(LO_INFO, "\nR_LoadTrigTables: "); + R_LoadTrigTables(); + lprintf(LO_INFO, "\nR_InitData: "); + R_InitData(); + R_SetViewSize(screenblocks); + lprintf(LO_INFO, "\nR_Init: R_InitPlanes "); + R_InitPlanes(); + lprintf(LO_INFO, "R_InitLightTables "); + R_InitLightTables(); + lprintf(LO_INFO, "R_InitSkyMap "); + R_InitSkyMap(); + lprintf(LO_INFO, "R_InitTranslationsTables "); + R_InitTranslationTables(); + lprintf(LO_INFO, "R_InitPatches "); + R_InitPatches(); +} + +// +// R_PointInSubsector +// +// killough 5/2/98: reformatted, cleaned up + +subsector_t *R_PointInSubsector(fixed_t x, fixed_t y) +{ + int nodenum = numnodes-1; + + // special case for trivial maps (single subsector, no nodes) + if (numnodes == 0) + return subsectors; + + while (!(nodenum & NF_SUBSECTOR)) + nodenum = nodes[nodenum].children[R_PointOnSide(x, y, nodes+nodenum)]; + return &subsectors[nodenum & ~NF_SUBSECTOR]; +} + +// +// R_SetupFrame +// + +static void R_SetupFrame (player_t *player) +{ + int cm; + boolean NoInterpolate = paused || (menuactive && !demoplayback); + + viewplayer = player; + + if (player->mo != oviewer || NoInterpolate) + { + R_ResetViewInterpolation (); + oviewer = player->mo; + } + tic_vars.frac = I_GetTimeFrac (); + if (NoInterpolate) + tic_vars.frac = FRACUNIT; + R_InterpolateView (player, tic_vars.frac); + + extralight = player->extralight; + + viewsin = finesine[viewangle>>ANGLETOFINESHIFT]; + viewcos = finecosine[viewangle>>ANGLETOFINESHIFT]; + + R_DoInterpolations(tic_vars.frac); + + // killough 3/20/98, 4/4/98: select colormap based on player status + + if (player->mo->subsector->sector->heightsec != -1) + { + const sector_t *s = player->mo->subsector->sector->heightsec + sectors; + cm = viewz < s->floorheight ? s->bottommap : viewz > s->ceilingheight ? + s->topmap : s->midmap; + if (cm < 0 || cm > numcolormaps) + cm = 0; + } + else + cm = 0; + + fullcolormap = colormaps[cm]; + zlight = c_zlight[cm]; + + if (player->fixedcolormap) + { + fixedcolormap = fullcolormap // killough 3/20/98: use fullcolormap + + player->fixedcolormap*256*sizeof(lighttable_t); + } + else + fixedcolormap = 0; + + validcount++; +} + +int autodetect_hom = 0; // killough 2/7/98: HOM autodetection flag + +// +// R_ShowStats +// +int rendered_visplanes, rendered_segs, rendered_vissprites; +boolean rendering_stats; + +static void R_ShowStats(void) +{ +//e6y +#if USE_SDL + static unsigned int FPS_SavedTick = 0, FPS_FrameCount = 0; + unsigned int tick = SDL_GetTicks(); + FPS_FrameCount++; + if(tick >= FPS_SavedTick + 1000) + { + doom_printf((V_GetMode() == VID_MODEGL) + ?"Frame rate %d fps\nWalls %d, Flats %d, Sprites %d" + :"Frame rate %d fps\nSegs %d, Visplanes %d, Sprites %d", + 1000 * FPS_FrameCount / (tick - FPS_SavedTick), rendered_segs, + rendered_visplanes, rendered_vissprites); + FPS_SavedTick = tick; + FPS_FrameCount = 0; + } +#else + +#define KEEPTIMES 10 + static int keeptime[KEEPTIMES], showtime; + int now = I_GetTime(); + + if (now - showtime > 35) { + doom_printf((V_GetMode() == VID_MODEGL) + ?"Frame rate %d fps\nWalls %d, Flats %d, Sprites %d" + :"Frame rate %d fps\nSegs %d, Visplanes %d, Sprites %d", + (35*KEEPTIMES)/(now - keeptime[0]), rendered_segs, + rendered_visplanes, rendered_vissprites); + showtime = now; + } + memmove(keeptime, keeptime+1, sizeof(keeptime[0]) * (KEEPTIMES-1)); + keeptime[KEEPTIMES-1] = now; + +#endif //e6y +} + +// +// R_RenderView +// +void R_RenderPlayerView (player_t* player) +{ + R_SetupFrame (player); + + // Clear buffers. + R_ClearClipSegs (); + R_ClearDrawSegs (); + R_ClearPlanes (); + R_ClearSprites (); + + rendered_segs = rendered_visplanes = 0; + if (V_GetMode() == VID_MODEGL) + { +#ifdef GL_DOOM + // proff 11/99: clear buffers + gld_InitDrawScene(); + // proff 11/99: switch to perspective mode + gld_StartDrawScene(); +#endif + } else { + if (autodetect_hom) + { // killough 2/10/98: add flashing red HOM indicators + unsigned char color=(gametic % 20) < 9 ? 0xb0 : 0; + V_FillRect(0, viewwindowx, viewwindowy, viewwidth, viewheight, color); + R_DrawViewBorder(); + } + } + + // check for new console commands. +#ifdef HAVE_NET + NetUpdate (); +#endif + + // The head node is the last node output. + R_RenderBSPNode (numnodes-1); + R_ResetColumnBuffer(); + + // Check for new console commands. +#ifdef HAVE_NET + NetUpdate (); +#endif + + if (V_GetMode() != VID_MODEGL) + R_DrawPlanes (); + + // Check for new console commands. +#ifdef HAVE_NET + NetUpdate (); +#endif + + if (V_GetMode() != VID_MODEGL) { + R_DrawMasked (); + R_ResetColumnBuffer(); + } + + // Check for new console commands. +#ifdef HAVE_NET + NetUpdate (); +#endif + + if (V_GetMode() == VID_MODEGL) { +#ifdef GL_DOOM + // proff 11/99: draw the scene + gld_DrawScene(player); + // proff 11/99: finishing off + gld_EndDrawScene(); +#endif + } + + if (rendering_stats) R_ShowStats(); + + R_RestoreInterpolations(); +} diff --git a/src/r_main.h b/src/r_main.h new file mode 100644 index 0000000..89a36ee --- /dev/null +++ b/src/r_main.h @@ -0,0 +1,120 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Renderer main interface. + * + *-----------------------------------------------------------------------------*/ + +#ifndef __R_MAIN__ +#define __R_MAIN__ + +#include "d_player.h" +#include "r_data.h" + +#ifdef __GNUG__ +#pragma interface +#endif + +// +// POV related. +// + +extern fixed_t viewcos; +extern fixed_t viewsin; +extern int viewwidth; +extern int viewheight; +extern int viewwindowx; +extern int viewwindowy; +extern int centerx; +extern int centery; +extern fixed_t centerxfrac; +extern fixed_t centeryfrac; +extern fixed_t viewheightfrac; //e6y: for correct clipping of things +extern fixed_t projection; +// proff 11/06/98: Added for high-res +extern fixed_t projectiony; +extern int validcount; + +// +// Rendering stats +// + +extern int rendered_visplanes, rendered_segs, rendered_vissprites; +extern boolean rendering_stats; + +// +// Lighting LUT. +// Used for z-depth cuing per column/row, +// and other lighting effects (sector ambient, flash). +// + +// Lighting constants. + +#define LIGHTLEVELS 16 +#define LIGHTSEGSHIFT 4 +#define MAXLIGHTSCALE 48 +#define LIGHTSCALESHIFT 12 +#define MAXLIGHTZ 128 +#define LIGHTZSHIFT 20 + +// killough 3/20/98: Allow colormaps to be dynamic (e.g. underwater) +extern const lighttable_t *(*zlight)[MAXLIGHTZ]; +extern const lighttable_t *fullcolormap; +extern int numcolormaps; // killough 4/4/98: dynamic number of maps +extern const lighttable_t **colormaps; +// killough 3/20/98, 4/4/98: end dynamic colormaps + +extern int extralight; +extern const lighttable_t *fixedcolormap; + +// Number of diminishing brightness levels. +// There a 0-31, i.e. 32 LUT in the COLORMAP lump. + +#define NUMCOLORMAPS 32 + +// +// Utility functions. +// + +PUREFUNC int R_PointOnSide(fixed_t x, fixed_t y, const node_t *node); +PUREFUNC int R_PointOnSegSide(fixed_t x, fixed_t y, const seg_t *line); +angle_t R_PointToAngle(fixed_t x, fixed_t y); +angle_t R_PointToAngle2(fixed_t x1, fixed_t y1, fixed_t x2, fixed_t y2); +subsector_t *R_PointInSubsector(fixed_t x, fixed_t y); + +// +// REFRESH - the actual rendering functions. +// + +void R_RenderPlayerView(player_t *player); // Called by G_Drawer. +void R_Init(void); // Called by startup code. +void R_SetViewSize(int blocks); // Called by M_Responder. +void R_ExecuteSetViewSize(void); // cph - called by D_Display to complete a view resize + +#endif diff --git a/src/r_patch.c b/src/r_patch.c new file mode 100644 index 0000000..e466fb2 --- /dev/null +++ b/src/r_patch.c @@ -0,0 +1,786 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2002 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + *-----------------------------------------------------------------------------*/ + +#include "z_zone.h" +#include "doomstat.h" +#include "w_wad.h" +#include "r_main.h" +#include "r_sky.h" +#include "r_bsp.h" +#include "r_things.h" +#include "p_tick.h" +#include "i_system.h" +#include "r_draw.h" +#include "lprintf.h" +#include "r_patch.h" +#include + +// posts are runs of non masked source pixels +typedef struct +{ + byte topdelta; // -1 is the last post in a column + byte length; // length data bytes follows +} post_t; + +// column_t is a list of 0 or more post_t, (byte)-1 terminated +typedef post_t column_t; + +// +// Patches. +// A patch holds one or more columns. +// Patches are used for sprites and all masked pictures, +// and we compose textures from the TEXTURE1/2 lists +// of patches. +// + +typedef struct +{ + short width, height; // bounding box size + short leftoffset; // pixels to the left of origin + short topoffset; // pixels below the origin + int columnofs[8]; // only [width] used +} patch_t; + +//--------------------------------------------------------------------------- +// Re-engineered patch support +//--------------------------------------------------------------------------- +static rpatch_t *patches = 0; + +static rpatch_t *texture_composites = 0; + +//--------------------------------------------------------------------------- +void R_InitPatches(void) { + if (!patches) + { + patches = (rpatch_t*)malloc(numlumps * sizeof(rpatch_t)); + // clear out new patches to signal they're uninitialized + memset(patches, 0, sizeof(rpatch_t)*numlumps); + } + if (!texture_composites) + { + texture_composites = (rpatch_t*)malloc(numtextures * sizeof(rpatch_t)); + // clear out new patches to signal they're uninitialized + memset(texture_composites, 0, sizeof(rpatch_t)*numtextures); + } +} + +//--------------------------------------------------------------------------- +void R_FlushAllPatches(void) { + int i; + + if (patches) + { + for (i=0; i < numlumps; i++) + if (patches[i].locks > 0) + I_Error("R_FlushAllPatches: patch number %i still locked",i); + free(patches); + patches = NULL; + } + if (texture_composites) + { + for (i=0; iwidth; + R_UnlockPatchNum(lump); + return width; +} + +//--------------------------------------------------------------------------- +int R_NumPatchHeight(int lump) +{ + const rpatch_t *patch = R_CachePatchNum(lump); + int height = patch->height; + R_UnlockPatchNum(lump); + return height; +} + +//--------------------------------------------------------------------------- +static int getPatchIsNotTileable(const patch_t *patch) { + int x=0, numPosts, lastColumnDelta = 0; + const column_t *column; + int cornerCount = 0; + int hasAHole = 0; + + for (x=0; xwidth); x++) { + column = (const column_t *)((const byte *)patch + LONG(patch->columnofs[x])); + if (!x) lastColumnDelta = column->topdelta; + else if (lastColumnDelta != column->topdelta) hasAHole = 1; + + numPosts = 0; + while (column->topdelta != 0xff) { + // check to see if a corner pixel filled + if (x == 0 && column->topdelta == 0) cornerCount++; + else if (x == 0 && column->topdelta + column->length >= SHORT(patch->height)) cornerCount++; + else if (x == SHORT(patch->width)-1 && column->topdelta == 0) cornerCount++; + else if (x == SHORT(patch->width)-1 && column->topdelta + column->length >= SHORT(patch->height)) cornerCount++; + + if (numPosts++) hasAHole = 1; + column = (const column_t *)((const byte *)column + column->length + 4); + } + } + + if (cornerCount == 4) return 0; + return hasAHole; +} + +//--------------------------------------------------------------------------- +static int getIsSolidAtSpot(const column_t *column, int spot) { + if (!column) return 0; + while (column->topdelta != 0xff) { + if (spot < column->topdelta) return 0; + if ((spot >= column->topdelta) && (spot <= column->topdelta + column->length)) return 1; + column = (const column_t*)((const byte*)column + 3 + column->length + 1); + } + return 0; +} + +//--------------------------------------------------------------------------- +// Used to determine whether a column edge (top or bottom) should slope +// up or down for smoothed masked edges - POPE +//--------------------------------------------------------------------------- +static int getColumnEdgeSlope(const column_t *prevcolumn, const column_t *nextcolumn, int spot) { + int holeToLeft = !getIsSolidAtSpot(prevcolumn, spot); + int holeToRight = !getIsSolidAtSpot(nextcolumn, spot); + + if (holeToLeft && !holeToRight) return 1; + if (!holeToLeft && holeToRight) return -1; + return 0; +} + +//--------------------------------------------------------------------------- +static void createPatch(int id) { + rpatch_t *patch; + const int patchNum = id; + const patch_t *oldPatch = (const patch_t*)W_CacheLumpNum(patchNum); + const column_t *oldColumn, *oldPrevColumn, *oldNextColumn; + int x, y; + int pixelDataSize; + int columnsDataSize; + int postsDataSize; + int dataSize; + int *numPostsInColumn; + int numPostsTotal; + const unsigned char *oldColumnPixelData; + int numPostsUsedSoFar; + int edgeSlope; + +#ifdef RANGECHECK + if (id >= numlumps) + I_Error("createPatch: %i >= numlumps", id); +#endif + + patch = &patches[id]; + // proff - 2003-02-16 What about endianess? + patch->width = SHORT(oldPatch->width); + patch->widthmask = 0; + patch->height = SHORT(oldPatch->height); + patch->leftoffset = SHORT(oldPatch->leftoffset); + patch->topoffset = SHORT(oldPatch->topoffset); + patch->isNotTileable = getPatchIsNotTileable(oldPatch); + + // work out how much memory we need to allocate for this patch's data + pixelDataSize = (patch->width * patch->height + 4) & ~3; + columnsDataSize = sizeof(rcolumn_t) * patch->width; + + // count the number of posts in each column + numPostsInColumn = (int*)malloc(sizeof(int) * patch->width); + numPostsTotal = 0; + + for (x=0; xwidth; x++) { + oldColumn = (const column_t *)((const byte *)oldPatch + LONG(oldPatch->columnofs[x])); + numPostsInColumn[x] = 0; + while (oldColumn->topdelta != 0xff) { + numPostsInColumn[x]++; + numPostsTotal++; + oldColumn = (const column_t *)((const byte *)oldColumn + oldColumn->length + 4); + } + } + + postsDataSize = numPostsTotal * sizeof(rpost_t); + + // allocate our data chunk + dataSize = pixelDataSize + columnsDataSize + postsDataSize; + patch->data = (unsigned char*)Z_Malloc(dataSize, PU_CACHE, (void **)&patch->data); + memset(patch->data, 0, dataSize); + + // set out pixel, column, and post pointers into our data array + patch->pixels = patch->data; + patch->columns = (rcolumn_t*)((unsigned char*)patch->pixels + pixelDataSize); + patch->posts = (rpost_t*)((unsigned char*)patch->columns + columnsDataSize); + + // sanity check that we've got all the memory allocated we need + assert((((byte*)patch->posts + numPostsTotal*sizeof(rpost_t)) - (byte*)patch->data) == dataSize); + + memset(patch->pixels, 0xff, (patch->width*patch->height)); + + // fill in the pixels, posts, and columns + numPostsUsedSoFar = 0; + for (x=0; xwidth; x++) { + + oldColumn = (const column_t *)((const byte *)oldPatch + LONG(oldPatch->columnofs[x])); + + if (patch->isNotTileable) { + // non-tiling + if (x == 0) oldPrevColumn = 0; + else oldPrevColumn = (const column_t *)((const byte *)oldPatch + LONG(oldPatch->columnofs[x-1])); + if (x == patch->width-1) oldNextColumn = 0; + else oldNextColumn = (const column_t *)((const byte *)oldPatch + LONG(oldPatch->columnofs[x+1])); + } + else { + // tiling + int prevColumnIndex = x-1; + int nextColumnIndex = x+1; + while (prevColumnIndex < 0) prevColumnIndex += patch->width; + while (nextColumnIndex >= patch->width) nextColumnIndex -= patch->width; + oldPrevColumn = (const column_t *)((const byte *)oldPatch + LONG(oldPatch->columnofs[prevColumnIndex])); + oldNextColumn = (const column_t *)((const byte *)oldPatch + LONG(oldPatch->columnofs[nextColumnIndex])); + } + + // setup the column's data + patch->columns[x].pixels = patch->pixels + (x*patch->height) + 0; + patch->columns[x].numPosts = numPostsInColumn[x]; + patch->columns[x].posts = patch->posts + numPostsUsedSoFar; + + while (oldColumn->topdelta != 0xff) { + // set up the post's data + patch->posts[numPostsUsedSoFar].topdelta = oldColumn->topdelta; + patch->posts[numPostsUsedSoFar].length = oldColumn->length; + patch->posts[numPostsUsedSoFar].slope = 0; + + edgeSlope = getColumnEdgeSlope(oldPrevColumn, oldNextColumn, oldColumn->topdelta); + if (edgeSlope == 1) patch->posts[numPostsUsedSoFar].slope |= RDRAW_EDGESLOPE_TOP_UP; + else if (edgeSlope == -1) patch->posts[numPostsUsedSoFar].slope |= RDRAW_EDGESLOPE_TOP_DOWN; + + edgeSlope = getColumnEdgeSlope(oldPrevColumn, oldNextColumn, oldColumn->topdelta+oldColumn->length); + if (edgeSlope == 1) patch->posts[numPostsUsedSoFar].slope |= RDRAW_EDGESLOPE_BOT_UP; + else if (edgeSlope == -1) patch->posts[numPostsUsedSoFar].slope |= RDRAW_EDGESLOPE_BOT_DOWN; + + // fill in the post's pixels + oldColumnPixelData = (const byte *)oldColumn + 3; + for (y=0; ylength; y++) { + patch->pixels[x * patch->height + oldColumn->topdelta + y] = oldColumnPixelData[y]; + } + + oldColumn = (const column_t *)((const byte *)oldColumn + oldColumn->length + 4); + numPostsUsedSoFar++; + } + } + + if (1 || patch->isNotTileable) { + const rcolumn_t *column, *prevColumn; + + // copy the patch image down and to the right where there are + // holes to eliminate the black halo from bilinear filtering + for (x=0; xwidth; x++) { + //oldColumn = (const column_t *)((const byte *)oldPatch + oldPatch->columnofs[x]); + + column = R_GetPatchColumnClamped(patch, x); + prevColumn = R_GetPatchColumnClamped(patch, x-1); + + if (column->pixels[0] == 0xff) { + // force the first pixel (which is a hole), to use + // the color from the next solid spot in the column + for (y=0; yheight; y++) { + if (column->pixels[y] != 0xff) { + column->pixels[0] = column->pixels[y]; + break; + } + } + } + + // copy from above or to the left + for (y=1; yheight; y++) { + //if (getIsSolidAtSpot(oldColumn, y)) continue; + if (column->pixels[y] != 0xff) continue; + + // this pixel is a hole + + if (x && prevColumn->pixels[y-1] != 0xff) { + // copy the color from the left + column->pixels[y] = prevColumn->pixels[y]; + } + else { + // copy the color from above + column->pixels[y] = column->pixels[y-1]; + } + } + } + + // verify that the patch truly is non-rectangular since + // this determines tiling later on + } + + W_UnlockLumpNum(patchNum); + free(numPostsInColumn); +} + +typedef struct { + unsigned short patches; + unsigned short posts; + unsigned short posts_used; +} count_t; + +static void switchPosts(rpost_t *post1, rpost_t *post2) { + rpost_t dummy; + + dummy.topdelta = post1->topdelta; + dummy.length = post1->length; + dummy.slope = post1->slope; + post1->topdelta = post2->topdelta; + post1->length = post2->length; + post1->slope = post2->slope; + post2->topdelta = dummy.topdelta; + post2->length = dummy.length; + post2->slope = dummy.slope; +} + +static void removePostFromColumn(rcolumn_t *column, int post) { + int i; +#ifdef RANGECHECK + if (post >= column->numPosts) + I_Error("removePostFromColumn: invalid post index"); +#endif + if (post < column->numPosts) + for (i=post; i<(column->numPosts-1); i++) { + rpost_t *post1 = &column->posts[i]; + rpost_t *post2 = &column->posts[i+1]; + post1->topdelta = post2->topdelta; + post1->length = post2->length; + post1->slope = post2->slope; + } + column->numPosts--; +} + +//--------------------------------------------------------------------------- +static void createTextureCompositePatch(int id) { + rpatch_t *composite_patch; + texture_t *texture; + texpatch_t *texpatch; + int patchNum; + const patch_t *oldPatch; + const column_t *oldColumn, *oldPrevColumn, *oldNextColumn; + int i, x, y; + int oy, count; + int pixelDataSize; + int columnsDataSize; + int postsDataSize; + int dataSize; + int numPostsTotal; + const unsigned char *oldColumnPixelData; + int numPostsUsedSoFar; + int edgeSlope; + count_t *countsInColumn; + +#ifdef RANGECHECK + if (id >= numtextures) + I_Error("createTextureCompositePatch: %i >= numtextures", id); +#endif + + composite_patch = &texture_composites[id]; + + texture = textures[id]; + + composite_patch->width = texture->width; + composite_patch->height = texture->height; + composite_patch->widthmask = texture->widthmask; + composite_patch->leftoffset = 0; + composite_patch->topoffset = 0; + composite_patch->isNotTileable = 0; + + // work out how much memory we need to allocate for this patch's data + pixelDataSize = (composite_patch->width * composite_patch->height + 4) & ~3; + columnsDataSize = sizeof(rcolumn_t) * composite_patch->width; + + // count the number of posts in each column + countsInColumn = (count_t *)calloc(sizeof(count_t), composite_patch->width); + numPostsTotal = 0; + + for (i=0; ipatchcount; i++) { + texpatch = &texture->patches[i]; + patchNum = texpatch->patch; + oldPatch = (const patch_t*)W_CacheLumpNum(patchNum); + + for (x=0; xwidth); x++) { + int tx = texpatch->originx + x; + + if (tx < 0) + continue; + if (tx >= composite_patch->width) + break; + + countsInColumn[tx].patches++; + + oldColumn = (const column_t *)((const byte *)oldPatch + LONG(oldPatch->columnofs[x])); + while (oldColumn->topdelta != 0xff) { + countsInColumn[tx].posts++; + numPostsTotal++; + oldColumn = (const column_t *)((const byte *)oldColumn + oldColumn->length + 4); + } + } + + W_UnlockLumpNum(patchNum); + } + + postsDataSize = numPostsTotal * sizeof(rpost_t); + + // allocate our data chunk + dataSize = pixelDataSize + columnsDataSize + postsDataSize; + composite_patch->data = (unsigned char*)Z_Malloc(dataSize, PU_STATIC, (void **)&composite_patch->data); + memset(composite_patch->data, 0, dataSize); + + // set out pixel, column, and post pointers into our data array + composite_patch->pixels = composite_patch->data; + composite_patch->columns = (rcolumn_t*)((unsigned char*)composite_patch->pixels + pixelDataSize); + composite_patch->posts = (rpost_t*)((unsigned char*)composite_patch->columns + columnsDataSize); + + // sanity check that we've got all the memory allocated we need + assert((((byte*)composite_patch->posts + numPostsTotal*sizeof(rpost_t)) - (byte*)composite_patch->data) == dataSize); + + memset(composite_patch->pixels, 0xff, (composite_patch->width*composite_patch->height)); + + numPostsUsedSoFar = 0; + + for (x=0; xwidth; x++) { + // setup the column's data + composite_patch->columns[x].pixels = composite_patch->pixels + (x*composite_patch->height); + composite_patch->columns[x].numPosts = countsInColumn[x].posts; + composite_patch->columns[x].posts = composite_patch->posts + numPostsUsedSoFar; + numPostsUsedSoFar += countsInColumn[x].posts; + } + + // fill in the pixels, posts, and columns + for (i=0; ipatchcount; i++) { + texpatch = &texture->patches[i]; + patchNum = texpatch->patch; + oldPatch = (const patch_t*)W_CacheLumpNum(patchNum); + + for (x=0; xwidth); x++) { + int tx = texpatch->originx + x; + + if (tx < 0) + continue; + if (tx >= composite_patch->width) + break; + + oldColumn = (const column_t *)((const byte *)oldPatch + LONG(oldPatch->columnofs[x])); + + { + // tiling + int prevColumnIndex = x-1; + int nextColumnIndex = x+1; + while (prevColumnIndex < 0) prevColumnIndex += SHORT(oldPatch->width); + while (nextColumnIndex >= SHORT(oldPatch->width)) nextColumnIndex -= SHORT(oldPatch->width); + oldPrevColumn = (const column_t *)((const byte *)oldPatch + LONG(oldPatch->columnofs[prevColumnIndex])); + oldNextColumn = (const column_t *)((const byte *)oldPatch + LONG(oldPatch->columnofs[nextColumnIndex])); + } + + while (oldColumn->topdelta != 0xff) { + rpost_t *post = &composite_patch->columns[tx].posts[countsInColumn[tx].posts_used]; + oldColumnPixelData = (const byte *)oldColumn + 3; + oy = texpatch->originy; + count = oldColumn->length; + // the original renderer had several bugs which we reproduce here + if (countsInColumn[tx].patches > 1) { + // when there are multiple patches, then we need to handle the + // column differently + if (i == 0) { + // draw first patch at original position, it will be partly + // overdrawn below + for (y=0; ytopdelta + y; + if (ty < 0) + continue; + if (ty >= composite_patch->height) + break; + composite_patch->pixels[tx * composite_patch->height + ty] = oldColumnPixelData[y]; + } + } + // do the buggy clipping + if ((oy + oldColumn->topdelta) < 0) { + count += oy; + oy = 0; + } + } else { + // with a single patch only negative y origins are wrong + oy = 0; + } + // set up the post's data + post->topdelta = oldColumn->topdelta + oy; + post->length = count; + if ((post->topdelta + post->length) > composite_patch->height) { + if (post->topdelta > composite_patch->height) + post->length = 0; + else + post->length = composite_patch->height - post->topdelta; + } + if (post->topdelta < 0) { + if ((post->topdelta + post->length) <= 0) + post->length = 0; + else + post->length -= post->topdelta; + post->topdelta = 0; + } + post->slope = 0; + + edgeSlope = getColumnEdgeSlope(oldPrevColumn, oldNextColumn, oldColumn->topdelta); + if (edgeSlope == 1) post->slope |= RDRAW_EDGESLOPE_TOP_UP; + else if (edgeSlope == -1) post->slope |= RDRAW_EDGESLOPE_TOP_DOWN; + + edgeSlope = getColumnEdgeSlope(oldPrevColumn, oldNextColumn, oldColumn->topdelta+count); + if (edgeSlope == 1) post->slope |= RDRAW_EDGESLOPE_BOT_UP; + else if (edgeSlope == -1) post->slope |= RDRAW_EDGESLOPE_BOT_DOWN; + + // fill in the post's pixels + for (y=0; ytopdelta + y; + if (ty < 0) + continue; + if (ty >= composite_patch->height) + break; + composite_patch->pixels[tx * composite_patch->height + ty] = oldColumnPixelData[y]; + } + + oldColumn = (const column_t *)((const byte *)oldColumn + oldColumn->length + 4); + countsInColumn[tx].posts_used++; + assert(countsInColumn[tx].posts_used <= countsInColumn[tx].posts); + } + } + + W_UnlockLumpNum(patchNum); + } + + for (x=0; xwidth; x++) { + rcolumn_t *column; + + if (countsInColumn[x].patches <= 1) + continue; + + // cleanup posts on multipatch columns + column = &composite_patch->columns[x]; + + i = 0; + while (i<(column->numPosts-1)) { + rpost_t *post1 = &column->posts[i]; + rpost_t *post2 = &column->posts[i+1]; + int length; + + if ((post2->topdelta - post1->topdelta) < 0) + switchPosts(post1, post2); + + if ((post1->topdelta + post1->length) >= post2->topdelta) { + length = (post1->length + post2->length) - ((post1->topdelta + post1->length) - post2->topdelta); + if (post1->length < length) { + post1->slope = post2->slope; + post1->length = length; + } + removePostFromColumn(column, i+1); + i = 0; + continue; + } + i++; + } + } + + if (1 || composite_patch->isNotTileable) { + const rcolumn_t *column, *prevColumn; + + // copy the patch image down and to the right where there are + // holes to eliminate the black halo from bilinear filtering + for (x=0; xwidth; x++) { + //oldColumn = (const column_t *)((const byte *)oldPatch + oldPatch->columnofs[x]); + + column = R_GetPatchColumnClamped(composite_patch, x); + prevColumn = R_GetPatchColumnClamped(composite_patch, x-1); + + if (column->pixels[0] == 0xff) { + // force the first pixel (which is a hole), to use + // the color from the next solid spot in the column + for (y=0; yheight; y++) { + if (column->pixels[y] != 0xff) { + column->pixels[0] = column->pixels[y]; + break; + } + } + } + + // copy from above or to the left + for (y=1; yheight; y++) { + //if (getIsSolidAtSpot(oldColumn, y)) continue; + if (column->pixels[y] != 0xff) continue; + + // this pixel is a hole + + if (x && prevColumn->pixels[y-1] != 0xff) { + // copy the color from the left + column->pixels[y] = prevColumn->pixels[y]; + } + else { + // copy the color from above + column->pixels[y] = column->pixels[y-1]; + } + } + } + + // verify that the patch truly is non-rectangular since + // this determines tiling later on + } + + free(countsInColumn); +} + +//--------------------------------------------------------------------------- +const rpatch_t *R_CachePatchNum(int id) { + const int locks = 1; + + if (!patches) + I_Error("R_CachePatchNum: Patches not initialized"); + +#ifdef RANGECHECK + if (id >= numlumps) + I_Error("createPatch: %i >= numlumps", id); +#endif + + if (!patches[id].data) + createPatch(id); + + /* cph - if wasn't locked but now is, tell z_zone to hold it */ + if (!patches[id].locks && locks) { + Z_ChangeTag(patches[id].data,PU_STATIC); +#ifdef TIMEDIAG + patches[id].locktic = gametic; +#endif + } + patches[id].locks += locks; + +#ifdef SIMPLECHECKS + if (!((patches[id].locks+1) & 0xf)) + lprintf(LO_DEBUG, "R_CachePatchNum: High lock on %8s (%d)\n", + lumpinfo[id].name, patches[id].locks); +#endif + + return &patches[id]; +} + +void R_UnlockPatchNum(int id) +{ + const int unlocks = 1; +#ifdef SIMPLECHECKS + if ((signed short)patches[id].locks < unlocks) + lprintf(LO_DEBUG, "R_UnlockPatchNum: Excess unlocks on %8s (%d-%d)\n", + lumpinfo[id].name, patches[id].locks, unlocks); +#endif + patches[id].locks -= unlocks; + /* cph - Note: must only tell z_zone to make purgeable if currently locked, + * else it might already have been purged + */ + if (unlocks && !patches[id].locks) + Z_ChangeTag(patches[id].data, PU_CACHE); +} + +//--------------------------------------------------------------------------- +const rpatch_t *R_CacheTextureCompositePatchNum(int id) { + const int locks = 1; + + if (!texture_composites) + I_Error("R_CacheTextureCompositePatchNum: Composite patches not initialized"); + +#ifdef RANGECHECK + if (id >= numtextures) + I_Error("createTextureCompositePatch: %i >= numtextures", id); +#endif + + if (!texture_composites[id].data) + createTextureCompositePatch(id); + + /* cph - if wasn't locked but now is, tell z_zone to hold it */ + if (!texture_composites[id].locks && locks) { + Z_ChangeTag(texture_composites[id].data,PU_STATIC); +#ifdef TIMEDIAG + texture_composites[id].locktic = gametic; +#endif + } + texture_composites[id].locks += locks; + +#ifdef SIMPLECHECKS + if (!((texture_composites[id].locks+1) & 0xf)) + lprintf(LO_DEBUG, "R_CacheTextureCompositePatchNum: High lock on %8s (%d)\n", + textures[id]->name, texture_composites[id].locks); +#endif + + return &texture_composites[id]; + +} + +void R_UnlockTextureCompositePatchNum(int id) +{ + const int unlocks = 1; +#ifdef SIMPLECHECKS + if ((signed short)texture_composites[id].locks < unlocks) + lprintf(LO_DEBUG, "R_UnlockTextureCompositePatchNum: Excess unlocks on %8s (%d-%d)\n", + textures[id]->name, texture_composites[id].locks, unlocks); +#endif + texture_composites[id].locks -= unlocks; + /* cph - Note: must only tell z_zone to make purgeable if currently locked, + * else it might already have been purged + */ + if (unlocks && !texture_composites[id].locks) + Z_ChangeTag(texture_composites[id].data, PU_CACHE); +} + +//--------------------------------------------------------------------------- +const rcolumn_t *R_GetPatchColumnWrapped(const rpatch_t *patch, int columnIndex) { + while (columnIndex < 0) columnIndex += patch->width; + columnIndex %= patch->width; + return &patch->columns[columnIndex]; +} + +//--------------------------------------------------------------------------- +const rcolumn_t *R_GetPatchColumnClamped(const rpatch_t *patch, int columnIndex) { + if (columnIndex < 0) columnIndex = 0; + if (columnIndex >= patch->width) columnIndex = patch->width-1; + return &patch->columns[columnIndex]; +} + +//--------------------------------------------------------------------------- +const rcolumn_t *R_GetPatchColumn(const rpatch_t *patch, int columnIndex) { + if (patch->isNotTileable) return R_GetPatchColumnClamped(patch, columnIndex); + else return R_GetPatchColumnWrapped(patch, columnIndex); +} + diff --git a/src/r_patch.h b/src/r_patch.h new file mode 100644 index 0000000..b0d1387 --- /dev/null +++ b/src/r_patch.h @@ -0,0 +1,111 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + *-----------------------------------------------------------------------------*/ + + +#ifndef R_PATCH_H +#define R_PATCH_H + +// Used to specify the sloping of the top and bottom of a column post +typedef enum { + RDRAW_EDGESLOPE_TOP_UP = (1<<0), + RDRAW_EDGESLOPE_TOP_DOWN = (1<<1), + RDRAW_EDGESLOPE_BOT_UP = (1<<2), + RDRAW_EDGESLOPE_BOT_DOWN = (1<<3), + RDRAW_EDGESLOPE_TOP_MASK = 0x3, + RDRAW_EDGESLOPE_BOT_MASK = 0xc, +} edgeslope_t; + +typedef struct { + int topdelta; + int length; + edgeslope_t slope; +} rpost_t; + +typedef struct { + int numPosts; + rpost_t *posts; + unsigned char *pixels; +} rcolumn_t; + +typedef struct { + int width; + int height; + unsigned widthmask; + + unsigned char isNotTileable; + + int leftoffset; + int topoffset; + + // this is the single malloc'ed/free'd array + // for this patch + unsigned char *data; + + // these are pointers into the data array + unsigned char *pixels; + rcolumn_t *columns; + rpost_t *posts; + +#ifdef TIMEDIAG + int locktic; +#endif + unsigned int locks; +} rpatch_t; + + +const rpatch_t *R_CachePatchNum(int id); +void R_UnlockPatchNum(int id); +#define R_CachePatchName(name) R_CachePatchNum(W_GetNumForName(name)) +#define R_UnlockPatchName(name) R_UnlockPatchNum(W_GetNumForName(name)) + +const rpatch_t *R_CacheTextureCompositePatchNum(int id); +void R_UnlockTextureCompositePatchNum(int id); + + +// Size query funcs +int R_NumPatchWidth(int lump) ; +int R_NumPatchHeight(int lump); +#define R_NamePatchWidth(name) R_NumPatchWidth(W_GetNumForName(name)) +#define R_NamePatchHeight(name) R_NumPatchHeight(W_GetNumForName(name)) + + +const rcolumn_t *R_GetPatchColumnWrapped(const rpatch_t *patch, int columnIndex); +const rcolumn_t *R_GetPatchColumnClamped(const rpatch_t *patch, int columnIndex); + + +// returns R_GetPatchColumnWrapped for square, non-holed textures +// and R_GetPatchColumnClamped otherwise +const rcolumn_t *R_GetPatchColumn(const rpatch_t *patch, int columnIndex); + + +void R_InitPatches(); +void R_FlushAllPatches(); + +#endif diff --git a/src/r_plane.c b/src/r_plane.c new file mode 100644 index 0000000..8eaa1e1 --- /dev/null +++ b/src/r_plane.c @@ -0,0 +1,468 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Here is a core component: drawing the floors and ceilings, + * while maintaining a per column clipping list only. + * Moreover, the sky areas have to be determined. + * + * MAXVISPLANES is no longer a limit on the number of visplanes, + * but a limit on the number of hash slots; larger numbers mean + * better performance usually but after a point they are wasted, + * and memory and time overheads creep in. + * + * For more information on visplanes, see: + * + * http://classicgaming.com/doom/editing/ + * + * Lee Killough + * + *-----------------------------------------------------------------------------*/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "z_zone.h" /* memory allocation wrappers -- killough */ + +#include "doomstat.h" +#include "w_wad.h" +#include "r_main.h" +#include "r_draw.h" +#include "r_things.h" +#include "r_sky.h" +#include "r_plane.h" +#include "v_video.h" +#include "lprintf.h" + +#define MAXVISPLANES 128 /* must be a power of 2 */ + +static visplane_t *visplanes[MAXVISPLANES]; // killough +static visplane_t *freetail; // killough +static visplane_t **freehead = &freetail; // killough +visplane_t *floorplane, *ceilingplane; + +// killough -- hash function for visplanes +// Empirically verified to be fairly uniform: + +#define visplane_hash(picnum,lightlevel,height) \ + ((unsigned)((picnum)*3+(lightlevel)+(height)*7) & (MAXVISPLANES-1)) + +size_t maxopenings; +int *openings,*lastopening; // dropoff overflow + +// Clip values are the solid pixel bounding the range. +// floorclip starts out SCREENHEIGHT +// ceilingclip starts out -1 + +int floorclip[MAX_SCREENWIDTH], ceilingclip[MAX_SCREENWIDTH]; // dropoff overflow + +// spanstart holds the start of a plane span; initialized to 0 at start + +static int spanstart[MAX_SCREENHEIGHT]; // killough 2/8/98 + +// +// texture mapping +// + +static const lighttable_t **planezlight; +static fixed_t planeheight; + +// killough 2/8/98: make variables static + +static fixed_t basexscale, baseyscale; +static fixed_t cachedheight[MAX_SCREENHEIGHT]; +static fixed_t cacheddistance[MAX_SCREENHEIGHT]; +static fixed_t cachedxstep[MAX_SCREENHEIGHT]; +static fixed_t cachedystep[MAX_SCREENHEIGHT]; +static fixed_t xoffs,yoffs; // killough 2/28/98: flat offsets + +fixed_t yslope[MAX_SCREENHEIGHT], distscale[MAX_SCREENWIDTH]; + +// +// R_InitPlanes +// Only at game startup. +// +void R_InitPlanes (void) +{ +} + +// +// R_MapPlane +// +// Uses global vars: +// planeheight +// dsvars.source +// basexscale +// baseyscale +// viewx +// viewy +// xoffs +// yoffs +// +// BASIC PRIMITIVE +// + +static void R_MapPlane(int y, int x1, int x2, draw_span_vars_t *dsvars) +{ + angle_t angle; + fixed_t distance, length; + unsigned index; + +#ifdef RANGECHECK + if (x2 < x1 || x1<0 || x2>=viewwidth || (unsigned)y>(unsigned)viewheight) + I_Error ("R_MapPlane: %i, %i at %i",x1,x2,y); +#endif + + if (planeheight != cachedheight[y]) + { + cachedheight[y] = planeheight; + distance = cacheddistance[y] = FixedMul (planeheight, yslope[y]); + dsvars->xstep = cachedxstep[y] = FixedMul (distance,basexscale); + dsvars->ystep = cachedystep[y] = FixedMul (distance,baseyscale); + } + else + { + distance = cacheddistance[y]; + dsvars->xstep = cachedxstep[y]; + dsvars->ystep = cachedystep[y]; + } + + length = FixedMul (distance,distscale[x1]); + angle = (viewangle + xtoviewangle[x1])>>ANGLETOFINESHIFT; + + // killough 2/28/98: Add offsets + dsvars->xfrac = viewx + FixedMul(finecosine[angle], length) + xoffs; + dsvars->yfrac = -viewy - FixedMul(finesine[angle], length) + yoffs; + + if (drawvars.filterfloor == RDRAW_FILTER_LINEAR) { + dsvars->xfrac -= (FRACUNIT>>1); + dsvars->yfrac -= (FRACUNIT>>1); + } + + if (!(dsvars->colormap = fixedcolormap)) + { + dsvars->z = distance; + index = distance >> LIGHTZSHIFT; + if (index >= MAXLIGHTZ ) + index = MAXLIGHTZ-1; + dsvars->colormap = planezlight[index]; + dsvars->nextcolormap = planezlight[index+1 >= MAXLIGHTZ ? MAXLIGHTZ-1 : index+1]; + } + else + { + dsvars->z = 0; + } + + dsvars->y = y; + dsvars->x1 = x1; + dsvars->x2 = x2; + + if (V_GetMode() != VID_MODEGL) + R_DrawSpan(dsvars); +} + +// +// R_ClearPlanes +// At begining of frame. +// + +void R_ClearPlanes(void) +{ + int i; + + // opening / clipping determination + for (i=0 ; inext; + + lastopening = openings; + + // texture calculation + memset (cachedheight, 0, sizeof(cachedheight)); + + // scale will be unit scale at SCREENWIDTH/2 distance + basexscale = FixedDiv (viewsin,projection); + baseyscale = FixedDiv (viewcos,projection); +} + +// New function, by Lee Killough + +static visplane_t *new_visplane(unsigned hash) +{ + visplane_t *check = freetail; + if (!check) + check = calloc(1, sizeof *check); + else + if (!(freetail = freetail->next)) + freehead = &freetail; + check->next = visplanes[hash]; + visplanes[hash] = check; + return check; +} + +/* + * R_DupPlane + * + * cph 2003/04/18 - create duplicate of existing visplane and set initial range + */ +visplane_t *R_DupPlane(const visplane_t *pl, int start, int stop) +{ + unsigned hash = visplane_hash(pl->picnum, pl->lightlevel, pl->height); + visplane_t *new_pl = new_visplane(hash); + + new_pl->height = pl->height; + new_pl->picnum = pl->picnum; + new_pl->lightlevel = pl->lightlevel; + new_pl->xoffs = pl->xoffs; // killough 2/28/98 + new_pl->yoffs = pl->yoffs; + new_pl->minx = start; + new_pl->maxx = stop; + memset(new_pl->top, 0xff, sizeof new_pl->top); + return new_pl; +} +// +// R_FindPlane +// +// killough 2/28/98: Add offsets + +visplane_t *R_FindPlane(fixed_t height, int picnum, int lightlevel, + fixed_t xoffs, fixed_t yoffs) +{ + visplane_t *check; + unsigned hash; // killough + + if (picnum == skyflatnum || picnum & PL_SKYFLAT) + height = lightlevel = 0; // killough 7/19/98: most skies map together + + // New visplane algorithm uses hash table -- killough + hash = visplane_hash(picnum,lightlevel,height); + + for (check=visplanes[hash]; check; check=check->next) // killough + if (height == check->height && + picnum == check->picnum && + lightlevel == check->lightlevel && + xoffs == check->xoffs && // killough 2/28/98: Add offset checks + yoffs == check->yoffs) + return check; + + check = new_visplane(hash); // killough + + check->height = height; + check->picnum = picnum; + check->lightlevel = lightlevel; + check->minx = viewwidth; // Was SCREENWIDTH -- killough 11/98 + check->maxx = -1; + check->xoffs = xoffs; // killough 2/28/98: Save offsets + check->yoffs = yoffs; + + memset (check->top, 0xff, sizeof check->top); + + return check; +} + +// +// R_CheckPlane +// +visplane_t *R_CheckPlane(visplane_t *pl, int start, int stop) +{ + int intrl, intrh, unionl, unionh, x; + + if (start < pl->minx) + intrl = pl->minx, unionl = start; + else + unionl = pl->minx, intrl = start; + + if (stop > pl->maxx) + intrh = pl->maxx, unionh = stop; + else + unionh = pl->maxx, intrh = stop; + + for (x=intrl ; x <= intrh && pl->top[x] == 0xffffffffu; x++) // dropoff overflow + ; + + if (x > intrh) { /* Can use existing plane; extend range */ + pl->minx = unionl; pl->maxx = unionh; + return pl; + } else /* Cannot use existing plane; create a new one */ + return R_DupPlane(pl,start,stop); +} + +// +// R_MakeSpans +// + +static void R_MakeSpans(int x, unsigned int t1, unsigned int b1, + unsigned int t2, unsigned int b2, + draw_span_vars_t *dsvars) +{ + for (; t1 < t2 && t1 <= b1; t1++) + R_MapPlane(t1, spanstart[t1], x-1, dsvars); + for (; b1 > b2 && b1 >= t1; b1--) + R_MapPlane(b1, spanstart[b1] ,x-1, dsvars); + while (t2 < t1 && t2 <= b2) + spanstart[t2++] = x; + while (b2 > b1 && b2 >= t2) + spanstart[b2--] = x; +} + +// New function, by Lee Killough + +static void R_DoDrawPlane(visplane_t *pl) +{ + register int x; + draw_column_vars_t dcvars; + R_DrawColumn_f colfunc = R_GetDrawColumnFunc(RDC_PIPELINE_STANDARD, drawvars.filterwall, drawvars.filterz); + + R_SetDefaultDrawColumnVars(&dcvars); + + if (pl->minx <= pl->maxx) { + if (pl->picnum == skyflatnum || pl->picnum & PL_SKYFLAT) { // sky flat + int texture; + const rpatch_t *tex_patch; + angle_t an, flip; + + // killough 10/98: allow skies to come from sidedefs. + // Allows scrolling and/or animated skies, as well as + // arbitrary multiple skies per level without having + // to use info lumps. + + an = viewangle; + + if (pl->picnum & PL_SKYFLAT) + { + // Sky Linedef + const line_t *l = &lines[pl->picnum & ~PL_SKYFLAT]; + + // Sky transferred from first sidedef + const side_t *s = *l->sidenum + sides; + + // Texture comes from upper texture of reference sidedef + texture = texturetranslation[s->toptexture]; + + // Horizontal offset is turned into an angle offset, + // to allow sky rotation as well as careful positioning. + // However, the offset is scaled very small, so that it + // allows a long-period of sky rotation. + + an += s->textureoffset; + + // Vertical offset allows careful sky positioning. + + dcvars.texturemid = s->rowoffset - 28*FRACUNIT; + + // We sometimes flip the picture horizontally. + // + // Doom always flipped the picture, so we make it optional, + // to make it easier to use the new feature, while to still + // allow old sky textures to be used. + + flip = l->special==272 ? 0u : ~0u; + } + else + { // Normal Doom sky, only one allowed per level + dcvars.texturemid = skytexturemid; // Default y-offset + texture = skytexture; // Default texture + flip = 0; // Doom flips it + } + + /* Sky is always drawn full bright, i.e. colormaps[0] is used. + * Because of this hack, sky is not affected by INVUL inverse mapping. + * Until Boom fixed this. Compat option added in MBF. */ + + if (comp[comp_skymap] || !(dcvars.colormap = fixedcolormap)) + dcvars.colormap = fullcolormap; // killough 3/20/98 + + dcvars.nextcolormap = dcvars.colormap; // for filtering -- POPE + + //dcvars.texturemid = skytexturemid; + dcvars.texheight = textureheight[skytexture]>>FRACBITS; // killough + // proff 09/21/98: Changed for high-res + dcvars.iscale = FRACUNIT*200/viewheight; + + tex_patch = R_CacheTextureCompositePatchNum(texture); + + // killough 10/98: Use sky scrolling offset, and possibly flip picture + for (x = pl->minx; (dcvars.x = x) <= pl->maxx; x++) + if ((dcvars.yl = pl->top[x]) != -1 && dcvars.yl <= (dcvars.yh = pl->bottom[x])) // dropoff overflow + { + dcvars.source = R_GetTextureColumn(tex_patch, ((an + xtoviewangle[x])^flip) >> ANGLETOSKYSHIFT); + dcvars.prevsource = R_GetTextureColumn(tex_patch, ((an + xtoviewangle[x-1])^flip) >> ANGLETOSKYSHIFT); + dcvars.nextsource = R_GetTextureColumn(tex_patch, ((an + xtoviewangle[x+1])^flip) >> ANGLETOSKYSHIFT); + colfunc(&dcvars); + } + + R_UnlockTextureCompositePatchNum(texture); + + } else { // regular flat + + int stop, light; + draw_span_vars_t dsvars; + + dsvars.source = W_CacheLumpNum(firstflat + flattranslation[pl->picnum]); + + xoffs = pl->xoffs; // killough 2/28/98: Add offsets + yoffs = pl->yoffs; + planeheight = D_abs(pl->height-viewz); + light = (pl->lightlevel >> LIGHTSEGSHIFT) + extralight; + + if (light >= LIGHTLEVELS) + light = LIGHTLEVELS-1; + + if (light < 0) + light = 0; + + stop = pl->maxx + 1; + planezlight = zlight[light]; + pl->top[pl->minx-1] = pl->top[stop] = 0xffffffffu; // dropoff overflow + + for (x = pl->minx ; x <= stop ; x++) + R_MakeSpans(x,pl->top[x-1],pl->bottom[x-1], + pl->top[x],pl->bottom[x], &dsvars); + + W_UnlockLumpNum(firstflat + flattranslation[pl->picnum]); + } + } +} + +// +// RDrawPlanes +// At the end of each frame. +// + +void R_DrawPlanes (void) +{ + visplane_t *pl; + int i; + for (i=0;inext, rendered_visplanes++) + R_DoDrawPlane(pl); +} diff --git a/src/r_plane.h b/src/r_plane.h new file mode 100644 index 0000000..7a2a515 --- /dev/null +++ b/src/r_plane.h @@ -0,0 +1,67 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Refresh, visplane stuff (floor, ceilings). + * + *-----------------------------------------------------------------------------*/ + +#ifndef __R_PLANE__ +#define __R_PLANE__ + +#include "r_data.h" + +#ifdef __GNUG__ +#pragma interface +#endif + +/* killough 10/98: special mask indicates sky flat comes from sidedef */ +#define PL_SKYFLAT (0x80000000) + +/* Visplane related. */ +extern int *lastopening; // dropoff overflow + +extern int floorclip[], ceilingclip[]; // dropoff overflow +extern fixed_t yslope[], distscale[]; + +void R_InitPlanes(void); +void R_ClearPlanes(void); +void R_DrawPlanes (void); + +visplane_t *R_FindPlane( + fixed_t height, + int picnum, + int lightlevel, + fixed_t xoffs, /* killough 2/28/98: add x-y offsets */ + fixed_t yoffs + ); + +visplane_t *R_CheckPlane(visplane_t *pl, int start, int stop); +visplane_t *R_DupPlane(const visplane_t *pl, int start, int stop); + +#endif diff --git a/src/r_segs.c b/src/r_segs.c new file mode 100644 index 0000000..cb30b75 --- /dev/null +++ b/src/r_segs.c @@ -0,0 +1,854 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2004 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * All the clipping: columns, horizontal spans, sky columns. + * + *-----------------------------------------------------------------------------*/ +// +// 4/25/98, 5/2/98 killough: reformatted, beautified + +#include "doomstat.h" +#include "r_main.h" +#include "r_bsp.h" +#include "r_segs.h" +#include "r_plane.h" +#include "r_things.h" +#include "r_draw.h" +#include "w_wad.h" +#include "v_video.h" +#include "lprintf.h" + +// OPTIMIZE: closed two sided lines as single sided + +// killough 1/6/98: replaced globals with statics where appropriate + +// True if any of the segs textures might be visible. +static boolean segtextured; +static boolean markfloor; // False if the back side is the same plane. +static boolean markceiling; +static boolean maskedtexture; +static int toptexture; +static int bottomtexture; +static int midtexture; + +static fixed_t toptexheight, midtexheight, bottomtexheight; // cph + +angle_t rw_normalangle; // angle to line origin +int rw_angle1; +fixed_t rw_distance; + +// +// regular wall +// +static int rw_x; +static int rw_stopx; +static angle_t rw_centerangle; +static fixed_t rw_offset; +static fixed_t rw_scale; +static fixed_t rw_scalestep; +static fixed_t rw_midtexturemid; +static fixed_t rw_toptexturemid; +static fixed_t rw_bottomtexturemid; +static int rw_lightlevel; +static int worldtop; +static int worldbottom; +static int worldhigh; +static int worldlow; +static fixed_t pixhigh; +static fixed_t pixlow; +static fixed_t pixhighstep; +static fixed_t pixlowstep; +static fixed_t topfrac; +static fixed_t topstep; +static fixed_t bottomfrac; +static fixed_t bottomstep; +static int *maskedtexturecol; // dropoff overflow + +// +// R_ScaleFromGlobalAngle +// Returns the texture mapping scale +// for the current line (horizontal span) +// at the given angle. +// rw_distance must be calculated first. +// +// killough 5/2/98: reformatted, cleaned up +// CPhipps - moved here from r_main.c + +static fixed_t R_ScaleFromGlobalAngle(angle_t visangle) +{ + int anglea = ANG90 + (visangle-viewangle); + int angleb = ANG90 + (visangle-rw_normalangle); + int den = FixedMul(rw_distance, finesine[anglea>>ANGLETOFINESHIFT]); +// proff 11/06/98: Changed for high-res + fixed_t num = FixedMul(projectiony, finesine[angleb>>ANGLETOFINESHIFT]); + return den > num>>16 ? (num = FixedDiv(num, den)) > 64*FRACUNIT ? + 64*FRACUNIT : num < 256 ? 256 : num : 64*FRACUNIT; +} + +// +// R_RenderMaskedSegRange +// + +void R_RenderMaskedSegRange(drawseg_t *ds, int x1, int x2) +{ + int texnum; + sector_t tempsec; // killough 4/13/98 + const rpatch_t *patch; + R_DrawColumn_f colfunc; + draw_column_vars_t dcvars; + angle_t angle; + + R_SetDefaultDrawColumnVars(&dcvars); + + // Calculate light table. + // Use different light tables + // for horizontal / vertical / diagonal. Diagonal? + + curline = ds->curline; // OPTIMIZE: get rid of LIGHTSEGSHIFT globally + + // killough 4/11/98: draw translucent 2s normal textures + + colfunc = R_GetDrawColumnFunc(RDC_PIPELINE_STANDARD, drawvars.filterwall, drawvars.filterz); + if (curline->linedef->tranlump >= 0 && general_translucency) + { + colfunc = R_GetDrawColumnFunc(RDC_PIPELINE_TRANSLUCENT, drawvars.filterwall, drawvars.filterz); + tranmap = main_tranmap; + if (curline->linedef->tranlump > 0) + tranmap = W_CacheLumpNum(curline->linedef->tranlump-1); + } + // killough 4/11/98: end translucent 2s normal code + + frontsector = curline->frontsector; + backsector = curline->backsector; + + // cph 2001/11/25 - middle textures did not animate in v1.2 + texnum = curline->sidedef->midtexture; + if (!comp[comp_maskedanim]) + texnum = texturetranslation[texnum]; + + // killough 4/13/98: get correct lightlevel for 2s normal textures + rw_lightlevel = R_FakeFlat(frontsector, &tempsec, NULL, NULL, false) ->lightlevel; + + maskedtexturecol = ds->maskedtexturecol; + + rw_scalestep = ds->scalestep; + spryscale = ds->scale1 + (x1 - ds->x1)*rw_scalestep; + mfloorclip = ds->sprbottomclip; + mceilingclip = ds->sprtopclip; + + // find positioning + if (curline->linedef->flags & ML_DONTPEGBOTTOM) + { + dcvars.texturemid = frontsector->floorheight > backsector->floorheight + ? frontsector->floorheight : backsector->floorheight; + dcvars.texturemid = dcvars.texturemid + textureheight[texnum] - viewz; + } + else + { + dcvars.texturemid =frontsector->ceilingheightceilingheight + ? frontsector->ceilingheight : backsector->ceilingheight; + dcvars.texturemid = dcvars.texturemid - viewz; + } + + dcvars.texturemid += curline->sidedef->rowoffset; + + if (fixedcolormap) { + dcvars.colormap = fixedcolormap; + dcvars.nextcolormap = dcvars.colormap; // for filtering -- POPE + } + + patch = R_CacheTextureCompositePatchNum(texnum); + + // draw the columns + for (dcvars.x = x1 ; dcvars.x <= x2 ; dcvars.x++, spryscale += rw_scalestep) + if (maskedtexturecol[dcvars.x] != INT_MAX) // dropoff overflow + { + // calculate texture offset - POPE + angle = (ds->rw_centerangle + xtoviewangle[dcvars.x]) >> ANGLETOFINESHIFT; + dcvars.texu = ds->rw_offset - FixedMul(finetangent[angle], ds->rw_distance); + if (drawvars.filterwall == RDRAW_FILTER_LINEAR) + dcvars.texu -= (FRACUNIT>>1); + + if (!fixedcolormap) + dcvars.z = spryscale; // for filtering -- POPE + dcvars.colormap = R_ColourMap(rw_lightlevel,spryscale); + dcvars.nextcolormap = R_ColourMap(rw_lightlevel+1,spryscale); // for filtering -- POPE + + // killough 3/2/98: + // + // This calculation used to overflow and cause crashes in Doom: + // + // sprtopscreen = centeryfrac - FixedMul(dcvars.texturemid, spryscale); + // + // This code fixes it, by using double-precision intermediate + // arithmetic and by skipping the drawing of 2s normals whose + // mapping to screen coordinates is totally out of range: + + { + int_64_t t = ((int_64_t) centeryfrac << FRACBITS) - + (int_64_t) dcvars.texturemid * spryscale; + if (t + (int_64_t) textureheight[texnum] * spryscale < 0 || + t > (int_64_t) MAX_SCREENHEIGHT << FRACBITS*2) + continue; // skip if the texture is out of screen's range + sprtopscreen = (long)(t >> FRACBITS); + } + + dcvars.iscale = 0xffffffffu / (unsigned) spryscale; + + // killough 1/25/98: here's where Medusa came in, because + // it implicitly assumed that the column was all one patch. + // Originally, Doom did not construct complete columns for + // multipatched textures, so there were no header or trailer + // bytes in the column referred to below, which explains + // the Medusa effect. The fix is to construct true columns + // when forming multipatched textures (see r_data.c). + + // draw the texture + R_DrawMaskedColumn( + patch, + colfunc, + &dcvars, + R_GetPatchColumnWrapped(patch, maskedtexturecol[dcvars.x]), + R_GetPatchColumnWrapped(patch, maskedtexturecol[dcvars.x]-1), + R_GetPatchColumnWrapped(patch, maskedtexturecol[dcvars.x]+1) + ); + + maskedtexturecol[dcvars.x] = INT_MAX; // dropoff overflow + } + + // Except for main_tranmap, mark others purgable at this point + if (curline->linedef->tranlump > 0 && general_translucency) + W_UnlockLumpNum(curline->linedef->tranlump-1); // cph - unlock it + + R_UnlockTextureCompositePatchNum(texnum); + + curline = NULL; /* cph 2001/11/18 - must clear curline now we're done with it, so R_ColourMap doesn't try using it for other things */ +} + +// +// R_RenderSegLoop +// Draws zero, one, or two textures (and possibly a masked texture) for walls. +// Can draw or mark the starting pixel of floor and ceiling textures. +// CALLED: CORE LOOPING ROUTINE. +// + +#define HEIGHTBITS 12 +#define HEIGHTUNIT (1<>HEIGHTBITS; + int yl = (topfrac+HEIGHTUNIT-1)>>HEIGHTBITS; + + // no space above wall? + int bottom,top = ceilingclip[rw_x]+1; + + if (yl < top) + yl = top; + + if (markceiling) + { + bottom = yl-1; + + if (bottom >= floorclip[rw_x]) + bottom = floorclip[rw_x]-1; + + if (top <= bottom) + { + ceilingplane->top[rw_x] = top; + ceilingplane->bottom[rw_x] = bottom; + } + // SoM: this should be set here + ceilingclip[rw_x] = bottom; + } + +// yh = bottomfrac>>HEIGHTBITS; + + bottom = floorclip[rw_x]-1; + if (yh > bottom) + yh = bottom; + + if (markfloor) + { + + top = yh < ceilingclip[rw_x] ? ceilingclip[rw_x] : yh; + + if (++top <= bottom) + { + floorplane->top[rw_x] = top; + floorplane->bottom[rw_x] = bottom; + } + // SoM: This should be set here to prevent overdraw + floorclip[rw_x] = top; + } + + // texturecolumn and lighting are independent of wall tiers + if (segtextured) + { + // calculate texture offset + angle_t angle =(rw_centerangle+xtoviewangle[rw_x])>>ANGLETOFINESHIFT; + + texturecolumn = rw_offset-FixedMul(finetangent[angle],rw_distance); + if (drawvars.filterwall == RDRAW_FILTER_LINEAR) + texturecolumn -= (FRACUNIT>>1); + dcvars.texu = texturecolumn; // for filtering -- POPE + texturecolumn >>= FRACBITS; + + dcvars.colormap = R_ColourMap(rw_lightlevel,rw_scale); + dcvars.nextcolormap = R_ColourMap(rw_lightlevel+1,rw_scale); // for filtering -- POPE + dcvars.z = rw_scale; // for filtering -- POPE + + dcvars.x = rw_x; + dcvars.iscale = 0xffffffffu / (unsigned)rw_scale; + } + + // draw the wall tiers + if (midtexture) + { + + dcvars.yl = yl; // single sided line + dcvars.yh = yh; + dcvars.texturemid = rw_midtexturemid; + tex_patch = R_CacheTextureCompositePatchNum(midtexture); + dcvars.source = R_GetTextureColumn(tex_patch, texturecolumn); + dcvars.prevsource = R_GetTextureColumn(tex_patch, texturecolumn-1); + dcvars.nextsource = R_GetTextureColumn(tex_patch, texturecolumn+1); + dcvars.texheight = midtexheight; + colfunc (&dcvars); + R_UnlockTextureCompositePatchNum(midtexture); + tex_patch = NULL; + ceilingclip[rw_x] = viewheight; + floorclip[rw_x] = -1; + } + else + { + + // two sided line + if (toptexture) + { + // top wall + int mid = pixhigh>>HEIGHTBITS; + pixhigh += pixhighstep; + + if (mid >= floorclip[rw_x]) + mid = floorclip[rw_x]-1; + + if (mid >= yl) + { + dcvars.yl = yl; + dcvars.yh = mid; + dcvars.texturemid = rw_toptexturemid; + tex_patch = R_CacheTextureCompositePatchNum(toptexture); + dcvars.source = R_GetTextureColumn(tex_patch,texturecolumn); + dcvars.prevsource = R_GetTextureColumn(tex_patch,texturecolumn-1); + dcvars.nextsource = R_GetTextureColumn(tex_patch,texturecolumn+1); + dcvars.texheight = toptexheight; + colfunc (&dcvars); + R_UnlockTextureCompositePatchNum(toptexture); + tex_patch = NULL; + ceilingclip[rw_x] = mid; + } + else + ceilingclip[rw_x] = yl-1; + } + else // no top wall + { + + if (markceiling) + ceilingclip[rw_x] = yl-1; + } + + if (bottomtexture) // bottom wall + { + int mid = (pixlow+HEIGHTUNIT-1)>>HEIGHTBITS; + pixlow += pixlowstep; + + // no space above wall? + if (mid <= ceilingclip[rw_x]) + mid = ceilingclip[rw_x]+1; + + if (mid <= yh) + { + dcvars.yl = mid; + dcvars.yh = yh; + dcvars.texturemid = rw_bottomtexturemid; + tex_patch = R_CacheTextureCompositePatchNum(bottomtexture); + dcvars.source = R_GetTextureColumn(tex_patch, texturecolumn); + dcvars.prevsource = R_GetTextureColumn(tex_patch, texturecolumn-1); + dcvars.nextsource = R_GetTextureColumn(tex_patch, texturecolumn+1); + dcvars.texheight = bottomtexheight; + colfunc (&dcvars); + R_UnlockTextureCompositePatchNum(bottomtexture); + tex_patch = NULL; + floorclip[rw_x] = mid; + } + else + floorclip[rw_x] = yh+1; + } + else // no bottom wall + { + if (markfloor) + floorclip[rw_x] = yh+1; + } + + // cph - if we completely blocked further sight through this column, + // add this info to the solid columns array for r_bsp.c + if ((markceiling || markfloor) && + (floorclip[rw_x] <= ceilingclip[rw_x] + 1)) { + solidcol[rw_x] = 1; didsolidcol = 1; + } + + // save texturecol for backdrawing of masked mid texture + if (maskedtexture) + maskedtexturecol[rw_x] = texturecolumn; + } + + rw_scale += rw_scalestep; + topfrac += topstep; + bottomfrac += bottomstep; + } +} + +// killough 5/2/98: move from r_main.c, made static, simplified + +static fixed_t R_PointToDist(fixed_t x, fixed_t y) +{ + fixed_t dx = D_abs(x - viewx); + fixed_t dy = D_abs(y - viewy); + + if (dy > dx) + { + fixed_t t = dx; + dx = dy; + dy = t; + } + + return FixedDiv(dx, finesine[(tantoangle[FixedDiv(dy,dx) >> DBITS] + + ANG90) >> ANGLETOFINESHIFT]); +} + +// +// R_StoreWallRange +// A wall segment will be drawn +// between start and stop pixels (inclusive). +// +void R_StoreWallRange(const int start, const int stop) +{ + fixed_t hyp; + angle_t offsetangle; + + if (ds_p == drawsegs+maxdrawsegs) // killough 1/98 -- fix 2s line HOM + { + unsigned pos = ds_p - drawsegs; // jff 8/9/98 fix from ZDOOM1.14a + unsigned newmax = maxdrawsegs ? maxdrawsegs*2 : 128; // killough + drawsegs = realloc(drawsegs,newmax*sizeof(*drawsegs)); + ds_p = drawsegs + pos; // jff 8/9/98 fix from ZDOOM1.14a + maxdrawsegs = newmax; + } + + if(curline->miniseg == false) // figgi -- skip minisegs + curline->linedef->flags |= ML_MAPPED; + +#ifdef GL_DOOM + if (V_GetMode() == VID_MODEGL) + { + // proff 11/99: the rest of the calculations is not needed for OpenGL + ds_p++->curline = curline; + gld_AddWall(curline); + + return; + } +#endif + + +#ifdef RANGECHECK + if (start >=viewwidth || start > stop) + I_Error ("Bad R_RenderWallRange: %i to %i", start , stop); +#endif + + sidedef = curline->sidedef; + linedef = curline->linedef; + + // mark the segment as visible for auto map + linedef->flags |= ML_MAPPED; + + // calculate rw_distance for scale calculation + rw_normalangle = curline->angle + ANG90; + + offsetangle = rw_normalangle-rw_angle1; + + if (D_abs(offsetangle) > ANG90) + offsetangle = ANG90; + + hyp = (viewx==curline->v1->x && viewy==curline->v1->y)? + 0 : R_PointToDist (curline->v1->x, curline->v1->y); + rw_distance = FixedMul(hyp, finecosine[offsetangle>>ANGLETOFINESHIFT]); + + ds_p->x1 = rw_x = start; + ds_p->x2 = stop; + ds_p->curline = curline; + rw_stopx = stop+1; + + { // killough 1/6/98, 2/1/98: remove limit on openings + extern int *openings; // dropoff overflow + extern size_t maxopenings; + size_t pos = lastopening - openings; + size_t need = (rw_stopx - start)*4 + pos; + if (need > maxopenings) + { + drawseg_t *ds; //jff 8/9/98 needed for fix from ZDoom + int *oldopenings = openings; // dropoff overflow + int *oldlast = lastopening; // dropoff overflow + + do + maxopenings = maxopenings ? maxopenings*2 : 16384; + while (need > maxopenings); + openings = realloc(openings, maxopenings * sizeof(*openings)); + lastopening = openings + pos; + + // jff 8/9/98 borrowed fix for openings from ZDOOM1.14 + // [RH] We also need to adjust the openings pointers that + // were already stored in drawsegs. + for (ds = drawsegs; ds < ds_p; ds++) + { +#define ADJUST(p) if (ds->p + ds->x1 >= oldopenings && ds->p + ds->x1 <= oldlast)\ + ds->p = ds->p - oldopenings + openings; + ADJUST (maskedtexturecol); + ADJUST (sprtopclip); + ADJUST (sprbottomclip); + } +#undef ADJUST + } + } // killough: end of code to remove limits on openings + + // calculate scale at both ends and step + + ds_p->scale1 = rw_scale = + R_ScaleFromGlobalAngle (viewangle + xtoviewangle[start]); + + if (stop > start) + { + ds_p->scale2 = R_ScaleFromGlobalAngle (viewangle + xtoviewangle[stop]); + ds_p->scalestep = rw_scalestep = (ds_p->scale2-rw_scale) / (stop-start); + } + else + ds_p->scale2 = ds_p->scale1; + + // calculate texture boundaries + // and decide if floor / ceiling marks are needed + + worldtop = frontsector->ceilingheight - viewz; + worldbottom = frontsector->floorheight - viewz; + + midtexture = toptexture = bottomtexture = maskedtexture = 0; + ds_p->maskedtexturecol = NULL; + + if (!backsector) + { + // single sided line + midtexture = texturetranslation[sidedef->midtexture]; + midtexheight = (linedef->r_flags & RF_MID_TILE) ? 0 : textureheight[midtexture] >> FRACBITS; + + // a single sided line is terminal, so it must mark ends + markfloor = markceiling = true; + + if (linedef->flags & ML_DONTPEGBOTTOM) + { // bottom of texture at bottom + fixed_t vtop = frontsector->floorheight + + textureheight[sidedef->midtexture]; + rw_midtexturemid = vtop - viewz; + } + else // top of texture at top + rw_midtexturemid = worldtop; + + rw_midtexturemid += FixedMod(sidedef->rowoffset, textureheight[midtexture]); + + ds_p->silhouette = SIL_BOTH; + ds_p->sprtopclip = screenheightarray; + ds_p->sprbottomclip = negonearray; + ds_p->bsilheight = INT_MAX; + ds_p->tsilheight = INT_MIN; + } + else // two sided line + { + ds_p->sprtopclip = ds_p->sprbottomclip = NULL; + ds_p->silhouette = 0; + + if (linedef->r_flags & RF_CLOSED) { /* cph - closed 2S line e.g. door */ + // cph - killough's (outdated) comment follows - this deals with both + // "automap fixes", his and mine + // killough 1/17/98: this test is required if the fix + // for the automap bug (r_bsp.c) is used, or else some + // sprites will be displayed behind closed doors. That + // fix prevents lines behind closed doors with dropoffs + // from being displayed on the automap. + + ds_p->silhouette = SIL_BOTH; + ds_p->sprbottomclip = negonearray; + ds_p->bsilheight = INT_MAX; + ds_p->sprtopclip = screenheightarray; + ds_p->tsilheight = INT_MIN; + + } else { /* not solid - old code */ + + if (frontsector->floorheight > backsector->floorheight) + { + ds_p->silhouette = SIL_BOTTOM; + ds_p->bsilheight = frontsector->floorheight; + } + else + if (backsector->floorheight > viewz) + { + ds_p->silhouette = SIL_BOTTOM; + ds_p->bsilheight = INT_MAX; + } + + if (frontsector->ceilingheight < backsector->ceilingheight) + { + ds_p->silhouette |= SIL_TOP; + ds_p->tsilheight = frontsector->ceilingheight; + } + else + if (backsector->ceilingheight < viewz) + { + ds_p->silhouette |= SIL_TOP; + ds_p->tsilheight = INT_MIN; + } + } + + worldhigh = backsector->ceilingheight - viewz; + worldlow = backsector->floorheight - viewz; + + // hack to allow height changes in outdoor areas + if (frontsector->ceilingpic == skyflatnum + && backsector->ceilingpic == skyflatnum) + worldtop = worldhigh; + + markfloor = worldlow != worldbottom + || backsector->floorpic != frontsector->floorpic + || backsector->lightlevel != frontsector->lightlevel + + // killough 3/7/98: Add checks for (x,y) offsets + || backsector->floor_xoffs != frontsector->floor_xoffs + || backsector->floor_yoffs != frontsector->floor_yoffs + + // killough 4/15/98: prevent 2s normals + // from bleeding through deep water + || frontsector->heightsec != -1 + + // killough 4/17/98: draw floors if different light levels + || backsector->floorlightsec != frontsector->floorlightsec + ; + + markceiling = worldhigh != worldtop + || backsector->ceilingpic != frontsector->ceilingpic + || backsector->lightlevel != frontsector->lightlevel + + // killough 3/7/98: Add checks for (x,y) offsets + || backsector->ceiling_xoffs != frontsector->ceiling_xoffs + || backsector->ceiling_yoffs != frontsector->ceiling_yoffs + + // killough 4/15/98: prevent 2s normals + // from bleeding through fake ceilings + || (frontsector->heightsec != -1 && + frontsector->ceilingpic!=skyflatnum) + + // killough 4/17/98: draw ceilings if different light levels + || backsector->ceilinglightsec != frontsector->ceilinglightsec + ; + + if (backsector->ceilingheight <= frontsector->floorheight + || backsector->floorheight >= frontsector->ceilingheight) + markceiling = markfloor = true; // closed door + + if (worldhigh < worldtop) // top texture + { + toptexture = texturetranslation[sidedef->toptexture]; + toptexheight = (linedef->r_flags & RF_TOP_TILE) ? 0 : textureheight[toptexture] >> FRACBITS; + rw_toptexturemid = linedef->flags & ML_DONTPEGTOP ? worldtop : + backsector->ceilingheight+textureheight[sidedef->toptexture]-viewz; + rw_toptexturemid += FixedMod(sidedef->rowoffset, textureheight[toptexture]); + } + + if (worldlow > worldbottom) // bottom texture + { + bottomtexture = texturetranslation[sidedef->bottomtexture]; + bottomtexheight = (linedef->r_flags & RF_BOT_TILE) ? 0 : textureheight[bottomtexture] >> FRACBITS; + rw_bottomtexturemid = linedef->flags & ML_DONTPEGBOTTOM ? worldtop : + worldlow; + rw_bottomtexturemid += FixedMod(sidedef->rowoffset, textureheight[bottomtexture]); + } + + // allocate space for masked texture tables + if (sidedef->midtexture) // masked midtexture + { + maskedtexture = true; + ds_p->maskedtexturecol = maskedtexturecol = lastopening - rw_x; + lastopening += rw_stopx - rw_x; + } + } + + // calculate rw_offset (only needed for textured lines) + segtextured = midtexture | toptexture | bottomtexture | maskedtexture; + + if (segtextured) + { + rw_offset = FixedMul (hyp, -finesine[offsetangle >>ANGLETOFINESHIFT]); + + rw_offset += sidedef->textureoffset + curline->offset; + + rw_centerangle = ANG90 + viewangle - rw_normalangle; + + rw_lightlevel = frontsector->lightlevel; + } + + // Remember the vars used to determine fractional U texture + // coords for later - POPE + ds_p->rw_offset = rw_offset; + ds_p->rw_distance = rw_distance; + ds_p->rw_centerangle = rw_centerangle; + + // if a floor / ceiling plane is on the wrong side of the view + // plane, it is definitely invisible and doesn't need to be marked. + + // killough 3/7/98: add deep water check + if (frontsector->heightsec == -1) + { + if (frontsector->floorheight >= viewz) // above view plane + markfloor = false; + if (frontsector->ceilingheight <= viewz && + frontsector->ceilingpic != skyflatnum) // below view plane + markceiling = false; + } + + // calculate incremental stepping values for texture edges + worldtop >>= 4; + worldbottom >>= 4; + + topstep = -FixedMul (rw_scalestep, worldtop); + topfrac = (centeryfrac>>4) - FixedMul (worldtop, rw_scale); + + bottomstep = -FixedMul (rw_scalestep,worldbottom); + bottomfrac = (centeryfrac>>4) - FixedMul (worldbottom, rw_scale); + + if (backsector) + { + worldhigh >>= 4; + worldlow >>= 4; + + if (worldhigh < worldtop) + { + pixhigh = (centeryfrac>>4) - FixedMul (worldhigh, rw_scale); + pixhighstep = -FixedMul (rw_scalestep,worldhigh); + } + if (worldlow > worldbottom) + { + pixlow = (centeryfrac>>4) - FixedMul (worldlow, rw_scale); + pixlowstep = -FixedMul (rw_scalestep,worldlow); + } + } + + // render it + if (markceiling) { + if (ceilingplane) // killough 4/11/98: add NULL ptr checks + ceilingplane = R_CheckPlane (ceilingplane, rw_x, rw_stopx-1); + else + markceiling = 0; + } + + if (markfloor) { + if (floorplane) // killough 4/11/98: add NULL ptr checks + /* cph 2003/04/18 - ceilingplane and floorplane might be the same + * visplane (e.g. if both skies); R_CheckPlane doesn't know about + * modifications to the plane that might happen in parallel with the check + * being made, so we have to override it and split them anyway if that is + * a possibility, otherwise the floor marking would overwrite the ceiling + * marking, resulting in HOM. */ + if (markceiling && ceilingplane == floorplane) + floorplane = R_DupPlane (floorplane, rw_x, rw_stopx-1); + else + floorplane = R_CheckPlane (floorplane, rw_x, rw_stopx-1); + else + markfloor = 0; + } + + didsolidcol = 0; + R_RenderSegLoop(); + + /* cph - if a column was made solid by this wall, we _must_ save full clipping info */ + if (backsector && didsolidcol) { + if (!(ds_p->silhouette & SIL_BOTTOM)) { + ds_p->silhouette |= SIL_BOTTOM; + ds_p->bsilheight = backsector->floorheight; + } + if (!(ds_p->silhouette & SIL_TOP)) { + ds_p->silhouette |= SIL_TOP; + ds_p->tsilheight = backsector->ceilingheight; + } + } + + // save sprite clipping info + if ((ds_p->silhouette & SIL_TOP || maskedtexture) && !ds_p->sprtopclip) + { + memcpy (lastopening, ceilingclip+start, sizeof(int)*(rw_stopx-start)); // dropoff overflow + ds_p->sprtopclip = lastopening - start; + lastopening += rw_stopx - start; + } + if ((ds_p->silhouette & SIL_BOTTOM || maskedtexture) && !ds_p->sprbottomclip) + { + memcpy (lastopening, floorclip+start, sizeof(int)*(rw_stopx-start)); // dropoff overflow + ds_p->sprbottomclip = lastopening - start; + lastopening += rw_stopx - start; + } + if (maskedtexture && !(ds_p->silhouette & SIL_TOP)) + { + ds_p->silhouette |= SIL_TOP; + ds_p->tsilheight = INT_MIN; + } + if (maskedtexture && !(ds_p->silhouette & SIL_BOTTOM)) + { + ds_p->silhouette |= SIL_BOTTOM; + ds_p->bsilheight = INT_MAX; + } + ds_p++; +} diff --git a/src/r_segs.h b/src/r_segs.h new file mode 100644 index 0000000..c5b29a6 --- /dev/null +++ b/src/r_segs.h @@ -0,0 +1,44 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Refresh module, drawing LineSegs from BSP. + * + *-----------------------------------------------------------------------------*/ + +#ifndef __R_SEGS__ +#define __R_SEGS__ + +#ifdef __GNUG__ +#pragma interface +#endif + +void R_RenderMaskedSegRange(drawseg_t *ds, int x1, int x2); +void R_StoreWallRange(const int start, const int stop); + +#endif diff --git a/src/r_sky.c b/src/r_sky.c new file mode 100644 index 0000000..bc33c63 --- /dev/null +++ b/src/r_sky.c @@ -0,0 +1,56 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Sky rendering. The DOOM sky is a texture map like any + * wall, wrapping around. A 1024 columns equal 360 degrees. + * The default sky map is 256 columns and repeats 4 times + * on a 320 screen? + * + *-----------------------------------------------------------------------------*/ + +#ifdef __GNUG__ +#pragma implementation "r_sky.h" +#endif +#include "r_sky.h" + +// +// sky mapping +// +int skyflatnum; +int skytexture; +int skytexturemid; + +// +// R_InitSkyMap +// Called whenever the view size changes. +// +void R_InitSkyMap (void) +{ + skytexturemid = 100*FRACUNIT; +} diff --git a/src/r_sky.h b/src/r_sky.h new file mode 100644 index 0000000..1a69ae4 --- /dev/null +++ b/src/r_sky.h @@ -0,0 +1,55 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Sky rendering. + * + *-----------------------------------------------------------------------------*/ + +#ifndef __R_SKY__ +#define __R_SKY__ + +#include "m_fixed.h" + +#ifdef __GNUG__ +#pragma interface +#endif + +/* SKY, store the number for name. */ +#define SKYFLATNAME "F_SKY1" + +/* The sky map is 256*128*4 maps. */ +#define ANGLETOSKYSHIFT 22 + +extern int skytexture; +extern int skytexturemid; + +/* Called whenever the view size changes. */ +void R_InitSkyMap(void); + +#endif diff --git a/src/r_state.h b/src/r_state.h new file mode 100644 index 0000000..44784f2 --- /dev/null +++ b/src/r_state.h @@ -0,0 +1,116 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Refresh/render internal state variables (global). + * + *-----------------------------------------------------------------------------*/ + + +#ifndef __R_STATE__ +#define __R_STATE__ + +// Need data structure definitions. +#include "d_player.h" +#include "r_data.h" + +#ifdef __GNUG__ +#pragma interface +#endif + + +// +// Refresh internal data structures, +// for rendering. +// + +// needed for texture pegging +extern fixed_t *textureheight; + +extern int scaledviewwidth; + +extern int firstflat, numflats; + +// for global animation +extern int *flattranslation; +extern int *texturetranslation; + +// Sprite.... +extern int firstspritelump; +extern int lastspritelump; +extern int numspritelumps; + +// +// Lookup tables for map data. +// +extern int numsprites; +extern spritedef_t *sprites; + +extern int numvertexes; +extern vertex_t *vertexes; + +extern int numsegs; +extern seg_t *segs; + +extern int numsectors; +extern sector_t *sectors; + +extern int numsubsectors; +extern subsector_t *subsectors; + +extern int numnodes; +extern node_t *nodes; + +extern int numlines; +extern line_t *lines; + +extern int numsides; +extern side_t *sides; + + +// +// POV data. +// +extern fixed_t viewx; +extern fixed_t viewy; +extern fixed_t viewz; +extern angle_t viewangle; +extern player_t *viewplayer; +extern angle_t clipangle; +extern int viewangletox[FINEANGLES/2]; +extern angle_t xtoviewangle[MAX_SCREENWIDTH+1]; // killough 2/8/98 +extern fixed_t rw_distance; +extern angle_t rw_normalangle; + +// angle to line origin +extern int rw_angle1; + +extern visplane_t *floorplane; +extern visplane_t *ceilingplane; + +#endif diff --git a/src/r_things.c b/src/r_things.c new file mode 100644 index 0000000..68b1011 --- /dev/null +++ b/src/r_things.c @@ -0,0 +1,1077 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Refresh of things, i.e. objects represented by sprites. + * + *-----------------------------------------------------------------------------*/ + +#include "doomstat.h" +#include "w_wad.h" +#include "r_main.h" +#include "r_bsp.h" +#include "r_segs.h" +#include "r_draw.h" +#include "r_things.h" +#include "r_fps.h" +#include "v_video.h" +#include "lprintf.h" + +#define MINZ (FRACUNIT*4) +#define BASEYCENTER 100 + +typedef struct { + int x1; + int x2; + int column; + int topclip; + int bottomclip; +} maskdraw_t; + +// +// Sprite rotation 0 is facing the viewer, +// rotation 1 is one angle turn CLOCKWISE around the axis. +// This is not the same as the angle, +// which increases counter clockwise (protractor). +// There was a lot of stuff grabbed wrong, so I changed it... +// + +fixed_t pspritescale; +fixed_t pspriteiscale; +// proff 11/06/98: Added for high-res +fixed_t pspriteyscale; + +// constant arrays +// used for psprite clipping and initializing clipping + +int negonearray[MAX_SCREENWIDTH]; // killough 2/8/98: // dropoff overflow +int screenheightarray[MAX_SCREENWIDTH]; // change to MAX_* // dropoff overflow + +// +// INITIALIZATION FUNCTIONS +// + +// variables used to look up and range check thing_t sprites patches + +spritedef_t *sprites; +int numsprites; + +#define MAX_SPRITE_FRAMES 29 /* Macroized -- killough 1/25/98 */ + +static spriteframe_t sprtemp[MAX_SPRITE_FRAMES]; +static int maxframe; + +// +// R_InstallSpriteLump +// Local function for R_InitSprites. +// + +static void R_InstallSpriteLump(int lump, unsigned frame, + unsigned rotation, boolean flipped) +{ + if (frame >= MAX_SPRITE_FRAMES || rotation > 8) + I_Error("R_InstallSpriteLump: Bad frame characters in lump %i", lump); + + if ((int) frame > maxframe) + maxframe = frame; + + if (rotation == 0) + { // the lump should be used for all rotations + int r; + for (r=0 ; r<8 ; r++) + if (sprtemp[frame].lump[r]==-1) + { + sprtemp[frame].lump[r] = lump - firstspritelump; + sprtemp[frame].flip[r] = (byte) flipped; + sprtemp[frame].rotate = false; //jff 4/24/98 if any subbed, rotless + } + return; + } + + // the lump is only used for one rotation + + if (sprtemp[frame].lump[--rotation] == -1) + { + sprtemp[frame].lump[rotation] = lump - firstspritelump; + sprtemp[frame].flip[rotation] = (byte) flipped; + sprtemp[frame].rotate = true; //jff 4/24/98 only change if rot used + } +} + +// +// R_InitSpriteDefs +// Pass a null terminated list of sprite names +// (4 chars exactly) to be used. +// +// Builds the sprite rotation matrixes to account +// for horizontally flipped sprites. +// +// Will report an error if the lumps are inconsistent. +// Only called at startup. +// +// Sprite lump names are 4 characters for the actor, +// a letter for the frame, and a number for the rotation. +// +// A sprite that is flippable will have an additional +// letter/number appended. +// +// The rotation character can be 0 to signify no rotations. +// +// 1/25/98, 1/31/98 killough : Rewritten for performance +// +// Empirically verified to have excellent hash +// properties across standard Doom sprites: + +#define R_SpriteNameHash(s) ((unsigned)((s)[0]-((s)[1]*3-(s)[3]*2-(s)[2])*2)) + +static void R_InitSpriteDefs(const char * const * namelist) +{ + size_t numentries = lastspritelump-firstspritelump+1; + struct { int index, next; } *hash; + int i; + + if (!numentries || !*namelist) + return; + + // count the number of sprite names + for (i=0; namelist[i]; i++) + ; + + numsprites = i; + + sprites = Z_Malloc(numsprites *sizeof(*sprites), PU_STATIC, NULL); + + // Create hash table based on just the first four letters of each sprite + // killough 1/31/98 + + hash = malloc(sizeof(*hash)*numentries); // allocate hash table + + for (i=0; (size_t)i= 0) + { + memset(sprtemp, -1, sizeof(sprtemp)); + maxframe = -1; + do + { + register lumpinfo_t *lump = lumpinfo + j + firstspritelump; + + // Fast portable comparison -- killough + // (using int pointer cast is nonportable): + + if (!((lump->name[0] ^ spritename[0]) | + (lump->name[1] ^ spritename[1]) | + (lump->name[2] ^ spritename[2]) | + (lump->name[3] ^ spritename[3]))) + { + R_InstallSpriteLump(j+firstspritelump, + lump->name[4] - 'A', + lump->name[5] - '0', + false); + if (lump->name[6]) + R_InstallSpriteLump(j+firstspritelump, + lump->name[6] - 'A', + lump->name[7] - '0', + true); + } + } + while ((j = hash[j].next) >= 0); + + // check the frames that were found for completeness + if ((sprites[i].numframes = ++maxframe)) // killough 1/31/98 + { + int frame; + for (frame = 0; frame < maxframe; frame++) + switch ((int) sprtemp[frame].rotate) + { + case -1: + // no rotations were found for that frame at all + I_Error ("R_InitSprites: No patches found " + "for %.8s frame %c", namelist[i], frame+'A'); + break; + + case 0: + // only the first rotation is needed + break; + + case 1: + // must have all 8 frames + { + int rotation; + for (rotation=0 ; rotation<8 ; rotation++) + if (sprtemp[frame].lump[rotation] == -1) + I_Error ("R_InitSprites: Sprite %.8s frame %c " + "is missing rotations", + namelist[i], frame+'A'); + break; + } + } + // allocate space for the frames present and copy sprtemp to it + sprites[i].spriteframes = + Z_Malloc (maxframe * sizeof(spriteframe_t), PU_STATIC, NULL); + memcpy (sprites[i].spriteframes, sprtemp, + maxframe*sizeof(spriteframe_t)); + } + } + } + free(hash); // free hash table +} + +// +// GAME FUNCTIONS +// + +static vissprite_t *vissprites, **vissprite_ptrs; // killough +static size_t num_vissprite, num_vissprite_alloc, num_vissprite_ptrs; + +// +// R_InitSprites +// Called at program start. +// + +void R_InitSprites(const char * const *namelist) +{ + int i; + for (i=0; i= num_vissprite_alloc) // killough + { + size_t num_vissprite_alloc_prev = num_vissprite_alloc; + + num_vissprite_alloc = num_vissprite_alloc ? num_vissprite_alloc*2 : 128; + vissprites = realloc(vissprites,num_vissprite_alloc*sizeof(*vissprites)); + + //e6y: set all fields to zero + memset(vissprites + num_vissprite_alloc_prev, 0, + (num_vissprite_alloc - num_vissprite_alloc_prev)*sizeof(*vissprites)); + } + return vissprites + num_vissprite++; +} + +// +// R_DrawMaskedColumn +// Used for sprites and masked mid textures. +// Masked means: partly transparent, i.e. stored +// in posts/runs of opaque pixels. +// + +int *mfloorclip; // dropoff overflow +int *mceilingclip; // dropoff overflow +fixed_t spryscale; +fixed_t sprtopscreen; + +void R_DrawMaskedColumn( + const rpatch_t *patch, + R_DrawColumn_f colfunc, + draw_column_vars_t *dcvars, + const rcolumn_t *column, + const rcolumn_t *prevcolumn, + const rcolumn_t *nextcolumn +) +{ + int i; + int topscreen; + int bottomscreen; + fixed_t basetexturemid = dcvars->texturemid; + + dcvars->texheight = patch->height; // killough 11/98 + for (i=0; inumPosts; i++) { + const rpost_t *post = &column->posts[i]; + + // calculate unclipped screen coordinates for post + topscreen = sprtopscreen + spryscale*post->topdelta; + bottomscreen = topscreen + spryscale*post->length; + + dcvars->yl = (topscreen+FRACUNIT-1)>>FRACBITS; + dcvars->yh = (bottomscreen-1)>>FRACBITS; + + if (dcvars->yh >= mfloorclip[dcvars->x]) + dcvars->yh = mfloorclip[dcvars->x]-1; + + if (dcvars->yl <= mceilingclip[dcvars->x]) + dcvars->yl = mceilingclip[dcvars->x]+1; + + // killough 3/2/98, 3/27/98: Failsafe against overflow/crash: + if (dcvars->yl <= dcvars->yh && dcvars->yh < viewheight) + { + dcvars->source = column->pixels + post->topdelta; + dcvars->prevsource = prevcolumn->pixels + post->topdelta; + dcvars->nextsource = nextcolumn->pixels + post->topdelta; + + dcvars->texturemid = basetexturemid - (post->topdelta<edgeslope = post->slope; + // Drawn by either R_DrawColumn + // or (SHADOW) R_DrawFuzzColumn. + dcvars->drawingmasked = 1; // POPE + colfunc (dcvars); + dcvars->drawingmasked = 0; // POPE + } + } + dcvars->texturemid = basetexturemid; +} + +// +// R_DrawVisSprite +// mfloorclip and mceilingclip should also be set. +// +// CPhipps - new wad lump handling, *'s to const*'s +static void R_DrawVisSprite(vissprite_t *vis, int x1, int x2) +{ + int texturecolumn; + fixed_t frac; + const rpatch_t *patch = R_CachePatchNum(vis->patch+firstspritelump); + R_DrawColumn_f colfunc; + draw_column_vars_t dcvars; + enum draw_filter_type_e filter; + enum draw_filter_type_e filterz; + + R_SetDefaultDrawColumnVars(&dcvars); + if (vis->isplayersprite) { + dcvars.edgetype = drawvars.patch_edges; + filter = drawvars.filterpatch; + filterz = RDRAW_FILTER_POINT; + } else { + dcvars.edgetype = drawvars.sprite_edges; + filter = drawvars.filtersprite; + filterz = drawvars.filterz; + } + + dcvars.colormap = vis->colormap; + dcvars.nextcolormap = dcvars.colormap; // for filtering -- POPE + + // killough 4/11/98: rearrange and handle translucent sprites + // mixed with translucent/non-translucenct 2s normals + + if (!dcvars.colormap) // NULL colormap = shadow draw + colfunc = R_GetDrawColumnFunc(RDC_PIPELINE_FUZZ, filter, filterz); // killough 3/14/98 + else + if (vis->mobjflags & MF_TRANSLATION) + { + colfunc = R_GetDrawColumnFunc(RDC_PIPELINE_TRANSLATED, filter, filterz); + dcvars.translation = translationtables - 256 + + ((vis->mobjflags & MF_TRANSLATION) >> (MF_TRANSSHIFT-8) ); + } + else + if (vis->mobjflags & MF_TRANSLUCENT && general_translucency) // phares + { + colfunc = R_GetDrawColumnFunc(RDC_PIPELINE_TRANSLUCENT, filter, filterz); + tranmap = main_tranmap; // killough 4/11/98 + } + else + colfunc = R_GetDrawColumnFunc(RDC_PIPELINE_STANDARD, filter, filterz); // killough 3/14/98, 4/11/98 + +// proff 11/06/98: Changed for high-res + dcvars.iscale = FixedDiv (FRACUNIT, vis->scale); + dcvars.texturemid = vis->texturemid; + frac = vis->startfrac; + if (filter == RDRAW_FILTER_LINEAR) + frac -= (FRACUNIT>>1); + spryscale = vis->scale; + sprtopscreen = centeryfrac - FixedMul(dcvars.texturemid,spryscale); + + for (dcvars.x=vis->x1 ; dcvars.x<=vis->x2 ; dcvars.x++, frac += vis->xiscale) + { + texturecolumn = frac>>FRACBITS; + dcvars.texu = frac; + + R_DrawMaskedColumn( + patch, + colfunc, + &dcvars, + R_GetPatchColumnClamped(patch, texturecolumn), + R_GetPatchColumnClamped(patch, texturecolumn-1), + R_GetPatchColumnClamped(patch, texturecolumn+1) + ); + } + R_UnlockPatchNum(vis->patch+firstspritelump); // cph - release lump +} + +// +// R_ProjectSprite +// Generates a vissprite for a thing if it might be visible. +// + +static void R_ProjectSprite (mobj_t* thing, int lightlevel) +{ + fixed_t gzt; // killough 3/27/98 + fixed_t tx; + fixed_t xscale; + int x1; + int x2; + spritedef_t *sprdef; + spriteframe_t *sprframe; + int lump; + boolean flip; + vissprite_t *vis; + fixed_t iscale; + int heightsec; // killough 3/27/98 + + // transform the origin point + fixed_t tr_x, tr_y; + fixed_t fx, fy, fz; + fixed_t gxt, gyt; + fixed_t tz; + int width; + + if (movement_smooth) + { + fx = thing->PrevX + FixedMul (tic_vars.frac, thing->x - thing->PrevX); + fy = thing->PrevY + FixedMul (tic_vars.frac, thing->y - thing->PrevY); + fz = thing->PrevZ + FixedMul (tic_vars.frac, thing->z - thing->PrevZ); + } + else + { + fx = thing->x; + fy = thing->y; + fz = thing->z; + } + tr_x = fx - viewx; + tr_y = fy - viewy; + + gxt = FixedMul(tr_x,viewcos); + gyt = -FixedMul(tr_y,viewsin); + + tz = gxt-gyt; + + // thing is behind view plane? + if (tz < MINZ) + return; + + xscale = FixedDiv(projection, tz); + + gxt = -FixedMul(tr_x,viewsin); + gyt = FixedMul(tr_y,viewcos); + tx = -(gyt+gxt); + + // too far off the side? + if (D_abs(tx)>(tz<<2)) + return; + + // decide which patch to use for sprite relative to player +#ifdef RANGECHECK + if ((unsigned) thing->sprite >= (unsigned)numsprites) + I_Error ("R_ProjectSprite: Invalid sprite number %i", thing->sprite); +#endif + + sprdef = &sprites[thing->sprite]; + +#ifdef RANGECHECK + if ((thing->frame&FF_FRAMEMASK) >= sprdef->numframes) + I_Error ("R_ProjectSprite: Invalid sprite frame %i : %i", thing->sprite, + thing->frame); +#endif + + if (!sprdef->spriteframes) + I_Error ("R_ProjectSprite: Missing spriteframes %i : %i", thing->sprite, + thing->frame); + + sprframe = &sprdef->spriteframes[thing->frame & FF_FRAMEMASK]; + + if (sprframe->rotate) + { + // choose a different rotation based on player view + angle_t ang = R_PointToAngle(fx, fy); + unsigned rot = (ang-thing->angle+(unsigned)(ANG45/2)*9)>>29; + lump = sprframe->lump[rot]; + flip = (boolean) sprframe->flip[rot]; + } + else + { + // use single rotation for all views + lump = sprframe->lump[0]; + flip = (boolean) sprframe->flip[0]; + } + + { + const rpatch_t* patch = R_CachePatchNum(lump+firstspritelump); + + /* calculate edges of the shape + * cph 2003/08/1 - fraggle points out that this offset must be flipped + * if the sprite is flipped; e.g. FreeDoom imp is messed up by this. */ + if (flip) { + tx -= (patch->width - patch->leftoffset) << FRACBITS; + } else { + tx -= patch->leftoffset << FRACBITS; + } + x1 = (centerxfrac + FixedMul(tx,xscale)) >> FRACBITS; + + tx += patch->width<> FRACBITS) - 1; + + gzt = fz + (patch->topoffset << FRACBITS); + width = patch->width; + R_UnlockPatchNum(lump+firstspritelump); + } + + // off the side? + if (x1 > viewwidth || x2 < 0) + return; + + // killough 4/9/98: clip things which are out of view due to height + // e6y: fix of hanging decoration disappearing in Batman Doom MAP02 + // centeryfrac -> viewheightfrac + if (fz > viewz + FixedDiv(viewheightfrac, xscale) || + gzt < viewz - FixedDiv(viewheightfrac-viewheight, xscale)) + return; + + // killough 3/27/98: exclude things totally separated + // from the viewer, by either water or fake ceilings + // killough 4/11/98: improve sprite clipping for underwater/fake ceilings + + heightsec = thing->subsector->sector->heightsec; + + if (heightsec != -1) // only clip things which are in special sectors + { + int phs = viewplayer->mo->subsector->sector->heightsec; + if (phs != -1 && viewz < sectors[phs].floorheight ? + fz >= sectors[heightsec].floorheight : + gzt < sectors[heightsec].floorheight) + return; + if (phs != -1 && viewz > sectors[phs].ceilingheight ? + gzt < sectors[heightsec].ceilingheight && + viewz >= sectors[heightsec].ceilingheight : + fz >= sectors[heightsec].ceilingheight) + return; + } + + // store information in a vissprite + vis = R_NewVisSprite (); + +#ifdef GL_DOOM + if (V_GetMode() == VID_MODEGL) + { + // proff 11/99: add sprite for OpenGL + vis->thing = thing; + vis->flip = flip; + vis->scale = FixedDiv(projectiony, tz); + vis->patch = lump; + gld_AddSprite(vis); + + return; + } +#endif + // killough 3/27/98: save sector for special clipping later + vis->heightsec = heightsec; + + vis->mobjflags = thing->flags; +// proff 11/06/98: Changed for high-res + vis->scale = FixedDiv(projectiony, tz); + vis->gx = fx; + vis->gy = fy; + vis->gz = fz; + vis->gzt = gzt; // killough 3/27/98 + vis->texturemid = vis->gzt - viewz; + vis->x1 = x1 < 0 ? 0 : x1; + vis->x2 = x2 >= viewwidth ? viewwidth-1 : x2; + iscale = FixedDiv (FRACUNIT, xscale); + + if (flip) + { + vis->startfrac = (width<xiscale = -iscale; + } + else + { + vis->startfrac = 0; + vis->xiscale = iscale; + } + + if (vis->x1 > x1) + vis->startfrac += vis->xiscale*(vis->x1-x1); + vis->patch = lump; + + // get light level + if (thing->flags & MF_SHADOW) + vis->colormap = NULL; // shadow draw + else if (fixedcolormap) + vis->colormap = fixedcolormap; // fixed map + else if (thing->frame & FF_FULLBRIGHT) + vis->colormap = fullcolormap; // full bright // killough 3/20/98 + else + { // diminished light + vis->colormap = R_ColourMap(lightlevel,xscale); + } +} + +// +// R_AddSprites +// During BSP traversal, this adds sprites by sector. +// +// killough 9/18/98: add lightlevel as parameter, fixing underwater lighting +void R_AddSprites(subsector_t* subsec, int lightlevel) +{ + sector_t* sec=subsec->sector; + mobj_t *thing; + + // BSP is traversed by subsector. + // A sector might have been split into several + // subsectors during BSP building. + // Thus we check whether its already added. + + if (sec->validcount == validcount) + return; + + // Well, now it will be done. + sec->validcount = validcount; + + // Handle all things in sector. + + for (thing = sec->thinglist; thing; thing = thing->snext) + R_ProjectSprite(thing, lightlevel); +} + +// +// R_DrawPSprite +// + +static void R_DrawPSprite (pspdef_t *psp, int lightlevel) +{ + int x1, x2; + spritedef_t *sprdef; + spriteframe_t *sprframe; + int lump; + boolean flip; + vissprite_t *vis; + vissprite_t avis; + int width; + fixed_t topoffset; + + avis.isplayersprite = true; + + // decide which patch to use + +#ifdef RANGECHECK + if ( (unsigned)psp->state->sprite >= (unsigned)numsprites) + I_Error ("R_ProjectSprite: Invalid sprite number %i", psp->state->sprite); +#endif + + sprdef = &sprites[psp->state->sprite]; + +#ifdef RANGECHECK + if ( (psp->state->frame & FF_FRAMEMASK) >= sprdef->numframes) + I_Error ("R_ProjectSprite: Invalid sprite frame %i : %li", + psp->state->sprite, psp->state->frame); +#endif + + sprframe = &sprdef->spriteframes[psp->state->frame & FF_FRAMEMASK]; + + lump = sprframe->lump[0]; + flip = (boolean) sprframe->flip[0]; + + { + const rpatch_t* patch = R_CachePatchNum(lump+firstspritelump); + // calculate edges of the shape + fixed_t tx; + tx = psp->sx-160*FRACUNIT; + + tx -= patch->leftoffset<>FRACBITS; + + tx += patch->width<>FRACBITS) - 1; + + width = patch->width; + topoffset = patch->topoffset< viewwidth) + return; + + // store information in a vissprite + vis = &avis; + vis->mobjflags = 0; + // killough 12/98: fix psprite positioning problem + vis->texturemid = (BASEYCENTER<sy-topoffset); + vis->x1 = x1 < 0 ? 0 : x1; + vis->x2 = x2 >= viewwidth ? viewwidth-1 : x2; +// proff 11/06/98: Added for high-res + vis->scale = pspriteyscale; + + if (flip) + { + vis->xiscale = -pspriteiscale; + vis->startfrac = (width<xiscale = pspriteiscale; + vis->startfrac = 0; + } + + if (vis->x1 > x1) + vis->startfrac += vis->xiscale*(vis->x1-x1); + + vis->patch = lump; + + if (viewplayer->powers[pw_invisibility] > 4*32 + || viewplayer->powers[pw_invisibility] & 8) + vis->colormap = NULL; // shadow draw + else if (fixedcolormap) + vis->colormap = fixedcolormap; // fixed color + else if (psp->state->frame & FF_FULLBRIGHT) + vis->colormap = fullcolormap; // full bright // killough 3/20/98 + else + // add a fudge factor to better match the original game + vis->colormap = R_ColourMap(lightlevel, + FixedMul(pspritescale, 0x2b000)); // local light + + // proff 11/99: don't use software stuff in OpenGL + if (V_GetMode() != VID_MODEGL) + { + R_DrawVisSprite(vis, vis->x1, vis->x2); + } +#ifdef GL_DOOM + else + { + int lightlevel; + sector_t tmpsec; + int floorlightlevel, ceilinglightlevel; + + if ((vis->colormap==fixedcolormap) || (vis->colormap==fullcolormap)) + lightlevel=255; + else + { +// lightlevel = (viewplayer->mo->subsector->sector->lightlevel) + (extralight << LIGHTSEGSHIFT); + R_FakeFlat( viewplayer->mo->subsector->sector, &tmpsec, + &floorlightlevel, &ceilinglightlevel, false); + lightlevel = ((floorlightlevel+ceilinglightlevel) >> 1) + (extralight << LIGHTSEGSHIFT); + + if (lightlevel < 0) + lightlevel = 0; + else if (lightlevel >= 255) + lightlevel = 255; + } + gld_DrawWeapon(lump,vis,lightlevel); + } +#endif +} + +// +// R_DrawPlayerSprites +// + +void R_DrawPlayerSprites(void) +{ + int i, lightlevel = viewplayer->mo->subsector->sector->lightlevel; + pspdef_t *psp; + + // clip to screen bounds + mfloorclip = screenheightarray; + mceilingclip = negonearray; + + // add all active psprites + for (i=0, psp=viewplayer->psprites; istate) + R_DrawPSprite (psp, lightlevel); +} + +// +// R_SortVisSprites +// +// Rewritten by Lee Killough to avoid using unnecessary +// linked lists, and to use faster sorting algorithm. +// + +#ifdef DJGPP + +// killough 9/22/98: inlined memcpy of pointer arrays +// CPhipps - added memory as modified +#define bcopyp(d, s, n) asm(" cld; rep; movsl;" :: "D"(d), "S"(s), "c"(n) : "%cc", "%esi", "%edi", "%ecx", "memory") + +#else + +#define bcopyp(d, s, n) memcpy(d, s, (n) * sizeof(void *)) + +#endif + +// killough 9/2/98: merge sort + +static void msort(vissprite_t **s, vissprite_t **t, int n) +{ + if (n >= 16) + { + int n1 = n/2, n2 = n - n1; + vissprite_t **s1 = s, **s2 = s + n1, **d = t; + + msort(s1, t, n1); + msort(s2, t, n2); + + while ((*s1)->scale > (*s2)->scale ? + (*d++ = *s1++, --n1) : (*d++ = *s2++, --n2)); + + if (n2) + bcopyp(d, s2, n2); + else + bcopyp(d, s1, n1); + + bcopyp(s, t, n); + } + else + { + int i; + for (i = 1; i < n; i++) + { + vissprite_t *temp = s[i]; + if (s[i-1]->scale < temp->scale) + { + int j = i; + while ((s[j] = s[j-1])->scale < temp->scale && --j); + s[j] = temp; + } + } + } +} + +void R_SortVisSprites (void) +{ + if (num_vissprite) + { + int i = num_vissprite; + + // If we need to allocate more pointers for the vissprites, + // allocate as many as were allocated for sprites -- killough + // killough 9/22/98: allocate twice as many + + if (num_vissprite_ptrs < num_vissprite*2) + { + free(vissprite_ptrs); // better than realloc -- no preserving needed + vissprite_ptrs = malloc((num_vissprite_ptrs = num_vissprite_alloc*2) + * sizeof *vissprite_ptrs); + } + + while (--i>=0) + vissprite_ptrs[i] = vissprites+i; + + // killough 9/22/98: replace qsort with merge sort, since the keys + // are roughly in order to begin with, due to BSP rendering. + + msort(vissprite_ptrs, vissprite_ptrs + num_vissprite, num_vissprite); + } +} + +// +// R_DrawSprite +// + +static void R_DrawSprite (vissprite_t* spr) +{ + drawseg_t *ds; + int clipbot[MAX_SCREENWIDTH]; // killough 2/8/98: // dropoff overflow + int cliptop[MAX_SCREENWIDTH]; // change to MAX_* // dropoff overflow + int x; + int r1; + int r2; + fixed_t scale; + fixed_t lowscale; + + for (x = spr->x1 ; x<=spr->x2 ; x++) + clipbot[x] = cliptop[x] = -2; + + // Scan drawsegs from end to start for obscuring segs. + // The first drawseg that has a greater scale is the clip seg. + + // Modified by Lee Killough: + // (pointer check was originally nonportable + // and buggy, by going past LEFT end of array): + + // for (ds=ds_p-1 ; ds >= drawsegs ; ds--) old buggy code + + for (ds=ds_p ; ds-- > drawsegs ; ) // new -- killough + { // determine if the drawseg obscures the sprite + if (ds->x1 > spr->x2 || ds->x2 < spr->x1 || + (!ds->silhouette && !ds->maskedtexturecol)) + continue; // does not cover sprite + + r1 = ds->x1 < spr->x1 ? spr->x1 : ds->x1; + r2 = ds->x2 > spr->x2 ? spr->x2 : ds->x2; + + if (ds->scale1 > ds->scale2) + { + lowscale = ds->scale2; + scale = ds->scale1; + } + else + { + lowscale = ds->scale1; + scale = ds->scale2; + } + + if (scale < spr->scale || (lowscale < spr->scale && + !R_PointOnSegSide (spr->gx, spr->gy, ds->curline))) + { + if (ds->maskedtexturecol) // masked mid texture? + R_RenderMaskedSegRange(ds, r1, r2); + continue; // seg is behind sprite + } + + // clip this piece of the sprite + // killough 3/27/98: optimized and made much shorter + + if (ds->silhouette&SIL_BOTTOM && spr->gz < ds->bsilheight) //bottom sil + for (x=r1 ; x<=r2 ; x++) + if (clipbot[x] == -2) + clipbot[x] = ds->sprbottomclip[x]; + + if (ds->silhouette&SIL_TOP && spr->gzt > ds->tsilheight) // top sil + for (x=r1 ; x<=r2 ; x++) + if (cliptop[x] == -2) + cliptop[x] = ds->sprtopclip[x]; + } + + // killough 3/27/98: + // Clip the sprite against deep water and/or fake ceilings. + // killough 4/9/98: optimize by adding mh + // killough 4/11/98: improve sprite clipping for underwater/fake ceilings + // killough 11/98: fix disappearing sprites + + if (spr->heightsec != -1) // only things in specially marked sectors + { + fixed_t h,mh; + int phs = viewplayer->mo->subsector->sector->heightsec; + if ((mh = sectors[spr->heightsec].floorheight) > spr->gz && + (h = centeryfrac - FixedMul(mh-=viewz, spr->scale)) >= 0 && + (h >>= FRACBITS) < viewheight) { + if (mh <= 0 || (phs != -1 && viewz > sectors[phs].floorheight)) + { // clip bottom + for (x=spr->x1 ; x<=spr->x2 ; x++) + if (clipbot[x] == -2 || h < clipbot[x]) + clipbot[x] = h; + } + else // clip top + if (phs != -1 && viewz <= sectors[phs].floorheight) // killough 11/98 + for (x=spr->x1 ; x<=spr->x2 ; x++) + if (cliptop[x] == -2 || h > cliptop[x]) + cliptop[x] = h; + } + + if ((mh = sectors[spr->heightsec].ceilingheight) < spr->gzt && + (h = centeryfrac - FixedMul(mh-viewz, spr->scale)) >= 0 && + (h >>= FRACBITS) < viewheight) { + if (phs != -1 && viewz >= sectors[phs].ceilingheight) + { // clip bottom + for (x=spr->x1 ; x<=spr->x2 ; x++) + if (clipbot[x] == -2 || h < clipbot[x]) + clipbot[x] = h; + } + else // clip top + for (x=spr->x1 ; x<=spr->x2 ; x++) + if (cliptop[x] == -2 || h > cliptop[x]) + cliptop[x] = h; + } + } + // killough 3/27/98: end special clipping for deep water / fake ceilings + + // all clipping has been performed, so draw the sprite + // check for unclipped columns + + for (x = spr->x1 ; x<=spr->x2 ; x++) { + if (clipbot[x] == -2) + clipbot[x] = viewheight; + + if (cliptop[x] == -2) + cliptop[x] = -1; + } + + mfloorclip = clipbot; + mceilingclip = cliptop; + R_DrawVisSprite (spr, spr->x1, spr->x2); +} + +// +// R_DrawMasked +// + +void R_DrawMasked(void) +{ + int i; + drawseg_t *ds; + + R_SortVisSprites(); + + // draw all vissprites back to front + + rendered_vissprites = num_vissprite; + for (i = num_vissprite ;--i>=0; ) + R_DrawSprite(vissprite_ptrs[i]); // killough + + // render any remaining masked mid textures + + // Modified by Lee Killough: + // (pointer check was originally nonportable + // and buggy, by going past LEFT end of array): + + // for (ds=ds_p-1 ; ds >= drawsegs ; ds--) old buggy code + + for (ds=ds_p ; ds-- > drawsegs ; ) // new -- killough + if (ds->maskedtexturecol) + R_RenderMaskedSegRange(ds, ds->x1, ds->x2); + + // draw the psprites on top of everything + // but does not draw on side views + if (!viewangleoffset) + R_DrawPlayerSprites (); +} diff --git a/src/r_things.h b/src/r_things.h new file mode 100644 index 0000000..a6c504e --- /dev/null +++ b/src/r_things.h @@ -0,0 +1,72 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Rendering of moving objects, sprites. + * + *-----------------------------------------------------------------------------*/ + +#ifndef __R_THINGS__ +#define __R_THINGS__ + +#ifdef __GNUG__ +#pragma interface +#endif + +#include "r_draw.h" + +/* Constant arrays used for psprite clipping and initializing clipping. */ + +extern int negonearray[MAX_SCREENWIDTH]; /* killough 2/8/98: */ // dropoff overflow +extern int screenheightarray[MAX_SCREENWIDTH]; /* change to MAX_* */ // dropoff overflow + +/* Vars for R_DrawMaskedColumn */ + +extern int *mfloorclip; // dropoff overflow +extern int *mceilingclip; // dropoff overflow +extern fixed_t spryscale; +extern fixed_t sprtopscreen; +extern fixed_t pspritescale; +extern fixed_t pspriteiscale; +/* proff 11/06/98: Added for high-res */ +extern fixed_t pspriteyscale; + +void R_DrawMaskedColumn(const rpatch_t *patch, + R_DrawColumn_f colfunc, + draw_column_vars_t *dcvars, + const rcolumn_t *column, + const rcolumn_t *prevcolumn, + const rcolumn_t *nextcolumn); +void R_SortVisSprites(void); +void R_AddSprites(subsector_t* subsec, int lightlevel); +void R_DrawPlayerSprites(void); +void R_InitSprites(const char * const * namelist); +void R_ClearSprites(void); +void R_DrawMasked(void); + +#endif diff --git a/src/s_sound.c b/src/s_sound.c new file mode 100644 index 0000000..fa28e9a --- /dev/null +++ b/src/s_sound.c @@ -0,0 +1,688 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: Platform-independent sound code + * + *-----------------------------------------------------------------------------*/ + +// killough 3/7/98: modified to allow arbitrary listeners in spy mode +// killough 5/2/98: reindented, removed useless code, beautified + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "doomstat.h" +#include "s_sound.h" +#include "i_sound.h" +#include "i_system.h" +#include "d_main.h" +#include "r_main.h" +#include "m_random.h" +#include "w_wad.h" +#include "lprintf.h" + +// when to clip out sounds +// Does not fit the large outdoor areas. +#define S_CLIPPING_DIST (1200<>FRACBITS) + +// Adjustable by menu. +#define NORM_PITCH 128 +#define NORM_PRIORITY 64 +#define NORM_SEP 128 +#define S_STEREO_SWING (96<= prboom_2_compatibility && sfx_id == sfx_noway); // killough 4/25/98 + sfx_id &= ~PICKUP_SOUND; + + // check for bogus sound # + if (sfx_id < 1 || sfx_id > NUMSFX) + I_Error("S_StartSoundAtVolume: Bad sfx #: %d", sfx_id); + + sfx = &S_sfx[sfx_id]; + + // Initialize sound parameters + if (sfx->link) + { + pitch = sfx->pitch; + priority = sfx->priority; + volume += sfx->volume; + + if (volume < 1) + return; + + if (volume > snd_SfxVolume) + volume = snd_SfxVolume; + } + else + { + pitch = NORM_PITCH; + priority = NORM_PRIORITY; + } + + // Check to see if it is audible, modify the params + // killough 3/7/98, 4/25/98: code rearranged slightly + + if (!origin || origin == players[displayplayer].mo) { + sep = NORM_SEP; + volume *= 8; + } else + if (!S_AdjustSoundParams(players[displayplayer].mo, origin, &volume, + &sep, &pitch)) + return; + else + if ( origin->x == players[displayplayer].mo->x && + origin->y == players[displayplayer].mo->y) + sep = NORM_SEP; + + // hacks to vary the sfx pitches + if (sfx_id >= sfx_sawup && sfx_id <= sfx_sawhit) + pitch += 8 - (M_Random()&15); + else + if (sfx_id != sfx_itemup && sfx_id != sfx_tink) + pitch += 16 - (M_Random()&31); + + if (pitch<0) + pitch = 0; + + if (pitch>255) + pitch = 255; + + // kill old sound + for (cnum=0 ; cnumlumpnum < 0 && (sfx->lumpnum = I_GetSfxLumpNum(sfx)) < 0) + return; + + // increase the usefulness + if (sfx->usefulness++ < 0) + sfx->usefulness = 1; + + // Assigns the handle to one of the channels in the mix/output buffer. + { // e6y: [Fix] Crash with zero-length sounds. + int h = I_StartSound(sfx_id, cnum, volume, sep, pitch, priority); + if (h != -1) channels[cnum].handle = h; + } +} + +void S_StartSound(void *origin, int sfx_id) +{ + S_StartSoundAtVolume(origin, sfx_id, snd_SfxVolume); +} + +void S_StopSound(void *origin) +{ + int cnum; + + //jff 1/22/98 return if sound is not enabled + if (!snd_card || nosfxparm) + return; + + for (cnum=0 ; cnumhandle); + mus_paused = true; + } +} + +void S_ResumeSound(void) +{ + //jff 1/22/98 return if music is not enabled + if (!mus_card || nomusicparm) + return; + + if (mus_playing && mus_paused) + { + I_ResumeSong(mus_playing->handle); + mus_paused = false; + } +} + + +// +// Updates music & sounds +// +void S_UpdateSounds(void* listener_p) +{ + mobj_t *listener = (mobj_t*) listener_p; + int cnum; + + //jff 1/22/98 return if sound is not enabled + if (!snd_card || nosfxparm) + return; + +#ifdef UPDATE_MUSIC + I_UpdateMusic(); +#endif + + for (cnum=0 ; cnumsfxinfo)) + { + if (I_SoundIsPlaying(c->handle)) + { + // initialize parameters + int volume = snd_SfxVolume; + int pitch = NORM_PITCH; + int sep = NORM_SEP; + + if (sfx->link) + { + pitch = sfx->pitch; + volume += sfx->volume; + if (volume < 1) + { + S_StopChannel(cnum); + continue; + } + else + if (volume > snd_SfxVolume) + volume = snd_SfxVolume; + } + + // check non-local sounds for distance clipping + // or modify their params + if (c->origin && listener_p != c->origin) { // killough 3/20/98 + if (!S_AdjustSoundParams(listener, c->origin, + &volume, &sep, &pitch)) + S_StopChannel(cnum); + else + I_UpdateSoundParams(c->handle, volume, sep, pitch); + } + } + else // if channel is allocated but sound has stopped, free it + S_StopChannel(cnum); + } + } +} + + + +void S_SetMusicVolume(int volume) +{ + //jff 1/22/98 return if music is not enabled + if (!mus_card || nomusicparm) + return; + if (volume < 0 || volume > 15) + I_Error("S_SetMusicVolume: Attempt to set music volume at %d", volume); + I_SetMusicVolume(volume); + snd_MusicVolume = volume; +} + + + +void S_SetSfxVolume(int volume) +{ + //jff 1/22/98 return if sound is not enabled + if (!snd_card || nosfxparm) + return; + if (volume < 0 || volume > 127) + I_Error("S_SetSfxVolume: Attempt to set sfx volume at %d", volume); + snd_SfxVolume = volume; +} + + + +// Starts some music with the music id found in sounds.h. +// +void S_StartMusic(int m_id) +{ + //jff 1/22/98 return if music is not enabled + if (!mus_card || nomusicparm) + return; + S_ChangeMusic(m_id, false); +} + + + +void S_ChangeMusic(int musicnum, int looping) +{ + musicinfo_t *music; + int music_file_failed; // cournia - if true load the default MIDI music + char* music_filename; // cournia + + //jff 1/22/98 return if music is not enabled + if (!mus_card || nomusicparm) + return; + + if (musicnum <= mus_None || musicnum >= NUMMUSIC) + I_Error("S_ChangeMusic: Bad music number %d", musicnum); + + music = &S_music[musicnum]; + + if (mus_playing == music) + return; + + // shutdown old music + S_StopMusic(); + + // get lumpnum if neccessary + if (!music->lumpnum) + { + char namebuf[9]; + sprintf(namebuf, "d_%s", music->name); + music->lumpnum = W_GetNumForName(namebuf); + } + + music_file_failed = 1; + + // proff_fs - only load when from IWAD + if (lumpinfo[music->lumpnum].source == source_iwad) + { + // cournia - check to see if we can play a higher quality music file + // rather than the default MIDI + music_filename = I_FindFile(S_music_files[musicnum], ""); + if (music_filename) + { + music_file_failed = I_RegisterMusic(music_filename, music); + free(music_filename); + } + } + + if (music_file_failed) + { + //cournia - could not load music file, play default MIDI music + + // load & register it + music->data = W_CacheLumpNum(music->lumpnum); + music->handle = I_RegisterSong(music->data, W_LumpLength(music->lumpnum)); + } + + // play it + I_PlaySong(music->handle, looping); + + mus_playing = music; +} + + +void S_StopMusic(void) +{ + //jff 1/22/98 return if music is not enabled + if (!mus_card || nomusicparm) + return; + + if (mus_playing) + { + if (mus_paused) + I_ResumeSong(mus_playing->handle); + + I_StopSong(mus_playing->handle); + I_UnRegisterSong(mus_playing->handle); + if (mus_playing->lumpnum >= 0) + W_UnlockLumpNum(mus_playing->lumpnum); // cph - release the music data + + mus_playing->data = 0; + mus_playing = 0; + } +} + + + +void S_StopChannel(int cnum) +{ + int i; + channel_t *c = &channels[cnum]; + + //jff 1/22/98 return if sound is not enabled + if (!snd_card || nosfxparm) + return; + + if (c->sfxinfo) + { + // stop the sound playing + if (I_SoundIsPlaying(c->handle)) + I_StopSound(c->handle); + + // check to see + // if other channels are playing the sound + for (i=0 ; isfxinfo == channels[i].sfxinfo) + break; + + // degrade usefulness of sound data + c->sfxinfo->usefulness--; + c->sfxinfo = 0; + } +} + +// +// Changes volume, stereo-separation, and pitch variables +// from the norm of a sound effect to be played. +// If the sound is not audible, returns a 0. +// Otherwise, modifies parameters and returns 1. +// + +int S_AdjustSoundParams(mobj_t *listener, mobj_t *source, + int *vol, int *sep, int *pitch) +{ + fixed_t adx, ady,approx_dist; + angle_t angle; + + //jff 1/22/98 return if sound is not enabled + if (!snd_card || nosfxparm) + return 0; + + // e6y + // Fix crash when the program wants to S_AdjustSoundParams() for player + // which is not displayplayer and displayplayer was not spawned at the moment. + // It happens in multiplayer demos only. + // + // Stack trace is: + // P_SetupLevel() \ P_LoadThings() \ P_SpawnMapThing() \ P_SpawnPlayer(players[0]) \ + // P_SetupPsprites() \ P_BringUpWeapon() \ S_StartSound(players[0]->mo, sfx_sawup) \ + // S_StartSoundAtVolume() \ S_AdjustSoundParams(players[displayplayer]->mo, ...); + // players[displayplayer]->mo is NULL + // + // There is no more crash on e1cmnet3.lmp between e1m2 and e1m3 + // http://competn.doom2.net/pub/compet-n/doom/coop/movies/e1cmnet3.zip + if (!listener) + return 0; + + // calculate the distance to sound origin + // and clip it if necessary + adx = D_abs(listener->x - source->x); + ady = D_abs(listener->y - source->y); + + // From _GG1_ p.428. Appox. eucledian distance fast. + approx_dist = adx + ady - ((adx < ady ? adx : ady)>>1); + + if (!approx_dist) // killough 11/98: handle zero-distance as special case + { + *sep = NORM_SEP; + *vol = snd_SfxVolume; + return *vol > 0; + } + + if (approx_dist > S_CLIPPING_DIST) + return 0; + + // angle of source to listener + angle = R_PointToAngle2(listener->x, listener->y, source->x, source->y); + + if (angle <= listener->angle) + angle += 0xffffffff; + angle -= listener->angle; + angle >>= ANGLETOFINESHIFT; + + // stereo separation + *sep = 128 - (FixedMul(S_STEREO_SWING,finesine[angle])>>FRACBITS); + + // volume calculation + if (approx_dist < S_CLOSE_DIST) + *vol = snd_SfxVolume*8; + else + // distance effect + *vol = (snd_SfxVolume * ((S_CLIPPING_DIST-approx_dist)>>FRACBITS) * 8) + / S_ATTENUATOR; + + return (*vol > 0); +} + +// +// S_getChannel : +// If none available, return -1. Otherwise channel #. +// +// killough 4/25/98: made static, added is_pickup argument + +static int S_getChannel(void *origin, sfxinfo_t *sfxinfo, int is_pickup) +{ + // channel number to use + int cnum; + channel_t *c; + + //jff 1/22/98 return if sound is not enabled + if (!snd_card || nosfxparm) + return -1; + + // Find an open channel + for (cnum=0; cnumpriority >= sfxinfo->priority) + break; + if (cnum == numChannels) + return -1; // No lower priority. Sorry, Charlie. + else + S_StopChannel(cnum); // Otherwise, kick out lower priority. + } + + c = &channels[cnum]; // channel is decided to be cnum. + c->sfxinfo = sfxinfo; + c->origin = origin; + c->is_pickup = is_pickup; // killough 4/25/98 + return cnum; +} diff --git a/src/s_sound.h b/src/s_sound.h new file mode 100644 index 0000000..29d3219 --- /dev/null +++ b/src/s_sound.h @@ -0,0 +1,100 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * The not so system specific sound interface. + * + *-----------------------------------------------------------------------------*/ + +#ifndef __S_SOUND__ +#define __S_SOUND__ + +#ifdef __GNUG__ +#pragma interface +#endif + +// +// Initializes sound stuff, including volume +// Sets channels, SFX and music volume, +// allocates channel buffer, sets S_sfx lookup. +// +void S_Init(int sfxVolume, int musicVolume); + +// Kills all sounds +void S_Stop(void); + +// +// Per level startup code. +// Kills playing sounds at start of level, +// determines music if any, changes music. +// +void S_Start(void); + +// +// Start sound for thing at +// using from sounds.h +// +void S_StartSound(void *origin, int sound_id); + +// Will start a sound at a given volume. +void S_StartSoundAtVolume(void *origin, int sound_id, int volume); + +// killough 4/25/98: mask used to indicate sound origin is player item pickup +#define PICKUP_SOUND (0x8000) + +// Stop sound for thing at +void S_StopSound(void* origin); + +// Start music using from sounds.h +void S_StartMusic(int music_id); + +// Start music using from sounds.h, and set whether looping +void S_ChangeMusic(int music_id, int looping); + +// Stops the music fer sure. +void S_StopMusic(void); + +// Stop and resume music, during game PAUSE. +void S_PauseSound(void); +void S_ResumeSound(void); + +// +// Updates music & sounds +// +void S_UpdateSounds(void* listener); +void S_SetMusicVolume(int volume); +void S_SetSfxVolume(int volume); + +// machine-independent sound params +extern int default_numChannels; +extern int numChannels; + +//jff 3/17/98 holds last IDMUS number, or -1 +extern int idmusnum; + +#endif diff --git a/src/sounds.c b/src/sounds.c new file mode 100644 index 0000000..cc68a3c --- /dev/null +++ b/src/sounds.c @@ -0,0 +1,245 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Created by a sound utility. + * Kept as a sample, DOOM2 sounds. + * + *-----------------------------------------------------------------------------*/ + +// killough 5/3/98: reformatted + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "doomtype.h" +#include "sounds.h" + +// +// Information about all the music +// + +musicinfo_t S_music[] = { + { 0 }, + { "e1m1", 0 }, + { "e1m2", 0 }, + { "e1m3", 0 }, + { "e1m4", 0 }, + { "e1m5", 0 }, + { "e1m6", 0 }, + { "e1m7", 0 }, + { "e1m8", 0 }, + { "e1m9", 0 }, + { "e2m1", 0 }, + { "e2m2", 0 }, + { "e2m3", 0 }, + { "e2m4", 0 }, + { "e2m5", 0 }, + { "e2m6", 0 }, + { "e2m7", 0 }, + { "e2m8", 0 }, + { "e2m9", 0 }, + { "e3m1", 0 }, + { "e3m2", 0 }, + { "e3m3", 0 }, + { "e3m4", 0 }, + { "e3m5", 0 }, + { "e3m6", 0 }, + { "e3m7", 0 }, + { "e3m8", 0 }, + { "e3m9", 0 }, + { "inter", 0 }, + { "intro", 0 }, + { "bunny", 0 }, + { "victor", 0 }, + { "introa", 0 }, + { "runnin", 0 }, + { "stalks", 0 }, + { "countd", 0 }, + { "betwee", 0 }, + { "doom", 0 }, + { "the_da", 0 }, + { "shawn", 0 }, + { "ddtblu", 0 }, + { "in_cit", 0 }, + { "dead", 0 }, + { "stlks2", 0 }, + { "theda2", 0 }, + { "doom2", 0 }, + { "ddtbl2", 0 }, + { "runni2", 0 }, + { "dead2", 0 }, + { "stlks3", 0 }, + { "romero", 0 }, + { "shawn2", 0 }, + { "messag", 0 }, + { "count2", 0 }, + { "ddtbl3", 0 }, + { "ampie", 0 }, + { "theda3", 0 }, + { "adrian", 0 }, + { "messg2", 0 }, + { "romer2", 0 }, + { "tense", 0 }, + { "shawn3", 0 }, + { "openin", 0 }, + { "evil", 0 }, + { "ultima", 0 }, + { "read_m", 0 }, + { "dm2ttl", 0 }, + { "dm2int", 0 }, +}; + + +// +// Information about all the sfx +// + +sfxinfo_t S_sfx[] = { + // S_sfx[0] needs to be a dummy for odd reasons. + { "none", false, 0, 0, -1, -1, 0 }, + + { "pistol", false, 64, 0, -1, -1, 0 }, + { "shotgn", false, 64, 0, -1, -1, 0 }, + { "sgcock", false, 64, 0, -1, -1, 0 }, + { "dshtgn", false, 64, 0, -1, -1, 0 }, + { "dbopn", false, 64, 0, -1, -1, 0 }, + { "dbcls", false, 64, 0, -1, -1, 0 }, + { "dbload", false, 64, 0, -1, -1, 0 }, + { "plasma", false, 64, 0, -1, -1, 0 }, + { "bfg", false, 64, 0, -1, -1, 0 }, + { "sawup", false, 64, 0, -1, -1, 0 }, + { "sawidl", false, 118, 0, -1, -1, 0 }, + { "sawful", false, 64, 0, -1, -1, 0 }, + { "sawhit", false, 64, 0, -1, -1, 0 }, + { "rlaunc", false, 64, 0, -1, -1, 0 }, + { "rxplod", false, 70, 0, -1, -1, 0 }, + { "firsht", false, 70, 0, -1, -1, 0 }, + { "firxpl", false, 70, 0, -1, -1, 0 }, + { "pstart", false, 100, 0, -1, -1, 0 }, + { "pstop", false, 100, 0, -1, -1, 0 }, + { "doropn", false, 100, 0, -1, -1, 0 }, + { "dorcls", false, 100, 0, -1, -1, 0 }, + { "stnmov", false, 119, 0, -1, -1, 0 }, + { "swtchn", false, 78, 0, -1, -1, 0 }, + { "swtchx", false, 78, 0, -1, -1, 0 }, + { "plpain", false, 96, 0, -1, -1, 0 }, + { "dmpain", false, 96, 0, -1, -1, 0 }, + { "popain", false, 96, 0, -1, -1, 0 }, + { "vipain", false, 96, 0, -1, -1, 0 }, + { "mnpain", false, 96, 0, -1, -1, 0 }, + { "pepain", false, 96, 0, -1, -1, 0 }, + { "slop", false, 78, 0, -1, -1, 0 }, + { "itemup", true, 78, 0, -1, -1, 0 }, + { "wpnup", true, 78, 0, -1, -1, 0 }, + { "oof", false, 96, 0, -1, -1, 0 }, + { "telept", false, 32, 0, -1, -1, 0 }, + { "posit1", true, 98, 0, -1, -1, 0 }, + { "posit2", true, 98, 0, -1, -1, 0 }, + { "posit3", true, 98, 0, -1, -1, 0 }, + { "bgsit1", true, 98, 0, -1, -1, 0 }, + { "bgsit2", true, 98, 0, -1, -1, 0 }, + { "sgtsit", true, 98, 0, -1, -1, 0 }, + { "cacsit", true, 98, 0, -1, -1, 0 }, + { "brssit", true, 94, 0, -1, -1, 0 }, + { "cybsit", true, 92, 0, -1, -1, 0 }, + { "spisit", true, 90, 0, -1, -1, 0 }, + { "bspsit", true, 90, 0, -1, -1, 0 }, + { "kntsit", true, 90, 0, -1, -1, 0 }, + { "vilsit", true, 90, 0, -1, -1, 0 }, + { "mansit", true, 90, 0, -1, -1, 0 }, + { "pesit", true, 90, 0, -1, -1, 0 }, + { "sklatk", false, 70, 0, -1, -1, 0 }, + { "sgtatk", false, 70, 0, -1, -1, 0 }, + { "skepch", false, 70, 0, -1, -1, 0 }, + { "vilatk", false, 70, 0, -1, -1, 0 }, + { "claw", false, 70, 0, -1, -1, 0 }, + { "skeswg", false, 70, 0, -1, -1, 0 }, + { "pldeth", false, 32, 0, -1, -1, 0 }, + { "pdiehi", false, 32, 0, -1, -1, 0 }, + { "podth1", false, 70, 0, -1, -1, 0 }, + { "podth2", false, 70, 0, -1, -1, 0 }, + { "podth3", false, 70, 0, -1, -1, 0 }, + { "bgdth1", false, 70, 0, -1, -1, 0 }, + { "bgdth2", false, 70, 0, -1, -1, 0 }, + { "sgtdth", false, 70, 0, -1, -1, 0 }, + { "cacdth", false, 70, 0, -1, -1, 0 }, + { "skldth", false, 70, 0, -1, -1, 0 }, + { "brsdth", false, 32, 0, -1, -1, 0 }, + { "cybdth", false, 32, 0, -1, -1, 0 }, + { "spidth", false, 32, 0, -1, -1, 0 }, + { "bspdth", false, 32, 0, -1, -1, 0 }, + { "vildth", false, 32, 0, -1, -1, 0 }, + { "kntdth", false, 32, 0, -1, -1, 0 }, + { "pedth", false, 32, 0, -1, -1, 0 }, + { "skedth", false, 32, 0, -1, -1, 0 }, + { "posact", true, 120, 0, -1, -1, 0 }, + { "bgact", true, 120, 0, -1, -1, 0 }, + { "dmact", true, 120, 0, -1, -1, 0 }, + { "bspact", true, 100, 0, -1, -1, 0 }, + { "bspwlk", true, 100, 0, -1, -1, 0 }, + { "vilact", true, 100, 0, -1, -1, 0 }, + { "noway", false, 78, 0, -1, -1, 0 }, + { "barexp", false, 60, 0, -1, -1, 0 }, + { "punch", false, 64, 0, -1, -1, 0 }, + { "hoof", false, 70, 0, -1, -1, 0 }, + { "metal", false, 70, 0, -1, -1, 0 }, + { "chgun", false, 64, &S_sfx[sfx_pistol], 150, 0, 0 }, + { "tink", false, 60, 0, -1, -1, 0 }, + { "bdopn", false, 100, 0, -1, -1, 0 }, + { "bdcls", false, 100, 0, -1, -1, 0 }, + { "itmbk", false, 100, 0, -1, -1, 0 }, + { "flame", false, 32, 0, -1, -1, 0 }, + { "flamst", false, 32, 0, -1, -1, 0 }, + { "getpow", false, 60, 0, -1, -1, 0 }, + { "bospit", false, 70, 0, -1, -1, 0 }, + { "boscub", false, 70, 0, -1, -1, 0 }, + { "bossit", false, 70, 0, -1, -1, 0 }, + { "bospn", false, 70, 0, -1, -1, 0 }, + { "bosdth", false, 70, 0, -1, -1, 0 }, + { "manatk", false, 70, 0, -1, -1, 0 }, + { "mandth", false, 70, 0, -1, -1, 0 }, + { "sssit", false, 70, 0, -1, -1, 0 }, + { "ssdth", false, 70, 0, -1, -1, 0 }, + { "keenpn", false, 70, 0, -1, -1, 0 }, + { "keendt", false, 70, 0, -1, -1, 0 }, + { "skeact", false, 70, 0, -1, -1, 0 }, + { "skesit", false, 70, 0, -1, -1, 0 }, + { "skeatk", false, 70, 0, -1, -1, 0 }, + { "radio", false, 60, 0, -1, -1, 0 }, + +#ifdef DOGS + // killough 11/98: dog sounds + { "dgsit", false, 98, 0, -1, -1, 0 }, + { "dgatk", false, 70, 0, -1, -1, 0 }, + { "dgact", false, 120, 0, -1, -1, 0 }, + { "dgdth", false, 70, 0, -1, -1, 0 }, + { "dgpain", false, 96, 0, -1, -1, 0 }, +#endif +}; diff --git a/src/sounds.h b/src/sounds.h new file mode 100644 index 0000000..0f82333 --- /dev/null +++ b/src/sounds.h @@ -0,0 +1,305 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Created by the sound utility written by Dave Taylor. + * Kept as a sample, DOOM2 sounds. Frozen. + * + *-----------------------------------------------------------------------------*/ + +#ifndef __SOUNDS__ +#define __SOUNDS__ + +// +// SoundFX struct. +// + +struct sfxinfo_struct; + +typedef struct sfxinfo_struct sfxinfo_t; + +struct sfxinfo_struct { + + // up to 6-character name + const char *name; // CPhipps - const + + // Sfx singularity (only one at a time) + int singularity; + + // Sfx priority + int priority; + + // referenced sound if a link + sfxinfo_t *link; + + // pitch if a link + int pitch; + + // volume if a link + int volume; + + // sound data + void *data; + + // this is checked every second to see if sound + // can be thrown out (if 0, then decrement, if -1, + // then throw out, if > 0, then it is in use) + int usefulness; + + // lump number of sfx + int lumpnum; +}; + +// +// MusicInfo struct. +// + +typedef struct { + // up to 6-character name + const char *name; // CPhipps - const + + // lump number of music + int lumpnum; + + /* music data - cphipps 4/11 made const void* */ + const void *data; + + // music handle once registered + int handle; +} musicinfo_t; + +// the complete set of sound effects +extern sfxinfo_t S_sfx[]; + +// the complete set of music +extern musicinfo_t S_music[]; + +// +// Identifiers for all music in game. +// + +typedef enum { + mus_None, + mus_e1m1, + mus_e1m2, + mus_e1m3, + mus_e1m4, + mus_e1m5, + mus_e1m6, + mus_e1m7, + mus_e1m8, + mus_e1m9, + mus_e2m1, + mus_e2m2, + mus_e2m3, + mus_e2m4, + mus_e2m5, + mus_e2m6, + mus_e2m7, + mus_e2m8, + mus_e2m9, + mus_e3m1, + mus_e3m2, + mus_e3m3, + mus_e3m4, + mus_e3m5, + mus_e3m6, + mus_e3m7, + mus_e3m8, + mus_e3m9, + mus_inter, + mus_intro, + mus_bunny, + mus_victor, + mus_introa, + mus_runnin, + mus_stalks, + mus_countd, + mus_betwee, + mus_doom, + mus_the_da, + mus_shawn, + mus_ddtblu, + mus_in_cit, + mus_dead, + mus_stlks2, + mus_theda2, + mus_doom2, + mus_ddtbl2, + mus_runni2, + mus_dead2, + mus_stlks3, + mus_romero, + mus_shawn2, + mus_messag, + mus_count2, + mus_ddtbl3, + mus_ampie, + mus_theda3, + mus_adrian, + mus_messg2, + mus_romer2, + mus_tense, + mus_shawn3, + mus_openin, + mus_evil, + mus_ultima, + mus_read_m, + mus_dm2ttl, + mus_dm2int, + NUMMUSIC +} musicenum_t; + +// +// Identifiers for all sfx in game. +// + +typedef enum { + sfx_None, + sfx_pistol, + sfx_shotgn, + sfx_sgcock, + sfx_dshtgn, + sfx_dbopn, + sfx_dbcls, + sfx_dbload, + sfx_plasma, + sfx_bfg, + sfx_sawup, + sfx_sawidl, + sfx_sawful, + sfx_sawhit, + sfx_rlaunc, + sfx_rxplod, + sfx_firsht, + sfx_firxpl, + sfx_pstart, + sfx_pstop, + sfx_doropn, + sfx_dorcls, + sfx_stnmov, + sfx_swtchn, + sfx_swtchx, + sfx_plpain, + sfx_dmpain, + sfx_popain, + sfx_vipain, + sfx_mnpain, + sfx_pepain, + sfx_slop, + sfx_itemup, + sfx_wpnup, + sfx_oof, + sfx_telept, + sfx_posit1, + sfx_posit2, + sfx_posit3, + sfx_bgsit1, + sfx_bgsit2, + sfx_sgtsit, + sfx_cacsit, + sfx_brssit, + sfx_cybsit, + sfx_spisit, + sfx_bspsit, + sfx_kntsit, + sfx_vilsit, + sfx_mansit, + sfx_pesit, + sfx_sklatk, + sfx_sgtatk, + sfx_skepch, + sfx_vilatk, + sfx_claw, + sfx_skeswg, + sfx_pldeth, + sfx_pdiehi, + sfx_podth1, + sfx_podth2, + sfx_podth3, + sfx_bgdth1, + sfx_bgdth2, + sfx_sgtdth, + sfx_cacdth, + sfx_skldth, + sfx_brsdth, + sfx_cybdth, + sfx_spidth, + sfx_bspdth, + sfx_vildth, + sfx_kntdth, + sfx_pedth, + sfx_skedth, + sfx_posact, + sfx_bgact, + sfx_dmact, + sfx_bspact, + sfx_bspwlk, + sfx_vilact, + sfx_noway, + sfx_barexp, + sfx_punch, + sfx_hoof, + sfx_metal, + sfx_chgun, + sfx_tink, + sfx_bdopn, + sfx_bdcls, + sfx_itmbk, + sfx_flame, + sfx_flamst, + sfx_getpow, + sfx_bospit, + sfx_boscub, + sfx_bossit, + sfx_bospn, + sfx_bosdth, + sfx_manatk, + sfx_mandth, + sfx_sssit, + sfx_ssdth, + sfx_keenpn, + sfx_keendt, + sfx_skeact, + sfx_skesit, + sfx_skeatk, + sfx_radio, + +#ifdef DOGS + /* killough 11/98: dog sounds */ + sfx_dgsit, + sfx_dgatk, + sfx_dgact, + sfx_dgdth, + sfx_dgpain, +#endif + + NUMSFX +} sfxenum_t; + +#endif diff --git a/src/st_lib.c b/src/st_lib.c new file mode 100644 index 0000000..87504d4 --- /dev/null +++ b/src/st_lib.c @@ -0,0 +1,374 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * The status bar widget code. + * + *-----------------------------------------------------------------------------*/ + +#include "doomdef.h" +#include "doomstat.h" +#include "v_video.h" +#include "w_wad.h" +#include "st_stuff.h" +#include "st_lib.h" +#include "r_main.h" +#include "lprintf.h" + +int sts_always_red; //jff 2/18/98 control to disable status color changes +int sts_pct_always_gray; // killough 2/21/98: always gray %'s? bug or feature? + +// +// STlib_init() +// +void STlib_init(void) +{ + // cph - no longer hold STMINUS pointer +} + +// +// STlib_initNum() +// +// Initializes an st_number_t widget +// +// Passed the widget, its position, the patches for the digits, a pointer +// to the value displayed, a pointer to the on/off control, and the width +// Returns nothing +// +void STlib_initNum +( st_number_t* n, + int x, + int y, + const patchnum_t* pl, + int* num, + boolean* on, + int width ) +{ + n->x = x; + n->y = y; + n->oldnum = 0; + n->width = width; + n->num = num; + n->on = on; + n->p = pl; +} + +/* + * STlib_drawNum() + * + * A fairly efficient way to draw a number based on differences from the + * old number. + * + * Passed a st_number_t widget, a color range for output, and a flag + * indicating whether refresh is needed. + * Returns nothing + * + * jff 2/16/98 add color translation to digit output + * cphipps 10/99 - const pointer to colour trans table, made function static + */ +static void STlib_drawNum +( st_number_t* n, + int cm, + boolean refresh ) +{ + + int numdigits = n->width; + int num = *n->num; + + int w = n->p[0].width; + int h = n->p[0].height; + int x = n->x; + + int neg; + + // leban 1/20/99: + // strange that somebody went through all the work to draw only the + // differences, and then went and constantly redrew all the numbers. + // return without drawing if the number didn't change and the bar + // isn't refreshing. + if(n->oldnum == num && !refresh) + return; + + // CPhipps - compact some code, use num instead of *n->num + if ((neg = (n->oldnum = num) < 0)) + { + if (numdigits == 2 && num < -9) + num = -9; + else if (numdigits == 3 && num < -99) + num = -99; + + num = -num; + } + + // clear the area + x = n->x - numdigits*w; + +#ifdef RANGECHECK + if (n->y - ST_Y < 0) + I_Error("STlib_drawNum: n->y - ST_Y < 0"); +#endif + + V_CopyRect(x, n->y - ST_Y, BG, w*numdigits, h, x, n->y, FG, VPT_STRETCH); + + // if non-number, do not draw it + if (num == 1994) + return; + + x = n->x; + + //jff 2/16/98 add color translation to digit output + // in the special case of 0, you draw 0 + if (!num) + // CPhipps - patch drawing updated, reformatted + V_DrawNumPatch(x - w, n->y, FG, n->p[0].lumpnum, cm, + (((cm!=CR_DEFAULT) && !sts_always_red) ? VPT_TRANS : VPT_NONE) | VPT_STRETCH); + + // draw the new number + //jff 2/16/98 add color translation to digit output + while (num && numdigits--) { + // CPhipps - patch drawing updated, reformatted + x -= w; + V_DrawNumPatch(x, n->y, FG, n->p[num % 10].lumpnum, cm, + (((cm!=CR_DEFAULT) && !sts_always_red) ? VPT_TRANS : VPT_NONE) | VPT_STRETCH); + num /= 10; + } + + // draw a minus sign if necessary + //jff 2/16/98 add color translation to digit output + // cph - patch drawing updated, load by name instead of acquiring pointer earlier + if (neg) + V_DrawNamePatch(x - w, n->y, FG, "STTMINUS", cm, + (((cm!=CR_DEFAULT) && !sts_always_red) ? VPT_TRANS : VPT_NONE) | VPT_STRETCH); +} + +/* + * STlib_updateNum() + * + * Draws a number conditionally based on the widget's enable + * + * Passed a number widget, the output color range, and a refresh flag + * Returns nothing + * + * jff 2/16/98 add color translation to digit output + * cphipps 10/99 - make that pointer const + */ +void STlib_updateNum +( st_number_t* n, + int cm, + boolean refresh ) +{ + if (*n->on) STlib_drawNum(n, cm, refresh); +} + +// +// STlib_initPercent() +// +// Initialize a st_percent_t number with percent sign widget +// +// Passed a st_percent_t widget, the position, the digit patches, a pointer +// to the number to display, a pointer to the enable flag, and patch +// for the percent sign. +// Returns nothing. +// +void STlib_initPercent +( st_percent_t* p, + int x, + int y, + const patchnum_t* pl, + int* num, + boolean* on, + const patchnum_t* percent ) +{ + STlib_initNum(&p->n, x, y, pl, num, on, 3); + p->p = percent; +} + +/* + * STlib_updatePercent() + * + * Draws a number/percent conditionally based on the widget's enable + * + * Passed a precent widget, the output color range, and a refresh flag + * Returns nothing + * + * jff 2/16/98 add color translation to digit output + * cphipps - const for pointer to the colour translation table + */ + +void STlib_updatePercent +( st_percent_t* per, + int cm, + int refresh ) +{ + if (*per->n.on && (refresh || (per->n.oldnum != *per->n.num))) { + // killough 2/21/98: fix percents not updated; + /* CPhipps - make %'s only be updated if number changed */ + // CPhipps - patch drawing updated + V_DrawNumPatch(per->n.x, per->n.y, FG, per->p->lumpnum, + sts_pct_always_gray ? CR_GRAY : cm, + (sts_always_red ? VPT_NONE : VPT_TRANS) | VPT_STRETCH); + } + + STlib_updateNum(&per->n, cm, refresh); +} + +// +// STlib_initMultIcon() +// +// Initialize a st_multicon_t widget, used for a multigraphic display +// like the status bar's keys. +// +// Passed a st_multicon_t widget, the position, the graphic patches, a pointer +// to the numbers representing what to display, and pointer to the enable flag +// Returns nothing. +// +void STlib_initMultIcon +( st_multicon_t* i, + int x, + int y, + const patchnum_t* il, + int* inum, + boolean* on ) +{ + i->x = x; + i->y = y; + i->oldinum = -1; + i->inum = inum; + i->on = on; + i->p = il; +} + +// +// STlib_updateMultIcon() +// +// Draw a st_multicon_t widget, used for a multigraphic display +// like the status bar's keys. Displays each when the control +// numbers change or refresh is true +// +// Passed a st_multicon_t widget, and a refresh flag +// Returns nothing. +// +void STlib_updateMultIcon +( st_multicon_t* mi, + boolean refresh ) +{ + int w; + int h; + int x; + int y; + + if (*mi->on && (mi->oldinum != *mi->inum || refresh)) + { + if (mi->oldinum != -1) + { + x = mi->x - mi->p[mi->oldinum].leftoffset; + y = mi->y - mi->p[mi->oldinum].topoffset; + w = mi->p[mi->oldinum].width; + h = mi->p[mi->oldinum].height; + +#ifdef RANGECHECK + if (y - ST_Y < 0) + I_Error("STlib_updateMultIcon: y - ST_Y < 0"); +#endif + + V_CopyRect(x, y-ST_Y, BG, w, h, x, y, FG, VPT_STRETCH); + } + if (*mi->inum != -1) // killough 2/16/98: redraw only if != -1 + V_DrawNumPatch(mi->x, mi->y, FG, mi->p[*mi->inum].lumpnum, CR_DEFAULT, VPT_STRETCH); + mi->oldinum = *mi->inum; + } +} + +// +// STlib_initBinIcon() +// +// Initialize a st_binicon_t widget, used for a multinumber display +// like the status bar's weapons, that are present or not. +// +// Passed a st_binicon_t widget, the position, the digit patches, a pointer +// to the flags representing what is displayed, and pointer to the enable flag +// Returns nothing. +// +void STlib_initBinIcon +( st_binicon_t* b, + int x, + int y, + const patchnum_t* i, + boolean* val, + boolean* on ) +{ + b->x = x; + b->y = y; + b->oldval = 0; + b->val = val; + b->on = on; + b->p = i; +} + +// +// STlib_updateBinIcon() +// +// DInitialize a st_binicon_t widget, used for a multinumber display +// like the status bar's weapons, that are present or not. +// +// Draw a st_binicon_t widget, used for a multinumber display +// like the status bar's weapons that are present or not. Displays each +// when the control flag changes or refresh is true +// +// Passed a st_binicon_t widget, and a refresh flag +// Returns nothing. +// +void STlib_updateBinIcon +( st_binicon_t* bi, + boolean refresh ) +{ + int x; + int y; + int w; + int h; + + if (*bi->on && (bi->oldval != *bi->val || refresh)) + { + x = bi->x - bi->p->leftoffset; + y = bi->y - bi->p->topoffset; + w = bi->p->width; + h = bi->p->height; + +#ifdef RANGECHECK + if (y - ST_Y < 0) + I_Error("STlib_updateBinIcon: y - ST_Y < 0"); +#endif + + if (*bi->val) + V_DrawNumPatch(bi->x, bi->y, FG, bi->p->lumpnum, CR_DEFAULT, VPT_STRETCH); + else + V_CopyRect(x, y-ST_Y, BG, w, h, x, y, FG, VPT_STRETCH); + + bi->oldval = *bi->val; + } +} diff --git a/src/st_lib.h b/src/st_lib.h new file mode 100644 index 0000000..769a75e --- /dev/null +++ b/src/st_lib.h @@ -0,0 +1,209 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * The status bar widget definitions and prototypes + * + *-----------------------------------------------------------------------------*/ + +#ifndef __STLIB__ +#define __STLIB__ + +// We are referring to patches. +#include "r_defs.h" +#include "v_video.h" // color ranges + +// +// Background and foreground screen numbers +// +#define BG 4 +#define FG 0 + +// +// Typedefs of widgets +// + +// Number widget + +typedef struct +{ + // upper right-hand corner + // of the number (right-justified) + int x; + int y; + + // max # of digits in number + int width; + + // last number value + int oldnum; + + // pointer to current value + int* num; + + // pointer to boolean stating + // whether to update number + boolean* on; + + // list of patches for 0-9 + const patchnum_t* p; + + // user data + int data; +} st_number_t; + +// Percent widget ("child" of number widget, +// or, more precisely, contains a number widget.) +typedef struct +{ + // number information + st_number_t n; + + // percent sign graphic + const patchnum_t* p; +} st_percent_t; + +// Multiple Icon widget +typedef struct +{ + // center-justified location of icons + int x; + int y; + + // last icon number + int oldinum; + + // pointer to current icon + int* inum; + + // pointer to boolean stating + // whether to update icon + boolean* on; + + // list of icons + const patchnum_t* p; + + // user data + int data; + +} st_multicon_t; + +// Binary Icon widget + +typedef struct +{ + // center-justified location of icon + int x; + int y; + + // last icon value + boolean oldval; + + // pointer to current icon status + boolean* val; + + // pointer to boolean + // stating whether to update icon + boolean* on; + + const patchnum_t* p; // icon + int data; // user data +} st_binicon_t; + +// +// Widget creation, access, and update routines +// + +// Initializes widget library. +// More precisely, initialize STMINUS, +// everything else is done somewhere else. +// +void STlib_init(void); + +// Number widget routines +void STlib_initNum +( st_number_t* n, + int x, + int y, + const patchnum_t* pl, + int* num, + boolean* on, + int width ); + +void STlib_updateNum +( st_number_t* n, + int cm, + boolean refresh ); + + +// Percent widget routines +void STlib_initPercent +( st_percent_t* p, + int x, + int y, + const patchnum_t* pl, + int* num, + boolean* on, + const patchnum_t* percent ); + + +void STlib_updatePercent +( st_percent_t* per, + int cm, + int refresh ); + + +// Multiple Icon widget routines +void STlib_initMultIcon +( st_multicon_t* mi, + int x, + int y, + const patchnum_t* il, + int* inum, + boolean* on ); + + +void STlib_updateMultIcon +( st_multicon_t* mi, + boolean refresh ); + +// Binary Icon widget routines + +void STlib_initBinIcon +( st_binicon_t* b, + int x, + int y, + const patchnum_t* i, + boolean* val, + boolean* on ); + +void STlib_updateBinIcon +( st_binicon_t* bi, + boolean refresh ); + +#endif diff --git a/src/st_stuff.c b/src/st_stuff.c new file mode 100644 index 0000000..ac0391b --- /dev/null +++ b/src/st_stuff.c @@ -0,0 +1,1160 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Status bar code. + * Does the face/direction indicator animatin. + * Does palette indicators as well (red pain/berserk, bright pickup) + * + *-----------------------------------------------------------------------------*/ + +#include "doomdef.h" +#include "doomstat.h" +#include "m_random.h" +#include "i_video.h" +#include "w_wad.h" +#include "st_stuff.h" +#include "st_lib.h" +#include "r_main.h" +#include "am_map.h" +#include "m_cheat.h" +#include "s_sound.h" +#include "sounds.h" +#include "dstrings.h" +#include "r_draw.h" + +// +// STATUS BAR DATA +// + +// Palette indices. +// For damage/bonus red-/gold-shifts +#define STARTREDPALS 1 +#define STARTBONUSPALS 9 +#define NUMREDPALS 8 +#define NUMBONUSPALS 4 +// Radiation suit, green shift. +#define RADIATIONPAL 13 + +// Location of status bar +#define ST_X 0 +#define ST_X2 104 + +// proff 08/18/98: Changed for high-res +#define ST_FX (ST_X+143) +#define ST_FY (ST_Y+1) +//#define ST_FX 143 +//#define ST_FY 169 + +// Should be set to patch width +// for tall numbers later on +#define ST_TALLNUMWIDTH (tallnum[0]->width) + +// Number of status faces. +#define ST_NUMPAINFACES 5 +#define ST_NUMSTRAIGHTFACES 3 +#define ST_NUMTURNFACES 2 +#define ST_NUMSPECIALFACES 3 + +#define ST_FACESTRIDE \ + (ST_NUMSTRAIGHTFACES+ST_NUMTURNFACES+ST_NUMSPECIALFACES) + +#define ST_NUMEXTRAFACES 2 + +#define ST_NUMFACES \ + (ST_FACESTRIDE*ST_NUMPAINFACES+ST_NUMEXTRAFACES) + +#define ST_TURNOFFSET (ST_NUMSTRAIGHTFACES) +#define ST_OUCHOFFSET (ST_TURNOFFSET + ST_NUMTURNFACES) +#define ST_EVILGRINOFFSET (ST_OUCHOFFSET + 1) +#define ST_RAMPAGEOFFSET (ST_EVILGRINOFFSET + 1) +#define ST_GODFACE (ST_NUMPAINFACES*ST_FACESTRIDE) +#define ST_DEADFACE (ST_GODFACE+1) + +// proff 08/18/98: Changed for high-res +#define ST_FACESX (ST_X+143) +#define ST_FACESY (ST_Y) +//#define ST_FACESX 143 +//#define ST_FACESY 168 + +#define ST_EVILGRINCOUNT (2*TICRATE) +#define ST_STRAIGHTFACECOUNT (TICRATE/2) +#define ST_TURNCOUNT (1*TICRATE) +#define ST_OUCHCOUNT (1*TICRATE) +#define ST_RAMPAGEDELAY (2*TICRATE) + +#define ST_MUCHPAIN 20 + +// Location and size of statistics, +// justified according to widget type. +// Problem is, within which space? STbar? Screen? +// Note: this could be read in by a lump. +// Problem is, is the stuff rendered +// into a buffer, +// or into the frame buffer? +// I dunno, why don't you go and find out!!! killough + +// AMMO number pos. +#define ST_AMMOWIDTH 3 +// proff 08/18/98: Changed for high-res +#define ST_AMMOX (ST_X+44) +#define ST_AMMOY (ST_Y+3) +//#define ST_AMMOX 44 +//#define ST_AMMOY 171 + +// HEALTH number pos. +#define ST_HEALTHWIDTH 3 +// proff 08/18/98: Changed for high-res +#define ST_HEALTHX (ST_X+90) +#define ST_HEALTHY (ST_Y+3) +//#define ST_HEALTHX 90 +//#define ST_HEALTHY 171 + +// Weapon pos. +// proff 08/18/98: Changed for high-res +#define ST_ARMSX (ST_X+111) +#define ST_ARMSY (ST_Y+4) +#define ST_ARMSBGX (ST_X+104) +#define ST_ARMSBGY (ST_Y) +//#define ST_ARMSX 111 +//#define ST_ARMSY 172 +//#define ST_ARMSBGX 104 +//#define ST_ARMSBGY 168 +#define ST_ARMSXSPACE 12 +#define ST_ARMSYSPACE 10 + +// Frags pos. +// proff 08/18/98: Changed for high-res +#define ST_FRAGSX (ST_X+138) +#define ST_FRAGSY (ST_Y+3) +//#define ST_FRAGSX 138 +//#define ST_FRAGSY 171 +#define ST_FRAGSWIDTH 2 + +// ARMOR number pos. +#define ST_ARMORWIDTH 3 +// proff 08/18/98: Changed for high-res +#define ST_ARMORX (ST_X+221) +#define ST_ARMORY (ST_Y+3) +//#define ST_ARMORX 221 +//#define ST_ARMORY 171 + +// Key icon positions. +#define ST_KEY0WIDTH 8 +#define ST_KEY0HEIGHT 5 +// proff 08/18/98: Changed for high-res +#define ST_KEY0X (ST_X+239) +#define ST_KEY0Y (ST_Y+3) +//#define ST_KEY0X 239 +//#define ST_KEY0Y 171 +#define ST_KEY1WIDTH ST_KEY0WIDTH +// proff 08/18/98: Changed for high-res +#define ST_KEY1X (ST_X+239) +#define ST_KEY1Y (ST_Y+13) +//#define ST_KEY1X 239 +//#define ST_KEY1Y 181 +#define ST_KEY2WIDTH ST_KEY0WIDTH +// proff 08/18/98: Changed for high-res +#define ST_KEY2X (ST_X+239) +#define ST_KEY2Y (ST_Y+23) +//#define ST_KEY2X 239 +//#define ST_KEY2Y 191 + +// Ammunition counter. +#define ST_AMMO0WIDTH 3 +#define ST_AMMO0HEIGHT 6 +// proff 08/18/98: Changed for high-res +#define ST_AMMO0X (ST_X+288) +#define ST_AMMO0Y (ST_Y+5) +//#define ST_AMMO0X 288 +//#define ST_AMMO0Y 173 +#define ST_AMMO1WIDTH ST_AMMO0WIDTH +// proff 08/18/98: Changed for high-res +#define ST_AMMO1X (ST_X+288) +#define ST_AMMO1Y (ST_Y+11) +//#define ST_AMMO1X 288 +//#define ST_AMMO1Y 179 +#define ST_AMMO2WIDTH ST_AMMO0WIDTH +// proff 08/18/98: Changed for high-res +#define ST_AMMO2X (ST_X+288) +#define ST_AMMO2Y (ST_Y+23) +//#define ST_AMMO2X 288 +//#define ST_AMMO2Y 191 +#define ST_AMMO3WIDTH ST_AMMO0WIDTH +// proff 08/18/98: Changed for high-res +#define ST_AMMO3X (ST_X+288) +#define ST_AMMO3Y (ST_Y+17) +//#define ST_AMMO3X 288 +//#define ST_AMMO3Y 185 + +// Indicate maximum ammunition. +// Only needed because backpack exists. +#define ST_MAXAMMO0WIDTH 3 +#define ST_MAXAMMO0HEIGHT 5 +// proff 08/18/98: Changed for high-res +#define ST_MAXAMMO0X (ST_X+314) +#define ST_MAXAMMO0Y (ST_Y+5) +//#define ST_MAXAMMO0X 314 +//#define ST_MAXAMMO0Y 173 +#define ST_MAXAMMO1WIDTH ST_MAXAMMO0WIDTH +// proff 08/18/98: Changed for high-res +#define ST_MAXAMMO1X (ST_X+314) +#define ST_MAXAMMO1Y (ST_Y+11) +//#define ST_MAXAMMO1X 314 +//#define ST_MAXAMMO1Y 179 +#define ST_MAXAMMO2WIDTH ST_MAXAMMO0WIDTH +// proff 08/18/98: Changed for high-res +#define ST_MAXAMMO2X (ST_X+314) +#define ST_MAXAMMO2Y (ST_Y+23) +//#define ST_MAXAMMO2X 314 +//#define ST_MAXAMMO2Y 191 +#define ST_MAXAMMO3WIDTH ST_MAXAMMO0WIDTH +// proff 08/18/98: Changed for high-res +#define ST_MAXAMMO3X (ST_X+314) +#define ST_MAXAMMO3Y (ST_Y+17) +//#define ST_MAXAMMO3X 314 +//#define ST_MAXAMMO3Y 185 + +// killough 2/8/98: weapon info position macros UNUSED, removed here + +// main player in game +static player_t *plyr; + +// ST_Start() has just been called +static boolean st_firsttime; + +// used to execute ST_Init() only once +static int veryfirsttime = 1; + +// CPhipps - no longer do direct PLAYPAL handling here + +// used for timing +static unsigned int st_clock; + +// used for making messages go away +static int st_msgcounter=0; + +// used when in chat +static st_chatstateenum_t st_chatstate; + +// whether in automap or first-person +static st_stateenum_t st_gamestate; + +// whether left-side main status bar is active +static boolean st_statusbaron; + +// whether status bar chat is active +static boolean st_chat; + +// value of st_chat before message popped up +static boolean st_oldchat; + +// whether chat window has the cursor on +static boolean st_cursoron; + +// !deathmatch +static boolean st_notdeathmatch; + +// !deathmatch && st_statusbaron +static boolean st_armson; + +// !deathmatch +static boolean st_fragson; + +// 0-9, tall numbers +static patchnum_t tallnum[10]; + +// tall % sign +static patchnum_t tallpercent; + +// 0-9, short, yellow (,different!) numbers +static patchnum_t shortnum[10]; + +// 3 key-cards, 3 skulls, 3 card/skull combos +// jff 2/24/98 extend number of patches by three skull/card combos +static patchnum_t keys[NUMCARDS+3]; + +// face status patches +static patchnum_t faces[ST_NUMFACES]; + +// face background +static patchnum_t faceback; // CPhipps - single background, translated for different players + +//e6y: status bar background +static patchnum_t stbarbg; + +// main bar right +static patchnum_t armsbg; + +// weapon ownership patches +static patchnum_t arms[6][2]; + +// ready-weapon widget +static st_number_t w_ready; + +//jff 2/16/98 status color change levels +int ammo_red; // ammo percent less than which status is red +int ammo_yellow; // ammo percent less is yellow more green +int health_red; // health amount less than which status is red +int health_yellow; // health amount less than which status is yellow +int health_green; // health amount above is blue, below is green +int armor_red; // armor amount less than which status is red +int armor_yellow; // armor amount less than which status is yellow +int armor_green; // armor amount above is blue, below is green + + // in deathmatch only, summary of frags stats +static st_number_t w_frags; + +// health widget +static st_percent_t w_health; + +// arms background +static st_binicon_t w_armsbg; + +// weapon ownership widgets +static st_multicon_t w_arms[6]; + +// face status widget +static st_multicon_t w_faces; + +// keycard widgets +static st_multicon_t w_keyboxes[3]; + +// armor widget +static st_percent_t w_armor; + +// ammo widgets +static st_number_t w_ammo[4]; + +// max ammo widgets +static st_number_t w_maxammo[4]; + + // number of frags so far in deathmatch +static int st_fragscount; + +// used to use appopriately pained face +static int st_oldhealth = -1; + +// used for evil grin +static boolean oldweaponsowned[NUMWEAPONS]; + + // count until face changes +static int st_facecount = 0; + +// current face index, used by w_faces +static int st_faceindex = 0; + +// holds key-type for each key box on bar +static int keyboxes[3]; + +// a random number per tick +static int st_randomnumber; + +extern char *mapnames[]; + +// +// STATUS BAR CODE +// + +static void ST_Stop(void); + +static void ST_refreshBackground(void) +{ + int y=0; + + if (st_statusbaron) + { + // proff 05/17/2000: draw to the frontbuffer in OpenGL + if (V_GetMode() == VID_MODEGL) + y=ST_Y; + V_DrawNumPatch(ST_X, y, BG, stbarbg.lumpnum, CR_DEFAULT, VPT_STRETCH); + if (st_armson) + V_DrawNumPatch(ST_ARMSBGX, y, BG, armsbg.lumpnum, CR_DEFAULT, VPT_STRETCH); + + // killough 3/7/98: make face background change with displayplayer + if (netgame) + { + V_DrawNumPatch(ST_FX, y, BG, faceback.lumpnum, + displayplayer ? CR_LIMIT+displayplayer : CR_DEFAULT, + displayplayer ? (VPT_TRANS | VPT_STRETCH) : VPT_STRETCH); + } + V_CopyRect(ST_X, y, BG, ST_SCALED_WIDTH, ST_SCALED_HEIGHT, ST_X, ST_SCALED_Y, FG, VPT_NONE); + } +} + + +// Respond to keyboard input events, +// intercept cheats. +boolean ST_Responder(event_t *ev) +{ + // Filter automap on/off. + if (ev->type == ev_keyup && (ev->data1 & 0xffff0000) == AM_MSGHEADER) + { + switch(ev->data1) + { + case AM_MSGENTERED: + st_gamestate = AutomapState; + st_firsttime = true; + break; + + case AM_MSGEXITED: + st_gamestate = FirstPersonState; + break; + } + } + else // if a user keypress... + if (ev->type == ev_keydown) // Try cheat responder in m_cheat.c + return M_FindCheats(ev->data1); // killough 4/17/98, 5/2/98 + return false; +} + +static int ST_calcPainOffset(void) +{ + static int lastcalc; + static int oldhealth = -1; + int health = plyr->health > 100 ? 100 : plyr->health; + + if (health != oldhealth) + { + lastcalc = ST_FACESTRIDE * (((100 - health) * ST_NUMPAINFACES) / 101); + oldhealth = health; + } + return lastcalc; +} + +// +// This is a not-very-pretty routine which handles +// the face states and their timing. +// the precedence of expressions is: +// dead > evil grin > turned head > straight ahead +// + +static void ST_updateFaceWidget(void) +{ + int i; + angle_t badguyangle; + angle_t diffang; + static int lastattackdown = -1; + static int priority = 0; + boolean doevilgrin; + + if (priority < 10) + { + // dead + if (!plyr->health) + { + priority = 9; + st_faceindex = ST_DEADFACE; + st_facecount = 1; + } + } + + if (priority < 9) + { + if (plyr->bonuscount) + { + // picking up bonus + doevilgrin = false; + + for (i=0;iweaponowned[i]) + { + doevilgrin = true; + oldweaponsowned[i] = plyr->weaponowned[i]; + } + } + if (doevilgrin) + { + // evil grin if just picked up weapon + priority = 8; + st_facecount = ST_EVILGRINCOUNT; + st_faceindex = ST_calcPainOffset() + ST_EVILGRINOFFSET; + } + } + + } + + if (priority < 8) + { + if (plyr->damagecount && plyr->attacker && plyr->attacker != plyr->mo) + { + // being attacked + priority = 7; + + // haleyjd 10/12/03: classic DOOM problem of missing OUCH face + // was due to inversion of this test: + // if(plyr->health - st_oldhealth > ST_MUCHPAIN) + if(st_oldhealth - plyr->health > ST_MUCHPAIN) + { + st_facecount = ST_TURNCOUNT; + st_faceindex = ST_calcPainOffset() + ST_OUCHOFFSET; + } + else + { + badguyangle = R_PointToAngle2(plyr->mo->x, + plyr->mo->y, + plyr->attacker->x, + plyr->attacker->y); + + if (badguyangle > plyr->mo->angle) + { + // whether right or left + diffang = badguyangle - plyr->mo->angle; + i = diffang > ANG180; + } + else + { + // whether left or right + diffang = plyr->mo->angle - badguyangle; + i = diffang <= ANG180; + } // confusing, aint it? + + + st_facecount = ST_TURNCOUNT; + st_faceindex = ST_calcPainOffset(); + + if (diffang < ANG45) + { + // head-on + st_faceindex += ST_RAMPAGEOFFSET; + } + else if (i) + { + // turn face right + st_faceindex += ST_TURNOFFSET; + } + else + { + // turn face left + st_faceindex += ST_TURNOFFSET+1; + } + } + } + } + + if (priority < 7) + { + // getting hurt because of your own damn stupidity + if (plyr->damagecount) + { + // haleyjd 10/12/03: classic DOOM problem of missing OUCH face + // was due to inversion of this test: + // if(plyr->health - st_oldhealth > ST_MUCHPAIN) + if(st_oldhealth - plyr->health > ST_MUCHPAIN) + { + priority = 7; + st_facecount = ST_TURNCOUNT; + st_faceindex = ST_calcPainOffset() + ST_OUCHOFFSET; + } + else + { + priority = 6; + st_facecount = ST_TURNCOUNT; + st_faceindex = ST_calcPainOffset() + ST_RAMPAGEOFFSET; + } + + } + + } + + if (priority < 6) + { + // rapid firing + if (plyr->attackdown) + { + if (lastattackdown==-1) + lastattackdown = ST_RAMPAGEDELAY; + else if (!--lastattackdown) + { + priority = 5; + st_faceindex = ST_calcPainOffset() + ST_RAMPAGEOFFSET; + st_facecount = 1; + lastattackdown = 1; + } + } + else + lastattackdown = -1; + + } + + if (priority < 5) + { + // invulnerability + if ((plyr->cheats & CF_GODMODE) + || plyr->powers[pw_invulnerability]) + { + priority = 4; + + st_faceindex = ST_GODFACE; + st_facecount = 1; + + } + + } + + // look left or look right if the facecount has timed out + if (!st_facecount) + { + st_faceindex = ST_calcPainOffset() + (st_randomnumber % 3); + st_facecount = ST_STRAIGHTFACECOUNT; + priority = 0; + } + + st_facecount--; + +} + +int sts_traditional_keys; // killough 2/28/98: traditional status bar keys + +static void ST_updateWidgets(void) +{ + static int largeammo = 1994; // means "n/a" + int i; + + // must redirect the pointer if the ready weapon has changed. + // if (w_ready.data != plyr->readyweapon) + // { + if (weaponinfo[plyr->readyweapon].ammo == am_noammo) + w_ready.num = &largeammo; + else + w_ready.num = &plyr->ammo[weaponinfo[plyr->readyweapon].ammo]; + //{ + // static int tic=0; + // static int dir=-1; + // if (!(tic&15)) + // plyr->ammo[weaponinfo[plyr->readyweapon].ammo]+=dir; + // if (plyr->ammo[weaponinfo[plyr->readyweapon].ammo] == -100) + // dir = 1; + // tic++; + // } + w_ready.data = plyr->readyweapon; + + // if (*w_ready.on) + // STlib_updateNum(&w_ready, true); + // refresh weapon change + // } + + // update keycard multiple widgets + for (i=0;i<3;i++) + { + keyboxes[i] = plyr->cards[i] ? i : -1; + + //jff 2/24/98 select double key + //killough 2/28/98: preserve traditional keys by config option + + if (plyr->cards[i+3]) + keyboxes[i] = keyboxes[i]==-1 || sts_traditional_keys ? i+3 : i+6; + } + + // refresh everything if this is him coming back to life + ST_updateFaceWidget(); + + // used by the w_armsbg widget + st_notdeathmatch = !deathmatch; + + // used by w_arms[] widgets + st_armson = st_statusbaron && !deathmatch; + + // used by w_frags widget + st_fragson = deathmatch && st_statusbaron; + st_fragscount = 0; + + for (i=0 ; ifrags[i]; + else + st_fragscount -= plyr->frags[i]; + } + + // get rid of chat window if up because of message + if (!--st_msgcounter) + st_chat = st_oldchat; + +} + +void ST_Ticker(void) +{ + st_clock++; + st_randomnumber = M_Random(); + ST_updateWidgets(); + st_oldhealth = plyr->health; +} + +int st_palette = 0; + +static void ST_doPaletteStuff(void) +{ + int palette; + int cnt = plyr->damagecount; + + if (plyr->powers[pw_strength]) + { + // slowly fade the berzerk out + int bzc = 12 - (plyr->powers[pw_strength]>>6); + if (bzc > cnt) + cnt = bzc; + } + + if (cnt) + { + palette = (cnt+7)>>3; + if (palette >= NUMREDPALS) + palette = NUMREDPALS-1; + + /* cph 2006/08/06 - if in the menu, reduce the red tint - navigating to + * load a game can be tricky if the screen is all red */ + if (menuactive) palette >>=1; + + palette += STARTREDPALS; + } + else + if (plyr->bonuscount) + { + palette = (plyr->bonuscount+7)>>3; + if (palette >= NUMBONUSPALS) + palette = NUMBONUSPALS-1; + palette += STARTBONUSPALS; + } + else + if (plyr->powers[pw_ironfeet] > 4*32 || plyr->powers[pw_ironfeet] & 8) + palette = RADIATIONPAL; + else + palette = 0; + + if (palette != st_palette) { + V_SetPalette(st_palette = palette); // CPhipps - use new palette function + + // have to redraw the entire status bar when the palette changes + // in truecolor modes - POPE + if (V_GetMode() == VID_MODE15 || V_GetMode() == VID_MODE16 || V_GetMode() == VID_MODE32) + st_firsttime = true; + } +} + +static void ST_drawWidgets(boolean refresh) +{ + int i; + + // used by w_arms[] widgets + st_armson = st_statusbaron && !deathmatch; + + // used by w_frags widget + st_fragson = deathmatch && st_statusbaron; + + //jff 2/16/98 make color of ammo depend on amount + if (*w_ready.num*100 < ammo_red*plyr->maxammo[weaponinfo[w_ready.data].ammo]) + STlib_updateNum(&w_ready, CR_RED, refresh); + else + if (*w_ready.num*100 < + ammo_yellow*plyr->maxammo[weaponinfo[w_ready.data].ammo]) + STlib_updateNum(&w_ready, CR_GOLD, refresh); + else + STlib_updateNum(&w_ready, CR_GREEN, refresh); + + for (i=0;i<4;i++) + { + STlib_updateNum(&w_ammo[i], CR_DEFAULT, refresh); //jff 2/16/98 no xlation + STlib_updateNum(&w_maxammo[i], CR_DEFAULT, refresh); + } + + //jff 2/16/98 make color of health depend on amount + if (*w_health.n.numweaponowned[i]; + + for (i=0;i<3;i++) + keyboxes[i] = -1; + + STlib_init(); +} + +static void ST_createWidgets(void) +{ + int i; + + // ready weapon ammo + STlib_initNum(&w_ready, + ST_AMMOX, + ST_AMMOY, + tallnum, + &plyr->ammo[weaponinfo[plyr->readyweapon].ammo], + &st_statusbaron, + ST_AMMOWIDTH ); + + // the last weapon type + w_ready.data = plyr->readyweapon; + + // health percentage + STlib_initPercent(&w_health, + ST_HEALTHX, + ST_HEALTHY, + tallnum, + &plyr->health, + &st_statusbaron, + &tallpercent); + + // arms background + STlib_initBinIcon(&w_armsbg, + ST_ARMSBGX, + ST_ARMSBGY, + &armsbg, + &st_notdeathmatch, + &st_statusbaron); + + // weapons owned + for(i=0;i<6;i++) + { + STlib_initMultIcon(&w_arms[i], + ST_ARMSX+(i%3)*ST_ARMSXSPACE, + ST_ARMSY+(i/3)*ST_ARMSYSPACE, + arms[i], (int *) &plyr->weaponowned[i+1], + &st_armson); + } + + // frags sum + STlib_initNum(&w_frags, + ST_FRAGSX, + ST_FRAGSY, + tallnum, + &st_fragscount, + &st_fragson, + ST_FRAGSWIDTH); + + // faces + STlib_initMultIcon(&w_faces, + ST_FACESX, + ST_FACESY, + faces, + &st_faceindex, + &st_statusbaron); + + // armor percentage - should be colored later + STlib_initPercent(&w_armor, + ST_ARMORX, + ST_ARMORY, + tallnum, + &plyr->armorpoints, + &st_statusbaron, &tallpercent); + + // keyboxes 0-2 + STlib_initMultIcon(&w_keyboxes[0], + ST_KEY0X, + ST_KEY0Y, + keys, + &keyboxes[0], + &st_statusbaron); + + STlib_initMultIcon(&w_keyboxes[1], + ST_KEY1X, + ST_KEY1Y, + keys, + &keyboxes[1], + &st_statusbaron); + + STlib_initMultIcon(&w_keyboxes[2], + ST_KEY2X, + ST_KEY2Y, + keys, + &keyboxes[2], + &st_statusbaron); + + // ammo count (all four kinds) + STlib_initNum(&w_ammo[0], + ST_AMMO0X, + ST_AMMO0Y, + shortnum, + &plyr->ammo[0], + &st_statusbaron, + ST_AMMO0WIDTH); + + STlib_initNum(&w_ammo[1], + ST_AMMO1X, + ST_AMMO1Y, + shortnum, + &plyr->ammo[1], + &st_statusbaron, + ST_AMMO1WIDTH); + + STlib_initNum(&w_ammo[2], + ST_AMMO2X, + ST_AMMO2Y, + shortnum, + &plyr->ammo[2], + &st_statusbaron, + ST_AMMO2WIDTH); + + STlib_initNum(&w_ammo[3], + ST_AMMO3X, + ST_AMMO3Y, + shortnum, + &plyr->ammo[3], + &st_statusbaron, + ST_AMMO3WIDTH); + + // max ammo count (all four kinds) + STlib_initNum(&w_maxammo[0], + ST_MAXAMMO0X, + ST_MAXAMMO0Y, + shortnum, + &plyr->maxammo[0], + &st_statusbaron, + ST_MAXAMMO0WIDTH); + + STlib_initNum(&w_maxammo[1], + ST_MAXAMMO1X, + ST_MAXAMMO1Y, + shortnum, + &plyr->maxammo[1], + &st_statusbaron, + ST_MAXAMMO1WIDTH); + + STlib_initNum(&w_maxammo[2], + ST_MAXAMMO2X, + ST_MAXAMMO2Y, + shortnum, + &plyr->maxammo[2], + &st_statusbaron, + ST_MAXAMMO2WIDTH); + + STlib_initNum(&w_maxammo[3], + ST_MAXAMMO3X, + ST_MAXAMMO3Y, + shortnum, + &plyr->maxammo[3], + &st_statusbaron, + ST_MAXAMMO3WIDTH); +} + +static boolean st_stopped = true; + +void ST_Start(void) +{ + if (!st_stopped) + ST_Stop(); + ST_initData(); + ST_createWidgets(); + st_stopped = false; +} + +static void ST_Stop(void) +{ + if (st_stopped) + return; + V_SetPalette(0); + st_stopped = true; +} + +void ST_Init(void) +{ + veryfirsttime = 0; + ST_loadData(); +} diff --git a/src/st_stuff.h b/src/st_stuff.h new file mode 100644 index 0000000..83ba51e --- /dev/null +++ b/src/st_stuff.h @@ -0,0 +1,102 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Status bar code. + * Does the face/direction indicator animatin. + * Does palette indicators as well (red pain/berserk, bright pickup) + * + *-----------------------------------------------------------------------------*/ + +#ifndef __STSTUFF_H__ +#define __STSTUFF_H__ + +#include "doomtype.h" +#include "d_event.h" + +// Size of statusbar. +// Now sensitive for scaling. + +// proff 08/18/98: Changed for high-res +#define ST_HEIGHT 32 +#define ST_WIDTH 320 +#define ST_Y (200 - ST_HEIGHT) +#define ST_SCALED_HEIGHT (ST_HEIGHT*SCREENHEIGHT/200) +#define ST_SCALED_WIDTH SCREENWIDTH +#define ST_SCALED_Y (SCREENHEIGHT - ST_SCALED_HEIGHT) + +// +// STATUS BAR +// + +// Called by main loop. +boolean ST_Responder(event_t* ev); + +// Called by main loop. +void ST_Ticker(void); + +// Called by main loop. +void ST_Drawer(boolean st_statusbaron, boolean refresh); + +// Called when the console player is spawned on each level. +void ST_Start(void); + +// Called by startup code. +void ST_Init(void); + +// States for status bar code. +typedef enum +{ + AutomapState, + FirstPersonState +} st_stateenum_t; + +// States for the chat code. +typedef enum +{ + StartChatState, + WaitDestState, + GetChatState +} st_chatstateenum_t; + +// killough 5/2/98: moved from m_misc.c: + +extern int health_red; // health amount less than which status is red +extern int health_yellow; // health amount less than which status is yellow +extern int health_green; // health amount above is blue, below is green +extern int armor_red; // armor amount less than which status is red +extern int armor_yellow; // armor amount less than which status is yellow +extern int armor_green; // armor amount above is blue, below is green +extern int ammo_red; // ammo percent less than which status is red +extern int ammo_yellow; // ammo percent less is yellow more green +extern int sts_always_red;// status numbers do not change colors +extern int sts_pct_always_gray;// status percents do not change colors +extern int sts_traditional_keys; // display keys the traditional way + +extern int st_palette; // cph 2006/04/06 - make palette visible +#endif diff --git a/src/tables.c b/src/tables.c new file mode 100644 index 0000000..2cf59e1 --- /dev/null +++ b/src/tables.c @@ -0,0 +1,128 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Lookup tables. + * Do not try to look them up :-). + * In the order of appearance: + * + * int finetangent[4096] - Tangens LUT. + * Should work with BAM fairly well (12 of 16bit, + * effectively, by shifting). + * + * int finesine[10240] - Sine lookup. + * Guess what, serves as cosine, too. + * Remarkable thing is, how to use BAMs with this? + * + * int tantoangle[2049] - ArcTan LUT, + * maps tan(angle) to angle fast. Gotta search. + * + *----------------------------------------------------------------------------- + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include "w_wad.h" +#include "tables.h" + +// killough 5/3/98: reformatted + +int SlopeDiv(unsigned num, unsigned den) +{ + unsigned ans; + + if (den < 512) + return SLOPERANGE; + ans = (num<<3)/(den>>8); + return ans <= SLOPERANGE ? ans : SLOPERANGE; +} + +fixed_t finetangent[4096]; + +//const fixed_t *const finecosine = &finesine[FINEANGLES/4]; + +fixed_t finesine[10240]; + +angle_t tantoangle[2049]; + +#include "m_swap.h" +#include "lprintf.h" + +// R_LoadTrigTables +// Load trig tables from a wad file lump +// CPhipps 24/12/98 - fix endianness (!) +// +void R_LoadTrigTables(void) +{ + int lump; + { + lump = (W_CheckNumForName)("SINETABL",ns_prboom); + if (lump == -1) I_Error("Failed to locate trig tables"); + if (W_LumpLength(lump) != sizeof(finesine)) + I_Error("R_LoadTrigTables: Invalid SINETABL"); + W_ReadLump(lump,(unsigned char*)finesine); + } + { + lump = (W_CheckNumForName)("TANGTABL",ns_prboom); + if (lump == -1) I_Error("Failed to locate trig tables"); + if (W_LumpLength(lump) != sizeof(finetangent)) + I_Error("R_LoadTrigTables: Invalid TANGTABL"); + W_ReadLump(lump,(unsigned char*)finetangent); + } + { + lump = (W_CheckNumForName)("TANTOANG",ns_prboom); + if (lump == -1) I_Error("Failed to locate trig tables"); + if (W_LumpLength(lump) != sizeof(tantoangle)) + I_Error("R_LoadTrigTables: Invalid TANTOANG"); + W_ReadLump(lump,(unsigned char*)tantoangle); + } + // Endianness correction - might still be non-portable, but is fast where possible + { + size_t n; + lprintf(LO_INFO, "Endianness..."); + + // This test doesn't assume the endianness of the tables, but deduces them from + // en entry. I hope this is portable. + if ((10 < finesine[1]) && (finesine[1] < 100)) { + lprintf(LO_INFO, "ok."); + return; // Endianness is correct + } + + // Must correct endianness of every long loaded (!) +#define CORRECT_TABLE_ENDIAN(tbl) \ + for (n = 0; nname; p++) + *p->map = W_CacheLumpName(p->name); +} + +// +// V_CopyRect +// +// Copies a source rectangle in a screen buffer to a destination +// rectangle in another screen buffer. Source origin in srcx,srcy, +// destination origin in destx,desty, common size in width and height. +// Source buffer specfified by srcscrn, destination buffer by destscrn. +// +// Marks the destination rectangle on the screen dirty. +// +// No return. +// +static void FUNC_V_CopyRect(int srcx, int srcy, int srcscrn, int width, + int height, int destx, int desty, int destscrn, + enum patch_translation_e flags) +{ + byte *src; + byte *dest; + + if (flags & VPT_STRETCH) + { + srcx=srcx*SCREENWIDTH/320; + srcy=srcy*SCREENHEIGHT/200; + width=width*SCREENWIDTH/320; + height=height*SCREENHEIGHT/200; + destx=destx*SCREENWIDTH/320; + desty=desty*SCREENHEIGHT/200; + } + +#ifdef RANGECHECK + if (srcx<0 + ||srcx+width >SCREENWIDTH + || srcy<0 + || srcy+height>SCREENHEIGHT + ||destx<0||destx+width >SCREENWIDTH + || desty<0 + || desty+height>SCREENHEIGHT) + I_Error ("V_CopyRect: Bad arguments"); +#endif + + src = screens[srcscrn].data+screens[srcscrn].byte_pitch*srcy+srcx*V_GetPixelDepth(); + dest = screens[destscrn].data+screens[destscrn].byte_pitch*desty+destx*V_GetPixelDepth(); + + for ( ; height>0 ; height--) + { + memcpy (dest, src, width*V_GetPixelDepth()); + src += screens[srcscrn].byte_pitch; + dest += screens[destscrn].byte_pitch; + } +} + +/* + * V_DrawBackground tiles a 64x64 patch over the entire screen, providing the + * background for the Help and Setup screens, and plot text betwen levels. + * cphipps - used to have M_DrawBackground, but that was used the framebuffer + * directly, so this is my code from the equivalent function in f_finale.c + */ +static void FUNC_V_DrawBackground(const char* flatname, int scrn) +{ + /* erase the entire screen to a tiled background */ + const byte *src; + int x,y; + int width,height; + int lump; + + // killough 4/17/98: + src = W_CacheLumpNum(lump = firstflat + R_FlatNumForName(flatname)); + + /* V_DrawBlock(0, 0, scrn, 64, 64, src, 0); */ + width = height = 64; + if (V_GetMode() == VID_MODE8) { + byte *dest = screens[scrn].data; + + while (height--) { + memcpy (dest, src, width); + src += width; + dest += screens[scrn].byte_pitch; + } + } else if (V_GetMode() == VID_MODE15) { + unsigned short *dest = (unsigned short *)screens[scrn].data; + + while (height--) { + int i; + for (i=0; itopoffset; + x -= patch->leftoffset; + + // CPhipps - auto-no-stretch if not high-res + if (flags & VPT_STRETCH) + if ((SCREENWIDTH==320) && (SCREENHEIGHT==200)) + flags &= ~VPT_STRETCH; + + // CPhipps - null translation pointer => no translation + if (!trans) + flags &= ~VPT_TRANS; + + if (V_GetMode() == VID_MODE8 && !(flags & VPT_STRETCH)) { + int col; + byte *desttop = screens[scrn].data+y*screens[scrn].byte_pitch+x*V_GetPixelDepth(); + unsigned int w = patch->width; + + if (y<0 || y+patch->height > ((flags & VPT_STRETCH) ? 200 : SCREENHEIGHT)) { + // killough 1/19/98: improved error message: + lprintf(LO_WARN, "V_DrawMemPatch8: Patch (%d,%d)-(%d,%d) exceeds LFB in vertical direction (horizontal is clipped)\n" + "Bad V_DrawMemPatch8 (flags=%u)", x, y, x+patch->width, y+patch->height, flags); + return; + } + + w--; // CPhipps - note: w = width-1 now, speeds up flipping + + for (col=0 ; (unsigned int)col<=w ; desttop++, col++, x++) { + int i; + const int colindex = (flags & VPT_FLIP) ? (w - col) : (col); + const rcolumn_t *column = R_GetPatchColumn(patch, colindex); + + if (x < 0) + continue; + if (x >= SCREENWIDTH) + break; + + // step through the posts in a column + for (i=0; inumPosts; i++) { + const rpost_t *post = &column->posts[i]; + // killough 2/21/98: Unrolled and performance-tuned + + const byte *source = column->pixels + post->topdelta; + byte *dest = desttop + post->topdelta*screens[scrn].byte_pitch; + int count = post->length; + + if (!(flags & VPT_TRANS)) { + if ((count-=4)>=0) + do { + register byte s0,s1; + s0 = source[0]; + s1 = source[1]; + dest[0] = s0; + dest[screens[scrn].byte_pitch] = s1; + dest += screens[scrn].byte_pitch*2; + s0 = source[2]; + s1 = source[3]; + source += 4; + dest[0] = s0; + dest[screens[scrn].byte_pitch] = s1; + dest += screens[scrn].byte_pitch*2; + } while ((count-=4)>=0); + if (count+=4) + do { + *dest = *source++; + dest += screens[scrn].byte_pitch; + } while (--count); + } else { + // CPhipps - merged translation code here + if ((count-=4)>=0) + do { + register byte s0,s1; + s0 = source[0]; + s1 = source[1]; + s0 = trans[s0]; + s1 = trans[s1]; + dest[0] = s0; + dest[screens[scrn].byte_pitch] = s1; + dest += screens[scrn].byte_pitch*2; + s0 = source[2]; + s1 = source[3]; + s0 = trans[s0]; + s1 = trans[s1]; + source += 4; + dest[0] = s0; + dest[screens[scrn].byte_pitch] = s1; + dest += screens[scrn].byte_pitch*2; + } while ((count-=4)>=0); + if (count+=4) + do { + *dest = trans[*source++]; + dest += screens[scrn].byte_pitch; + } while (--count); + } + } + } + } + else { + // CPhipps - move stretched patch drawing code here + // - reformat initialisers, move variables into inner blocks + + int col; + int w = (patch->width << 16) - 1; // CPhipps - -1 for faster flipping + int left, right, top, bottom; + int DX = (SCREENWIDTH<<16) / 320; + int DXI = (320<<16) / SCREENWIDTH; + int DY = (SCREENHEIGHT<<16) / 200; + int DYI = (200<<16) / SCREENHEIGHT; + R_DrawColumn_f colfunc; + draw_column_vars_t dcvars; + draw_vars_t olddrawvars = drawvars; + + R_SetDefaultDrawColumnVars(&dcvars); + + drawvars.byte_topleft = screens[scrn].data; + drawvars.short_topleft = (unsigned short *)screens[scrn].data; + drawvars.int_topleft = (unsigned int *)screens[scrn].data; + drawvars.byte_pitch = screens[scrn].byte_pitch; + drawvars.short_pitch = screens[scrn].short_pitch; + drawvars.int_pitch = screens[scrn].int_pitch; + + if (!(flags & VPT_STRETCH)) { + DX = 1 << 16; + DXI = 1 << 16; + DY = 1 << 16; + DYI = 1 << 16; + } + + if (flags & VPT_TRANS) { + colfunc = R_GetDrawColumnFunc(RDC_PIPELINE_TRANSLATED, drawvars.filterpatch, RDRAW_FILTER_NONE); + dcvars.translation = trans; + } else { + colfunc = R_GetDrawColumnFunc(RDC_PIPELINE_STANDARD, drawvars.filterpatch, RDRAW_FILTER_NONE); + } + + left = ( x * DX ) >> FRACBITS; + top = ( y * DY ) >> FRACBITS; + right = ( (x + patch->width) * DX ) >> FRACBITS; + bottom = ( (y + patch->height) * DY ) >> FRACBITS; + + dcvars.texheight = patch->height; + dcvars.iscale = DYI; + dcvars.drawingmasked = MAX(patch->width, patch->height) > 8; + dcvars.edgetype = drawvars.patch_edges; + + if (drawvars.filterpatch == RDRAW_FILTER_LINEAR) { + // bias the texture u coordinate + if (patch->isNotTileable) + col = -(FRACUNIT>>1); + else + col = (patch->width<>1); + } + else { + col = 0; + } + + for (dcvars.x=left; dcvars.x>16): (col>>16); + const rcolumn_t *column = R_GetPatchColumn(patch, colindex); + const rcolumn_t *prevcolumn = R_GetPatchColumn(patch, colindex-1); + const rcolumn_t *nextcolumn = R_GetPatchColumn(patch, colindex+1); + + // ignore this column if it's to the left of our clampRect + if (dcvars.x < 0) + continue; + if (dcvars.x >= SCREENWIDTH) + break; + + dcvars.texu = ((flags & VPT_FLIP) ? ((patch->width<width<numPosts; i++) { + const rpost_t *post = &column->posts[i]; + int yoffset = 0; + + dcvars.yl = (((y + post->topdelta) * DY)>>FRACBITS); + dcvars.yh = (((y + post->topdelta + post->length) * DY - (FRACUNIT>>1))>>FRACBITS); + dcvars.edgeslope = post->slope; + + if ((dcvars.yh < 0) || (dcvars.yh < top)) + continue; + if ((dcvars.yl >= SCREENHEIGHT) || (dcvars.yl >= bottom)) + continue; + + if (dcvars.yh >= bottom) { + dcvars.yh = bottom-1; + dcvars.edgeslope &= ~RDRAW_EDGESLOPE_BOT_MASK; + } + if (dcvars.yh >= SCREENHEIGHT) { + dcvars.yh = SCREENHEIGHT-1; + dcvars.edgeslope &= ~RDRAW_EDGESLOPE_BOT_MASK; + } + + if (dcvars.yl < 0) { + yoffset = 0-dcvars.yl; + dcvars.yl = 0; + dcvars.edgeslope &= ~RDRAW_EDGESLOPE_TOP_MASK; + } + if (dcvars.yl < top) { + yoffset = top-dcvars.yl; + dcvars.yl = top; + dcvars.edgeslope &= ~RDRAW_EDGESLOPE_TOP_MASK; + } + + dcvars.source = column->pixels + post->topdelta + yoffset; + dcvars.prevsource = prevcolumn ? prevcolumn->pixels + post->topdelta + yoffset: dcvars.source; + dcvars.nextsource = nextcolumn ? nextcolumn->pixels + post->topdelta + yoffset: dcvars.source; + + dcvars.texturemid = -((dcvars.yl-centery)*dcvars.iscale); + + colfunc(&dcvars); + } + } + + R_ResetColumnBuffer(); + drawvars = olddrawvars; + } +} + +// CPhipps - some simple, useful wrappers for that function, for drawing patches from wads + +// CPhipps - GNU C only suppresses generating a copy of a function if it is +// static inline; other compilers have different behaviour. +// This inline is _only_ for the function below + +static void FUNC_V_DrawNumPatch(int x, int y, int scrn, int lump, + int cm, enum patch_translation_e flags) +{ + V_DrawMemPatch(x, y, scrn, R_CachePatchNum(lump), cm, flags); + R_UnlockPatchNum(lump); +} + +unsigned short *V_Palette15 = NULL; +unsigned short *V_Palette16 = NULL; +unsigned int *V_Palette32 = NULL; +static unsigned short *Palettes15 = NULL; +static unsigned short *Palettes16 = NULL; +static unsigned int *Palettes32 = NULL; +static int currentPaletteIndex = 0; + +// +// V_UpdateTrueColorPalette +// +void V_UpdateTrueColorPalette(video_mode_t mode) { + int i, w, p; + byte r,g,b; + int nr,ng,nb; + float t; + int paletteNum = (V_GetMode() == VID_MODEGL ? 0 : currentPaletteIndex); + static int usegammaOnLastPaletteGeneration = -1; + + int pplump = W_GetNumForName("PLAYPAL"); + int gtlump = (W_CheckNumForName)("GAMMATBL",ns_prboom); + const byte *pal = W_CacheLumpNum(pplump); + // opengl doesn't use the gamma + const byte *const gtable = + (const byte *)W_CacheLumpNum(gtlump) + + (V_GetMode() == VID_MODEGL ? 0 : 256*(usegamma)) + ; + + int numPals = W_LumpLength(pplump) / (3*256); + const float dontRoundAbove = 220; + float roundUpR, roundUpG, roundUpB; + + if (usegammaOnLastPaletteGeneration != usegamma) { + if (Palettes15) free(Palettes15); + if (Palettes16) free(Palettes16); + if (Palettes32) free(Palettes32); + Palettes15 = NULL; + Palettes16 = NULL; + Palettes32 = NULL; + usegammaOnLastPaletteGeneration = usegamma; + } + + if (mode == VID_MODE32) { + if (!Palettes32) { + // set int palette + Palettes32 = (int*)malloc(numPals*256*sizeof(int)*VID_NUMCOLORWEIGHTS); + for (p=0; p dontRoundAbove) ? 0 : 0.5f; + roundUpG = (g > dontRoundAbove) ? 0 : 0.5f; + roundUpB = (b > dontRoundAbove) ? 0 : 0.5f; + + for (w=0; w dontRoundAbove) ? 0 : 0.5f; + roundUpG = (g > dontRoundAbove) ? 0 : 0.5f; + roundUpB = (b > dontRoundAbove) ? 0 : 0.5f; + + for (w=0; w>3)*t+roundUpR); + ng = (int)((g>>2)*t+roundUpG); + nb = (int)((b>>3)*t+roundUpB); + Palettes16[((p*256+i)*VID_NUMCOLORWEIGHTS)+w] = ( + (nr<<11) | (ng<<5) | nb + ); + } + } + } + } + V_Palette16 = Palettes16 + paletteNum*256*VID_NUMCOLORWEIGHTS; + } + else if (mode == VID_MODE15) { + if (!Palettes15) { + // set short palette + Palettes15 = (short*)malloc(numPals*256*sizeof(short)*VID_NUMCOLORWEIGHTS); + for (p=0; p dontRoundAbove) ? 0 : 0.5f; + roundUpG = (g > dontRoundAbove) ? 0 : 0.5f; + roundUpB = (b > dontRoundAbove) ? 0 : 0.5f; + + for (w=0; w>3)*t+roundUpR); + ng = (int)((g>>3)*t+roundUpG); + nb = (int)((b>>3)*t+roundUpB); + Palettes15[((p*256+i)*VID_NUMCOLORWEIGHTS)+w] = ( + (nr<<10) | (ng<<5) | nb + ); + } + } + } + } + V_Palette15 = Palettes15 + paletteNum*256*VID_NUMCOLORWEIGHTS; + } + + W_UnlockLumpNum(pplump); + W_UnlockLumpNum(gtlump); +} + + +//--------------------------------------------------------------------------- +// V_DestroyTrueColorPalette +//--------------------------------------------------------------------------- +static void V_DestroyTrueColorPalette(video_mode_t mode) { + if (mode == VID_MODE15) { + if (Palettes15) free(Palettes15); + Palettes15 = NULL; + V_Palette15 = NULL; + } + if (mode == VID_MODE16) { + if (Palettes16) free(Palettes16); + Palettes16 = NULL; + V_Palette16 = NULL; + } + if (mode == VID_MODE32) { + if (Palettes32) free(Palettes32); + Palettes32 = NULL; + V_Palette32 = NULL; + } +} + +void V_DestroyUnusedTrueColorPalettes(void) { + if (V_GetMode() != VID_MODE15) V_DestroyTrueColorPalette(VID_MODE15); + if (V_GetMode() != VID_MODE16) V_DestroyTrueColorPalette(VID_MODE16); + if (V_GetMode() != VID_MODE32) V_DestroyTrueColorPalette(VID_MODE32); +} + +// +// V_SetPalette +// +// CPhipps - New function to set the palette to palette number pal. +// Handles loading of PLAYPAL and calls I_SetPalette + +void V_SetPalette(int pal) +{ + currentPaletteIndex = pal; + + if (V_GetMode() == VID_MODEGL) { +#ifdef GL_DOOM + gld_SetPalette(pal); +#endif + } else { + I_SetPalette(pal); + if (V_GetMode() == VID_MODE15 || V_GetMode() == VID_MODE16 || V_GetMode() == VID_MODE32) { + // V_SetPalette can be called as part of the gamma setting before + // we've loaded any wads, which prevents us from reading the palette - POPE + if (W_CheckNumForName("PLAYPAL") >= 0) { + V_UpdateTrueColorPalette(V_GetMode()); + } + } + } +} + +// +// V_FillRect +// +// CPhipps - New function to fill a rectangle with a given colour +static void V_FillRect8(int scrn, int x, int y, int width, int height, byte colour) +{ + byte* dest = screens[scrn].data + x + y*screens[scrn].byte_pitch; + while (height--) { + memset(dest, colour, width); + dest += screens[scrn].byte_pitch; + } +} + +static void V_FillRect15(int scrn, int x, int y, int width, int height, byte colour) +{ + unsigned short* dest = (unsigned short *)screens[scrn].data + x + y*screens[scrn].short_pitch; + int w; + short c = VID_PAL15(colour, VID_COLORWEIGHTMASK); + while (height--) { + for (w=0; wa.x, fl->a.y, fl->b.x, fl->b.y, color); +} +#endif + +static void NULL_FillRect(int scrn, int x, int y, int width, int height, byte colour) {} +static void NULL_CopyRect(int srcx, int srcy, int srcscrn, int width, int height, int destx, int desty, int destscrn, enum patch_translation_e flags) {} +static void NULL_DrawBackground(const char *flatname, int n) {} +static void NULL_DrawNumPatch(int x, int y, int scrn, int lump, int cm, enum patch_translation_e flags) {} +static void NULL_DrawBlock(int x, int y, int scrn, int width, int height, const byte *src, enum patch_translation_e flags) {} +static void NULL_PlotPixel(int scrn, int x, int y, byte color) {} +static void NULL_DrawLine(fline_t* fl, int color) {} + +const char *default_videomode; +static video_mode_t current_videomode = VID_MODE8; + +V_CopyRect_f V_CopyRect = NULL_CopyRect; +V_FillRect_f V_FillRect = NULL_FillRect; +V_DrawNumPatch_f V_DrawNumPatch = NULL_DrawNumPatch; +V_DrawBackground_f V_DrawBackground = NULL_DrawBackground; +V_PlotPixel_f V_PlotPixel = NULL_PlotPixel; +V_DrawLine_f V_DrawLine = NULL_DrawLine; + +// +// V_InitMode +// +void V_InitMode(video_mode_t mode) { +#ifndef GL_DOOM + if (mode == VID_MODEGL) + mode = VID_MODE8; +#endif + switch (mode) { + case VID_MODE8: + lprintf(LO_INFO, "V_InitMode: using 8 bit video mode\n"); + V_CopyRect = FUNC_V_CopyRect; + V_FillRect = V_FillRect8; + V_DrawNumPatch = FUNC_V_DrawNumPatch; + V_DrawBackground = FUNC_V_DrawBackground; + V_PlotPixel = V_PlotPixel8; + V_DrawLine = WRAP_V_DrawLine; + current_videomode = VID_MODE8; + break; + case VID_MODE15: + lprintf(LO_INFO, "V_InitMode: using 15 bit video mode\n"); + V_CopyRect = FUNC_V_CopyRect; + V_FillRect = V_FillRect15; + V_DrawNumPatch = FUNC_V_DrawNumPatch; + V_DrawBackground = FUNC_V_DrawBackground; + V_PlotPixel = V_PlotPixel15; + V_DrawLine = WRAP_V_DrawLine; + current_videomode = VID_MODE15; + break; + case VID_MODE16: + lprintf(LO_INFO, "V_InitMode: using 16 bit video mode\n"); + V_CopyRect = FUNC_V_CopyRect; + V_FillRect = V_FillRect16; + V_DrawNumPatch = FUNC_V_DrawNumPatch; + V_DrawBackground = FUNC_V_DrawBackground; + V_PlotPixel = V_PlotPixel16; + V_DrawLine = WRAP_V_DrawLine; + current_videomode = VID_MODE16; + break; + case VID_MODE32: + lprintf(LO_INFO, "V_InitMode: using 32 bit video mode\n"); + V_CopyRect = FUNC_V_CopyRect; + V_FillRect = V_FillRect32; + V_DrawNumPatch = FUNC_V_DrawNumPatch; + V_DrawBackground = FUNC_V_DrawBackground; + V_PlotPixel = V_PlotPixel32; + V_DrawLine = WRAP_V_DrawLine; + current_videomode = VID_MODE32; + break; +#ifdef GL_DOOM + case VID_MODEGL: + lprintf(LO_INFO, "V_InitMode: using OpenGL video mode\n"); + V_CopyRect = WRAP_gld_CopyRect; + V_FillRect = WRAP_gld_FillRect; + V_DrawNumPatch = WRAP_gld_DrawNumPatch; + V_DrawBackground = WRAP_gld_DrawBackground; + V_PlotPixel = V_PlotPixelGL; + V_DrawLine = WRAP_gld_DrawLine; + current_videomode = VID_MODEGL; + break; +#endif + } + R_FilterInit(); +} + +// +// V_GetMode +// +video_mode_t V_GetMode(void) { + return current_videomode; +} + +// +// V_GetModePixelDepth +// +int V_GetModePixelDepth(video_mode_t mode) { + switch (mode) { + case VID_MODE8: return 1; + case VID_MODE15: return 2; + case VID_MODE16: return 2; + case VID_MODE32: return 4; + default: return 0; + } +} + +// +// V_GetNumPixelBits +// +int V_GetNumPixelBits(void) { + switch (current_videomode) { + case VID_MODE8: return 8; + case VID_MODE15: return 15; + case VID_MODE16: return 16; + case VID_MODE32: return 32; + default: return 0; + } +} + +// +// V_GetPixelDepth +// +int V_GetPixelDepth(void) { + return V_GetModePixelDepth(current_videomode); +} + +// +// V_AllocScreen +// +void V_AllocScreen(screeninfo_t *scrn) { + if (!scrn->not_on_heap) + if ((scrn->byte_pitch * scrn->height) > 0) + scrn->data = malloc(scrn->byte_pitch*scrn->height); +} + +// +// V_AllocScreens +// +void V_AllocScreens(void) { + int i; + + for (i=0; inot_on_heap) { + free(scrn->data); + scrn->data = NULL; + } +} + +// +// V_FreeScreens +// +void V_FreeScreens(void) { + int i; + + for (i=0; ia.x < 0 || fl->a.x >= SCREENWIDTH + || fl->a.y < 0 || fl->a.y >= SCREENHEIGHT + || fl->b.x < 0 || fl->b.x >= SCREENWIDTH + || fl->b.y < 0 || fl->b.y >= SCREENHEIGHT + ) + { + //jff 8/3/98 use logical output routine + lprintf(LO_DEBUG, "fuck %d \r", fuck++); + return; + } +#endif + +#define PUTDOT(xx,yy,cc) V_PlotPixel(0,xx,yy,(byte)cc) + + dx = fl->b.x - fl->a.x; + ax = 2 * (dx<0 ? -dx : dx); + sx = dx<0 ? -1 : 1; + + dy = fl->b.y - fl->a.y; + ay = 2 * (dy<0 ? -dy : dy); + sy = dy<0 ? -1 : 1; + + x = fl->a.x; + y = fl->a.y; + + if (ax > ay) + { + d = ay - ax/2; + while (1) + { + PUTDOT(x,y,color); + if (x == fl->b.x) return; + if (d>=0) + { + y += sy; + d -= ax; + } + x += sx; + d += ay; + } + } + else + { + d = ax - ay/2; + while (1) + { + PUTDOT(x, y, color); + if (y == fl->b.y) return; + if (d >= 0) + { + x += sx; + d -= ay; + } + y += sy; + d += ax; + } + } +} diff --git a/src/v_video.h b/src/v_video.h new file mode 100644 index 0000000..76cfaa2 --- /dev/null +++ b/src/v_video.h @@ -0,0 +1,207 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Gamma correction LUT. + * Color range translation support + * Functions to draw patches (by post) directly to screen. + * Functions to blit a block to the screen. + * + *-----------------------------------------------------------------------------*/ + +#ifndef __V_VIDEO__ +#define __V_VIDEO__ + +#include "doomtype.h" +#include "doomdef.h" +// Needed because we are refering to patches. +#include "r_data.h" + +// +// VIDEO +// + +#define CENTERY (SCREENHEIGHT/2) + +// Screen 0 is the screen updated by I_Update screen. +// Screen 1 is an extra buffer. + +// array of pointers to color translation tables +extern const byte *colrngs[]; + +// symbolic indices into color translation table pointer array +typedef enum +{ + CR_BRICK, //0 + CR_TAN, //1 + CR_GRAY, //2 + CR_GREEN, //3 + CR_BROWN, //4 + CR_GOLD, //5 + CR_RED, //6 + CR_BLUE, //7 + CR_ORANGE, //8 + CR_YELLOW, //9 + CR_BLUE2, //10 // proff + CR_LIMIT //11 //jff 2/27/98 added for range check +} crange_idx_e; +//jff 1/16/98 end palette color range additions + +#define CR_DEFAULT CR_RED /* default value for out of range colors */ + +typedef struct { + byte *data; // pointer to the screen content + boolean not_on_heap; // if set, no malloc or free is preformed and + // data never set to NULL. Used i.e. with SDL doublebuffer. + int width; // the width of the surface + int height; // the height of the surface, used when mallocing + int byte_pitch; // tha actual width of one line, used when mallocing + int short_pitch; // tha actual width of one line, used when mallocing + int int_pitch; // tha actual width of one line, used when mallocing +} screeninfo_t; + +#define NUM_SCREENS 6 +extern screeninfo_t screens[NUM_SCREENS]; +extern int usegamma; + +// Varying bit-depth support -POPE +// +// For bilinear filtering, each palette color is pre-weighted and put in a +// table for fast blending operations. These macros decide how many weights +// to create for each color. The lower the number, the lower the blend +// accuracy, which can produce very bad artifacts in texture filtering. +#define VID_NUMCOLORWEIGHTS 64 +#define VID_COLORWEIGHTMASK (VID_NUMCOLORWEIGHTS-1) +#define VID_COLORWEIGHTBITS 6 + +// Palettes for converting from 8 bit color to 16 and 32 bit. Also +// contains the weighted versions of each palette color for filtering +// operations +extern unsigned short *V_Palette15; +extern unsigned short *V_Palette16; +extern unsigned int *V_Palette32; + +#define VID_PAL15(color, weight) V_Palette15[ (color)*VID_NUMCOLORWEIGHTS + (weight) ] +#define VID_PAL16(color, weight) V_Palette16[ (color)*VID_NUMCOLORWEIGHTS + (weight) ] +#define VID_PAL32(color, weight) V_Palette32[ (color)*VID_NUMCOLORWEIGHTS + (weight) ] + +// The available bit-depth modes +typedef enum { + VID_MODE8, + VID_MODE15, + VID_MODE16, + VID_MODE32, + VID_MODEGL, + VID_MODEMAX +} video_mode_t; + +extern const char *default_videomode; + +void V_InitMode(video_mode_t mode); + +// video mode query interface +video_mode_t V_GetMode(void); +int V_GetModePixelDepth(video_mode_t mode); +int V_GetNumPixelBits(void); +int V_GetPixelDepth(void); + +//jff 4/24/98 loads color translation lumps +void V_InitColorTranslation(void); + +// Allocates buffer screens, call before R_Init. +void V_Init (void); + +// V_CopyRect +typedef void (*V_CopyRect_f)(int srcx, int srcy, int srcscrn, + int width, int height, + int destx, int desty, int destscrn, + enum patch_translation_e flags); +extern V_CopyRect_f V_CopyRect; + +// V_FillRect +typedef void (*V_FillRect_f)(int scrn, int x, int y, + int width, int height, byte colour); +extern V_FillRect_f V_FillRect; + +// CPhipps - patch drawing +// Consolidated into the 3 really useful functions: + +// V_DrawNumPatch - Draws the patch from lump num +typedef void (*V_DrawNumPatch_f)(int x, int y, int scrn, + int lump, int cm, + enum patch_translation_e flags); +extern V_DrawNumPatch_f V_DrawNumPatch; + +// V_DrawNamePatch - Draws the patch from lump "name" +#define V_DrawNamePatch(x,y,s,n,t,f) V_DrawNumPatch(x,y,s,W_GetNumForName(n),t,f) + +/* cph - + * Functions to return width & height of a patch. + * Doesn't really belong here, but is often used in conjunction with + * this code. + */ +#define V_NamePatchWidth(name) R_NumPatchWidth(W_GetNumForName(name)) +#define V_NamePatchHeight(name) R_NumPatchHeight(W_GetNumForName(name)) + +/* cphipps 10/99: function to tile a flat over the screen */ +typedef void (*V_DrawBackground_f)(const char* flatname, int scrn); +extern V_DrawBackground_f V_DrawBackground; + +void V_DestroyUnusedTrueColorPalettes(void); +// CPhipps - function to set the palette to palette number pal. +void V_SetPalette(int pal); + +// CPhipps - function to plot a pixel + +// V_PlotPixel +typedef void (*V_PlotPixel_f)(int,int,int,byte); +extern V_PlotPixel_f V_PlotPixel; + +typedef struct +{ + int x, y; +} fpoint_t; + +typedef struct +{ + fpoint_t a, b; +} fline_t; + +// V_DrawLine +typedef void (*V_DrawLine_f)(fline_t* fl, int color); +extern V_DrawLine_f V_DrawLine; + +void V_AllocScreen(screeninfo_t *scrn); +void V_AllocScreens(); +void V_FreeScreen(screeninfo_t *scrn); +void V_FreeScreens(); + +#ifdef GL_DOOM +#include "gl_struct.h" +#endif +#endif diff --git a/src/version.c b/src/version.c new file mode 100644 index 0000000..142017e --- /dev/null +++ b/src/version.c @@ -0,0 +1,38 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Date stamp + * + *----------------------------------------------------------------------------- + */ + + +#include "version.h" + +const char version_date[] = __DATE__; diff --git a/src/version.h b/src/version.h new file mode 100644 index 0000000..f7ad161 --- /dev/null +++ b/src/version.h @@ -0,0 +1,40 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Doom version indicators. + * + *-----------------------------------------------------------------------------*/ + + +#ifndef __DOOMVERSION__ +#define __DOOMVERSION__ + +extern const char version_date[]; + +#endif diff --git a/src/w_mmap.c b/src/w_mmap.c new file mode 100644 index 0000000..51094b4 --- /dev/null +++ b/src/w_mmap.c @@ -0,0 +1,333 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 2001 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Transparent access to data in WADs using mmap + * + *----------------------------------------------------------------------------- + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef HAVE_UNISTD_H +#include +#endif +#ifdef _WIN32 +#define WIN32_LEAN_AND_MEAN +#include +#else +#include +#endif + +#include "doomstat.h" +#include "doomtype.h" + +#ifdef __GNUG__ +#pragma implementation "w_wad.h" +#endif +#include "w_wad.h" +#include "z_zone.h" +#include "lprintf.h" +#include "i_system.h" + +static struct { + void *cache; +#ifdef TIMEDIAG + int locktic; +#endif + int locks; +} *cachelump; + +#ifdef HEAPDUMP +void W_PrintLump(FILE* fp, void* p) { + int i; + for (i=0; i 0) + lprintf(LO_DEBUG, "%8.8s %6u %2d %6d\n", lumpinfo[i].name, + W_LumpLength(i), cachelump[i].locks, gametic - cachelump[i].locktic); + } +} +#endif + +#ifdef _WIN32 +typedef struct { + HANDLE hnd; + OFSTRUCT fileinfo; + HANDLE hnd_map; + void *data; +} mmap_info_t; + +mmap_info_t *mapped_wad; + +void W_DoneCache(void) +{ + size_t i; + + if (cachelump) { + free(cachelump); + cachelump = NULL; + } + + if (!mapped_wad) + return; + for (i=0; i=numwadfiles)) + I_Error("W_InitCache: wad_index out of range"); +#endif + if (!mapped_wad[wad_index].data) + { + mapped_wad[wad_index].hnd = + (HANDLE)OpenFile( + wadfiles[wad_index].name, + &mapped_wad[wad_index].fileinfo, + OF_READ + ); + if (mapped_wad[wad_index].hnd==(HANDLE)HFILE_ERROR) + I_Error("W_InitCache: OpenFile for memory mapping failed (LastError %i)",GetLastError()); + mapped_wad[wad_index].hnd_map = + CreateFileMapping( + mapped_wad[wad_index].hnd, + NULL, + PAGE_READONLY, + 0, + 0, + NULL + ); + if (mapped_wad[wad_index].hnd_map==NULL) + I_Error("W_InitCache: CreateFileMapping for memory mapping failed (LastError %i)",GetLastError()); + mapped_wad[wad_index].data = + MapViewOfFile( + mapped_wad[wad_index].hnd_map, + FILE_MAP_READ, + 0, + 0, + 0 + ); + if (mapped_wad[wad_index].hnd_map==NULL) + I_Error("W_InitCache: MapViewOfFile for memory mapping failed (LastError %i)",GetLastError()); + } + } + } +} + +const void* W_CacheLumpNum(int lump) +{ + int wad_index = (int)(lumpinfo[lump].wadfile-wadfiles); +#ifdef RANGECHECK + if ((wad_index<0)||((size_t)wad_index>=numwadfiles)) + I_Error("W_CacheLumpNum: wad_index out of range"); + if ((unsigned)lump >= (unsigned)numlumps) + I_Error ("W_CacheLumpNum: %i >= numlumps",lump); +#endif + if (!lumpinfo[lump].wadfile) + return NULL; + return (void*)((unsigned char *)mapped_wad[wad_index].data+lumpinfo[lump].position); +} + +#else + +void ** mapped_wad; + +void W_InitCache(void) +{ + int maxfd = 0; + // set up caching + cachelump = calloc(numlumps, sizeof *cachelump); + if (!cachelump) + I_Error ("W_Init: Couldn't allocate lumpcache"); + +#ifdef TIMEDIAG + atexit(W_ReportLocks); +#endif + + { + int i; + for (i=0; ihandle > maxfd) maxfd = lumpinfo[i].wadfile->handle; + } + mapped_wad = calloc(maxfd+1,sizeof *mapped_wad); + { + int i; + for (i=0; ihandle; + if (!mapped_wad[fd]) + if ((mapped_wad[fd] = mmap(NULL,I_Filelength(fd),PROT_READ,MAP_SHARED,fd,0)) == MAP_FAILED) + I_Error("W_InitCache: failed to mmap"); + } + } + } +} + +void W_DoneCache(void) +{ + { + int i; + for (i=0; ihandle; + if (mapped_wad[fd]) { + if (munmap(mapped_wad[fd],I_Filelength(fd))) + I_Error("W_DoneCache: failed to munmap"); + mapped_wad[fd] = NULL; + } + } + } + free(mapped_wad); +} + +const void* W_CacheLumpNum(int lump) +{ +#ifdef RANGECHECK + if ((unsigned)lump >= (unsigned)numlumps) + I_Error ("W_CacheLumpNum: %i >= numlumps",lump); +#endif + if (!lumpinfo[lump].wadfile) + return NULL; + return (mapped_wad[lumpinfo[lump].wadfile->handle]+lumpinfo[lump].position); +} +#endif + +/* + * W_LockLumpNum + * + * This copies the lump into a malloced memory region and returns its address + * instead of returning a pointer into the memory mapped area + * + */ +const void* W_LockLumpNum(int lump) +{ + size_t len = W_LumpLength(lump); + const void *data = W_CacheLumpNum(lump); + + if (!cachelump[lump].cache) { + // read the lump in + Z_Malloc(len, PU_CACHE, &cachelump[lump].cache); + memcpy(cachelump[lump].cache, data, len); + } + + /* cph - if wasn't locked but now is, tell z_zone to hold it */ + if (cachelump[lump].locks <= 0) { + Z_ChangeTag(cachelump[lump].cache,PU_STATIC); +#ifdef TIMEDIAG + cachelump[lump].locktic = gametic; +#endif + // reset lock counter + cachelump[lump].locks = 1; + } else { + // increment lock counter + cachelump[lump].locks += 1; + } + +#ifdef SIMPLECHECKS + if (!((cachelump[lump].locks+1) & 0xf)) + lprintf(LO_DEBUG, "W_CacheLumpNum: High lock on %8s (%d)\n", + lumpinfo[lump].name, cachelump[lump].locks); +#endif + + return cachelump[lump].cache; +} + +void W_UnlockLumpNum(int lump) { + if (cachelump[lump].locks == -1) + return; // this lump is memory mapped + +#ifdef SIMPLECHECKS + if (cachelump[lump].locks == 0) + lprintf(LO_DEBUG, "W_UnlockLumpNum: Excess unlocks on %8s\n", + lumpinfo[lump].name); +#endif + cachelump[lump].locks -= 1; + /* cph - Note: must only tell z_zone to make purgeable if currently locked, + * else it might already have been purged + */ + if (cachelump[lump].locks == 0) + Z_ChangeTag(cachelump[lump].cache, PU_CACHE); +} + diff --git a/src/w_wad.c b/src/w_wad.c new file mode 100644 index 0000000..753a227 --- /dev/null +++ b/src/w_wad.c @@ -0,0 +1,476 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2001 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Handles WAD file header, directory, lump I/O. + * + *----------------------------------------------------------------------------- + */ + +// use config.h if autoconf made one -- josh +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#ifdef HAVE_UNISTD_H +#include +#endif +#ifdef _MSC_VER +#include +#include +#endif +#include + +#include "doomstat.h" +#include "d_net.h" +#include "doomtype.h" +#include "i_system.h" + +#ifdef __GNUG__ +#pragma implementation "w_wad.h" +#endif +#include "w_wad.h" +#include "lprintf.h" + +// +// GLOBALS +// + +// Location of each lump on disk. +lumpinfo_t *lumpinfo; +int numlumps; // killough + +void ExtractFileBase (const char *path, char *dest) +{ + const char *src = path + strlen(path) - 1; + int length; + + // back up until a \ or the start + while (src != path && src[-1] != ':' // killough 3/22/98: allow c:filename + && *(src-1) != '\\' + && *(src-1) != '/') + { + src--; + } + + // copy up to eight characters + memset(dest,0,8); + length = 0; + + while ((*src) && (*src != '.') && (++length<9)) + { + *dest++ = toupper(*src); + *src++; + } + /* cph - length check removed, just truncate at 8 chars. + * If there are 8 or more chars, we'll copy 8, and no zero termination + */ +} + +// +// 1/18/98 killough: adds a default extension to a path +// Note: Backslashes are treated specially, for MS-DOS. +// + +char *AddDefaultExtension(char *path, const char *ext) +{ + char *p = path; + while (*p++); + while (p-->path && *p!='/' && *p!='\\') + if (*p=='.') + return path; + if (*ext!='.') + strcat(path,"."); + return strcat(path,ext); +} + +// +// LUMP BASED ROUTINES. +// + +// +// W_AddFile +// All files are optional, but at least one file must be +// found (PWAD, if all required lumps are present). +// Files with a .wad extension are wadlink files +// with multiple lumps. +// Other files are single lumps with the base filename +// for the lump name. +// +// Reload hack removed by Lee Killough +// CPhipps - source is an enum +// +// proff - changed using pointer to wadfile_info_t +static void W_AddFile(wadfile_info_t *wadfile) +// killough 1/31/98: static, const +{ + wadinfo_t header; + lumpinfo_t* lump_p; + unsigned i; + int length; + int startlump; + filelump_t *fileinfo, *fileinfo2free=NULL; //killough + filelump_t singleinfo; + + // open the file and add to directory + + wadfile->handle = open(wadfile->name,O_RDONLY | O_BINARY); + +#ifdef HAVE_NET + if (wadfile->handle == -1 && D_NetGetWad(wadfile->name)) // CPhipps + wadfile->handle = open(wadfile->name,O_RDONLY | O_BINARY); +#endif + + if (wadfile->handle == -1) + { + if ( strlen(wadfile->name)<=4 || // add error check -- killough + (strcasecmp(wadfile->name+strlen(wadfile->name)-4 , ".lmp" ) && + strcasecmp(wadfile->name+strlen(wadfile->name)-4 , ".gwa" ) ) + ) + I_Error("W_AddFile: couldn't open %s",wadfile->name); + return; + } + + //jff 8/3/98 use logical output routine + lprintf (LO_INFO," adding %s\n",wadfile->name); + startlump = numlumps; + + if ( strlen(wadfile->name)<=4 || + ( + strcasecmp(wadfile->name+strlen(wadfile->name)-4,".wad") && + strcasecmp(wadfile->name+strlen(wadfile->name)-4,".gwa") + ) + ) + { + // single lump file + fileinfo = &singleinfo; + singleinfo.filepos = 0; + singleinfo.size = LONG(I_Filelength(wadfile->handle)); + ExtractFileBase(wadfile->name, singleinfo.name); + numlumps++; + } + else + { + // WAD file + I_Read(wadfile->handle, &header, sizeof(header)); + if (strncmp(header.identification,"IWAD",4) && + strncmp(header.identification,"PWAD",4)) + I_Error("W_AddFile: Wad file %s doesn't have IWAD or PWAD id", wadfile->name); + header.numlumps = LONG(header.numlumps); + header.infotableofs = LONG(header.infotableofs); + length = header.numlumps*sizeof(filelump_t); + fileinfo2free = fileinfo = malloc(length); // killough + lseek(wadfile->handle, header.infotableofs, SEEK_SET); + I_Read(wadfile->handle, fileinfo, length); + numlumps += header.numlumps; + } + + // Fill in lumpinfo + lumpinfo = realloc(lumpinfo, numlumps*sizeof(lumpinfo_t)); + + lump_p = &lumpinfo[startlump]; + + for (i=startlump ; (int)iwadfile = wadfile; // killough 4/25/98 + lump_p->position = LONG(fileinfo->filepos); + lump_p->size = LONG(fileinfo->size); + lump_p->li_namespace = ns_global; // killough 4/17/98 + strncpy (lump_p->name, fileinfo->name, 8); + lump_p->source = wadfile->src; // Ty 08/29/98 + } + + free(fileinfo2free); // killough +} + +// jff 1/23/98 Create routines to reorder the master directory +// putting all flats into one marked block, and all sprites into another. +// This will allow loading of sprites and flats from a PWAD with no +// other changes to code, particularly fast hashes of the lumps. +// +// killough 1/24/98 modified routines to be a little faster and smaller + +static int IsMarker(const char *marker, const char *name) +{ + return !strncasecmp(name, marker, 8) || + (*name == *marker && !strncasecmp(name+1, marker, 7)); +} + +// killough 4/17/98: add namespace tags + +static void W_CoalesceMarkedResource(const char *start_marker, + const char *end_marker, int li_namespace) +{ + lumpinfo_t *marked = malloc(sizeof(*marked) * numlumps); + size_t i, num_marked = 0, num_unmarked = 0; + int is_marked = 0, mark_end = 0; + lumpinfo_t *lump = lumpinfo; + + for (i=numlumps; i--; lump++) + if (IsMarker(start_marker, lump->name)) // start marker found + { // If this is the first start marker, add start marker to marked lumps + if (!num_marked) + { + strncpy(marked->name, start_marker, 8); + marked->size = 0; // killough 3/20/98: force size to be 0 + marked->li_namespace = ns_global; // killough 4/17/98 + marked->wadfile = NULL; + num_marked = 1; + } + is_marked = 1; // start marking lumps + } + else + if (IsMarker(end_marker, lump->name)) // end marker found + { + mark_end = 1; // add end marker below + is_marked = 0; // stop marking lumps + } + else + if (is_marked) // if we are marking lumps, + { // move lump to marked list + marked[num_marked] = *lump; + marked[num_marked++].li_namespace = li_namespace; // killough 4/17/98 + } + else + lumpinfo[num_unmarked++] = *lump; // else move down THIS list + + // Append marked list to end of unmarked list + memcpy(lumpinfo + num_unmarked, marked, num_marked * sizeof(*marked)); + + free(marked); // free marked list + + numlumps = num_unmarked + num_marked; // new total number of lumps + + if (mark_end) // add end marker + { + lumpinfo[numlumps].size = 0; // killough 3/20/98: force size to be 0 + lumpinfo[numlumps].wadfile = NULL; + lumpinfo[numlumps].li_namespace = ns_global; // killough 4/17/98 + strncpy(lumpinfo[numlumps++].name, end_marker, 8); + } +} + +// Hash function used for lump names. +// Must be mod'ed with table size. +// Can be used for any 8-character names. +// by Lee Killough + +unsigned W_LumpNameHash(const char *s) +{ + unsigned hash; + (void) ((hash = toupper(s[0]), s[1]) && + (hash = hash*3+toupper(s[1]), s[2]) && + (hash = hash*2+toupper(s[2]), s[3]) && + (hash = hash*2+toupper(s[3]), s[4]) && + (hash = hash*2+toupper(s[4]), s[5]) && + (hash = hash*2+toupper(s[5]), s[6]) && + (hash = hash*2+toupper(s[6]), + hash = hash*2+toupper(s[7])) + ); + return hash; +} + +// +// W_CheckNumForName +// Returns -1 if name not found. +// +// Rewritten by Lee Killough to use hash table for performance. Significantly +// cuts down on time -- increases Doom performance over 300%. This is the +// single most important optimization of the original Doom sources, because +// lump name lookup is used so often, and the original Doom used a sequential +// search. For large wads with > 1000 lumps this meant an average of over +// 500 were probed during every search. Now the average is under 2 probes per +// search. There is no significant benefit to packing the names into longwords +// with this new hashing algorithm, because the work to do the packing is +// just as much work as simply doing the string comparisons with the new +// algorithm, which minimizes the expected number of comparisons to under 2. +// +// killough 4/17/98: add namespace parameter to prevent collisions +// between different resources such as flats, sprites, colormaps +// + +int (W_CheckNumForName)(register const char *name, register int li_namespace) +{ + // Hash function maps the name to one of possibly numlump chains. + // It has been tuned so that the average chain length never exceeds 2. + + // proff 2001/09/07 - check numlumps==0, this happens when called before WAD loaded + register int i = (numlumps==0)?(-1):(lumpinfo[W_LumpNameHash(name) % (unsigned) numlumps].index); + + // We search along the chain until end, looking for case-insensitive + // matches which also match a namespace tag. Separate hash tables are + // not used for each namespace, because the performance benefit is not + // worth the overhead, considering namespace collisions are rare in + // Doom wads. + + while (i >= 0 && (strncasecmp(lumpinfo[i].name, name, 8) || + lumpinfo[i].li_namespace != li_namespace)) + i = lumpinfo[i].next; + + // Return the matching lump, or -1 if none found. + + return i; +} + +// +// killough 1/31/98: Initialize lump hash table +// + +void W_HashLumps(void) +{ + int i; + + for (i=0; i= numlumps) + I_Error ("W_LumpLength: %i >= numlumps",lump); + return lumpinfo[lump].size; +} + +// +// W_ReadLump +// Loads the lump into the given buffer, +// which must be >= W_LumpLength(). +// + +void W_ReadLump(int lump, void *dest) +{ + lumpinfo_t *l = lumpinfo + lump; + +#ifdef RANGECHECK + if (lump >= numlumps) + I_Error ("W_ReadLump: %i >= numlumps",lump); +#endif + + { + if (l->wadfile) + { + lseek(l->wadfile->handle, l->position, SEEK_SET); + I_Read(l->wadfile->handle, dest, l->size); + } + } +} + diff --git a/src/w_wad.h b/src/w_wad.h new file mode 100644 index 0000000..399869e --- /dev/null +++ b/src/w_wad.h @@ -0,0 +1,146 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * WAD I/O functions. + * + *-----------------------------------------------------------------------------*/ + + +#ifndef __W_WAD__ +#define __W_WAD__ + +#ifdef __GNUG__ +#pragma interface +#endif + +// +// TYPES +// + +typedef struct +{ + char identification[4]; // Should be "IWAD" or "PWAD". + int numlumps; + int infotableofs; +} wadinfo_t; + +typedef struct +{ + int filepos; + int size; + char name[8]; +} filelump_t; + +// +// WADFILE I/O related stuff. +// + +// CPhipps - defined enum in wider scope +// Ty 08/29/98 - add source field to identify where this lump came from +typedef enum { + // CPhipps - define elements in order of 'how new/unusual' + source_iwad=0, // iwad file load + source_pre, // predefined lump + source_auto_load, // lump auto-loaded by config file + source_pwad, // pwad file load + source_lmp, // lmp file load + source_net // CPhipps +} wad_source_t; + +// CPhipps - changed wad init +// We _must_ have the wadfiles[] the same as those actually loaded, so there +// is no point having these separate entities. This belongs here. +typedef struct { + const char* name; + wad_source_t src; + int handle; +} wadfile_info_t; + +extern wadfile_info_t *wadfiles; + +extern size_t numwadfiles; // CPhipps - size of the wadfiles array + +void W_Init(void); // CPhipps - uses the above array +void W_ReleaseAllWads(void); // Proff - Added for iwad switching +void W_InitCache(void); +void W_DoneCache(void); + +typedef struct +{ + // WARNING: order of some fields important (see info.c). + + char name[9]; + int size; + + // killough 1/31/98: hash table fields, used for ultra-fast hash table lookup + int index, next; + + // killough 4/17/98: namespace tags, to prevent conflicts between resources + enum { + ns_global=0, + ns_sprites, + ns_flats, + ns_colormaps, + ns_prboom + } li_namespace; // haleyjd 05/21/02: renamed from "namespace" + + wadfile_info_t *wadfile; + int position; + wad_source_t source; +} lumpinfo_t; + +extern lumpinfo_t *lumpinfo; +extern int numlumps; + +// killough 4/17/98: if W_CheckNumForName() called with only +// one argument, pass ns_global as the default namespace + +#define W_CheckNumForName(name) (W_CheckNumForName)(name, ns_global) +int (W_CheckNumForName)(const char* name, int); // killough 4/17/98 +int W_GetNumForName (const char* name); +int W_LumpLength (int lump); +void W_ReadLump (int lump, void *dest); +// CPhipps - modified for 'new' lump locking +const void* W_CacheLumpNum (int lump); +const void* W_LockLumpNum(int lump); +void W_UnlockLumpNum(int lump); + +// CPhipps - convenience macros +//#define W_CacheLumpNum(num) (W_CacheLumpNum)((num),1) +#define W_CacheLumpName(name) W_CacheLumpNum (W_GetNumForName(name)) + +//#define W_UnlockLumpNum(num) (W_UnlockLumpNum)((num),1) +#define W_UnlockLumpName(name) W_UnlockLumpNum (W_GetNumForName(name)) + +char *AddDefaultExtension(char *, const char *); // killough 1/18/98 +void ExtractFileBase(const char *, char *); // killough +unsigned W_LumpNameHash(const char *s); // killough 1/31/98 +void W_HashLumps(void); // cph 2001/07/07 - made public + +#endif diff --git a/src/wi_stuff.c b/src/wi_stuff.c new file mode 100644 index 0000000..1495a5c --- /dev/null +++ b/src/wi_stuff.c @@ -0,0 +1,1968 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Intermission screens. + * + *----------------------------------------------------------------------------- + */ + +#include "doomstat.h" +#include "m_random.h" +#include "w_wad.h" +#include "g_game.h" +#include "r_main.h" +#include "v_video.h" +#include "wi_stuff.h" +#include "s_sound.h" +#include "sounds.h" +#include "lprintf.h" // jff 08/03/98 - declaration of lprintf +#include "r_draw.h" + +// Ty 03/17/98: flag that new par times have been loaded in d_deh +extern boolean deh_pars; + +// +// Data needed to add patches to full screen intermission pics. +// Patches are statistics messages, and animations. +// Loads of by-pixel layout and placement, offsets etc. +// + +// +// Different vetween registered DOOM (1994) and +// Ultimate DOOM - Final edition (retail, 1995?). +// This is supposedly ignored for commercial +// release (aka DOOM II), which had 34 maps +// in one episode. So there. +#define NUMEPISODES 4 +#define NUMMAPS 9 + + +// Not used +// in tics +//U #define PAUSELEN (TICRATE*2) +//U #define SCORESTEP 100 +//U #define ANIMPERIOD 32 +// pixel distance from "(YOU)" to "PLAYER N" +//U #define STARDIST 10 +//U #define WK 1 + + +// GLOBAL LOCATIONS +#define WI_TITLEY 2 +#define WI_SPACINGY 33 + +// SINGLE-PLAYER STUFF +#define SP_STATSX 50 +#define SP_STATSY 50 + +#define SP_TIMEX 8 +// proff/nicolas 09/20/98 -- changed for hi-res +#define SP_TIMEY 160 +//#define SP_TIMEY (SCREENHEIGHT-32) + + +// NET GAME STUFF +#define NG_STATSY 50 +#define NG_STATSX (32 + V_NamePatchWidth(star)/2 + 32*!dofrags) + +#define NG_SPACINGX 64 + + +// Used to display the frags matrix at endgame +// DEATHMATCH STUFF +#define DM_MATRIXX 42 +#define DM_MATRIXY 68 + +#define DM_SPACINGX 40 + +#define DM_TOTALSX 269 + +#define DM_KILLERSX 10 +#define DM_KILLERSY 100 +#define DM_VICTIMSX 5 +#define DM_VICTIMSY 50 + + +// These animation variables, structures, etc. are used for the +// DOOM/Ultimate DOOM intermission screen animations. This is +// totally different from any sprite or texture/flat animations +typedef enum +{ + ANIM_ALWAYS, // determined by patch entry + ANIM_RANDOM, // occasional + ANIM_LEVEL // continuous +} animenum_t; + +typedef struct +{ + int x; // x/y coordinate pair structure + int y; +} point_t; + + +// +// Animation. +// There is another anim_t used in p_spec. +// +typedef struct +{ + animenum_t type; + + // period in tics between animations + int period; + + // number of animation frames + int nanims; + + // location of animation + point_t loc; + + // ALWAYS: n/a, + // RANDOM: period deviation (<256), + // LEVEL: level + int data1; + + // ALWAYS: n/a, + // RANDOM: random base period, + // LEVEL: n/a + int data2; + + /* actual graphics for frames of animations + * cphipps - const + */ + patchnum_t p[3]; + + // following must be initialized to zero before use! + + // next value of bcnt (used in conjunction with period) + int nexttic; + + // last drawn animation frame + int lastdrawn; + + // next frame number to animate + int ctr; + + // used by RANDOM and LEVEL when animating + int state; +} anim_t; + + +static point_t lnodes[NUMEPISODES][NUMMAPS] = +{ + // Episode 0 World Map + { + { 185, 164 }, // location of level 0 (CJ) + { 148, 143 }, // location of level 1 (CJ) + { 69, 122 }, // location of level 2 (CJ) + { 209, 102 }, // location of level 3 (CJ) + { 116, 89 }, // location of level 4 (CJ) + { 166, 55 }, // location of level 5 (CJ) + { 71, 56 }, // location of level 6 (CJ) + { 135, 29 }, // location of level 7 (CJ) + { 71, 24 } // location of level 8 (CJ) + }, + + // Episode 1 World Map should go here + { + { 254, 25 }, // location of level 0 (CJ) + { 97, 50 }, // location of level 1 (CJ) + { 188, 64 }, // location of level 2 (CJ) + { 128, 78 }, // location of level 3 (CJ) + { 214, 92 }, // location of level 4 (CJ) + { 133, 130 }, // location of level 5 (CJ) + { 208, 136 }, // location of level 6 (CJ) + { 148, 140 }, // location of level 7 (CJ) + { 235, 158 } // location of level 8 (CJ) + }, + + // Episode 2 World Map should go here + { + { 156, 168 }, // location of level 0 (CJ) + { 48, 154 }, // location of level 1 (CJ) + { 174, 95 }, // location of level 2 (CJ) + { 265, 75 }, // location of level 3 (CJ) + { 130, 48 }, // location of level 4 (CJ) + { 279, 23 }, // location of level 5 (CJ) + { 198, 48 }, // location of level 6 (CJ) + { 140, 25 }, // location of level 7 (CJ) + { 281, 136 } // location of level 8 (CJ) + } +}; + + +// +// Animation locations for episode 0 (1). +// Using patches saves a lot of space, +// as they replace 320x200 full screen frames. +// +static anim_t epsd0animinfo[] = +{ + { ANIM_ALWAYS, TICRATE/3, 3, { 224, 104 } }, + { ANIM_ALWAYS, TICRATE/3, 3, { 184, 160 } }, + { ANIM_ALWAYS, TICRATE/3, 3, { 112, 136 } }, + { ANIM_ALWAYS, TICRATE/3, 3, { 72, 112 } }, + { ANIM_ALWAYS, TICRATE/3, 3, { 88, 96 } }, + { ANIM_ALWAYS, TICRATE/3, 3, { 64, 48 } }, + { ANIM_ALWAYS, TICRATE/3, 3, { 192, 40 } }, + { ANIM_ALWAYS, TICRATE/3, 3, { 136, 16 } }, + { ANIM_ALWAYS, TICRATE/3, 3, { 80, 16 } }, + { ANIM_ALWAYS, TICRATE/3, 3, { 64, 24 } } +}; + +static anim_t epsd1animinfo[] = +{ + { ANIM_LEVEL, TICRATE/3, 1, { 128, 136 }, 1 }, + { ANIM_LEVEL, TICRATE/3, 1, { 128, 136 }, 2 }, + { ANIM_LEVEL, TICRATE/3, 1, { 128, 136 }, 3 }, + { ANIM_LEVEL, TICRATE/3, 1, { 128, 136 }, 4 }, + { ANIM_LEVEL, TICRATE/3, 1, { 128, 136 }, 5 }, + { ANIM_LEVEL, TICRATE/3, 1, { 128, 136 }, 6 }, + { ANIM_LEVEL, TICRATE/3, 1, { 128, 136 }, 7 }, + { ANIM_LEVEL, TICRATE/3, 3, { 192, 144 }, 8 }, + { ANIM_LEVEL, TICRATE/3, 1, { 128, 136 }, 8 } +}; + +static anim_t epsd2animinfo[] = +{ + { ANIM_ALWAYS, TICRATE/3, 3, { 104, 168 } }, + { ANIM_ALWAYS, TICRATE/3, 3, { 40, 136 } }, + { ANIM_ALWAYS, TICRATE/3, 3, { 160, 96 } }, + { ANIM_ALWAYS, TICRATE/3, 3, { 104, 80 } }, + { ANIM_ALWAYS, TICRATE/3, 3, { 120, 32 } }, + { ANIM_ALWAYS, TICRATE/4, 3, { 40, 0 } } +}; + +static int NUMANIMS[NUMEPISODES] = +{ + sizeof(epsd0animinfo)/sizeof(anim_t), + sizeof(epsd1animinfo)/sizeof(anim_t), + sizeof(epsd2animinfo)/sizeof(anim_t) +}; + +static anim_t *anims[NUMEPISODES] = +{ + epsd0animinfo, + epsd1animinfo, + epsd2animinfo +}; + + +// +// GENERAL DATA +// + +// +// Locally used stuff. +// +#define FB 0 + + +// States for single-player +#define SP_KILLS 0 +#define SP_ITEMS 2 +#define SP_SECRET 4 +#define SP_FRAGS 6 +#define SP_TIME 8 +#define SP_PAR ST_TIME + +#define SP_PAUSE 1 + +// in seconds +#define SHOWNEXTLOCDELAY 4 +//#define SHOWLASTLOCDELAY SHOWNEXTLOCDELAY + + +// used to accelerate or skip a stage +int acceleratestage; // killough 3/28/98: made global + +// wbs->pnum +static int me; + + // specifies current state +static stateenum_t state; + +// contains information passed into intermission +static wbstartstruct_t* wbs; + +static wbplayerstruct_t* plrs; // wbs->plyr[] + +// used for general timing +static int cnt; + +// used for timing of background animation +static int bcnt; + +// signals to refresh everything for one frame +static int firstrefresh; + +static int cnt_time; +static int cnt_total_time; +static int cnt_par; +static int cnt_pause; + +// +// GRAPHICS +// + +// You Are Here graphic +static const char* yah[2] = { "WIURH0", "WIURH1" }; + +// splat +static const char* splat = "WISPLAT"; + +// %, : graphics +static const char percent[] = {"WIPCNT"}; +static const char colon[] = {"WICOLON"}; + +// 0-9 graphic +static patchnum_t num[10]; + +// minus sign +static const char wiminus[] = {"WIMINUS"}; + +// "Finished!" graphics +static const char finished[] = {"WIF"}; + +// "Entering" graphic +static const char entering[] = {"WIENTER"}; + +// "secret" +static const char sp_secret[] = {"WISCRT2"}; + +// "Kills", "Scrt", "Items", "Frags" +static const char kills[] = {"WIOSTK"}; +static const char secret[] = {"WIOSTS"}; +static const char items[] = {"WIOSTI"}; +static const char frags[] = {"WIFRGS"}; + +// Time sucks. +static const char time1[] = {"WITIME"}; +static const char par[] = {"WIPAR"}; +static const char sucks[] = {"WISUCKS"}; + +// "killers", "victims" +static const char killers[] = {"WIKILRS"}; +static const char victims[] = {"WIVCTMS"}; + +// "Total", your face, your dead face +static const char total[] = {"WIMSTT"}; +static const char star[] = {"STFST01"}; +static const char bstar[] = {"STFDEAD0"}; + +// "red P[1..MAXPLAYERS]" +static const char facebackp[] = {"STPB0"}; + +// +// CODE +// + +static void WI_endDeathmatchStats(void); +static void WI_endNetgameStats(void); +#define WI_endStats WI_endNetgameStats + +/* ==================================================================== + * WI_levelNameLump + * Purpore: Returns the name of the graphic lump containing the name of + * the given level. + * Args: Episode and level, and buffer (must by 9 chars) to write to + * Returns: void + */ +void WI_levelNameLump(int epis, int map, char* buf) +{ + if (gamemode == commercial) { + sprintf(buf, "CWILV%2.2d", map); + } else { + sprintf(buf, "WILV%d%d", epis, map); + } +} + +// ==================================================================== +// WI_slamBackground +// Purpose: Put the full-screen background up prior to patches +// Args: none +// Returns: void +// +static void WI_slamBackground(void) +{ + char name[9]; // limited to 8 characters + + if (gamemode == commercial || (gamemode == retail && wbs->epsd == 3)) + strcpy(name, "INTERPIC"); + else + sprintf(name, "WIMAP%d", wbs->epsd); + + // background + V_DrawNamePatch(0, 0, FB, name, CR_DEFAULT, VPT_STRETCH); +} + + +// ==================================================================== +// WI_Responder +// Purpose: Draw animations on intermission background screen +// Args: ev -- event pointer, not actually used here. +// Returns: False -- dummy routine +// +// The ticker is used to detect keys +// because of timing issues in netgames. +boolean WI_Responder(event_t* ev) +{ + return false; +} + + +// ==================================================================== +// WI_drawLF +// Purpose: Draw the "Finished" level name before showing stats +// Args: none +// Returns: void +// +void WI_drawLF(void) +{ + int y = WI_TITLEY; + char lname[9]; + + // draw + /* cph - get the graphic lump name and use it */ + WI_levelNameLump(wbs->epsd, wbs->last, lname); + // CPhipps - patch drawing updated + V_DrawNamePatch((320 - V_NamePatchWidth(lname))/2, y, + FB, lname, CR_DEFAULT, VPT_STRETCH); + + // draw "Finished!" + y += (5*V_NamePatchHeight(lname))/4; + + // CPhipps - patch drawing updated + V_DrawNamePatch((320 - V_NamePatchWidth(finished))/2, y, + FB, finished, CR_DEFAULT, VPT_STRETCH); +} + + +// ==================================================================== +// WI_drawEL +// Purpose: Draw introductory "Entering" and level name +// Args: none +// Returns: void +// +void WI_drawEL(void) +{ + int y = WI_TITLEY; + char lname[9]; + + /* cph - get the graphic lump name */ + WI_levelNameLump(wbs->epsd, wbs->next, lname); + + // draw "Entering" + // CPhipps - patch drawing updated + V_DrawNamePatch((320 - V_NamePatchWidth(entering))/2, + y, FB, entering, CR_DEFAULT, VPT_STRETCH); + + // draw level + y += (5*V_NamePatchHeight(lname))/4; + + // CPhipps - patch drawing updated + V_DrawNamePatch((320 - V_NamePatchWidth(lname))/2, y, FB, + lname, CR_DEFAULT, VPT_STRETCH); +} + + +/* ==================================================================== + * WI_drawOnLnode + * Purpose: Draw patches at a location based on episode/map + * Args: n -- index to map# within episode + * c[] -- array of names of patches to be drawn + * Returns: void + */ +void +WI_drawOnLnode // draw stuff at a location by episode/map# +( int n, + const char* const c[] ) +{ + int i; + boolean fits = false; + + i = 0; + do + { + int left; + int top; + int right; + int bottom; + const rpatch_t* patch = R_CachePatchName(c[i]); + + left = lnodes[wbs->epsd][n].x - patch->leftoffset; + top = lnodes[wbs->epsd][n].y - patch->topoffset; + right = left + patch->width; + bottom = top + patch->height; + R_UnlockPatchName(c[i]); + + if (left >= 0 + && right < 320 + && top >= 0 + && bottom < 200) + { + fits = true; + } + else + { + i++; + } + } while (!fits && i!=2); + + if (fits && i<2) + { + // CPhipps - patch drawing updated + V_DrawNamePatch(lnodes[wbs->epsd][n].x, lnodes[wbs->epsd][n].y, + FB, c[i], CR_DEFAULT, VPT_STRETCH); + } + else + { + // DEBUG + //jff 8/3/98 use logical output routine + lprintf(LO_DEBUG,"Could not place patch on level %d", n+1); + } +} + + +// ==================================================================== +// WI_initAnimatedBack +// Purpose: Initialize pointers and styles for background animation +// Args: none +// Returns: void +// +void WI_initAnimatedBack(void) +{ + int i; + anim_t* a; + + if (gamemode == commercial) // no animation for DOOM2 + return; + + if (wbs->epsd > 2) + return; + + for (i=0;iepsd];i++) + { + a = &anims[wbs->epsd][i]; + + // init variables + a->ctr = -1; + + // specify the next time to draw it + if (a->type == ANIM_ALWAYS) + a->nexttic = bcnt + 1 + (M_Random()%a->period); + else + if (a->type == ANIM_RANDOM) + a->nexttic = bcnt + 1 + a->data2+(M_Random()%a->data1); + else + if (a->type == ANIM_LEVEL) + a->nexttic = bcnt + 1; + } +} + + +// ==================================================================== +// WI_updateAnimatedBack +// Purpose: Figure out what animation we do on this iteration +// Args: none +// Returns: void +// +void WI_updateAnimatedBack(void) +{ + int i; + anim_t* a; + + if (gamemode == commercial) + return; + + if (wbs->epsd > 2) + return; + + for (i=0;iepsd];i++) + { + a = &anims[wbs->epsd][i]; + + if (bcnt == a->nexttic) + { + switch (a->type) + { + case ANIM_ALWAYS: + if (++a->ctr >= a->nanims) a->ctr = 0; + a->nexttic = bcnt + a->period; + break; + + case ANIM_RANDOM: + a->ctr++; + if (a->ctr == a->nanims) + { + a->ctr = -1; + a->nexttic = bcnt+a->data2+(M_Random()%a->data1); + } + else + a->nexttic = bcnt + a->period; + break; + + case ANIM_LEVEL: + // gawd-awful hack for level anims + if (!(state == StatCount && i == 7) + && wbs->next == a->data1) + { + a->ctr++; + if (a->ctr == a->nanims) a->ctr--; + a->nexttic = bcnt + a->period; + } + break; + } + } + } +} + + +// ==================================================================== +// WI_drawAnimatedBack +// Purpose: Actually do the animation (whew!) +// Args: none +// Returns: void +// +void WI_drawAnimatedBack(void) +{ + int i; + anim_t* a; + + if (gamemode==commercial) //jff 4/25/98 Someone forgot commercial an enum + return; + + if (wbs->epsd > 2) + return; + + for (i=0 ; iepsd] ; i++) + { + a = &anims[wbs->epsd][i]; + + if (a->ctr >= 0) + // CPhipps - patch drawing updated + V_DrawNumPatch(a->loc.x, a->loc.y, FB, a->p[a->ctr].lumpnum, CR_DEFAULT, VPT_STRETCH); + } +} + + +// ==================================================================== +// WI_drawNum +// Purpose: Draws a number. If digits > 0, then use that many digits +// minimum, otherwise only use as many as necessary +// Args: x, y -- location +// n -- the number to be drawn +// digits -- number of digits minimum or zero +// Returns: new x position after drawing (note we are going to the left) +// CPhipps - static +static int WI_drawNum (int x, int y, int n, int digits) +{ + int fontwidth = num[0].width; + int neg; + int temp; + + if (digits < 0) + { + if (!n) + { + // make variable-length zeros 1 digit long + digits = 1; + } + else + { + // figure out # of digits in # + digits = 0; + temp = n; + + while (temp) + { + temp /= 10; + digits++; + } + } + } + + neg = n < 0; + if (neg) + n = -n; + + // if non-number, do not draw it + if (n == 1994) + return 0; + + // draw the new number + while (digits--) + { + x -= fontwidth; + // CPhipps - patch drawing updated + V_DrawNumPatch(x, y, FB, num[ n % 10 ].lumpnum, CR_DEFAULT, VPT_STRETCH); + n /= 10; + } + + // draw a minus sign if necessary + if (neg) + // CPhipps - patch drawing updated + V_DrawNamePatch(x-=8, y, FB, wiminus, CR_DEFAULT, VPT_STRETCH); + + return x; +} + + +// ==================================================================== +// WI_drawPercent +// Purpose: Draws a percentage, really just a call to WI_drawNum +// after putting a percent sign out there +// Args: x, y -- location +// p -- the percentage value to be drawn, no negatives +// Returns: void +// CPhipps - static +static void WI_drawPercent(int x, int y, int p) +{ + if (p < 0) + return; + + // CPhipps - patch drawing updated + V_DrawNamePatch(x, y, FB, percent, CR_DEFAULT, VPT_STRETCH); + WI_drawNum(x, y, p, -1); +} + + +// ==================================================================== +// WI_drawTime +// Purpose: Draws the level completion time or par time, or "Sucks" +// if 1 hour or more +// Args: x, y -- location +// t -- the time value to be drawn +// Returns: void +// +// CPhipps - static +// - largely rewritten to display hours and use slightly better algorithm + +static void WI_drawTime(int x, int y, int t) +{ + int n; + + if (t<0) + return; + + if (t < 100*60*60) + for(;;) { + n = t % 60; + t /= 60; + x = WI_drawNum(x, y, n, (t || n>9) ? 2 : 1) - V_NamePatchWidth(colon); + + // draw + if (t) + // CPhipps - patch drawing updated + V_DrawNamePatch(x, y, FB, colon, CR_DEFAULT, VPT_STRETCH); + else break; + } + else // "sucks" (maybe should be "addicted", even I've never had a 100 hour game ;) + V_DrawNamePatch(x - V_NamePatchWidth(sucks), + y, FB, sucks, CR_DEFAULT, VPT_STRETCH); +} + + +// ==================================================================== +// WI_End +// Purpose: Unloads data structures (inverse of WI_Start) +// Args: none +// Returns: void +// +void WI_End(void) +{ + if (deathmatch) + WI_endDeathmatchStats(); + else if (netgame) + WI_endNetgameStats(); + else + WI_endStats(); +} + + +// ==================================================================== +// WI_initNoState +// Purpose: Clear state, ready for end of level activity +// Args: none +// Returns: void +// +void WI_initNoState(void) +{ + state = NoState; + acceleratestage = 0; + cnt = 10; +} + + +// ==================================================================== +// WI_drawTimeStats +// Purpose: Put the times on the screen +// Args: time, total time, par time, in seconds +// Returns: void +// +// cph - pulled from WI_drawStats below + +static void WI_drawTimeStats(int cnt_time, int cnt_total_time, int cnt_par) +{ + V_DrawNamePatch(SP_TIMEX, SP_TIMEY, FB, time1, CR_DEFAULT, VPT_STRETCH); + WI_drawTime(320/2 - SP_TIMEX, SP_TIMEY, cnt_time); + + V_DrawNamePatch(SP_TIMEX, (SP_TIMEY+200)/2, FB, total, CR_DEFAULT, VPT_STRETCH); + WI_drawTime(320/2 - SP_TIMEX, (SP_TIMEY+200)/2, cnt_total_time); + + // Ty 04/11/98: redid logic: should skip only if with pwad but + // without deh patch + // killough 2/22/98: skip drawing par times on pwads + // Ty 03/17/98: unless pars changed with deh patch + + if (!(modifiedgame && !deh_pars)) + { + if (wbs->epsd < 3) + { + V_DrawNamePatch(320/2 + SP_TIMEX, SP_TIMEY, FB, par, CR_DEFAULT, VPT_STRETCH); + WI_drawTime(320 - SP_TIMEX, SP_TIMEY, cnt_par); + } + } +} + +// ==================================================================== +// WI_updateNoState +// Purpose: Cycle until end of level activity is done +// Args: none +// Returns: void +// +void WI_updateNoState(void) +{ + + WI_updateAnimatedBack(); + + if (!--cnt) + G_WorldDone(); +} + +static boolean snl_pointeron = false; + + +// ==================================================================== +// WI_initShowNextLoc +// Purpose: Prepare to show the next level's location +// Args: none +// Returns: void +// +void WI_initShowNextLoc(void) +{ + if ((gamemode != commercial) && (gamemap == 8)) { + G_WorldDone(); + return; + } + + state = ShowNextLoc; + acceleratestage = 0; + + // e6y: That was pretty easy - only a HEX editor and luck + // There is no more desync on ddt-tas.zip\e4tux231.lmp + // --------- tasdoom.idb --------- + // .text:00031194 loc_31194: ; CODE XREF: WI_updateStats+3A9j + // .text:00031194 mov ds:state, 1 + // .text:0003119E mov ds:acceleratestage, 0 + // .text:000311A8 mov ds:cnt, 3Ch + // nowhere no hide + if (compatibility_level == tasdoom_compatibility) + cnt = 60; + else + cnt = SHOWNEXTLOCDELAY * TICRATE; + + WI_initAnimatedBack(); +} + + +// ==================================================================== +// WI_updateShowNextLoc +// Purpose: Prepare to show the next level's location +// Args: none +// Returns: void +// +void WI_updateShowNextLoc(void) +{ + WI_updateAnimatedBack(); + + if (!--cnt || acceleratestage) + WI_initNoState(); + else + snl_pointeron = (cnt & 31) < 20; +} + + +// ==================================================================== +// WI_drawShowNextLoc +// Purpose: Show the next level's location on animated backgrounds +// Args: none +// Returns: void +// +void WI_drawShowNextLoc(void) +{ + int i; + int last; + + WI_slamBackground(); + + // draw animated background + WI_drawAnimatedBack(); + + if ( gamemode != commercial) + { + if (wbs->epsd > 2) + { + WI_drawEL(); // "Entering..." if not E1 or E2 + return; + } + + last = (wbs->last == 8) ? wbs->next - 1 : wbs->last; + + // draw a splat on taken cities. + for (i=0 ; i<=last ; i++) + WI_drawOnLnode(i, &splat); + + // splat the secret level? + if (wbs->didsecret) + WI_drawOnLnode(8, &splat); + + // draw flashing ptr + if (snl_pointeron) + WI_drawOnLnode(wbs->next, yah); + } + + // draws which level you are entering.. + if ( (gamemode != commercial) + || wbs->next != 30) // check for MAP30 end game + WI_drawEL(); +} + +// ==================================================================== +// WI_drawNoState +// Purpose: Draw the pointer and next location +// Args: none +// Returns: void +// +void WI_drawNoState(void) +{ + snl_pointeron = true; + WI_drawShowNextLoc(); +} + +// ==================================================================== +// WI_fragSum +// Purpose: Calculate frags for this player based on the current totals +// of all the other players. Subtract self-frags. +// Args: playernum -- the player to be calculated +// Returns: the total frags for this player +// +int WI_fragSum(int playernum) +{ + int i; + int frags = 0; + + for (i=0 ; i 999) // Ty 03/17/98 3-digit frag count + dm_frags[i][j] = 999; + + if (dm_frags[i][j] < -999) + dm_frags[i][j] = -999; + + stillticking = true; + } + } + dm_totals[i] = WI_fragSum(i); + + if (dm_totals[i] > 999) + dm_totals[i] = 999; + + if (dm_totals[i] < -999) + dm_totals[i] = -999; // Ty 03/17/98 end 3-digit frag count + } + } + + if (!stillticking) + { + S_StartSound(0, sfx_barexp); + dm_state++; + } + } + else if (dm_state == 4) + { + if (acceleratestage) + { + S_StartSound(0, sfx_slop); + + if ( gamemode == commercial) + WI_initNoState(); + else + WI_initShowNextLoc(); + } + } + else if (dm_state & 1) + { + if (!--cnt_pause) + { + dm_state++; + cnt_pause = TICRATE; + } + } +} + + +// ==================================================================== +// WI_drawDeathmatchStats +// Purpose: Draw the stats on the screen in a matrix +// Args: none +// Returns: void +// +// proff/nicolas 09/20/98 -- changed for hi-res +// CPhipps - patch drawing updated +void WI_drawDeathmatchStats(void) +{ + int i; + int j; + int x; + int y; + int w; + + int lh; // line height + int halfface = V_NamePatchWidth(facebackp)/2; + + lh = WI_SPACINGY; + + WI_slamBackground(); + + // draw animated background + WI_drawAnimatedBack(); + WI_drawLF(); + + // draw stat titles (top line) + V_DrawNamePatch(DM_TOTALSX-V_NamePatchWidth(total)/2, + DM_MATRIXY-WI_SPACINGY+10, FB, total, CR_DEFAULT, VPT_STRETCH); + + V_DrawNamePatch(DM_KILLERSX, DM_KILLERSY, FB, killers, CR_DEFAULT, VPT_STRETCH); + V_DrawNamePatch(DM_VICTIMSX, DM_VICTIMSY, FB, victims, CR_DEFAULT, VPT_STRETCH); + + // draw P? + x = DM_MATRIXX + DM_SPACINGX; + y = DM_MATRIXY; + + for (i=0 ; imaxkills; + cnt_items[i] = (plrs[i].sitems * 100) / wbs->maxitems; + + // killough 2/22/98: Make secrets = 100% if maxsecret = 0: + cnt_secret[i] = wbs->maxsecret ? + (plrs[i].ssecret * 100) / wbs->maxsecret : 100; + if (dofrags) + cnt_frags[i] = WI_fragSum(i); // we had frags + } + S_StartSound(0, sfx_barexp); // bang + ng_state = 10; + } + + if (ng_state == 2) + { + if (!(bcnt&3)) + S_StartSound(0, sfx_pistol); // pop + + stillticking = false; + + for (i=0 ; i= (plrs[i].skills * 100) / wbs->maxkills) + cnt_kills[i] = (plrs[i].skills * 100) / wbs->maxkills; + else + stillticking = true; // still got stuff to tally + } + + if (!stillticking) + { + S_StartSound(0, sfx_barexp); + ng_state++; + } + } + else if (ng_state == 4) + { + if (!(bcnt&3)) + S_StartSound(0, sfx_pistol); + + stillticking = false; + + for (i=0 ; i= (plrs[i].sitems * 100) / wbs->maxitems) + cnt_items[i] = (plrs[i].sitems * 100) / wbs->maxitems; + else + stillticking = true; + } + + if (!stillticking) + { + S_StartSound(0, sfx_barexp); + ng_state++; + } + } + else if (ng_state == 6) + { + if (!(bcnt&3)) + S_StartSound(0, sfx_pistol); + + stillticking = false; + + for (i=0 ; i= (wbs->maxsecret ? (plrs[i].ssecret * 100) / wbs->maxsecret : compatibility_level < lxdoom_1_compatibility ? 0 : 100)) + cnt_secret[i] = wbs->maxsecret ? (plrs[i].ssecret * 100) / wbs->maxsecret : 100; + else + stillticking = true; + } + + if (!stillticking) + { + S_StartSound(0, sfx_barexp); + ng_state += 1 + 2*!dofrags; + } + } + else if (ng_state == 8) + { + if (!(bcnt&3)) + S_StartSound(0, sfx_pistol); + + stillticking = false; + + for (i=0 ; i= (fsum = WI_fragSum(i))) + cnt_frags[i] = fsum; + else + stillticking = true; + } + + if (!stillticking) + { + S_StartSound(0, sfx_pldeth); + ng_state++; + } + } + else if (ng_state == 10) + { + if (acceleratestage) + { + S_StartSound(0, sfx_sgcock); + if ( gamemode == commercial ) + WI_initNoState(); + else + WI_initShowNextLoc(); + } + } + else if (ng_state & 1) + { + if (!--cnt_pause) + { + ng_state++; + cnt_pause = TICRATE; + } + } +} + + +// ==================================================================== +// WI_drawNetgameStats +// Purpose: Put the coop stats on the screen +// Args: none +// Returns: void +// +// proff/nicolas 09/20/98 -- changed for hi-res +// CPhipps - patch drawing updated +void WI_drawNetgameStats(void) +{ + int i; + int x; + int y; + int pwidth = V_NamePatchWidth(percent); + int fwidth = V_NamePatchWidth(facebackp); + + WI_slamBackground(); + + // draw animated background + WI_drawAnimatedBack(); + + WI_drawLF(); + + // draw stat titles (top line) + V_DrawNamePatch(NG_STATSX+NG_SPACINGX-V_NamePatchWidth(kills), + NG_STATSY, FB, kills, CR_DEFAULT, VPT_STRETCH); + + V_DrawNamePatch(NG_STATSX+2*NG_SPACINGX-V_NamePatchWidth(items), + NG_STATSY, FB, items, CR_DEFAULT, VPT_STRETCH); + + V_DrawNamePatch(NG_STATSX+3*NG_SPACINGX-V_NamePatchWidth(secret), + NG_STATSY, FB, secret, CR_DEFAULT, VPT_STRETCH); + + if (dofrags) + V_DrawNamePatch(NG_STATSX+4*NG_SPACINGX-V_NamePatchWidth(frags), + NG_STATSY, FB, frags, CR_DEFAULT, VPT_STRETCH); + + // draw stats + y = NG_STATSY + V_NamePatchHeight(kills); + + for (i=0 ; itotaltimes / TICRATE, wbs->partime / TICRATE); +} + +static int sp_state; + +// ==================================================================== +// WI_initStats +// Purpose: Get ready for single player stats +// Args: none +// Returns: void +// Comment: Seems like we could do all these stats in a more generic +// set of routines that weren't duplicated for dm, coop, sp +// +void WI_initStats(void) +{ + state = StatCount; + acceleratestage = 0; + sp_state = 1; + + // CPhipps - allocate (awful code, I know, but saves changing it all) and initialise + *(cnt_kills = malloc(sizeof(*cnt_kills))) = + *(cnt_items = malloc(sizeof(*cnt_items))) = + *(cnt_secret= malloc(sizeof(*cnt_secret))) = -1; + cnt_time = cnt_par = cnt_total_time = -1; + cnt_pause = TICRATE; + + WI_initAnimatedBack(); +} + +// ==================================================================== +// WI_updateStats +// Purpose: Calculate solo stats +// Args: none +// Returns: void +// +void WI_updateStats(void) +{ + WI_updateAnimatedBack(); + + if (acceleratestage && sp_state != 10) + { + acceleratestage = 0; + cnt_kills[0] = (plrs[me].skills * 100) / wbs->maxkills; + cnt_items[0] = (plrs[me].sitems * 100) / wbs->maxitems; + + // killough 2/22/98: Make secrets = 100% if maxsecret = 0: + cnt_secret[0] = (wbs->maxsecret ? + (plrs[me].ssecret * 100) / wbs->maxsecret : 100); + + cnt_total_time = wbs->totaltimes / TICRATE; + cnt_time = plrs[me].stime / TICRATE; + cnt_par = wbs->partime / TICRATE; + S_StartSound(0, sfx_barexp); + sp_state = 10; + } + + if (sp_state == 2) + { + cnt_kills[0] += 2; + + if (!(bcnt&3)) + S_StartSound(0, sfx_pistol); + + if (cnt_kills[0] >= (plrs[me].skills * 100) / wbs->maxkills) + { + cnt_kills[0] = (plrs[me].skills * 100) / wbs->maxkills; + S_StartSound(0, sfx_barexp); + sp_state++; + } + } + else if (sp_state == 4) + { + cnt_items[0] += 2; + + if (!(bcnt&3)) + S_StartSound(0, sfx_pistol); + + if (cnt_items[0] >= (plrs[me].sitems * 100) / wbs->maxitems) + { + cnt_items[0] = (plrs[me].sitems * 100) / wbs->maxitems; + S_StartSound(0, sfx_barexp); + sp_state++; + } + } + else if (sp_state == 6) + { + cnt_secret[0] += 2; + + if (!(bcnt&3)) + S_StartSound(0, sfx_pistol); + + // killough 2/22/98: Make secrets = 100% if maxsecret = 0: + if ((!wbs->maxsecret && compatibility_level < lxdoom_1_compatibility) || + cnt_secret[0] >= (wbs->maxsecret ? + (plrs[me].ssecret * 100) / wbs->maxsecret : 100)) + { + cnt_secret[0] = (wbs->maxsecret ? + (plrs[me].ssecret * 100) / wbs->maxsecret : 100); + S_StartSound(0, sfx_barexp); + sp_state++; + } + } + else if (sp_state == 8) + { + if (!(bcnt&3)) + S_StartSound(0, sfx_pistol); + + cnt_time += 3; + + if (cnt_time >= plrs[me].stime / TICRATE) + cnt_time = plrs[me].stime / TICRATE; + + cnt_total_time += 3; + + if (cnt_total_time >= wbs->totaltimes / TICRATE) + cnt_total_time = wbs->totaltimes / TICRATE; + + cnt_par += 3; + + if (cnt_par >= wbs->partime / TICRATE) + { + cnt_par = wbs->partime / TICRATE; + + if ((cnt_time >= plrs[me].stime / TICRATE) && (compatibility_level < lxdoom_1_compatibility || cnt_total_time >= wbs->totaltimes / TICRATE)) + { + S_StartSound(0, sfx_barexp); + sp_state++; + } + } + } + else if (sp_state == 10) + { + if (acceleratestage) + { + S_StartSound(0, sfx_sgcock); + + if (gamemode == commercial) + WI_initNoState(); + else + WI_initShowNextLoc(); + } + } + else if (sp_state & 1) + { + if (!--cnt_pause) + { + sp_state++; + cnt_pause = TICRATE; + } + } +} + +// ==================================================================== +// WI_drawStats +// Purpose: Put the solo stats on the screen +// Args: none +// Returns: void +// +// proff/nicolas 09/20/98 -- changed for hi-res +// CPhipps - patch drawing updated +void WI_drawStats(void) +{ + // line height + int lh; + + lh = (3*num[0].height)/2; + + WI_slamBackground(); + + // draw animated background + WI_drawAnimatedBack(); + + WI_drawLF(); + + V_DrawNamePatch(SP_STATSX, SP_STATSY, FB, kills, CR_DEFAULT, VPT_STRETCH); + if (cnt_kills) + WI_drawPercent(320 - SP_STATSX, SP_STATSY, cnt_kills[0]); + + V_DrawNamePatch(SP_STATSX, SP_STATSY+lh, FB, items, CR_DEFAULT, VPT_STRETCH); + if (cnt_items) + WI_drawPercent(320 - SP_STATSX, SP_STATSY+lh, cnt_items[0]); + + V_DrawNamePatch(SP_STATSX, SP_STATSY+2*lh, FB, sp_secret, CR_DEFAULT, VPT_STRETCH); + if (cnt_secret) + WI_drawPercent(320 - SP_STATSX, SP_STATSY+2*lh, cnt_secret[0]); + + WI_drawTimeStats(cnt_time, cnt_total_time, cnt_par); +} + +// ==================================================================== +// WI_checkForAccelerate +// Purpose: See if the player has hit either the attack or use key +// or mouse button. If so we set acceleratestage to 1 and +// all those display routines above jump right to the end. +// Args: none +// Returns: void +// +void WI_checkForAccelerate(void) +{ + int i; + player_t *player; + + // check for button presses to skip delays + for (i=0, player = players ; icmd.buttons & BT_ATTACK) + { + if (!player->attackdown) + acceleratestage = 1; + player->attackdown = true; + } + else + player->attackdown = false; + + if (player->cmd.buttons & BT_USE) + { + if (!player->usedown) + acceleratestage = 1; + player->usedown = true; + } + else + player->usedown = false; + } + } +} + +// ==================================================================== +// WI_Ticker +// Purpose: Do various updates every gametic, for stats, animation, +// checking that intermission music is running, etc. +// Args: none +// Returns: void +// +void WI_Ticker(void) +{ + // counter for general background animation + bcnt++; + + if (bcnt == 1) + { + // intermission music + if ( gamemode == commercial ) + S_ChangeMusic(mus_dm2int, true); + else + S_ChangeMusic(mus_inter, true); + } + + WI_checkForAccelerate(); + + switch (state) + { + case StatCount: + if (deathmatch) WI_updateDeathmatchStats(); + else if (netgame) WI_updateNetgameStats(); + else WI_updateStats(); + break; + + case ShowNextLoc: + WI_updateShowNextLoc(); + break; + + case NoState: + WI_updateNoState(); + break; + } +} + +/* ==================================================================== + * WI_loadData + * Purpose: Initialize intermission data such as background graphics, + * patches, map names, etc. + * Args: none + * Returns: void + * + * CPhipps - modified for new wad lump handling. + * - no longer preload most graphics, other funcs can use + * them by name + */ + +void WI_loadData(void) +{ + int i; + int j; + char name[9]; // limited to 8 characters + anim_t* a; + + if (gamemode != commercial) + { + if (wbs->epsd < 3) + { + for (j=0;jepsd];j++) + { + a = &anims[wbs->epsd][j]; + for (i=0;inanims;i++) + { + // MONDO HACK! + if (wbs->epsd != 1 || j != 8) + { + // animations + sprintf(name, "WIA%d%.2d%.2d", wbs->epsd, j, i); + R_SetPatchNum(&a->p[i], name); + } + else + { + // HACK ALERT! + a->p[i] = anims[1][4].p[i]; + } + } + } + } + } + + for (i=0;i<10;i++) + { + // numbers 0-9 + sprintf(name, "WINUM%d", i); + R_SetPatchNum(&num[i], name); + } +} + + +// ==================================================================== +// WI_Drawer +// Purpose: Call the appropriate stats drawing routine depending on +// what kind of game is being played (DM, coop, solo) +// Args: none +// Returns: void +// +void WI_Drawer (void) +{ + switch (state) + { + case StatCount: + if (deathmatch) + WI_drawDeathmatchStats(); + else if (netgame) + WI_drawNetgameStats(); + else + WI_drawStats(); + break; + + case ShowNextLoc: + WI_drawShowNextLoc(); + break; + + case NoState: + WI_drawNoState(); + break; + } +} + + +// ==================================================================== +// WI_initVariables +// Purpose: Initialize the intermission information structure +// Note: wbstartstruct_t is defined in d_player.h +// Args: wbstartstruct -- pointer to the structure with the data +// Returns: void +// +void WI_initVariables(wbstartstruct_t* wbstartstruct) +{ + + wbs = wbstartstruct; + +#ifdef RANGECHECKING + if (gamemode != commercial) + { + if ( gamemode == retail ) + RNGCHECK(wbs->epsd, 0, 3); + else + RNGCHECK(wbs->epsd, 0, 2); + } + else + { + RNGCHECK(wbs->last, 0, 8); + RNGCHECK(wbs->next, 0, 8); + } + RNGCHECK(wbs->pnum, 0, MAXPLAYERS); + RNGCHECK(wbs->pnum, 0, MAXPLAYERS); +#endif + + acceleratestage = 0; + cnt = bcnt = 0; + firstrefresh = 1; + me = wbs->pnum; + plrs = wbs->plyr; + + if (!wbs->maxkills) + wbs->maxkills = 1; // probably only useful in MAP30 + + if (!wbs->maxitems) + wbs->maxitems = 1; + + if ( gamemode != retail ) + if (wbs->epsd > 2) + wbs->epsd -= 3; +} + +// ==================================================================== +// WI_Start +// Purpose: Call the various init routines +// Note: wbstartstruct_t is defined in d_player.h +// Args: wbstartstruct -- pointer to the structure with the +// intermission data +// Returns: void +// +void WI_Start(wbstartstruct_t* wbstartstruct) +{ + WI_initVariables(wbstartstruct); + WI_loadData(); + + if (deathmatch) + WI_initDeathmatchStats(); + else if (netgame) + WI_initNetgameStats(); + else + WI_initStats(); +} diff --git a/src/wi_stuff.h b/src/wi_stuff.h new file mode 100644 index 0000000..c3363c8 --- /dev/null +++ b/src/wi_stuff.h @@ -0,0 +1,64 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Intermission screens. + * + *-----------------------------------------------------------------------------*/ + +#ifndef __WI_STUFF__ +#define __WI_STUFF__ + +//#include "v_video.h" + +#include "doomdef.h" + +// States for the intermission + +typedef enum +{ + NoState = -1, + StatCount, + ShowNextLoc + +} stateenum_t; + +// Called by main loop, animate the intermission. +void WI_Ticker (void); + +// Called by main loop, +// draws the intermission directly into the screen buffer. +void WI_Drawer (void); + +// Setup for an intermission screen. +void WI_Start(wbstartstruct_t* wbstartstruct); + +// Release intermission screen memory +void WI_End(void); + +#endif diff --git a/src/z_bmalloc.c b/src/z_bmalloc.c new file mode 100644 index 0000000..b415381 --- /dev/null +++ b/src/z_bmalloc.c @@ -0,0 +1,123 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * This is designed to be a fast allocator for small, regularly used block sizes + *----------------------------------------------------------------------------- + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "doomtype.h" +#include "z_zone.h" +#include "z_bmalloc.h" +#include "lprintf.h" + +typedef struct bmalpool_s { + struct bmalpool_s *nextpool; + size_t blocks; + byte used[0]; +} bmalpool_t; + +inline static void* getelem(bmalpool_t *p, size_t size, size_t n) +{ + return (((byte*)p) + sizeof(bmalpool_t) + sizeof(byte)*(p->blocks) + size*n); +} + +inline static PUREFUNC int iselem(const bmalpool_t *pool, size_t size, const void* p) +{ + // CPhipps - need portable # of bytes between pointers + int dif = (const char*)p - (const char*)pool; + + dif -= sizeof(bmalpool_t); + dif -= pool->blocks; + if (dif<0) return -1; + dif /= size; + return (((size_t)dif >= pool->blocks) ? -1 : dif); +} + +enum { unused_block = 0, used_block = 1}; + +void* Z_BMalloc(struct block_memory_alloc_s *pzone) +{ + register bmalpool_t **pool = (bmalpool_t **)&(pzone->firstpool); + while (*pool != NULL) { + byte *p = memchr((*pool)->used, unused_block, (*pool)->blocks); // Scan for unused marker + if (p) { + int n = p - (*pool)->used; +#ifdef SIMPLECHECKS + if ((n<0) || ((size_t)n>=(*pool)->blocks)) + I_Error("Z_BMalloc: memchr returned pointer outside of array"); +#endif + (*pool)->used[n] = used_block; + return getelem(*pool, pzone->size, n); + } else + pool = &((*pool)->nextpool); + } + { + // Nothing available, must allocate a new pool + bmalpool_t *newpool; + + // CPhipps: Allocate new memory, initialised to 0 + + *pool = newpool = Z_Calloc(sizeof(*newpool) + (sizeof(byte) + pzone->size)*(pzone->perpool), + 1, pzone->tag, NULL); + newpool->nextpool = NULL; // NULL = (void*)0 so this is redundant + + // Return element 0 from this pool to satisfy the request + newpool->used[0] = used_block; + newpool->blocks = pzone->perpool; + return getelem(newpool, pzone->size, 0); + } +} + +void Z_BFree(struct block_memory_alloc_s *pzone, void* p) +{ + register bmalpool_t **pool = (bmalpool_t**)&(pzone->firstpool); + + while (*pool != NULL) { + int n = iselem(*pool, pzone->size, p); + if (n >= 0) { +#ifdef SIMPLECHECKS + if ((*pool)->used[n] == unused_block) + I_Error("Z_BFree: Refree in zone %s", pzone->desc); +#endif + (*pool)->used[n] = unused_block; + if (memchr(((*pool)->used), used_block, (*pool)->blocks) == NULL) { + // Block is all unused, can be freed + bmalpool_t *oldpool = *pool; + *pool = (*pool)->nextpool; + Z_Free(oldpool); + } + return; + } else pool = &((*pool)->nextpool); + } + I_Error("Z_BFree: Free not in zone %s", pzone->desc); +} diff --git a/src/z_bmalloc.h b/src/z_bmalloc.h new file mode 100644 index 0000000..ed30c9f --- /dev/null +++ b/src/z_bmalloc.h @@ -0,0 +1,52 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Block memory allocator + * This is designed to be a fast allocator for small, regularly used block sizes + *-----------------------------------------------------------------------------*/ + +struct block_memory_alloc_s { + void *firstpool; + size_t size; + size_t perpool; + int tag; + const char *desc; +}; + +#define DECLARE_BLOCK_MEMORY_ALLOC_ZONE(name) extern struct block_memory_alloc_s name +#define IMPLEMENT_BLOCK_MEMORY_ALLOC_ZONE(name, size, tag, num, desc) \ +struct block_memory_alloc_s name = { NULL, size, num, tag, desc} +#define NULL_BLOCK_MEMORY_ALLOC_ZONE(name) name.firstpool = NULL + +void* Z_BMalloc(struct block_memory_alloc_s *pzone); + +inline static void* Z_BCalloc(struct block_memory_alloc_s *pzone) +{ void *p = Z_BMalloc(pzone); memset(p,0,pzone->size); return p; } + +void Z_BFree(struct block_memory_alloc_s *pzone, void* p); diff --git a/src/z_zone.c b/src/z_zone.c new file mode 100644 index 0000000..9b972fe --- /dev/null +++ b/src/z_zone.c @@ -0,0 +1,705 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Zone Memory Allocation. Neat. + * + * Neat enough to be rewritten by Lee Killough... + * + * Must not have been real neat :) + * + * Made faster and more general, and added wrappers for all of Doom's + * memory allocation functions, including malloc() and similar functions. + * Added line and file numbers, in case of error. Added performance + * statistics and tunables. + *----------------------------------------------------------------------------- + */ + + +// use config.h if autoconf made one -- josh +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include + +#include "z_zone.h" +#include "doomstat.h" +#include "m_argv.h" +#include "v_video.h" +#include "g_game.h" +#include "lprintf.h" + +#ifdef DJGPP +#include +#endif + +// Tunables + +// Alignment of zone memory (benefit may be negated by HEADER_SIZE, CHUNK_SIZE) +#define CACHE_ALIGN 32 + +// Minimum chunk size at which blocks are allocated +#define CHUNK_SIZE 32 + +// Minimum size a block must be to become part of a split +#define MIN_BLOCK_SPLIT (1024) + +// How much RAM to leave aside for other libraries +#define LEAVE_ASIDE (128*1024) + +// Amount to subtract when retrying failed attempts to allocate initial pool +#define RETRY_AMOUNT (256*1024) + +// signature for block header +#define ZONEID 0x931d4a11 + +// Number of mallocs & frees kept in history buffer (must be a power of 2) +#define ZONE_HISTORY 4 + +// End Tunables + +typedef struct memblock { + +#ifdef ZONEIDCHECK + unsigned id; +#endif + + struct memblock *next,*prev; + size_t size; + void **user; + unsigned char tag; + +#ifdef INSTRUMENTED + const char *file; + int line; +#endif + +} memblock_t; + +/* size of block header + * cph - base on sizeof(memblock_t), which can be larger than CHUNK_SIZE on + * 64bit architectures */ +static const size_t HEADER_SIZE = (sizeof(memblock_t)+CHUNK_SIZE-1) & ~(CHUNK_SIZE-1); + +static memblock_t *blockbytag[PU_MAX]; + +// 0 means unlimited, any other value is a hard limit +//static int memory_size = 8192*1024; +static int memory_size = 0; +static int free_memory = 0; + +#ifdef INSTRUMENTED + +// statistics for evaluating performance +static int active_memory = 0; +static int purgable_memory = 0; + +static void Z_DrawStats(void) // Print allocation statistics +{ + if (gamestate != GS_LEVEL) + return; + + if (memory_size > 0) { + unsigned long total_memory = free_memory + memory_size + active_memory + purgable_memory; + double s = 100.0 / total_memory; + + doom_printf("%-5i\t%6.01f%%\tstatic\n" + "%-5i\t%6.01f%%\tpurgable\n" + "%-5i\t%6.01f%%\tfree\n" + "%-5li\t\ttotal\n", + active_memory, + active_memory*s, + purgable_memory, + purgable_memory*s, + (free_memory + memory_size), + (free_memory + memory_size)*s, + total_memory + ); + } else { + unsigned long total_memory = active_memory + purgable_memory; + double s = 100.0 / total_memory; + + doom_printf("%-5i\t%6.01f%%\tstatic\n" + "%-5i\t%6.01f%%\tpurgable\n" + "%-5li\t\ttotal\n", + active_memory, + active_memory*s, + purgable_memory, + purgable_memory*s, + total_memory + ); + } +} + +#ifdef HEAPDUMP + +#ifndef HEAPDUMP_DIR +#define HEAPDUMP_DIR "." +#endif + +void W_PrintLump(FILE* fp, void* p); + +void Z_DumpMemory(void) +{ + static int dump; + char buf[PATH_MAX + 1]; + FILE* fp; + size_t total_cache = 0, total_free = 0, total_malloc = 0; + int tag; + + sprintf(buf, "%s/memdump.%d", HEAPDUMP_DIR, dump++); + fp = fopen(buf, "w"); + for (tag = PU_FREE; tag < PU_MAX; tag++) + { + memblock_t* end_block, *block; + block = blockbytag[tag]; + if (!block) + continue; + end_block = block->prev; + while (1) + { + switch (block->tag) { + case PU_FREE: + fprintf(fp, "free %d\n", block->size); + total_free += block->size; + break; + case PU_CACHE: + fprintf(fp, "cache %s:%d:%d\n", block->file, block->line, block->size); + total_cache += block->size; + break; + case PU_LEVEL: + fprintf(fp, "level %s:%d:%d\n", block->file, block->line, block->size); + total_malloc += block->size; + break; + default: + fprintf(fp, "malloc %s:%d:%d", block->file, block->line, block->size); + total_malloc += block->size; + if (block->file) + if (strstr(block->file,"w_memcache.c")) + W_PrintLump(fp, (char*)block + HEADER_SIZE); + fputc('\n', fp); + break; + } + if (block == end_block) + break; + block=block->next; + } + } + fprintf(fp, "malloc %d, cache %d, free %d, total %d\n", + total_malloc, total_cache, total_free, + total_malloc + total_cache + total_free); + fclose(fp); +} +#endif +#endif + +#ifdef INSTRUMENTED + +// killough 4/26/98: Add history information + +enum {malloc_history, free_history, NUM_HISTORY_TYPES}; + +static const char *file_history[NUM_HISTORY_TYPES][ZONE_HISTORY]; +static int line_history[NUM_HISTORY_TYPES][ZONE_HISTORY]; +static int history_index[NUM_HISTORY_TYPES]; +static const char *const desc[NUM_HISTORY_TYPES] = {"malloc()'s", "free()'s"}; + +void Z_DumpHistory(char *buf) +{ + int i,j; + char s[1024]; + strcat(buf,"\n"); + for (i=0;i= sizeof(memblock_t) && size > HEADER_SIZE)) + I_Error("Z_Init: Sanity check failed"); +#endif + + size = (size+CHUNK_SIZE-1) & ~(CHUNK_SIZE-1); // round to chunk size + size += HEADER_SIZE + CACHE_ALIGN; + + // Allocate the memory + + zonebase=(malloc)(size); + if (!zonebase) + I_Error("Z_Init: Failed on allocation of %lu bytes", (unsigned long)size); + + lprintf(LO_INFO,"Z_Init : Allocated %lukb zone memory\n", + (long unsigned)size / 1000); + + // Align on cache boundary + + zone = (memblock_t *) ((char *) zonebase + CACHE_ALIGN - + ((unsigned) zonebase & (CACHE_ALIGN-1))); + + rover = zone; // Rover points to base of zone mem + zone->next = zone->prev = zone; // Single node + zone->size = size; // All memory in one block + zone->tag = PU_FREE; // A free block + zone->vm = 0; + +#ifdef ZONEIDCHECK + zone->id = 0; +#endif + +#ifdef INSTRUMENTED + free_memory = size; + /* cph - remove unnecessary initialisations to 0 */ +#endif +#ifdef HEAPDUMP + atexit(Z_DumpMemory); +#endif +#endif +} + +/* Z_Malloc + * You can pass a NULL user if the tag is < PU_PURGELEVEL. + * + * cph - the algorithm here was a very simple first-fit round-robin + * one - just keep looping around, freeing everything we can until + * we get a large enough space + * + * This has been changed now; we still do the round-robin first-fit, + * but we only free the blocks we actually end up using; we don't + * free all the stuff we just pass on the way. + */ + +void *(Z_Malloc)(size_t size, int tag, void **user +#ifdef INSTRUMENTED + , const char *file, int line +#endif + ) +{ + memblock_t *block = NULL; + +#ifdef INSTRUMENTED +#ifdef CHECKHEAP + Z_CheckHeap(); +#endif + + file_history[malloc_history][history_index[malloc_history]] = file; + line_history[malloc_history][history_index[malloc_history]++] = line; + history_index[malloc_history] &= ZONE_HISTORY-1; +#endif + +#ifdef ZONEIDCHECK + if (tag >= PU_PURGELEVEL && !user) + I_Error ("Z_Malloc: An owner is required for purgable blocks" +#ifdef INSTRUMENTED + "Source: %s:%d", file, line +#endif + ); +#endif + + if (!size) + return user ? *user = NULL : NULL; // malloc(0) returns NULL + + size = (size+CHUNK_SIZE-1) & ~(CHUNK_SIZE-1); // round to chunk size + + if (memory_size > 0 && ((free_memory + memory_size) < (int)(size + HEADER_SIZE))) + { + memblock_t *end_block; + block = blockbytag[PU_CACHE]; + if (block) + { + end_block = block->prev; + while (1) + { + memblock_t *next = block->next; +#ifdef INSTRUMENTED + (Z_Free)((char *) block + HEADER_SIZE, file, line); +#else + (Z_Free)((char *) block + HEADER_SIZE); +#endif + if (((free_memory + memory_size) >= (int)(size + HEADER_SIZE)) || (block == end_block)) + break; + block = next; // Advance to next block + } + } + block = NULL; + } + +#ifdef HAVE_LIBDMALLOC + while (!(block = dmalloc_malloc(file,line,size + HEADER_SIZE,DMALLOC_FUNC_MALLOC,0,0))) { +#else + while (!(block = (malloc)(size + HEADER_SIZE))) { +#endif + if (!blockbytag[PU_CACHE]) + I_Error ("Z_Malloc: Failure trying to allocate %lu bytes" +#ifdef INSTRUMENTED + "\nSource: %s:%d" +#endif + ,(unsigned long) size +#ifdef INSTRUMENTED + , file, line +#endif + ); + Z_FreeTags(PU_CACHE,PU_CACHE); + } + + if (!blockbytag[tag]) + { + blockbytag[tag] = block; + block->next = block->prev = block; + } + else + { + blockbytag[tag]->prev->next = block; + block->prev = blockbytag[tag]->prev; + block->next = blockbytag[tag]; + blockbytag[tag]->prev = block; + } + + block->size = size; + +#ifdef INSTRUMENTED + if (tag >= PU_PURGELEVEL) + purgable_memory += block->size; + else + active_memory += block->size; +#endif + free_memory -= block->size; + +#ifdef INSTRUMENTED + block->file = file; + block->line = line; +#endif + +#ifdef ZONEIDCHECK + block->id = ZONEID; // signature required in block header +#endif + block->tag = tag; // tag + block->user = user; // user + block = (memblock_t *)((char *) block + HEADER_SIZE); + if (user) // if there is a user + *user = block; // set user to point to new block + +#ifdef INSTRUMENTED + Z_DrawStats(); // print memory allocation stats + // scramble memory -- weed out any bugs + memset(block, gametic & 0xff, size); +#endif + + return block; +} + +void (Z_Free)(void *p +#ifdef INSTRUMENTED + , const char *file, int line +#endif + ) +{ + memblock_t *block = (memblock_t *)((char *) p - HEADER_SIZE); + +#ifdef INSTRUMENTED +#ifdef CHECKHEAP + Z_CheckHeap(); +#endif + file_history[free_history][history_index[free_history]] = file; + line_history[free_history][history_index[free_history]++] = line; + history_index[free_history] &= ZONE_HISTORY-1; +#endif + + if (!p) + return; + + +#ifdef ZONEIDCHECK + if (block->id != ZONEID) + I_Error("Z_Free: freed a pointer without ZONEID" +#ifdef INSTRUMENTED + "\nSource: %s:%d" + "\nSource of malloc: %s:%d" + , file, line, block->file, block->line +#endif + ); + block->id = 0; // Nullify id so another free fails +#endif + + if (block->user) // Nullify user if one exists + *block->user = NULL; + + if (block == block->next) + blockbytag[block->tag] = NULL; + else + if (blockbytag[block->tag] == block) + blockbytag[block->tag] = block->next; + block->prev->next = block->next; + block->next->prev = block->prev; + + free_memory += block->size; +#ifdef INSTRUMENTED + if (block->tag >= PU_PURGELEVEL) + purgable_memory -= block->size; + else + active_memory -= block->size; + + /* scramble memory -- weed out any bugs */ + memset(block, gametic & 0xff, block->size + HEADER_SIZE); +#endif + +#ifdef HAVE_LIBDMALLOC + dmalloc_free(file,line,block,DMALLOC_FUNC_MALLOC); +#else + (free)(block); +#endif +#ifdef INSTRUMENTED + Z_DrawStats(); // print memory allocation stats +#endif +} + +void (Z_FreeTags)(int lowtag, int hightag +#ifdef INSTRUMENTED + , const char *file, int line +#endif + ) +{ +#ifdef HEAPDUMP + Z_DumpMemory(); +#endif + + if (lowtag <= PU_FREE) + lowtag = PU_FREE+1; + + if (hightag > PU_CACHE) + hightag = PU_CACHE; + + for (;lowtag <= hightag; lowtag++) + { + memblock_t *block, *end_block; + block = blockbytag[lowtag]; + if (!block) + continue; + end_block = block->prev; + while (1) + { + memblock_t *next = block->next; +#ifdef INSTRUMENTED + (Z_Free)((char *) block + HEADER_SIZE, file, line); +#else + (Z_Free)((char *) block + HEADER_SIZE); +#endif + if (block == end_block) + break; + block = next; // Advance to next block + } + } +} + +void (Z_ChangeTag)(void *ptr, int tag +#ifdef INSTRUMENTED + , const char *file, int line +#endif + ) +{ + memblock_t *block = (memblock_t *)((char *) ptr - HEADER_SIZE); + + // proff - added sanity check, this can happen when an empty lump is locked + if (!ptr) + return; + + // proff - do nothing if tag doesn't differ + if (tag == block->tag) + return; + +#ifdef INSTRUMENTED +#ifdef CHECKHEAP + Z_CheckHeap(); +#endif +#endif + +#ifdef ZONEIDCHECK + if (block->id != ZONEID) + I_Error ("Z_ChangeTag: freed a pointer without ZONEID" +#ifdef INSTRUMENTED + "\nSource: %s:%d" + "\nSource of malloc: %s:%d" + , file, line, block->file, block->line +#endif + ); + + if (tag >= PU_PURGELEVEL && !block->user) + I_Error ("Z_ChangeTag: an owner is required for purgable blocks\n" +#ifdef INSTRUMENTED + "Source: %s:%d" + "\nSource of malloc: %s:%d" + , file, line, block->file, block->line +#endif + ); + +#endif // ZONEIDCHECK + + if (block == block->next) + blockbytag[block->tag] = NULL; + else + if (blockbytag[block->tag] == block) + blockbytag[block->tag] = block->next; + block->prev->next = block->next; + block->next->prev = block->prev; + + if (!blockbytag[tag]) + { + blockbytag[tag] = block; + block->next = block->prev = block; + } + else + { + blockbytag[tag]->prev->next = block; + block->prev = blockbytag[tag]->prev; + block->next = blockbytag[tag]; + blockbytag[tag]->prev = block; + } + +#ifdef INSTRUMENTED + if (block->tag < PU_PURGELEVEL && tag >= PU_PURGELEVEL) + { + active_memory -= block->size; + purgable_memory += block->size; + } + else + if (block->tag >= PU_PURGELEVEL && tag < PU_PURGELEVEL) + { + active_memory += block->size; + purgable_memory -= block->size; + } +#endif + + block->tag = tag; +} + +void *(Z_Realloc)(void *ptr, size_t n, int tag, void **user +#ifdef INSTRUMENTED + , const char *file, int line +#endif + ) +{ + void *p = (Z_Malloc)(n, tag, user DA(file, line)); + if (ptr) + { + memblock_t *block = (memblock_t *)((char *) ptr - HEADER_SIZE); + memcpy(p, ptr, n <= block->size ? n : block->size); + (Z_Free)(ptr DA(file, line)); + if (user) // in case Z_Free nullified same user + *user=p; + } + return p; +} + +void *(Z_Calloc)(size_t n1, size_t n2, int tag, void **user +#ifdef INSTRUMENTED + , const char *file, int line +#endif + ) +{ + return + (n1*=n2) ? memset((Z_Malloc)(n1, tag, user DA(file, line)), 0, n1) : NULL; +} + +char *(Z_Strdup)(const char *s, int tag, void **user +#ifdef INSTRUMENTED + , const char *file, int line +#endif + ) +{ + return strcpy((Z_Malloc)(strlen(s)+1, tag, user DA(file, line)), s); +} + +void (Z_CheckHeap)( +#ifdef INSTRUMENTED + const char *file, int line +#else + void +#endif + ) +{ +#if 0 + memblock_t *block; // Start at base of zone mem + if (block) + do { // Consistency check (last node treated special) + if ((block->next != zone && + (memblock_t *)((char *) block+HEADER_SIZE+block->size) != block->next) + || block->next->prev != block || block->prev->next != block) + I_Error("Z_CheckHeap: Block size does not touch the next block\n" +#ifdef INSTRUMENTED + "Source: %s:%d" + "\nSource of offending block: %s:%d" + , file, line, block->file, block->line +#endif + ); +//#ifdef INSTRUMENTED +// shouldn't be needed anymore, was just for testing +#if 0 + if (((int)block->file < 0x00001000) && (block->file != NULL) && (block->tag != 0)) { + block->file = NULL; + } +#endif + } while ((block=block->next) != zone); +#endif +} diff --git a/src/z_zone.h b/src/z_zone.h new file mode 100644 index 0000000..f70ce08 --- /dev/null +++ b/src/z_zone.h @@ -0,0 +1,129 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Zone Memory Allocation, perhaps NeXT ObjectiveC inspired. + * Remark: this was the only stuff that, according + * to John Carmack, might have been useful for + * Quake. + * + * Rewritten by Lee Killough, though, since it was not efficient enough. + * + *---------------------------------------------------------------------*/ + +#ifndef __Z_ZONE__ +#define __Z_ZONE__ + +#ifndef __GNUC__ +#define __attribute__(x) +#endif + +// Include system definitions so that prototypes become +// active before macro replacements below are in effect. + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#include +#include +#include +#include + +// ZONE MEMORY +// PU - purge tags. + +enum {PU_FREE, PU_STATIC, PU_SOUND, PU_MUSIC, PU_LEVEL, PU_LEVSPEC, PU_CACHE, + /* Must always be last -- killough */ PU_MAX}; + +#define PU_PURGELEVEL PU_CACHE /* First purgable tag's level */ + +#ifdef INSTRUMENTED +#define DA(x,y) ,x,y +#define DAC(x,y) x,y +#else +#define DA(x,y) +#define DAC(x,y) +#endif + +void *(Z_Malloc)(size_t size, int tag, void **ptr DA(const char *, int)); +void (Z_Free)(void *ptr DA(const char *, int)); +void (Z_FreeTags)(int lowtag, int hightag DA(const char *, int)); +void (Z_ChangeTag)(void *ptr, int tag DA(const char *, int)); +void (Z_Init)(void); +void Z_Close(void); +void *(Z_Calloc)(size_t n, size_t n2, int tag, void **user DA(const char *, int)); +void *(Z_Realloc)(void *p, size_t n, int tag, void **user DA(const char *, int)); +char *(Z_Strdup)(const char *s, int tag, void **user DA(const char *, int)); +void (Z_CheckHeap)(DAC(const char *,int)); // killough 3/22/98: add file/line info +void Z_DumpHistory(char *); + +#ifdef INSTRUMENTED +/* cph - save space if not debugging, don't require file + * and line to memory calls */ +#define Z_Free(a) (Z_Free) (a, __FILE__,__LINE__) +#define Z_FreeTags(a,b) (Z_FreeTags) (a,b, __FILE__,__LINE__) +#define Z_ChangeTag(a,b) (Z_ChangeTag)(a,b, __FILE__,__LINE__) +#define Z_Malloc(a,b,c) (Z_Malloc) (a,b,c, __FILE__,__LINE__) +#define Z_Strdup(a,b,c) (Z_Strdup) (a,b,c, __FILE__,__LINE__) +#define Z_Calloc(a,b,c,d) (Z_Calloc) (a,b,c,d,__FILE__,__LINE__) +#define Z_Realloc(a,b,c,d) (Z_Realloc) (a,b,c,d,__FILE__,__LINE__) +#define Z_CheckHeap() (Z_CheckHeap)(__FILE__,__LINE__) +#endif + +/* cphipps 2001/11/18 - + * If we're using memory mapped file access to WADs, we won't need to maintain + * our own heap. So we *could* let "normal" malloc users use the libc malloc + * directly, for efficiency. Except we do need a wrapper to handle out of memory + * errors... damn, ok, we'll leave it for now. + */ +#ifndef HAVE_LIBDMALLOC +// Remove all definitions before including system definitions + +#undef malloc +#undef free +#undef realloc +#undef calloc +#undef strdup + +#define malloc(n) Z_Malloc(n,PU_STATIC,0) +#define free(p) Z_Free(p) +#define realloc(p,n) Z_Realloc(p,n,PU_STATIC,0) +#define calloc(n1,n2) Z_Calloc(n1,n2,PU_STATIC,0) +#define strdup(s) Z_Strdup(s,PU_STATIC,0) + +#else + +#ifdef HAVE_LIBDMALLOC +#include +#endif + +#endif + +void Z_ZoneHistory(char *); + +#endif -- cgit v1.2.3