Repository: maximevince/fbDOOM Branch: master Commit: 17280163bc95 Files: 215 Total size: 1.8 MB Directory structure: gitextract_f8lk69kj/ ├── README.TXT ├── fbdoom/ │ ├── Makefile │ ├── am_map.c │ ├── am_map.h │ ├── config.h │ ├── d_englsh.h │ ├── d_event.c │ ├── d_event.h │ ├── d_items.c │ ├── d_items.h │ ├── d_iwad.c │ ├── d_iwad.h │ ├── d_loop.c │ ├── d_loop.h │ ├── d_main.c │ ├── d_main.h │ ├── d_mode.c │ ├── d_mode.h │ ├── d_net.c │ ├── d_player.h │ ├── d_textur.h │ ├── d_think.h │ ├── d_ticcmd.h │ ├── deh_main.h │ ├── deh_misc.h │ ├── deh_str.h │ ├── doom.h │ ├── doomdata.h │ ├── doomdef.c │ ├── doomdef.h │ ├── doomfeatures.h │ ├── doomkeys.h │ ├── doomstat.c │ ├── doomstat.h │ ├── doomtype.h │ ├── dstrings.c │ ├── dstrings.h │ ├── dummy.c │ ├── f_finale.c │ ├── f_finale.h │ ├── f_wipe.c │ ├── f_wipe.h │ ├── g_game.c │ ├── g_game.h │ ├── gusconf.c │ ├── gusconf.h │ ├── hu_lib.c │ ├── hu_lib.h │ ├── hu_stuff.c │ ├── hu_stuff.h │ ├── i_cdmus.c │ ├── i_cdmus.h │ ├── i_endoom.c │ ├── i_endoom.h │ ├── i_input_sdl.c │ ├── i_input_tty.c │ ├── i_input_tty.h │ ├── i_joystick.c │ ├── i_joystick.h │ ├── i_main.c │ ├── i_oplmusic.c │ ├── i_pcsound.c │ ├── i_scale.c │ ├── i_scale.h │ ├── i_sdlmusic.c │ ├── i_sdlsound.c │ ├── i_sound.c │ ├── i_sound.h │ ├── i_sound_dummy.c │ ├── i_swap.h │ ├── i_system.c │ ├── i_system.h │ ├── i_timer.c │ ├── i_timer.h │ ├── i_video.c │ ├── i_video.h │ ├── i_video_fbdev.c │ ├── icon.c │ ├── info.c │ ├── info.h │ ├── m_argv.c │ ├── m_argv.h │ ├── m_bbox.c │ ├── m_bbox.h │ ├── m_cheat.c │ ├── m_cheat.h │ ├── m_config.c │ ├── m_config.h │ ├── m_controls.c │ ├── m_controls.h │ ├── m_fixed.c │ ├── m_fixed.h │ ├── m_menu.c │ ├── m_menu.h │ ├── m_misc.c │ ├── m_misc.h │ ├── m_random.c │ ├── m_random.h │ ├── memio.c │ ├── memio.h │ ├── net_client.h │ ├── net_dedicated.h │ ├── net_defs.h │ ├── net_gui.h │ ├── net_io.h │ ├── net_loop.h │ ├── net_packet.h │ ├── net_query.h │ ├── net_sdl.h │ ├── net_server.h │ ├── p_ceilng.c │ ├── p_doors.c │ ├── p_enemy.c │ ├── p_floor.c │ ├── p_inter.c │ ├── p_inter.h │ ├── p_lights.c │ ├── p_local.h │ ├── p_map.c │ ├── p_maputl.c │ ├── p_mobj.c │ ├── p_mobj.h │ ├── p_plats.c │ ├── p_pspr.c │ ├── p_pspr.h │ ├── p_saveg.c │ ├── p_saveg.h │ ├── p_setup.c │ ├── p_setup.h │ ├── p_sight.c │ ├── p_spec.c │ ├── p_spec.h │ ├── p_switch.c │ ├── p_telept.c │ ├── p_tick.c │ ├── p_tick.h │ ├── p_user.c │ ├── r_bsp.c │ ├── r_bsp.h │ ├── r_data.c │ ├── r_data.h │ ├── r_defs.h │ ├── r_draw.c │ ├── r_draw.h │ ├── r_local.h │ ├── r_main.c │ ├── r_main.h │ ├── r_plane.c │ ├── r_plane.h │ ├── r_segs.c │ ├── r_segs.h │ ├── r_sky.c │ ├── r_sky.h │ ├── r_state.h │ ├── r_things.c │ ├── r_things.h │ ├── s_sound.c │ ├── s_sound.h │ ├── sha1.c │ ├── sha1.h │ ├── sounds.c │ ├── sounds.h │ ├── st_lib.c │ ├── st_lib.h │ ├── st_stuff.c │ ├── st_stuff.h │ ├── statdump.c │ ├── statdump.h │ ├── stubs.c │ ├── tables.c │ ├── tables.h │ ├── v_patch.h │ ├── v_video.c │ ├── v_video.h │ ├── w_checksum.c │ ├── w_checksum.h │ ├── w_file.c │ ├── w_file.h │ ├── w_file_stdc.c │ ├── w_file_stdc_unbuffered.c │ ├── w_main.c │ ├── w_main.h │ ├── w_merge.h │ ├── w_wad.c │ ├── w_wad.h │ ├── wi_stuff.c │ ├── wi_stuff.h │ ├── z_zone.c │ └── z_zone.h ├── ipx/ │ ├── DOOMNET.C │ ├── DOOMNET.H │ ├── IPXNET.C │ ├── IPXNET.H │ ├── IPXSETUP.C │ ├── IPXSTR.H │ ├── IPX_FRCH.H │ └── README ├── sersrc/ │ ├── DOOMNET.C │ ├── DOOMNET.H │ ├── PORT.C │ ├── README.TXT │ ├── SERSETUP.C │ ├── SERSETUP.H │ ├── SERSTR.H │ └── SER_FRCH.H └── sndserv/ ├── Makefile ├── README.sndserv ├── linux.c ├── sounds.c ├── sounds.h ├── soundsrv.c ├── soundsrv.h ├── soundst.h ├── wadread.c └── wadread.h ================================================ FILE CONTENTS ================================================ ================================================ FILE: README.TXT ================================================ ================ = fbDOOM = ================ Adaptation of the original DOOM to be easily portable to framebuffer devices with minimal depenencies. Runs on: * Plain desktop Linux framebuffer, * Siglent SSA 3021X Spectrum Analyzer, * Frosted OS To build (example): * cd fbdoom * make CROSS_COMPILE=arm-linux-gnueabihf- Original readme =============== Here it is, at long last. The DOOM source code is released for your non-profit use. You still need real DOOM data to work with this code. If you don't actually own a real copy of one of the DOOMs, you should still be able to find them at software stores. Many thanks to Bernd Kreimeier for taking the time to clean up the project and make sure that it actually works. Projects tends to rot if you leave it alone for a few years, and it takes effort for someone to deal with it again. The bad news: this code only compiles and runs on linux. We couldn't release the dos code because of a copyrighted sound library we used (wow, was that a mistake -- I write my own sound code now), and I honestly don't even know what happened to the port that microsoft did to windows. Still, the code is quite portable, and it should be straightforward to bring it up on just about any platform. I wrote this code a long, long time ago, and there are plenty of things that seem downright silly in retrospect (using polar coordinates for clipping comes to mind), but overall it should still be a usefull base to experiment and build on. The basic rendering concept -- horizontal and vertical lines of constant Z with fixed light shading per band was dead-on, but the implementation could be improved dramatically from the original code if it were revisited. The way the rendering proceded from walls to floors to sprites could be collapsed into a single front-to-back walk of the bsp tree to collect information, then draw all the contents of a subsector on the way back up the tree. It requires treating floors and ceilings as polygons, rather than just the gaps between walls, and it requires clipping sprite billboards into subsector fragments, but it would be The Right Thing. The movement and line of sight checking against the lines is one of the bigger misses that I look back on. It is messy code that had some failure cases, and there was a vastly simpler (and faster) solution sitting in front of my face. I used the BSP tree for rendering things, but I didn't realize at the time that it could also be used for environment testing. Replacing the line of sight test with a bsp line clip would be pretty easy. Sweeping volumes for movement gets a bit tougher, and touches on many of the challenges faced in quake / quake2 with edge bevels on polyhedrons. Some project ideas: Port it to your favorite operating system. Add some rendering features -- transparency, look up / down, slopes, etc. Add some game features -- weapons, jumping, ducking, flying, etc. Create a packet server based internet game. Create a client / server based internet game. Do a 3D accelerated version. On modern hardware (fast pentium + 3DFX) you probably wouldn't even need to be clever -- you could just draw the entire level and get reasonable speed. With a touch of effort, it should easily lock at 60 fps (well, there are some issues with DOOM's 35 hz timebase...). The biggest issues would probably be the non-power of two texture sizes and the walls composed of multiple textures. I don't have a real good guess at how many people are going to be playing with this, but if significant projects are undertaken, it would be cool to see a level of community cooperation. I know that most early projects are going to be rough hacks done in isolation, but I would be very pleased to see a coordinated 'net release of an improved, backwards compatable version of DOOM on multiple platforms next year. Have fun. John Carmack 12-23-97 ================================================ FILE: fbdoom/Makefile ================================================ ################################################################ # # $Id:$ # # $Log:$ # CROSS_COMPILE ?= #arm-linux-gnueabihf- ifeq ($(V),1) VB='' else VB=@ endif #LIBS+=-lXext -lX11 -lnsl -lm -lSDL #CFLAGS+=-Wunused-const-variable=0 #CFLAGS+=-fsanitize=address OBJS+=$(OBJDIR)/i_video_fbdev.o OBJS+=$(OBJDIR)/i_input_tty.o CC=$(CROSS_COMPILE)gcc # gcc or g++ CFLAGS+=-ggdb3 -Os LDFLAGS+=-Wl,--gc-sections CFLAGS+=-ggdb3 -Wall -DNORMALUNIX -DLINUX -DSNDSERV # -DUSEASM LIBS+=-lm -lc ifneq ($(NOSDL),1) LIBS+= -lSDL endif # subdirectory for objects OBJDIR=build OUTPUT=fbdoom SRC_DOOM = i_main.o dummy.o am_map.o doomdef.o doomstat.o dstrings.o d_event.o d_items.o d_iwad.o d_loop.o d_main.o d_mode.o d_net.o f_finale.o f_wipe.o g_game.o hu_lib.o hu_stuff.o info.o i_cdmus.o i_endoom.o i_joystick.o i_scale.o i_sound.o i_system.o i_timer.o memio.o m_argv.o m_bbox.o m_cheat.o m_config.o m_controls.o m_fixed.o m_menu.o m_misc.o m_random.o p_ceilng.o p_doors.o p_enemy.o p_floor.o p_inter.o p_lights.o p_map.o p_maputl.o p_mobj.o p_plats.o p_pspr.o p_saveg.o p_setup.o p_sight.o p_spec.o p_switch.o p_telept.o p_tick.o p_user.o r_bsp.o r_data.o r_draw.o r_main.o r_plane.o r_segs.o r_sky.o r_things.o sha1.o sounds.o statdump.o st_lib.o st_stuff.o s_sound.o tables.o v_video.o wi_stuff.o w_checksum.o w_file.o w_file_stdc_unbuffered.o w_main.o w_wad.o z_zone.o OBJS += $(addprefix $(OBJDIR)/, $(SRC_DOOM)) all: $(OUTPUT) clean: rm -rf $(OBJDIR) rm -f $(OUTPUT) rm -f $(OUTPUT).gdb rm -f $(OUTPUT).map $(OUTPUT): $(OBJS) @echo [Linking $@] $(VB)$(CC) $(CFLAGS) $(LDFLAGS) $(OBJS) \ -o $(OUTPUT) $(LIBS) -Wl,-Map,$(OUTPUT).map @echo [Size] -$(CROSS_COMPILE)size $(OUTPUT) $(OBJS): | $(OBJDIR) $(OBJDIR): mkdir -p $(OBJDIR) $(OBJDIR)/%.o: %.c @echo [Compiling $<] $(VB)$(CC) $(CFLAGS) -c $< -o $@ print: @echo OBJS: $(OBJS) ================================================ FILE: fbdoom/am_map.c ================================================ // // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // 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. // // // DESCRIPTION: the automap code // #include #include "deh_main.h" #include "z_zone.h" #include "doomkeys.h" #include "doomdef.h" #include "st_stuff.h" #include "p_local.h" #include "w_wad.h" #include "m_cheat.h" #include "m_controls.h" #include "m_misc.h" #include "i_system.h" // Needs access to LFB. #include "v_video.h" // State. #include "doomstat.h" #include "r_state.h" // Data. #include "dstrings.h" #include "am_map.h" // For use if I do walls with outsides/insides #define REDS (256-5*16) #define REDRANGE 16 #define BLUES (256-4*16+8) #define BLUERANGE 8 #define GREENS (7*16) #define GREENRANGE 16 #define GRAYS (6*16) #define GRAYSRANGE 16 #define BROWNS (4*16) #define BROWNRANGE 16 #define YELLOWS (256-32+7) #define YELLOWRANGE 1 #define BLACK 0 #define WHITE (256-47) // Automap colors #define BACKGROUND BLACK #define YOURCOLORS WHITE #define YOURRANGE 0 #define WALLCOLORS REDS #define WALLRANGE REDRANGE #define TSWALLCOLORS GRAYS #define TSWALLRANGE GRAYSRANGE #define FDWALLCOLORS BROWNS #define FDWALLRANGE BROWNRANGE #define CDWALLCOLORS YELLOWS #define CDWALLRANGE YELLOWRANGE #define THINGCOLORS GREENS #define THINGRANGE GREENRANGE #define SECRETWALLCOLORS WALLCOLORS #define SECRETWALLRANGE WALLRANGE #define GRIDCOLORS (GRAYS + GRAYSRANGE/2) #define GRIDRANGE 0 #define XHAIRCOLORS GRAYS // drawing stuff #define AM_NUMMARKPOINTS 10 // 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)) // translates between frame-buffer and map distances #define FTOM(x) FixedMul(((x)<<16),scale_ftom) #define MTOF(x) (FixedMul((x),scale_mtof)>>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))) // the following is crap #define LINE_NEVERSEE ML_DONTDRAW typedef struct { int x, y; } fpoint_t; typedef struct { fpoint_t a, b; } fline_t; typedef struct { fixed_t x,y; } mpoint_t; typedef struct { mpoint_t a, b; } mline_t; typedef struct { fixed_t slp, islp; } islope_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 R ((8*PLAYERRADIUS)/7) mline_t cheat_player_arrow[] = { { { -R+R/8, 0 }, { R, 0 } }, // ----- { { R, 0 }, { R-R/2, R/6 } }, // -----> { { R, 0 }, { R-R/2, -R/6 } }, { { -R+R/8, 0 }, { -R-R/8, R/6 } }, // >-----> { { -R+R/8, 0 }, { -R-R/8, -R/6 } }, { { -R+3*R/8, 0 }, { -R+R/8, R/6 } }, // >>-----> { { -R+3*R/8, 0 }, { -R+R/8, -R/6 } }, { { -R/2, 0 }, { -R/2, -R/6 } }, // >>-d---> { { -R/2, -R/6 }, { -R/2+R/6, -R/6 } }, { { -R/2+R/6, -R/6 }, { -R/2+R/6, R/4 } }, { { -R/6, 0 }, { -R/6, -R/6 } }, // >>-dd--> { { -R/6, -R/6 }, { 0, -R/6 } }, { { 0, -R/6 }, { 0, R/4 } }, { { R/6, R/4 }, { R/6, -R/7 } }, // >>-ddt-> { { R/6, -R/7 }, { R/6+R/32, -R/7-R/32 } }, { { R/6+R/32, -R/7-R/32 }, { R/6+R/10, -R/7 } } }; #undef R #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 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 static int cheating = 0; static int grid = 0; static int leveljuststarted = 1; // kluge until AM_LevelInit() is called boolean automapactive = false; static int finit_width = SCREENWIDTH; static int finit_height = SCREENHEIGHT - 32; // 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 int lightlev; // used for funky strobing effect static byte* fb; // pseudo-frame buffer static int amclock; static mpoint_t m_paninc; // how far the window pans each tic (map coords) static fixed_t mtof_zoommul; // how far the window zooms in each tic (map coords) static fixed_t ftom_zoommul; // how far the window zooms in each tic (fb coords) static fixed_t m_x, m_y; // LL x,y where the window is on the map (map coords) static fixed_t m_x2, m_y2; // UR x,y where the window is 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 static patch_t *marknums[10]; // numbers used for marking by the automap static mpoint_t markpoints[AM_NUMMARKPOINTS]; // where the points are static int markpointnum = 0; // next point to be assigned static int followplayer = 1; // specifies whether to follow the player around cheatseq_t cheat_amap = CHEAT("iddt", 0); static boolean stopped = true; // Calculates the slope and slope according to the x-axis of a line // segment in map coordinates (with the upright y-axis n' all) so // that it can be used with the brain-dead drawing stuff. void AM_getIslope ( mline_t* ml, islope_t* is ) { int dx, dy; dy = ml->a.y - ml->b.y; dx = ml->b.x - ml->a.x; if (!dy) is->islp = (dx<0?-INT_MAX:INT_MAX); else is->islp = FixedDiv(dx, dy); if (!dx) is->slp = (dy<0?-INT_MAX:INT_MAX); else is->slp = FixedDiv(dy, dx); } // // // 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; } // // // void AM_saveScaleAndLoc(void) { old_m_x = m_x; old_m_y = m_y; old_m_w = m_w; old_m_h = m_h; } // // // void AM_restoreScaleAndLoc(void) { m_w = old_m_w; m_h = old_m_h; if (!followplayer) { m_x = old_m_x; m_y = old_m_y; } else { m_x = plr->mo->x - m_w/2; m_y = plr->mo->y - m_h/2; } m_x2 = m_x + m_w; m_y2 = m_y + m_h; // Change the scaling multipliers scale_mtof = FixedDiv(f_w< 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 - min_x; max_h = max_y - min_y; 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; } // // // void AM_initVariables(void) { int pnum; static event_t st_notify = { ev_keyup, AM_MSGENTERED, 0, 0 }; automapactive = true; fb = I_VideoBuffer; f_oldloc.x = INT_MAX; amclock = 0; lightlev = 0; 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[consoleplayer]) { plr = &players[consoleplayer]; } else { plr = &players[0]; for (pnum=0;pnummo->x - m_w/2; m_y = plr->mo->y - m_h/2; 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); } // // // void AM_loadPics(void) { int i; char namebuf[9]; for (i=0;i<10;i++) { DEH_snprintf(namebuf, 9, "AMMNUM%d", i); marknums[i] = W_CacheLumpName(namebuf, PU_STATIC); } } void AM_unloadPics(void) { int i; char namebuf[9]; for (i=0;i<10;i++) { DEH_snprintf(namebuf, 9, "AMMNUM%d", i); W_ReleaseLumpName(namebuf); } } void AM_clearMarks(void) { int i; for (i=0;i max_scale_mtof) scale_mtof = min_scale_mtof; scale_ftom = FixedDiv(FRACUNIT, scale_mtof); } // // // void AM_Stop (void) { static event_t st_notify = { 0, ev_keyup, AM_MSGEXITED, 0 }; AM_unloadPics(); automapactive = false; ST_Responder(&st_notify); stopped = true; } // // // 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(); } // // set the window scale to the maximum size // void AM_minOutWindowScale(void) { scale_mtof = min_scale_mtof; scale_ftom = FixedDiv(FRACUNIT, scale_mtof); AM_activateNewScale(); } // // set the window scale to the minimum size // void AM_maxOutWindowScale(void) { scale_mtof = max_scale_mtof; scale_ftom = FixedDiv(FRACUNIT, scale_mtof); AM_activateNewScale(); } // // Handle events (user inputs) in automap mode // boolean AM_Responder ( event_t* ev ) { int rc; static int bigstate=0; static char buffer[20]; int key; rc = false; if (!automapactive) { if (ev->type == ev_keydown && ev->data1 == key_map_toggle) { AM_Start (); viewactive = false; rc = true; } } else if (ev->type == ev_keydown) { rc = true; key = ev->data1; if (key == key_map_east) // pan right { if (!followplayer) m_paninc.x = FTOM(F_PANINC); else rc = false; } else if (key == key_map_west) // pan left { if (!followplayer) m_paninc.x = -FTOM(F_PANINC); else rc = false; } else if (key == key_map_north) // pan up { if (!followplayer) m_paninc.y = FTOM(F_PANINC); else rc = false; } else if (key == key_map_south) // pan down { if (!followplayer) m_paninc.y = -FTOM(F_PANINC); else rc = false; } else if (key == key_map_zoomout) // zoom out { mtof_zoommul = M_ZOOMOUT; ftom_zoommul = M_ZOOMIN; } else if (key == key_map_zoomin) // zoom in { mtof_zoommul = M_ZOOMIN; ftom_zoommul = M_ZOOMOUT; } else if (key == key_map_toggle) { bigstate = 0; viewactive = true; AM_Stop (); } else if (key == key_map_maxzoom) { bigstate = !bigstate; if (bigstate) { AM_saveScaleAndLoc(); AM_minOutWindowScale(); } else AM_restoreScaleAndLoc(); } else if (key == key_map_follow) { followplayer = !followplayer; f_oldloc.x = INT_MAX; if (followplayer) plr->message = DEH_String(AMSTR_FOLLOWON); else plr->message = DEH_String(AMSTR_FOLLOWOFF); } else if (key == key_map_grid) { grid = !grid; if (grid) plr->message = DEH_String(AMSTR_GRIDON); else plr->message = DEH_String(AMSTR_GRIDOFF); } else if (key == key_map_mark) { M_snprintf(buffer, sizeof(buffer), "%s %d", DEH_String(AMSTR_MARKEDSPOT), markpointnum); plr->message = buffer; AM_addMark(); } else if (key == key_map_clearmark) { AM_clearMarks(); plr->message = DEH_String(AMSTR_MARKSCLEARED); } else { rc = false; } if (!deathmatch && cht_CheckCheat(&cheat_amap, ev->data2)) { rc = false; cheating = (cheating+1) % 3; } } else if (ev->type == ev_keyup) { rc = false; key = ev->data1; if (key == key_map_east) { if (!followplayer) m_paninc.x = 0; } else if (key == key_map_west) { if (!followplayer) m_paninc.x = 0; } else if (key == key_map_north) { if (!followplayer) m_paninc.y = 0; } else if (key == key_map_south) { if (!followplayer) m_paninc.y = 0; } else if (key == key_map_zoomout || key == key_map_zoomin) { mtof_zoommul = FRACUNIT; ftom_zoommul = FRACUNIT; } } return rc; } // // Zooming // 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(); } // // // void AM_doFollowPlayer(void) { if (f_oldloc.x != plr->mo->x || f_oldloc.y != plr->mo->y) { m_x = FTOM(MTOF(plr->mo->x)) - m_w/2; m_y = FTOM(MTOF(plr->mo->y)) - m_h/2; m_x2 = m_x + m_w; m_y2 = m_y + m_h; f_oldloc.x = plr->mo->x; f_oldloc.y = plr->mo->y; // m_x = FTOM(MTOF(plr->mo->x - m_w/2)); // m_y = FTOM(MTOF(plr->mo->y - m_h/2)); // m_x = plr->mo->x - m_w/2; // m_y = plr->mo->y - m_h/2; } } // // // void AM_updateLightLev(void) { static int nexttic = 0; //static int litelevels[] = { 0, 3, 5, 6, 6, 7, 7, 7 }; static int litelevels[] = { 0, 4, 7, 10, 12, 14, 15, 15 }; static int litelevelscnt = 0; // Change light level if (amclock>nexttic) { lightlev = litelevels[litelevelscnt++]; if (litelevelscnt == arrlen(litelevels)) litelevelscnt = 0; nexttic = amclock + 6 - (amclock % 6); } } // // Updates on Game Tick // void AM_Ticker (void) { if (!automapactive) return; amclock++; if (followplayer) 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(); // Update light level // AM_updateLightLev(); } // // Clear automap frame buffer. // void AM_clearFB(int color) { memset(fb, color, f_w*f_h); } // // 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. // 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; } else { tmp.x = 0; tmp.y = 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 // // Classic Bresenham w/ whatever optimizations needed for speed // void AM_drawFline ( fline_t* fl, int color ) { register int x; register int y; register int dx; register int dy; register int sx; register int sy; register int ax; register int ay; register int d; static int fuck = 0; // For debugging only if ( fl->a.x < 0 || fl->a.x >= f_w || fl->a.y < 0 || fl->a.y >= f_h || fl->b.x < 0 || fl->b.x >= f_w || fl->b.y < 0 || fl->b.y >= f_h) { DEH_fprintf(stderr, "fuck %d \r", fuck++); return; } #define PUTDOT(xx,yy,cc) fb[(yy)*f_w+(xx)]=(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; } } } // // Clip lines, draw visible part sof lines. // void AM_drawMline ( mline_t* ml, int color ) { static fline_t fl; if (AM_clipMline(ml, &fl)) AM_drawFline(&fl, color); // draws it on frame buffer using fb coords } // // Draws flat (floor/ceiling tile) aligned grid lines. // 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<x; l.a.y = lines[i].v1->y; l.b.x = lines[i].v2->x; l.b.y = lines[i].v2->y; if (cheating || (lines[i].flags & ML_MAPPED)) { if ((lines[i].flags & LINE_NEVERSEE) && !cheating) continue; if (!lines[i].backsector) { AM_drawMline(&l, WALLCOLORS+lightlev); } else { if (lines[i].special == 39) { // teleporters AM_drawMline(&l, WALLCOLORS+WALLRANGE/2); } else if (lines[i].flags & ML_SECRET) // secret door { if (cheating) AM_drawMline(&l, SECRETWALLCOLORS + lightlev); else AM_drawMline(&l, WALLCOLORS+lightlev); } else if (lines[i].backsector->floorheight != lines[i].frontsector->floorheight) { AM_drawMline(&l, FDWALLCOLORS + lightlev); // floor level change } else if (lines[i].backsector->ceilingheight != lines[i].frontsector->ceilingheight) { AM_drawMline(&l, CDWALLCOLORS+lightlev); // ceiling level change } else if (cheating) { AM_drawMline(&l, TSWALLCOLORS+lightlev); } } } else if (plr->powers[pw_allmap]) { if (!(lines[i].flags & LINE_NEVERSEE)) AM_drawMline(&l, GRAYS+3); } } } // // Rotation in 2D. // Used to rotate player arrow line character. // void AM_rotate ( fixed_t* x, fixed_t* y, angle_t a ) { fixed_t tmpx; tmpx = FixedMul(*x,finecosine[a>>ANGLETOFINESHIFT]) - FixedMul(*y,finesine[a>>ANGLETOFINESHIFT]); *y = FixedMul(*x,finesine[a>>ANGLETOFINESHIFT]) + FixedMul(*y,finecosine[a>>ANGLETOFINESHIFT]); *x = tmpx; } 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; for (i=0;imo->angle, WHITE, plr->mo->x, plr->mo->y); else AM_drawLineCharacter (player_arrow, arrlen(player_arrow), 0, plr->mo->angle, WHITE, plr->mo->x, plr->mo->y); return; } for (i=0;ipowers[pw_invisibility]) color = 246; // *close* to black else color = their_colors[their_color]; AM_drawLineCharacter (player_arrow, arrlen(player_arrow), 0, p->mo->angle, color, p->mo->x, p->mo->y); } } void AM_drawThings ( int colors, int colorrange) { int i; mobj_t* t; for (i=0;iangle, colors+lightlev, t->x, t->y); t = t->snext; } } } void AM_drawMarks(void) { int i, fx, fy, w, h; for (i=0;iwidth); // h = SHORT(marknums[i]->height); w = 5; // because something's wrong with the wad, i guess h = 6; // because something's wrong with the wad, i guess fx = CXMTOF(markpoints[i].x); fy = CYMTOF(markpoints[i].y); if (fx >= f_x && fx <= f_w - w && fy >= f_y && fy <= f_h - h) V_DrawPatch(fx, fy, marknums[i]); } } } void AM_drawCrosshair(int color) { fb[(f_w*(f_h+1))/2] = color; // single point for now } void AM_Drawer (void) { if (!automapactive) return; AM_clearFB(BACKGROUND); if (grid) AM_drawGrid(GRIDCOLORS); AM_drawWalls(); AM_drawPlayers(); if (cheating==2) AM_drawThings(THINGCOLORS, THINGRANGE); AM_drawCrosshair(XHAIRCOLORS); AM_drawMarks(); V_MarkRect(f_x, f_y, f_w, f_h); } ================================================ FILE: fbdoom/am_map.h ================================================ // // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // 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. // // DESCRIPTION: // AutoMap module. // #ifndef __AMMAP_H__ #define __AMMAP_H__ #include "d_event.h" #include "m_cheat.h" // 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); extern cheatseq_t cheat_amap; #endif ================================================ FILE: fbdoom/config.h ================================================ /* config.hin. Generated from configure.ac by autoheader. */ /* Define to 1 if you have the header file. */ #undef HAVE_DEV_ISA_SPKRIO_H /* Define to 1 if you have the header file. */ #undef HAVE_DEV_SPEAKER_SPEAKER_H /* Define to 1 if you have the header file. */ #define HAVE_INTTYPES_H 1 /* Define to 1 if you have the `ioperm' function. */ #undef HAVE_IOPERM /* Define to 1 if you have the `amd64' library (-lamd64). */ #undef HAVE_LIBAMD64 /* Define to 1 if you have the `i386' library (-li386). */ #undef HAVE_LIBI386 /* Define to 1 if you have the `m' library (-lm). */ #undef HAVE_LIBM /* Define to 1 if you have the `png' library (-lpng). */ #undef HAVE_LIBPNG /* Define to 1 if you have the `samplerate' library (-lsamplerate). */ #undef HAVE_LIBSAMPLERATE /* Define to 1 if you have the `z' library (-lz). */ #undef HAVE_LIBZ /* Define to 1 if you have the header file. */ #undef HAVE_LINUX_KD_H /* Define to 1 if you have the header file. */ #undef HAVE_MEMORY_H /* Define to 1 if you have the `mmap' function. */ #undef HAVE_MMAP /* Define to 1 if you have the `sched_setaffinity' function. */ #undef HAVE_SCHED_SETAFFINITY /* Define to 1 if you have the header file. */ #define HAVE_STDINT_H 1 /* Define to 1 if you have the header file. */ #define HAVE_STDLIB_H 1 /* Define to 1 if you have the header file. */ #define HAVE_STRINGS_H 1 /* Define to 1 if you have the header file. */ #define HAVE_STRING_H 1 /* Define to 1 if you have the header file. */ #undef HAVE_SYS_STAT_H /* Define to 1 if you have the header file. */ #define HAVE_SYS_TYPES_H 1 /* Define to 1 if you have the header file. */ #undef HAVE_UNISTD_H /* Name of package */ #define PACKAGE "Doom" /* Define to the address where bug reports for this package should be sent. */ #undef PACKAGE_BUGREPORT /* Define to the full name of this package. */ #define PACKAGE_NAME "FDoom" /* Define to the full name and version of this package. */ #define PACKAGE_STRING "FDoom 0.1" /* Define to the one symbol short name of this package. */ #define PACKAGE_TARNAME "fdoom.tar" /* Define to the home page for this package. */ #define PACKAGE_URL "" /* Define to the version of this package. */ #define PACKAGE_VERSION 0.1 /* Change this when you create your awesome forked version */ #define PROGRAM_PREFIX "fdoom" /* Define to 1 if you have the ANSI C header files. */ #define STDC_HEADERS 1 /* Version number of package */ #define VERSION 0.1 /* Define to 1 if you want to compile the unmodified code */ #undef ORIGCODE /* Define to the directory where all game files are located */ #define FILES_DIR "/mnt" ================================================ FILE: fbdoom/d_englsh.h ================================================ // // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // 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. // // DESCRIPTION: // Printed strings for translation. // English language support (default). // #ifndef __D_ENGLSH__ #define __D_ENGLSH__ // // Printed strings for translation // // // 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 DOSY "(press y to quit to dos.)" #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" // // 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 "Green: " #define HUSTR_PLRINDIGO "Indigo: " #define HUSTR_PLRBROWN "Brown: " #define HUSTR_PLRRED "Red: " #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" // // 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..." // // 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" // 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." #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 ================================================ FILE: fbdoom/d_event.c ================================================ // // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // 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. // // // DESCRIPTION: Event handling. // // Events are asynchronous inputs generally generated by the game user. // Events can be discarded if no responder claims them // #include #include "d_event.h" #define MAXEVENTS 64 static event_t events[MAXEVENTS]; static int eventhead; static int eventtail; // // D_PostEvent // Called by the I/O functions when input is detected // void D_PostEvent (event_t* ev) { events[eventhead] = *ev; eventhead = (eventhead + 1) % MAXEVENTS; } // Read an event from the queue. event_t *D_PopEvent(void) { event_t *result; // No more events waiting. if (eventtail == eventhead) { return NULL; } result = &events[eventtail]; // Advance to the next event in the queue. eventtail = (eventtail + 1) % MAXEVENTS; return result; } ================================================ FILE: fbdoom/d_event.h ================================================ // // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // 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. // // DESCRIPTION: // // #ifndef __D_EVENT__ #define __D_EVENT__ #include "doomtype.h" // // Event handling. // // Input event types. typedef enum { ev_keydown, ev_keyup, ev_mouse, ev_joystick, ev_quit } evtype_t; // Event structure. typedef struct { evtype_t type; // Event-related data that depends on the type of event: // // ev_keydown/ev_keyup: // data1: Key code (from doomkeys.h) of the key that was // pressed or released. // data2: Ascii text of the character that was pressed, // shifted appropriately (eg. '$' if 4 was pressed // while shift was held). // // ev_mouse: // data1: Bitfield of buttons currently held down. // (bit 0 = left; bit 1 = right; bit 2 = middle). // data2: X axis mouse movement (turn). // data3: Y axis mouse movement (forward/backward). // // ev_joystick: // data1: Bitfield of buttons currently pressed. // data2: X axis mouse movement (turn). // data3: Y axis mouse movement (forward/backward). // data4: Third axis mouse movement (strafe). int data1, data2, data3, data4; } event_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 3 bits hold weapon num. BT_CHANGE = 4, // The 3bit weapon mask and shift, convenience. BT_WEAPONMASK = (8+16+32), BT_WEAPONSHIFT = 3, // Pause the game. BTS_PAUSE = 1, // Save the game at each console. BTS_SAVEGAME = 2, // Savegame slot numbers // occupy the second byte of buttons. BTS_SAVEMASK = (4+8+16), BTS_SAVESHIFT = 2, } buttoncode_t; // villsa [STRIFE] Strife specific buttons // TODO - not finished typedef enum { // Player view look up BT2_LOOKUP = 1, // Player view look down BT2_LOOKDOWN = 2, // Center player's view BT2_CENTERVIEW = 4, // Use inventory item BT2_INVUSE = 8, // Drop inventory item BT2_INVDROP = 16, // Jump up and down BT2_JUMP = 32, // Use medkit BT2_HEALTH = 128, } buttoncode2_t; // Called by IO functions when input is detected. void D_PostEvent (event_t *ev); // Read an event from the event queue event_t *D_PopEvent(void); #endif ================================================ FILE: fbdoom/d_items.c ================================================ // // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // 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. // // DESCRIPTION: // // We are referring to sprite numbers. #include "info.h" #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 }, }; ================================================ FILE: fbdoom/d_items.h ================================================ // // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // 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. // // DESCRIPTION: // Items: key cards, artifacts, weapon, ammunition. // #ifndef __D_ITEMS__ #define __D_ITEMS__ #include "doomdef.h" // 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 ================================================ FILE: fbdoom/d_iwad.c ================================================ // // Copyright(C) 2005-2014 Simon Howard // // 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. // // DESCRIPTION: // Search for and locate an IWAD file, and initialize according // to the IWAD type. // #include #include #include #include #include "config.h" #include "deh_str.h" #include "doomkeys.h" #include "d_iwad.h" #include "i_system.h" #include "m_argv.h" #include "m_config.h" #include "m_misc.h" #include "w_wad.h" #include "z_zone.h" static const iwad_t iwads[] = { { "doom2.wad", doom2, commercial, "Doom II" }, { "plutonia.wad", pack_plut, commercial, "Final Doom: Plutonia Experiment" }, { "tnt.wad", pack_tnt, commercial, "Final Doom: TNT: Evilution" }, { "doom.wad", doom, retail, "Doom" }, { "DOOM1.WAD", doom, shareware, "Doom Shareware" }, { "chex.wad", pack_chex, shareware, "Chex Quest" }, { "hacx.wad", pack_hacx, commercial, "Hacx" }, { "freedm.wad", doom2, commercial, "FreeDM" }, { "freedoom2.wad", doom2, commercial, "Freedoom: Phase 2" }, { "freedoom1.wad", doom, retail, "Freedoom: Phase 1" }, { "heretic.wad", heretic, retail, "Heretic" }, { "heretic1.wad", heretic, shareware, "Heretic Shareware" }, { "hexen.wad", hexen, commercial, "Hexen" }, //{ "strife0.wad", strife, commercial, "Strife" }, // haleyjd: STRIFE-FIXME { "strife1.wad", strife, commercial, "Strife" }, }; // Array of locations to search for IWAD files // // "128 IWAD search directories should be enough for anybody". #define MAX_IWAD_DIRS 128 static boolean iwad_dirs_built = false; static char *iwad_dirs[MAX_IWAD_DIRS]; static int num_iwad_dirs = 0; static void AddIWADDir(char *dir) { if (num_iwad_dirs < MAX_IWAD_DIRS) { iwad_dirs[num_iwad_dirs] = dir; ++num_iwad_dirs; } } // This is Windows-specific code that automatically finds the location // of installed IWAD files. The registry is inspected to find special // keys installed by the Windows installers for various CD versions // of Doom. From these keys we can deduce where to find an IWAD. #if defined(_WIN32) && !defined(_WIN32_WCE) #define WIN32_LEAN_AND_MEAN #include typedef struct { HKEY root; char *path; char *value; } registry_value_t; #define UNINSTALLER_STRING "\\uninstl.exe /S " // Keys installed by the various CD editions. These are actually the // commands to invoke the uninstaller and look like this: // // C:\Program Files\Path\uninstl.exe /S C:\Program Files\Path // // With some munging we can find where Doom was installed. // [AlexMax] From the persepctive of a 64-bit executable, 32-bit registry // keys are located in a different spot. #if _WIN64 #define SOFTWARE_KEY "Software\\Wow6432Node" #else #define SOFTWARE_KEY "Software" #endif static registry_value_t uninstall_values[] = { // Ultimate Doom, CD version (Depths of Doom trilogy) { HKEY_LOCAL_MACHINE, SOFTWARE_KEY "\\Microsoft\\Windows\\CurrentVersion\\" "Uninstall\\Ultimate Doom for Windows 95", "UninstallString", }, // Doom II, CD version (Depths of Doom trilogy) { HKEY_LOCAL_MACHINE, SOFTWARE_KEY "\\Microsoft\\Windows\\CurrentVersion\\" "Uninstall\\Doom II for Windows 95", "UninstallString", }, // Final Doom { HKEY_LOCAL_MACHINE, SOFTWARE_KEY "\\Microsoft\\Windows\\CurrentVersion\\" "Uninstall\\Final Doom for Windows 95", "UninstallString", }, // Shareware version { HKEY_LOCAL_MACHINE, SOFTWARE_KEY "\\Microsoft\\Windows\\CurrentVersion\\" "Uninstall\\Doom Shareware for Windows 95", "UninstallString", }, }; // Value installed by the Collector's Edition when it is installed static registry_value_t collectors_edition_value = { HKEY_LOCAL_MACHINE, SOFTWARE_KEY "\\Activision\\DOOM Collector's Edition\\v1.0", "INSTALLPATH", }; // Subdirectories of the above install path, where IWADs are installed. static char *collectors_edition_subdirs[] = { "Doom2", "Final Doom", "Ultimate Doom", }; // Location where Steam is installed static registry_value_t steam_install_location = { HKEY_LOCAL_MACHINE, SOFTWARE_KEY "\\Valve\\Steam", "InstallPath", }; // Subdirs of the steam install directory where IWADs are found static char *steam_install_subdirs[] = { "steamapps\\common\\doom 2\\base", "steamapps\\common\\final doom\\base", "steamapps\\common\\ultimate doom\\base", "steamapps\\common\\heretic shadow of the serpent riders\\base", "steamapps\\common\\hexen\\base", "steamapps\\common\\hexen deathkings of the dark citadel\\base", // From Doom 3: BFG Edition: "steamapps\\common\\DOOM 3 BFG Edition\\base\\wads", }; #define STEAM_BFG_GUS_PATCHES \ "steamapps\\common\\DOOM 3 BFG Edition\\base\\classicmusic\\instruments" static char *GetRegistryString(registry_value_t *reg_val) { HKEY key; DWORD len; DWORD valtype; char *result; // Open the key (directory where the value is stored) if (RegOpenKeyEx(reg_val->root, reg_val->path, 0, KEY_READ, &key) != ERROR_SUCCESS) { return NULL; } result = NULL; // Find the type and length of the string, and only accept strings. if (RegQueryValueEx(key, reg_val->value, NULL, &valtype, NULL, &len) == ERROR_SUCCESS && valtype == REG_SZ) { // Allocate a buffer for the value and read the value result = malloc(len); if (RegQueryValueEx(key, reg_val->value, NULL, &valtype, (unsigned char *) result, &len) != ERROR_SUCCESS) { free(result); result = NULL; } } // Close the key RegCloseKey(key); return result; } // Check for the uninstall strings from the CD versions static void CheckUninstallStrings(void) { unsigned int i; for (i=0; i 0) { return; } install_path = GetRegistryString(&steam_install_location); if (install_path == NULL) { return; } len = strlen(install_path) + strlen(STEAM_BFG_GUS_PATCHES) + 20; patch_path = malloc(len); M_snprintf(patch_path, len, "%s\\%s\\ACBASS.PAT", install_path, STEAM_BFG_GUS_PATCHES); // Does acbass.pat exist? If so, then set gus_patch_path. if (M_FileExists(patch_path)) { M_snprintf(patch_path, len, "%s\\%s", install_path, STEAM_BFG_GUS_PATCHES); M_SetVariable("gus_patch_path", patch_path); } free(patch_path); free(install_path); } // Default install directories for DOS Doom static void CheckDOSDefaults(void) { // These are the default install directories used by the deice // installer program: AddIWADDir("\\doom2"); // Doom II AddIWADDir("\\plutonia"); // Final Doom AddIWADDir("\\tnt"); AddIWADDir("\\doom_se"); // Ultimate Doom AddIWADDir("\\doom"); // Shareware / Registered Doom AddIWADDir("\\dooms"); // Shareware versions AddIWADDir("\\doomsw"); AddIWADDir("\\heretic"); // Heretic AddIWADDir("\\hrtic_se"); // Heretic Shareware from Quake disc AddIWADDir("\\hexen"); // Hexen AddIWADDir("\\hexendk"); // Hexen Deathkings of the Dark Citadel AddIWADDir("\\strife"); // Strife } #endif // Returns true if the specified path is a path to a file // of the specified name. static boolean DirIsFile(char *path, char *filename) { size_t path_len; size_t filename_len; path_len = strlen(path); filename_len = strlen(filename); return path_len >= filename_len + 1 && path[path_len - filename_len - 1] == DIR_SEPARATOR && !strcasecmp(&path[path_len - filename_len], filename); } // Check if the specified directory contains the specified IWAD // file, returning the full path to the IWAD if found, or NULL // if not found. static char *CheckDirectoryHasIWAD(char *dir, char *iwadname) { char *filename; // As a special case, the "directory" may refer directly to an // IWAD file if the path comes from DOOMWADDIR or DOOMWADPATH. if (DirIsFile(dir, iwadname) && M_FileExists(dir)) { return strdup(dir); } // Construct the full path to the IWAD if it is located in // this directory, and check if it exists. if (!strcmp(dir, ".")) { filename = strdup(iwadname); } else { filename = M_StringJoin(dir, DIR_SEPARATOR_S, iwadname, NULL); } if (M_FileExists(filename)) { return filename; } free(filename); return NULL; } // Search a directory to try to find an IWAD // Returns the location of the IWAD if found, otherwise NULL. static char *SearchDirectoryForIWAD(char *dir, int mask, GameMission_t *mission) { char *filename; size_t i; for (i=0; i // iwadparm = M_CheckParmWithArgs("-iwad", 1); if (iwadparm) { // Search through IWAD dirs for an IWAD with the given name. iwadfile = myargv[iwadparm + 1]; result = D_FindWADByName(iwadfile); if (result == NULL) { I_Error("IWAD file '%s' not found!", iwadfile); } *mission = IdentifyIWADByName(result, mask); } else { // Search through the list and look for an IWAD result = NULL; BuildIWADDirList(); for (i=0; result == NULL && i #include #include "doomfeatures.h" #include "d_event.h" #include "d_loop.h" #include "d_ticcmd.h" #include "i_system.h" #include "i_timer.h" #include "i_video.h" #include "m_argv.h" #include "m_fixed.h" #include "net_client.h" #include "net_gui.h" #include "net_io.h" #include "net_query.h" #include "net_server.h" #include "net_sdl.h" #include "net_loop.h" // The complete set of data for a particular tic. typedef struct { ticcmd_t cmds[NET_MAXPLAYERS]; boolean ingame[NET_MAXPLAYERS]; } ticcmd_set_t; // // gametic is the tic about to (or currently being) run // maketic is the tic that hasn't had control made for it yet // recvtic is the latest tic received from the server. // // a gametic cannot be run until ticcmds are received for it // from all players. // static ticcmd_set_t ticdata[BACKUPTICS]; // The index of the next tic to be made (with a call to BuildTiccmd). static int maketic; // The number of complete tics received from the server so far. static int recvtic; // The number of tics that have been run (using RunTic) so far. int gametic; // When set to true, a single tic is run each time TryRunTics() is called. // This is used for -timedemo mode. boolean singletics = false; // Index of the local player. static int localplayer; // Used for original sync code. static int skiptics = 0; // Reduce the bandwidth needed by sampling game input less and transmitting // less. If ticdup is 2, sample half normal, 3 = one third normal, etc. int ticdup; // Amount to offset the timer for game sync. fixed_t offsetms; // Use new client syncronisation code static boolean new_sync = true; // Callback functions for loop code. static loop_interface_t *loop_interface = NULL; // Current players in the multiplayer game. // This is distinct from playeringame[] used by the game code, which may // modify playeringame[] when playing back multiplayer demos. static boolean local_playeringame[NET_MAXPLAYERS]; // Requested player class "sent" to the server on connect. // If we are only doing a single player game then this needs to be remembered // and saved in the game settings. static int player_class; // 35 fps clock adjusted by offsetms milliseconds static int GetAdjustedTime(void) { int time_ms; time_ms = I_GetTimeMS(); if (new_sync) { // Use the adjustments from net_client.c only if we are // using the new sync mode. time_ms += (offsetms / FRACUNIT); } return (time_ms * TICRATE) / 1000; } static boolean BuildNewTic(void) { int gameticdiv; ticcmd_t cmd; gameticdiv = gametic/ticdup; I_StartTic (); loop_interface->ProcessEvents(); // Always run the menu loop_interface->RunMenu(); if (drone) { // In drone mode, do not generate any ticcmds. return false; } if (new_sync) { // If playing single player, do not allow tics to buffer // up very far if (!net_client_connected && maketic - gameticdiv > 2) return false; // Never go more than ~200ms ahead if (maketic - gameticdiv > 8) return false; } else { if (maketic - gameticdiv >= 5) return false; } //printf ("mk:%i ",maketic); memset(&cmd, 0, sizeof(ticcmd_t)); loop_interface->BuildTiccmd(&cmd, maketic); #ifdef FEATURE_MULTIPLAYER if (net_client_connected) { NET_CL_SendTiccmd(&cmd, maketic); } #endif ticdata[maketic % BACKUPTICS].cmds[localplayer] = cmd; ticdata[maketic % BACKUPTICS].ingame[localplayer] = true; ++maketic; return true; } // // NetUpdate // Builds ticcmds for console player, // sends out a packet // int lasttime; void NetUpdate (void) { int nowtime; int newtics; int i; // If we are running with singletics (timing a demo), this // is all done separately. if (singletics) return; #ifdef FEATURE_MULTIPLAYER // Run network subsystems NET_CL_Run(); NET_SV_Run(); #endif // check time nowtime = GetAdjustedTime() / ticdup; newtics = nowtime - lasttime; lasttime = nowtime; if (skiptics <= newtics) { newtics -= skiptics; skiptics = 0; } else { skiptics -= newtics; newtics = 0; } // build new ticcmds for console player for (i=0 ; iconsoleplayer = 0; settings->num_players = 1; settings->player_classes[0] = player_class; //! // @category net // // Use new network client sync code rather than the classic // sync code. This is currently disabled by default because it // has some bugs. // if (M_CheckParm("-newsync") > 0) settings->new_sync = 1; else settings->new_sync = 0; // TODO: New sync code is not enabled by default because it's // currently broken. //if (M_CheckParm("-oldsync") > 0) // settings->new_sync = 0; //else // settings->new_sync = 1; //! // @category net // @arg // // Send n extra tics in every packet as insurance against dropped // packets. // i = M_CheckParmWithArgs("-extratics", 1); if (i > 0) settings->extratics = atoi(myargv[i+1]); else settings->extratics = 1; //! // @category net // @arg // // Reduce the resolution of the game by a factor of n, reducing // the amount of network bandwidth needed. // i = M_CheckParmWithArgs("-dup", 1); if (i > 0) settings->ticdup = atoi(myargv[i+1]); else settings->ticdup = 1; if (net_client_connected) { // Send our game settings and block until game start is received // from the server. NET_CL_StartGame(settings); BlockUntilStart(settings, callback); // Read the game settings that were received. NET_CL_GetSettings(settings); } if (drone) { settings->consoleplayer = 0; } // Set the local player and playeringame[] values. localplayer = settings->consoleplayer; for (i = 0; i < NET_MAXPLAYERS; ++i) { local_playeringame[i] = i < settings->num_players; } // Copy settings to global variables. ticdup = settings->ticdup; new_sync = settings->new_sync; // TODO: Message disabled until we fix new_sync. //if (!new_sync) //{ // printf("Syncing netgames like Vanilla Doom.\n"); //} #else settings->consoleplayer = 0; settings->num_players = 1; settings->player_classes[0] = player_class; settings->new_sync = 0; settings->extratics = 1; settings->ticdup = 1; ticdup = settings->ticdup; new_sync = settings->new_sync; #endif } boolean D_InitNetGame(net_connect_data_t *connect_data) { boolean result = false; #ifdef FEATURE_MULTIPLAYER net_addr_t *addr = NULL; int i; #endif // Call D_QuitNetGame on exit: I_AtExit(D_QuitNetGame, true); player_class = connect_data->player_class; #ifdef FEATURE_MULTIPLAYER //! // @category net // // Start a multiplayer server, listening for connections. // if (M_CheckParm("-server") > 0 || M_CheckParm("-privateserver") > 0) { NET_SV_Init(); NET_SV_AddModule(&net_loop_server_module); NET_SV_AddModule(&net_sdl_module); NET_SV_RegisterWithMaster(); net_loop_client_module.InitClient(); addr = net_loop_client_module.ResolveAddress(NULL); } else { //! // @category net // // Automatically search the local LAN for a multiplayer // server and join it. // i = M_CheckParm("-autojoin"); if (i > 0) { addr = NET_FindLANServer(); if (addr == NULL) { I_Error("No server found on local LAN"); } } //! // @arg
// @category net // // Connect to a multiplayer server running on the given // address. // i = M_CheckParmWithArgs("-connect", 1); if (i > 0) { net_sdl_module.InitClient(); addr = net_sdl_module.ResolveAddress(myargv[i+1]); if (addr == NULL) { I_Error("Unable to resolve '%s'\n", myargv[i+1]); } } } if (addr != NULL) { if (M_CheckParm("-drone") > 0) { connect_data->drone = true; } if (!NET_CL_Connect(addr, connect_data)) { I_Error("D_InitNetGame: Failed to connect to %s\n", NET_AddrToString(addr)); } printf("D_InitNetGame: Connected to %s\n", NET_AddrToString(addr)); // Wait for launch message received from server. NET_WaitForLaunch(); result = true; } #endif return result; } // // D_QuitNetGame // Called before quitting to leave a net game // without hanging the other players // void D_QuitNetGame (void) { #ifdef FEATURE_MULTIPLAYER NET_SV_Shutdown(); NET_CL_Disconnect(); #endif } static int GetLowTic(void) { int lowtic; lowtic = maketic; #ifdef FEATURE_MULTIPLAYER if (net_client_connected) { if (drone || recvtic < lowtic) { lowtic = recvtic; } } #endif return lowtic; } static int frameon; static int frameskip[4]; static int oldnettics; static void OldNetSync(void) { unsigned int i; int keyplayer = -1; frameon++; // ideally maketic should be 1 - 3 tics above lowtic // if we are consistantly slower, speed up time for (i=0 ; i recvtic; oldnettics = maketic; if (frameskip[0] && frameskip[1] && frameskip[2] && frameskip[3]) { skiptics = 1; // printf ("+"); } } } // Returns true if there are players in the game: static boolean PlayersInGame(void) { boolean result = false; unsigned int i; // If we are connected to a server, check if there are any players // in the game. if (net_client_connected) { for (i = 0; i < NET_MAXPLAYERS; ++i) { result = result || local_playeringame[i]; } } // Whether single or multi-player, unless we are running as a drone, // we are in the game. if (!drone) { result = true; } return result; } // When using ticdup, certain values must be cleared out when running // the duplicate ticcmds. static void TicdupSquash(ticcmd_set_t *set) { ticcmd_t *cmd; unsigned int i; for (i = 0; i < NET_MAXPLAYERS ; ++i) { cmd = &set->cmds[i]; cmd->chatchar = 0; if (cmd->buttons & BT_SPECIAL) cmd->buttons = 0; } } // When running in single player mode, clear all the ingame[] array // except the local player. static void SinglePlayerClear(ticcmd_set_t *set) { unsigned int i; for (i = 0; i < NET_MAXPLAYERS; ++i) { if (i != localplayer) { set->ingame[i] = false; } } } // // TryRunTics // void TryRunTics (void) { int i; int lowtic; int entertic; static int oldentertics; int realtics; int availabletics; int counts; // get real tics entertic = I_GetTime() / ticdup; realtics = entertic - oldentertics; oldentertics = entertic; // in singletics mode, run a single tic every time this function // is called. if (singletics) { BuildNewTic(); } else { NetUpdate (); } lowtic = GetLowTic(); availabletics = lowtic - gametic/ticdup; // decide how many tics to run if (new_sync) { counts = availabletics; } else { // decide how many tics to run if (realtics < availabletics-1) counts = realtics+1; else if (realtics < availabletics) counts = realtics; else counts = availabletics; if (counts < 1) counts = 1; if (net_client_connected) { OldNetSync(); } } if (counts < 1) counts = 1; // wait for new tics if needed while (!PlayersInGame() || lowtic < gametic/ticdup + counts) { NetUpdate (); lowtic = GetLowTic(); if (lowtic < gametic/ticdup) I_Error ("TryRunTics: lowtic < gametic"); // Don't stay in this loop forever. The menu is still running, // so return to update the screen if (I_GetTime() / ticdup - entertic > 0) { return; } I_Sleep(1); } // run the count * ticdup dics while (counts--) { ticcmd_set_t *set; if (!PlayersInGame()) { return; } set = &ticdata[(gametic / ticdup) % BACKUPTICS]; if (!net_client_connected) { SinglePlayerClear(set); } for (i=0 ; i lowtic) I_Error ("gametic>lowtic"); memcpy(local_playeringame, set->ingame, sizeof(local_playeringame)); loop_interface->RunTic(set->cmds, set->ingame); gametic++; // modify command for duplicated tics TicdupSquash(set); } NetUpdate (); // check for new console commands } } void D_RegisterLoopCallbacks(loop_interface_t *i) { loop_interface = i; } ================================================ FILE: fbdoom/d_loop.h ================================================ // // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // 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. // // DESCRIPTION: // Main loop stuff. // #ifndef __D_LOOP__ #define __D_LOOP__ #include "net_defs.h" // Callback function invoked while waiting for the netgame to start. // The callback is invoked when new players are ready. The callback // should return true, or return false to abort startup. typedef boolean (*netgame_startup_callback_t)(int ready_players, int num_players); typedef struct { // Read events from the event queue, and process them. void (*ProcessEvents)(); // Given the current input state, fill in the fields of the specified // ticcmd_t structure with data for a new tic. void (*BuildTiccmd)(ticcmd_t *cmd, int maketic); // Advance the game forward one tic, using the specified player input. void (*RunTic)(ticcmd_t *cmds, boolean *ingame); // Run the menu (runs independently of the game). void (*RunMenu)(); } loop_interface_t; // Register callback functions for the main loop code to use. void D_RegisterLoopCallbacks(loop_interface_t *i); // Create any new ticcmds and broadcast to other players. void NetUpdate (void); // Broadcasts special packets to other players // to notify of game exit void D_QuitNetGame (void); //? how many ticks to run? void TryRunTics (void); // Called at start of game loop to initialize timers void D_StartGameLoop(void); // Initialize networking code and connect to server. boolean D_InitNetGame(net_connect_data_t *connect_data); // Start game with specified settings. The structure will be updated // with the actual settings for the game. void D_StartNetGame(net_gamesettings_t *settings, netgame_startup_callback_t callback); extern boolean singletics; extern int gametic, ticdup; #endif ================================================ FILE: fbdoom/d_main.c ================================================ // // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // 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. // // 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. // #include #include #include #include #include "config.h" #include "deh_main.h" #include "doomdef.h" #include "doomstat.h" #include "dstrings.h" #include "doomfeatures.h" #include "sounds.h" #include "d_iwad.h" #include "z_zone.h" #include "w_main.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_config.h" #include "m_controls.h" #include "m_misc.h" #include "m_menu.h" #include "p_saveg.h" #include "i_endoom.h" #include "i_joystick.h" #include "i_system.h" #include "i_timer.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 "net_client.h" #include "net_dedicated.h" #include "net_query.h" #include "p_setup.h" #include "r_local.h" #include "statdump.h" #include "d_main.h" // // 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 // void D_DoomLoop (void); // Location where savegames are stored char * savegamedir; // location of IWAD and WAD files char * iwadfile; boolean devparm; // started game with -devparm boolean nomonsters; // checkparm of -nomonsters boolean respawnparm; // checkparm of -respawn boolean fastparm; // checkparm of -fast //extern int soundVolume; //extern int sfxVolume; //extern int musicVolume; extern boolean inhelpscreens; skill_t startskill; int startepisode; int startmap; boolean autostart; int startloadgame; boolean advancedemo; // Store demo, do not accept any inputs boolean storedemo; // "BFG Edition" version of doom2.wad does not include TITLEPIC. boolean bfgedition; // If true, the main game loop has started. boolean main_loop_started = false; char wadfile[1024]; // primary wad file char mapdir[1024]; // directory of development maps int show_endoom = 1; void D_ConnectNetGame(void); void D_CheckNetGame(void); // // D_ProcessEvents // Send all the events of the given timestamp down the responder chain // void D_ProcessEvents (void) { event_t* ev; // IF STORE DEMO, DO NOT ACCEPT INPUT if (storedemo) return; while ((ev = D_PopEvent()) != NULL) { if (M_Responder (ev)) continue; // menu ate the event G_Responder (ev); } } // // 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 R_ExecuteSetViewSize (void); void D_Display (void) { static boolean viewactivestate = false; static boolean menuactivestate = false; static boolean inhelpscreensstate = false; static boolean fullscreen = false; static gamestate_t oldgamestate = -1; static int borderdrawcount; int nowtime; int tics; int wipestart; int y; boolean done; boolean wipe; boolean redrawsbar; if (nodrawers) return; // for comparative timing / profiling redrawsbar = false; // change the view size if needed if (setsizeneeded) { R_ExecuteSetViewSize (); oldgamestate = -1; // force background redraw borderdrawcount = 3; } // save the current screen if about to wipe if (gamestate != wipegamestate) { wipe = true; wipe_StartScreen(0, 0, SCREENWIDTH, SCREENHEIGHT); } else wipe = false; if (gamestate == GS_LEVEL && gametic) HU_Erase(); // do buffered drawing switch (gamestate) { case GS_LEVEL: if (!gametic) break; if (automapactive) AM_Drawer (); if (wipe || (viewheight != 200 && fullscreen) ) redrawsbar = true; if (inhelpscreensstate && !inhelpscreens) redrawsbar = true; // just put away the help screen ST_Drawer (viewheight == 200, redrawsbar ); fullscreen = viewheight == 200; break; case GS_INTERMISSION: WI_Drawer (); break; case GS_FINALE: F_Drawer (); break; case GS_DEMOSCREEN: D_PageDrawer (); break; } // draw buffered stuff to screen I_UpdateNoBlit (); // draw the view directly if (gamestate == GS_LEVEL && !automapactive && gametic) R_RenderPlayerView (&players[displayplayer]); if (gamestate == GS_LEVEL && gametic) HU_Drawer (); // clean up border stuff if (gamestate != oldgamestate && gamestate != GS_LEVEL) I_SetPalette (W_CacheLumpName (DEH_String("PLAYPAL"),PU_CACHE)); // see if the border needs to be initially drawn if (gamestate == GS_LEVEL && oldgamestate != GS_LEVEL) { viewactivestate = false; // view was not active R_FillBackScreen (); // draw the pattern into the back screen } // see if the border needs to be updated to the screen if (gamestate == GS_LEVEL && !automapactive && scaledviewwidth != 320) { if (menuactive || menuactivestate || !viewactivestate) borderdrawcount = 3; if (borderdrawcount) { R_DrawViewBorder (); // erase old menu stuff borderdrawcount--; } } if (testcontrols) { // Box showing current mouse speed V_DrawMouseSpeedBox(testcontrols_mousespeed); } menuactivestate = menuactive; viewactivestate = viewactive; inhelpscreensstate = inhelpscreens; oldgamestate = wipegamestate = gamestate; // draw pause pic if (paused) { if (automapactive) y = 4; else y = viewwindowy+4; V_DrawPatchDirect(viewwindowx + (scaledviewwidth - 68) / 2, y, W_CacheLumpName (DEH_String("M_PAUSE"), PU_CACHE)); } // menus go directly to the screen M_Drawer (); // menu is drawn even on top of everything NetUpdate (); // send out any new accumulation // normal update if (!wipe) { I_FinishUpdate (); // page flip or blit buffer return; } // wipe update wipe_EndScreen(0, 0, SCREENWIDTH, SCREENHEIGHT); wipestart = I_GetTime () - 1; do { do { nowtime = I_GetTime (); tics = nowtime - wipestart; I_Sleep(1); } while (tics <= 0); wipestart = nowtime; done = wipe_ScreenWipe(wipe_Melt , 0, 0, SCREENWIDTH, SCREENHEIGHT, tics); I_UpdateNoBlit (); M_Drawer (); // menu is drawn even on top of wipes I_FinishUpdate (); // page flip or blit buffer } while (!done); } // // Add configuration file variable bindings. // void D_BindVariables(void) { int i; M_ApplyPlatformDefaults(); I_BindVideoVariables(); I_BindJoystickVariables(); I_BindSoundVariables(); M_BindBaseControls(); M_BindWeaponControls(); M_BindMapControls(); M_BindMenuControls(); M_BindChatControls(MAXPLAYERS); key_multi_msgplayer[0] = HUSTR_KEYGREEN; key_multi_msgplayer[1] = HUSTR_KEYINDIGO; key_multi_msgplayer[2] = HUSTR_KEYBROWN; key_multi_msgplayer[3] = HUSTR_KEYRED; #ifdef FEATURE_MULTIPLAYER NET_BindVariables(); #endif M_BindVariable("mouse_sensitivity", &mouseSensitivity); M_BindVariable("sfx_volume", &sfxVolume); M_BindVariable("music_volume", &musicVolume); M_BindVariable("show_messages", &showMessages); M_BindVariable("screenblocks", &screenblocks); M_BindVariable("detaillevel", &detailLevel); M_BindVariable("snd_channels", &snd_channels); M_BindVariable("vanilla_savegame_limit", &vanilla_savegame_limit); M_BindVariable("vanilla_demo_limit", &vanilla_demo_limit); M_BindVariable("show_endoom", &show_endoom); // Multiplayer chat macros for (i=0; i<10; ++i) { char buf[12]; M_snprintf(buf, sizeof(buf), "chatmacro%i", i); M_BindVariable(buf, &chat_macros[i]); } } // // D_GrabMouseCallback // // Called to determine whether to grab the mouse pointer // boolean D_GrabMouseCallback(void) { // Drone players don't need mouse focus if (drone) return false; // when menu is active or game is paused, release the mouse if (menuactive || paused) return false; // only grab mouse when playing levels (but not demos) return (gamestate == GS_LEVEL) && !demoplayback && !advancedemo; } // // D_DoomLoop // void D_DoomLoop (void) { if (bfgedition && (demorecording || (gameaction == ga_playdemo) || netgame)) { printf(" WARNING: You are playing using one of the Doom Classic\n" " IWAD files shipped with the Doom 3: BFG Edition. These are\n" " known to be incompatible with the regular IWAD files and\n" " may cause demos and network games to get out of sync.\n"); } if (demorecording) G_BeginRecording (); main_loop_started = true; TryRunTics(); I_SetWindowTitle(gamedescription); I_GraphicsCheckCommandLine(); I_SetGrabMouseCallback(D_GrabMouseCallback); I_InitGraphics(); I_EnableLoadingDisk(); V_RestoreBuffer(); R_ExecuteSetViewSize(); D_StartGameLoop(); if (testcontrols) { wipegamestate = gamestate; } while (1) { // frame syncronous IO operations I_StartFrame (); TryRunTics (); // will run at least one tic S_UpdateSounds (players[consoleplayer].mo);// move positional sounds // Update display, next frame, with current state. if (screenvisible) { D_Display (); } } } // // DEMO LOOP // int demosequence; int pagetic; char *pagename; // // D_PageTicker // Handles timing for warped projection // void D_PageTicker (void) { if (--pagetic < 0) D_AdvanceDemo (); } // // D_PageDrawer // void D_PageDrawer (void) { V_DrawPatch (0, 0, W_CacheLumpName(pagename, PU_CACHE)); } // // D_AdvanceDemo // Called after each demo or intro demosequence finishes // void D_AdvanceDemo (void) { advancedemo = true; } // // This cycles through the demo sequences. // FIXME - version dependend demo numbers? // void D_DoAdvanceDemo (void) { players[consoleplayer].playerstate = PST_LIVE; // not reborn advancedemo = false; usergame = false; // no save / end game here paused = false; gameaction = ga_nothing; // The Ultimate Doom executable changed the demo sequence to add // a DEMO4 demo. Final Doom was based on Ultimate, so also // includes this change; however, the Final Doom IWADs do not // include a DEMO4 lump, so the game bombs out with an error // when it reaches this point in the demo sequence. // However! There is an alternate version of Final Doom that // includes a fixed executable. if (gameversion == exe_ultimate || gameversion == exe_final) demosequence = (demosequence+1)%7; else demosequence = (demosequence+1)%6; switch (demosequence) { case 0: if ( gamemode == commercial ) pagetic = TICRATE * 11; else pagetic = 170; gamestate = GS_DEMOSCREEN; pagename = DEH_String("TITLEPIC"); if ( gamemode == commercial ) S_StartMusic(mus_dm2ttl); else S_StartMusic (mus_intro); break; case 1: G_DeferedPlayDemo(DEH_String("demo1")); break; case 2: pagetic = 200; gamestate = GS_DEMOSCREEN; pagename = DEH_String("CREDIT"); break; case 3: G_DeferedPlayDemo(DEH_String("demo2")); break; case 4: gamestate = GS_DEMOSCREEN; if ( gamemode == commercial) { pagetic = TICRATE * 11; pagename = DEH_String("TITLEPIC"); S_StartMusic(mus_dm2ttl); } else { pagetic = 200; if ( gamemode == retail ) pagename = DEH_String("CREDIT"); else pagename = DEH_String("HELP2"); } break; case 5: G_DeferedPlayDemo(DEH_String("demo3")); break; // THE DEFINITIVE DOOM Special Edition demo case 6: G_DeferedPlayDemo(DEH_String("demo4")); break; } // The Doom 3: BFG Edition version of doom2.wad does not have a // TITLETPIC lump. Use INTERPIC instead as a workaround. if (bfgedition && !strcasecmp(pagename, "TITLEPIC") && W_CheckNumForName("titlepic") < 0) { pagename = DEH_String("INTERPIC"); } } // // D_StartTitle // void D_StartTitle (void) { gameaction = ga_nothing; demosequence = -1; D_AdvanceDemo (); } // Strings for dehacked replacements of the startup banner // // These are from the original source: some of them are perhaps // not used in any dehacked patches static char *banners[] = { // doom2.wad " " "DOOM 2: Hell on Earth v%i.%i" " ", // doom1.wad " " "DOOM Shareware Startup v%i.%i" " ", // doom.wad " " "DOOM Registered Startup v%i.%i" " ", // Registered DOOM uses this " " "DOOM System Startup v%i.%i" " ", // doom.wad (Ultimate DOOM) " " "The Ultimate DOOM Startup v%i.%i" " ", // tnt.wad " " "DOOM 2: TNT - Evilution v%i.%i" " ", // plutonia.wad " " "DOOM 2: Plutonia Experiment v%i.%i" " ", }; // // Get game name: if the startup banner has been replaced, use that. // Otherwise, use the name given // static char *GetGameName(char *gamename) { size_t i; char *deh_sub; for (i=0; i 0) { // Ultimate Doom gamemode = retail; } else if (W_CheckNumForName("E3M1") > 0) { gamemode = registered; } else { gamemode = shareware; } } else { int p; // Doom 2 of some kind. gamemode = commercial; // We can manually override the gamemission that we got from the // IWAD detection code. This allows us to eg. play Plutonia 2 // with Freedoom and get the right level names. //! // @arg // // Explicitly specify a Doom II "mission pack" to run as, instead of // detecting it based on the filename. Valid values are: "doom2", // "tnt" and "plutonia". // p = M_CheckParmWithArgs("-pack", 1); if (p > 0) { SetMissionForPackName(myargv[p + 1]); } } } // Set the gamedescription string void D_SetGameDescription(void) { boolean is_freedoom = W_CheckNumForName("FREEDOOM") >= 0, is_freedm = W_CheckNumForName("FREEDM") >= 0; gamedescription = "Unknown"; if (logical_gamemission == doom) { // Doom 1. But which version? if (is_freedoom) { gamedescription = GetGameName("Freedoom: Phase 1"); } else if (gamemode == retail) { // Ultimate Doom gamedescription = GetGameName("The Ultimate DOOM"); } else if (gamemode == registered) { gamedescription = GetGameName("DOOM Registered"); } else if (gamemode == shareware) { gamedescription = GetGameName("DOOM Shareware"); } } else { // Doom 2 of some kind. But which mission? if (is_freedoom) { if (is_freedm) { gamedescription = GetGameName("FreeDM"); } else { gamedescription = GetGameName("Freedoom: Phase 2"); } } else if (logical_gamemission == doom2) { gamedescription = GetGameName("DOOM 2: Hell on Earth"); } else if (logical_gamemission == pack_plut) { gamedescription = GetGameName("DOOM 2: Plutonia Experiment"); } else if (logical_gamemission == pack_tnt) { gamedescription = GetGameName("DOOM 2: TNT - Evilution"); } } } // print title for every printed line char title[128]; static boolean D_AddFile(char *filename) { wad_file_t *handle; printf(" adding %s\n", filename); handle = W_AddFile(filename); return handle != NULL; } // Copyright message banners // Some dehacked mods replace these. These are only displayed if they are // replaced by dehacked. static char *copyright_banners[] = { "===========================================================================\n" "ATTENTION: This version of DOOM has been modified. If you would like to\n" "get a copy of the original game, call 1-800-IDGAMES or see the readme file.\n" " You will not receive technical support for modified games.\n" " press enter to continue\n" "===========================================================================\n", "===========================================================================\n" " Commercial product - do not distribute!\n" " Please report software piracy to the SPA: 1-800-388-PIR8\n" "===========================================================================\n", "===========================================================================\n" " Shareware!\n" "===========================================================================\n" }; // Prints a message only if it has been modified by dehacked. void PrintDehackedBanners(void) { size_t i; for (i=0; i // @category compat // // Emulate a specific version of Doom. Valid values are "1.9", // "ultimate", "final", "final2", "hacx" and "chex". // p = M_CheckParmWithArgs("-gameversion", 1); if (p) { for (i=0; gameversions[i].description != NULL; ++i) { if (!strcmp(myargv[p+1], gameversions[i].cmdline)) { gameversion = gameversions[i].version; break; } } if (gameversions[i].description == NULL) { printf("Supported game versions:\n"); for (i=0; gameversions[i].description != NULL; ++i) { printf("\t%s (%s)\n", gameversions[i].cmdline, gameversions[i].description); } I_Error("Unknown game version '%s'", myargv[p+1]); } } else { // Determine automatically if (gamemission == pack_chex) { // chex.exe - identified by iwad filename gameversion = exe_chex; } else if (gamemission == pack_hacx) { // hacx.exe: identified by iwad filename gameversion = exe_hacx; } else if (gamemode == shareware || gamemode == registered) { // original gameversion = exe_doom_1_9; // TODO: Detect IWADs earlier than Doom v1.9. } else if (gamemode == retail) { gameversion = exe_ultimate; } else if (gamemode == commercial) { if (gamemission == doom2) { gameversion = exe_doom_1_9; } else { // Final Doom: tnt or plutonia // Defaults to emulating the first Final Doom executable, // which has the crash in the demo loop; however, having // this as the default should mean that it plays back // most demos correctly. gameversion = exe_final; } } } // The original exe does not support retail - 4th episode not supported if (gameversion < exe_ultimate && gamemode == retail) { gamemode = registered; } // EXEs prior to the Final Doom exes do not support Final Doom. if (gameversion < exe_final && gamemode == commercial && (gamemission == pack_tnt || gamemission == pack_plut)) { gamemission = doom2; } } void PrintGameVersion(void) { int i; for (i=0; gameversions[i].description != NULL; ++i) { if (gameversions[i].version == gameversion) { printf("Emulating the behavior of the " "'%s' executable.\n", gameversions[i].description); break; } } } // Function called at exit to display the ENDOOM screen static void D_Endoom(void) { byte *endoom; // Don't show ENDOOM if we have it disabled, or we're running // in screensaver or control test mode. Only show it once the // game has actually started. if (!show_endoom || !main_loop_started || screensaver_mode || M_CheckParm("-testcontrols") > 0) { return; } endoom = W_CacheLumpName(DEH_String("ENDOOM"), PU_STATIC); I_Endoom(endoom); } #if ORIGCODE // Load dehacked patches needed for certain IWADs. static void LoadIwadDeh(void) { // The Freedoom IWADs have DEHACKED lumps that must be loaded. if (W_CheckNumForName("FREEDOOM") >= 0) { // Old versions of Freedoom (before 2014-09) did not have technically // valid DEHACKED lumps, so ignore errors and just continue if this // is an old IWAD. DEH_LoadLumpByName("DEHACKED", false, true); } // If this is the HACX IWAD, we need to load the DEHACKED lump. if (gameversion == exe_hacx) { if (!DEH_LoadLumpByName("DEHACKED", true, false)) { I_Error("DEHACKED lump not found. Please check that this is the " "Hacx v1.2 IWAD."); } } // Chex Quest needs a separate Dehacked patch which must be downloaded // and installed next to the IWAD. if (gameversion == exe_chex) { char *chex_deh = NULL; char *sep; // Look for chex.deh in the same directory as the IWAD file. sep = strrchr(iwadfile, DIR_SEPARATOR); if (sep != NULL) { size_t chex_deh_len = strlen(iwadfile) + 9; chex_deh = malloc(chex_deh_len); M_StringCopy(chex_deh, iwadfile, chex_deh_len); chex_deh[sep - iwadfile + 1] = '\0'; M_StringConcat(chex_deh, "chex.deh", chex_deh_len); } else { chex_deh = strdup("chex.deh"); } // If the dehacked patch isn't found, try searching the WAD // search path instead. We might find it... if (!M_FileExists(chex_deh)) { free(chex_deh); chex_deh = D_FindWADByName("chex.deh"); } // Still not found? if (chex_deh == NULL) { I_Error("Unable to find Chex Quest dehacked file (chex.deh).\n" "The dehacked file is required in order to emulate\n" "chex.exe correctly. It can be found in your nearest\n" "/idgames repository mirror at:\n\n" " utils/exe_edit/patches/chexdeh.zip"); } if (!DEH_LoadFile(chex_deh)) { I_Error("Failed to load chex.deh needed for emulating chex.exe."); } } } #endif // // D_DoomMain // void D_DoomMain (void) { int p; char file[256]; char demolumpname[9]; #if ORIGCODE int numiwadlumps; #endif I_AtExit(D_Endoom, false); // print banner I_PrintBanner(PACKAGE_STRING); DEH_printf("Z_Init: Init zone memory allocation daemon. \n"); Z_Init (); #ifdef FEATURE_MULTIPLAYER //! // @category net // // Start a dedicated server, routing packets but not participating // in the game itself. // if (M_CheckParm("-dedicated") > 0) { printf("Dedicated server mode.\n"); NET_DedicatedServer(); // Never returns } //! // @category net // // Query the Internet master server for a global list of active // servers. // if (M_CheckParm("-search")) { NET_MasterQuery(); exit(0); } //! // @arg
// @category net // // Query the status of the server running on the given IP // address. // p = M_CheckParmWithArgs("-query", 1); if (p) { NET_QueryAddress(myargv[p+1]); exit(0); } //! // @category net // // Search the local LAN for running servers. // if (M_CheckParm("-localsearch")) { NET_LANQuery(); exit(0); } #endif //! // @vanilla // // Disable monsters. // nomonsters = M_CheckParm ("-nomonsters"); //! // @vanilla // // Monsters respawn after being killed. // respawnparm = M_CheckParm ("-respawn"); //! // @vanilla // // Monsters move faster. // fastparm = M_CheckParm ("-fast"); //! // @vanilla // // Developer mode. F1 saves a screenshot in the current working // directory. // devparm = M_CheckParm ("-devparm"); I_DisplayFPSDots(devparm); //! // @category net // @vanilla // // Start a deathmatch game. // if (M_CheckParm ("-deathmatch")) deathmatch = 1; //! // @category net // @vanilla // // Start a deathmatch 2.0 game. Weapons do not stay in place and // all items respawn after 30 seconds. // if (M_CheckParm ("-altdeath")) deathmatch = 2; if (devparm) DEH_printf(D_DEVSTR); // find which dir to use for config files #ifdef _WIN32 //! // @platform windows // @vanilla // // Save configuration data and savegames in c:\doomdata, // allowing play from CD. // if (M_ParmExists("-cdrom")) { printf(D_CDROM); M_SetConfigDir("c:\\doomdata\\"); } else #endif { // Auto-detect the configuration dir. M_SetConfigDir(NULL); } //! // @arg // @vanilla // // Turbo mode. The player's speed is multiplied by x%. If unspecified, // x defaults to 200. Values are rounded up to 10 and down to 400. // if ( (p=M_CheckParm ("-turbo")) ) { int scale = 200; extern int forwardmove[2]; extern int sidemove[2]; if (p 400) scale = 400; DEH_printf("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; } // init subsystems DEH_printf("V_Init: allocate screens.\n"); V_Init (); // Load configuration files before initialising other subsystems. DEH_printf("M_LoadDefaults: Load system defaults.\n"); M_SetConfigFilenames("default.cfg", PROGRAM_PREFIX "doom.cfg"); D_BindVariables(); M_LoadDefaults(); // Save configuration at exit. I_AtExit(M_SaveDefaults, false); // Find main IWAD file and load it. iwadfile = D_FindIWAD(IWAD_MASK_DOOM, &gamemission); // None found? if (iwadfile == NULL) { I_Error("Game mode indeterminate. No IWAD file was found. Try\n" "specifying one with the '-iwad' command line parameter.\n"); } modifiedgame = false; DEH_printf("W_Init: Init WADfiles.\n"); D_AddFile(iwadfile); #if ORIGCODE numiwadlumps = numlumps; #endif W_CheckCorrectIWAD(doom); // Now that we've loaded the IWAD, we can figure out what gamemission // we're playing and which version of Vanilla Doom we need to emulate. D_IdentifyVersion(); InitGameVersion(); #if ORIGCODE //! // @category mod // // Disable automatic loading of Dehacked patches for certain // IWAD files. // if (!M_ParmExists("-nodeh")) { // Some IWADs have dehacked patches that need to be loaded for // them to be played properly. LoadIwadDeh(); } #endif // Doom 3: BFG Edition includes modified versions of the classic // IWADs which can be identified by an additional DMENUPIC lump. // Furthermore, the M_GDHIGH lumps have been modified in a way that // makes them incompatible to Vanilla Doom and the modified version // of doom2.wad is missing the TITLEPIC lump. // We specifically check for DMENUPIC here, before PWADs have been // loaded which could probably include a lump of that name. if (W_CheckNumForName("dmenupic") >= 0) { printf("BFG Edition: Using workarounds as needed.\n"); bfgedition = true; // BFG Edition changes the names of the secret levels to // censor the Wolfenstein references. It also has an extra // secret level (MAP33). In Vanilla Doom (meaning the DOS // version), MAP33 overflows into the Plutonia level names // array, so HUSTR_33 is actually PHUSTR_1. DEH_AddStringReplacement(HUSTR_31, "level 31: idkfa"); DEH_AddStringReplacement(HUSTR_32, "level 32: keen"); DEH_AddStringReplacement(PHUSTR_1, "level 33: betray"); // The BFG edition doesn't have the "low detail" menu option (fair // enough). But bizarrely, it reuses the M_GDHIGH patch as a label // for the options menu (says "Fullscreen:"). Why the perpetrators // couldn't just add a new graphic lump and had to reuse this one, // I don't know. // // The end result is that M_GDHIGH is too wide and causes the game // to crash. As a workaround to get a minimum level of support for // the BFG edition IWADs, use the "ON"/"OFF" graphics instead. DEH_AddStringReplacement("M_GDHIGH", "M_MSGON"); DEH_AddStringReplacement("M_GDLOW", "M_MSGOFF"); } #ifdef FEATURE_DEHACKED // Load Dehacked patches specified on the command line with -deh. // Note that there's a very careful and deliberate ordering to how // Dehacked patches are loaded. The order we use is: // 1. IWAD dehacked patches. // 2. Command line dehacked patches specified with -deh. // 3. PWAD dehacked patches in DEHACKED lumps. DEH_ParseCommandLine(); #endif // Load PWAD files. modifiedgame = W_ParseCommandLine(); // Debug: // W_PrintDirectory(); //! // @arg // @category demo // @vanilla // // Play back the demo named demo.lmp. // p = M_CheckParmWithArgs ("-playdemo", 1); if (!p) { //! // @arg // @category demo // @vanilla // // Play back the demo named demo.lmp, determining the framerate // of the screen. // p = M_CheckParmWithArgs("-timedemo", 1); } if (p) { // With Vanilla you have to specify the file without extension, // but make that optional. if (M_StringEndsWith(myargv[p + 1], ".lmp")) { M_StringCopy(file, myargv[p + 1], sizeof(file)); } else { DEH_snprintf(file, sizeof(file), "%s.lmp", myargv[p+1]); } if (D_AddFile(file)) { M_StringCopy(demolumpname, lumpinfo[numlumps - 1].name, sizeof(demolumpname)); } else { // If file failed to load, still continue trying to play // the demo in the same way as Vanilla Doom. This makes // tricks like "-playdemo demo1" possible. M_StringCopy(demolumpname, myargv[p + 1], sizeof(demolumpname)); } printf("Playing demo %s.\n", file); } I_AtExit((atexit_func_t) G_CheckDemoStatus, true); // Generate the WAD hash table. Speed things up a bit. W_GenerateHashTable(); // Load DEHACKED lumps from WAD files - but only if we give the right // command line parameter. #if ORIGCODE //! // @category mod // // Load Dehacked patches from DEHACKED lumps contained in one of the // loaded PWAD files. // if (M_ParmExists("-dehlump")) { int i, loaded = 0; for (i = numiwadlumps; i < numlumps; ++i) { if (!strncmp(lumpinfo[i].name, "DEHACKED", 8)) { DEH_LoadLump(i, false, false); loaded++; } } printf(" loaded %i DEHACKED lumps from PWAD files.\n", loaded); } #endif // Set the gamedescription string. This is only possible now that // we've finished loading Dehacked patches. D_SetGameDescription(); #ifdef _WIN32 // In -cdrom mode, we write savegames to c:\doomdata as well as configs. if (M_ParmExists("-cdrom")) { savegamedir = configdir; } else #endif { savegamedir = M_GetSaveGameDir(D_SaveGameIWADName(gamemission)); } // Check for -file in shareware if (modifiedgame) { // These are the lumps that will be checked in IWAD, // if any one is not present, execution will be aborted. char name[23][8]= { "e2m1","e2m2","e2m3","e2m4","e2m5","e2m6","e2m7","e2m8","e2m9", "e3m1","e3m3","e3m3","e3m4","e3m5","e3m6","e3m7","e3m8","e3m9", "dphoof","bfgga0","heada1","cybra1","spida1d1" }; int i; if ( gamemode == shareware) I_Error(DEH_String("\nYou cannot -file with the shareware " "version. Register!")); // Check for fake IWAD with right name, // but w/o all the lumps of the registered version. if (gamemode == registered) for (i = 0;i < 23; i++) if (W_CheckNumForName(name[i])<0) I_Error(DEH_String("\nThis is not the registered version.")); } if (W_CheckNumForName("SS_START") >= 0 || W_CheckNumForName("FF_END") >= 0) { I_PrintDivider(); printf(" WARNING: The loaded WAD file contains modified sprites or\n" " floor textures. You may want to use the '-merge' command\n" " line option instead of '-file'.\n"); } I_PrintStartupBanner(gamedescription); PrintDehackedBanners(); // Freedoom's IWADs are Boom-compatible, which means they usually // don't work in Vanilla (though FreeDM is okay). Show a warning // message and give a link to the website. if (W_CheckNumForName("FREEDOOM") >= 0 && W_CheckNumForName("FREEDM") < 0) { printf(" WARNING: You are playing using one of the Freedoom IWAD\n" " files, which might not work in this port. See this page\n" " for more information on how to play using Freedoom:\n" " http://www.chocolate-doom.org/wiki/index.php/Freedoom\n"); I_PrintDivider(); } DEH_printf("I_Init: Setting up machine state.\n"); I_CheckIsScreensaver(); I_InitTimer(); I_InitJoystick(); I_InitSound(true); I_InitMusic(); #ifdef FEATURE_MULTIPLAYER printf ("NET_Init: Init network subsystem.\n"); NET_Init (); #endif // Initial netgame startup. Connect to server etc. D_ConnectNetGame(); // get skill / episode / map from parms startskill = sk_medium; startepisode = 1; startmap = 1; autostart = false; //! // @arg // @vanilla // // Set the game skill, 1-5 (1: easiest, 5: hardest). A skill of // 0 disables all monsters. // p = M_CheckParmWithArgs("-skill", 1); if (p) { startskill = myargv[p+1][0]-'1'; autostart = true; } //! // @arg // @vanilla // // Start playing on episode n (1-4) // p = M_CheckParmWithArgs("-episode", 1); if (p) { startepisode = myargv[p+1][0]-'0'; startmap = 1; autostart = true; } timelimit = 0; //! // @arg // @category net // @vanilla // // For multiplayer games: exit each level after n minutes. // p = M_CheckParmWithArgs("-timer", 1); if (p) { timelimit = atoi(myargv[p+1]); } //! // @category net // @vanilla // // Austin Virtual Gaming: end levels after 20 minutes. // p = M_CheckParm ("-avg"); if (p) { timelimit = 20; } //! // @arg [ | ] // @vanilla // // Start a game immediately, warping to ExMy (Doom 1) or MAPxy // (Doom 2) // p = M_CheckParmWithArgs("-warp", 1); if (p) { if (gamemode == commercial) startmap = atoi (myargv[p+1]); else { startepisode = myargv[p+1][0]-'0'; if (p + 2 < myargc) { startmap = myargv[p+2][0]-'0'; } else { startmap = 1; } } autostart = true; } // Undocumented: // Invoked by setup to test the controls. p = M_CheckParm("-testcontrols"); if (p > 0) { startepisode = 1; startmap = 1; autostart = true; testcontrols = true; } // Check for load game parameter // We do this here and save the slot number, so that the network code // can override it or send the load slot to other players. //! // @arg // @vanilla // // Load the game in slot s. // p = M_CheckParmWithArgs("-loadgame", 1); if (p) { startloadgame = atoi(myargv[p+1]); } else { // Not loading a game startloadgame = -1; } DEH_printf("M_Init: Init miscellaneous info.\n"); M_Init (); DEH_printf("R_Init: Init DOOM refresh daemon - "); R_Init (); DEH_printf("\nP_Init: Init Playloop state.\n"); P_Init (); DEH_printf("S_Init: Setting up sound.\n"); S_Init (sfxVolume * 8, musicVolume * 8); DEH_printf("D_CheckNetGame: Checking network game status.\n"); D_CheckNetGame (); PrintGameVersion(); DEH_printf("HU_Init: Setting up heads up display.\n"); HU_Init (); DEH_printf("ST_Init: Init status bar.\n"); ST_Init (); // If Doom II without a MAP01 lump, this is a store demo. // Moved this here so that MAP01 isn't constantly looked up // in the main loop. if (gamemode == commercial && W_CheckNumForName("map01") < 0) storedemo = true; if (M_CheckParmWithArgs("-statdump", 1)) { I_AtExit(StatDump, true); DEH_printf("External statistics registered.\n"); } //! // @arg // @category demo // @vanilla // // Record a demo named x.lmp. // p = M_CheckParmWithArgs("-record", 1); if (p) { G_RecordDemo (myargv[p+1]); autostart = true; } p = M_CheckParmWithArgs("-playdemo", 1); if (p) { singledemo = true; // quit after one demo G_DeferedPlayDemo (demolumpname); D_DoomLoop (); // never returns } p = M_CheckParmWithArgs("-timedemo", 1); if (p) { G_TimeDemo (demolumpname); D_DoomLoop (); // never returns } if (startloadgame >= 0) { M_StringCopy(file, P_SaveGameFile(startloadgame), sizeof(file)); G_LoadGame(file); } if (gameaction != ga_loadgame ) { if (autostart || netgame) G_InitNew (startskill, startepisode, startmap); else D_StartTitle (); // start up intro loop } D_DoomLoop (); // never returns } ================================================ FILE: fbdoom/d_main.h ================================================ // // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // 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. // // DESCRIPTION: // System specific interface stuff. // #ifndef __D_MAIN__ #define __D_MAIN__ #include "doomdef.h" // Read events from all input devices void D_ProcessEvents (void); // // BASE LEVEL // void D_PageTicker (void); void D_PageDrawer (void); void D_AdvanceDemo (void); void D_DoAdvanceDemo (void); void D_StartTitle (void); // // GLOBAL VARIABLES // extern gameaction_t gameaction; #endif ================================================ FILE: fbdoom/d_mode.c ================================================ // // Copyright(C) 2005-2014 Simon Howard // // 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. // // // DESCRIPTION: // Functions and definitions relating to the game type and operational // mode. // #include "doomtype.h" #include "d_mode.h" // Valid game mode/mission combinations, with the number of // episodes/maps for each. static struct { GameMission_t mission; GameMode_t mode; int episode; int map; } valid_modes[] = { { pack_chex, shareware, 1, 5 }, { doom, shareware, 1, 9 }, { doom, registered, 3, 9 }, { doom, retail, 4, 9 }, { doom2, commercial, 1, 32 }, { pack_tnt, commercial, 1, 32 }, { pack_plut, commercial, 1, 32 }, { pack_hacx, commercial, 1, 32 }, { heretic, shareware, 1, 9 }, { heretic, registered, 3, 9 }, { heretic, retail, 5, 9 }, { hexen, commercial, 1, 60 }, { strife, commercial, 1, 34 }, }; // Check that a gamemode+gamemission received over the network is valid. boolean D_ValidGameMode(GameMission_t mission, GameMode_t mode) { int i; for (i=0; i= 1 && map <= 3; } else if (mode == registered && episode == 4) { return map == 1; } } // Find the table entry for this mission/mode combination. for (i=0; i= 1 && episode <= valid_modes[i].episode && map >= 1 && map <= valid_modes[i].map; } } // Unknown mode/mission combination return false; } // Get the number of valid episodes for the specified mission/mode. int D_GetNumEpisodes(GameMission_t mission, GameMode_t mode) { int episode; episode = 1; while (D_ValidEpisodeMap(mission, mode, episode, 1)) { ++episode; } return episode - 1; } // Table of valid versions static struct { GameMission_t mission; GameVersion_t version; } valid_versions[] = { { doom, exe_doom_1_9 }, { doom, exe_hacx }, { doom, exe_ultimate }, { doom, exe_final }, { doom, exe_final2 }, { doom, exe_chex }, { heretic, exe_heretic_1_3 }, { hexen, exe_hexen_1_1 }, { strife, exe_strife_1_2 }, { strife, exe_strife_1_31 }, }; boolean D_ValidGameVersion(GameMission_t mission, GameVersion_t version) { int i; // All Doom variants can use the Doom versions. if (mission == doom2 || mission == pack_plut || mission == pack_tnt || mission == pack_hacx || mission == pack_chex) { mission = doom; } for (i=0; i #include "doomfeatures.h" #include "d_main.h" #include "m_argv.h" #include "m_menu.h" #include "m_misc.h" #include "i_system.h" #include "i_timer.h" #include "i_video.h" #include "g_game.h" #include "doomdef.h" #include "doomstat.h" #include "w_checksum.h" #include "w_wad.h" #include "deh_main.h" #include "d_loop.h" ticcmd_t *netcmds; // Called when a player leaves the game static void PlayerQuitGame(player_t *player) { static char exitmsg[80]; unsigned int player_num; player_num = player - players; // Do this the same way as Vanilla Doom does, to allow dehacked // replacements of this message M_StringCopy(exitmsg, DEH_String("Player 1 left the game"), sizeof(exitmsg)); exitmsg[7] += player_num; playeringame[player_num] = false; players[consoleplayer].message = exitmsg; // TODO: check if it is sensible to do this: if (demorecording) { G_CheckDemoStatus (); } } static void RunTic(ticcmd_t *cmds, boolean *ingame) { extern boolean advancedemo; unsigned int i; // Check for player quits. for (i = 0; i < MAXPLAYERS; ++i) { if (!demoplayback && playeringame[i] && !ingame[i]) { PlayerQuitGame(&players[i]); } } netcmds = cmds; // check that there are players in the game. if not, we cannot // run a tic. if (advancedemo) D_DoAdvanceDemo (); G_Ticker (); } static loop_interface_t doom_loop_interface = { D_ProcessEvents, G_BuildTiccmd, RunTic, M_Ticker }; // Load game settings from the specified structure and // set global variables. static void LoadGameSettings(net_gamesettings_t *settings) { unsigned int i; deathmatch = settings->deathmatch; startepisode = settings->episode; startmap = settings->map; startskill = settings->skill; startloadgame = settings->loadgame; lowres_turn = settings->lowres_turn; nomonsters = settings->nomonsters; fastparm = settings->fast_monsters; respawnparm = settings->respawn_monsters; timelimit = settings->timelimit; consoleplayer = settings->consoleplayer; if (lowres_turn) { printf("NOTE: Turning resolution is reduced; this is probably " "because there is a client recording a Vanilla demo.\n"); } for (i = 0; i < MAXPLAYERS; ++i) { playeringame[i] = i < settings->num_players; } } // Save the game settings from global variables to the specified // game settings structure. static void SaveGameSettings(net_gamesettings_t *settings) { // Fill in game settings structure with appropriate parameters // for the new game settings->deathmatch = deathmatch; settings->episode = startepisode; settings->map = startmap; settings->skill = startskill; settings->loadgame = startloadgame; settings->gameversion = gameversion; settings->nomonsters = nomonsters; settings->fast_monsters = fastparm; settings->respawn_monsters = respawnparm; settings->timelimit = timelimit; settings->lowres_turn = M_CheckParm("-record") > 0 && M_CheckParm("-longtics") == 0; } static void InitConnectData(net_connect_data_t *connect_data) { connect_data->max_players = MAXPLAYERS; connect_data->drone = false; //! // @category net // // Run as the left screen in three screen mode. // if (M_CheckParm("-left") > 0) { viewangleoffset = ANG90; connect_data->drone = true; } //! // @category net // // Run as the right screen in three screen mode. // if (M_CheckParm("-right") > 0) { viewangleoffset = ANG270; connect_data->drone = true; } // // Connect data // // Game type fields: connect_data->gamemode = gamemode; connect_data->gamemission = gamemission; // Are we recording a demo? Possibly set lowres turn mode connect_data->lowres_turn = M_CheckParm("-record") > 0 && M_CheckParm("-longtics") == 0; // Read checksums of our WAD directory and dehacked information W_Checksum(connect_data->wad_sha1sum); #if ORIGCODE DEH_Checksum(connect_data->deh_sha1sum); #endif // Are we playing with the Freedoom IWAD? connect_data->is_freedoom = W_CheckNumForName("FREEDOOM") >= 0; } void D_ConnectNetGame(void) { net_connect_data_t connect_data; InitConnectData(&connect_data); netgame = D_InitNetGame(&connect_data); //! // @category net // // Start the game playing as though in a netgame with a single // player. This can also be used to play back single player netgame // demos. // if (M_CheckParm("-solo-net") > 0) { netgame = true; } } // // D_CheckNetGame // Works out player numbers among the net participants // void D_CheckNetGame (void) { net_gamesettings_t settings; if (netgame) { autostart = true; } D_RegisterLoopCallbacks(&doom_loop_interface); SaveGameSettings(&settings); D_StartNetGame(&settings, NULL); LoadGameSettings(&settings); DEH_printf("startskill %i deathmatch: %i startmap: %i startepisode: %i\n", startskill, deathmatch, startmap, startepisode); DEH_printf("player %i of %i (%i nodes)\n", consoleplayer+1, settings.num_players, settings.num_players); // Show players here; the server might have specified a time limit if (timelimit > 0 && deathmatch) { // Gross hack to work like Vanilla: if (timelimit == 20 && M_CheckParm("-avg")) { DEH_printf("Austin Virtual Gaming: Levels will end " "after 20 minutes\n"); } else { DEH_printf("Levels will end after %d minute", timelimit); if (timelimit > 1) printf("s"); printf(".\n"); } } } ================================================ FILE: fbdoom/d_player.h ================================================ // // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // 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. // // DESCRIPTION: // // #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" #include "net_defs.h" // // 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; // 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. 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]; } wbstartstruct_t; #endif ================================================ FILE: fbdoom/d_textur.h ================================================ // // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // 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. // // DESCRIPTION: // Typedefs related to to textures etc., // isolated here to make it easier separating modules. // #ifndef __D_TEXTUR__ #define __D_TEXTUR__ #include "doomtype.h" // // Flats? // // a pic is an unmasked block of pixels typedef struct { byte width; byte height; byte data; } pic_t; #endif ================================================ FILE: fbdoom/d_think.h ================================================ // // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // 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. // // 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__ // // Experimental stuff. // To compile this as "ANSI C with classes" // we will need to handle the various // action functions cleanly. // typedef void (*actionf_v)(); typedef void (*actionf_p1)( void* ); typedef void (*actionf_p2)( void*, void* ); typedef union { actionf_v acv; actionf_p1 acp1; 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; } thinker_t; #endif ================================================ FILE: fbdoom/d_ticcmd.h ================================================ // // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 1993-2008 Raven Software // Copyright(C) 2005-2014 Simon Howard // // 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. // // DESCRIPTION: // System specific interface stuff. // #ifndef __D_TICCMD__ #define __D_TICCMD__ #include "doomtype.h" // 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. typedef struct { signed char forwardmove; // *2048 for move signed char sidemove; // *2048 for move short angleturn; // <<16 for angle delta byte chatchar; byte buttons; // villsa [STRIFE] according to the asm, // consistancy is a short, not a byte byte consistancy; // checks for net game // villsa - Strife specific: byte buttons2; int inventory; // Heretic/Hexen specific: byte lookfly; // look/fly up/down/centering byte arti; // artitype_t to use } ticcmd_t; #endif ================================================ FILE: fbdoom/deh_main.h ================================================ // // Copyright(C) 2005-2014 Simon Howard // // 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. // // // Dehacked entrypoint and common code // #ifndef DEH_MAIN_H #define DEH_MAIN_H #include "doomtype.h" #include "doomfeatures.h" #include "deh_str.h" #include "sha1.h" // These are the limits that dehacked uses (from dheinit.h in the dehacked // source). If these limits are exceeded, it does not generate an error, but // a warning is displayed. #define DEH_VANILLA_NUMSTATES 966 #define DEH_VANILLA_NUMSFX 107 void DEH_ParseCommandLine(void); int DEH_LoadFile(char *filename); int DEH_LoadLump(int lumpnum, boolean allow_long, boolean allow_error); int DEH_LoadLumpByName(char *name, boolean allow_long, boolean allow_error); boolean DEH_ParseAssignment(char *line, char **variable_name, char **value); void DEH_Checksum(sha1_digest_t digest); extern boolean deh_allow_extended_strings; extern boolean deh_allow_long_strings; extern boolean deh_allow_long_cheats; extern boolean deh_apply_cheats; #endif /* #ifndef DEH_MAIN_H */ ================================================ FILE: fbdoom/deh_misc.h ================================================ // // Copyright(C) 2005-2014 Simon Howard // // 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. // // // Parses "Misc" sections in dehacked files // #ifndef DEH_MISC_H #define DEH_MISC_H #include "doomfeatures.h" #define DEH_DEFAULT_INITIAL_HEALTH 100 #define DEH_DEFAULT_INITIAL_BULLETS 50 #define DEH_DEFAULT_MAX_HEALTH 200 #define DEH_DEFAULT_MAX_ARMOR 200 #define DEH_DEFAULT_GREEN_ARMOR_CLASS 1 #define DEH_DEFAULT_BLUE_ARMOR_CLASS 2 #define DEH_DEFAULT_MAX_SOULSPHERE 200 #define DEH_DEFAULT_SOULSPHERE_HEALTH 100 #define DEH_DEFAULT_MEGASPHERE_HEALTH 200 #define DEH_DEFAULT_GOD_MODE_HEALTH 100 #define DEH_DEFAULT_IDFA_ARMOR 200 #define DEH_DEFAULT_IDFA_ARMOR_CLASS 2 #define DEH_DEFAULT_IDKFA_ARMOR 200 #define DEH_DEFAULT_IDKFA_ARMOR_CLASS 2 #define DEH_DEFAULT_BFG_CELLS_PER_SHOT 40 #define DEH_DEFAULT_SPECIES_INFIGHTING 0 #ifdef FEATURE_DEHACKED extern int deh_initial_health; extern int deh_initial_bullets; extern int deh_max_health; extern int deh_max_armor; extern int deh_green_armor_class; extern int deh_blue_armor_class; extern int deh_max_soulsphere; extern int deh_soulsphere_health; extern int deh_megasphere_health; extern int deh_god_mode_health; extern int deh_idfa_armor; extern int deh_idfa_armor_class; extern int deh_idkfa_armor; extern int deh_idkfa_armor_class; extern int deh_bfg_cells_per_shot; extern int deh_species_infighting; #else // If dehacked is disabled, hard coded values #define deh_initial_health DEH_DEFAULT_INITIAL_HEALTH #define deh_initial_bullets DEH_DEFAULT_INITIAL_BULLETS #define deh_max_health DEH_DEFAULT_MAX_HEALTH #define deh_max_armor DEH_DEFAULT_MAX_ARMOR #define deh_green_armor_class DEH_DEFAULT_GREEN_ARMOR_CLASS #define deh_blue_armor_class DEH_DEFAULT_BLUE_ARMOR_CLASS #define deh_max_soulsphere DEH_DEFAULT_MAX_SOULSPHERE #define deh_soulsphere_health DEH_DEFAULT_SOULSPHERE_HEALTH #define deh_megasphere_health DEH_DEFAULT_MEGASPHERE_HEALTH #define deh_god_mode_health DEH_DEFAULT_GOD_MODE_HEALTH #define deh_idfa_armor DEH_DEFAULT_IDFA_ARMOR #define deh_idfa_armor_class DEH_DEFAULT_IDFA_ARMOR_CLASS #define deh_idkfa_armor DEH_DEFAULT_IDKFA_ARMOR #define deh_idkfa_armor_class DEH_DEFAULT_IDKFA_ARMOR_CLASS #define deh_bfg_cells_per_shot DEH_DEFAULT_BFG_CELLS_PER_SHOT #define deh_species_infighting DEH_DEFAULT_SPECIES_INFIGHTING #endif #endif /* #ifndef DEH_MISC_H */ ================================================ FILE: fbdoom/deh_str.h ================================================ // // Copyright(C) 2005-2014 Simon Howard // // 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. // // // Dehacked string replacements // #ifndef DEH_STR_H #define DEH_STR_H #include #include "doomfeatures.h" // Used to do dehacked text substitutions throughout the program #ifdef FEATURE_DEHACKED char *DEH_String(char *s); void DEH_printf(char *fmt, ...); void DEH_fprintf(FILE *fstream, char *fmt, ...); void DEH_snprintf(char *buffer, size_t len, char *fmt, ...); void DEH_AddStringReplacement(char *from_text, char *to_text); #else #define DEH_String(x) (x) #define DEH_printf printf #define DEH_fprintf fprintf #define DEH_snprintf snprintf #define DEH_AddStringReplacement(x, y) #endif #endif /* #ifndef DEH_STR_H */ ================================================ FILE: fbdoom/doom.h ================================================ /* * doom.h * * Created on: 18.02.2015 * Author: Florian */ #ifndef SRC_CHOCDOOM_DOOM_H_ #define SRC_CHOCDOOM_DOOM_H_ /*---------------------------------------------------------------------* * additional includes * *---------------------------------------------------------------------*/ /*---------------------------------------------------------------------* * global definitions * *---------------------------------------------------------------------*/ /*---------------------------------------------------------------------* * type declarations * *---------------------------------------------------------------------*/ /*---------------------------------------------------------------------* * function prototypes * *---------------------------------------------------------------------*/ void D_DoomMain (void); /*---------------------------------------------------------------------* * global data * *---------------------------------------------------------------------*/ /*---------------------------------------------------------------------* * inline functions and function-like macros * *---------------------------------------------------------------------*/ /*---------------------------------------------------------------------* * eof * *---------------------------------------------------------------------*/ #endif /* SRC_CHOCDOOM_DOOM_H_ */ ================================================ FILE: fbdoom/doomdata.h ================================================ // // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // 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. // // 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 "doomtype.h" // Some global defines, that configure the game. #include "doomdef.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 }; // A single Vertex. typedef struct { short x; short 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]; // Front sector, towards viewer. short sector; } PACKEDATTR mapsidedef_t; // A LineDef, as used for editing, and as input // to the BSP builder. typedef struct { short v1; short v2; short flags; short special; short tag; // sidenum[1] will be -1 if one sided short sidenum[2]; } PACKEDATTR maplinedef_t; // // LineDef attributes. // // Solid, is an obstacle. #define ML_BLOCKING 1 // Blocks monsters only. #define ML_BLOCKMONSTERS 2 // Backside will not be present at all // 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 allways 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 // 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 { short numsegs; // Index of first one, segs are stored sequentially. short firstseg; } PACKEDATTR mapsubsector_t; // LineSeg, generated by splitting LineDefs // using partition lines selected by BSP builder. typedef struct { short v1; short v2; short angle; short linedef; short side; short offset; } PACKEDATTR mapseg_t; // BSP node structure. // Indicate a leaf. #define NF_SUBSECTOR 0x8000 typedef struct { // Partition line from (x,y) to x+dx,y+dy) short x; 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; #endif // __DOOMDATA__ ================================================ FILE: fbdoom/doomdef.c ================================================ // // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // 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. // // DESCRIPTION: // DoomDef - basic defines for DOOM, e.g. Version, game mode // and skill level, and display parameters. // #include "doomdef.h" // Location for any defines turned variables. // None. ================================================ FILE: fbdoom/doomdef.h ================================================ // // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // 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. // // DESCRIPTION: // Internally used data structures for virtually everything, // lots of other stuff. // #ifndef __DOOMDEF__ #define __DOOMDEF__ #include #include #include "doomtype.h" #include "i_timer.h" #include "d_mode.h" // // Global parameters/defines. // // DOOM version #define DOOM_VERSION 109 // Version code for cph's longtics hack ("v1.91") #define DOOM_191_VERSION 111 // If rangecheck is undefined, // most parameter validation debugging code will not be compiled #define RANGECHECK // The maximum number of players, multiplayer/networking. #define MAXPLAYERS 4 // 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; typedef enum { ga_nothing, ga_loadlevel, ga_newgame, ga_loadgame, ga_savegame, ga_playdemo, ga_completed, ga_victory, ga_worlddone, ga_screenshot } gameaction_t; // // Difficulty/skill settings/filters. // // 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 // // 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, // No pending weapon change. wp_nochange } 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, // assuming TICRATE is 35 ticks/second. // typedef enum { INVULNTICS = (30*TICRATE), INVISTICS = (60*TICRATE), INFRATICS = (120*TICRATE), IRONTICS = (60*TICRATE) } powerduration_t; #endif // __DOOMDEF__ ================================================ FILE: fbdoom/doomfeatures.h ================================================ // // Copyright(C) 2005-2014 Simon Howard // // 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. // // DESCRIPTION: // List of features which can be enabled/disabled to slim down the // program. // #ifndef DOOM_FEATURES_H #define DOOM_FEATURES_H // Enables wad merging (the '-merge' command line parameter) #undef FEATURE_WAD_MERGE // Enables dehacked support ('-deh') #undef FEATURE_DEHACKED // Enables multiplayer support (network games) #undef FEATURE_MULTIPLAYER // Enables sound output #undef FEATURE_SOUND #endif /* #ifndef DOOM_FEATURES_H */ ================================================ FILE: fbdoom/doomkeys.h ================================================ // // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // 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. // // DESCRIPTION: // Key definitions // #ifndef __DOOMKEYS__ #define __DOOMKEYS__ // // DOOM keyboard definition. // This is the stuff configured by Setup.Exe. // Most key data are simple ascii (uppercased). // #define KEY_RIGHTARROW 0xae #define KEY_LEFTARROW 0xac #define KEY_UPARROW 0xad #define KEY_DOWNARROW 0xaf #define KEY_STRAFE_L 0xa0 #define KEY_STRAFE_R 0xa1 #define KEY_USE 0xa2 #define KEY_FIRE 0xa3 #define KEY_ESCAPE 27 #define KEY_ENTER 13 #define KEY_TAB 9 #define KEY_F1 (0x80+0x3b) #define KEY_F2 (0x80+0x3c) #define KEY_F3 (0x80+0x3d) #define KEY_F4 (0x80+0x3e) #define KEY_F5 (0x80+0x3f) #define KEY_F6 (0x80+0x40) #define KEY_F7 (0x80+0x41) #define KEY_F8 (0x80+0x42) #define KEY_F9 (0x80+0x43) #define KEY_F10 (0x80+0x44) #define KEY_F11 (0x80+0x57) #define KEY_F12 (0x80+0x58) #define KEY_BACKSPACE 0x7f #define KEY_PAUSE 0xff #define KEY_EQUALS 0x3d #define KEY_MINUS 0x2d #define KEY_RSHIFT (0x80+0x36) #define KEY_RCTRL (0x80+0x1d) #define KEY_RALT (0x80+0x38) #define KEY_LALT KEY_RALT // new keys: #define KEY_CAPSLOCK (0x80+0x3a) #define KEY_NUMLOCK (0x80+0x45) #define KEY_SCRLCK (0x80+0x46) #define KEY_PRTSCR (0x80+0x59) #define KEY_HOME (0x80+0x47) #define KEY_END (0x80+0x4f) #define KEY_PGUP (0x80+0x49) #define KEY_PGDN (0x80+0x51) #define KEY_INS (0x80+0x52) #define KEY_DEL (0x80+0x53) #define KEYP_0 0 #define KEYP_1 KEY_END #define KEYP_2 KEY_DOWNARROW #define KEYP_3 KEY_PGDN #define KEYP_4 KEY_LEFTARROW #define KEYP_5 '5' #define KEYP_6 KEY_RIGHTARROW #define KEYP_7 KEY_HOME #define KEYP_8 KEY_UPARROW #define KEYP_9 KEY_PGUP #define KEYP_DIVIDE '/' #define KEYP_PLUS '+' #define KEYP_MINUS '-' #define KEYP_MULTIPLY '*' #define KEYP_PERIOD 0 #define KEYP_EQUALS KEY_EQUALS #define KEYP_ENTER KEY_ENTER #endif // __DOOMKEYS__ ================================================ FILE: fbdoom/doomstat.c ================================================ // // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // 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. // // DESCRIPTION: // Put all global tate variables here. // #include #include "doomstat.h" // Game Mode - identify IWAD as shareware, retail etc. GameMode_t gamemode = indetermined; GameMission_t gamemission = doom; GameVersion_t gameversion = exe_final2; char *gamedescription; // Set if homebrew PWAD stuff has been added. boolean modifiedgame; ================================================ FILE: fbdoom/doomstat.h ================================================ // // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // 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. // // 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 globally shared data structures, // for defining the global state variables. #include "doomdata.h" #include "d_loop.h" // We need the playr data structure as well. #include "d_player.h" // Game mode/mission #include "d_mode.h" #include "net_defs.h" // ------------------------ // 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; extern GameVersion_t gameversion; extern char *gamedescription; // If true, we're using one of the mangled BFG edition IWADs. extern boolean bfgedition; // Convenience macro. // 'gamemission' can be equal to pack_chex or pack_hacx, but these are // just modified versions of doom and doom2, and should be interpreted // as the same most of the time. #define logical_gamemission \ (gamemission == pack_chex ? doom : \ gamemission == pack_hacx ? doom2 : gamemission) // Set if homebrew PWAD stuff has been added. extern boolean modifiedgame; // ------------------------------------------- // Selected skill type, map etc. // // Defaults for menu, methinks. extern skill_t startskill; extern int startepisode; extern int startmap; // Savegame slot to load on startup. This is the value provided to // the -loadgame option. If this has not been provided, this is -1. extern int startloadgame; extern boolean autostart; // Selected by user. extern skill_t gameskill; extern int gameepisode; extern int gamemap; // If non-zero, exit the level after this number of minutes extern int timelimit; // Nightmare mode flag, single player. extern boolean respawnmonsters; // Netgame? Only true if >1 player. extern boolean netgame; // 0=Cooperative; 1=Deathmatch; 2=Altdeath extern int 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. // From m_menu.c: // Sound FX volume has default, 0 - 15 // Music volume has default, 0 - 15 // These are multiplied by 8. extern int sfxVolume; extern int musicVolume; // Current music/sfx card - index useless // w/o a reference LUT in a sound module. // Ideally, this would use indices found // in: /usr/include/linux/soundcard.h extern int snd_MusicDevice; extern int snd_SfxDevice; // Config file? Same disclaimer as above. extern int snd_DesiredMusicDevice; extern int snd_DesiredSfxDevice; // ------------------------- // Status flags for refresh. // // Depending on view size - no status bar? // Note that there is no way to disable the // status bar explicitely. extern boolean statusbaractive; extern boolean automapactive; // In AutoMap mode? extern boolean menuactive; // Menu overlayed? extern boolean paused; // Game Pause? extern boolean viewactive; extern boolean nodrawers; extern boolean testcontrols; extern int testcontrols_mousespeed; // 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; extern int totalitems; extern int totalsecret; // Timer, for scores. extern int levelstarttic; // gametic at level start extern int leveltime; // tics in game play for par // -------------------------------------- // DEMO playback/recording related stuff. // No demo, there is a human player in charge? // Disable save/end game? extern boolean usergame; //? extern boolean demoplayback; extern boolean demorecording; // Round angleturn in ticcmds to the nearest 256. This is used when // recording Vanilla demos in netgames. extern boolean lowres_turn; // Quit after playing a demo from cmdline. extern boolean singledemo; //? 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. // Bookkeeping on players - state. extern player_t players[MAXPLAYERS]; // Alive? Disconnected? extern boolean playeringame[MAXPLAYERS]; // Player spawn spots for deathmatch. #define MAX_DM_STARTS 10 extern mapthing_t deathmatchstarts[MAX_DM_STARTS]; extern mapthing_t* deathmatch_p; // Player spawn spots. extern mapthing_t playerstarts[MAXPLAYERS]; // Intermission stats. // Parameters for world map / intermission. extern wbstartstruct_t wminfo; //----------------------------------------- // Internal parameters, used for engine. // // File handling stuff. extern char * savegamedir; extern char basedefault[1024]; // 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; 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; // Netgame stuff (buffers and pointers, i.e. indices). extern int rndindex; extern ticcmd_t *netcmds; #endif ================================================ FILE: fbdoom/doomtype.h ================================================ // // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // 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. // // DESCRIPTION: // Simple basic typedefs, isolated here to make it easier // separating modules. // #ifndef __DOOMTYPE__ #define __DOOMTYPE__ // #define macros to provide functions missing in Windows. // Outside Windows, we use strings.h for str[n]casecmp. #ifdef _WIN32 #define strcasecmp stricmp #define strncasecmp strnicmp #else #include #endif // // The packed attribute forces structures to be packed into the minimum // space necessary. If this is not done, the compiler may align structure // fields differently to optimize memory access, inflating the overall // structure size. It is important to use the packed attribute on certain // structures where alignment is important, particularly data read/written // to disk. // #ifdef __GNUC__ #define PACKEDATTR __attribute__((packed)) #else #define PACKEDATTR #endif // C99 integer types; with gcc we just use this. Other compilers // should add conditional statements that define the C99 types. // What is really wanted here is stdint.h; however, some old versions // of Solaris don't have stdint.h and only have inttypes.h (the // pre-standardisation version). inttypes.h is also in the C99 // standard and defined to include stdint.h, so include this. #include #ifdef __cplusplus // Use builtin bool type with C++. typedef bool boolean; #else typedef enum { false = 0, true = 1, undef = 0xFFFFFFFF } boolean; #endif typedef uint8_t byte; #include #ifdef _WIN32 #define DIR_SEPARATOR '\\' #define DIR_SEPARATOR_S "\\" #define PATH_SEPARATOR ';' #else #define DIR_SEPARATOR '/' #define DIR_SEPARATOR_S "/" #define PATH_SEPARATOR ':' #endif #define arrlen(array) (sizeof(array) / sizeof(*array)) #endif ================================================ FILE: fbdoom/dstrings.c ================================================ // // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // 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. // // DESCRIPTION: // Globally defined strings. // #include "dstrings.h" char *doom1_endmsg[] = { "are you sure you want to\nquit this great game?", "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.", }; char *doom2_endmsg[] = { // QuitDOOM II messages "are you sure you want to\nquit this great game?", "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.", }; #if 0 // UNUSED messages included in the source release char* endmsg[] = { // DOOM1 QUITMSG, // FinalDOOM? "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." }; #endif ================================================ FILE: fbdoom/dstrings.h ================================================ // // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // 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. // // // DESCRIPTION: // DOOM strings, by language. // #ifndef __DSTRINGS__ #define __DSTRINGS__ // All important printed strings. #include "d_englsh.h" // Misc. other strings. #define SAVEGAMENAME "doomsav" // QuitDOOM messages // 8 per each game type #define NUM_QUITMESSAGES 8 extern char *doom1_endmsg[]; extern char *doom2_endmsg[]; #endif ================================================ FILE: fbdoom/dummy.c ================================================ /* * dummy.c * * Created on: 16.02.2015 * Author: Florian */ /*---------------------------------------------------------------------* * include files * *---------------------------------------------------------------------*/ #include "doomtype.h" /*---------------------------------------------------------------------* * local definitions * *---------------------------------------------------------------------*/ /*---------------------------------------------------------------------* * external declarations * *---------------------------------------------------------------------*/ /*---------------------------------------------------------------------* * public data * *---------------------------------------------------------------------*/ boolean net_client_connected = false; boolean drone = false; /*---------------------------------------------------------------------* * private data * *---------------------------------------------------------------------*/ /*---------------------------------------------------------------------* * private functions * *---------------------------------------------------------------------*/ /*---------------------------------------------------------------------* * public functions * *---------------------------------------------------------------------*/ void I_InitTimidityConfig(void) { } /*---------------------------------------------------------------------* * eof * *---------------------------------------------------------------------*/ ================================================ FILE: fbdoom/f_finale.c ================================================ // // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // 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. // // DESCRIPTION: // Game completion, final screen animation. // #include #include // Functions. #include "deh_main.h" #include "i_system.h" #include "i_swap.h" #include "z_zone.h" #include "v_video.h" #include "w_wad.h" #include "s_sound.h" // Data. #include "d_main.h" #include "dstrings.h" #include "sounds.h" #include "doomstat.h" #include "r_state.h" typedef enum { F_STAGE_TEXT, F_STAGE_ARTSCREEN, F_STAGE_CAST, } finalestage_t; // ? //#include "doomstat.h" //#include "r_local.h" //#include "f_finale.h" // Stage of animation: finalestage_t finalestage; unsigned int finalecount; #define TEXTSPEED 3 #define TEXTWAIT 250 typedef struct { GameMission_t mission; int episode, level; char *background; char *text; } textscreen_t; static textscreen_t textscreens[] = { { doom, 1, 8, "FLOOR4_8", E1TEXT}, { doom, 2, 8, "SFLR6_1", E2TEXT}, { doom, 3, 8, "MFLR8_4", E3TEXT}, { doom, 4, 8, "MFLR8_3", E4TEXT}, { doom2, 1, 6, "SLIME16", C1TEXT}, { doom2, 1, 11, "RROCK14", C2TEXT}, { doom2, 1, 20, "RROCK07", C3TEXT}, { doom2, 1, 30, "RROCK17", C4TEXT}, { doom2, 1, 15, "RROCK13", C5TEXT}, { doom2, 1, 31, "RROCK19", C6TEXT}, { pack_tnt, 1, 6, "SLIME16", T1TEXT}, { pack_tnt, 1, 11, "RROCK14", T2TEXT}, { pack_tnt, 1, 20, "RROCK07", T3TEXT}, { pack_tnt, 1, 30, "RROCK17", T4TEXT}, { pack_tnt, 1, 15, "RROCK13", T5TEXT}, { pack_tnt, 1, 31, "RROCK19", T6TEXT}, { pack_plut, 1, 6, "SLIME16", P1TEXT}, { pack_plut, 1, 11, "RROCK14", P2TEXT}, { pack_plut, 1, 20, "RROCK07", P3TEXT}, { pack_plut, 1, 30, "RROCK17", P4TEXT}, { pack_plut, 1, 15, "RROCK13", P5TEXT}, { pack_plut, 1, 31, "RROCK19", P6TEXT}, }; char* finaletext; char* finaleflat; void F_StartCast (void); void F_CastTicker (void); boolean F_CastResponder (event_t *ev); void F_CastDrawer (void); // // F_StartFinale // void F_StartFinale (void) { size_t i; gameaction = ga_nothing; gamestate = GS_FINALE; viewactive = false; automapactive = false; if (logical_gamemission == doom) { S_ChangeMusic(mus_victor, true); } else { S_ChangeMusic(mus_read_m, true); } // Find the right screen and set the text and background for (i=0; imission == doom) { screen->level = 5; } if (logical_gamemission == screen->mission && (logical_gamemission != doom || gameepisode == screen->episode) && gamemap == screen->level) { finaletext = screen->text; finaleflat = screen->background; } } // Do dehacked substitutions of strings finaletext = DEH_String(finaletext); finaleflat = DEH_String(finaleflat); finalestage = F_STAGE_TEXT; finalecount = 0; } boolean F_Responder (event_t *event) { if (finalestage == F_STAGE_CAST) return F_CastResponder (event); return false; } // // F_Ticker // void F_Ticker (void) { size_t i; // check for skipping if ( (gamemode == commercial) && ( finalecount > 50) ) { // go on to the next level for (i=0 ; istrlen (finaletext)*TEXTSPEED + TEXTWAIT) { finalecount = 0; finalestage = F_STAGE_ARTSCREEN; wipegamestate = -1; // force a wipe if (gameepisode == 3) S_StartMusic (mus_bunny); } } // // F_TextWrite // #include "hu_stuff.h" extern patch_t *hu_font[HU_FONTSIZE]; void F_TextWrite (void) { byte* src; byte* dest; int x,y,w; signed int count; char* ch; int c; int cx; int cy; // erase the entire screen to a tiled background src = W_CacheLumpName ( finaleflat , PU_CACHE); dest = I_VideoBuffer; for (y=0 ; y HU_FONTSIZE) { cx += 4; continue; } w = SHORT (hu_font[c]->width); if (cx+w > SCREENWIDTH) break; V_DrawPatch(cx, cy, hu_font[c]); cx+=w; } } // // Final DOOM 2 animation // Casting by id Software. // in order of appearance // typedef struct { char *name; mobjtype_t type; } castinfo_t; castinfo_t castorder[] = { {CC_ZOMBIE, MT_POSSESSED}, {CC_SHOTGUN, MT_SHOTGUY}, {CC_HEAVY, MT_CHAINGUY}, {CC_IMP, MT_TROOP}, {CC_DEMON, MT_SERGEANT}, {CC_LOST, MT_SKULL}, {CC_CACO, MT_HEAD}, {CC_HELL, MT_KNIGHT}, {CC_BARON, MT_BRUISER}, {CC_ARACH, MT_BABY}, {CC_PAIN, MT_PAIN}, {CC_REVEN, MT_UNDEAD}, {CC_MANCU, MT_FATSO}, {CC_ARCH, MT_VILE}, {CC_SPIDER, MT_SPIDER}, {CC_CYBER, MT_CYBORG}, {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 = F_STAGE_CAST; 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; } void F_CastPrint (char* text) { char* ch; 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 = SHORT (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 = SHORT (hu_font[c]->width); V_DrawPatch(cx, 180, hu_font[c]); cx+=w; } } // // F_CastDrawer // void F_CastDrawer (void) { spritedef_t* sprdef; spriteframe_t* sprframe; int lump; boolean flip; patch_t* patch; // erase the entire screen to a background V_DrawPatch (0, 0, W_CacheLumpName (DEH_String("BOSSBACK"), PU_CACHE)); F_CastPrint (DEH_String(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]; patch = W_CacheLumpNum (lump+firstspritelump, PU_CACHE); if (flip) V_DrawPatchFlipped(160, 170, patch); else V_DrawPatch(160, 170, patch); } // // F_DrawPatchCol // void F_DrawPatchCol ( int x, patch_t* patch, int col ) { column_t* column; byte* source; byte* dest; byte* desttop; int count; column = (column_t *)((byte *)patch + LONG(patch->columnofs[col])); desttop = I_VideoBuffer + x; // step through the posts in a column while (column->topdelta != 0xff ) { source = (byte *)column + 3; dest = desttop + column->topdelta*SCREENWIDTH; count = column->length; while (count--) { *dest = *source++; dest += SCREENWIDTH; } column = (column_t *)( (byte *)column + column->length + 4 ); } } // // F_BunnyScroll // void F_BunnyScroll (void) { signed int scrolled; int x; patch_t* p1; patch_t* p2; char name[10]; int stage; static int laststage; p1 = W_CacheLumpName (DEH_String("PFUB2"), PU_LEVEL); p2 = W_CacheLumpName (DEH_String("PFUB1"), PU_LEVEL); V_MarkRect (0, 0, SCREENWIDTH, SCREENHEIGHT); scrolled = (320 - ((signed int) finalecount-230)/2); if (scrolled > 320) scrolled = 320; if (scrolled < 0) scrolled = 0; for ( x=0 ; x 6) stage = 6; if (stage > laststage) { S_StartSound (NULL, sfx_pistol); laststage = stage; } DEH_snprintf(name, 10, "END%i", stage); V_DrawPatch((SCREENWIDTH - 13 * 8) / 2, (SCREENHEIGHT - 8 * 8) / 2, W_CacheLumpName (name,PU_CACHE)); } static void F_ArtScreenDrawer(void) { char *lumpname; if (gameepisode == 3) { F_BunnyScroll(); } else { switch (gameepisode) { case 1: if (gamemode == retail) { lumpname = "CREDIT"; } else { lumpname = "HELP2"; } break; case 2: lumpname = "VICTORY2"; break; case 4: lumpname = "ENDPIC"; break; default: return; } lumpname = DEH_String(lumpname); V_DrawPatch (0, 0, W_CacheLumpName(lumpname, PU_CACHE)); } } // // F_Drawer // void F_Drawer (void) { switch (finalestage) { case F_STAGE_CAST: F_CastDrawer(); break; case F_STAGE_TEXT: F_TextWrite(); break; case F_STAGE_ARTSCREEN: F_ArtScreenDrawer(); break; } } ================================================ FILE: fbdoom/f_finale.h ================================================ // // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // 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. // // DESCRIPTION: // // #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 ================================================ FILE: fbdoom/f_wipe.c ================================================ // // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // 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. // // DESCRIPTION: // Mission begin melt/wipe screen special effect. // #include #include "z_zone.h" #include "i_video.h" #include "v_video.h" #include "m_random.h" #include "doomtype.h" #include "f_wipe.h" // // SCREEN WIPE PACKAGE // // when zero, stop the wipe static boolean go = 0; static byte* wipe_scr_start; static byte* wipe_scr_end; static byte* wipe_scr; void wipe_shittyColMajorXform ( short* array, int width, int height ) { int x; int y; short* dest; dest = (short*) Z_Malloc(width*height*2, PU_STATIC, 0); for(y=0;y *e) { newval = *w - ticks; if (newval < *e) *w = *e; else *w = newval; changed = true; } else if (*w < *e) { newval = *w + ticks; if (newval > *e) *w = *e; else *w = newval; changed = true; } } w++; e++; } return !changed; } int wipe_exitColorXForm ( int width, int height, int ticks ) { return 0; } static int* y; int wipe_initMelt ( int width, int height, int ticks ) { int i, r; // copy start screen to main screen memcpy(wipe_scr, wipe_scr_start, width*height); // makes this wipe faster (in theory) // to have stuff in column-major format wipe_shittyColMajorXform((short*)wipe_scr_start, width/2, height); wipe_shittyColMajorXform((short*)wipe_scr_end, width/2, height); // setup initial column positions // (y<0 => not ready to scroll yet) y = (int *) Z_Malloc(width*sizeof(int), PU_STATIC, 0); y[0] = -(M_Random()%16); for (i=1;i 0) y[i] = 0; else if (y[i] == -16) y[i] = -15; } return 0; } int wipe_doMelt ( int width, int height, int ticks ) { int i; int j; int dy; int idx; short* s; short* d; boolean done = true; width/=2; while (ticks--) { for (i=0;i= height) dy = height - y[i]; s = &((short *)wipe_scr_end)[i*height+y[i]]; d = &((short *)wipe_scr)[y[i]*width+i]; idx = 0; for (j=dy;j;j--) { d[idx] = *(s++); idx += width; } y[i] += dy; s = &((short *)wipe_scr_start)[i*height]; d = &((short *)wipe_scr)[y[i]*width+i]; idx = 0; for (j=height-y[i];j;j--) { d[idx] = *(s++); idx += width; } done = false; } } } return done; } int wipe_exitMelt ( int width, int height, int ticks ) { Z_Free(y); Z_Free(wipe_scr_start); Z_Free(wipe_scr_end); return 0; } int wipe_StartScreen ( int x, int y, int width, int height ) { wipe_scr_start = Z_Malloc(SCREENWIDTH * SCREENHEIGHT, PU_STATIC, NULL); I_ReadScreen(wipe_scr_start); return 0; } int wipe_EndScreen ( int x, int y, int width, int height ) { wipe_scr_end = Z_Malloc(SCREENWIDTH * SCREENHEIGHT, PU_STATIC, NULL); I_ReadScreen(wipe_scr_end); V_DrawBlock(x, y, width, height, wipe_scr_start); // restore start scr. return 0; } int wipe_ScreenWipe ( int wipeno, int x, int y, int width, int height, int ticks ) { int rc; static int (*wipes[])(int, int, int) = { wipe_initColorXForm, wipe_doColorXForm, wipe_exitColorXForm, wipe_initMelt, wipe_doMelt, wipe_exitMelt }; // initial stuff if (!go) { go = 1; // wipe_scr = (byte *) Z_Malloc(width*height, PU_STATIC, 0); // DEBUG wipe_scr = I_VideoBuffer; (*wipes[wipeno*3])(width, height, ticks); } // do a piece of wipe-in V_MarkRect(0, 0, width, height); rc = (*wipes[wipeno*3+1])(width, height, ticks); // V_DrawBlock(x, y, 0, width, height, wipe_scr); // DEBUG // final stuff if (rc) { go = 0; (*wipes[wipeno*3+2])(width, height, ticks); } return !go; } ================================================ FILE: fbdoom/f_wipe.h ================================================ // // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // 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. // // DESCRIPTION: // Mission start screen wipe/melt, special effects. // #ifndef __F_WIPE_H__ #define __F_WIPE_H__ // // SCREEN WIPE PACKAGE // enum { // simple gradual pixel change for 8-bit only wipe_ColorXForm, // weird screen melt wipe_Melt, wipe_NUMWIPES }; int wipe_StartScreen ( int x, int y, int width, int height ); int wipe_EndScreen ( int x, int y, int width, int height ); int wipe_ScreenWipe ( int wipeno, int x, int y, int width, int height, int ticks ); #endif ================================================ FILE: fbdoom/g_game.c ================================================ // // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // 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. // // DESCRIPTION: none // #include #include #include #include "doomdef.h" #include "doomkeys.h" #include "doomstat.h" #include "deh_main.h" #include "deh_misc.h" #include "z_zone.h" #include "f_finale.h" #include "m_argv.h" #include "m_controls.h" #include "m_misc.h" #include "m_menu.h" #include "m_random.h" #include "i_system.h" #include "i_timer.h" #include "i_video.h" #include "p_setup.h" #include "p_saveg.h" #include "p_tick.h" #include "d_main.h" #include "wi_stuff.h" #include "hu_stuff.h" #include "st_stuff.h" #include "am_map.h" #include "statdump.h" // Needs access to LFB. #include "v_video.h" #include "w_wad.h" #include "p_local.h" #include "s_sound.h" // Data. #include "dstrings.h" #include "sounds.h" // SKY handling - still the wrong place. #include "r_data.h" #include "r_sky.h" #include "g_game.h" #define SAVEGAMESIZE 0x2c000 void G_ReadDemoTiccmd (ticcmd_t* cmd); void G_WriteDemoTiccmd (ticcmd_t* cmd); void G_PlayerReborn (int player); void G_DoReborn (int playernum); void G_DoLoadLevel (void); void G_DoNewGame (void); void G_DoPlayDemo (void); void G_DoCompleted (void); void G_DoVictory (void); void G_DoWorldDone (void); void G_DoSaveGame (void); // Gamestate the last time G_Ticker was called. gamestate_t oldgamestate; gameaction_t gameaction; gamestate_t gamestate; skill_t gameskill; boolean respawnmonsters; int gameepisode; int gamemap; // If non-zero, exit the level after this number of minutes. int timelimit; boolean paused; boolean sendpause; // send a pause event next tic boolean sendsave; // send a save event next tic boolean usergame; // ok to save / end game boolean timingdemo; // if true, exit with report on completion boolean nodrawers; // for comparative timing purposes int starttime; // for comparative timing purposes boolean viewactive; int deathmatch; // only if started as net death boolean netgame; // only true if packets are broadcast boolean playeringame[MAXPLAYERS]; player_t players[MAXPLAYERS]; boolean turbodetected[MAXPLAYERS]; int consoleplayer; // player taking events and displaying int displayplayer; // view being displayed int levelstarttic; // gametic at level start int totalkills, totalitems, totalsecret; // for intermission char *demoname; boolean demorecording; boolean longtics; // cph's doom 1.91 longtics hack boolean lowres_turn; // low resolution turning for longtics boolean demoplayback; boolean netdemo; byte* demobuffer; byte* demo_p; byte* demoend; boolean singledemo; // quit after playing a demo from cmdline boolean precache = true; // if true, load all graphics at start boolean testcontrols = false; // Invoked by setup to test controls int testcontrols_mousespeed; wbstartstruct_t wminfo; // parms for world map / intermission byte consistancy[MAXPLAYERS][BACKUPTICS]; #define MAXPLMOVE (forwardmove[1]) #define TURBOTHRESHOLD 0x32 fixed_t forwardmove[2] = {0x19, 0x32}; fixed_t sidemove[2] = {0x18, 0x28}; fixed_t angleturn[3] = {640, 1280, 320}; // + slow turn static int *weapon_keys[] = { &key_weapon1, &key_weapon2, &key_weapon3, &key_weapon4, &key_weapon5, &key_weapon6, &key_weapon7, &key_weapon8 }; // Set to -1 or +1 to switch to the previous or next weapon. static int next_weapon = 0; // Used for prev/next weapon keys. static const struct { weapontype_t weapon; weapontype_t weapon_num; } weapon_order_table[] = { { wp_fist, wp_fist }, { wp_chainsaw, wp_fist }, { wp_pistol, wp_pistol }, { wp_shotgun, wp_shotgun }, { wp_supershotgun, wp_shotgun }, { wp_chaingun, wp_chaingun }, { wp_missile, wp_missile }, { wp_plasma, wp_plasma }, { wp_bfg, wp_bfg } }; #define SLOWTURNTICS 6 #define NUMKEYS 256 #define MAX_JOY_BUTTONS 20 static boolean gamekeydown[NUMKEYS]; static int turnheld; // for accelerative turning static boolean mousearray[MAX_MOUSE_BUTTONS + 1]; static boolean *mousebuttons = &mousearray[1]; // allow [-1] // mouse values are used once int mousex; int mousey; static int dclicktime; static boolean dclickstate; static int dclicks; static int dclicktime2; static boolean dclickstate2; static int dclicks2; // joystick values are repeated static int joyxmove; static int joyymove; static int joystrafemove; static boolean joyarray[MAX_JOY_BUTTONS + 1]; static boolean *joybuttons = &joyarray[1]; // allow [-1] static int savegameslot; static char savedescription[32]; #define BODYQUESIZE 32 mobj_t* bodyque[BODYQUESIZE]; int bodyqueslot; int vanilla_savegame_limit = 1; int vanilla_demo_limit = 1; int G_CmdChecksum (ticcmd_t* cmd) { size_t i; int sum = 0; for (i=0 ; i< sizeof(*cmd)/4 - 1 ; i++) sum += ((int *)cmd)[i]; return sum; } static boolean WeaponSelectable(weapontype_t weapon) { // Can't select the super shotgun in Doom 1. if (weapon == wp_supershotgun && logical_gamemission == doom) { return false; } // These weapons aren't available in shareware. if ((weapon == wp_plasma || weapon == wp_bfg) && gamemission == doom && gamemode == shareware) { return false; } // Can't select a weapon if we don't own it. if (!players[consoleplayer].weaponowned[weapon]) { return false; } // Can't select the fist if we have the chainsaw, unless // we also have the berserk pack. if (weapon == wp_fist && players[consoleplayer].weaponowned[wp_chainsaw] && !players[consoleplayer].powers[pw_strength]) { return false; } return true; } static int G_NextWeapon(int direction) { weapontype_t weapon; int start_i, i; // Find index in the table. if (players[consoleplayer].pendingweapon == wp_nochange) { weapon = players[consoleplayer].readyweapon; } else { weapon = players[consoleplayer].pendingweapon; } for (i=0; iconsistancy = consistancy[consoleplayer][maketic%BACKUPTICS]; strafe = gamekeydown[key_strafe] || mousebuttons[mousebstrafe] || joybuttons[joybstrafe]; // fraggle: support the old "joyb_speed = 31" hack which // allowed an autorun effect speed = key_speed >= NUMKEYS || joybspeed >= MAX_JOY_BUTTONS || gamekeydown[key_speed] || joybuttons[joybspeed]; 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; // let movement keys cancel each other out if (strafe) { if (gamekeydown[key_right]) { // fprintf(stderr, "strafe right\n"); side += sidemove[speed]; } if (gamekeydown[key_left]) { // fprintf(stderr, "strafe left\n"); 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]) { // fprintf(stderr, "up\n"); forward += forwardmove[speed]; } if (gamekeydown[key_down]) { // fprintf(stderr, "down\n"); forward -= forwardmove[speed]; } if (joyymove < 0) forward += forwardmove[speed]; if (joyymove > 0) forward -= forwardmove[speed]; if (gamekeydown[key_strafeleft] || joybuttons[joybstrafeleft] || mousebuttons[mousebstrafeleft] || joystrafemove < 0) { side -= sidemove[speed]; } if (gamekeydown[key_straferight] || joybuttons[joybstraferight] || mousebuttons[mousebstraferight] || joystrafemove > 0) { 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] || mousebuttons[mousebuse]) { cmd->buttons |= BT_USE; // clear double clicks if hit use button dclicks = 0; } // If the previous or next weapon button is pressed, the // next_weapon variable is set to change weapons when // we generate a ticcmd. Choose a new weapon. if (gamestate == GS_LEVEL && next_weapon != 0) { i = G_NextWeapon(next_weapon); cmd->buttons |= BT_CHANGE; cmd->buttons |= i << BT_WEAPONSHIFT; } else { // Check weapon keys. for (i=0; ibuttons |= BT_CHANGE; cmd->buttons |= i< 1 ) { dclickstate = mousebuttons[mousebforward]; if (dclickstate) dclicks++; if (dclicks == 2) { cmd->buttons |= BT_USE; dclicks = 0; } else dclicktime = 0; } else { dclicktime += ticdup; if (dclicktime > 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 { dclicktime2 += ticdup; if (dclicktime2 > 20) { dclicks2 = 0; dclickstate2 = 0; } } } forward += mousey; if (strafe) side += mousex*2; else cmd->angleturn -= mousex*0x8; if (mousex == 0) { // No movement in the previous frame testcontrols_mousespeed = 0; } 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 += forward; cmd->sidemove += side; // special buttons if (sendpause) { sendpause = false; cmd->buttons = BT_SPECIAL | BTS_PAUSE; } if (sendsave) { sendsave = false; cmd->buttons = BT_SPECIAL | BTS_SAVEGAME | (savegameslot<angleturn + carry; // round angleturn to the nearest 256 unit boundary // for recording demos with single byte values for turn cmd->angleturn = (desired_angleturn + 128) & 0xff00; // Carry forward the error from the reduced resolution to the // next tic, so that successive small movements can accumulate. carry = desired_angleturn - cmd->angleturn; } } // // G_DoLoadLevel // 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(DEH_String(SKYFLATNAME)); // The "Sky never changes in Doom II" bug was fixed in // the id Anthology version of doom2.exe for Final Doom. if ((gamemode == commercial) && (gameversion == exe_final2 || gameversion == exe_chex)) { char *skytexturename; if (gamemap < 12) { skytexturename = "SKY1"; } else if (gamemap < 21) { skytexturename = "SKY2"; } else { skytexturename = "SKY3"; } skytexturename = DEH_String(skytexturename); skytexture = R_TextureNumForName(skytexturename); } levelstarttic = gametic; // for time calculation if (wipegamestate == GS_LEVEL) wipegamestate = -1; // force a wipe gamestate = GS_LEVEL; for (i=0 ; itype == ev_keydown && ev->data1 == key_spy && (singledemo || !deathmatch) ) { // spy mode do { displayplayer++; if (displayplayer == MAXPLAYERS) displayplayer = 0; } while (!playeringame[displayplayer] && displayplayer != consoleplayer); return true; } // any other key pops up menu if in demos if (gameaction == ga_nothing && !singledemo && (demoplayback || gamestate == GS_DEMOSCREEN) ) { if (ev->type == ev_keydown || (ev->type == ev_mouse && ev->data1) || (ev->type == ev_joystick && ev->data1) ) { M_StartControlPanel (); return true; } return false; } if (gamestate == GS_LEVEL) { #if 0 if (devparm && ev->type == ev_keydown && ev->data1 == ';') { G_DeathMatchSpawnPlayer (0); return true; } #endif if (HU_Responder (ev)) return true; // chat ate the event if (ST_Responder (ev)) return true; // status window ate it if (AM_Responder (ev)) return true; // automap ate it } if (gamestate == GS_FINALE) { if (F_Responder (ev)) return true; // finale ate the event } if (testcontrols && ev->type == ev_mouse) { // If we are invoked by setup to test the controls, save the // mouse speed so that we can display it on-screen. // Perform a low pass filter on this so that the thermometer // appears to move smoothly. testcontrols_mousespeed = abs(ev->data2); } // If the next/previous weapon keys are pressed, set the next_weapon // variable to change weapons when the next ticcmd is generated. if (ev->type == ev_keydown && ev->data1 == key_prevweapon) { next_weapon = -1; } else if (ev->type == ev_keydown && ev->data1 == key_nextweapon) { next_weapon = 1; } switch (ev->type) { case ev_keydown: if (ev->data1 == key_pause) { sendpause = true; } else 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: SetMouseButtons(ev->data1); mousex = ev->data2*(mouseSensitivity+5)/10; mousey = ev->data3*(mouseSensitivity+5)/10; return true; // eat events case ev_joystick: SetJoyButtons(ev->data1); joyxmove = ev->data2; joyymove = ev->data3; joystrafemove = ev->data4; return true; // eat events default: break; } return false; } // // G_Ticker // Make ticcmd_ts for the players. // void G_Ticker (void) { int i; int buf; ticcmd_t* cmd; // do player reborns if needed for (i=0 ; iforwardmove > TURBOTHRESHOLD) { turbodetected[i] = true; } if ((gametic & 31) == 0 && ((gametic >> 5) % MAXPLAYERS) == i && turbodetected[i]) { static char turbomessage[80]; extern char *player_names[4]; M_snprintf(turbomessage, sizeof(turbomessage), "%s is turbo!", player_names[i]); players[consoleplayer].message = turbomessage; turbodetected[i] = false; } if (netgame && !netdemo && !(gametic%ticdup) ) { if (gametic > BACKUPTICS && consistancy[i][buf] != cmd->consistancy) { I_Error ("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] = rndindex; } } } // check for special buttons for (i=0 ; i>BTS_SAVESHIFT; gameaction = ga_savegame; break; } } } } // Have we just finished displaying an intermission screen? if (oldgamestate == GS_INTERMISSION && gamestate != GS_INTERMISSION) { WI_End(); } oldgamestate = gamestate; // 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_InitPlayer // Called at the start. // Called by the game initialization functions. // void G_InitPlayer (int player) { // clear everything else to defaults G_PlayerReborn (player); } // // G_PlayerFinishLevel // Can when a player completes a level. // void G_PlayerFinishLevel (int player) { player_t* p; p = &players[player]; memset (p->powers, 0, sizeof (p->powers)); memset (p->cards, 0, sizeof (p->cards)); p->mo->flags &= ~MF_SHADOW; // cancel invisibility p->extralight = 0; // cancel gun flashes p->fixedcolormap = 0; // cancel ir gogles p->damagecount = 0; // no palette changes p->bonuscount = 0; } // // 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]; memset (p, 0, sizeof(*p)); 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 = deh_initial_health; // Use dehacked value p->readyweapon = p->pendingweapon = wp_pistol; p->weaponowned[wp_fist] = true; p->weaponowned[wp_pistol] = true; p->ammo[am_clip] = deh_initial_bullets; 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 // void P_SpawnPlayer (mapthing_t* mthing); boolean G_CheckSpot ( int playernum, mapthing_t* mthing ) { fixed_t x; fixed_t y; subsector_t* ss; mobj_t* mo; 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; if (!P_CheckPosition (players[playernum].mo, x, y) ) return false; // flush an old corpse if needed if (bodyqueslot >= BODYQUESIZE) P_RemoveMobj (bodyque[bodyqueslot%BODYQUESIZE]); bodyque[bodyqueslot%BODYQUESIZE] = players[playernum].mo; bodyqueslot++; // spawn a teleport fog ss = R_PointInSubsector (x,y); // The code in the released source looks like this: // // an = ( ANG45 * (((unsigned int) mthing->angle)/45) ) // >> ANGLETOFINESHIFT; // mo = P_SpawnMobj (x+20*finecosine[an], y+20*finesine[an] // , ss->sector->floorheight // , MT_TFOG); // // But 'an' can be a signed value in the DOS version. This means that // we get a negative index and the lookups into finecosine/finesine // end up dereferencing values in finetangent[]. // A player spawning on a deathmatch start facing directly west spawns // "silently" with no spawn fog. Emulate this. // // This code is imported from PrBoom+. { fixed_t xa, ya; signed int an; // This calculation overflows in Vanilla Doom, but here we deliberately // avoid integer overflow as it is undefined behavior, so the value of // 'an' will always be positive. an = (ANG45 >> ANGLETOFINESHIFT) * ((signed int) mthing->angle / 45); switch (an) { case 4096: // -4096: xa = finetangent[2048]; // finecosine[-4096] ya = finetangent[0]; // finesine[-4096] break; case 5120: // -3072: xa = finetangent[3072]; // finecosine[-3072] ya = finetangent[1024]; // finesine[-3072] break; case 6144: // -2048: xa = finesine[0]; // finecosine[-2048] ya = finetangent[2048]; // finesine[-2048] break; case 7168: // -1024: xa = finesine[1024]; // finecosine[-1024] ya = finetangent[3072]; // finesine[-1024] break; case 0: case 1024: case 2048: case 3072: xa = finecosine[an]; ya = finesine[an]; break; default: I_Error("G_CheckSpot: unexpected angle %d\n", an); xa = ya = 0; break; } 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 i,j; int selections; selections = deathmatch_p - deathmatchstarts; if (selections < 4) I_Error ("Only %i deathmatch spots, 4 required", selections); for (j=0 ; j<20 ; j++) { i = P_Random() % selections; if (G_CheckSpot (playernum, &deathmatchstarts[i]) ) { deathmatchstarts[i].type = playernum+1; P_SpawnPlayer (&deathmatchstarts[i]); return; } } // no good spot, so the player will probably get stuck P_SpawnPlayer (&playerstarts[playernum]); } // // G_DoReborn // void G_DoReborn (int playernum) { int i; if (!netgame) { // reload the level from scratch gameaction = ga_loadlevel; } else { // respawn at the start // 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 (&playerstarts[playernum]); return; } // try to spawn at one of the other players spots for (i=0 ; i SAVEGAMESIZE) { I_Error ("Savegame buffer overrun"); } // Finish up, close the savegame file. fclose(save_stream); if (recovery_savegame_file != NULL) { // We failed to save to the normal location, but we wrote a // recovery file to the temp directory. Now we can bomb out // with an error. I_Error("Failed to open savegame file '%s' for writing.\n" "But your game has been saved to '%s' for recovery.", temp_savegame_file, recovery_savegame_file); } // Now rename the temporary savegame file to the actual savegame // file, overwriting the old savegame if there was one there. remove(savegame_file); rename(temp_savegame_file, savegame_file); gameaction = ga_nothing; M_StringCopy(savedescription, "", sizeof(savedescription)); players[consoleplayer].message = DEH_String(GGSAVED); // draw the pattern into the back screen R_FillBackScreen (); } // // G_InitNew // Can be called by the startup code or the menu task, // consoleplayer, displayplayer, playeringame[] should be set. // skill_t d_skill; int d_episode; 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; } void G_DoNewGame (void) { demoplayback = false; netdemo = false; netgame = false; deathmatch = false; playeringame[1] = playeringame[2] = playeringame[3] = 0; respawnparm = false; fastparm = false; nomonsters = false; consoleplayer = 0; G_InitNew (d_skill, d_episode, d_map); gameaction = ga_nothing; } void G_InitNew ( skill_t skill, int episode, int map ) { char *skytexturename; int i; if (paused) { paused = false; S_ResumeSound (); } /* // Note: This commented-out block of code was added at some point // between the DOS version(s) and the Doom source release. It isn't // found in disassemblies of the DOS version and causes IDCLEV and // the -warp command line parameter to behave differently. // This is left here for posterity. // This was quite messy with SPECIAL and commented parts. // Supposedly hacks to make the latest edition work. // It might not work properly. 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 (skill > sk_nightmare) skill = sk_nightmare; if (gameversion >= exe_ultimate) { if (episode == 0) { episode = 4; } } else { if (episode < 1) { episode = 1; } if (episode > 3) { episode = 3; } } if (episode > 1 && gamemode == shareware) { episode = 1; } if (map < 1) map = 1; if ( (map > 9) && ( gamemode != commercial) ) map = 9; M_ClearRandom (); if (skill == sk_nightmare || respawnparm ) respawnmonsters = true; else respawnmonsters = false; if (fastparm || (skill == sk_nightmare && gameskill != sk_nightmare) ) { for (i=S_SARG_RUN1 ; i<=S_SARG_PAIN2 ; i++) states[i].tics >>= 1; mobjinfo[MT_BRUISERSHOT].speed = 20*FRACUNIT; mobjinfo[MT_HEADSHOT].speed = 20*FRACUNIT; mobjinfo[MT_TROOPSHOT].speed = 20*FRACUNIT; } else if (skill != sk_nightmare && gameskill == sk_nightmare) { 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; } // force players to be initialized upon first level load for (i=0 ; iforwardmove = ((signed char)*demo_p++); cmd->sidemove = ((signed char)*demo_p++); // If this is a longtics demo, read back in higher resolution if (longtics) { cmd->angleturn = *demo_p++; cmd->angleturn |= (*demo_p++) << 8; } else { cmd->angleturn = ((unsigned char) *demo_p++)<<8; } cmd->buttons = (unsigned char)*demo_p++; } // Increase the size of the demo buffer to allow unlimited demos static void IncreaseDemoBuffer(void) { int current_length; byte *new_demobuffer; byte *new_demop; int new_length; // Find the current size current_length = demoend - demobuffer; // Generate a new buffer twice the size new_length = current_length * 2; new_demobuffer = Z_Malloc(new_length, PU_STATIC, 0); new_demop = new_demobuffer + (demo_p - demobuffer); // Copy over the old data memcpy(new_demobuffer, demobuffer, current_length); // Free the old buffer and point the demo pointers at the new buffer. Z_Free(demobuffer); demobuffer = new_demobuffer; demo_p = new_demop; demoend = demobuffer + new_length; } void G_WriteDemoTiccmd (ticcmd_t* cmd) { byte *demo_start; if (gamekeydown[key_demo_quit]) // press q to end demo recording G_CheckDemoStatus (); demo_start = demo_p; *demo_p++ = cmd->forwardmove; *demo_p++ = cmd->sidemove; // If this is a longtics demo, record in higher resolution if (longtics) { *demo_p++ = (cmd->angleturn & 0xff); *demo_p++ = (cmd->angleturn >> 8) & 0xff; } else { *demo_p++ = cmd->angleturn >> 8; } *demo_p++ = cmd->buttons; // reset demo pointer back demo_p = demo_start; if (demo_p > demoend - 16) { if (vanilla_demo_limit) { // no more space G_CheckDemoStatus (); return; } else { // Vanilla demo limit disabled: unlimited // demo lengths! IncreaseDemoBuffer(); } } G_ReadDemoTiccmd (cmd); // make SURE it is exactly the same } // // G_RecordDemo // void G_RecordDemo (char *name) { size_t demoname_size; int i; int maxsize; usergame = false; demoname_size = strlen(name) + 5; demoname = Z_Malloc(demoname_size, PU_STATIC, NULL); M_snprintf(demoname, demoname_size, "%s.lmp", name); maxsize = 0x20000; //! // @arg // @category demo // @vanilla // // Specify the demo buffer size (KiB) // i = M_CheckParmWithArgs("-maxdemo", 1); if (i) maxsize = atoi(myargv[i+1])*1024; demobuffer = Z_Malloc (maxsize,PU_STATIC,NULL); demoend = demobuffer + maxsize; demorecording = true; } // Get the demo version code appropriate for the version set in gameversion. int G_VanillaVersionCode(void) { switch (gameversion) { case exe_doom_1_2: I_Error("Doom 1.2 does not have a version code!"); case exe_doom_1_666: return 106; case exe_doom_1_7: return 107; case exe_doom_1_8: return 108; case exe_doom_1_9: default: // All other versions are variants on v1.9: return 109; } } void G_BeginRecording (void) { int i; //! // @category demo // // Record a high resolution "Doom 1.91" demo. // longtics = M_CheckParm("-longtics") != 0; // If not recording a longtics demo, record in low res lowres_turn = !longtics; demo_p = demobuffer; // Save the right version code for this demo if (longtics) { *demo_p++ = DOOM_191_VERSION; } else { *demo_p++ = G_VanillaVersionCode(); } *demo_p++ = gameskill; *demo_p++ = gameepisode; *demo_p++ = gamemap; *demo_p++ = deathmatch; *demo_p++ = respawnparm; *demo_p++ = fastparm; *demo_p++ = nomonsters; *demo_p++ = consoleplayer; for (i=0 ; i= 0 && version <= 4) { return "v1.0/v1.1/v1.2"; } else { M_snprintf(resultbuf, sizeof(resultbuf), "%i.%i (unknown)", version / 100, version % 100); return resultbuf; } } void G_DoPlayDemo (void) { skill_t skill; int i, episode, map; int demoversion; gameaction = ga_nothing; demobuffer = demo_p = W_CacheLumpName (defdemoname, PU_STATIC); demoversion = *demo_p++; if (demoversion == G_VanillaVersionCode()) { longtics = false; } else if (demoversion == DOOM_191_VERSION) { // demo recorded with cph's modified "v1.91" doom exe longtics = true; } else { char *message = "Demo is from a different game version!\n" "(read %i, should be %i)\n" "\n" "*** You may need to upgrade your version " "of Doom to v1.9. ***\n" " See: https://www.doomworld.com/classicdoom" "/info/patches.php\n" " This appears to be %s."; //I_Error(message, demoversion, G_VanillaVersionCode(), printf(message, demoversion, G_VanillaVersionCode(), DemoVersionDescription(demoversion)); } skill = *demo_p++; episode = *demo_p++; map = *demo_p++; deathmatch = *demo_p++; respawnparm = *demo_p++; fastparm = *demo_p++; nomonsters = *demo_p++; consoleplayer = *demo_p++; for (i=0 ; i 0 || M_CheckParm("-netdemo") > 0) { netgame = true; netdemo = true; } // don't spend a lot of time in loadlevel precache = false; G_InitNew (skill, episode, map); precache = true; starttime = I_GetTime (); usergame = false; demoplayback = true; } // // G_TimeDemo // void G_TimeDemo (char* name) { //! // @vanilla // // Disable rendering the screen entirely. // nodrawers = M_CheckParm ("-nodraw"); timingdemo = true; singletics = true; defdemoname = name; gameaction = ga_playdemo; } /* =================== = = G_CheckDemoStatus = = Called after a death or level completion to allow demos to be cleaned up = Returns true if a new demo loop action will take place =================== */ boolean G_CheckDemoStatus (void) { int endtime; if (timingdemo) { float fps; int realtics; endtime = I_GetTime (); realtics = endtime - starttime; fps = ((float) gametic * TICRATE) / realtics; // Prevent recursive calls timingdemo = false; demoplayback = false; I_Error ("timed %i gametics in %i realtics (%f fps)", gametic, realtics, fps); } if (demoplayback) { W_ReleaseLumpName(defdemoname); demoplayback = false; netdemo = false; netgame = false; deathmatch = false; playeringame[1] = playeringame[2] = playeringame[3] = 0; respawnparm = false; fastparm = false; nomonsters = false; consoleplayer = 0; if (singledemo) I_Quit (); else D_AdvanceDemo (); return true; } if (demorecording) { *demo_p++ = DEMOMARKER; M_WriteFile (demoname, demobuffer, demo_p - demobuffer); Z_Free (demobuffer); demorecording = false; I_Error ("Demo %s recorded",demoname); } return false; } ================================================ FILE: fbdoom/g_game.h ================================================ // // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // 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. // // DESCRIPTION: // Duh. // #ifndef __G_GAME__ #define __G_GAME__ #include "doomdef.h" #include "d_event.h" #include "d_ticcmd.h" // // GAME // void G_DeathMatchSpawnPlayer (int playernum); void G_InitNew (skill_t skill, int episode, int map); // Can be called by the startup code or M_Responder. // A normal game starts at map 1, // but a warp test can start elsewhere void G_DeferedInitNew (skill_t skill, int episode, int map); void G_DeferedPlayDemo (char* demo); // Can be called by the startup code or M_Responder, // calls P_SetupLevel or W_EnterWorld. void G_LoadGame (char* name); void G_DoLoadGame (void); // Called by M_Responder. void G_SaveGame (int slot, char* description); // Only called by startup code. void G_RecordDemo (char* name); void G_BeginRecording (void); void G_PlayDemo (char* name); void G_TimeDemo (char* name); boolean G_CheckDemoStatus (void); void G_ExitLevel (void); void G_SecretExitLevel (void); void G_WorldDone (void); // Read current data from inputs and build a player movement command. void G_BuildTiccmd (ticcmd_t *cmd, int maketic); void G_Ticker (void); boolean G_Responder (event_t* ev); void G_ScreenShot (void); void G_DrawMouseSpeedBox(void); int G_VanillaVersionCode(void); extern int vanilla_savegame_limit; extern int vanilla_demo_limit; #endif ================================================ FILE: fbdoom/gusconf.c ================================================ // // Copyright(C) 2005-2014 Simon Howard // // 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. // // DESCRIPTION: // GUS emulation code. // // Actually emulating a GUS is far too much work; fortunately // GUS "emulation" already exists in the form of Timidity, which // supports GUS patch files. This code therefore converts Doom's // DMXGUS lump into an equivalent Timidity configuration file. // #include #include #include #include #include "w_wad.h" #include "z_zone.h" #define MAX_INSTRUMENTS 256 typedef struct { char *patch_names[MAX_INSTRUMENTS]; int mapping[MAX_INSTRUMENTS]; } gus_config_t; char *gus_patch_path = ""; unsigned int gus_ram_kb = 1024; static unsigned int MappingIndex(void) { unsigned int result = gus_ram_kb / 256; if (result < 1) { return 1; } else if (result > 4) { return 4; } else { return result; } } static int SplitLine(char *line, char **fields, unsigned int max_fields) { unsigned int num_fields; char *p; fields[0] = line; num_fields = 1; for (p = line; *p != '\0'; ++p) { if (*p == ',') { *p = '\0'; // Skip spaces following the comma. do { ++p; } while (*p != '\0' && isspace(*p)); fields[num_fields] = p; ++num_fields; --p; if (num_fields >= max_fields) { break; } } else if (*p == '#') { *p = '\0'; break; } } // Strip off trailing whitespace from the end of the line. p = fields[num_fields - 1] + strlen(fields[num_fields - 1]); while (p > fields[num_fields - 1] && isspace(*(p - 1))) { --p; *p = '\0'; } return num_fields; } static void ParseLine(gus_config_t *config, char *line) { char *fields[6]; unsigned int num_fields; unsigned int instr_id, mapped_id; num_fields = SplitLine(line, fields, 6); if (num_fields < 6) { return; } instr_id = atoi(fields[0]); mapped_id = atoi(fields[MappingIndex()]); free(config->patch_names[instr_id]); config->patch_names[instr_id] = strdup(fields[5]); config->mapping[instr_id] = mapped_id; } static void ParseDMXConfig(char *dmxconf, gus_config_t *config) { char *p, *newline; unsigned int i; memset(config, 0, sizeof(gus_config_t)); for (i = 0; i < MAX_INSTRUMENTS; ++i) { config->mapping[i] = -1; } p = dmxconf; for (;;) { newline = strchr(p, '\n'); if (newline != NULL) { *newline = '\0'; } ParseLine(config, p); if (newline == NULL) { break; } else { p = newline + 1; } } } static void FreeDMXConfig(gus_config_t *config) { unsigned int i; for (i = 0; i < MAX_INSTRUMENTS; ++i) { free(config->patch_names[i]); } } static char *ReadDMXConfig(void) { int lumpnum; unsigned int len; char *data; // TODO: This should be chosen based on gamemode == commercial: lumpnum = W_CheckNumForName("DMXGUS"); if (lumpnum < 0) { lumpnum = W_GetNumForName("DMXGUSC"); } len = W_LumpLength(lumpnum); data = Z_Malloc(len + 1, PU_STATIC, NULL); W_ReadLump(lumpnum, data); return data; } static boolean WriteTimidityConfig(char *path, gus_config_t *config) { FILE *fstream; unsigned int i; fstream = fopen(path, "w"); if (fstream == NULL) { return false; } fprintf(fstream, "# Autogenerated Timidity config.\n\n"); fprintf(fstream, "dir %s\n", gus_patch_path); fprintf(fstream, "\nbank 0\n\n"); for (i = 0; i < 128; ++i) { if (config->mapping[i] >= 0 && config->mapping[i] < MAX_INSTRUMENTS && config->patch_names[config->mapping[i]] != NULL) { fprintf(fstream, "%i %s\n", i, config->patch_names[config->mapping[i]]); } } fprintf(fstream, "\ndrumset 0\n\n"); for (i = 128 + 25; i < MAX_INSTRUMENTS; ++i) { if (config->mapping[i] >= 0 && config->mapping[i] < MAX_INSTRUMENTS && config->patch_names[config->mapping[i]] != NULL) { fprintf(fstream, "%i %s\n", i - 128, config->patch_names[config->mapping[i]]); } } fprintf(fstream, "\n"); fclose(fstream); return true; } boolean GUS_WriteConfig(char *path) { boolean result; char *dmxconf; gus_config_t config; if (!strcmp(gus_patch_path, "")) { printf("You haven't configured gus_patch_path.\n"); printf("gus_patch_path needs to point to the location of " "your GUS patch set.\n" "To get a copy of the \"standard\" GUS patches, " "download a copy of dgguspat.zip.\n"); return false; } dmxconf = ReadDMXConfig(); ParseDMXConfig(dmxconf, &config); result = WriteTimidityConfig(path, &config); FreeDMXConfig(&config); Z_Free(dmxconf); return result; } ================================================ FILE: fbdoom/gusconf.h ================================================ // // Copyright(C) 2005-2014 Simon Howard // // 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. // // DESCRIPTION: // GUS emulation code. // #ifndef __GUSCONF_H__ #define __GUSCONF_H__ #include "doomtype.h" extern char *gus_patch_path; extern unsigned int gus_ram_kb; boolean GUS_WriteConfig(char *path); #endif /* #ifndef __GUSCONF_H__ */ ================================================ FILE: fbdoom/hu_lib.c ================================================ // // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // 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. // // DESCRIPTION: heads-up text and input code // #include #include "doomdef.h" #include "doomkeys.h" #include "v_video.h" #include "i_swap.h" #include "hu_lib.h" #include "r_local.h" #include "r_draw.h" // boolean : whether the screen is always erased #define noterased viewwindowx extern boolean automapactive; // in AM_map.c void HUlib_init(void) { } void HUlib_clearTextLine(hu_textline_t* t) { t->len = 0; t->l[0] = 0; t->needsupdate = true; } void HUlib_initTextLine ( hu_textline_t* t, int x, int y, patch_t** f, int sc ) { t->x = x; t->y = y; t->f = f; t->sc = sc; HUlib_clearTextLine(t); } boolean HUlib_addCharToTextLine ( hu_textline_t* t, char ch ) { if (t->len == HU_MAXLINELENGTH) return false; else { t->l[t->len++] = ch; t->l[t->len] = 0; t->needsupdate = 4; return true; } } boolean HUlib_delCharFromTextLine(hu_textline_t* t) { if (!t->len) return false; else { t->l[--t->len] = 0; t->needsupdate = 4; return true; } } void HUlib_drawTextLine ( hu_textline_t* l, boolean drawcursor ) { int i; int w; int x; unsigned char c; // draw the new stuff x = l->x; for (i=0;ilen;i++) { c = toupper((int)l->l[i]); if (c != ' ' && c >= l->sc && c <= '_') { w = SHORT(l->f[c - l->sc]->width); if (x+w > SCREENWIDTH) break; V_DrawPatchDirect(x, l->y, l->f[c - l->sc]); x += w; } else { x += 4; if (x >= SCREENWIDTH) break; } } // draw the cursor if requested if (drawcursor && x + SHORT(l->f['_' - l->sc]->width) <= SCREENWIDTH) { V_DrawPatchDirect(x, l->y, l->f['_' - l->sc]); } } // sorta called by HU_Erase and just better darn get things straight void HUlib_eraseTextLine(hu_textline_t* l) { int lh; int y; int yoffset; // 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 (!automapactive && viewwindowx && l->needsupdate) { lh = SHORT(l->f[0]->height) + 1; for (y=l->y,yoffset=y*SCREENWIDTH ; yy+lh ; y++,yoffset+=SCREENWIDTH) { if (y < viewwindowy || y >= viewwindowy + viewheight) R_VideoErase(yoffset, SCREENWIDTH); // erase entire line else { R_VideoErase(yoffset, viewwindowx); // erase left border R_VideoErase(yoffset + viewwindowx + viewwidth, viewwindowx); // erase right border } } } if (l->needsupdate) l->needsupdate--; } void HUlib_initSText ( hu_stext_t* s, int x, int y, int h, patch_t** font, int startchar, boolean* on ) { int i; s->h = h; s->on = on; s->laston = true; s->cl = 0; for (i=0;il[i], x, y - i*(SHORT(font[0]->height)+1), font, startchar); } 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; } void HUlib_addMessageToSText ( hu_stext_t* s, char* prefix, 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++)); } 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 } } 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; } void HUlib_initIText ( hu_itext_t* it, int x, int y, patch_t** font, int startchar, 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); } // The following deletion routines adhere to the left margin restriction void HUlib_delCharFromIText(hu_itext_t* it) { if (it->l.len != it->lm) HUlib_delCharFromTextLine(&it->l); } void HUlib_eraseLineFromIText(hu_itext_t* it) { while (it->lm != it->l.len) HUlib_delCharFromTextLine(&it->l); } // Resets left margin as well void HUlib_resetIText(hu_itext_t* it) { it->lm = 0; HUlib_clearTextLine(&it->l); } void HUlib_addPrefixToIText ( hu_itext_t* it, char* str ) { while (*str) HUlib_addCharToTextLine(&it->l, *(str++)); it->lm = it->l.len; } // wrapper function for handling general keyed input. // returns true if it ate the key boolean HUlib_keyInIText ( hu_itext_t* it, unsigned char ch ) { ch = toupper(ch); if (ch >= ' ' && ch <= '_') HUlib_addCharToTextLine(&it->l, (char) ch); else if (ch == KEY_BACKSPACE) HUlib_delCharFromIText(it); else if (ch != KEY_ENTER) return false; // did not eat key return true; // ate the key } 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 } void HUlib_eraseIText(hu_itext_t* it) { if (it->laston && !*it->on) it->l.needsupdate = 4; HUlib_eraseTextLine(&it->l); it->laston = *it->on; } ================================================ FILE: fbdoom/hu_lib.h ================================================ // // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // 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. // // DESCRIPTION: none // #ifndef __HULIB__ #define __HULIB__ // We are referring to patches. #include "r_defs.h" // font stuff #define HU_CHARERASE KEY_BACKSPACE #define HU_MAXLINES 4 #define HU_MAXLINELENGTH 80 // // 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; patch_t** f; // font int sc; // start character char l[HU_MAXLINELENGTH+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; // 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 // // initializes heads-up widget library void HUlib_init(void); // // 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, patch_t **f, int sc); // returns success boolean HUlib_addCharToTextLine(hu_textline_t *t, char ch); // returns success boolean HUlib_delCharFromTextLine(hu_textline_t *t); // 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 // // ? void HUlib_initSText ( hu_stext_t* s, int x, int y, int h, patch_t** font, int startchar, boolean* on ); // add a new line void HUlib_addLineToSText(hu_stext_t* s); // ? void HUlib_addMessageToSText ( hu_stext_t* s, char* prefix, char* msg ); // draws stext void HUlib_drawSText(hu_stext_t* s); // erases all stext lines void HUlib_eraseSText(hu_stext_t* s); // Input Text Line widget routines void HUlib_initIText ( hu_itext_t* it, int x, int y, patch_t** font, int startchar, boolean* on ); // enforces left margin void HUlib_delCharFromIText(hu_itext_t* it); // enforces left margin void HUlib_eraseLineFromIText(hu_itext_t* it); // 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 ================================================ FILE: fbdoom/hu_stuff.c ================================================ // // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // 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. // // DESCRIPTION: Heads-up displays // #include #include "doomdef.h" #include "doomkeys.h" #include "z_zone.h" #include "deh_main.h" #include "i_swap.h" #include "i_video.h" #include "hu_stuff.h" #include "hu_lib.h" #include "m_controls.h" #include "m_misc.h" #include "w_wad.h" #include "s_sound.h" #include "doomstat.h" // Data. #include "dstrings.h" #include "sounds.h" // // Locally used constants, shortcuts. // #define HU_TITLE (mapnames[(gameepisode-1)*9+gamemap-1]) #define HU_TITLE2 (mapnames_commercial[gamemap-1]) #define HU_TITLEP (mapnames_commercial[gamemap-1 + 32]) #define HU_TITLET (mapnames_commercial[gamemap-1 + 64]) #define HU_TITLE_CHEX (mapnames[gamemap - 1]) #define HU_TITLEHEIGHT 1 #define HU_TITLEX 0 #define HU_TITLEY (167 - SHORT(hu_font[0]->height)) #define HU_INPUTTOGGLE 't' #define HU_INPUTX HU_MSGX #define HU_INPUTY (HU_MSGY + HU_MSGHEIGHT*(SHORT(hu_font[0]->height) +1)) #define HU_INPUTWIDTH 64 #define HU_INPUTHEIGHT 1 char *chat_macros[10] = { HUSTR_CHATMACRO0, HUSTR_CHATMACRO1, HUSTR_CHATMACRO2, HUSTR_CHATMACRO3, HUSTR_CHATMACRO4, HUSTR_CHATMACRO5, HUSTR_CHATMACRO6, HUSTR_CHATMACRO7, HUSTR_CHATMACRO8, HUSTR_CHATMACRO9 }; char* player_names[] = { HUSTR_PLRGREEN, HUSTR_PLRINDIGO, HUSTR_PLRBROWN, HUSTR_PLRRED }; char chat_char; // remove later. static player_t* plr; patch_t* hu_font[HU_FONTSIZE]; static hu_textline_t w_title; boolean chat_on; static hu_itext_t w_chat; static boolean always_off = false; static char chat_dest[MAXPLAYERS]; static hu_itext_t w_inputbuffer[MAXPLAYERS]; static boolean message_on; boolean message_dontfuckwithme; static boolean message_nottobefuckedwith; static hu_stext_t w_message; static int message_counter; extern int showMessages; static boolean headsupactive = false; // // Builtin map names. // The actual names can be found in DStrings.h. // char* mapnames[] = // DOOM shareware/registered/retail (Ultimate) names. { HUSTR_E1M1, HUSTR_E1M2, HUSTR_E1M3, HUSTR_E1M4, HUSTR_E1M5, HUSTR_E1M6, HUSTR_E1M7, HUSTR_E1M8, HUSTR_E1M9, HUSTR_E2M1, HUSTR_E2M2, HUSTR_E2M3, HUSTR_E2M4, HUSTR_E2M5, HUSTR_E2M6, HUSTR_E2M7, HUSTR_E2M8, HUSTR_E2M9, HUSTR_E3M1, HUSTR_E3M2, HUSTR_E3M3, HUSTR_E3M4, HUSTR_E3M5, HUSTR_E3M6, HUSTR_E3M7, HUSTR_E3M8, HUSTR_E3M9, HUSTR_E4M1, HUSTR_E4M2, HUSTR_E4M3, HUSTR_E4M4, HUSTR_E4M5, HUSTR_E4M6, HUSTR_E4M7, HUSTR_E4M8, HUSTR_E4M9, "NEWLEVEL", "NEWLEVEL", "NEWLEVEL", "NEWLEVEL", "NEWLEVEL", "NEWLEVEL", "NEWLEVEL", "NEWLEVEL", "NEWLEVEL" }; // List of names for levels in commercial IWADs // (doom2.wad, plutonia.wad, tnt.wad). These are stored in a // single large array; WADs like pl2.wad have a MAP33, and rely on // the layout in the Vanilla executable, where it is possible to // overflow the end of one array into the next. char *mapnames_commercial[] = { // DOOM 2 map names. HUSTR_1, HUSTR_2, HUSTR_3, HUSTR_4, HUSTR_5, HUSTR_6, HUSTR_7, HUSTR_8, HUSTR_9, HUSTR_10, HUSTR_11, HUSTR_12, HUSTR_13, HUSTR_14, HUSTR_15, HUSTR_16, HUSTR_17, HUSTR_18, HUSTR_19, HUSTR_20, HUSTR_21, HUSTR_22, HUSTR_23, HUSTR_24, HUSTR_25, HUSTR_26, HUSTR_27, HUSTR_28, HUSTR_29, HUSTR_30, HUSTR_31, HUSTR_32, // Plutonia WAD map names. PHUSTR_1, PHUSTR_2, PHUSTR_3, PHUSTR_4, PHUSTR_5, PHUSTR_6, PHUSTR_7, PHUSTR_8, PHUSTR_9, PHUSTR_10, PHUSTR_11, PHUSTR_12, PHUSTR_13, PHUSTR_14, PHUSTR_15, PHUSTR_16, PHUSTR_17, PHUSTR_18, PHUSTR_19, PHUSTR_20, PHUSTR_21, PHUSTR_22, PHUSTR_23, PHUSTR_24, PHUSTR_25, PHUSTR_26, PHUSTR_27, PHUSTR_28, PHUSTR_29, PHUSTR_30, PHUSTR_31, PHUSTR_32, // TNT WAD map names. THUSTR_1, THUSTR_2, THUSTR_3, THUSTR_4, THUSTR_5, THUSTR_6, THUSTR_7, THUSTR_8, THUSTR_9, THUSTR_10, THUSTR_11, THUSTR_12, THUSTR_13, THUSTR_14, THUSTR_15, THUSTR_16, THUSTR_17, THUSTR_18, THUSTR_19, THUSTR_20, THUSTR_21, THUSTR_22, THUSTR_23, THUSTR_24, THUSTR_25, THUSTR_26, THUSTR_27, THUSTR_28, THUSTR_29, THUSTR_30, THUSTR_31, THUSTR_32 }; void HU_Init(void) { int i; int j; char buffer[9]; // load the heads-up font j = HU_FONTSTART; for (i=0;imessage && !message_nottobefuckedwith) || (plr->message && message_dontfuckwithme)) { HUlib_addMessageToSText(&w_message, 0, plr->message); plr->message = 0; message_on = true; message_counter = HU_MSGTIMEOUT; message_nottobefuckedwith = message_dontfuckwithme; message_dontfuckwithme = 0; } } // else message_on = false; // check for incoming chat characters if (netgame) { for (i=0 ; imessage = DEH_String(HUSTR_MSGU); } else { chatchars[head] = c; head = (head + 1) & (QUEUESIZE-1); } } char HU_dequeueChatChar(void) { char c; if (head != tail) { c = chatchars[tail]; tail = (tail + 1) & (QUEUESIZE-1); } else { c = 0; } return c; } boolean HU_Responder(event_t *ev) { static char lastmessage[HU_MAXLINELENGTH+1]; char* macromessage; boolean eatkey = false; static boolean altdown = false; unsigned char c; int i; int numplayers; static int num_nobrainers = 0; numplayers = 0; for (i=0 ; idata1 == KEY_RSHIFT) { return false; } else if (ev->data1 == KEY_RALT || ev->data1 == KEY_LALT) { altdown = ev->type == ev_keydown; return false; } if (ev->type != ev_keydown) return false; if (!chat_on) { if (ev->data1 == key_message_refresh) { message_on = true; message_counter = HU_MSGTIMEOUT; eatkey = true; } else if (netgame && ev->data2 == key_multi_msg) { eatkey = chat_on = true; HUlib_resetIText(&w_chat); HU_queueChatChar(HU_BROADCAST); } else if (netgame && numplayers > 2) { for (i=0; idata2 == key_multi_msgplayer[i]) { if (playeringame[i] && i!=consoleplayer) { eatkey = chat_on = true; HUlib_resetIText(&w_chat); HU_queueChatChar(i+1); break; } else if (i == consoleplayer) { num_nobrainers++; if (num_nobrainers < 3) plr->message = DEH_String(HUSTR_TALKTOSELF1); else if (num_nobrainers < 6) plr->message = DEH_String(HUSTR_TALKTOSELF2); else if (num_nobrainers < 9) plr->message = DEH_String(HUSTR_TALKTOSELF3); else if (num_nobrainers < 32) plr->message = DEH_String(HUSTR_TALKTOSELF4); else plr->message = DEH_String(HUSTR_TALKTOSELF5); } } } } } else { // send a macro if (altdown) { c = ev->data1 - '0'; if (c > 9) return false; // fprintf(stderr, "got here\n"); macromessage = chat_macros[c]; // kill last message with a '\n' HU_queueChatChar(KEY_ENTER); // DEBUG!!! // send the macro message while (*macromessage) HU_queueChatChar(*macromessage++); HU_queueChatChar(KEY_ENTER); // leave chat mode and notify that it was sent chat_on = false; M_StringCopy(lastmessage, chat_macros[c], sizeof(lastmessage)); plr->message = lastmessage; eatkey = true; } else { c = ev->data2; eatkey = HUlib_keyInIText(&w_chat, c); if (eatkey) { // static unsigned char buf[20]; // DEBUG HU_queueChatChar(c); // M_snprintf(buf, sizeof(buf), "KEY: %d => %d", ev->data1, c); // plr->message = buf; } if (c == KEY_ENTER) { chat_on = false; if (w_chat.l.len) { M_StringCopy(lastmessage, w_chat.l.l, sizeof(lastmessage)); plr->message = lastmessage; } } else if (c == KEY_ESCAPE) chat_on = false; } } return eatkey; } ================================================ FILE: fbdoom/hu_stuff.h ================================================ // // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // 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. // // 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 '_' // the last font characters // Calculate # of glyphs in font. #define HU_FONTSIZE (HU_FONTEND - HU_FONTSTART + 1) #define HU_BROADCAST 5 #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); extern char *chat_macros[10]; #endif ================================================ FILE: fbdoom/i_cdmus.c ================================================ // // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 1993-2008 Raven Software // // 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. // // // SDL implementation of the Hexen CD interface. // #include #ifdef ORIGCODE #include "SDL.h" #include "SDL_cdrom.h" #endif #include "doomtype.h" #include "i_cdmus.h" #ifdef ORIGCODE static SDL_CD *cd_handle = NULL; static char *startup_error = NULL; static const char *cd_name = NULL; #endif int cd_Error; int I_CDMusInit(void) { #ifdef ORIGCODE int drive_num = 0; // The initialize function is re-invoked when the CD track play cheat // is used, so use the opportunity to call SDL_CDStatus() to update // the status of the drive. if (cd_handle == NULL) { if (SDL_Init(SDL_INIT_CDROM) < 0) { startup_error = "Failed to init CD subsystem."; cd_Error = 1; return -1; } // TODO: config variable to control CDROM to use. cd_handle = SDL_CDOpen(drive_num); if (cd_handle == NULL) { startup_error = "Failed to open CD-ROM drive."; cd_Error = 1; return -1; } cd_name = SDL_CDName(drive_num); } if (SDL_CDStatus(cd_handle) == CD_ERROR) { startup_error = "Failed to read CD status."; cd_Error = 1; return -1; } if (!CD_INDRIVE(cd_handle->status)) { startup_error = "No CD in drive."; cd_Error = 1; return -1; } cd_Error = 0; #endif return 0; } // We cannot print status messages inline during startup, they must // be deferred until after I_CDMusInit has returned. void I_CDMusPrintStartup(void) { #ifdef ORIGCODE if (cd_name != NULL) { printf("I_CDMusInit: Using CD-ROM drive: %s\n", cd_name); } if (startup_error != NULL) { fprintf(stderr, "I_CDMusInit: %s\n", startup_error); } #endif } int I_CDMusPlay(int track) { #ifdef ORIGCODE int result; if (cd_handle == NULL) { cd_Error = 1; return -1; } // Play one track // Track is indexed from 1. result = SDL_CDPlayTracks(cd_handle, track - 1, 0, 1, 0); cd_Error = 0; return result; #else return 0; #endif } int I_CDMusStop(void) { #ifdef ORIGCODE int result; result = SDL_CDStop(cd_handle); cd_Error = 0; return result; #else return 0; #endif } int I_CDMusResume(void) { #ifdef ORIGCODE int result; result = SDL_CDResume(cd_handle); cd_Error = 0; return result; #else return 0; #endif } int I_CDMusSetVolume(int volume) { /* Not supported yet */ cd_Error = 0; return 0; } int I_CDMusFirstTrack(void) { #ifdef ORIGCODE int i; if (cd_handle == NULL) { cd_Error = 1; return -1; } // Find the first audio track. for (i=0; inumtracks; ++i) { if (cd_handle->track[i].type == SDL_AUDIO_TRACK) { cd_Error = 0; // Tracks are indexed from 1. return i + 1; } } // Don't know? cd_Error = 1; return -1; #else return 0; #endif } int I_CDMusLastTrack(void) { #ifdef ORIGCODE if (cd_handle == NULL) { cd_Error = 1; return -1; } cd_Error = 0; return cd_handle->numtracks; #else return 0; #endif } int I_CDMusTrackLength(int track_num) { #ifdef ORIGCODE SDL_CDtrack *track; if (cd_handle == NULL || track_num < 1 || track_num > cd_handle->numtracks) { cd_Error = 1; return -1; } // Track number is indexed from 1. track = &cd_handle->track[track_num - 1]; // Round up to the next second cd_Error = 0; return (track->length + CD_FPS - 1) / CD_FPS; #else return 0; #endif } ================================================ FILE: fbdoom/i_cdmus.h ================================================ // // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 1993-2008 Raven Software // // 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. // // i_cdmus.h #ifndef __ICDMUS__ #define __ICDMUS__ #define CDERR_NOTINSTALLED 10 // MSCDEX not installed #define CDERR_NOAUDIOSUPPORT 11 // CD-ROM Doesn't support audio #define CDERR_NOAUDIOTRACKS 12 // Current CD has no audio tracks #define CDERR_BADDRIVE 20 // Bad drive number #define CDERR_BADTRACK 21 // Bad track number #define CDERR_IOCTLBUFFMEM 22 // Not enough low memory for IOCTL #define CDERR_DEVREQBASE 100 // DevReq errors extern int cd_Error; int I_CDMusInit(void); void I_CDMusPrintStartup(void); int I_CDMusPlay(int track); int I_CDMusStop(void); int I_CDMusResume(void); int I_CDMusSetVolume(int volume); int I_CDMusFirstTrack(void); int I_CDMusLastTrack(void); int I_CDMusTrackLength(int track); #endif ================================================ FILE: fbdoom/i_endoom.c ================================================ // // Copyright(C) 2005-2014 Simon Howard // // 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. // // DESCRIPTION: // Exit text-mode ENDOOM screen. // #include #include #include "config.h" #include "doomtype.h" #include "i_video.h" #ifdef ORIGCODE #include "txt_main.h" #endif #define ENDOOM_W 80 #define ENDOOM_H 25 // // Displays the text mode ending screen after the game quits // void I_Endoom(byte *endoom_data) { #ifdef ORIGCODE unsigned char *screendata; int y; int indent; // Set up text mode screen TXT_Init(); I_InitWindowTitle(); I_InitWindowIcon(); // Write the data to the screen memory screendata = TXT_GetScreenData(); indent = (ENDOOM_W - TXT_SCREEN_W) / 2; for (y=0; y 0) { break; } TXT_Sleep(0); } // Shut down text mode screen TXT_Shutdown(); #endif } ================================================ FILE: fbdoom/i_endoom.h ================================================ // // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // 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. // // DESCRIPTION: // Exit text-mode ENDOOM screen. // #ifndef __I_ENDOOM__ #define __I_ENDOOM__ // Display the Endoom screen on shutdown. Pass a pointer to the // ENDOOM lump. void I_Endoom(byte *data); #endif ================================================ FILE: fbdoom/i_input_sdl.c ================================================ // // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // 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. // // DESCRIPTION: // DOOM graphics stuff for SDL. // #include "SDL/SDL.h" #include #include #include #include #include "config.h" #include "deh_str.h" #include "doomtype.h" #include "doomkeys.h" #include "i_joystick.h" #include "i_system.h" #include "i_swap.h" #include "i_timer.h" #include "i_video.h" #include "i_scale.h" #include "m_argv.h" #include "m_config.h" #include "m_misc.h" #include "tables.h" #include "v_video.h" #include "w_wad.h" #include "z_zone.h" // SDL surface for the screen. static SDL_Surface *screen; // If true, keyboard mapping is ignored, like in Vanilla Doom. // The sensible thing to do is to disable this if you have a non-US // keyboard. int vanilla_keyboard_mapping = true; // Is the shift key currently down? static int shiftdown = 0; // Lookup table for mapping ASCII characters to their equivalent when // shift is pressed on an American layout keyboard: static const char 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 }; // // Translates the SDL key // static int TranslateKey(SDL_keysym *sym) { switch(sym->sym) { case SDLK_LEFT: return KEY_LEFTARROW; case SDLK_RIGHT: return KEY_RIGHTARROW; case SDLK_DOWN: return KEY_DOWNARROW; case SDLK_UP: return KEY_UPARROW; case SDLK_ESCAPE: return KEY_ESCAPE; case SDLK_RETURN: return KEY_ENTER; case SDLK_TAB: return KEY_TAB; case SDLK_F1: return KEY_F1; case SDLK_F2: return KEY_F2; case SDLK_F3: return KEY_F3; case SDLK_F4: return KEY_F4; case SDLK_F5: return KEY_F5; case SDLK_F6: return KEY_F6; case SDLK_F7: return KEY_F7; case SDLK_F8: return KEY_F8; case SDLK_F9: return KEY_F9; case SDLK_F10: return KEY_F10; case SDLK_F11: return KEY_F11; case SDLK_F12: return KEY_F12; case SDLK_PRINT: return KEY_PRTSCR; case SDLK_BACKSPACE: return KEY_BACKSPACE; case SDLK_DELETE: return KEY_DEL; case SDLK_PAUSE: return KEY_PAUSE; #if !SDL_VERSION_ATLEAST(1, 3, 0) case SDLK_EQUALS: return KEY_EQUALS; #endif case SDLK_MINUS: return KEY_MINUS; case SDLK_LSHIFT: case SDLK_RSHIFT: return KEY_RSHIFT; case SDLK_LCTRL: case SDLK_RCTRL: return KEY_RCTRL; case SDLK_LALT: case SDLK_RALT: #if !SDL_VERSION_ATLEAST(1, 3, 0) case SDLK_LMETA: case SDLK_RMETA: #endif return KEY_RALT; case SDLK_CAPSLOCK: return KEY_CAPSLOCK; case SDLK_SCROLLOCK: return KEY_SCRLCK; case SDLK_NUMLOCK: return KEY_NUMLOCK; case SDLK_KP0: return KEYP_0; case SDLK_KP1: return KEYP_1; case SDLK_KP2: return KEYP_2; case SDLK_KP3: return KEYP_3; case SDLK_KP4: return KEYP_4; case SDLK_KP5: return KEYP_5; case SDLK_KP6: return KEYP_6; case SDLK_KP7: return KEYP_7; case SDLK_KP8: return KEYP_8; case SDLK_KP9: return KEYP_9; case SDLK_KP_PERIOD: return KEYP_PERIOD; case SDLK_KP_MULTIPLY: return KEYP_MULTIPLY; case SDLK_KP_PLUS: return KEYP_PLUS; case SDLK_KP_MINUS: return KEYP_MINUS; case SDLK_KP_DIVIDE: return KEYP_DIVIDE; case SDLK_KP_EQUALS: return KEYP_EQUALS; case SDLK_KP_ENTER: return KEYP_ENTER; case SDLK_HOME: return KEY_HOME; case SDLK_INSERT: return KEY_INS; case SDLK_END: return KEY_END; case SDLK_PAGEUP: return KEY_PGUP; case SDLK_PAGEDOWN: return KEY_PGDN; #ifdef SDL_HAVE_APP_KEYS case SDLK_APP1: return KEY_F1; case SDLK_APP2: return KEY_F2; case SDLK_APP3: return KEY_F3; case SDLK_APP4: return KEY_F4; case SDLK_APP5: return KEY_F5; case SDLK_APP6: return KEY_F6; #endif default: return tolower(sym->sym); } } // Get the equivalent ASCII (Unicode?) character for a keypress. static int GetTypedChar(SDL_Event *event) { int key; // If Vanilla keyboard mapping enabled, the keyboard // scan code is used to give the character typed. // This does not change depending on keyboard layout. // If you have a German keyboard, pressing 'z' will // give 'y', for example. It is desirable to be able // to fix this so that people with non-standard // keyboard mappings can type properly. If vanilla // mode is disabled, use the properly translated // version. if (vanilla_keyboard_mapping) { key = TranslateKey(&event->key.keysym); // Is shift held down? If so, perform a translation. if (shiftdown > 0) { if (key >= 0 && key < arrlen(shiftxform)) { key = shiftxform[key]; } else { key = 0; } } return key; } else { // Unicode value, from key layout. return tolower(event->key.keysym.unicode); } } static void UpdateShiftStatus(SDL_Event *event) { int change; if (event->type == SDL_KEYDOWN) { change = 1; } else if (event->type == SDL_KEYUP) { change = -1; } else { return; } if (event->key.keysym.sym == SDLK_LSHIFT || event->key.keysym.sym == SDLK_RSHIFT) { shiftdown += change; } } void I_GetEvent(void) { SDL_Event sdlevent; event_t event; // possibly not needed SDL_PumpEvents(); // put event-grabbing stuff in here while (SDL_PollEvent(&sdlevent)) { if (sdlevent.type == SDL_QUIT) { I_Quit(); } UpdateShiftStatus(&sdlevent); // process event switch (sdlevent.type) { case SDL_KEYDOWN: // data1 has the key pressed, data2 has the character // (shift-translated, etc) event.type = ev_keydown; event.data1 = TranslateKey(&sdlevent.key.keysym); event.data2 = GetTypedChar(&sdlevent); if (event.data1 != 0) { D_PostEvent(&event); } break; case SDL_KEYUP: event.type = ev_keyup; event.data1 = TranslateKey(&sdlevent.key.keysym); // data2 is just initialized to zero for ev_keyup. // For ev_keydown it's the shifted Unicode character // that was typed, but if something wants to detect // key releases it should do so based on data1 // (key ID), not the printable char. event.data2 = 0; if (event.data1 != 0) { D_PostEvent(&event); } break; /* case SDL_MOUSEMOTION: event.type = ev_mouse; event.data1 = mouse_button_state; event.data2 = AccelerateMouse(sdlevent.motion.xrel); event.data3 = -AccelerateMouse(sdlevent.motion.yrel); D_PostEvent(&event); break; */ case SDL_QUIT: event.type = ev_quit; D_PostEvent(&event); break; case SDL_ACTIVEEVENT: // need to update our focus state //UpdateFocus(); break; default: break; } } } //void I_ShutdownGraphics(void) //{ // //SDL_QuitSubSystem(SDL_INIT_VIDEO); //} // // I_StartTic // void I_StartTic (void) { I_GetEvent(); } void I_InitInput(void) { SDL_Event dummy; byte *doompal; char *env; // Pass through the XSCREENSAVER_WINDOW environment variable to // SDL_WINDOWID, to embed the SDL window into the Xscreensaver // window. env = getenv("XSCREENSAVER_WINDOW"); if (env != NULL) { char winenv[30]; int winid; sscanf(env, "0x%x", &winid); M_snprintf(winenv, sizeof(winenv), "SDL_WINDOWID=%i", winid); putenv(winenv); } if (SDL_Init(SDL_INIT_VIDEO) < 0) { I_Error("Failed to initialize video: %s", SDL_GetError()); } //UpdateFocus(); // We need SDL to give us translated versions of keys as well SDL_EnableUNICODE(1); // Repeat key presses - this is what Vanilla Doom does // Not sure about repeat rate - probably dependent on which DOS // driver is used. This is good enough though. SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL); // clear out any events waiting at the start and center the mouse while (SDL_PollEvent(&dummy)); } ================================================ FILE: fbdoom/i_input_tty.c ================================================ // // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // 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. // // DESCRIPTION: // DOOM keyboard input via linux tty // #include #include #include #include #include #include #include #include #include #include #include #include "config.h" #include "deh_str.h" #include "doomtype.h" #include "doomkeys.h" #include "i_joystick.h" #include "i_system.h" #include "i_swap.h" #include "i_timer.h" #include "i_video.h" #include "i_scale.h" #include "m_argv.h" #include "m_config.h" #include "m_misc.h" #include "tables.h" #include "v_video.h" #include "w_wad.h" #include "z_zone.h" int vanilla_keyboard_mapping = 1; // Is the shift key currently down? static int shiftdown = 0; // Lookup table for mapping AT keycodes to their doom keycode static const char at_to_doom[] = { /* 0x00 */ 0x00, /* 0x01 */ KEY_ESCAPE, /* 0x02 */ '1', /* 0x03 */ '2', /* 0x04 */ '3', /* 0x05 */ '4', /* 0x06 */ '5', /* 0x07 */ '6', /* 0x08 */ '7', /* 0x09 */ '8', /* 0x0a */ '9', /* 0x0b */ '0', /* 0x0c */ '-', /* 0x0d */ '=', /* 0x0e */ KEY_BACKSPACE, /* 0x0f */ KEY_TAB, /* 0x10 */ 'q', /* 0x11 */ 'w', /* 0x12 */ 'e', /* 0x13 */ 'r', /* 0x14 */ 't', /* 0x15 */ 'y', /* 0x16 */ 'u', /* 0x17 */ 'i', /* 0x18 */ 'o', /* 0x19 */ 'p', /* 0x1a */ '[', /* 0x1b */ ']', /* 0x1c */ KEY_ENTER, /* 0x1d */ KEY_FIRE, /* KEY_RCTRL, */ /* 0x1e */ 'a', /* 0x1f */ 's', /* 0x20 */ 'd', /* 0x21 */ 'f', /* 0x22 */ 'g', /* 0x23 */ 'h', /* 0x24 */ 'j', /* 0x25 */ 'k', /* 0x26 */ 'l', /* 0x27 */ ';', /* 0x28 */ '\'', /* 0x29 */ '`', /* 0x2a */ KEY_RSHIFT, /* 0x2b */ '\\', /* 0x2c */ 'z', /* 0x2d */ 'x', /* 0x2e */ 'c', /* 0x2f */ 'v', /* 0x30 */ 'b', /* 0x31 */ 'n', /* 0x32 */ 'm', /* 0x33 */ ',', /* 0x34 */ '.', /* 0x35 */ '/', /* 0x36 */ KEY_RSHIFT, /* 0x37 */ KEYP_MULTIPLY, /* 0x38 */ KEY_LALT, /* 0x39 */ KEY_USE, /* 0x3a */ KEY_CAPSLOCK, /* 0x3b */ KEY_F1, /* 0x3c */ KEY_F2, /* 0x3d */ KEY_F3, /* 0x3e */ KEY_F4, /* 0x3f */ KEY_F5, /* 0x40 */ KEY_F6, /* 0x41 */ KEY_F7, /* 0x42 */ KEY_F8, /* 0x43 */ KEY_F9, /* 0x44 */ KEY_F10, /* 0x45 */ KEY_NUMLOCK, /* 0x46 */ 0x0, /* 0x47 */ 0x0, /* 47 (Keypad-7/Home) */ /* 0x48 */ 0x0, /* 48 (Keypad-8/Up) */ /* 0x49 */ 0x0, /* 49 (Keypad-9/PgUp) */ /* 0x4a */ 0x0, /* 4a (Keypad--) */ /* 0x4b */ 0x0, /* 4b (Keypad-4/Left) */ /* 0x4c */ 0x0, /* 4c (Keypad-5) */ /* 0x4d */ 0x0, /* 4d (Keypad-6/Right) */ /* 0x4e */ 0x0, /* 4e (Keypad-+) */ /* 0x4f */ 0x0, /* 4f (Keypad-1/End) */ /* 0x50 */ 0x0, /* 50 (Keypad-2/Down) */ /* 0x51 */ 0x0, /* 51 (Keypad-3/PgDn) */ /* 0x52 */ 0x0, /* 52 (Keypad-0/Ins) */ /* 0x53 */ 0x0, /* 53 (Keypad-./Del) */ /* 0x54 */ 0x0, /* 54 (Alt-SysRq) on a 84+ key keyboard */ /* 0x55 */ 0x0, /* 0x56 */ 0x0, /* 0x57 */ 0x0, /* 0x58 */ 0x0, /* 0x59 */ 0x0, /* 0x5a */ 0x0, /* 0x5b */ 0x0, /* 0x5c */ 0x0, /* 0x5d */ 0x0, /* 0x5e */ 0x0, /* 0x5f */ 0x0, /* 0x60 */ 0x0, /* 0x61 */ 0x0, /* 0x62 */ 0x0, /* 0x63 */ 0x0, /* 0x64 */ 0x0, /* 0x65 */ 0x0, /* 0x66 */ 0x0, /* 0x67 */ KEY_UPARROW, /* 0x68 */ 0x0, /* 0x69 */ KEY_LEFTARROW, /* 0x6a */ KEY_RIGHTARROW, /* 0x6b */ 0x0, /* 0x6c */ KEY_DOWNARROW, /* 0x6d */ 0x0, /* 0x6e */ 0x0, /* 0x6f */ 0x0, /* 0x70 */ 0x0, /* 0x71 */ 0x0, /* 0x72 */ 0x0, /* 0x73 */ 0x0, /* 0x74 */ 0x0, /* 0x75 */ 0x0, /* 0x76 */ 0x0, /* 0x77 */ 0x0, /* 0x78 */ 0x0, /* 0x79 */ 0x0, /* 0x7a */ 0x0, /* 0x7b */ 0x0, /* 0x7c */ 0x0, /* 0x7d */ 0x0, /* 0x7e */ 0x0, /* 0x7f */ KEY_FIRE, //KEY_RCTRL, }; // Lookup table for mapping ASCII characters to their equivalent when // shift is pressed on an American layout keyboard: static const char 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 }; /* Checks whether or not the given file descriptor is associated with a local keyboard. Returns 1 if it is, 0 if not (or if something prevented us from checking). */ int tty_is_kbd(int fd) { int data = 0; if (ioctl(fd, KDGKBTYPE, &data) != 0) return 0; if (data == KB_84) { printf("84-key keyboard found.\n"); return 1; } else if (data == KB_101) { printf("101-key keyboard found.\n"); return 1; } else { printf("KDGKBTYPE = 0x%x.\n", data); return 0; } } static int old_mode = -1; static struct termios old_term; static int kb = -1; /* keyboard file descriptor */ void kbd_shutdown(void) { /* Shut down nicely. */ printf("Cleaning up.\n"); fflush(stdout); printf("Exiting normally.\n"); if (old_mode != -1) { ioctl(kb, KDSKBMODE, old_mode); tcsetattr(kb, 0, &old_term); } if (kb > 3) close(kb); exit(0); } static int kbd_init(void) { struct termios new_term; char *files_to_try[] = {"/dev/tty", "/dev/tty0", "/dev/console", NULL}; int i; int flags; int found = 0; /* First we need to find a file descriptor that represents the system's keyboard. This should be /dev/tty, /dev/console, stdin, stdout, or stderr. We'll try them in that order. If none are acceptable, we're probably not being run from a VT. */ for (i = 0; files_to_try[i] != NULL; i++) { /* Try to open the file. */ kb = open(files_to_try[i], O_RDONLY); if (kb < 0) continue; /* See if this is valid for our purposes. */ if (tty_is_kbd(kb)) { printf("Using keyboard on %s.\n", files_to_try[i]); found = 1; break; } close(kb); } /* If those didn't work, not all is lost. We can try the 3 standard file descriptors, in hopes that one of them might point to a console. This is not especially likely. */ if (files_to_try[i] == NULL) { for (kb = 0; kb < 3; kb++) { if (tty_is_kbd(i)) { found = 1; break; } } } if (!found) { printf("Unable to find a file descriptor associated with "\ "the keyboard.\n" \ "Perhaps you're not using a virtual terminal?\n"); return 1; } /* Find the keyboard's mode so we can restore it later. */ if (ioctl(kb, KDGKBMODE, &old_mode) != 0) { printf("Unable to query keyboard mode.\n"); kbd_shutdown(); } /* Adjust the terminal's settings. In particular, disable echoing, signal generation, and line buffering. Any of these could cause trouble. Save the old settings first. */ if (tcgetattr(kb, &old_term) != 0) { printf("Unable to query terminal settings.\n"); kbd_shutdown(); } new_term = old_term; new_term.c_iflag = 0; new_term.c_lflag &= ~(ECHO | ICANON | ISIG); /* TCSAFLUSH discards unread input before making the change. A good idea. */ if (tcsetattr(kb, TCSAFLUSH, &new_term) != 0) { printf("Unable to change terminal settings.\n"); } /* Put the keyboard in mediumraw mode. */ if (ioctl(kb, KDSKBMODE, K_MEDIUMRAW) != 0) { printf("Unable to set mediumraw mode.\n"); kbd_shutdown(); } /* Put in non-blocking mode */ flags = fcntl(kb, F_GETFL, 0); fcntl(kb, F_SETFL, flags | O_NONBLOCK); printf("Ready to read keycodes. Press Backspace to exit.\n"); return 0; } int kbd_read(int *pressed, unsigned char *key) { unsigned char data; if (read(kb, &data, 1) < 1) { return 0; } *pressed = (data & 0x80) == 0x80; *key = data & 0x7F; /* Print the keycode. The top bit is the pressed/released flag, and the lower seven are the keycode. */ //printf("%s: 0x%2X (%i)\n", *pressed ? "Released" : " Pressed", (unsigned int)*key, (unsigned int)*key); return 1; } static unsigned char TranslateKey(unsigned char key) { if (key < sizeof(at_to_doom)) return at_to_doom[key]; else return 0x0; //default: // return tolower(key); } // Get the equivalent ASCII (Unicode?) character for a keypress. static unsigned char GetTypedChar(unsigned char key) { key = TranslateKey(key); // Is shift held down? If so, perform a translation. if (shiftdown > 0) { if (key >= 0 && key < arrlen(shiftxform)) { key = shiftxform[key]; } else { key = 0; } } return key; } static void UpdateShiftStatus(int pressed, unsigned char key) { int change; if (pressed) { change = 1; } else { change = -1; } if (key == 0x2a || key == 0x36) { shiftdown += change; } } void I_GetEvent(void) { event_t event; int pressed; unsigned char key; // put event-grabbing stuff in here while (kbd_read(&pressed, &key)) { if (key == 0x0E) { kbd_shutdown(); I_Quit(); } UpdateShiftStatus(pressed, key); // process event if (!pressed) { // data1 has the key pressed, data2 has the character // (shift-translated, etc) event.type = ev_keydown; event.data1 = TranslateKey(key); event.data2 = GetTypedChar(key); if (event.data1 != 0) { D_PostEvent(&event); } } else { event.type = ev_keyup; event.data1 = TranslateKey(key); // data2 is just initialized to zero for ev_keyup. // For ev_keydown it's the shifted Unicode character // that was typed, but if something wants to detect // key releases it should do so based on data1 // (key ID), not the printable char. event.data2 = 0; if (event.data1 != 0) { D_PostEvent(&event); } break; } } /* case SDL_MOUSEMOTION: event.type = ev_mouse; event.data1 = mouse_button_state; event.data2 = AccelerateMouse(sdlevent.motion.xrel); event.data3 = -AccelerateMouse(sdlevent.motion.yrel); D_PostEvent(&event); break; */ } void I_InitInput(void) { kbd_init(); //UpdateFocus(); } ================================================ FILE: fbdoom/i_input_tty.h ================================================ #ifndef __I_INPUT_TTY__ #define __I_INPUT_TTY__ void kbd_shutdown(void); #endif /* #ifndef __I_INPUT_TTY__ */ ================================================ FILE: fbdoom/i_joystick.c ================================================ // // Copyright(C) 2005-2014 Simon Howard // // 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. // // DESCRIPTION: // SDL Joystick code. // #ifdef ORIGCODE #include "SDL.h" #include "SDL_joystick.h" #endif #include #include #include #include "doomtype.h" #include "d_event.h" #include "i_joystick.h" #include "i_system.h" #include "m_config.h" #include "m_misc.h" // When an axis is within the dead zone, it is set to zero. // This is 5% of the full range: #define DEAD_ZONE (32768 / 3) #ifdef ORIGCODE static SDL_Joystick *joystick = NULL; #endif // Configuration variables: // Standard default.cfg Joystick enable/disable static int usejoystick = 0; // Joystick to use, as an SDL joystick index: static int joystick_index = -1; // Which joystick axis to use for horizontal movement, and whether to // invert the direction: static int joystick_x_axis = 0; static int joystick_x_invert = 0; // Which joystick axis to use for vertical movement, and whether to // invert the direction: static int joystick_y_axis = 1; static int joystick_y_invert = 0; // Which joystick axis to use for strafing? static int joystick_strafe_axis = -1; static int joystick_strafe_invert = 0; // Virtual to physical button joystick button mapping. By default this // is a straight mapping. static int joystick_physical_buttons[NUM_VIRTUAL_BUTTONS] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; void I_ShutdownJoystick(void) { #ifdef ORIGCODE if (joystick != NULL) { SDL_JoystickClose(joystick); joystick = NULL; SDL_QuitSubSystem(SDL_INIT_JOYSTICK); } #endif } #ifdef ORIGCODE static boolean IsValidAxis(int axis) { int num_axes; if (axis < 0) { return true; } if (IS_BUTTON_AXIS(axis)) { return true; } if (IS_HAT_AXIS(axis)) { return HAT_AXIS_HAT(axis) < SDL_JoystickNumHats(joystick); } num_axes = SDL_JoystickNumAxes(joystick); return axis < num_axes; } #endif void I_InitJoystick(void) { #ifdef ORIGCODE if (!usejoystick) { return; } if (SDL_Init(SDL_INIT_JOYSTICK) < 0) { return; } if (joystick_index < 0 || joystick_index >= SDL_NumJoysticks()) { printf("I_InitJoystick: Invalid joystick ID: %i\n", joystick_index); SDL_QuitSubSystem(SDL_INIT_JOYSTICK); return; } // Open the joystick joystick = SDL_JoystickOpen(joystick_index); if (joystick == NULL) { printf("I_InitJoystick: Failed to open joystick #%i\n", joystick_index); SDL_QuitSubSystem(SDL_INIT_JOYSTICK); return; } if (!IsValidAxis(joystick_x_axis) || !IsValidAxis(joystick_y_axis) || !IsValidAxis(joystick_strafe_axis)) { printf("I_InitJoystick: Invalid joystick axis for joystick #%i " "(run joystick setup again)\n", joystick_index); SDL_JoystickClose(joystick); joystick = NULL; SDL_QuitSubSystem(SDL_INIT_JOYSTICK); } SDL_JoystickEventState(SDL_ENABLE); // Initialized okay! printf("I_InitJoystick: %s\n", SDL_JoystickName(joystick_index)); I_AtExit(I_ShutdownJoystick, true); #endif } #ifdef ORIGCODE static boolean IsAxisButton(int physbutton) { if (IS_BUTTON_AXIS(joystick_x_axis)) { if (physbutton == BUTTON_AXIS_NEG(joystick_x_axis) || physbutton == BUTTON_AXIS_POS(joystick_x_axis)) { return true; } } if (IS_BUTTON_AXIS(joystick_y_axis)) { if (physbutton == BUTTON_AXIS_NEG(joystick_y_axis) || physbutton == BUTTON_AXIS_POS(joystick_y_axis)) { return true; } } if (IS_BUTTON_AXIS(joystick_strafe_axis)) { if (physbutton == BUTTON_AXIS_NEG(joystick_strafe_axis) || physbutton == BUTTON_AXIS_POS(joystick_strafe_axis)) { return true; } } return false; } // Get the state of the given virtual button. static int ReadButtonState(int vbutton) { int physbutton; // Map from virtual button to physical (SDL) button. if (vbutton < NUM_VIRTUAL_BUTTONS) { physbutton = joystick_physical_buttons[vbutton]; } else { physbutton = vbutton; } // Never read axis buttons as buttons. if (IsAxisButton(physbutton)) { return 0; } return SDL_JoystickGetButton(joystick, physbutton); } // Get a bitmask of all currently-pressed buttons static int GetButtonsState(void) { int i; int result; result = 0; for (i = 0; i < 20; ++i) { if (ReadButtonState(i)) { result |= 1 << i; } } return result; } // Read the state of an axis, inverting if necessary. static int GetAxisState(int axis, int invert) { int result; // Axis -1 means disabled. if (axis < 0) { return 0; } // Is this a button axis, or a hat axis? // If so, we need to handle it specially. result = 0; if (IS_BUTTON_AXIS(axis)) { if (SDL_JoystickGetButton(joystick, BUTTON_AXIS_NEG(axis))) { result -= 32767; } if (SDL_JoystickGetButton(joystick, BUTTON_AXIS_POS(axis))) { result += 32767; } } else if (IS_HAT_AXIS(axis)) { int direction = HAT_AXIS_DIRECTION(axis); int hatval = SDL_JoystickGetHat(joystick, HAT_AXIS_HAT(axis)); if (direction == HAT_AXIS_HORIZONTAL) { if ((hatval & SDL_HAT_LEFT) != 0) { result -= 32767; } else if ((hatval & SDL_HAT_RIGHT) != 0) { result += 32767; } } else if (direction == HAT_AXIS_VERTICAL) { if ((hatval & SDL_HAT_UP) != 0) { result -= 32767; } else if ((hatval & SDL_HAT_DOWN) != 0) { result += 32767; } } } else { result = SDL_JoystickGetAxis(joystick, axis); if (result < DEAD_ZONE && result > -DEAD_ZONE) { result = 0; } } if (invert) { result = -result; } return result; } #endif void I_UpdateJoystick(void) { #ifdef ORIGCODE if (joystick != NULL) { event_t ev; ev.type = ev_joystick; ev.data1 = GetButtonsState(); ev.data2 = GetAxisState(joystick_x_axis, joystick_x_invert); ev.data3 = GetAxisState(joystick_y_axis, joystick_y_invert); ev.data4 = GetAxisState(joystick_strafe_axis, joystick_strafe_invert); D_PostEvent(&ev); } #endif } void I_BindJoystickVariables(void) { int i; M_BindVariable("use_joystick", &usejoystick); M_BindVariable("joystick_index", &joystick_index); M_BindVariable("joystick_x_axis", &joystick_x_axis); M_BindVariable("joystick_y_axis", &joystick_y_axis); M_BindVariable("joystick_strafe_axis", &joystick_strafe_axis); M_BindVariable("joystick_x_invert", &joystick_x_invert); M_BindVariable("joystick_y_invert", &joystick_y_invert); M_BindVariable("joystick_strafe_invert",&joystick_strafe_invert); for (i = 0; i < NUM_VIRTUAL_BUTTONS; ++i) { char name[32]; M_snprintf(name, sizeof(name), "joystick_physical_button%i", i); M_BindVariable(name, &joystick_physical_buttons[i]); } } ================================================ FILE: fbdoom/i_joystick.h ================================================ // // Copyright(C) 2005-2014 Simon Howard // // 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. // // DESCRIPTION: // System-specific joystick interface. // #ifndef __I_JOYSTICK__ #define __I_JOYSTICK__ // Number of "virtual" joystick buttons defined in configuration files. // This needs to be at least as large as the number of different key // bindings supported by the higher-level game code (joyb* variables). #define NUM_VIRTUAL_BUTTONS 10 // If this bit is set in a configuration file axis value, the axis is // not actually a joystick axis, but instead is a "button axis". This // means that instead of reading an SDL joystick axis, we read the // state of two buttons to get the axis value. This is needed for eg. // the PS3 SIXAXIS controller, where the D-pad buttons register as // buttons, not as two axes. #define BUTTON_AXIS 0x10000 // Query whether a given axis value describes a button axis. #define IS_BUTTON_AXIS(axis) ((axis) >= 0 && ((axis) & BUTTON_AXIS) != 0) // Get the individual buttons from a button axis value. #define BUTTON_AXIS_NEG(axis) ((axis) & 0xff) #define BUTTON_AXIS_POS(axis) (((axis) >> 8) & 0xff) // Create a button axis value from two button values. #define CREATE_BUTTON_AXIS(neg, pos) (BUTTON_AXIS | (neg) | ((pos) << 8)) // If this bit is set in an axis value, the axis is not actually a // joystick axis, but is a "hat" axis. This means that we read (one of) // the hats on the joystick. #define HAT_AXIS 0x20000 #define IS_HAT_AXIS(axis) ((axis) >= 0 && ((axis) & HAT_AXIS) != 0) // Get the hat number from a hat axis value. #define HAT_AXIS_HAT(axis) ((axis) & 0xff) // Which axis of the hat? (horizonal or vertical) #define HAT_AXIS_DIRECTION(axis) (((axis) >> 8) & 0xff) #define CREATE_HAT_AXIS(hat, direction) \ (HAT_AXIS | (hat) | ((direction) << 8)) #define HAT_AXIS_HORIZONTAL 1 #define HAT_AXIS_VERTICAL 2 void I_InitJoystick(void); void I_ShutdownJoystick(void); void I_UpdateJoystick(void); void I_BindJoystickVariables(void); #endif /* #ifndef __I_JOYSTICK__ */ ================================================ FILE: fbdoom/i_main.c ================================================ // // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // 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. // // DESCRIPTION: // Main program, simply calls D_DoomMain high level loop. // #include "config.h" #include #include "doomtype.h" #include "i_system.h" #include "m_argv.h" // // D_DoomMain() // Not a globally visible function, just included for source reference, // calls all startup code, parses command line options. // void D_DoomMain (void); int main(int argc, char **argv) { // save arguments myargc = argc; myargv = argv; M_FindResponseFile(); // start doom printf("Starting D_DoomMain\r\n"); D_DoomMain (); return 0; } ================================================ FILE: fbdoom/i_oplmusic.c ================================================ // // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // 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. // // DESCRIPTION: // System interface for music. // #include #include #include #include "memio.h" #include "mus2mid.h" #include "deh_main.h" #include "i_sound.h" #include "i_swap.h" #include "m_misc.h" #include "w_wad.h" #include "z_zone.h" #include "opl.h" #include "midifile.h" // #define OPL_MIDI_DEBUG #define MAXMIDLENGTH (96 * 1024) #define GENMIDI_NUM_INSTRS 128 #define GENMIDI_NUM_PERCUSSION 47 #define GENMIDI_HEADER "#OPL_II#" #define GENMIDI_FLAG_FIXED 0x0001 /* fixed pitch */ #define GENMIDI_FLAG_2VOICE 0x0004 /* double voice (OPL3) */ #define PERCUSSION_LOG_LEN 16 typedef struct { byte tremolo; byte attack; byte sustain; byte waveform; byte scale; byte level; } PACKEDATTR genmidi_op_t; typedef struct { genmidi_op_t modulator; byte feedback; genmidi_op_t carrier; byte unused; short base_note_offset; } PACKEDATTR genmidi_voice_t; typedef struct { unsigned short flags; byte fine_tuning; byte fixed_note; genmidi_voice_t voices[2]; } PACKEDATTR genmidi_instr_t; // Data associated with a channel of a track that is currently playing. typedef struct { // The instrument currently used for this track. genmidi_instr_t *instrument; // Volume level int volume; // Pitch bend value: int bend; } opl_channel_data_t; // Data associated with a track that is currently playing. typedef struct { // Data for each channel. opl_channel_data_t channels[MIDI_CHANNELS_PER_TRACK]; // Track iterator used to read new events. midi_track_iter_t *iter; } opl_track_data_t; typedef struct opl_voice_s opl_voice_t; struct opl_voice_s { // Index of this voice: int index; // The operators used by this voice: int op1, op2; // Currently-loaded instrument data genmidi_instr_t *current_instr; // The voice number in the instrument to use. // This is normally set to zero; if this is a double voice // instrument, it may be one. unsigned int current_instr_voice; // The channel currently using this voice. opl_channel_data_t *channel; // The midi key that this voice is playing. unsigned int key; // The note being played. This is normally the same as // the key, but if the instrument is a fixed pitch // instrument, it is different. unsigned int note; // The frequency value being used. unsigned int freq; // The volume of the note being played on this channel. unsigned int note_volume; // The current volume (register value) that has been set for this channel. unsigned int reg_volume; // Next in linked list; a voice is always either in the // free list or the allocated list. opl_voice_t *next; }; // Operators used by the different voices. static const int voice_operators[2][OPL_NUM_VOICES] = { { 0x00, 0x01, 0x02, 0x08, 0x09, 0x0a, 0x10, 0x11, 0x12 }, { 0x03, 0x04, 0x05, 0x0b, 0x0c, 0x0d, 0x13, 0x14, 0x15 } }; // Frequency values to use for each note. static const unsigned short frequency_curve[] = { 0x133, 0x133, 0x134, 0x134, 0x135, 0x136, 0x136, 0x137, // -1 0x137, 0x138, 0x138, 0x139, 0x139, 0x13a, 0x13b, 0x13b, 0x13c, 0x13c, 0x13d, 0x13d, 0x13e, 0x13f, 0x13f, 0x140, 0x140, 0x141, 0x142, 0x142, 0x143, 0x143, 0x144, 0x144, 0x145, 0x146, 0x146, 0x147, 0x147, 0x148, 0x149, 0x149, // -2 0x14a, 0x14a, 0x14b, 0x14c, 0x14c, 0x14d, 0x14d, 0x14e, 0x14f, 0x14f, 0x150, 0x150, 0x151, 0x152, 0x152, 0x153, 0x153, 0x154, 0x155, 0x155, 0x156, 0x157, 0x157, 0x158, // These are used for the first seven MIDI note values: 0x158, 0x159, 0x15a, 0x15a, 0x15b, 0x15b, 0x15c, 0x15d, // 0 0x15d, 0x15e, 0x15f, 0x15f, 0x160, 0x161, 0x161, 0x162, 0x162, 0x163, 0x164, 0x164, 0x165, 0x166, 0x166, 0x167, 0x168, 0x168, 0x169, 0x16a, 0x16a, 0x16b, 0x16c, 0x16c, 0x16d, 0x16e, 0x16e, 0x16f, 0x170, 0x170, 0x171, 0x172, // 1 0x172, 0x173, 0x174, 0x174, 0x175, 0x176, 0x176, 0x177, 0x178, 0x178, 0x179, 0x17a, 0x17a, 0x17b, 0x17c, 0x17c, 0x17d, 0x17e, 0x17e, 0x17f, 0x180, 0x181, 0x181, 0x182, 0x183, 0x183, 0x184, 0x185, 0x185, 0x186, 0x187, 0x188, // 2 0x188, 0x189, 0x18a, 0x18a, 0x18b, 0x18c, 0x18d, 0x18d, 0x18e, 0x18f, 0x18f, 0x190, 0x191, 0x192, 0x192, 0x193, 0x194, 0x194, 0x195, 0x196, 0x197, 0x197, 0x198, 0x199, 0x19a, 0x19a, 0x19b, 0x19c, 0x19d, 0x19d, 0x19e, 0x19f, // 3 0x1a0, 0x1a0, 0x1a1, 0x1a2, 0x1a3, 0x1a3, 0x1a4, 0x1a5, 0x1a6, 0x1a6, 0x1a7, 0x1a8, 0x1a9, 0x1a9, 0x1aa, 0x1ab, 0x1ac, 0x1ad, 0x1ad, 0x1ae, 0x1af, 0x1b0, 0x1b0, 0x1b1, 0x1b2, 0x1b3, 0x1b4, 0x1b4, 0x1b5, 0x1b6, 0x1b7, 0x1b8, // 4 0x1b8, 0x1b9, 0x1ba, 0x1bb, 0x1bc, 0x1bc, 0x1bd, 0x1be, 0x1bf, 0x1c0, 0x1c0, 0x1c1, 0x1c2, 0x1c3, 0x1c4, 0x1c4, 0x1c5, 0x1c6, 0x1c7, 0x1c8, 0x1c9, 0x1c9, 0x1ca, 0x1cb, 0x1cc, 0x1cd, 0x1ce, 0x1ce, 0x1cf, 0x1d0, 0x1d1, 0x1d2, // 5 0x1d3, 0x1d3, 0x1d4, 0x1d5, 0x1d6, 0x1d7, 0x1d8, 0x1d8, 0x1d9, 0x1da, 0x1db, 0x1dc, 0x1dd, 0x1de, 0x1de, 0x1df, 0x1e0, 0x1e1, 0x1e2, 0x1e3, 0x1e4, 0x1e5, 0x1e5, 0x1e6, 0x1e7, 0x1e8, 0x1e9, 0x1ea, 0x1eb, 0x1ec, 0x1ed, 0x1ed, // 6 0x1ee, 0x1ef, 0x1f0, 0x1f1, 0x1f2, 0x1f3, 0x1f4, 0x1f5, 0x1f6, 0x1f6, 0x1f7, 0x1f8, 0x1f9, 0x1fa, 0x1fb, 0x1fc, 0x1fd, 0x1fe, 0x1ff, 0x200, 0x201, 0x201, 0x202, 0x203, // First note of looped range used for all octaves: 0x204, 0x205, 0x206, 0x207, 0x208, 0x209, 0x20a, 0x20b, // 7 0x20c, 0x20d, 0x20e, 0x20f, 0x210, 0x210, 0x211, 0x212, 0x213, 0x214, 0x215, 0x216, 0x217, 0x218, 0x219, 0x21a, 0x21b, 0x21c, 0x21d, 0x21e, 0x21f, 0x220, 0x221, 0x222, 0x223, 0x224, 0x225, 0x226, 0x227, 0x228, 0x229, 0x22a, // 8 0x22b, 0x22c, 0x22d, 0x22e, 0x22f, 0x230, 0x231, 0x232, 0x233, 0x234, 0x235, 0x236, 0x237, 0x238, 0x239, 0x23a, 0x23b, 0x23c, 0x23d, 0x23e, 0x23f, 0x240, 0x241, 0x242, 0x244, 0x245, 0x246, 0x247, 0x248, 0x249, 0x24a, 0x24b, // 9 0x24c, 0x24d, 0x24e, 0x24f, 0x250, 0x251, 0x252, 0x253, 0x254, 0x256, 0x257, 0x258, 0x259, 0x25a, 0x25b, 0x25c, 0x25d, 0x25e, 0x25f, 0x260, 0x262, 0x263, 0x264, 0x265, 0x266, 0x267, 0x268, 0x269, 0x26a, 0x26c, 0x26d, 0x26e, // 10 0x26f, 0x270, 0x271, 0x272, 0x273, 0x275, 0x276, 0x277, 0x278, 0x279, 0x27a, 0x27b, 0x27d, 0x27e, 0x27f, 0x280, 0x281, 0x282, 0x284, 0x285, 0x286, 0x287, 0x288, 0x289, 0x28b, 0x28c, 0x28d, 0x28e, 0x28f, 0x290, 0x292, 0x293, // 11 0x294, 0x295, 0x296, 0x298, 0x299, 0x29a, 0x29b, 0x29c, 0x29e, 0x29f, 0x2a0, 0x2a1, 0x2a2, 0x2a4, 0x2a5, 0x2a6, 0x2a7, 0x2a9, 0x2aa, 0x2ab, 0x2ac, 0x2ae, 0x2af, 0x2b0, 0x2b1, 0x2b2, 0x2b4, 0x2b5, 0x2b6, 0x2b7, 0x2b9, 0x2ba, // 12 0x2bb, 0x2bd, 0x2be, 0x2bf, 0x2c0, 0x2c2, 0x2c3, 0x2c4, 0x2c5, 0x2c7, 0x2c8, 0x2c9, 0x2cb, 0x2cc, 0x2cd, 0x2ce, 0x2d0, 0x2d1, 0x2d2, 0x2d4, 0x2d5, 0x2d6, 0x2d8, 0x2d9, 0x2da, 0x2dc, 0x2dd, 0x2de, 0x2e0, 0x2e1, 0x2e2, 0x2e4, // 13 0x2e5, 0x2e6, 0x2e8, 0x2e9, 0x2ea, 0x2ec, 0x2ed, 0x2ee, 0x2f0, 0x2f1, 0x2f2, 0x2f4, 0x2f5, 0x2f6, 0x2f8, 0x2f9, 0x2fb, 0x2fc, 0x2fd, 0x2ff, 0x300, 0x302, 0x303, 0x304, 0x306, 0x307, 0x309, 0x30a, 0x30b, 0x30d, 0x30e, 0x310, // 14 0x311, 0x312, 0x314, 0x315, 0x317, 0x318, 0x31a, 0x31b, 0x31c, 0x31e, 0x31f, 0x321, 0x322, 0x324, 0x325, 0x327, 0x328, 0x329, 0x32b, 0x32c, 0x32e, 0x32f, 0x331, 0x332, 0x334, 0x335, 0x337, 0x338, 0x33a, 0x33b, 0x33d, 0x33e, // 15 0x340, 0x341, 0x343, 0x344, 0x346, 0x347, 0x349, 0x34a, 0x34c, 0x34d, 0x34f, 0x350, 0x352, 0x353, 0x355, 0x357, 0x358, 0x35a, 0x35b, 0x35d, 0x35e, 0x360, 0x361, 0x363, 0x365, 0x366, 0x368, 0x369, 0x36b, 0x36c, 0x36e, 0x370, // 16 0x371, 0x373, 0x374, 0x376, 0x378, 0x379, 0x37b, 0x37c, 0x37e, 0x380, 0x381, 0x383, 0x384, 0x386, 0x388, 0x389, 0x38b, 0x38d, 0x38e, 0x390, 0x392, 0x393, 0x395, 0x397, 0x398, 0x39a, 0x39c, 0x39d, 0x39f, 0x3a1, 0x3a2, 0x3a4, // 17 0x3a6, 0x3a7, 0x3a9, 0x3ab, 0x3ac, 0x3ae, 0x3b0, 0x3b1, 0x3b3, 0x3b5, 0x3b7, 0x3b8, 0x3ba, 0x3bc, 0x3bd, 0x3bf, 0x3c1, 0x3c3, 0x3c4, 0x3c6, 0x3c8, 0x3ca, 0x3cb, 0x3cd, // The last note has an incomplete range, and loops round back to // the start. Note that the last value is actually a buffer overrun // and does not fit with the other values. 0x3cf, 0x3d1, 0x3d2, 0x3d4, 0x3d6, 0x3d8, 0x3da, 0x3db, // 18 0x3dd, 0x3df, 0x3e1, 0x3e3, 0x3e4, 0x3e6, 0x3e8, 0x3ea, 0x3ec, 0x3ed, 0x3ef, 0x3f1, 0x3f3, 0x3f5, 0x3f6, 0x3f8, 0x3fa, 0x3fc, 0x3fe, 0x36c, }; // Mapping from MIDI volume level to OPL level value. static const unsigned int volume_mapping_table[] = { 0, 1, 3, 5, 6, 8, 10, 11, 13, 14, 16, 17, 19, 20, 22, 23, 25, 26, 27, 29, 30, 32, 33, 34, 36, 37, 39, 41, 43, 45, 47, 49, 50, 52, 54, 55, 57, 59, 60, 61, 63, 64, 66, 67, 68, 69, 71, 72, 73, 74, 75, 76, 77, 79, 80, 81, 82, 83, 84, 84, 85, 86, 87, 88, 89, 90, 91, 92, 92, 93, 94, 95, 96, 96, 97, 98, 99, 99, 100, 101, 101, 102, 103, 103, 104, 105, 105, 106, 107, 107, 108, 109, 109, 110, 110, 111, 112, 112, 113, 113, 114, 114, 115, 115, 116, 117, 117, 118, 118, 119, 119, 120, 120, 121, 121, 122, 122, 123, 123, 123, 124, 124, 125, 125, 126, 126, 127, 127 }; static boolean music_initialized = false; //static boolean musicpaused = false; static int current_music_volume; // GENMIDI lump instrument data: static genmidi_instr_t *main_instrs; static genmidi_instr_t *percussion_instrs; static char (*main_instr_names)[32]; static char (*percussion_names)[32]; // Voices: static opl_voice_t voices[OPL_NUM_VOICES]; static opl_voice_t *voice_free_list; static opl_voice_t *voice_alloced_list; // Track data for playing tracks: static opl_track_data_t *tracks; static unsigned int num_tracks = 0; static unsigned int running_tracks = 0; static boolean song_looping; // Tempo control variables static unsigned int ticks_per_beat; static unsigned int us_per_beat; // Mini-log of recently played percussion instruments: static uint8_t last_perc[PERCUSSION_LOG_LEN]; static unsigned int last_perc_count; // Configuration file variable, containing the port number for the // adlib chip. int opl_io_port = 0x388; // Load instrument table from GENMIDI lump: static boolean LoadInstrumentTable(void) { byte *lump; lump = W_CacheLumpName("GENMIDI", PU_STATIC); // Check header if (strncmp((char *) lump, GENMIDI_HEADER, strlen(GENMIDI_HEADER)) != 0) { W_ReleaseLumpName("GENMIDI"); return false; } main_instrs = (genmidi_instr_t *) (lump + strlen(GENMIDI_HEADER)); percussion_instrs = main_instrs + GENMIDI_NUM_INSTRS; main_instr_names = (char (*)[32]) (percussion_instrs + GENMIDI_NUM_PERCUSSION); percussion_names = main_instr_names + GENMIDI_NUM_INSTRS; return true; } // Get the next available voice from the freelist. static opl_voice_t *GetFreeVoice(void) { opl_voice_t *result; // None available? if (voice_free_list == NULL) { return NULL; } // Remove from free list result = voice_free_list; voice_free_list = voice_free_list->next; // Add to allocated list result->next = voice_alloced_list; voice_alloced_list = result; return result; } // Remove a voice from the allocated voices list. static void RemoveVoiceFromAllocedList(opl_voice_t *voice) { opl_voice_t **rover; rover = &voice_alloced_list; // Search the list until we find the voice, then remove it. while (*rover != NULL) { if (*rover == voice) { *rover = voice->next; voice->next = NULL; break; } rover = &(*rover)->next; } } // Release a voice back to the freelist. static void ReleaseVoice(opl_voice_t *voice) { opl_voice_t **rover; voice->channel = NULL; voice->note = 0; // Remove from alloced list. RemoveVoiceFromAllocedList(voice); // Search to the end of the freelist (This is how Doom behaves!) rover = &voice_free_list; while (*rover != NULL) { rover = &(*rover)->next; } *rover = voice; voice->next = NULL; } // Load data to the specified operator static void LoadOperatorData(int operator, genmidi_op_t *data, boolean max_level) { int level; // The scale and level fields must be combined for the level register. // For the carrier wave we always set the maximum level. level = (data->scale & 0xc0) | (data->level & 0x3f); if (max_level) { level |= 0x3f; } OPL_WriteRegister(OPL_REGS_LEVEL + operator, level); OPL_WriteRegister(OPL_REGS_TREMOLO + operator, data->tremolo); OPL_WriteRegister(OPL_REGS_ATTACK + operator, data->attack); OPL_WriteRegister(OPL_REGS_SUSTAIN + operator, data->sustain); OPL_WriteRegister(OPL_REGS_WAVEFORM + operator, data->waveform); } // Set the instrument for a particular voice. static void SetVoiceInstrument(opl_voice_t *voice, genmidi_instr_t *instr, unsigned int instr_voice) { genmidi_voice_t *data; unsigned int modulating; // Instrument already set for this channel? if (voice->current_instr == instr && voice->current_instr_voice == instr_voice) { return; } voice->current_instr = instr; voice->current_instr_voice = instr_voice; data = &instr->voices[instr_voice]; // Are we usind modulated feedback mode? modulating = (data->feedback & 0x01) == 0; // Doom loads the second operator first, then the first. // The carrier is set to minimum volume until the voice volume // is set in SetVoiceVolume (below). If we are not using // modulating mode, we must set both to minimum volume. LoadOperatorData(voice->op2, &data->carrier, true); LoadOperatorData(voice->op1, &data->modulator, !modulating); // Set feedback register that control the connection between the // two operators. Turn on bits in the upper nybble; I think this // is for OPL3, where it turns on channel A/B. OPL_WriteRegister(OPL_REGS_FEEDBACK + voice->index, data->feedback | 0x30); // Hack to force a volume update. voice->reg_volume = 999; } static void SetVoiceVolume(opl_voice_t *voice, unsigned int volume) { genmidi_voice_t *opl_voice; unsigned int full_volume; unsigned int op_volume; unsigned int reg_volume; voice->note_volume = volume; opl_voice = &voice->current_instr->voices[voice->current_instr_voice]; // Multiply note volume and channel volume to get the actual volume. full_volume = (volume_mapping_table[voice->note_volume] * volume_mapping_table[voice->channel->volume] * volume_mapping_table[current_music_volume]) / (127 * 127); // The volume of each instrument can be controlled via GENMIDI: op_volume = 0x3f - opl_voice->carrier.level; // The volume value to use in the register: reg_volume = (op_volume * full_volume) / 128; reg_volume = (0x3f - reg_volume) | opl_voice->carrier.scale; // Update the volume register(s) if necessary. if (reg_volume != voice->reg_volume) { voice->reg_volume = reg_volume; OPL_WriteRegister(OPL_REGS_LEVEL + voice->op2, reg_volume); // If we are using non-modulated feedback mode, we must set the // volume for both voices. // Note that the same register volume value is written for // both voices, always calculated from the carrier's level // value. if ((opl_voice->feedback & 0x01) != 0) { OPL_WriteRegister(OPL_REGS_LEVEL + voice->op1, reg_volume); } } } // Initialize the voice table and freelist static void InitVoices(void) { int i; // Start with an empty free list. voice_free_list = NULL; // Initialize each voice. for (i=0; iindex, voice->freq >> 8); } // Get the frequency that we should be using for a voice. static void KeyOffEvent(opl_track_data_t *track, midi_event_t *event) { opl_channel_data_t *channel; unsigned int key; unsigned int i; /* printf("note off: channel %i, %i, %i\n", event->data.channel.channel, event->data.channel.param1, event->data.channel.param2); */ channel = &track->channels[event->data.channel.channel]; key = event->data.channel.param1; // Turn off voices being used to play this key. // If it is a double voice instrument there will be two. for (i=0; inext) { if (rover->current_instr_voice != 0 || (rover->channel > channel && CompareChannelPriorities(channel, rover->channel) > 0)) { result = rover; break; } } // If we didn't find a voice, find an existing voice being used to // play a note on the same channel, and use that. if (result == NULL) { for (rover = voice_alloced_list; rover != NULL; rover = rover->next) { if (rover->channel == channel) { result = rover; break; } } } // Still nothing found? Give up and just use the first voice in // the list. if (result == NULL) { result = voice_alloced_list; } // Stop playing this voice playing and release it back to the free // list. VoiceKeyOff(result); ReleaseVoice(result); // Re-allocate the voice again and return it. return GetFreeVoice(); } static unsigned int FrequencyForVoice(opl_voice_t *voice) { genmidi_voice_t *gm_voice; unsigned int freq_index; unsigned int octave; unsigned int sub_index; unsigned int note; note = voice->note; // Apply note offset. // Don't apply offset if the instrument is a fixed note instrument. gm_voice = &voice->current_instr->voices[voice->current_instr_voice]; if ((SHORT(voice->current_instr->flags) & GENMIDI_FLAG_FIXED) == 0) { note += (signed short) SHORT(gm_voice->base_note_offset); } // Avoid possible overflow due to base note offset: if (note > 0x7f) { note = voice->note; } freq_index = 64 + 32 * note + voice->channel->bend; // If this is the second voice of a double voice instrument, the // frequency index can be adjusted by the fine tuning field. if (voice->current_instr_voice != 0) { freq_index += (voice->current_instr->fine_tuning / 2) - 64; } // The first 7 notes use the start of the table, while // consecutive notes loop around the latter part. if (freq_index < 284) { return frequency_curve[freq_index]; } sub_index = (freq_index - 284) % (12 * 32); octave = (freq_index - 284) / (12 * 32); // Once the seventh octave is reached, things break down. // We can only go up to octave 7 as a maximum anyway (the OPL // register only has three bits for octave number), but for the // notes in octave 7, the first five bits have octave=7, the // following notes have octave=6. This 7/6 pattern repeats in // following octaves (which are technically impossible to // represent anyway). if (octave >= 7) { if (sub_index < 5) { octave = 7; } else { octave = 6; } } // Calculate the resulting register value to use for the frequency. return frequency_curve[sub_index + 284] | (octave << 10); } // Update the frequency that a voice is programmed to use. static void UpdateVoiceFrequency(opl_voice_t *voice) { unsigned int freq; // Calculate the frequency to use for this voice and update it // if neccessary. freq = FrequencyForVoice(voice); if (voice->freq != freq) { OPL_WriteRegister(OPL_REGS_FREQ_1 + voice->index, freq & 0xff); OPL_WriteRegister(OPL_REGS_FREQ_2 + voice->index, (freq >> 8) | 0x20); voice->freq = freq; } } // Program a single voice for an instrument. For a double voice // instrument (GENMIDI_FLAG_2VOICE), this is called twice for each // key on event. static void VoiceKeyOn(opl_channel_data_t *channel, genmidi_instr_t *instrument, unsigned int instrument_voice, unsigned int key, unsigned int volume) { opl_voice_t *voice; // Find a voice to use for this new note. voice = GetFreeVoice(); // If there are no more voices left, we must decide what to do. // If this is the first voice of the instrument, free an existing // voice and use that. Otherwise, if this is the second voice, // it isn't as important; just discard it. if (voice == NULL) { if (instrument_voice == 0) { voice = ReplaceExistingVoice(channel); } else { return; } } voice->channel = channel; voice->key = key; // Work out the note to use. This is normally the same as // the key, unless it is a fixed pitch instrument. if ((SHORT(instrument->flags) & GENMIDI_FLAG_FIXED) != 0) { voice->note = instrument->fixed_note; } else { voice->note = key; } // Program the voice with the instrument data: SetVoiceInstrument(voice, instrument, instrument_voice); // Set the volume level. SetVoiceVolume(voice, volume); // Write the frequency value to turn the note on. voice->freq = 0; UpdateVoiceFrequency(voice); } static void KeyOnEvent(opl_track_data_t *track, midi_event_t *event) { genmidi_instr_t *instrument; opl_channel_data_t *channel; unsigned int key; unsigned int volume; /* printf("note on: channel %i, %i, %i\n", event->data.channel.channel, event->data.channel.param1, event->data.channel.param2); */ key = event->data.channel.param1; volume = event->data.channel.param2; // A volume of zero means key off. Some MIDI tracks, eg. the ones // in AV.wad, use a second key on with a volume of zero to mean // key off. if (volume <= 0) { KeyOffEvent(track, event); return; } // The channel. channel = &track->channels[event->data.channel.channel]; // Percussion channel (10) is treated differently. if (event->data.channel.channel == 9) { if (key < 35 || key > 81) { return; } instrument = &percussion_instrs[key - 35]; last_perc[last_perc_count] = key; last_perc_count = (last_perc_count + 1) % PERCUSSION_LOG_LEN; } else { instrument = channel->instrument; } // Find and program a voice for this instrument. If this // is a double voice instrument, we must do this twice. VoiceKeyOn(channel, instrument, 0, key, volume); if ((SHORT(instrument->flags) & GENMIDI_FLAG_2VOICE) != 0) { VoiceKeyOn(channel, instrument, 1, key, volume); } } static void ProgramChangeEvent(opl_track_data_t *track, midi_event_t *event) { int channel; int instrument; // Set the instrument used on this channel. channel = event->data.channel.channel; instrument = event->data.channel.param1; track->channels[channel].instrument = &main_instrs[instrument]; // TODO: Look through existing voices that are turned on on this // channel, and change the instrument. } static void SetChannelVolume(opl_channel_data_t *channel, unsigned int volume) { unsigned int i; channel->volume = volume; // Update all voices that this channel is using. for (i=0; idata.channel.channel, event->data.channel.param1, event->data.channel.param2); */ channel = &track->channels[event->data.channel.channel]; controller = event->data.channel.param1; param = event->data.channel.param2; switch (controller) { case MIDI_CONTROLLER_MAIN_VOLUME: SetChannelVolume(channel, param); break; case MIDI_CONTROLLER_ALL_NOTES_OFF: AllNotesOff(channel, param); break; default: #ifdef OPL_MIDI_DEBUG fprintf(stderr, "Unknown MIDI controller type: %i\n", controller); #endif break; } } // Process a pitch bend event. static void PitchBendEvent(opl_track_data_t *track, midi_event_t *event) { opl_channel_data_t *channel; unsigned int i; // Update the channel bend value. Only the MSB of the pitch bend // value is considered: this is what Doom does. channel = &track->channels[event->data.channel.channel]; channel->bend = event->data.channel.param2 - 64; // Update all voices for this channel. for (i=0; idata.meta.data; unsigned int data_len = event->data.meta.length; switch (event->data.meta.type) { // Things we can just ignore. case MIDI_META_SEQUENCE_NUMBER: case MIDI_META_TEXT: case MIDI_META_COPYRIGHT: case MIDI_META_TRACK_NAME: case MIDI_META_INSTR_NAME: case MIDI_META_LYRICS: case MIDI_META_MARKER: case MIDI_META_CUE_POINT: case MIDI_META_SEQUENCER_SPECIFIC: break; case MIDI_META_SET_TEMPO: if (data_len == 3) { MetaSetTempo((data[0] << 16) | (data[1] << 8) | data[2]); } break; // End of track - actually handled when we run out of events // in the track, see below. case MIDI_META_END_OF_TRACK: break; default: #ifdef OPL_MIDI_DEBUG fprintf(stderr, "Unknown MIDI meta event type: %i\n", event->data.meta.type); #endif break; } } // Process a MIDI event from a track. static void ProcessEvent(opl_track_data_t *track, midi_event_t *event) { switch (event->event_type) { case MIDI_EVENT_NOTE_OFF: KeyOffEvent(track, event); break; case MIDI_EVENT_NOTE_ON: KeyOnEvent(track, event); break; case MIDI_EVENT_CONTROLLER: ControllerEvent(track, event); break; case MIDI_EVENT_PROGRAM_CHANGE: ProgramChangeEvent(track, event); break; case MIDI_EVENT_PITCH_BEND: PitchBendEvent(track, event); break; case MIDI_EVENT_META: MetaEvent(track, event); break; // SysEx events can be ignored. case MIDI_EVENT_SYSEX: case MIDI_EVENT_SYSEX_SPLIT: break; default: #ifdef OPL_MIDI_DEBUG fprintf(stderr, "Unknown MIDI event type %i\n", event->event_type); #endif break; } } static void ScheduleTrack(opl_track_data_t *track); // Restart a song from the beginning. static void RestartSong(void *unused) { unsigned int i; running_tracks = num_tracks; for (i=0; iiter, &event)) { return; } ProcessEvent(track, event); // End of track? if (event->event_type == MIDI_EVENT_META && event->data.meta.type == MIDI_META_END_OF_TRACK) { --running_tracks; // When all tracks have finished, restart the song. // Don't restart the song immediately, but wait for 5ms // before triggering a restart. Otherwise it is possible // to construct an empty MIDI file that causes the game // to lock up in an infinite loop. (5ms should be short // enough not to be noticeable by the listener). if (running_tracks <= 0 && song_looping) { OPL_SetCallback(5000, RestartSong, NULL); } return; } // Reschedule the callback for the next event in the track. ScheduleTrack(track); } static void ScheduleTrack(opl_track_data_t *track) { unsigned int nticks; uint64_t us; // Get the number of microseconds until the next event. nticks = MIDI_GetDeltaTime(track->iter); us = ((uint64_t) nticks * us_per_beat) / ticks_per_beat; // Set a timer to be invoked when the next event is // ready to play. OPL_SetCallback(us, TrackTimerCallback, track); } // Initialize a channel. static void InitChannel(opl_track_data_t *track, opl_channel_data_t *channel) { // TODO: Work out sensible defaults? channel->instrument = &main_instrs[0]; channel->volume = 127; channel->bend = 0; } // Start a MIDI track playing: static void StartTrack(midi_file_t *file, unsigned int track_num) { opl_track_data_t *track; unsigned int i; track = &tracks[track_num]; track->iter = MIDI_IterateTrack(file, track_num); for (i=0; ichannels[i]); } // Schedule the first event. ScheduleTrack(track); } // Start playing a mid static void I_OPL_PlaySong(void *handle, boolean looping) { midi_file_t *file; unsigned int i; if (!music_initialized || handle == NULL) { return; } file = handle; // Allocate track data. tracks = malloc(MIDI_NumTracks(file) * sizeof(opl_track_data_t)); num_tracks = MIDI_NumTracks(file); running_tracks = num_tracks; song_looping = looping; ticks_per_beat = MIDI_GetFileTimeDivision(file); // Default is 120 bpm. // TODO: this is wrong us_per_beat = 500 * 1000; for (i=0; i 4 && !memcmp(mem, "MThd", 4); } static boolean ConvertMus(byte *musdata, int len, char *filename) { MEMFILE *instream; MEMFILE *outstream; void *outbuf; size_t outbuf_len; int result; instream = mem_fopen_read(musdata, len); outstream = mem_fopen_write(); result = mus2mid(instream, outstream); if (result == 0) { mem_get_buf(outstream, &outbuf, &outbuf_len); M_WriteFile(filename, outbuf, outbuf_len); } mem_fclose(instream); mem_fclose(outstream); return result; } static void *I_OPL_RegisterSong(void *data, int len) { midi_file_t *result; char *filename; if (!music_initialized) { return NULL; } // MUS files begin with "MUS" // Reject anything which doesnt have this signature filename = M_TempFile("doom.mid"); if (IsMid(data, len) && len < MAXMIDLENGTH) { M_WriteFile(filename, data, len); } else { // Assume a MUS file and try to convert ConvertMus(data, len, filename); } result = MIDI_LoadFile(filename); if (result == NULL) { fprintf(stderr, "I_OPL_RegisterSong: Failed to load MID.\n"); } // remove file now remove(filename); free(filename); return result; } // Is the song playing? static boolean I_OPL_MusicIsPlaying(void) { if (!music_initialized) { return false; } return num_tracks > 0; } // Shutdown music static void I_OPL_ShutdownMusic(void) { if (music_initialized) { // Stop currently-playing track, if there is one: I_OPL_StopSong(); OPL_Shutdown(); // Release GENMIDI lump W_ReleaseLumpName("GENMIDI"); music_initialized = false; } } // Initialize music subsystem static boolean I_OPL_InitMusic(void) { OPL_SetSampleRate(snd_samplerate); if (!OPL_Init(opl_io_port)) { printf("Dude. The Adlib isn't responding.\n"); return false; } // Load instruments from GENMIDI lump: if (!LoadInstrumentTable()) { OPL_Shutdown(); return false; } InitVoices(); tracks = NULL; num_tracks = 0; music_initialized = true; return true; } static snddevice_t music_opl_devices[] = { SNDDEVICE_ADLIB, SNDDEVICE_SB, }; music_module_t music_opl_module = { music_opl_devices, arrlen(music_opl_devices), I_OPL_InitMusic, I_OPL_ShutdownMusic, I_OPL_SetMusicVolume, I_OPL_PauseSong, I_OPL_ResumeSong, I_OPL_RegisterSong, I_OPL_UnRegisterSong, I_OPL_PlaySong, I_OPL_StopSong, I_OPL_MusicIsPlaying, NULL, // Poll }; //---------------------------------------------------------------------- // // Development / debug message generation, to help developing GENMIDI // lumps. // //---------------------------------------------------------------------- static int NumActiveChannels(void) { int i; for (i = MIDI_CHANNELS_PER_TRACK - 1; i >= 0; --i) { if (tracks[0].channels[i].instrument != &main_instrs[0]) { return i + 1; } } return 0; } static int ChannelInUse(opl_channel_data_t *channel) { opl_voice_t *voice; for (voice = voice_alloced_list; voice != NULL; voice = voice->next) { if (voice->channel == channel) { return 1; } } return 0; } void I_OPL_DevMessages(char *result, size_t result_len) { char tmp[80]; int instr_num; int lines; int i; if (num_tracks == 0) { M_snprintf(result, result_len, "No OPL track!"); return; } M_snprintf(result, result_len, "Tracks:\n"); lines = 1; for (i = 0; i < NumActiveChannels(); ++i) { if (tracks[0].channels[i].instrument == NULL) { continue; } instr_num = tracks[0].channels[i].instrument - main_instrs; M_snprintf(tmp, sizeof(tmp), "chan %i: %c i#%i (%s)\n", i, ChannelInUse(&tracks[0].channels[i]) ? '\'' : ' ', instr_num + 1, main_instr_names[instr_num]); M_StringConcat(result, tmp, result_len); ++lines; } M_snprintf(tmp, sizeof(tmp), "\nLast percussion:\n"); M_StringConcat(result, tmp, result_len); lines += 2; i = (last_perc_count + PERCUSSION_LOG_LEN - 1) % PERCUSSION_LOG_LEN; do { if (last_perc[i] == 0) { break; } M_snprintf(tmp, sizeof(tmp), "%cp#%i (%s)\n", i == 0 ? '\'' : ' ', last_perc[i], percussion_names[last_perc[i] - 35]); M_StringConcat(result, tmp, result_len); ++lines; i = (i + PERCUSSION_LOG_LEN - 1) % PERCUSSION_LOG_LEN; } while (lines < 25 && i != last_perc_count); } ================================================ FILE: fbdoom/i_pcsound.c ================================================ // // Copyright(C) 2005-2014 Simon Howard // // 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. // // DESCRIPTION: // System interface for PC speaker sound. // #include "SDL.h" #include #include "doomtype.h" #include "deh_str.h" #include "i_sound.h" #include "m_misc.h" #include "w_wad.h" #include "z_zone.h" #include "pcsound.h" #define TIMER_FREQ 1193181 /* hz */ static boolean pcs_initialized = false; static SDL_mutex *sound_lock; static boolean use_sfx_prefix; static uint8_t *current_sound_lump = NULL; static uint8_t *current_sound_pos = NULL; static unsigned int current_sound_remaining = 0; static int current_sound_handle = 0; static int current_sound_lump_num = -1; static const uint16_t divisors[] = { 0, 6818, 6628, 6449, 6279, 6087, 5906, 5736, 5575, 5423, 5279, 5120, 4971, 4830, 4697, 4554, 4435, 4307, 4186, 4058, 3950, 3836, 3728, 3615, 3519, 3418, 3323, 3224, 3131, 3043, 2960, 2875, 2794, 2711, 2633, 2560, 2485, 2415, 2348, 2281, 2213, 2153, 2089, 2032, 1975, 1918, 1864, 1810, 1757, 1709, 1659, 1612, 1565, 1521, 1478, 1435, 1395, 1355, 1316, 1280, 1242, 1207, 1173, 1140, 1107, 1075, 1045, 1015, 986, 959, 931, 905, 879, 854, 829, 806, 783, 760, 739, 718, 697, 677, 658, 640, 621, 604, 586, 570, 553, 538, 522, 507, 493, 479, 465, 452, 439, 427, 415, 403, 391, 380, 369, 359, 348, 339, 329, 319, 310, 302, 293, 285, 276, 269, 261, 253, 246, 239, 232, 226, 219, 213, 207, 201, 195, 190, 184, 179, }; static void PCSCallbackFunc(int *duration, int *freq) { unsigned int tone; *duration = 1000 / 140; if (SDL_LockMutex(sound_lock) < 0) { *freq = 0; return; } if (current_sound_lump != NULL && current_sound_remaining > 0) { // Read the next tone tone = *current_sound_pos; // Use the tone -> frequency lookup table. See pcspkr10.zip // for a full discussion of this. // Check we don't overflow the frequency table. if (tone < arrlen(divisors) && divisors[tone] != 0) { *freq = (int) (TIMER_FREQ / divisors[tone]); } else { *freq = 0; } ++current_sound_pos; --current_sound_remaining; } else { *freq = 0; } SDL_UnlockMutex(sound_lock); } static boolean CachePCSLump(sfxinfo_t *sfxinfo) { int lumplen; int headerlen; // Free the current sound lump back to the cache if (current_sound_lump != NULL) { W_ReleaseLumpNum(current_sound_lump_num); current_sound_lump = NULL; } // Load from WAD current_sound_lump = W_CacheLumpNum(sfxinfo->lumpnum, PU_STATIC); lumplen = W_LumpLength(sfxinfo->lumpnum); // Read header if (current_sound_lump[0] != 0x00 || current_sound_lump[1] != 0x00) { return false; } headerlen = (current_sound_lump[3] << 8) | current_sound_lump[2]; if (headerlen > lumplen - 4) { return false; } // Header checks out ok current_sound_remaining = headerlen; current_sound_pos = current_sound_lump + 4; current_sound_lump_num = sfxinfo->lumpnum; return true; } // These Doom PC speaker sounds are not played - this can be seen in the // Heretic source code, where there are remnants of this left over // from Doom. static boolean IsDisabledSound(sfxinfo_t *sfxinfo) { int i; const char *disabled_sounds[] = { "posact", "bgact", "dmact", "dmpain", "popain", "sawidl", }; for (i=0; iname, disabled_sounds[i])) { return true; } } return false; } static int I_PCS_StartSound(sfxinfo_t *sfxinfo, int channel, int vol, int sep) { int result; if (!pcs_initialized) { return -1; } if (IsDisabledSound(sfxinfo)) { return -1; } if (SDL_LockMutex(sound_lock) < 0) { return -1; } result = CachePCSLump(sfxinfo); if (result) { current_sound_handle = channel; } SDL_UnlockMutex(sound_lock); if (result) { return channel; } else { return -1; } } static void I_PCS_StopSound(int handle) { if (!pcs_initialized) { return; } if (SDL_LockMutex(sound_lock) < 0) { return; } // If this is the channel currently playing, immediately end it. if (current_sound_handle == handle) { current_sound_remaining = 0; } SDL_UnlockMutex(sound_lock); } // // Retrieve the raw data lump index // for a given SFX name. // static int I_PCS_GetSfxLumpNum(sfxinfo_t* sfx) { char namebuf[9]; if (use_sfx_prefix) { M_snprintf(namebuf, sizeof(namebuf), "dp%s", DEH_String(sfx->name)); } else { M_StringCopy(namebuf, DEH_String(sfx->name), sizeof(namebuf)); } return W_GetNumForName(namebuf); } static boolean I_PCS_SoundIsPlaying(int handle) { if (!pcs_initialized) { return false; } if (handle != current_sound_handle) { return false; } return current_sound_lump != NULL && current_sound_remaining > 0; } static boolean I_PCS_InitSound(boolean _use_sfx_prefix) { use_sfx_prefix = _use_sfx_prefix; // Use the sample rate from the configuration file PCSound_SetSampleRate(snd_samplerate); // Initialize the PC speaker subsystem. pcs_initialized = PCSound_Init(PCSCallbackFunc); if (pcs_initialized) { sound_lock = SDL_CreateMutex(); } return pcs_initialized; } static void I_PCS_ShutdownSound(void) { if (pcs_initialized) { PCSound_Shutdown(); } } static void I_PCS_UpdateSound(void) { // no-op. } void I_PCS_UpdateSoundParams(int channel, int vol, int sep) { // no-op. } static snddevice_t sound_pcsound_devices[] = { SNDDEVICE_PCSPEAKER, }; sound_module_t sound_pcsound_module = { sound_pcsound_devices, arrlen(sound_pcsound_devices), I_PCS_InitSound, I_PCS_ShutdownSound, I_PCS_GetSfxLumpNum, I_PCS_UpdateSound, I_PCS_UpdateSoundParams, I_PCS_StartSound, I_PCS_StopSound, I_PCS_SoundIsPlaying, }; ================================================ FILE: fbdoom/i_scale.c ================================================ // // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // 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. // // DESCRIPTION: // Screen scale-up code: // 1x,2x,3x,4x pixel doubling // Aspect ratio-correcting stretch functions // #include #include #include #include "doomtype.h" #include "i_video.h" #include "m_argv.h" #include "z_zone.h" #if defined(_MSC_VER) && !defined(__cplusplus) #define inline __inline #endif // Should be I_VideoBuffer static byte *src_buffer; // Destination buffer, ie. screen->pixels. static byte *dest_buffer; // Pitch of destination buffer, ie. screen->pitch. static int dest_pitch; // Lookup tables used for aspect ratio correction stretching code. // stretch_tables[0] : 20% / 80% // stretch_tables[1] : 40% / 60% // All other combinations can be reached from these two tables. static byte *stretch_tables[2] = { NULL, NULL }; // 50%/50% stretch table, for 800x600 squash mode static byte *half_stretch_table = NULL; // Called to set the source and destination buffers before doing the // scale. void I_InitScale(byte *_src_buffer, byte *_dest_buffer, int _dest_pitch) { src_buffer = _src_buffer; dest_buffer = _dest_buffer; dest_pitch = _dest_pitch; } // // Pixel doubling scale-up functions. // // 1x scale doesn't really do any scaling: it just copies the buffer // a line at a time for when pitch != SCREENWIDTH (!native_surface) static boolean I_Scale1x(int x1, int y1, int x2, int y2) { byte *bufp, *screenp; int y; int w = x2 - x1; // Need to byte-copy from buffer into the screen buffer bufp = src_buffer + y1 * SCREENWIDTH + x1; screenp = (byte *) dest_buffer + y1 * dest_pitch + x1; for (y=y1; y 240) for (y=0; y 480) for (y=0; y 720) for (y=0; y 960) for (y=0; y 1200) for (y=0; y 0) { screenp = (byte *) dest_buffer + 2 * dest_pitch; for (y=0; y<1198; y += 3) { memset(screenp, 0, 1600); screenp += dest_pitch * 3; } } return true; } screen_mode_t mode_stretch_5x = { SCREENWIDTH * 5, SCREENHEIGHT_4_3 * 5, I_InitStretchTables, I_Stretch5x, false, }; // // Aspect ratio correcting "squash" functions. // // These do the opposite of the "stretch" functions above: while the // stretch functions increase the vertical dimensions, the squash // functions decrease the horizontal dimensions for the same result. // // The same blend tables from the stretch functions are reused; as // a result, the dimensions are *slightly* wrong (eg. 320x200 should // squash to 266x200, but actually squashes to 256x200). // // // 1x squashed scale (256x200) // static inline void WriteSquashedLine1x(byte *dest, byte *src) { int x; for (x=0; x multiples of 320x240) extern screen_mode_t mode_stretch_1x; extern screen_mode_t mode_stretch_2x; extern screen_mode_t mode_stretch_3x; extern screen_mode_t mode_stretch_4x; extern screen_mode_t mode_stretch_5x; // Horizontally squashed modes (320x200 -> multiples of 256x200) extern screen_mode_t mode_squash_1x; extern screen_mode_t mode_squash_2x; extern screen_mode_t mode_squash_3x; extern screen_mode_t mode_squash_4x; extern screen_mode_t mode_squash_5x; #endif /* #ifndef __I_SCALE__ */ ================================================ FILE: fbdoom/i_sdlmusic.c ================================================ // // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // 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. // // DESCRIPTION: // System interface for music. // #include #include #include #include "SDL.h" #include "SDL_mixer.h" #include "config.h" #include "doomtype.h" #include "memio.h" #include "mus2mid.h" #include "deh_str.h" #include "gusconf.h" #include "i_sound.h" #include "i_system.h" #include "i_swap.h" #include "m_argv.h" #include "m_config.h" #include "m_misc.h" #include "sha1.h" #include "w_wad.h" #include "z_zone.h" #define MAXMIDLENGTH (96 * 1024) #define MID_HEADER_MAGIC "MThd" #define MUS_HEADER_MAGIC "MUS\x1a" #define FLAC_HEADER "fLaC" #define OGG_HEADER "OggS" // Looping Vorbis metadata tag names. These have been defined by ZDoom // for specifying the start and end positions for looping music tracks // in .ogg and .flac files. // More information is here: http://zdoom.org/wiki/Audio_loop #define LOOP_START_TAG "LOOP_START" #define LOOP_END_TAG "LOOP_END" // FLAC metadata headers that we care about. #define FLAC_STREAMINFO 0 #define FLAC_VORBIS_COMMENT 4 // Ogg metadata headers that we care about. #define OGG_ID_HEADER 1 #define OGG_COMMENT_HEADER 3 // Structure for music substitution. // We store a mapping based on SHA1 checksum -> filename of substitute music // file to play, so that substitution occurs based on content rather than // lump name. This has some inherent advantages: // * Music for Plutonia (reused from Doom 1) works automatically. // * If a PWAD replaces music, the replacement music is used rather than // the substitute music for the IWAD. // * If a PWAD reuses music from an IWAD (even from a different game), we get // the high quality version of the music automatically (neat!) typedef struct { sha1_digest_t hash; char *filename; } subst_music_t; // Structure containing parsed metadata read from a digital music track: typedef struct { boolean valid; unsigned int samplerate_hz; int start_time, end_time; } file_metadata_t; static subst_music_t *subst_music = NULL; static unsigned int subst_music_len = 0; static const char *subst_config_filenames[] = { "doom1-music.cfg", "doom2-music.cfg", "tnt-music.cfg", "heretic-music.cfg", "hexen-music.cfg", "strife-music.cfg", }; static boolean music_initialized = false; // If this is true, this module initialized SDL sound and has the // responsibility to shut it down static boolean sdl_was_initialized = false; static boolean musicpaused = false; static int current_music_volume; char *timidity_cfg_path = ""; static char *temp_timidity_cfg = NULL; // If true, we are playing a substitute digital track rather than in-WAD // MIDI/MUS track, and file_metadata contains loop metadata. static boolean playing_substitute = false; static file_metadata_t file_metadata; // Position (in samples) that we have reached in the current track. // This is updated by the TrackPositionCallback function. static unsigned int current_track_pos; // Currently playing music track. static Mix_Music *current_track_music = NULL; // If true, the currently playing track is being played on loop. static boolean current_track_loop; // Given a time string (for LOOP_START/LOOP_END), parse it and return // the time (in # samples since start of track) it represents. static unsigned int ParseVorbisTime(unsigned int samplerate_hz, char *value) { char *num_start, *p; unsigned int result = 0; char c; if (strchr(value, ':') == NULL) { return atoi(value); } result = 0; num_start = value; for (p = value; *p != '\0'; ++p) { if (*p == '.' || *p == ':') { c = *p; *p = '\0'; result = result * 60 + atoi(num_start); num_start = p + 1; *p = c; } if (*p == '.') { return result * samplerate_hz + (unsigned int) (atof(p) * samplerate_hz); } } return (result * 60 + atoi(num_start)) * samplerate_hz; } // Given a vorbis comment string (eg. "LOOP_START=12345"), set fields // in the metadata structure as appropriate. static void ParseVorbisComment(file_metadata_t *metadata, char *comment) { char *eq, *key, *value; eq = strchr(comment, '='); if (eq == NULL) { return; } key = comment; *eq = '\0'; value = eq + 1; if (!strcmp(key, LOOP_START_TAG)) { metadata->start_time = ParseVorbisTime(metadata->samplerate_hz, value); } else if (!strcmp(key, LOOP_END_TAG)) { metadata->end_time = ParseVorbisTime(metadata->samplerate_hz, value); } } // Parse a vorbis comments structure, reading from the given file. static void ParseVorbisComments(file_metadata_t *metadata, FILE *fs) { uint32_t buf; unsigned int num_comments, i, comment_len; char *comment; // We must have read the sample rate already from an earlier header. if (metadata->samplerate_hz == 0) { return; } // Skip the starting part we don't care about. if (fread(&buf, 4, 1, fs) < 1) { return; } if (fseek(fs, LONG(buf), SEEK_CUR) != 0) { return; } // Read count field for number of comments. if (fread(&buf, 4, 1, fs) < 1) { return; } num_comments = LONG(buf); // Read each individual comment. for (i = 0; i < num_comments; ++i) { // Read length of comment. if (fread(&buf, 4, 1, fs) < 1) { return; } comment_len = LONG(buf); // Read actual comment data into string buffer. comment = calloc(1, comment_len + 1); if (comment == NULL || fread(comment, 1, comment_len, fs) < comment_len) { free(comment); break; } // Parse comment string. ParseVorbisComment(metadata, comment); free(comment); } } static void ParseFlacStreaminfo(file_metadata_t *metadata, FILE *fs) { byte buf[34]; // Read block data. if (fread(buf, sizeof(buf), 1, fs) < 1) { return; } // We only care about sample rate and song length. metadata->samplerate_hz = (buf[10] << 12) | (buf[11] << 4) | (buf[12] >> 4); // Song length is actually a 36 bit field, but 32 bits should be // enough for everybody. //metadata->song_length = (buf[14] << 24) | (buf[15] << 16) // | (buf[16] << 8) | buf[17]; } static void ParseFlacFile(file_metadata_t *metadata, FILE *fs) { byte header[4]; unsigned int block_type; size_t block_len; boolean last_block; for (;;) { long pos = -1; // Read METADATA_BLOCK_HEADER: if (fread(header, 4, 1, fs) < 1) { return; } block_type = header[0] & ~0x80; last_block = (header[0] & 0x80) != 0; block_len = (header[1] << 16) | (header[2] << 8) | header[3]; pos = ftell(fs); if (pos < 0) { return; } if (block_type == FLAC_STREAMINFO) { ParseFlacStreaminfo(metadata, fs); } else if (block_type == FLAC_VORBIS_COMMENT) { ParseVorbisComments(metadata, fs); } if (last_block) { break; } // Seek to start of next block. if (fseek(fs, pos + block_len, SEEK_SET) != 0) { return; } } } static void ParseOggIdHeader(file_metadata_t *metadata, FILE *fs) { byte buf[21]; if (fread(buf, sizeof(buf), 1, fs) < 1) { return; } metadata->samplerate_hz = (buf[8] << 24) | (buf[7] << 16) | (buf[6] << 8) | buf[5]; } static void ParseOggFile(file_metadata_t *metadata, FILE *fs) { byte buf[7]; unsigned int offset; // Scan through the start of the file looking for headers. They // begin '[byte]vorbis' where the byte value indicates header type. memset(buf, 0, sizeof(buf)); for (offset = 0; offset < 100 * 1024; ++offset) { // buf[] is used as a sliding window. Each iteration, we // move the buffer one byte to the left and read an extra // byte onto the end. memmove(buf, buf + 1, sizeof(buf) - 1); if (fread(&buf[6], 1, 1, fs) < 1) { return; } if (!memcmp(buf + 1, "vorbis", 6)) { switch (buf[0]) { case OGG_ID_HEADER: ParseOggIdHeader(metadata, fs); break; case OGG_COMMENT_HEADER: ParseVorbisComments(metadata, fs); break; default: break; } } } } static void ReadLoopPoints(char *filename, file_metadata_t *metadata) { FILE *fs; char header[4]; metadata->valid = false; metadata->samplerate_hz = 0; metadata->start_time = 0; metadata->end_time = -1; fs = fopen(filename, "r"); if (fs == NULL) { return; } // Check for a recognized file format; use the first four bytes // of the file. if (fread(header, 4, 1, fs) < 1) { fclose(fs); return; } if (memcmp(header, FLAC_HEADER, 4) == 0) { ParseFlacFile(metadata, fs); } else if (memcmp(header, OGG_HEADER, 4) == 0) { ParseOggFile(metadata, fs); } fclose(fs); // Only valid if at the very least we read the sample rate. metadata->valid = metadata->samplerate_hz > 0; } // Given a MUS lump, look up a substitute MUS file to play instead // (or NULL to just use normal MIDI playback). static char *GetSubstituteMusicFile(void *data, size_t data_len) { sha1_context_t context; sha1_digest_t hash; char *filename; int i; // Don't bother doing a hash if we're never going to find anything. if (subst_music_len == 0) { return NULL; } SHA1_Init(&context); SHA1_Update(&context, data, data_len); SHA1_Final(hash, &context); // Look for a hash that matches. // The substitute mapping list can (intentionally) contain multiple // filename mappings for the same hash. This allows us to try // different files and fall back if our first choice isn't found. filename = NULL; for (i = 0; i < subst_music_len; ++i) { if (memcmp(hash, subst_music[i].hash, sizeof(hash)) == 0) { filename = subst_music[i].filename; // If the file exists, then use this file in preference to // any fallbacks. But we always return a filename if it's // in the list, even if it's just so we can print an error // message to the user saying it doesn't exist. if (M_FileExists(filename)) { break; } } } return filename; } // Add a substitute music file to the lookup list. static void AddSubstituteMusic(subst_music_t *subst) { ++subst_music_len; subst_music = realloc(subst_music, sizeof(subst_music_t) * subst_music_len); memcpy(&subst_music[subst_music_len - 1], subst, sizeof(subst_music_t)); } static int ParseHexDigit(char c) { c = tolower(c); if (c >= '0' && c <= '9') { return c - '0'; } else if (c >= 'a' && c <= 'f') { return 10 + (c - 'a'); } else { return -1; } } static char *GetFullPath(char *base_filename, char *path) { char *basedir, *result; char *p; // Starting with directory separator means we have an absolute path, // so just return it. if (path[0] == DIR_SEPARATOR) { return strdup(path); } #ifdef _WIN32 // d:\path\... if (isalpha(path[0]) && path[1] == ':' && path[2] == DIR_SEPARATOR) { return strdup(path); } #endif // Paths in the substitute filenames can contain Unix-style / // path separators, but we should convert this to the separator // for the native platform. path = M_StringReplace(path, "/", DIR_SEPARATOR_S); // Copy config filename and cut off the filename to just get the // parent dir. basedir = strdup(base_filename); p = strrchr(basedir, DIR_SEPARATOR); if (p != NULL) { p[1] = '\0'; result = M_StringJoin(basedir, path, NULL); } else { result = strdup(path); } free(basedir); free(path); return result; } // Parse a line from substitute music configuration file; returns error // message or NULL for no error. static char *ParseSubstituteLine(char *filename, char *line) { subst_music_t subst; char *p; int hash_index; // Strip out comments if present. p = strchr(line, '#'); if (p != NULL) { while (p > line && isspace(*(p - 1))) { --p; } *p = '\0'; } // Skip leading spaces. for (p = line; *p != '\0' && isspace(*p); ++p); // Empty line? This includes comment lines now that comments have // been stripped. if (*p == '\0') { return NULL; } // Read hash. hash_index = 0; while (*p != '\0' && *p != '=' && !isspace(*p)) { int d1, d2; d1 = ParseHexDigit(p[0]); d2 = ParseHexDigit(p[1]); if (d1 < 0 || d2 < 0) { return "Invalid hex digit in SHA1 hash"; } else if (hash_index >= sizeof(sha1_digest_t)) { return "SHA1 hash too long"; } subst.hash[hash_index] = (d1 << 4) | d2; ++hash_index; p += 2; } if (hash_index != sizeof(sha1_digest_t)) { return "SHA1 hash too short"; } // Skip spaces. for (; *p != '\0' && isspace(*p); ++p); if (*p != '=') { return "Expected '='"; } ++p; // Skip spaces. for (; *p != '\0' && isspace(*p); ++p); // We're now at the filename. Cut off trailing space characters. while (strlen(p) > 0 && isspace(p[strlen(p) - 1])) { p[strlen(p) - 1] = '\0'; } if (strlen(p) == 0) { return "No filename specified for music substitution"; } // Expand full path and add to our database of substitutes. subst.filename = GetFullPath(filename, p); AddSubstituteMusic(&subst); return NULL; } // Read a substitute music configuration file. static boolean ReadSubstituteConfig(char *filename) { char line[128]; FILE *fs; char *error; int linenum = 1; int old_subst_music_len; fs = fopen(filename, "r"); if (fs == NULL) { return false; } old_subst_music_len = subst_music_len; while (!feof(fs)) { M_StringCopy(line, "", sizeof(line)); fgets(line, sizeof(line), fs); error = ParseSubstituteLine(filename, line); if (error != NULL) { fprintf(stderr, "%s:%i: Error: %s\n", filename, linenum, error); } ++linenum; } fclose(fs); return true; } // Find substitute configs and try to load them. static void LoadSubstituteConfigs(void) { char *musicdir; char *path; unsigned int i; if (!strcmp(configdir, "")) { musicdir = strdup(""); } else { musicdir = M_StringJoin(configdir, "music", DIR_SEPARATOR_S, NULL); } // Load all music packs. We always load all music substitution packs for // all games. Why? Suppose we have a Doom PWAD that reuses some music from // Heretic. If we have the Heretic music pack loaded, then we get an // automatic substitution. for (i = 0; i < arrlen(subst_config_filenames); ++i) { path = M_StringJoin(musicdir, subst_config_filenames[i], NULL); ReadSubstituteConfig(path); free(path); } free(musicdir); if (subst_music_len > 0) { printf("Loaded %i music substitutions from config files.\n", subst_music_len); } } // Returns true if the given lump number is a music lump that should // be included in substitute configs. // Identifying music lumps by name is not feasible; some games (eg. // Heretic, Hexen) don't have a common naming pattern for music lumps. static boolean IsMusicLump(int lumpnum) { byte *data; boolean result; if (W_LumpLength(lumpnum) < 4) { return false; } data = W_CacheLumpNum(lumpnum, PU_STATIC); result = memcmp(data, MUS_HEADER_MAGIC, 4) == 0 || memcmp(data, MID_HEADER_MAGIC, 4) == 0; W_ReleaseLumpNum(lumpnum); return result; } // Dump an example config file containing checksums for all MIDI music // found in the WAD directory. static void DumpSubstituteConfig(char *filename) { sha1_context_t context; sha1_digest_t digest; char name[9]; byte *data; FILE *fs; int lumpnum, h; fs = fopen(filename, "w"); if (fs == NULL) { I_Error("Failed to open %s for writing", filename); return; } fprintf(fs, "# Example %s substitute MIDI file.\n\n", PACKAGE_NAME); fprintf(fs, "# SHA1 hash = filename\n"); for (lumpnum = 0; lumpnum < numlumps; ++lumpnum) { strncpy(name, lumpinfo[lumpnum].name, 8); name[8] = '\0'; if (!IsMusicLump(lumpnum)) { continue; } // Calculate hash. data = W_CacheLumpNum(lumpnum, PU_STATIC); SHA1_Init(&context); SHA1_Update(&context, data, W_LumpLength(lumpnum)); SHA1_Final(digest, &context); W_ReleaseLumpNum(lumpnum); // Print line. for (h = 0; h < sizeof(sha1_digest_t); ++h) { fprintf(fs, "%02x", digest[h]); } fprintf(fs, " = %s.ogg\n", name); } fprintf(fs, "\n"); fclose(fs); printf("Substitute MIDI config file written to %s.\n", filename); I_Quit(); } // If the temp_timidity_cfg config variable is set, generate a "wrapper" // config file for Timidity to point to the actual config file. This // is needed to inject a "dir" command so that the patches are read // relative to the actual config file. static boolean WriteWrapperTimidityConfig(char *write_path) { char *p, *path; FILE *fstream; if (!strcmp(timidity_cfg_path, "")) { return false; } fstream = fopen(write_path, "w"); if (fstream == NULL) { return false; } p = strrchr(timidity_cfg_path, DIR_SEPARATOR); if (p != NULL) { path = strdup(timidity_cfg_path); path[p - timidity_cfg_path] = '\0'; fprintf(fstream, "dir %s\n", path); free(path); } fprintf(fstream, "source %s\n", timidity_cfg_path); fclose(fstream); return true; } void I_InitTimidityConfig(void) { char *env_string; boolean success; temp_timidity_cfg = M_TempFile("timidity.cfg"); if (snd_musicdevice == SNDDEVICE_GUS) { success = GUS_WriteConfig(temp_timidity_cfg); } else { success = WriteWrapperTimidityConfig(temp_timidity_cfg); } // Set the TIMIDITY_CFG environment variable to point to the temporary // config file. if (success) { env_string = M_StringJoin("TIMIDITY_CFG=", temp_timidity_cfg, NULL); putenv(env_string); } else { free(temp_timidity_cfg); temp_timidity_cfg = NULL; } } // Remove the temporary config file generated by I_InitTimidityConfig(). static void RemoveTimidityConfig(void) { if (temp_timidity_cfg != NULL) { remove(temp_timidity_cfg); free(temp_timidity_cfg); } } // Shutdown music static void I_SDL_ShutdownMusic(void) { if (music_initialized) { Mix_HaltMusic(); music_initialized = false; if (sdl_was_initialized) { Mix_CloseAudio(); SDL_QuitSubSystem(SDL_INIT_AUDIO); sdl_was_initialized = false; } } } static boolean SDLIsInitialized(void) { int freq, channels; Uint16 format; return Mix_QuerySpec(&freq, &format, &channels) != 0; } // Callback function that is invoked to track current track position. void TrackPositionCallback(int chan, void *stream, int len, void *udata) { // Position is doubled up twice: for 16-bit samples and for stereo. current_track_pos += len / 4; } // Initialize music subsystem static boolean I_SDL_InitMusic(void) { int i; // SDL_mixer prior to v1.2.11 has a bug that causes crashes // with MIDI playback. Print a warning message if we are // using an old version. #ifdef __MACOSX__ { const SDL_version *v = Mix_Linked_Version(); if (SDL_VERSIONNUM(v->major, v->minor, v->patch) < SDL_VERSIONNUM(1, 2, 11)) { printf("\n" " *** WARNING ***\n" " You are using an old version of SDL_mixer.\n" " Music playback on this version may cause crashes\n" " under OS X and is disabled by default.\n" "\n"); } } #endif //! // @arg // // Read all MIDI files from loaded WAD files, dump an example substitution // music config file to the specified filename and quit. // i = M_CheckParmWithArgs("-dumpsubstconfig", 1); if (i > 0) { DumpSubstituteConfig(myargv[i + 1]); } // If SDL_mixer is not initialized, we have to initialize it // and have the responsibility to shut it down later on. if (SDLIsInitialized()) { music_initialized = true; } else { if (SDL_Init(SDL_INIT_AUDIO) < 0) { fprintf(stderr, "Unable to set up sound.\n"); } else if (Mix_OpenAudio(snd_samplerate, AUDIO_S16SYS, 2, 1024) < 0) { fprintf(stderr, "Error initializing SDL_mixer: %s\n", Mix_GetError()); SDL_QuitSubSystem(SDL_INIT_AUDIO); } else { SDL_PauseAudio(0); sdl_was_initialized = true; music_initialized = true; } } // Once initialization is complete, the temporary Timidity config // file can be removed. RemoveTimidityConfig(); // If snd_musiccmd is set, we need to call Mix_SetMusicCMD to // configure an external music playback program. if (strlen(snd_musiccmd) > 0) { Mix_SetMusicCMD(snd_musiccmd); } // Register an effect function to track the music position. Mix_RegisterEffect(MIX_CHANNEL_POST, TrackPositionCallback, NULL, NULL); // If we're in GENMIDI mode, try to load sound packs. if (snd_musicdevice == SNDDEVICE_GENMIDI) { LoadSubstituteConfigs(); } return music_initialized; } // // SDL_mixer's native MIDI music playing does not pause properly. // As a workaround, set the volume to 0 when paused. // static void UpdateMusicVolume(void) { int vol; if (musicpaused) { vol = 0; } else { vol = (current_music_volume * MIX_MAX_VOLUME) / 127; } Mix_VolumeMusic(vol); } // Set music volume (0 - 127) static void I_SDL_SetMusicVolume(int volume) { // Internal state variable. current_music_volume = volume; UpdateMusicVolume(); } // Start playing a mid static void I_SDL_PlaySong(void *handle, boolean looping) { int loops; if (!music_initialized) { return; } if (handle == NULL) { return; } current_track_music = (Mix_Music *) handle; current_track_loop = looping; if (looping) { loops = -1; } else { loops = 1; } // Don't loop when playing substitute music, as we do it // ourselves instead. if (playing_substitute && file_metadata.valid) { loops = 1; SDL_LockAudio(); current_track_pos = 0; // start of track SDL_UnlockAudio(); } Mix_PlayMusic(current_track_music, loops); } static void I_SDL_PauseSong(void) { if (!music_initialized) { return; } musicpaused = true; UpdateMusicVolume(); } static void I_SDL_ResumeSong(void) { if (!music_initialized) { return; } musicpaused = false; UpdateMusicVolume(); } static void I_SDL_StopSong(void) { if (!music_initialized) { return; } Mix_HaltMusic(); playing_substitute = false; current_track_music = NULL; } static void I_SDL_UnRegisterSong(void *handle) { Mix_Music *music = (Mix_Music *) handle; if (!music_initialized) { return; } if (handle == NULL) { return; } Mix_FreeMusic(music); } // Determine whether memory block is a .mid file static boolean IsMid(byte *mem, int len) { return len > 4 && !memcmp(mem, "MThd", 4); } static boolean ConvertMus(byte *musdata, int len, char *filename) { MEMFILE *instream; MEMFILE *outstream; void *outbuf; size_t outbuf_len; int result; instream = mem_fopen_read(musdata, len); outstream = mem_fopen_write(); result = mus2mid(instream, outstream); if (result == 0) { mem_get_buf(outstream, &outbuf, &outbuf_len); M_WriteFile(filename, outbuf, outbuf_len); } mem_fclose(instream); mem_fclose(outstream); return result; } static void *I_SDL_RegisterSong(void *data, int len) { char *filename; Mix_Music *music; if (!music_initialized) { return NULL; } playing_substitute = false; // See if we're substituting this MUS for a high-quality replacement. filename = GetSubstituteMusicFile(data, len); if (filename != NULL) { music = Mix_LoadMUS(filename); if (music == NULL) { // Fall through and play MIDI normally, but print an error // message. fprintf(stderr, "Failed to load substitute music file: %s: %s\n", filename, Mix_GetError()); } else { // Read loop point metadata from the file so that we know where // to loop the music. playing_substitute = true; ReadLoopPoints(filename, &file_metadata); return music; } } // MUS files begin with "MUS" // Reject anything which doesnt have this signature filename = M_TempFile("doom.mid"); if (IsMid(data, len) && len < MAXMIDLENGTH) { M_WriteFile(filename, data, len); } else { // Assume a MUS file and try to convert ConvertMus(data, len, filename); } // Load the MIDI. In an ideal world we'd be using Mix_LoadMUS_RW() // by now, but Mix_SetMusicCMD() only works with Mix_LoadMUS(), so // we have to generate a temporary file. music = Mix_LoadMUS(filename); if (music == NULL) { // Failed to load fprintf(stderr, "Error loading midi: %s\n", Mix_GetError()); } // Remove the temporary MIDI file; however, when using an external // MIDI program we can't delete the file. Otherwise, the program // won't find the file to play. This means we leave a mess on // disk :( if (strlen(snd_musiccmd) == 0) { remove(filename); } free(filename); return music; } // Is the song playing? static boolean I_SDL_MusicIsPlaying(void) { if (!music_initialized) { return false; } return Mix_PlayingMusic(); } // Get position in substitute music track, in seconds since start of track. static double GetMusicPosition(void) { unsigned int music_pos; int freq; Mix_QuerySpec(&freq, NULL, NULL); SDL_LockAudio(); music_pos = current_track_pos; SDL_UnlockAudio(); return (double) music_pos / freq; } static void RestartCurrentTrack(void) { double start = (double) file_metadata.start_time / file_metadata.samplerate_hz; // If the track is playing on loop then reset to the start point. // Otherwise we need to stop the track. if (current_track_loop) { // If the track finished we need to restart it. if (current_track_music != NULL) { Mix_PlayMusic(current_track_music, 1); } Mix_SetMusicPosition(start); SDL_LockAudio(); current_track_pos = file_metadata.start_time; SDL_UnlockAudio(); } else { Mix_HaltMusic(); current_track_music = NULL; playing_substitute = false; } } // Poll music position; if we have passed the loop point end position // then we need to go back. static void I_SDL_PollMusic(void) { if (playing_substitute && file_metadata.valid) { double end = (double) file_metadata.end_time / file_metadata.samplerate_hz; // If we have reached the loop end point then we have to take action. if (file_metadata.end_time >= 0 && GetMusicPosition() >= end) { RestartCurrentTrack(); } // Have we reached the actual end of track (not loop end)? if (!Mix_PlayingMusic() && current_track_loop) { RestartCurrentTrack(); } } } static snddevice_t music_sdl_devices[] = { SNDDEVICE_PAS, SNDDEVICE_GUS, SNDDEVICE_WAVEBLASTER, SNDDEVICE_SOUNDCANVAS, SNDDEVICE_GENMIDI, SNDDEVICE_AWE32, }; music_module_t music_sdl_module = { music_sdl_devices, arrlen(music_sdl_devices), I_SDL_InitMusic, I_SDL_ShutdownMusic, I_SDL_SetMusicVolume, I_SDL_PauseSong, I_SDL_ResumeSong, I_SDL_RegisterSong, I_SDL_UnRegisterSong, I_SDL_PlaySong, I_SDL_StopSong, I_SDL_MusicIsPlaying, I_SDL_PollMusic, }; ================================================ FILE: fbdoom/i_sdlsound.c ================================================ // // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // Copyright(C) 2008 David Flater // // 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. // // DESCRIPTION: // System interface for sound. // #include "config.h" #include #include #include #include #include "SDL.h" #include "SDL_mixer.h" #ifdef HAVE_LIBSAMPLERATE #include #endif #include "deh_str.h" #include "i_sound.h" #include "i_system.h" #include "i_swap.h" #include "m_argv.h" #include "m_misc.h" #include "w_wad.h" #include "z_zone.h" #include "doomtype.h" #define LOW_PASS_FILTER //#define DEBUG_DUMP_WAVS #define NUM_CHANNELS 16 typedef struct allocated_sound_s allocated_sound_t; struct allocated_sound_s { sfxinfo_t *sfxinfo; Mix_Chunk chunk; int use_count; allocated_sound_t *prev, *next; }; static boolean setpanning_workaround = false; static boolean sound_initialized = false; static sfxinfo_t *channels_playing[NUM_CHANNELS]; static int mixer_freq; static Uint16 mixer_format; static int mixer_channels; static boolean use_sfx_prefix; static boolean (*ExpandSoundData)(sfxinfo_t *sfxinfo, byte *data, int samplerate, int length) = NULL; // Doubly-linked list of allocated sounds. // When a sound is played, it is moved to the head, so that the oldest // sounds not used recently are at the tail. static allocated_sound_t *allocated_sounds_head = NULL; static allocated_sound_t *allocated_sounds_tail = NULL; static int allocated_sounds_size = 0; int use_libsamplerate = 0; // Scale factor used when converting libsamplerate floating point numbers // to integers. Too high means the sounds can clip; too low means they // will be too quiet. This is an amount that should avoid clipping most // of the time: with all the Doom IWAD sound effects, at least. If a PWAD // is used, clipping might occur. float libsamplerate_scale = 0.65f; // Hook a sound into the linked list at the head. static void AllocatedSoundLink(allocated_sound_t *snd) { snd->prev = NULL; snd->next = allocated_sounds_head; allocated_sounds_head = snd; if (allocated_sounds_tail == NULL) { allocated_sounds_tail = snd; } else { snd->next->prev = snd; } } // Unlink a sound from the linked list. static void AllocatedSoundUnlink(allocated_sound_t *snd) { if (snd->prev == NULL) { allocated_sounds_head = snd->next; } else { snd->prev->next = snd->next; } if (snd->next == NULL) { allocated_sounds_tail = snd->prev; } else { snd->next->prev = snd->prev; } } static void FreeAllocatedSound(allocated_sound_t *snd) { // Unlink from linked list. AllocatedSoundUnlink(snd); // Unlink from higher-level code. snd->sfxinfo->driver_data = NULL; // Keep track of the amount of allocated sound data: allocated_sounds_size -= snd->chunk.alen; free(snd); } // Search from the tail backwards along the allocated sounds list, find // and free a sound that is not in use, to free up memory. Return true // for success. static boolean FindAndFreeSound(void) { allocated_sound_t *snd; snd = allocated_sounds_tail; while (snd != NULL) { if (snd->use_count == 0) { FreeAllocatedSound(snd); return true; } snd = snd->prev; } // No available sounds to free... return false; } // Enforce SFX cache size limit. We are just about to allocate "len" // bytes on the heap for a new sound effect, so free up some space // so that we keep allocated_sounds_size < snd_cachesize static void ReserveCacheSpace(size_t len) { if (snd_cachesize <= 0) { return; } // Keep freeing sound effects that aren't currently being played, // until there is enough space for the new sound. while (allocated_sounds_size + len > snd_cachesize) { // Free a sound. If there is nothing more to free, stop. if (!FindAndFreeSound()) { break; } } } // Allocate a block for a new sound effect. static Mix_Chunk *AllocateSound(sfxinfo_t *sfxinfo, size_t len) { allocated_sound_t *snd; // Keep allocated sounds within the cache size. ReserveCacheSpace(len); // Allocate the sound structure and data. The data will immediately // follow the structure, which acts as a header. do { snd = malloc(sizeof(allocated_sound_t) + len); // Out of memory? Try to free an old sound, then loop round // and try again. if (snd == NULL && !FindAndFreeSound()) { return NULL; } } while (snd == NULL); // Skip past the chunk structure for the audio buffer snd->chunk.abuf = (byte *) (snd + 1); snd->chunk.alen = len; snd->chunk.allocated = 1; snd->chunk.volume = MIX_MAX_VOLUME; snd->sfxinfo = sfxinfo; snd->use_count = 0; // driver_data pointer points to the allocated_sound structure. sfxinfo->driver_data = snd; // Keep track of how much memory all these cached sounds are using... allocated_sounds_size += len; AllocatedSoundLink(snd); return &snd->chunk; } // Lock a sound, to indicate that it may not be freed. static void LockAllocatedSound(allocated_sound_t *snd) { // Increase use count, to stop the sound being freed. ++snd->use_count; //printf("++ %s: Use count=%i\n", snd->sfxinfo->name, snd->use_count); // When we use a sound, re-link it into the list at the head, so // that the oldest sounds fall to the end of the list for freeing. AllocatedSoundUnlink(snd); AllocatedSoundLink(snd); } // Unlock a sound to indicate that it may now be freed. static void UnlockAllocatedSound(allocated_sound_t *snd) { if (snd->use_count <= 0) { I_Error("Sound effect released more times than it was locked..."); } --snd->use_count; //printf("-- %s: Use count=%i\n", snd->sfxinfo->name, snd->use_count); } // When a sound stops, check if it is still playing. If it is not, // we can mark the sound data as CACHE to be freed back for other // means. static void ReleaseSoundOnChannel(int channel) { sfxinfo_t *sfxinfo = channels_playing[channel]; if (sfxinfo == NULL) { return; } channels_playing[channel] = NULL; UnlockAllocatedSound(sfxinfo->driver_data); } #ifdef HAVE_LIBSAMPLERATE // Returns the conversion mode for libsamplerate to use. static int SRC_ConversionMode(void) { switch (use_libsamplerate) { // 0 = disabled default: case 0: return -1; // Ascending numbers give higher quality case 1: return SRC_LINEAR; case 2: return SRC_ZERO_ORDER_HOLD; case 3: return SRC_SINC_FASTEST; case 4: return SRC_SINC_MEDIUM_QUALITY; case 5: return SRC_SINC_BEST_QUALITY; } } // libsamplerate-based generic sound expansion function for any sample rate // unsigned 8 bits --> signed 16 bits // mono --> stereo // samplerate --> mixer_freq // Returns number of clipped samples. // DWF 2008-02-10 with cleanups by Simon Howard. static boolean ExpandSoundData_SRC(sfxinfo_t *sfxinfo, byte *data, int samplerate, int length) { SRC_DATA src_data; uint32_t i, abuf_index=0, clipped=0; uint32_t alen; int retn; int16_t *expanded; Mix_Chunk *chunk; src_data.input_frames = length; src_data.data_in = malloc(length * sizeof(float)); src_data.src_ratio = (double)mixer_freq / samplerate; // We include some extra space here in case of rounding-up. src_data.output_frames = src_data.src_ratio * length + (mixer_freq / 4); src_data.data_out = malloc(src_data.output_frames * sizeof(float)); assert(src_data.data_in != NULL && src_data.data_out != NULL); // Convert input data to floats for (i=0; iabuf; // Convert the result back into 16-bit integers. for (i=0; i INT16_MAX) { cvtval_i = INT16_MAX; ++clipped; } // Left and right channels expanded[abuf_index++] = cvtval_i; expanded[abuf_index++] = cvtval_i; } free(src_data.data_in); free(src_data.data_out); if (clipped > 0) { fprintf(stderr, "Sound '%s': clipped %u samples (%0.2f %%)\n", sfxinfo->name, clipped, 400.0 * clipped / chunk->alen); } return true; } #endif static boolean ConvertibleRatio(int freq1, int freq2) { int ratio; if (freq1 > freq2) { return ConvertibleRatio(freq2, freq1); } else if ((freq2 % freq1) != 0) { // Not in a direct ratio return false; } else { // Check the ratio is a power of 2 ratio = freq2 / freq1; while ((ratio & 1) == 0) { ratio = ratio >> 1; } return ratio == 1; } } #ifdef DEBUG_DUMP_WAVS // Debug code to dump resampled sound effects to WAV files for analysis. static void WriteWAV(char *filename, byte *data, uint32_t length, int samplerate) { FILE *wav; unsigned int i; unsigned short s; wav = fopen(filename, "wb"); // Header fwrite("RIFF", 1, 4, wav); i = LONG(36 + samplerate); fwrite(&i, 4, 1, wav); fwrite("WAVE", 1, 4, wav); // Subchunk 1 fwrite("fmt ", 1, 4, wav); i = LONG(16); fwrite(&i, 4, 1, wav); // Length s = SHORT(1); fwrite(&s, 2, 1, wav); // Format (PCM) s = SHORT(2); fwrite(&s, 2, 1, wav); // Channels (2=stereo) i = LONG(samplerate); fwrite(&i, 4, 1, wav); // Sample rate i = LONG(samplerate * 2 * 2); fwrite(&i, 4, 1, wav); // Byte rate (samplerate * stereo * 16 bit) s = SHORT(2 * 2); fwrite(&s, 2, 1, wav); // Block align (stereo * 16 bit) s = SHORT(16); fwrite(&s, 2, 1, wav); // Bits per sample (16 bit) // Data subchunk fwrite("data", 1, 4, wav); i = LONG(length); fwrite(&i, 4, 1, wav); // Data length fwrite(data, 1, length, wav); // Data fclose(wav); } #endif // Generic sound expansion function for any sample rate. // Returns number of clipped samples (always 0). static boolean ExpandSoundData_SDL(sfxinfo_t *sfxinfo, byte *data, int samplerate, int length) { SDL_AudioCVT convertor; Mix_Chunk *chunk; uint32_t expanded_length; // Calculate the length of the expanded version of the sample. expanded_length = (uint32_t) ((((uint64_t) length) * mixer_freq) / samplerate); // Double up twice: 8 -> 16 bit and mono -> stereo expanded_length *= 4; // Allocate a chunk in which to expand the sound chunk = AllocateSound(sfxinfo, expanded_length); if (chunk == NULL) { return false; } // If we can, use the standard / optimized SDL conversion routines. if (samplerate <= mixer_freq && ConvertibleRatio(samplerate, mixer_freq) && SDL_BuildAudioCVT(&convertor, AUDIO_U8, 1, samplerate, mixer_format, mixer_channels, mixer_freq)) { convertor.buf = chunk->abuf; convertor.len = length; memcpy(convertor.buf, data, length); SDL_ConvertAudio(&convertor); } else { Sint16 *expanded = (Sint16 *) chunk->abuf; int expanded_length; int expand_ratio; int i; // Generic expansion if conversion does not work: // // SDL's audio conversion only works for rate conversions that are // powers of 2; if the two formats are not in a direct power of 2 // ratio, do this naive conversion instead. // number of samples in the converted sound expanded_length = ((uint64_t) length * mixer_freq) / samplerate; expand_ratio = (length << 8) / expanded_length; for (i=0; i> 8; sample = data[src] | (data[src] << 8); sample -= 32768; // expand 8->16 bits, mono->stereo expanded[i * 2] = expanded[i * 2 + 1] = sample; } #ifdef LOW_PASS_FILTER // Perform a low-pass filter on the upscaled sound to filter // out high-frequency noise from the conversion process. { float rc, dt, alpha; // Low-pass filter for cutoff frequency f: // // For sampling rate r, dt = 1 / r // rc = 1 / 2*pi*f // alpha = dt / (rc + dt) // Filter to the half sample rate of the original sound effect // (maximum frequency, by nyquist) dt = 1.0f / mixer_freq; rc = 1.0f / (3.14f * samplerate); alpha = dt / (rc + dt); // Both channels are processed in parallel, hence [i-2]: for (i=2; ilumpnum; data = W_CacheLumpNum(lumpnum, PU_STATIC); lumplen = W_LumpLength(lumpnum); // Check the header, and ensure this is a valid sound if (lumplen < 8 || data[0] != 0x03 || data[1] != 0x00) { // Invalid sound return false; } // 16 bit sample rate field, 32 bit length field samplerate = (data[3] << 8) | data[2]; length = (data[7] << 24) | (data[6] << 16) | (data[5] << 8) | data[4]; // If the header specifies that the length of the sound is greater than // the length of the lump itself, this is an invalid sound lump // We also discard sound lumps that are less than 49 samples long, // as this is how DMX behaves - although the actual cut-off length // seems to vary slightly depending on the sample rate. This needs // further investigation to better understand the correct // behavior. if (length > lumplen - 8 || length <= 48) { return false; } // The DMX sound library seems to skip the first 16 and last 16 // bytes of the lump - reason unknown. data += 16; length -= 32; // Sample rate conversion if (!ExpandSoundData(sfxinfo, data + 8, samplerate, length)) { return false; } #ifdef DEBUG_DUMP_WAVS { char filename[16]; M_snprintf(filename, sizeof(filename), "%s.wav", DEH_String(S_sfx[sound].name)); WriteWAV(filename, sound_chunks[sound].abuf, sound_chunks[sound].alen, mixer_freq); } #endif // don't need the original lump any more W_ReleaseLumpNum(lumpnum); return true; } static void GetSfxLumpName(sfxinfo_t *sfx, char *buf, size_t buf_len) { // Linked sfx lumps? Get the lump number for the sound linked to. if (sfx->link != NULL) { sfx = sfx->link; } // Doom adds a DS* prefix to sound lumps; Heretic and Hexen don't // do this. if (use_sfx_prefix) { M_snprintf(buf, buf_len, "ds%s", DEH_String(sfx->name)); } else { M_StringCopy(buf, DEH_String(sfx->name), buf_len); } } #ifdef HAVE_LIBSAMPLERATE // Preload all the sound effects - stops nasty ingame freezes static void I_SDL_PrecacheSounds(sfxinfo_t *sounds, int num_sounds) { char namebuf[9]; int i; // Don't need to precache the sounds unless we are using libsamplerate. if (use_libsamplerate == 0) { return; } printf("I_SDL_PrecacheSounds: Precaching all sound effects.."); for (i=0; idriver_data == NULL) { if (!CacheSFX(sfxinfo)) { return false; } } LockAllocatedSound(sfxinfo->driver_data); return true; } // // Retrieve the raw data lump index // for a given SFX name. // static int I_SDL_GetSfxLumpNum(sfxinfo_t *sfx) { char namebuf[9]; GetSfxLumpName(sfx, namebuf, sizeof(namebuf)); return W_GetNumForName(namebuf); } static void I_SDL_UpdateSoundParams(int handle, int vol, int sep) { int left, right; if (!sound_initialized || handle < 0 || handle >= NUM_CHANNELS) { return; } left = ((254 - sep) * vol) / 127; right = ((sep) * vol) / 127; if (left < 0) left = 0; else if ( left > 255) left = 255; if (right < 0) right = 0; else if (right > 255) right = 255; // SDL_mixer version 1.2.8 and earlier has a bug in the Mix_SetPanning // function. A workaround is to call Mix_UnregisterAllEffects for // the channel before calling it. This is undesirable as it may lead // to the channel volumes resetting briefly. if (setpanning_workaround) { Mix_UnregisterAllEffects(handle); } Mix_SetPanning(handle, left, right); } // // 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. // static int I_SDL_StartSound(sfxinfo_t *sfxinfo, int channel, int vol, int sep) { allocated_sound_t *snd; if (!sound_initialized || channel < 0 || channel >= NUM_CHANNELS) { return -1; } // Release a sound effect if there is already one playing // on this channel ReleaseSoundOnChannel(channel); // Get the sound data if (!LockSound(sfxinfo)) { return -1; } snd = sfxinfo->driver_data; // play sound Mix_PlayChannelTimed(channel, &snd->chunk, 0, -1); channels_playing[channel] = sfxinfo; // set separation, etc. I_SDL_UpdateSoundParams(channel, vol, sep); return channel; } static void I_SDL_StopSound(int handle) { if (!sound_initialized || handle < 0 || handle >= NUM_CHANNELS) { return; } Mix_HaltChannel(handle); // Sound data is no longer needed; release the // sound data being used for this channel ReleaseSoundOnChannel(handle); } static boolean I_SDL_SoundIsPlaying(int handle) { if (!sound_initialized || handle < 0 || handle >= NUM_CHANNELS) { return false; } return Mix_Playing(handle); } // // Periodically called to update the sound system // static void I_SDL_UpdateSound(void) { int i; // Check all channels to see if a sound has finished for (i=0; i limit) { return (1 << n); } } // Should never happen? return 1024; } static boolean I_SDL_InitSound(boolean _use_sfx_prefix) { int i; use_sfx_prefix = _use_sfx_prefix; // No sounds yet for (i=0; imajor, mixer_version->minor, mixer_version->patch); if (v <= SDL_VERSIONNUM(1, 2, 8)) { setpanning_workaround = true; fprintf(stderr, "\n" "ATTENTION: You are using an old version of SDL_mixer!\n" " This version has a bug that may cause " "your sound to stutter.\n" " Please upgrade to a newer version!\n" "\n"); } } Mix_AllocateChannels(NUM_CHANNELS); SDL_PauseAudio(0); sound_initialized = true; return true; } static snddevice_t sound_sdl_devices[] = { SNDDEVICE_SB, SNDDEVICE_PAS, SNDDEVICE_GUS, SNDDEVICE_WAVEBLASTER, SNDDEVICE_SOUNDCANVAS, SNDDEVICE_AWE32, }; sound_module_t sound_sdl_module = { sound_sdl_devices, arrlen(sound_sdl_devices), I_SDL_InitSound, I_SDL_ShutdownSound, I_SDL_GetSfxLumpNum, I_SDL_UpdateSound, I_SDL_UpdateSoundParams, I_SDL_StartSound, I_SDL_StopSound, I_SDL_SoundIsPlaying, I_SDL_PrecacheSounds, }; ================================================ FILE: fbdoom/i_sound.c ================================================ // // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // 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. // // DESCRIPTION: none // #include #include #ifdef ORIGCODE #include "SDL_mixer.h" #endif #include "config.h" #include "doomfeatures.h" #include "doomtype.h" #ifdef ORIGCODE #include "gusconf.h" #endif #include "i_sound.h" #include "i_video.h" #include "m_argv.h" #include "m_config.h" // Sound sample rate to use for digital output (Hz) int snd_samplerate = 44100; // Maximum number of bytes to dedicate to allocated sound effects. // (Default: 64MB) int snd_cachesize = 64 * 1024 * 1024; // Config variable that controls the sound buffer size. // We default to 28ms (1000 / 35fps = 1 buffer per tic). int snd_maxslicetime_ms = 28; // External command to invoke to play back music. char *snd_musiccmd = ""; // Low-level sound and music modules we are using static sound_module_t *sound_module; static music_module_t *music_module; int snd_musicdevice = SNDDEVICE_SB; int snd_sfxdevice = SNDDEVICE_SB; // Sound modules extern void I_InitTimidityConfig(void); extern sound_module_t sound_sdl_module; extern sound_module_t sound_pcsound_module; extern music_module_t music_sdl_module; extern music_module_t music_opl_module; // For OPL module: extern int opl_io_port; // For native music module: extern char *timidity_cfg_path; // DOS-specific options: These are unused but should be maintained // so that the config file can be shared between chocolate // doom and doom.exe #if ORIGCODE static int snd_sbport = 0; static int snd_sbirq = 0; static int snd_sbdma = 0; static int snd_mport = 0; #endif // Compiled-in sound modules: static sound_module_t *sound_modules[] = { #ifdef FEATURE_SOUND &sound_sdl_module, &sound_pcsound_module, #endif NULL, }; // Compiled-in music modules: static music_module_t *music_modules[] = { #ifdef FEATURE_SOUND &music_sdl_module, &music_opl_module, #endif NULL, }; // Check if a sound device is in the given list of devices static boolean SndDeviceInList(snddevice_t device, snddevice_t *list, int len) { int i; for (i=0; isound_devices, sound_modules[i]->num_sound_devices)) { // Initialize the module if (sound_modules[i]->Init(use_sfx_prefix)) { sound_module = sound_modules[i]; return; } } } } // Initialize music according to snd_musicdevice. static void InitMusicModule(void) { int i; music_module = NULL; for (i=0; music_modules[i] != NULL; ++i) { // Is the music device in the list of devices supported // by this module? if (SndDeviceInList(snd_musicdevice, music_modules[i]->sound_devices, music_modules[i]->num_sound_devices)) { // Initialize the module if (music_modules[i]->Init()) { music_module = music_modules[i]; return; } } } } // // Initializes sound stuff, including volume // Sets channels, SFX and music volume, // allocates channel buffer, sets S_sfx lookup. // void I_InitSound(boolean use_sfx_prefix) { boolean nosound, nosfx, nomusic; //! // @vanilla // // Disable all sound output. // nosound = M_CheckParm("-nosound") > 0; //! // @vanilla // // Disable sound effects. // nosfx = M_CheckParm("-nosfx") > 0; //! // @vanilla // // Disable music. // nomusic = M_CheckParm("-nomusic") > 0; // Initialize the sound and music subsystems. if (!nosound && !screensaver_mode) { // This is kind of a hack. If native MIDI is enabled, set up // the TIMIDITY_CFG environment variable here before SDL_mixer // is opened. if (!nomusic && (snd_musicdevice == SNDDEVICE_GENMIDI || snd_musicdevice == SNDDEVICE_GUS)) { I_InitTimidityConfig(); } if (!nosfx) { InitSfxModule(use_sfx_prefix); } if (!nomusic) { InitMusicModule(); } } } void I_ShutdownSound(void) { if (sound_module != NULL) { sound_module->Shutdown(); } if (music_module != NULL) { music_module->Shutdown(); } } int I_GetSfxLumpNum(sfxinfo_t *sfxinfo) { if (sound_module != NULL) { return sound_module->GetSfxLumpNum(sfxinfo); } else { return 0; } } void I_UpdateSound(void) { if (sound_module != NULL) { sound_module->Update(); } if (music_module != NULL && music_module->Poll != NULL) { music_module->Poll(); } } static void CheckVolumeSeparation(int *vol, int *sep) { if (*sep < 0) { *sep = 0; } else if (*sep > 254) { *sep = 254; } if (*vol < 0) { *vol = 0; } else if (*vol > 127) { *vol = 127; } } void I_UpdateSoundParams(int channel, int vol, int sep) { if (sound_module != NULL) { CheckVolumeSeparation(&vol, &sep); sound_module->UpdateSoundParams(channel, vol, sep); } } int I_StartSound(sfxinfo_t *sfxinfo, int channel, int vol, int sep) { if (sound_module != NULL) { CheckVolumeSeparation(&vol, &sep); return sound_module->StartSound(sfxinfo, channel, vol, sep); } else { return 0; } } void I_StopSound(int channel) { if (sound_module != NULL) { sound_module->StopSound(channel); } } boolean I_SoundIsPlaying(int channel) { if (sound_module != NULL) { return sound_module->SoundIsPlaying(channel); } else { return false; } } void I_PrecacheSounds(sfxinfo_t *sounds, int num_sounds) { if (sound_module != NULL && sound_module->CacheSounds != NULL) { sound_module->CacheSounds(sounds, num_sounds); } } void I_InitMusic(void) { } void I_ShutdownMusic(void) { } void I_SetMusicVolume(int volume) { if (music_module != NULL) { music_module->SetMusicVolume(volume); } } void I_PauseSong(void) { if (music_module != NULL) { music_module->PauseMusic(); } } void I_ResumeSong(void) { if (music_module != NULL) { music_module->ResumeMusic(); } } void *I_RegisterSong(void *data, int len) { if (music_module != NULL) { return music_module->RegisterSong(data, len); } else { return NULL; } } void I_UnRegisterSong(void *handle) { if (music_module != NULL) { music_module->UnRegisterSong(handle); } } void I_PlaySong(void *handle, boolean looping) { if (music_module != NULL) { music_module->PlaySong(handle, looping); } } void I_StopSong(void) { if (music_module != NULL) { music_module->StopSong(); } } boolean I_MusicIsPlaying(void) { if (music_module != NULL) { return music_module->MusicIsPlaying(); } else { return false; } } void I_BindSoundVariables(void) { #ifdef ORIGCODE extern int use_libsamplerate; extern float libsamplerate_scale; M_BindVariable("snd_musicdevice", &snd_musicdevice); M_BindVariable("snd_sfxdevice", &snd_sfxdevice); M_BindVariable("snd_sbport", &snd_sbport); M_BindVariable("snd_sbirq", &snd_sbirq); M_BindVariable("snd_sbdma", &snd_sbdma); M_BindVariable("snd_mport", &snd_mport); M_BindVariable("snd_maxslicetime_ms", &snd_maxslicetime_ms); M_BindVariable("snd_musiccmd", &snd_musiccmd); M_BindVariable("snd_samplerate", &snd_samplerate); M_BindVariable("snd_cachesize", &snd_cachesize); M_BindVariable("opl_io_port", &opl_io_port); M_BindVariable("timidity_cfg_path", &timidity_cfg_path); M_BindVariable("gus_patch_path", &gus_patch_path); M_BindVariable("gus_ram_kb", &gus_ram_kb); #ifdef FEATURE_SOUND M_BindVariable("use_libsamplerate", &use_libsamplerate); M_BindVariable("libsamplerate_scale", &libsamplerate_scale); #endif // Before SDL_mixer version 1.2.11, MIDI music caused the game // to crash when it looped. If this is an old SDL_mixer version, // disable MIDI. #ifdef __MACOSX__ { const SDL_version *v = Mix_Linked_Version(); if (SDL_VERSIONNUM(v->major, v->minor, v->patch) < SDL_VERSIONNUM(1, 2, 11)) { snd_musicdevice = SNDDEVICE_NONE; } } #endif #endif } ================================================ FILE: fbdoom/i_sound.h ================================================ // // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // 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. // // DESCRIPTION: // The not so system specific sound interface. // #ifndef __I_SOUND__ #define __I_SOUND__ #include "doomtype.h" // // SoundFX struct. // typedef struct sfxinfo_struct sfxinfo_t; struct sfxinfo_struct { // tag name, used for hexen. char *tagname; // lump name. If we are running with use_sfx_prefix=true, a // 'DS' (or 'DP' for PC speaker sounds) is prepended to this. char name[9]; // Sfx priority int priority; // referenced sound if a link sfxinfo_t *link; // pitch if a link int pitch; // volume if a link int volume; // 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; // Maximum number of channels that the sound can be played on // (Heretic) int numchannels; // data used by the low level code void *driver_data; }; // // MusicInfo struct. // typedef struct { // up to 6-character name char *name; // lump number of music int lumpnum; // music data void *data; // music handle once registered void *handle; } musicinfo_t; typedef enum { SNDDEVICE_NONE = 0, SNDDEVICE_PCSPEAKER = 1, SNDDEVICE_ADLIB = 2, SNDDEVICE_SB = 3, SNDDEVICE_PAS = 4, SNDDEVICE_GUS = 5, SNDDEVICE_WAVEBLASTER = 6, SNDDEVICE_SOUNDCANVAS = 7, SNDDEVICE_GENMIDI = 8, SNDDEVICE_AWE32 = 9, SNDDEVICE_CD = 10, } snddevice_t; // Interface for sound modules typedef struct { // List of sound devices that this sound module is used for. snddevice_t *sound_devices; int num_sound_devices; // Initialise sound module // Returns true if successfully initialised boolean (*Init)(boolean use_sfx_prefix); // Shutdown sound module void (*Shutdown)(void); // Returns the lump index of the given sound. int (*GetSfxLumpNum)(sfxinfo_t *sfxinfo); // Called periodically to update the subsystem. void (*Update)(void); // Update the sound settings on the given channel. void (*UpdateSoundParams)(int channel, int vol, int sep); // Start a sound on a given channel. Returns the channel id // or -1 on failure. int (*StartSound)(sfxinfo_t *sfxinfo, int channel, int vol, int sep); // Stop the sound playing on the given channel. void (*StopSound)(int channel); // Query if a sound is playing on the given channel boolean (*SoundIsPlaying)(int channel); // Called on startup to precache sound effects (if necessary) void (*CacheSounds)(sfxinfo_t *sounds, int num_sounds); } sound_module_t; void I_InitSound(boolean use_sfx_prefix); void I_ShutdownSound(void); int I_GetSfxLumpNum(sfxinfo_t *sfxinfo); void I_UpdateSound(void); void I_UpdateSoundParams(int channel, int vol, int sep); int I_StartSound(sfxinfo_t *sfxinfo, int channel, int vol, int sep); void I_StopSound(int channel); boolean I_SoundIsPlaying(int channel); void I_PrecacheSounds(sfxinfo_t *sounds, int num_sounds); // Interface for music modules typedef struct { // List of sound devices that this music module is used for. snddevice_t *sound_devices; int num_sound_devices; // Initialise the music subsystem boolean (*Init)(void); // Shutdown the music subsystem void (*Shutdown)(void); // Set music volume - range 0-127 void (*SetMusicVolume)(int volume); // Pause music void (*PauseMusic)(void); // Un-pause music void (*ResumeMusic)(void); // Register a song handle from data // Returns a handle that can be used to play the song void *(*RegisterSong)(void *data, int len); // Un-register (free) song data void (*UnRegisterSong)(void *handle); // Play the song void (*PlaySong)(void *handle, boolean looping); // Stop playing the current song. void (*StopSong)(void); // Query if music is playing. boolean (*MusicIsPlaying)(void); // Invoked periodically to poll. void (*Poll)(void); } music_module_t; void I_InitMusic(void); void I_ShutdownMusic(void); void I_SetMusicVolume(int volume); void I_PauseSong(void); void I_ResumeSong(void); void *I_RegisterSong(void *data, int len); void I_UnRegisterSong(void *handle); void I_PlaySong(void *handle, boolean looping); void I_StopSong(void); boolean I_MusicIsPlaying(void); extern int snd_sfxdevice; extern int snd_musicdevice; extern int snd_samplerate; extern int snd_cachesize; extern int snd_maxslicetime_ms; extern char *snd_musiccmd; void I_BindSoundVariables(void); #endif ================================================ FILE: fbdoom/i_sound_dummy.c ================================================ // Emacs style mode select -*- C++ -*- //----------------------------------------------------------------------------- // // $Id:$ // // Copyright (C) 1993-1996 by id Software, Inc. // // This source is available for distribution and/or modification // only under the terms of the DOOM Source Code License as // published by id Software. All rights reserved. // // The source is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License // for more details. // // $Log:$ // // DESCRIPTION: // System interface for sound. // //----------------------------------------------------------------------------- static const char rcsid[] = "$Id: i_unix.c,v 1.5 1997/02/03 22:45:10 b1 Exp $"; #include #include #include #include #include #include #include #ifndef LINUX #include #endif #include #include #include // Timer stuff. Experimental. #include #include #include "z_zone.h" #include "i_system.h" #include "i_sound.h" #include "m_argv.h" #include "m_misc.h" #include "w_wad.h" #include "doomdef.h" // UNIX hack, to be removed. #ifdef SNDSERV // Separate sound server process. FILE* sndserver=0; char* sndserver_filename = "./sndserver "; #elif SNDINTR // Update all 30 millisecs, approx. 30fps synchronized. // Linux resolution is allegedly 10 millisecs, // scale is microseconds. #define SOUND_INTERVAL 500 // Get the interrupt. Set duration in millisecs. int I_SoundSetTimer( int duration_of_tick ); void I_SoundDelTimer( void ); #else // None? #endif // A quick hack to establish a protocol between // synchronous mix buffer updates and asynchronous // audio writes. Probably redundant with gametic. static int flag = 0; // 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. // Needed for calling the actual sound output. #define SAMPLECOUNT 512 #define NUM_CHANNELS 8 // It is 2 for 16bit, and 2 for two channels. #define BUFMUL 4 #define MIXBUFFERSIZE (SAMPLECOUNT*BUFMUL) #define SAMPLERATE 11025 // Hz #define SAMPLESIZE 2 // 16bit // The actual lengths of all sound effects. int lengths[NUMSFX]; // The actual output device. int audio_fd; // The global mixing buffer. // Basically, samples from all active internal channels // are modifed and added, and stored in the buffer // that is submitted to the audio device. signed short mixbuffer[MIXBUFFERSIZE]; // The channel step amount... unsigned int channelstep[NUM_CHANNELS]; // ... and a 0.16 bit remainder of last step. unsigned int channelstepremainder[NUM_CHANNELS]; // The channel data pointers, start and end. unsigned char* channels[NUM_CHANNELS]; unsigned char* channelsend[NUM_CHANNELS]; // 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 channelstart[NUM_CHANNELS]; // The sound in channel handles, // determined on registration, // might be used to unregister/stop/modify, // currently unused. int channelhandles[NUM_CHANNELS]; // SFX id of the playing sound effect. // Used to catch duplicates (like chainsaw). int channelids[NUM_CHANNELS]; // Pitch to stepping lookup, unused. int steptable[256]; // Volume lookups. int vol_lookup[128*256]; // Hardware left and right channel volume lookup. int* channelleftvol_lookup[NUM_CHANNELS]; int* channelrightvol_lookup[NUM_CHANNELS]; // // Safe ioctl, convenience. // void myioctl ( int fd, int command, int* arg ) { int rc; rc = ioctl(fd, command, arg); if (rc < 0) { fprintf(stderr, "ioctl(dsp,%d,arg) failed\n", command); fprintf(stderr, "errno=%d\n", errno); exit(-1); } } // // This function loads the sound data from the WAD lump, // for single sound. // void* getsfx ( char* sfxname, int* len ) { unsigned char* sfx; unsigned char* paddedsfx; int i; int size; int paddedsize; char name[20]; int sfxlump; // Get the sound data from the WAD, allocate lump // in zone memory. sprintf(name, "ds%s", sfxname); // Now, there is a severe problem with the // sound handling, in it is not (yet/anymore) // gamemode aware. That means, sounds from // DOOM II will be requested even with DOOM // shareware. // The sound list is wired into sounds.c, // which sets the external variable. // I do not do runtime patches to that // variable. Instead, we will use a // default sound for replacement. if ( W_CheckNumForName(name) == -1 ) sfxlump = W_GetNumForName("dspistol"); else sfxlump = W_GetNumForName(name); size = W_LumpLength( sfxlump ); // Debug. // fprintf( stderr, "." ); //fprintf( stderr, " -loading %s (lump %d, %d bytes)\n", // sfxname, sfxlump, size ); //fflush( stderr ); sfx = (unsigned char*)W_CacheLumpNum( sfxlump, PU_STATIC ); // Pads the sound effect out to the mixing buffer size. // The original realloc would interfere with zone memory. paddedsize = ((size-8 + (SAMPLECOUNT-1)) / SAMPLECOUNT) * SAMPLECOUNT; // Allocate from zone memory. paddedsfx = (unsigned char*)Z_Malloc( paddedsize+8, PU_STATIC, 0 ); // ddt: (unsigned char *) realloc(sfx, paddedsize+8); // This should interfere with zone memory handling, // which does not kick in in the soundserver. // Now copy and pad. memcpy( paddedsfx, sfx, size ); for (i=size ; i> 16); ///(256*256); 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??? channelleftvol_lookup[slot] = &vol_lookup[leftvol*256]; channelrightvol_lookup[slot] = &vol_lookup[rightvol*256]; // Preserve sound SFX id, // e.g. for avoiding duplicates of chainsaw. channelids[slot] = sfxid; // You tell me. return rc; } // // 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() { // 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 vol, int sep, int pitch, int priority ) { // UNUSED priority = 0; #ifdef SNDSERV if (sndserver) { fprintf(sndserver, "p%2.2x%2.2x%2.2x%2.2x\n", id, pitch, vol, sep); fflush(sndserver); } // warning: control reaches end of non-void function. return id; #else // Debug. //fprintf( stderr, "starting sound %d", id ); // Returns a handle (not used). id = addsfx( id, vol, steptable[pitch], sep ); // fprintf( stderr, "/handle is %d\n", id ); return id; #endif } void I_StopSound (int handle) { // You need the handle returned by StartSound. // Would be looping all channels, // tracking down the handle, // an setting the channel to zero. // UNUSED. handle = 0; } int I_SoundIsPlaying(int handle) { // Ouch. return gametic < handle; } // // This function loops all active (internal) sound // channels, retrieves a given number of samples // from the raw sound data, modifies it according // to the current (internal) channel parameters, // mixes the per channel samples into the global // mixbuffer, clamping it to the allowed range, // and sets up everything for transferring the // contents of the mixbuffer to the (two) // hardware channels (left and right, that is). // // This function currently supports only 16bit. // void I_UpdateSound( void ) { #ifdef SNDINTR // Debug. Count buffer misses with interrupt. static int misses = 0; #endif // Mix current sound data. // Data, from raw sound, for right and left. register unsigned int sample; register int dl; register int dr; // Pointers in global mixbuffer, left, right, end. signed short* leftout; signed short* rightout; signed short* leftend; // Step in mixbuffer, left and right, thus two. int step; // Mixing channel index. int chan; // Left and right channel // are in global mixbuffer, alternating. leftout = mixbuffer; rightout = mixbuffer+1; step = 2; // Determine end, for left channel only // (right channel is implicit). leftend = mixbuffer + SAMPLECOUNT*step; // Mix sounds into the mixing buffer. // Loop over step*SAMPLECOUNT, // that is 512 values for two channels. while (leftout != leftend) { // Reset left/right value. dl = 0; dr = 0; // Love thy L2 chache - made this a loop. // Now more channels could be set at compile time // as well. Thus loop those channels. for ( chan = 0; chan < NUM_CHANNELS; chan++ ) { // Check channel, if active. if (channels[ chan ]) { // Get the raw data from the channel. sample = *channels[ chan ]; // Add left and right part // for this channel (sound) // to the current data. // Adjust volume accordingly. dl += channelleftvol_lookup[ chan ][sample]; dr += channelrightvol_lookup[ chan ][sample]; // Increment index ??? channelstepremainder[ chan ] += channelstep[ chan ]; // MSB is next sample??? channels[ chan ] += channelstepremainder[ chan ] >> 16; // Limit to LSB??? channelstepremainder[ chan ] &= 65536-1; // Check whether we are done. if (channels[ chan ] >= channelsend[ chan ]) channels[ chan ] = 0; } } // Clamp to range. Left hardware channel. // Has been char instead of short. // if (dl > 127) *leftout = 127; // else if (dl < -128) *leftout = -128; // else *leftout = dl; if (dl > 0x7fff) *leftout = 0x7fff; else if (dl < -0x8000) *leftout = -0x8000; else *leftout = dl; // Same for right hardware channel. if (dr > 0x7fff) *rightout = 0x7fff; else if (dr < -0x8000) *rightout = -0x8000; else *rightout = dr; // Increment current pointers in mixbuffer. leftout += step; rightout += step; } #ifdef SNDINTR // Debug check. if ( flag ) { misses += flag; flag = 0; } if ( misses > 10 ) { fprintf( stderr, "I_SoundUpdate: missed 10 buffer writes\n"); misses = 0; } // Increment flag for update. flag++; #endif } // // This would be used to write out the mixbuffer // during each game loop update. // Updates sound buffer and audio device at runtime. // It is called during Timer interrupt with SNDINTR. // Mixing now done synchronous, and // only output be done asynchronous? // void I_SubmitSound(void) { // Write it to DSP device. write(audio_fd, mixbuffer, SAMPLECOUNT*BUFMUL); } void I_UpdateSoundParams ( int handle, int vol, int sep, int pitch) { // I fail too see that this is used. // Would be using the handle to identify // on which channel the sound might be active, // and resetting the channel parameters. // UNUSED. handle = vol = sep = pitch = 0; } void I_ShutdownSound(void) { #ifdef SNDSERV if (sndserver) { // Send a "quit" command. fprintf(sndserver, "q\n"); fflush(sndserver); } #else // Wait till all pending sounds are finished. int done = 0; int i; // FIXME (below). fprintf( stderr, "I_ShutdownSound: NOT finishing pending sounds\n"); fflush( stderr ); while ( !done ) { for( i=0 ; i<8 && !channels[i] ; i++); // FIXME. No proper channel output. //if (i==8) done=1; } #ifdef SNDINTR I_SoundDelTimer(); #endif // Cleaning up -releasing the DSP device. close ( audio_fd ); #endif // Done. return; } void I_InitSound() { #ifdef SNDSERV char buffer[256]; if (getenv("DOOMWADDIR")) sprintf(buffer, "%s/%s", getenv("DOOMWADDIR"), sndserver_filename); else sprintf(buffer, "%s", sndserver_filename); // start sound process if ( !access(buffer, X_OK) ) { strcat(buffer, " -quiet"); sndserver = NULL; //popen(buffer, "w"); } else fprintf(stderr, "Could not start sound server [%s]\n", buffer); #else int i; #ifdef SNDINTR fprintf( stderr, "I_SoundSetTimer: %d microsecs\n", SOUND_INTERVAL ); I_SoundSetTimer( SOUND_INTERVAL ); #endif // Secure and configure sound device first. fprintf( stderr, "I_InitSound: "); audio_fd = open("/dev/dsp", O_WRONLY); if (audio_fd<0) fprintf(stderr, "Could not open /dev/dsp\n"); i = 11 | (2<<16); myioctl(audio_fd, SNDCTL_DSP_SETFRAGMENT, &i); myioctl(audio_fd, SNDCTL_DSP_RESET, 0); i=SAMPLERATE; myioctl(audio_fd, SNDCTL_DSP_SPEED, &i); i=1; myioctl(audio_fd, SNDCTL_DSP_STEREO, &i); myioctl(audio_fd, SNDCTL_DSP_GETFMTS, &i); if (i&=AFMT_S16_LE) myioctl(audio_fd, SNDCTL_DSP_SETFMT, &i); else fprintf(stderr, "Could not play signed 16 data\n"); fprintf(stderr, " configured audio device\n" ); // Initialize external data (all sounds) at start, keep static. fprintf( stderr, "I_InitSound: "); for (i=1 ; idata; lengths[i] = lengths[(S_sfx[i].link - S_sfx)/sizeof(sfxinfo_t)]; } } fprintf( stderr, " pre-cached all sound data\n"); // Now initialize mixbuffer with zero. for ( i = 0; i< MIXBUFFERSIZE; i++ ) mixbuffer[i] = 0; // Finished initialization. fprintf(stderr, "I_InitSound: sound module ready\n"); #endif } // // MUSIC API. // Still no music done. // Remains. Dummies. // void I_InitMusic(void) { } void I_ShutdownMusic(void) { } static int looping=0; static int musicdies=-1; void I_PlaySong(int handle, int looping) { // UNUSED. handle = looping = 0; musicdies = gametic + TICRATE*30; } void I_PauseSong (int handle) { // UNUSED. handle = 0; } void I_ResumeSong (int handle) { // UNUSED. handle = 0; } void I_StopSong(int handle) { // UNUSED. handle = 0; looping = 0; musicdies = 0; } void I_UnRegisterSong(int handle) { // UNUSED. handle = 0; } int I_RegisterSong(void* data) { // UNUSED. data = NULL; return 1; } // Is the song playing? int I_QrySongPlaying(int handle) { // UNUSED. handle = 0; return looping || musicdies > gametic; } // // Experimental stuff. // A Linux timer interrupt, for asynchronous // sound output. // I ripped this out of the Timer class in // our Difference Engine, including a few // SUN remains... // #ifdef sun typedef sigset_t tSigSet; #else typedef int tSigSet; #endif // We might use SIGVTALRM and ITIMER_VIRTUAL, if the process // time independend timer happens to get lost due to heavy load. // SIGALRM and ITIMER_REAL doesn't really work well. // There are issues with profiling as well. // Interrupt handler. void I_HandleSoundTimer( int ignore ) { // Debug. //fprintf( stderr, "%c", '+' ); fflush( stderr ); // Feed sound device if necesary. if ( flag ) { // See I_SubmitSound(). // Write it to DSP device. write(audio_fd, mixbuffer, SAMPLECOUNT*BUFMUL); // Reset flag counter. flag = 0; } else return; // UNUSED, but required. ignore = 0; return; } // Get the interrupt. Set duration in millisecs. int I_SoundSetTimer( int duration_of_tick ) { return -1; } // Remove the interrupt. Set duration to zero. void I_SoundDelTimer() { // Debug. if ( I_SoundSetTimer( 0 ) == -1) fprintf( stderr, "I_SoundDelTimer: failed to remove interrupt. Doh!\n"); } ================================================ FILE: fbdoom/i_swap.h ================================================ // // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // 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. // // DESCRIPTION: // Endianess handling, swapping 16bit and 32bit. // #ifndef __I_SWAP__ #define __I_SWAP__ #ifdef ORIGCODE #include "SDL_endian.h" // Endianess handling. // WAD files are stored little endian. // Just use SDL's endianness swapping functions. // These are deliberately cast to signed values; this is the behaviour // of the macros in the original source and some code relies on it. #define SHORT(x) ((signed short) SDL_SwapLE16(x)) #define LONG(x) ((signed int) SDL_SwapLE32(x)) // Defines for checking the endianness of the system. #if SDL_BYTEORDER == SYS_LIL_ENDIAN #define SYS_LITTLE_ENDIAN #elif SDL_BYTEORDER == SYS_BIG_ENDIAN #define SYS_BIG_ENDIAN #endif #else #define SHORT(x) ((signed short) (x)) #define LONG(x) ((signed int) (x)) #define SYS_LITTLE_ENDIAN #endif #endif ================================================ FILE: fbdoom/i_system.c ================================================ // // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // 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. // // DESCRIPTION: // #include #include #include #include #ifdef _WIN32 #define WIN32_LEAN_AND_MEAN #include #else #include #endif #ifdef ORIGCODE #include "SDL.h" #endif #include "config.h" #include "deh_str.h" #include "doomtype.h" #include "m_argv.h" #include "m_config.h" #include "m_misc.h" #include "i_joystick.h" #include "i_sound.h" #include "i_timer.h" #include "i_video.h" #include "i_system.h" #include "w_wad.h" #include "z_zone.h" #ifdef __MACOSX__ #include #endif #define DEFAULT_RAM 6 /* MiB */ #define MIN_RAM 6 /* MiB */ typedef struct atexit_listentry_s atexit_listentry_t; struct atexit_listentry_s { atexit_func_t func; boolean run_on_error; atexit_listentry_t *next; }; static atexit_listentry_t *exit_funcs = NULL; void I_AtExit(atexit_func_t func, boolean run_on_error) { atexit_listentry_t *entry; entry = malloc(sizeof(*entry)); entry->func = func; entry->run_on_error = run_on_error; entry->next = exit_funcs; exit_funcs = entry; } // Tactile feedback function, probably used for the Logitech Cyberman void I_Tactile(int on, int off, int total) { } // Zone memory auto-allocation function that allocates the zone size // by trying progressively smaller zone sizes until one is found that // works. static byte *AutoAllocMemory(int *size, int default_ram, int min_ram) { byte *zonemem; // Allocate the zone memory. This loop tries progressively smaller // zone sizes until a size is found that can be allocated. // If we used the -mb command line parameter, only the parameter // provided is accepted. zonemem = NULL; while (zonemem == NULL) { // We need a reasonable minimum amount of RAM to start. if (default_ram < min_ram) { I_Error("Unable to allocate %i MiB of RAM for zone", default_ram); } // Try to allocate the zone memory. *size = default_ram * 1024 * 1024; zonemem = malloc(*size); // Failed to allocate? Reduce zone size until we reach a size // that is acceptable. if (zonemem == NULL) { default_ram -= 1; } } return zonemem; } byte *I_ZoneBase (int *size) { byte *zonemem; int min_ram, default_ram; int p; //! // @arg // // Specify the heap size, in MiB (default 16). // p = M_CheckParmWithArgs("-mb", 1); if (p > 0) { default_ram = atoi(myargv[p+1]); min_ram = default_ram; } else { default_ram = DEFAULT_RAM; min_ram = MIN_RAM; } zonemem = AutoAllocMemory(size, default_ram, min_ram); printf("zone memory: %p, %x allocated for zone\n", zonemem, *size); return zonemem; } void I_PrintBanner(char *msg) { int i; int spaces = 35 - (strlen(msg) / 2); for (i=0; ifunc(); entry = entry->next; } #if ORIGCODE SDL_Quit(); exit(0); #endif } #if !defined(_WIN32) && !defined(__MACOSX__) #define ZENITY_BINARY "/usr/bin/zenity" // returns non-zero if zenity is available static int ZenityAvailable(void) { return system(ZENITY_BINARY " --help >/dev/null 2>&1") == 0; } // Escape special characters in the given string so that they can be // safely enclosed in shell quotes. static char *EscapeShellString(char *string) { char *result; char *r, *s; // In the worst case, every character might be escaped. result = malloc(strlen(string) * 2 + 3); r = result; // Enclosing quotes. *r = '"'; ++r; for (s = string; *s != '\0'; ++s) { // From the bash manual: // // "Enclosing characters in double quotes preserves the literal // value of all characters within the quotes, with the exception // of $, `, \, and, when history expansion is enabled, !." // // Therefore, escape these characters by prefixing with a backslash. if (strchr("$`\\!", *s) != NULL) { *r = '\\'; ++r; } *r = *s; ++r; } // Enclosing quotes. *r = '"'; ++r; *r = '\0'; return result; } // Open a native error box with a message using zenity static int ZenityErrorBox(char *message) { int result; char *escaped_message; char *errorboxpath; static size_t errorboxpath_size; if (!ZenityAvailable()) { return 0; } escaped_message = EscapeShellString(message); errorboxpath_size = strlen(ZENITY_BINARY) + strlen(escaped_message) + 19; errorboxpath = malloc(errorboxpath_size); M_snprintf(errorboxpath, errorboxpath_size, "%s --error --text=%s", ZENITY_BINARY, escaped_message); result = system(errorboxpath); free(errorboxpath); free(escaped_message); return result; } #endif /* !defined(_WIN32) && !defined(__MACOSX__) */ // // I_Error // static boolean already_quitting = false; void I_Error (char *error, ...) { char msgbuf[512]; va_list argptr; atexit_listentry_t *entry; boolean exit_gui_popup; if (already_quitting) { fprintf(stderr, "Warning: recursive call to I_Error detected.\n"); #if ORIGCODE exit(-1); #endif } else { already_quitting = true; } // Message first. va_start(argptr, error); //fprintf(stderr, "\nError: "); vfprintf(stderr, error, argptr); fprintf(stderr, "\n\n"); va_end(argptr); fflush(stderr); // Write a copy of the message into buffer. va_start(argptr, error); memset(msgbuf, 0, sizeof(msgbuf)); M_vsnprintf(msgbuf, sizeof(msgbuf), error, argptr); va_end(argptr); // Shutdown. Here might be other errors. entry = exit_funcs; while (entry != NULL) { if (entry->run_on_error) { entry->func(); } entry = entry->next; } exit_gui_popup = !M_ParmExists("-nogui"); // Pop up a GUI dialog box to show the error message, if the // game was not run from the console (and the user will // therefore be unable to otherwise see the message). if (exit_gui_popup && !I_ConsoleStdout()) #ifdef _WIN32 { wchar_t wmsgbuf[512]; MultiByteToWideChar(CP_ACP, 0, msgbuf, strlen(msgbuf) + 1, wmsgbuf, sizeof(wmsgbuf)); MessageBoxW(NULL, wmsgbuf, L"", MB_OK); } #elif defined(__MACOSX__) { CFStringRef message; int i; // The CoreFoundation message box wraps text lines, so replace // newline characters with spaces so that multiline messages // are continuous. for (i = 0; msgbuf[i] != '\0'; ++i) { if (msgbuf[i] == '\n') { msgbuf[i] = ' '; } } message = CFStringCreateWithCString(NULL, msgbuf, kCFStringEncodingUTF8); CFUserNotificationDisplayNotice(0, kCFUserNotificationCautionAlertLevel, NULL, NULL, NULL, CFSTR(PACKAGE_STRING), message, NULL); } #else { ZenityErrorBox(msgbuf); } #endif // abort(); #if ORIGCODE SDL_Quit(); exit(-1); #else while (true) { } #endif } // // Read Access Violation emulation. // // From PrBoom+, by entryway. // // C:\>debug // -d 0:0 // // DOS 6.22: // 0000:0000 (57 92 19 00) F4 06 70 00-(16 00) // DOS 7.1: // 0000:0000 (9E 0F C9 00) 65 04 70 00-(16 00) // Win98: // 0000:0000 (9E 0F C9 00) 65 04 70 00-(16 00) // DOSBox under XP: // 0000:0000 (00 00 00 F1) ?? ?? ?? 00-(07 00) #define DOS_MEM_DUMP_SIZE 10 static const unsigned char mem_dump_dos622[DOS_MEM_DUMP_SIZE] = { 0x57, 0x92, 0x19, 0x00, 0xF4, 0x06, 0x70, 0x00, 0x16, 0x00}; static const unsigned char mem_dump_win98[DOS_MEM_DUMP_SIZE] = { 0x9E, 0x0F, 0xC9, 0x00, 0x65, 0x04, 0x70, 0x00, 0x16, 0x00}; static const unsigned char mem_dump_dosbox[DOS_MEM_DUMP_SIZE] = { 0x00, 0x00, 0x00, 0xF1, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00}; static unsigned char mem_dump_custom[DOS_MEM_DUMP_SIZE]; static const unsigned char *dos_mem_dump = mem_dump_dos622; boolean I_GetMemoryValue(unsigned int offset, void *value, int size) { static boolean firsttime = true; if (firsttime) { int p, i, val; firsttime = false; i = 0; //! // @category compat // @arg // // Specify DOS version to emulate for NULL pointer dereference // emulation. Supported versions are: dos622, dos71, dosbox. // The default is to emulate DOS 7.1 (Windows 98). // p = M_CheckParmWithArgs("-setmem", 1); if (p > 0) { if (!strcasecmp(myargv[p + 1], "dos622")) { dos_mem_dump = mem_dump_dos622; } if (!strcasecmp(myargv[p + 1], "dos71")) { dos_mem_dump = mem_dump_win98; } else if (!strcasecmp(myargv[p + 1], "dosbox")) { dos_mem_dump = mem_dump_dosbox; } else { for (i = 0; i < DOS_MEM_DUMP_SIZE; ++i) { ++p; if (p >= myargc || myargv[p][0] == '-') { break; } M_StrToInt(myargv[p], &val); mem_dump_custom[i++] = (unsigned char) val; } dos_mem_dump = mem_dump_custom; } } } switch (size) { case 1: *((unsigned char *) value) = dos_mem_dump[offset]; return true; case 2: *((unsigned short *) value) = dos_mem_dump[offset] | (dos_mem_dump[offset + 1] << 8); return true; case 4: *((unsigned int *) value) = dos_mem_dump[offset] | (dos_mem_dump[offset + 1] << 8) | (dos_mem_dump[offset + 2] << 16) | (dos_mem_dump[offset + 3] << 24); return true; } return false; } ================================================ FILE: fbdoom/i_system.h ================================================ // // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // 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. // // DESCRIPTION: // System specific interface stuff. // #ifndef __I_SYSTEM__ #define __I_SYSTEM__ #include "d_ticcmd.h" #include "d_event.h" typedef void (*atexit_func_t)(void); // Called by DoomMain. void I_Init (void); // Called by startup code // to get the ammount of memory to malloc // for the zone management. byte* I_ZoneBase (int *size); boolean I_ConsoleStdout(void); // Asynchronous interrupt functions should maintain private queues // that are read by the synchronous functions // to be converted into events. // Either returns a null ticcmd, // or calls a loadable driver to build it. // This ticcmd will then be modified by the gameloop // for normal input. ticcmd_t* I_BaseTiccmd (void); // Called by M_Responder when quit is selected. // Clean exit, displays sell blurb. void I_Quit (void); void I_Error (char *error, ...); void I_Tactile (int on, int off, int total); boolean I_GetMemoryValue(unsigned int offset, void *value, int size); // Schedule a function to be called when the program exits. // If run_if_error is true, the function is called if the exit // is due to an error (I_Error) void I_AtExit(atexit_func_t func, boolean run_if_error); // Add all system-specific config file variable bindings. void I_BindVariables(void); // Print startup banner copyright message. void I_PrintStartupBanner(char *gamedescription); // Print a centered text banner displaying the given string. void I_PrintBanner(char *text); // Print a dividing line for startup banners. void I_PrintDivider(void); #endif ================================================ FILE: fbdoom/i_timer.c ================================================ // // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // 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. // // DESCRIPTION: // Timer functions. // #include "i_timer.h" #include "doomtype.h" #include #include #include // // I_GetTime // returns time in 1/35th second tics // static uint32_t basetime = 0; int I_GetTicks(void) { struct timeval tp; struct timezone tzp; gettimeofday(&tp, &tzp); return (tp.tv_sec * 1000) + (tp.tv_usec / 1000); /* return milliseconds */ } int I_GetTime (void) { uint32_t ticks; ticks = I_GetTicks(); if (basetime == 0) basetime = ticks; ticks -= basetime; return (ticks * TICRATE) / 1000; } // // Same as I_GetTime, but returns time in milliseconds // int I_GetTimeMS(void) { uint32_t ticks; ticks = I_GetTicks(); if (basetime == 0) basetime = ticks; return ticks - basetime; } // Sleep for a specified number of ms void I_Sleep(int ms) { //SDL_Delay(ms); usleep (ms * 1000); } void I_WaitVBL(int count) { //I_Sleep((count * 1000) / 70); } void I_InitTimer(void) { // initialize timer //SDL_Init(SDL_INIT_TIMER); } ================================================ FILE: fbdoom/i_timer.h ================================================ // // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // 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. // // DESCRIPTION: // System-specific timer interface // #ifndef __I_TIMER__ #define __I_TIMER__ #define TICRATE 35 // Called by D_DoomLoop, // returns current time in tics. int I_GetTime (void); // returns current time in ms int I_GetTimeMS (void); // Pause for a specified number of ms void I_Sleep(int ms); // Initialize timer void I_InitTimer(void); // Wait for vertical retrace or pause a bit. void I_WaitVBL(int count); #endif ================================================ FILE: fbdoom/i_video.c ================================================ // // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // 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. // // DESCRIPTION: // DOOM graphics stuff for SDL. // #include "SDL/SDL.h" #include #include #include #include #ifdef _WIN32 #define WIN32_LEAN_AND_MEAN #include #endif #include "icon.c" #include "config.h" #include "deh_str.h" #include "doomtype.h" #include "doomkeys.h" #include "i_joystick.h" #include "i_system.h" #include "i_swap.h" #include "i_timer.h" #include "i_video.h" #include "i_scale.h" #include "m_argv.h" #include "m_config.h" #include "m_misc.h" #include "tables.h" #include "v_video.h" #include "w_wad.h" #include "z_zone.h" // Lookup table for mapping ASCII characters to their equivalent when // shift is pressed on an American layout keyboard: static const char 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 }; #define LOADING_DISK_W 16 #define LOADING_DISK_H 16 // Non aspect ratio-corrected modes (direct multiples of 320x200) static screen_mode_t *screen_modes[] = { &mode_scale_1x, &mode_scale_2x, &mode_scale_3x, &mode_scale_4x, &mode_scale_5x, }; // Aspect ratio corrected modes (4:3 ratio) static screen_mode_t *screen_modes_corrected[] = { // Vertically stretched modes (320x200 -> 320x240 and multiples) &mode_stretch_1x, &mode_stretch_2x, &mode_stretch_3x, &mode_stretch_4x, &mode_stretch_5x, // Horizontally squashed modes (320x200 -> 256x200 and multiples) &mode_squash_1x, &mode_squash_2x, &mode_squash_3x, &mode_squash_4x, &mode_squash_5x, }; // SDL video driver name char *video_driver = ""; // Window position: static char *window_position = ""; // SDL surface for the screen. static SDL_Surface *screen; // Window title static char *window_title = ""; // Intermediate 8-bit buffer that we draw to instead of 'screen'. // This is used when we are rendering in 32-bit screen mode. // When in a real 8-bit screen mode, screenbuffer == screen. static SDL_Surface *screenbuffer = NULL; // palette static SDL_Color palette[256]; static boolean palette_to_set; // display has been set up? static boolean initialized = false; // disable mouse? static boolean nomouse = false; int usemouse = 1; // Bit mask of mouse button state. static unsigned int mouse_button_state = 0; // Disallow mouse and joystick movement to cause forward/backward // motion. Specified with the '-novert' command line parameter. // This is an int to allow saving to config file int novert = 0; // Save screenshots in PNG format. int png_screenshots = 0; // if true, I_VideoBuffer is screen->pixels static boolean native_surface; // Screen width and height, from configuration file. int screen_width = SCREENWIDTH; int screen_height = SCREENHEIGHT; // Color depth. int screen_bpp = 0; // Automatically adjust video settings if the selected mode is // not a valid video mode. static int autoadjust_video_settings = 1; // Run in full screen mode? (int type for config code) int fullscreen = true; // Aspect ratio correction mode int aspect_ratio_correct = true; // Time to wait for the screen to settle on startup before starting the // game (ms) static int startup_delay = 1000; // Grab the mouse? (int type for config code) static int grabmouse = true; // The screen buffer; this is modified to draw things to the screen byte *I_VideoBuffer = NULL; // If true, game is running as a screensaver boolean screensaver_mode = false; // Flag indicating whether the screen is currently visible: // when the screen isnt visible, don't render the screen boolean screenvisible; // If true, we display dots at the bottom of the screen to // indicate FPS. static boolean display_fps_dots; // If this is true, the screen is rendered but not blitted to the // video buffer. static boolean noblit; // Callback function to invoke to determine whether to grab the // mouse pointer. static grabmouse_callback_t grabmouse_callback = NULL; // disk image data and background overwritten by the disk to be // restored by EndRead static byte *disk_image = NULL; static byte *saved_background; static boolean window_focused; // Empty mouse cursor static SDL_Cursor *cursors[2]; // The screen mode and scale functions being used static screen_mode_t *screen_mode; // Window resize state. static boolean need_resize = false; static unsigned int resize_w, resize_h; static unsigned int last_resize_time; // If true, keyboard mapping is ignored, like in Vanilla Doom. // The sensible thing to do is to disable this if you have a non-US // keyboard. int vanilla_keyboard_mapping = true; // Is the shift key currently down? static int shiftdown = 0; // Mouse acceleration // // This emulates some of the behavior of DOS mouse drivers by increasing // the speed when the mouse is moved fast. // // The mouse input values are input directly to the game, but when // the values exceed the value of mouse_threshold, they are multiplied // by mouse_acceleration to increase the speed. float mouse_acceleration = 2.0; int mouse_threshold = 10; // Gamma correction level to use int usegamma = 0; static void ApplyWindowResize(unsigned int w, unsigned int h); static boolean MouseShouldBeGrabbed() { // never grab the mouse when in screensaver mode if (screensaver_mode) return false; // if the window doesn't have focus, never grab it if (!window_focused) return false; // always grab the mouse when full screen (dont want to // see the mouse pointer) if (fullscreen) return true; // Don't grab the mouse if mouse input is disabled if (!usemouse || nomouse) return false; // if we specify not to grab the mouse, never grab if (!grabmouse) return false; // Invoke the grabmouse callback function to determine whether // the mouse should be grabbed if (grabmouse_callback != NULL) { return grabmouse_callback(); } else { return true; } } void I_SetGrabMouseCallback(grabmouse_callback_t func) { grabmouse_callback = func; } // Set the variable controlling FPS dots. void I_DisplayFPSDots(boolean dots_on) { display_fps_dots = dots_on; } // Update the value of window_focused when we get a focus event // // We try to make ourselves be well-behaved: the grab on the mouse // is removed if we lose focus (such as a popup window appearing), // and we dont move the mouse around if we aren't focused either. static void UpdateFocus(void) { Uint8 state; state = SDL_GetAppState(); // We should have input (keyboard) focus and be visible // (not minimized) window_focused = (state & SDL_APPINPUTFOCUS) && (state & SDL_APPACTIVE); // Should the screen be grabbed? screenvisible = (state & SDL_APPACTIVE) != 0; } // Show or hide the mouse cursor. We have to use different techniques // depending on the OS. static void SetShowCursor(boolean show) { // On Windows, using SDL_ShowCursor() adds lag to the mouse input, // so work around this by setting an invisible cursor instead. On // other systems, it isn't possible to change the cursor, so this // hack has to be Windows-only. (Thanks to entryway for this) #ifdef _WIN32 if (show) { SDL_SetCursor(cursors[1]); } else { SDL_SetCursor(cursors[0]); } #else SDL_ShowCursor(show); #endif // When the cursor is hidden, grab the input. if (!screensaver_mode) { SDL_WM_GrabInput(!show); } } void I_EnableLoadingDisk(void) { patch_t *disk; byte *tmpbuf; char *disk_name; int y; char buf[20]; SDL_VideoDriverName(buf, 15); if (!strcmp(buf, "Quartz")) { // MacOS Quartz gives us pageflipped graphics that screw up the // display when we use the loading disk. Disable it. // This is a gross hack. return; } if (M_CheckParm("-cdrom") > 0) disk_name = DEH_String("STCDROM"); else disk_name = DEH_String("STDISK"); disk = W_CacheLumpName(disk_name, PU_STATIC); // Draw the patch into a temporary buffer tmpbuf = Z_Malloc(SCREENWIDTH * (disk->height + 1), PU_STATIC, NULL); V_UseBuffer(tmpbuf); // Draw the disk to the screen: V_DrawPatch(0, 0, disk); disk_image = Z_Malloc(LOADING_DISK_W * LOADING_DISK_H, PU_STATIC, NULL); saved_background = Z_Malloc(LOADING_DISK_W * LOADING_DISK_H, PU_STATIC, NULL); for (y=0; ysym) { case SDLK_LEFT: return KEY_LEFTARROW; case SDLK_RIGHT: return KEY_RIGHTARROW; case SDLK_DOWN: return KEY_DOWNARROW; case SDLK_UP: return KEY_UPARROW; case SDLK_ESCAPE: return KEY_ESCAPE; case SDLK_RETURN: return KEY_ENTER; case SDLK_TAB: return KEY_TAB; case SDLK_F1: return KEY_F1; case SDLK_F2: return KEY_F2; case SDLK_F3: return KEY_F3; case SDLK_F4: return KEY_F4; case SDLK_F5: return KEY_F5; case SDLK_F6: return KEY_F6; case SDLK_F7: return KEY_F7; case SDLK_F8: return KEY_F8; case SDLK_F9: return KEY_F9; case SDLK_F10: return KEY_F10; case SDLK_F11: return KEY_F11; case SDLK_F12: return KEY_F12; case SDLK_PRINT: return KEY_PRTSCR; case SDLK_BACKSPACE: return KEY_BACKSPACE; case SDLK_DELETE: return KEY_DEL; case SDLK_PAUSE: return KEY_PAUSE; #if !SDL_VERSION_ATLEAST(1, 3, 0) case SDLK_EQUALS: return KEY_EQUALS; #endif case SDLK_MINUS: return KEY_MINUS; case SDLK_LSHIFT: case SDLK_RSHIFT: return KEY_RSHIFT; case SDLK_LCTRL: case SDLK_RCTRL: return KEY_RCTRL; case SDLK_LALT: case SDLK_RALT: #if !SDL_VERSION_ATLEAST(1, 3, 0) case SDLK_LMETA: case SDLK_RMETA: #endif return KEY_RALT; case SDLK_CAPSLOCK: return KEY_CAPSLOCK; case SDLK_SCROLLOCK: return KEY_SCRLCK; case SDLK_NUMLOCK: return KEY_NUMLOCK; case SDLK_KP0: return KEYP_0; case SDLK_KP1: return KEYP_1; case SDLK_KP2: return KEYP_2; case SDLK_KP3: return KEYP_3; case SDLK_KP4: return KEYP_4; case SDLK_KP5: return KEYP_5; case SDLK_KP6: return KEYP_6; case SDLK_KP7: return KEYP_7; case SDLK_KP8: return KEYP_8; case SDLK_KP9: return KEYP_9; case SDLK_KP_PERIOD: return KEYP_PERIOD; case SDLK_KP_MULTIPLY: return KEYP_MULTIPLY; case SDLK_KP_PLUS: return KEYP_PLUS; case SDLK_KP_MINUS: return KEYP_MINUS; case SDLK_KP_DIVIDE: return KEYP_DIVIDE; case SDLK_KP_EQUALS: return KEYP_EQUALS; case SDLK_KP_ENTER: return KEYP_ENTER; case SDLK_HOME: return KEY_HOME; case SDLK_INSERT: return KEY_INS; case SDLK_END: return KEY_END; case SDLK_PAGEUP: return KEY_PGUP; case SDLK_PAGEDOWN: return KEY_PGDN; #ifdef SDL_HAVE_APP_KEYS case SDLK_APP1: return KEY_F1; case SDLK_APP2: return KEY_F2; case SDLK_APP3: return KEY_F3; case SDLK_APP4: return KEY_F4; case SDLK_APP5: return KEY_F5; case SDLK_APP6: return KEY_F6; #endif default: return tolower(sym->sym); } } void I_ShutdownGraphics(void) { if (initialized) { SetShowCursor(true); SDL_QuitSubSystem(SDL_INIT_VIDEO); initialized = false; } } void __attribute__ ((weak)) I_GetEvent(void) { } // // I_StartFrame // void I_StartFrame (void) { I_GetEvent(); } static void UpdateMouseButtonState(unsigned int button, boolean on) { event_t event; if (button < SDL_BUTTON_LEFT || button > MAX_MOUSE_BUTTONS) { return; } // Note: button "0" is left, button "1" is right, // button "2" is middle for Doom. This is different // to how SDL sees things. switch (button) { case SDL_BUTTON_LEFT: button = 0; break; case SDL_BUTTON_RIGHT: button = 1; break; case SDL_BUTTON_MIDDLE: button = 2; break; default: // SDL buttons are indexed from 1. --button; break; } // Turn bit representing this button on or off. if (on) { mouse_button_state |= (1 << button); } else { mouse_button_state &= ~(1 << button); } // Post an event with the new button state. event.type = ev_mouse; event.data1 = mouse_button_state; event.data2 = event.data3 = 0; D_PostEvent(&event); } static int AccelerateMouse(int val) { if (val < 0) return -AccelerateMouse(-val); if (val > mouse_threshold) { return (int)((val - mouse_threshold) * mouse_acceleration + mouse_threshold); } else { return val; } } // Get the equivalent ASCII (Unicode?) character for a keypress. static int GetTypedChar(SDL_Event *event) { int key; // If Vanilla keyboard mapping enabled, the keyboard // scan code is used to give the character typed. // This does not change depending on keyboard layout. // If you have a German keyboard, pressing 'z' will // give 'y', for example. It is desirable to be able // to fix this so that people with non-standard // keyboard mappings can type properly. If vanilla // mode is disabled, use the properly translated // version. if (vanilla_keyboard_mapping) { key = TranslateKey(&event->key.keysym); // Is shift held down? If so, perform a translation. if (shiftdown > 0) { if (key >= 0 && key < arrlen(shiftxform)) { key = shiftxform[key]; } else { key = 0; } } return key; } else { // Unicode value, from key layout. return tolower(event->key.keysym.unicode); } } static void UpdateShiftStatus(SDL_Event *event) { int change; if (event->type == SDL_KEYDOWN) { change = 1; } else if (event->type == SDL_KEYUP) { change = -1; } else { return; } if (event->key.keysym.sym == SDLK_LSHIFT || event->key.keysym.sym == SDLK_RSHIFT) { shiftdown += change; } } void I_GetEvent(void) { SDL_Event sdlevent; event_t event; // possibly not needed SDL_PumpEvents(); // put event-grabbing stuff in here while (SDL_PollEvent(&sdlevent)) { // ignore mouse events when the window is not focused if (!window_focused && (sdlevent.type == SDL_MOUSEMOTION || sdlevent.type == SDL_MOUSEBUTTONDOWN || sdlevent.type == SDL_MOUSEBUTTONUP)) { continue; } if (screensaver_mode && sdlevent.type == SDL_QUIT) { I_Quit(); } UpdateShiftStatus(&sdlevent); // process event switch (sdlevent.type) { case SDL_KEYDOWN: // data1 has the key pressed, data2 has the character // (shift-translated, etc) event.type = ev_keydown; event.data1 = TranslateKey(&sdlevent.key.keysym); event.data2 = GetTypedChar(&sdlevent); if (event.data1 != 0) { D_PostEvent(&event); } break; case SDL_KEYUP: event.type = ev_keyup; event.data1 = TranslateKey(&sdlevent.key.keysym); // data2 is just initialized to zero for ev_keyup. // For ev_keydown it's the shifted Unicode character // that was typed, but if something wants to detect // key releases it should do so based on data1 // (key ID), not the printable char. event.data2 = 0; if (event.data1 != 0) { D_PostEvent(&event); } break; /* case SDL_MOUSEMOTION: event.type = ev_mouse; event.data1 = mouse_button_state; event.data2 = AccelerateMouse(sdlevent.motion.xrel); event.data3 = -AccelerateMouse(sdlevent.motion.yrel); D_PostEvent(&event); break; */ case SDL_MOUSEBUTTONDOWN: if (usemouse && !nomouse) { UpdateMouseButtonState(sdlevent.button.button, true); } break; case SDL_MOUSEBUTTONUP: if (usemouse && !nomouse) { UpdateMouseButtonState(sdlevent.button.button, false); } break; case SDL_QUIT: event.type = ev_quit; D_PostEvent(&event); break; case SDL_ACTIVEEVENT: // need to update our focus state UpdateFocus(); break; case SDL_VIDEOEXPOSE: palette_to_set = true; break; case SDL_RESIZABLE: need_resize = true; resize_w = sdlevent.resize.w; resize_h = sdlevent.resize.h; last_resize_time = SDL_GetTicks(); break; default: break; } } } // Warp the mouse back to the middle of the screen static void CenterMouse(void) { // Warp the the screen center SDL_WarpMouse(screen->w / 2, screen->h / 2); // Clear any relative movement caused by warping SDL_PumpEvents(); #if SDL_VERSION_ATLEAST(1, 3, 0) SDL_GetRelativeMouseState(0, NULL, NULL); #else SDL_GetRelativeMouseState(NULL, NULL); #endif } // // Read the change in mouse state to generate mouse motion events // // This is to combine all mouse movement for a tic into one mouse // motion event. static void I_ReadMouse(void) { int x, y; event_t ev; #if SDL_VERSION_ATLEAST(1, 3, 0) SDL_GetRelativeMouseState(0, &x, &y); #else SDL_GetRelativeMouseState(&x, &y); #endif if (x != 0 || y != 0) { ev.type = ev_mouse; ev.data1 = mouse_button_state; ev.data2 = AccelerateMouse(x); if (!novert) { ev.data3 = -AccelerateMouse(y); } else { ev.data3 = 0; } D_PostEvent(&ev); } if (MouseShouldBeGrabbed()) { CenterMouse(); } } // // I_StartTic // void I_StartTic (void) { if (!initialized) { return; } I_GetEvent(); if (usemouse && !nomouse) { I_ReadMouse(); } I_UpdateJoystick(); } // // I_UpdateNoBlit // void I_UpdateNoBlit (void) { // what is this? } static void UpdateGrab(void) { static boolean currently_grabbed = false; boolean grab; grab = MouseShouldBeGrabbed(); if (screensaver_mode) { // Hide the cursor in screensaver mode SetShowCursor(false); } else if (grab && !currently_grabbed) { SetShowCursor(false); CenterMouse(); } else if (!grab && currently_grabbed) { SetShowCursor(true); // When releasing the mouse from grab, warp the mouse cursor to // the bottom-right of the screen. This is a minimally distracting // place for it to appear - we may only have released the grab // because we're at an end of level intermission screen, for // example. SDL_WarpMouse(screen->w - 16, screen->h - 16); SDL_GetRelativeMouseState(NULL, NULL); } currently_grabbed = grab; } // Update a small portion of the screen // // Does stretching and buffer blitting if neccessary // // Return true if blit was successful. static boolean BlitArea(int x1, int y1, int x2, int y2) { int x_offset, y_offset; boolean result; // No blit needed on native surface if (native_surface) { return true; } x_offset = (screenbuffer->w - screen_mode->width) / 2; y_offset = (screenbuffer->h - screen_mode->height) / 2; if (SDL_LockSurface(screenbuffer) >= 0) { I_InitScale(I_VideoBuffer, (byte *) screenbuffer->pixels + (y_offset * screenbuffer->pitch) + x_offset, screenbuffer->pitch); result = screen_mode->DrawScreen(x1, y1, x2, y2); SDL_UnlockSurface(screenbuffer); } else { result = false; } return result; } static void UpdateRect(int x1, int y1, int x2, int y2) { int x1_scaled, x2_scaled, y1_scaled, y2_scaled; // Do stretching and blitting if (BlitArea(x1, y1, x2, y2)) { // Update the area x1_scaled = (x1 * screen_mode->width) / SCREENWIDTH; y1_scaled = (y1 * screen_mode->height) / SCREENHEIGHT; x2_scaled = (x2 * screen_mode->width) / SCREENWIDTH; y2_scaled = (y2 * screen_mode->height) / SCREENHEIGHT; SDL_UpdateRect(screen, x1_scaled, y1_scaled, x2_scaled - x1_scaled, y2_scaled - y1_scaled); } } void I_BeginRead(void) { byte *screenloc = I_VideoBuffer + (SCREENHEIGHT - LOADING_DISK_H) * SCREENWIDTH + (SCREENWIDTH - LOADING_DISK_W); int y; if (!initialized || disk_image == NULL) return; // save background and copy the disk image in for (y=0; y last_resize_time + 500) { ApplyWindowResize(resize_w, resize_h); need_resize = false; palette_to_set = true; } UpdateGrab(); // Don't update the screen if the window isn't visible. // Not doing this breaks under Windows when we alt-tab away // while fullscreen. if (!(SDL_GetAppState() & SDL_APPACTIVE)) return; // draws little dots on the bottom of the screen if (display_fps_dots) { i = I_GetTime(); tics = i - lasttic; lasttic = i; if (tics > 20) tics = 20; for (i=0 ; iw - screenbuffer->w) / 2; dst_rect.y = (screen->h - screenbuffer->h) / 2; SDL_BlitSurface(screenbuffer, NULL, screen, &dst_rect); } SDL_Flip(screen); } // // I_ReadScreen // void I_ReadScreen (byte* scr) { memcpy(scr, I_VideoBuffer, SCREENWIDTH*SCREENHEIGHT); } // // I_SetPalette // void I_SetPalette (byte *doompalette) { int i; for (i=0; i<256; ++i) { // Zero out the bottom two bits of each channel - the PC VGA // controller only supports 6 bits of accuracy. palette[i].r = gammatable[usegamma][*doompalette++] & ~3; palette[i].g = gammatable[usegamma][*doompalette++] & ~3; palette[i].b = gammatable[usegamma][*doompalette++] & ~3; } palette_to_set = true; } // Given an RGB value, find the closest matching palette index. int I_GetPaletteIndex(int r, int g, int b) { int best, best_diff, diff; int i; best = 0; best_diff = INT_MAX; for (i = 0; i < 256; ++i) { diff = (r - palette[i].r) * (r - palette[i].r) + (g - palette[i].g) * (g - palette[i].g) + (b - palette[i].b) * (b - palette[i].b); if (diff < best_diff) { best = i; best_diff = diff; } if (diff == 0) { break; } } return best; } // // Set the window title // void I_SetWindowTitle(char *title) { window_title = title; } // // Call the SDL function to set the window title, based on // the title set with I_SetWindowTitle. // void I_InitWindowTitle(void) { char *buf; buf = M_StringJoin(window_title, " - ", PACKAGE_STRING, NULL); SDL_WM_SetCaption(buf, NULL); free(buf); } // Set the application icon void I_InitWindowIcon(void) { SDL_Surface *surface; Uint8 *mask; int i; // Generate the mask mask = malloc(icon_w * icon_h / 8); memset(mask, 0, icon_w * icon_h / 8); for (i=0; iwidth > w || modes_list[i]->height > h) { continue; } num_pixels = modes_list[i]->width * modes_list[i]->height; if (num_pixels > best_num_pixels) { // This is a better mode than the current one best_mode = modes_list[i]; best_num_pixels = num_pixels; } } return best_mode; } // Adjust to an appropriate fullscreen mode. // Returns true if successful. static boolean AutoAdjustFullscreen(void) { SDL_Rect **modes; SDL_Rect *best_mode; screen_mode_t *screen_mode; int diff, best_diff; int i; modes = SDL_ListModes(NULL, SDL_FULLSCREEN); // No fullscreen modes available at all? if (modes == NULL || modes == (SDL_Rect **) -1 || *modes == NULL) { return false; } // Find the best mode that matches the mode specified in the // configuration file best_mode = NULL; best_diff = INT_MAX; for (i=0; modes[i] != NULL; ++i) { //printf("%ix%i?\n", modes[i]->w, modes[i]->h); // What screen_mode_t would be used for this video mode? screen_mode = I_FindScreenMode(modes[i]->w, modes[i]->h); // Never choose a screen mode that we cannot run in, or // is poor quality for fullscreen if (screen_mode == NULL || screen_mode->poor_quality) { // printf("\tUnsupported / poor quality\n"); continue; } // Do we have the exact mode? // If so, no autoadjust needed if (screen_width == modes[i]->w && screen_height == modes[i]->h) { // printf("\tExact mode!\n"); return true; } // Is this mode better than the current mode? diff = (screen_width - modes[i]->w) * (screen_width - modes[i]->w) + (screen_height - modes[i]->h) * (screen_height - modes[i]->h); if (diff < best_diff) { // printf("\tA valid mode\n"); best_mode = modes[i]; best_diff = diff; } } if (best_mode == NULL) { // Unable to find a valid mode! return false; } printf("I_InitGraphics: %ix%i mode not supported on this machine.\n", screen_width, screen_height); screen_width = best_mode->w; screen_height = best_mode->h; return true; } // Auto-adjust to a valid windowed mode. static void AutoAdjustWindowed(void) { screen_mode_t *best_mode; // Find a screen_mode_t to fit within the current settings best_mode = I_FindScreenMode(screen_width, screen_height); if (best_mode == NULL) { // Nothing fits within the current settings. // Pick the closest to 320x200 possible. best_mode = I_FindScreenMode(SCREENWIDTH, SCREENHEIGHT_4_3); } // Switch to the best mode if necessary. if (best_mode->width != screen_width || best_mode->height != screen_height) { printf("I_InitGraphics: Cannot run at specified mode: %ix%i\n", screen_width, screen_height); screen_width = best_mode->width; screen_height = best_mode->height; } } // Auto-adjust to a valid color depth. static void AutoAdjustColorDepth(void) { SDL_Rect **modes; SDL_PixelFormat format; const SDL_VideoInfo *info; int flags; // If screen_bpp=0, we should use the current (default) pixel depth. // Fetch it from SDL. if (screen_bpp == 0) { info = SDL_GetVideoInfo(); if (info != NULL && info->vfmt != NULL) { screen_bpp = info->vfmt->BitsPerPixel; } } if (fullscreen) { flags = SDL_FULLSCREEN; } else { flags = 0; } format.BitsPerPixel = screen_bpp; format.BytesPerPixel = (screen_bpp + 7) / 8; // Are any screen modes supported at the configured color depth? modes = SDL_ListModes(&format, flags); // If not, we must autoadjust to something sensible. if (modes == NULL) { printf("I_InitGraphics: %ibpp color depth not supported.\n", screen_bpp); info = SDL_GetVideoInfo(); if (info != NULL && info->vfmt != NULL) { screen_bpp = info->vfmt->BitsPerPixel; } } } // If the video mode set in the configuration file is not available, // try to choose a different mode. static void I_AutoAdjustSettings(void) { int old_screen_w, old_screen_h, old_screen_bpp; old_screen_w = screen_width; old_screen_h = screen_height; old_screen_bpp = screen_bpp; // Possibly adjust color depth. AutoAdjustColorDepth(); // If we are running fullscreen, try to autoadjust to a valid fullscreen // mode. If this is impossible, switch to windowed. if (fullscreen && !AutoAdjustFullscreen()) { fullscreen = 0; } // If we are running windowed, pick a valid window size. if (!fullscreen) { AutoAdjustWindowed(); } // Have the settings changed? Show a message. if (screen_width != old_screen_w || screen_height != old_screen_h || screen_bpp != old_screen_bpp) { printf("I_InitGraphics: Auto-adjusted to %ix%ix%ibpp.\n", screen_width, screen_height, screen_bpp); printf("NOTE: Your video settings have been adjusted. " "To disable this behavior,\n" "set autoadjust_video_settings to 0 in your " "configuration file.\n"); } } // Set video size to a particular scale factor (1x, 2x, 3x, etc.) static void SetScaleFactor(int factor) { int w, h; // Pick 320x200 or 320x240, depending on aspect ratio correct if (aspect_ratio_correct) { w = SCREENWIDTH; h = SCREENHEIGHT_4_3; } else { w = SCREENWIDTH; h = SCREENHEIGHT; } screen_width = w * factor; screen_height = h * factor; } void I_GraphicsCheckCommandLine(void) { int i; //! // @vanilla // // Disable blitting the screen. // noblit = M_CheckParm ("-noblit"); //! // @category video // // Grab the mouse when running in windowed mode. // if (M_CheckParm("-grabmouse")) { grabmouse = true; } //! // @category video // // Don't grab the mouse when running in windowed mode. // if (M_CheckParm("-nograbmouse")) { grabmouse = false; } // default to fullscreen mode, allow override with command line // nofullscreen because we love prboom //! // @category video // // Run in a window. // if (M_CheckParm("-window") || M_CheckParm("-nofullscreen")) { fullscreen = false; } //! // @category video // // Run in fullscreen mode. // if (M_CheckParm("-fullscreen")) { fullscreen = true; } //! // @category video // // Disable the mouse. // nomouse = M_CheckParm("-nomouse") > 0; //! // @category video // @arg // // Specify the screen width, in pixels. // i = M_CheckParmWithArgs("-width", 1); if (i > 0) { screen_width = atoi(myargv[i + 1]); } //! // @category video // @arg // // Specify the screen height, in pixels. // i = M_CheckParmWithArgs("-height", 1); if (i > 0) { screen_height = atoi(myargv[i + 1]); } //! // @category video // @arg // // Specify the color depth of the screen, in bits per pixel. // i = M_CheckParmWithArgs("-bpp", 1); if (i > 0) { screen_bpp = atoi(myargv[i + 1]); } // Because we love Eternity: //! // @category video // // Set the color depth of the screen to 32 bits per pixel. // if (M_CheckParm("-8in32")) { screen_bpp = 32; } //! // @category video // @arg [wf] // // Specify the dimensions of the window or fullscreen mode. An // optional letter of w or f appended to the dimensions selects // windowed or fullscreen mode. i = M_CheckParmWithArgs("-geometry", 1); if (i > 0) { int w, h, s; char f; s = sscanf(myargv[i + 1], "%ix%i%1c", &w, &h, &f); if (s == 2 || s == 3) { screen_width = w; screen_height = h; if (s == 3 && f == 'f') { fullscreen = true; } else if (s == 3 && f == 'w') { fullscreen = false; } } } //! // @category video // // Don't scale up the screen. // if (M_CheckParm("-1")) { SetScaleFactor(1); } //! // @category video // // Double up the screen to 2x its normal size. // if (M_CheckParm("-2")) { SetScaleFactor(2); } //! // @category video // // Double up the screen to 3x its normal size. // if (M_CheckParm("-3")) { SetScaleFactor(3); } //! // @category video // // Disable vertical mouse movement. // if (M_CheckParm("-novert")) { novert = true; } //! // @category video // // Enable vertical mouse movement. // if (M_CheckParm("-nonovert")) { novert = false; } } // Check if we have been invoked as a screensaver by xscreensaver. void I_CheckIsScreensaver(void) { char *env; env = getenv("XSCREENSAVER_WINDOW"); if (env != NULL) { screensaver_mode = true; } } static void CreateCursors(void) { static Uint8 empty_cursor_data = 0; // Save the default cursor so it can be recalled later cursors[1] = SDL_GetCursor(); // Create an empty cursor cursors[0] = SDL_CreateCursor(&empty_cursor_data, &empty_cursor_data, 1, 1, 0, 0); } static void SetSDLVideoDriver(void) { // Allow a default value for the SDL video driver to be specified // in the configuration file. if (strcmp(video_driver, "") != 0) { char *env_string; env_string = M_StringJoin("SDL_VIDEODRIVER=", video_driver, NULL); putenv(env_string); free(env_string); } } static void SetWindowPositionVars(void) { char buf[64]; int x, y; if (window_position == NULL || !strcmp(window_position, "")) { return; } if (!strcmp(window_position, "center")) { putenv("SDL_VIDEO_CENTERED=1"); } else if (sscanf(window_position, "%i,%i", &x, &y) == 2) { M_snprintf(buf, sizeof(buf), "SDL_VIDEO_WINDOW_POS=%i,%i", x, y); putenv(buf); } } static char *WindowBoxType(screen_mode_t *mode, int w, int h) { if (mode->width != w && mode->height != h) { return "Windowboxed"; } else if (mode->width == w) { return "Letterboxed"; } else if (mode->height == h) { return "Pillarboxed"; } else { return "..."; } } static void SetVideoMode(screen_mode_t *mode, int w, int h) { byte *doompal; int flags = 0; doompal = W_CacheLumpName(DEH_String("PLAYPAL"), PU_CACHE); // If we are already running and in a true color mode, we need // to free the screenbuffer surface before setting the new mode. if (screenbuffer != NULL && screen != screenbuffer) { SDL_FreeSurface(screenbuffer); } // Generate lookup tables before setting the video mode. if (mode != NULL && mode->InitMode != NULL) { mode->InitMode(doompal); } // Set the video mode. flags |= SDL_SWSURFACE | SDL_DOUBLEBUF; if (screen_bpp == 8) { flags |= SDL_HWPALETTE; } if (fullscreen) { flags |= SDL_FULLSCREEN; } else { // In windowed mode, the window can be resized while the game is // running. This feature is disabled on OS X, as it adds an ugly // scroll handle to the corner of the screen. #ifndef __MACOSX__ flags |= SDL_RESIZABLE; #endif } screen = SDL_SetVideoMode(w, h, screen_bpp, flags); if (screen == NULL) { I_Error("Error setting video mode %ix%ix%ibpp: %s\n", w, h, screen_bpp, SDL_GetError()); } // Blank out the full screen area in case there is any junk in // the borders that won't otherwise be overwritten. SDL_FillRect(screen, NULL, 0); // If mode was not set, it must be set now that we know the // screen size. if (mode == NULL) { mode = I_FindScreenMode(screen->w, screen->h); if (mode == NULL) { I_Error("I_InitGraphics: Unable to find a screen mode small " "enough for %ix%i", screen->w, screen->h); } // Generate lookup tables before setting the video mode. if (mode->InitMode != NULL) { mode->InitMode(doompal); } } // Create the screenbuffer surface; if we have a real 8-bit palettized // screen, then we can use the screen as the screenbuffer. if (screen->format->BitsPerPixel == 8) { screenbuffer = screen; } else { screenbuffer = SDL_CreateRGBSurface(SDL_SWSURFACE, mode->width, mode->height, 8, 0, 0, 0, 0); SDL_FillRect(screenbuffer, NULL, 0); } // Save screen mode. screen_mode = mode; } static void ApplyWindowResize(unsigned int w, unsigned int h) { screen_mode_t *mode; // Find the biggest screen mode that will fall within these // dimensions, falling back to the smallest mode possible if // none is found. mode = I_FindScreenMode(w, h); if (mode == NULL) { mode = I_FindScreenMode(SCREENWIDTH, SCREENHEIGHT); } // Reset mode to resize window. printf("Resize to %ix%i\n", mode->width, mode->height); SetVideoMode(mode, mode->width, mode->height); // Save settings. screen_width = mode->width; screen_height = mode->height; } void I_InitGraphics(void) { SDL_Event dummy; byte *doompal; char *env; // Pass through the XSCREENSAVER_WINDOW environment variable to // SDL_WINDOWID, to embed the SDL window into the Xscreensaver // window. env = getenv("XSCREENSAVER_WINDOW"); if (env != NULL) { char winenv[30]; int winid; sscanf(env, "0x%x", &winid); M_snprintf(winenv, sizeof(winenv), "SDL_WINDOWID=%i", winid); putenv(winenv); } SetSDLVideoDriver(); SetWindowPositionVars(); if (SDL_Init(SDL_INIT_VIDEO) < 0) { I_Error("Failed to initialize video: %s", SDL_GetError()); } // Set up title and icon. Windows cares about the ordering; this // has to be done before the call to SDL_SetVideoMode. I_InitWindowTitle(); #if !SDL_VERSION_ATLEAST(1, 3, 0) I_InitWindowIcon(); #endif // Warning to OS X users... though they might never see it :( #ifdef __MACOSX__ if (fullscreen) { printf("Some old versions of OS X might crash in fullscreen mode.\n" "If this happens to you, switch back to windowed mode.\n"); } #endif // // Enter into graphics mode. // // When in screensaver mode, run full screen and auto detect // screen dimensions (don't change video mode) // if (screensaver_mode) { SetVideoMode(NULL, 0, 0); } else { int w, h; if (autoadjust_video_settings) { I_AutoAdjustSettings(); } w = screen_width; h = screen_height; screen_mode = I_FindScreenMode(w, h); if (screen_mode == NULL) { I_Error("I_InitGraphics: Unable to find a screen mode small " "enough for %ix%i", w, h); } if (w != screen_mode->width || h != screen_mode->height) { printf("I_InitGraphics: %s (%ix%i within %ix%i)\n", WindowBoxType(screen_mode, w, h), screen_mode->width, screen_mode->height, w, h); } SetVideoMode(screen_mode, w, h); } // Start with a clear black screen // (screen will be flipped after we set the palette) SDL_FillRect(screenbuffer, NULL, 0); // Set the palette doompal = W_CacheLumpName(DEH_String("PLAYPAL"), PU_CACHE); I_SetPalette(doompal); SDL_SetColors(screenbuffer, palette, 0, 256); CreateCursors(); UpdateFocus(); UpdateGrab(); // On some systems, it takes a second or so for the screen to settle // after changing modes. We include the option to add a delay when // setting the screen mode, so that the game doesn't start immediately // with the player unable to see anything. if (fullscreen && !screensaver_mode) { SDL_Delay(startup_delay); } // Check if we have a native surface we can use // If we have to lock the screen, draw to a buffer and copy // Likewise if the screen pitch is not the same as the width // If we have to multiply, drawing is done to a separate 320x200 buf native_surface = screen == screenbuffer && !SDL_MUSTLOCK(screen) && screen_mode == &mode_scale_1x && screen->pitch == SCREENWIDTH && aspect_ratio_correct; // If not, allocate a buffer and copy from that buffer to the // screen when we do an update if (native_surface) { I_VideoBuffer = (unsigned char *) screen->pixels; I_VideoBuffer += (screen->h - SCREENHEIGHT) / 2; } else { I_VideoBuffer = (unsigned char *) Z_Malloc (SCREENWIDTH * SCREENHEIGHT, PU_STATIC, NULL); } V_RestoreBuffer(); // Clear the screen to black. memset(I_VideoBuffer, 0, SCREENWIDTH * SCREENHEIGHT); // We need SDL to give us translated versions of keys as well SDL_EnableUNICODE(1); // Repeat key presses - this is what Vanilla Doom does // Not sure about repeat rate - probably dependent on which DOS // driver is used. This is good enough though. SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL); // clear out any events waiting at the start and center the mouse while (SDL_PollEvent(&dummy)); initialized = true; // Call I_ShutdownGraphics on quit I_AtExit(I_ShutdownGraphics, true); } // Bind all variables controlling video options into the configuration // file system. void I_BindVideoVariables(void) { M_BindVariable("use_mouse", &usemouse); M_BindVariable("autoadjust_video_settings", &autoadjust_video_settings); M_BindVariable("fullscreen", &fullscreen); M_BindVariable("aspect_ratio_correct", &aspect_ratio_correct); M_BindVariable("startup_delay", &startup_delay); M_BindVariable("screen_width", &screen_width); M_BindVariable("screen_height", &screen_height); M_BindVariable("screen_bpp", &screen_bpp); M_BindVariable("grabmouse", &grabmouse); M_BindVariable("mouse_acceleration", &mouse_acceleration); M_BindVariable("mouse_threshold", &mouse_threshold); M_BindVariable("video_driver", &video_driver); M_BindVariable("window_position", &window_position); M_BindVariable("usegamma", &usegamma); M_BindVariable("vanilla_keyboard_mapping", &vanilla_keyboard_mapping); M_BindVariable("novert", &novert); M_BindVariable("png_screenshots", &png_screenshots); // Windows Vista or later? Set screen color depth to // 32 bits per pixel, as 8-bit palettized screen modes // don't work properly in recent versions. #if defined(_WIN32) && !defined(_WIN32_WCE) { OSVERSIONINFOEX version_info; ZeroMemory(&version_info, sizeof(OSVERSIONINFOEX)); version_info.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX); GetVersionEx((OSVERSIONINFO *) &version_info); if (version_info.dwPlatformId == VER_PLATFORM_WIN32_NT && version_info.dwMajorVersion >= 6) { screen_bpp = 32; } } #endif // Disable fullscreen by default on OS X, as there is an SDL bug // where some old versions of OS X (<= Snow Leopard) crash. #ifdef __MACOSX__ fullscreen = 0; screen_width = 800; screen_height = 600; #endif } ================================================ FILE: fbdoom/i_video.h ================================================ // // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // 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. // // DESCRIPTION: // System specific interface stuff. // #ifndef __I_VIDEO__ #define __I_VIDEO__ #include "doomtype.h" // Screen width and height. #define SCREENWIDTH 320 #define SCREENHEIGHT 200 // Screen width used for "squash" scale functions #define SCREENWIDTH_4_3 256 // Screen height used for "stretch" scale functions. #define SCREENHEIGHT_4_3 240 #define MAX_MOUSE_BUTTONS 8 typedef struct { // Screen width and height int width; int height; // Initialisation function to call when using this mode. // Called with a pointer to the Doom palette. // // If NULL, no init function is called. void (*InitMode)(byte *palette); // Function to call to draw the screen from the source buffer. // Return true if draw was successful. boolean (*DrawScreen)(int x1, int y1, int x2, int y2); // If true, this is a "poor quality" mode. The autoadjust // code should always attempt to use a different mode to this // mode in fullscreen. // // Some notes about what "poor quality" means in this context: // // The aspect ratio correction works by scaling up to the larger // screen size and then drawing pixels on the edges between the // "virtual" pixels so that an authentic blocky look-and-feel is // achieved. // // For a mode like 640x480, you can imagine the grid of the // "original" pixels spaced out, with extra "blurry" pixels added // in the space between them to fill it out. However, when you're // running at a resolution like 320x240, this is not the case. In // the small screen case, every single pixel has to be a blurry // interpolation of two pixels from the original image. // // If you run in 320x240 and put your face up close to the screen // you can see this: it's particularly visible in the small yellow // status bar numbers for example. Overall it still looks "okay" // but there's an obvious - albeit small - deterioration in // quality. // // Once you get to 640x480, all the original pixels are there at // least once and it's okay (the higher the resolution, the more // accurate it is). When I first wrote the code I was expecting // that even higher resolutions would be needed before it would // look acceptable, but it turned out to be okay even at 640x480. boolean poor_quality; } screen_mode_t; typedef boolean (*grabmouse_callback_t)(void); // Called by D_DoomMain, // determines the hardware configuration // and sets up the video mode void I_InitGraphics (void); void I_GraphicsCheckCommandLine(void); void I_ShutdownGraphics(void); // Takes full 8 bit values. void I_SetPalette (byte* palette); int I_GetPaletteIndex(int r, int g, int b); void I_UpdateNoBlit (void); void I_FinishUpdate (void); void I_ReadScreen (byte* scr); void I_BeginRead (void); void I_EndRead(void); void I_SetWindowTitle(char *title); void I_CheckIsScreensaver(void); void I_SetGrabMouseCallback(grabmouse_callback_t func); void I_DisplayFPSDots(boolean dots_on); void I_BindVideoVariables(void); void I_InitWindowTitle(void); void I_InitWindowIcon(void); // Called before processing any tics in a frame (just after displaying a frame). // Time consuming syncronous operations are performed here (joystick reading). void I_StartFrame (void); // Called before processing each tic in a frame. // Quick syncronous operations are performed here. void I_StartTic (void); // Enable the loading disk image displayed when reading from disk. void I_EnableLoadingDisk(void); extern char *video_driver; extern boolean screenvisible; extern float mouse_acceleration; extern int mouse_threshold; extern int vanilla_keyboard_mapping; extern boolean screensaver_mode; extern int usegamma; extern byte *I_VideoBuffer; extern int screen_width; extern int screen_height; extern int screen_bpp; extern int fullscreen; extern int aspect_ratio_correct; extern int show_diskicon; extern int diskicon_readbytes; #endif ================================================ FILE: fbdoom/i_video_fbdev.c ================================================ // Emacs style mode select -*- C++ -*- //----------------------------------------------------------------------------- // // $Id:$ // // Copyright (C) 1993-1996 by id Software, Inc. // // 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. // // $Log:$ // // DESCRIPTION: // DOOM graphics stuff for X11, UNIX. // //----------------------------------------------------------------------------- static const char rcsid[] = "$Id: i_x.c,v 1.6 1997/02/03 22:45:10 b1 Exp $"; #include "config.h" #include "v_video.h" #include "m_argv.h" #include "d_event.h" #include "d_main.h" #include "i_video.h" #include "z_zone.h" #include "tables.h" #include "doomkeys.h" #include #include #include #include #include #include #include #include #include #include //#define CMAP256 struct fb_var_screeninfo fb = {}; int fb_scaling = 1; int usemouse = 0; struct color { uint32_t b:8; uint32_t g:8; uint32_t r:8; uint32_t a:8; }; static struct color colors[256]; // The screen buffer; this is modified to draw things to the screen byte *I_VideoBuffer = NULL; byte *I_VideoBuffer_FB = NULL; /* framebuffer file descriptor */ int fd_fb = 0; int X_width; int X_height; // If true, game is running as a screensaver boolean screensaver_mode = false; // Flag indicating whether the screen is currently visible: // when the screen isnt visible, don't render the screen boolean screenvisible; // Mouse acceleration // // This emulates some of the behavior of DOS mouse drivers by increasing // the speed when the mouse is moved fast. // // The mouse input values are input directly to the game, but when // the values exceed the value of mouse_threshold, they are multiplied // by mouse_acceleration to increase the speed. float mouse_acceleration = 2.0; int mouse_threshold = 10; // Gamma correction level to use int usegamma = 0; typedef struct { byte r; byte g; byte b; } col_t; // Palette converted to RGB565 static uint16_t rgb565_palette[256]; void cmap_to_rgb565(uint16_t * out, uint8_t * in, int in_pixels) { int i, j; struct color c; uint16_t r, g, b; for (i = 0; i < in_pixels; i++) { c = colors[*in]; r = ((uint16_t)(c.r >> 3)) << 11; g = ((uint16_t)(c.g >> 2)) << 5; b = ((uint16_t)(c.b >> 3)) << 0; *out = (r | g | b); in++; for (j = 0; j < fb_scaling; j++) { out++; } } } void cmap_to_fb(uint8_t * out, uint8_t * in, int in_pixels) { int i, j, k; struct color c; uint32_t pix; uint16_t r, g, b; for (i = 0; i < in_pixels; i++) { c = colors[*in]; /* R:8 G:8 B:8 format! */ r = (uint16_t)(c.r >> (8 - fb.red.length)); g = (uint16_t)(c.g >> (8 - fb.green.length)); b = (uint16_t)(c.b >> (8 - fb.blue.length)); pix = r << fb.red.offset; pix |= g << fb.green.offset; pix |= b << fb.blue.offset; for (k = 0; k < fb_scaling; k++) { for (j = 0; j < fb.bits_per_pixel/8; j++) { *out = (pix >> (j*8)); out++; } } in++; } } void I_InitGraphics (void) { int i; /* Open fbdev file descriptor */ fd_fb = open("/dev/fb0", O_RDWR); if (fd_fb < 0) { printf("Could not open /dev/fb0"); exit(-1); } /* fetch framebuffer info */ ioctl(fd_fb, FBIOGET_VSCREENINFO, &fb); /* change params if needed */ //ioctl(fd_fb, FBIOPUT_VSCREENINFO, &fb); printf("I_InitGraphics: framebuffer: x_res: %d, y_res: %d, x_virtual: %d, y_virtual: %d, bpp: %d, grayscale: %d\n", fb.xres, fb.yres, fb.xres_virtual, fb.yres_virtual, fb.bits_per_pixel, fb.grayscale); printf("I_InitGraphics: framebuffer: RGBA: %d%d%d%d, red_off: %d, green_off: %d, blue_off: %d, transp_off: %d\n", fb.red.length, fb.green.length, fb.blue.length, fb.transp.length, fb.red.offset, fb.green.offset, fb.blue.offset, fb.transp.offset); printf("I_InitGraphics: DOOM screen size: w x h: %d x %d\n", SCREENWIDTH, SCREENHEIGHT); i = M_CheckParmWithArgs("-scaling", 1); if (i > 0) { i = atoi(myargv[i + 1]); fb_scaling = i; printf("I_InitGraphics: Scaling factor: %d\n", fb_scaling); } else { fb_scaling = fb.xres / SCREENWIDTH; if (fb.yres / SCREENHEIGHT < fb_scaling) fb_scaling = fb.yres / SCREENHEIGHT; printf("I_InitGraphics: Auto-scaling factor: %d\n", fb_scaling); } /* Allocate screen to draw to */ I_VideoBuffer = (byte*)Z_Malloc (SCREENWIDTH * SCREENHEIGHT, PU_STATIC, NULL); // For DOOM to draw on I_VideoBuffer_FB = (byte*)malloc(fb.xres * fb.yres * (fb.bits_per_pixel/8)); // For a single write() syscall to fbdev screenvisible = true; extern int I_InitInput(void); I_InitInput(); } void I_ShutdownGraphics (void) { Z_Free (I_VideoBuffer); free(I_VideoBuffer_FB); } void I_StartFrame (void) { } __attribute__ ((weak)) void I_GetEvent (void) { // event_t event; // bool button_state; // // button_state = button_read (); // // if (last_button_state != button_state) // { // last_button_state = button_state; // // event.type = last_button_state ? ev_keydown : ev_keyup; // event.data1 = KEY_FIRE; // event.data2 = -1; // event.data3 = -1; // // D_PostEvent (&event); // } // // touch_main (); // // if ((touch_state.x != last_touch_state.x) || (touch_state.y != last_touch_state.y) || (touch_state.status != last_touch_state.status)) // { // last_touch_state = touch_state; // // event.type = (touch_state.status == TOUCH_PRESSED) ? ev_keydown : ev_keyup; // event.data1 = -1; // event.data2 = -1; // event.data3 = -1; // // if ((touch_state.x > 49) // && (touch_state.x < 72) // && (touch_state.y > 104) // && (touch_state.y < 143)) // { // // select weapon // if (touch_state.x < 60) // { // // lower row (5-7) // if (touch_state.y < 119) // { // event.data1 = '5'; // } // else if (touch_state.y < 131) // { // event.data1 = '6'; // } // else // { // event.data1 = '1'; // } // } // else // { // // upper row (2-4) // if (touch_state.y < 119) // { // event.data1 = '2'; // } // else if (touch_state.y < 131) // { // event.data1 = '3'; // } // else // { // event.data1 = '4'; // } // } // } // else if (touch_state.x < 40) // { // // button bar at bottom screen // if (touch_state.y < 40) // { // // enter // event.data1 = KEY_ENTER; // } // else if (touch_state.y < 80) // { // // escape // event.data1 = KEY_ESCAPE; // } // else if (touch_state.y < 120) // { // // use // event.data1 = KEY_USE; // } // else if (touch_state.y < 160) // { // // map // event.data1 = KEY_TAB; // } // else if (touch_state.y < 200) // { // // pause // event.data1 = KEY_PAUSE; // } // else if (touch_state.y < 240) // { // // toggle run // if (touch_state.status == TOUCH_PRESSED) // { // run = !run; // // event.data1 = KEY_RSHIFT; // // if (run) // { // event.type = ev_keydown; // } // else // { // event.type = ev_keyup; // } // } // else // { // return; // } // } // else if (touch_state.y < 280) // { // // save // event.data1 = KEY_F2; // } // else if (touch_state.y < 320) // { // // load // event.data1 = KEY_F3; // } // } // else // { // // movement/menu navigation // if (touch_state.x < 100) // { // if (touch_state.y < 100) // { // event.data1 = KEY_STRAFE_L; // } // else if (touch_state.y < 220) // { // event.data1 = KEY_DOWNARROW; // } // else // { // event.data1 = KEY_STRAFE_R; // } // } // else if (touch_state.x < 180) // { // if (touch_state.y < 160) // { // event.data1 = KEY_LEFTARROW; // } // else // { // event.data1 = KEY_RIGHTARROW; // } // } // else // { // event.data1 = KEY_UPARROW; // } // } // // D_PostEvent (&event); // } } __attribute__ ((weak)) void I_StartTic (void) { I_GetEvent(); } void I_UpdateNoBlit (void) { } // // I_FinishUpdate // void I_FinishUpdate (void) { int y; int x_offset, y_offset, x_offset_end; unsigned char *line_in, *line_out; /* Offsets in case FB is bigger than DOOM */ /* 600 = fb heigt, 200 screenheight */ /* 600 = fb heigt, 200 screenheight */ /* 2048 =fb width, 320 screenwidth */ y_offset = (((fb.yres - (SCREENHEIGHT * fb_scaling)) * fb.bits_per_pixel/8)) / 2; x_offset = (((fb.xres - (SCREENWIDTH * fb_scaling)) * fb.bits_per_pixel/8)) / 2; // XXX: siglent FB hack: /4 instead of /2, since it seems to handle the resolution in a funny way //x_offset = 0; x_offset_end = ((fb.xres - (SCREENWIDTH * fb_scaling)) * fb.bits_per_pixel/8) - x_offset; /* DRAW SCREEN */ line_in = (unsigned char *) I_VideoBuffer; line_out = (unsigned char *) I_VideoBuffer_FB; y = SCREENHEIGHT; while (y--) { int i; for (i = 0; i < fb_scaling; i++) { line_out += x_offset; #ifdef CMAP256 for (fb_scaling == 1) { memcpy(line_out, line_in, SCREENWIDTH); /* fb_width is bigger than Doom SCREENWIDTH... */ } else { //XXX FIXME fb_scaling support! } #else //cmap_to_rgb565((void*)line_out, (void*)line_in, SCREENWIDTH); cmap_to_fb((void*)line_out, (void*)line_in, SCREENWIDTH); #endif line_out += (SCREENWIDTH * fb_scaling * (fb.bits_per_pixel/8)) + x_offset_end; } line_in += SCREENWIDTH; } /* Start drawing from y-offset */ lseek(fd_fb, y_offset * fb.xres, SEEK_SET); write(fd_fb, I_VideoBuffer_FB, (SCREENHEIGHT * fb_scaling * (fb.bits_per_pixel/8)) * fb.xres); /* draw only portion used by doom + x-offsets */ } // // I_ReadScreen // void I_ReadScreen (byte* scr) { memcpy (scr, I_VideoBuffer, SCREENWIDTH * SCREENHEIGHT); } // // I_SetPalette // #define GFX_RGB565(r, g, b) ((((r & 0xF8) >> 3) << 11) | (((g & 0xFC) >> 2) << 5) | ((b & 0xF8) >> 3)) #define GFX_RGB565_R(color) ((0xF800 & color) >> 11) #define GFX_RGB565_G(color) ((0x07E0 & color) >> 5) #define GFX_RGB565_B(color) (0x001F & color) void I_SetPalette (byte* palette) { int i; //col_t* c; //for (i = 0; i < 256; i++) //{ // c = (col_t*)palette; // rgb565_palette[i] = GFX_RGB565(gammatable[usegamma][c->r], // gammatable[usegamma][c->g], // gammatable[usegamma][c->b]); // palette += 3; //} /* performance boost: * map to the right pixel format over here! */ for (i=0; i<256; ++i ) { colors[i].a = 0; colors[i].r = gammatable[usegamma][*palette++]; colors[i].g = gammatable[usegamma][*palette++]; colors[i].b = gammatable[usegamma][*palette++]; } /* Set new color map in kernel framebuffer driver */ //XXX FIXME ioctl(fd_fb, IOCTL_FB_PUTCMAP, colors); } // Given an RGB value, find the closest matching palette index. int I_GetPaletteIndex (int r, int g, int b) { int best, best_diff, diff; int i; col_t color; printf("I_GetPaletteIndex\n"); best = 0; best_diff = INT_MAX; for (i = 0; i < 256; ++i) { color.r = GFX_RGB565_R(rgb565_palette[i]); color.g = GFX_RGB565_G(rgb565_palette[i]); color.b = GFX_RGB565_B(rgb565_palette[i]); diff = (r - color.r) * (r - color.r) + (g - color.g) * (g - color.g) + (b - color.b) * (b - color.b); if (diff < best_diff) { best = i; best_diff = diff; } if (diff == 0) { break; } } return best; } void I_BeginRead (void) { } void I_EndRead (void) { } void I_SetWindowTitle (char *title) { } void I_GraphicsCheckCommandLine (void) { } void I_SetGrabMouseCallback (grabmouse_callback_t func) { } void I_EnableLoadingDisk(void) { } void I_BindVideoVariables (void) { } void I_DisplayFPSDots (boolean dots_on) { } void I_CheckIsScreensaver (void) { } ================================================ FILE: fbdoom/icon.c ================================================ static int icon_w = 32; static int icon_h = 32; static unsigned char icon_data[] = { 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0xa2,0x86,0x73, 0xa9,0x8d,0x7a, 0xbd,0xa0,0x8c, 0xda,0xba,0xa0, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0xbd,0x8d,0x67, 0xd7,0xb9,0xa5, 0xeb,0xd8,0xcd, 0xd3,0xbf,0xae, 0xbd,0xa0,0x8c, 0xeb,0xd8,0xcd, 0xc2,0x9d,0x86, 0x95,0x5d,0x38, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x9b,0x7e,0x66, 0xc5,0x9e,0x81, 0xd3,0xb3,0x99, 0xd4,0xac,0x8e, 0xee,0xdc,0xd1, 0xb9,0x93,0x76, 0xad,0x71,0x45, 0xd4,0xac,0x8e, 0xb9,0x93,0x76, 0xa3,0x77,0x58, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x39,0x1d,0x2d, 0x55,0x20,0x22, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0xda,0xb4,0x9c, 0xd3,0xa3,0x83, 0xaf,0x91,0x78, 0xa7,0x83,0x6d, 0xc4,0xa7,0x93, 0xee,0xe2,0xd5, 0xeb,0xd8,0xcd, 0x8c,0x60,0x3d, 0x9b,0x7e,0x66, 0xce,0x9f,0x7e, 0x84,0x54,0x33, 0xba,0x83,0x5b, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x24,0x1c,0x35, 0x00,0x0f,0x32, 0x29,0x18,0x2e, 0x55,0x20,0x22, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0xd3,0xb3,0x99, 0xca,0x93,0x6f, 0xc4,0x94,0x6e, 0x98,0x66,0x45, 0x78,0x50,0x2d, 0xd7,0xb9,0xa5, 0xee,0xdc,0xd1, 0xc4,0x9b,0x79, 0xa1,0x6d,0x45, 0x66,0x40,0x24, 0xb8,0x7a,0x4f, 0xcf,0xa6,0x83, 0x98,0x6d,0x4e, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x30,0x1c,0x2f, 0x08,0x13,0x30, 0x00,0x0f,0x32, 0x00,0x0f,0x32, 0x39,0x1d,0x2d, 0x52,0x1c,0x1a, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x9e,0x7b,0x65, 0xb9,0x89,0x64, 0xaa,0x7d,0x5e, 0x9e,0x72,0x53, 0x88,0x5e,0x40, 0xc4,0xa7,0x93, 0xb9,0x89,0x64, 0x90,0x6c,0x51, 0x7f,0x50,0x2f, 0x90,0x5e,0x37, 0x75,0x4d,0x30, 0x7f,0x50,0x2f, 0xd3,0xa3,0x83, 0xd4,0xac,0x8e, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x29,0x18,0x2e, 0x08,0x13,0x30, 0x08,0x13,0x30, 0x08,0x13,0x30, 0x00,0x0f,0x32, 0x08,0x13,0x30, 0x49,0x1e,0x2b, 0x49,0x1a,0x16, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0xda,0xba,0xa0, 0xd4,0xac,0x8e, 0xc4,0x9b,0x79, 0xaa,0x7d,0x5e, 0xaa,0x7d,0x5e, 0xbd,0xa0,0x8c, 0x8c,0x60,0x3d, 0x70,0x49,0x2c, 0x89,0x60,0x42, 0x57,0x38,0x20, 0x6c,0x45,0x29, 0x66,0x40,0x24, 0x51,0x35,0x21, 0x7e,0x55,0x38, 0xce,0x9f,0x7e, 0xc2,0x8a,0x61, 0x00,0x00,0x00, 0x30,0x1c,0x2f, 0x00,0x0f,0x32, 0x00,0x0f,0x32, 0x08,0x13,0x30, 0x00,0x0f,0x32, 0x00,0x0f,0x32, 0x08,0x13,0x30, 0x08,0x13,0x30, 0x59,0x25,0x2b, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0xcb,0x9a,0x74, 0xb7,0x81,0x58, 0x8c,0x60,0x3d, 0x79,0x4b,0x2b, 0x89,0x58,0x31, 0x89,0x58,0x31, 0x7f,0x50,0x2f, 0x9e,0x64,0x39, 0x75,0x4c,0x2a, 0x51,0x35,0x21, 0x84,0x54,0x33, 0x54,0x36,0x1d, 0x98,0x6d,0x4e, 0xb4,0x7f,0x5c, 0xba,0x83,0x5b, 0xb8,0x7a,0x4f, 0x00,0x00,0x00, 0x3e,0x28,0x36, 0x08,0x13,0x30, 0x00,0x0f,0x32, 0x08,0x13,0x30, 0x00,0x0f,0x32, 0x00,0x0f,0x32, 0x00,0x0f,0x32, 0x08,0x13,0x30, 0x20,0x1f,0x36, 0x35,0x19,0x12, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0xc2,0x8a,0x61, 0x89,0x60,0x42, 0x84,0x54,0x33, 0x7f,0x50,0x2f, 0x86,0x56,0x35, 0x8d,0x5b,0x35, 0x75,0x4c,0x2a, 0x8d,0x5b,0x35, 0x5c,0x38,0x22, 0x5e,0x3f,0x27, 0x75,0x4d,0x30, 0x9d,0x64,0x3f, 0x75,0x4c,0x2a, 0x78,0x50,0x2d, 0x7f,0x50,0x2f, 0xb7,0x81,0x58, 0x00,0x00,0x00, 0x46,0x35,0x42, 0x04,0x18,0x3a, 0x08,0x13,0x30, 0x5d,0x30,0x28, 0x20,0x1f,0x36, 0x08,0x13,0x30, 0x08,0x13,0x30, 0x04,0x18,0x3a, 0x19,0x1c,0x37, 0x3a,0x1d,0x16, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x91,0x5f,0x3e, 0x84,0x54,0x33, 0x89,0x58,0x31, 0x7e,0x6e,0x64, 0xc4,0x94,0x6e, 0x78,0x50,0x2d, 0x92,0x6f,0x59, 0xa1,0x7c,0x60, 0x9c,0x6f,0x4b, 0x8d,0x5b,0x35, 0xbc,0x7f,0x53, 0xad,0x71,0x45, 0x75,0x4d,0x30, 0x51,0x35,0x21, 0x4b,0x2f,0x1c, 0x70,0x49,0x2c, 0x00,0x00,0x00, 0x59,0x44,0x4d, 0x1e,0x28,0x42, 0x1e,0x28,0x42, 0x48,0x19,0x10, 0x42,0x19,0x12, 0x53,0x2b,0x30, 0x0c,0x26,0x48, 0x1e,0x28,0x42, 0x24,0x2d,0x48, 0x5f,0x2c,0x1d, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x89,0x58,0x31, 0xa1,0x84,0x6c, 0xc4,0x94,0x6e, 0x88,0x64,0x44, 0xb5,0x8f,0x73, 0x9e,0x72,0x53, 0xa1,0x6d,0x45, 0x93,0x60,0x3a, 0xad,0x71,0x45, 0xb4,0x7f,0x5c, 0xbd,0x8d,0x67, 0xc2,0x8a,0x61, 0xb3,0x76,0x4b, 0xb8,0x7a,0x4f, 0x88,0x64,0x44, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x61,0x50,0x52, 0x1c,0x34,0x52, 0x1c,0x34,0x52, 0x54,0x27,0x16, 0x29,0x17,0x09, 0x5d,0x30,0x28, 0x1c,0x34,0x52, 0x1c,0x34,0x52, 0x24,0x35,0x4f, 0x69,0x34,0x24, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0xa7,0x83,0x6d, 0xac,0x86,0x6a, 0x74,0x47,0x2d, 0x84,0x54,0x33, 0x5c,0x38,0x22, 0x54,0x36,0x1d, 0x6c,0x45,0x29, 0x96,0x63,0x3c, 0xa3,0x6e,0x41, 0xb3,0x76,0x4b, 0xb3,0x76,0x4b, 0xa2,0x68,0x3d, 0x7c,0x4e,0x2d, 0x63,0x3e,0x27, 0x96,0x63,0x3c, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x6b,0x5d,0x59, 0x22,0x42,0x5f, 0x22,0x42,0x5f, 0x5d,0x34,0x1a, 0x38,0x23,0x0f, 0x5c,0x38,0x22, 0x22,0x42,0x5f, 0x22,0x42,0x5f, 0x2c,0x45,0x5e, 0x6f,0x3e,0x2b, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0xb7,0x81,0x58, 0x98,0x74,0x59, 0x6c,0x45,0x29, 0x4b,0x35,0x25, 0x78,0x50,0x2d, 0x78,0x50,0x2d, 0x78,0x50,0x2d, 0x7f,0x50,0x2f, 0x84,0x54,0x33, 0x8d,0x5b,0x35, 0x96,0x63,0x3c, 0x74,0x47,0x2d, 0x65,0x45,0x26, 0x65,0x45,0x26, 0x7c,0x4e,0x2d, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x77,0x69,0x64, 0x30,0x4e,0x6d, 0x32,0x52,0x6b, 0x69,0x42,0x26, 0x49,0x31,0x11, 0x6c,0x47,0x2f, 0x27,0x4f,0x6d, 0x27,0x4f,0x6d, 0x32,0x52,0x6b, 0x70,0x49,0x2c, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0xb7,0x81,0x58, 0x8a,0x5a,0x39, 0x8a,0x5a,0x39, 0x91,0x5f,0x3e, 0x5e,0x3f,0x27, 0x5c,0x38,0x22, 0x89,0x58,0x31, 0x89,0x58,0x31, 0x95,0x5d,0x38, 0x9d,0x64,0x3f, 0x65,0x45,0x26, 0x4b,0x2f,0x1c, 0x7f,0x50,0x2f, 0x78,0x50,0x2d, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x77,0x69,0x64, 0x36,0x5c,0x7a, 0x3e,0x5e,0x78, 0x76,0x52,0x2e, 0x5d,0x42,0x22, 0x75,0x4d,0x30, 0x36,0x5c,0x7a, 0x36,0x5c,0x7a, 0x3e,0x5e,0x78, 0x74,0x47,0x2d, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x89,0x58,0x31, 0x63,0x3e,0x27, 0xa8,0x6d,0x42, 0x4b,0x2f,0x1c, 0x65,0x45,0x26, 0x70,0x49,0x2c, 0x51,0x35,0x21, 0x78,0x50,0x2d, 0x42,0x30,0x14, 0x49,0x31,0x11, 0x59,0x44,0x22, 0x7c,0x5c,0x2a, 0x8a,0x71,0x27, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x71,0x67,0x5c, 0x37,0x52,0x66, 0x3f,0x55,0x64, 0x80,0x55,0x27, 0x64,0x4c,0x1f, 0x7e,0x59,0x2e, 0x37,0x52,0x66, 0x37,0x52,0x66, 0x3f,0x55,0x64, 0x6c,0x47,0x2f, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x70,0x49,0x2c, 0x65,0x45,0x26, 0x65,0x45,0x26, 0x63,0x3e,0x27, 0x76,0x4d,0x25, 0x5d,0x42,0x22, 0x5e,0x3f,0x27, 0x4e,0x43,0x18, 0x4e,0x43,0x18, 0x6a,0x5b,0x1c, 0x4e,0x43,0x18, 0x5f,0x51,0x19, 0x8a,0x76,0x2a, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x63,0x38,0x19, 0x34,0x11,0x04, 0x32,0x0f,0x00, 0x86,0x58,0x1e, 0x74,0x59,0x25, 0x86,0x58,0x1e, 0x34,0x15,0x00, 0x32,0x0f,0x00, 0x34,0x15,0x00, 0x4e,0x31,0x18, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x7e,0x6c,0x27, 0x5a,0x4d,0x1c, 0x4d,0x3e,0x15, 0x67,0x58,0x21, 0x5a,0x4d,0x1c, 0x57,0x4b,0x1a, 0x5f,0x51,0x19, 0x64,0x55,0x1e, 0x5a,0x4d,0x1c, 0x8a,0x71,0x27, 0x8e,0x79,0x26, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x63,0x38,0x19, 0x34,0x11,0x04, 0x32,0x0f,0x00, 0x8d,0x63,0x1f, 0x83,0x66,0x2c, 0x8d,0x63,0x1f, 0x32,0x0f,0x00, 0x35,0x19,0x12, 0x34,0x11,0x04, 0x53,0x3a,0x20, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0xac,0x93,0x39, 0x76,0x65,0x20, 0x6a,0x5b,0x1c, 0x6a,0x5b,0x1c, 0x67,0x58,0x21, 0x4e,0x43,0x18, 0x4e,0x43,0x18, 0x9b,0x85,0x32, 0xb8,0x9e,0x3c, 0xb1,0x8d,0x36, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x63,0x38,0x19, 0x34,0x15,0x00, 0x32,0x0f,0x00, 0x8d,0x63,0x1f, 0x83,0x66,0x2c, 0x8d,0x63,0x1f, 0x32,0x0f,0x00, 0x32,0x0f,0x00, 0x34,0x15,0x00, 0x53,0x3a,0x20, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0xb0,0x96,0x34, 0x76,0x65,0x20, 0x7e,0x66,0x23, 0x8e,0x79,0x26, 0x8a,0x71,0x27, 0x7e,0x6c,0x27, 0x8a,0x71,0x27, 0x8a,0x71,0x27, 0xb0,0x96,0x34, 0x98,0x82,0x2f, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x75,0x4c,0x2a, 0x38,0x19,0x05, 0x38,0x19,0x05, 0x99,0x6d,0x22, 0x96,0x70,0x2a, 0x99,0x6d,0x22, 0x38,0x19,0x05, 0x38,0x19,0x05, 0x38,0x19,0x05, 0x59,0x3f,0x25, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0xac,0x93,0x39, 0x8a,0x76,0x2a, 0x7e,0x66,0x23, 0x76,0x65,0x20, 0x93,0x7d,0x2a, 0x82,0x6f,0x23, 0x9f,0x88,0x35, 0xb8,0xa0,0x4c, 0xb8,0xa0,0x4c, 0xc4,0xa8,0x3f, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x7c,0x5c,0x2a, 0x52,0x2e,0x0d, 0x52,0x2e,0x0d, 0xa4,0x7b,0x27, 0xa1,0x80,0x37, 0x9f,0x77,0x1a, 0x52,0x2e,0x0d, 0x52,0x2e,0x0d, 0x52,0x2e,0x0d, 0x5f,0x4e,0x2a, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x86,0x72,0x26, 0xac,0x93,0x39, 0x97,0x82,0x36, 0xb1,0x8d,0x36, 0xac,0x93,0x39, 0x97,0x82,0x36, 0xa4,0x8c,0x32, 0xbd,0xa2,0x41, 0x8a,0x71,0x27, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x8b,0x6d,0x32, 0x66,0x44,0x14, 0x6d,0x4a,0x20, 0xab,0x86,0x29, 0xb1,0x8d,0x36, 0xa4,0x7b,0x27, 0x66,0x44,0x14, 0x66,0x44,0x14, 0x66,0x44,0x14, 0x69,0x56,0x2c, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0xac,0x93,0x31, 0x7e,0x6c,0x27, 0x9f,0x88,0x35, 0x97,0x82,0x36, 0x7e,0x66,0x23, 0x7e,0x66,0x23, 0xb2,0x99,0x3f, 0xbd,0xa2,0x41, 0x8a,0x76,0x2a, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x99,0x7a,0x38, 0x86,0x58,0x1e, 0x7f,0x59,0x22, 0xb2,0x8b,0x1c, 0x94,0x6e,0x21, 0x7f,0x59,0x22, 0x7f,0x59,0x22, 0x7f,0x59,0x22, 0x7f,0x59,0x22, 0x69,0x56,0x2c, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0xac,0x93,0x39, 0x8a,0x71,0x27, 0xb4,0x9c,0x48, 0x7e,0x66,0x23, 0xac,0x93,0x39, 0x9c,0x87,0x3a, 0x9c,0x87,0x3a, 0xbd,0xa2,0x41, 0x8e,0x79,0x26, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0xa8,0x86,0x3d, 0x96,0x70,0x2a, 0x96,0x70,0x2a, 0x96,0x70,0x2a, 0x96,0x70,0x2a, 0x96,0x70,0x2a, 0x96,0x70,0x2a, 0x96,0x70,0x2a, 0xa1,0x80,0x37, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0xb4,0x9c,0x48, 0xac,0x93,0x31, 0x93,0x7d,0x2a, 0xbd,0xa3,0x48, 0x93,0x7d,0x2a, 0xb8,0xa0,0x4c, 0xb4,0x9c,0x48, 0xcc,0xa5,0x4e, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0xa8,0x86,0x3d, 0xaf,0x85,0x31, 0xaf,0x85,0x31, 0xaf,0x85,0x31, 0xaf,0x85,0x31, 0xaf,0x85,0x31, 0xaf,0x85,0x31, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x97,0x82,0x36, 0xb4,0x9c,0x48, 0xb2,0x99,0x3f, 0xb4,0x9c,0x48, 0xb0,0x96,0x34, 0xc1,0xa7,0x4c, 0x9b,0x84,0x2a, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0xbd,0x9e,0x4c, 0xc7,0x9a,0x3f, 0xc7,0x9a,0x3f, 0xc7,0x9a,0x3f, 0xc7,0x9a,0x3f, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x9b,0x85,0x32, 0xa7,0x8e,0x2c, 0xac,0x93,0x39, 0xb5,0x91,0x41, 0x76,0x65,0x20, 0xa7,0x8e,0x2c, 0xb4,0x9a,0x38, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0xcc,0xa5,0x4e, 0xe0,0xaf,0x45, 0xe0,0xaf,0x45, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0xa4,0x8c,0x32, 0xb8,0x9e,0x44, 0x86,0x72,0x26, 0x9f,0x88,0x35, 0xbd,0xa3,0x48, 0x9b,0x85,0x32, 0xa3,0x81,0x32, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0xd1,0xae,0x4e, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0xb0,0x97,0x3c, 0xb4,0x9a,0x38, 0xac,0x94,0x41, 0xb2,0x99,0x3f, 0xb4,0x9a,0x38, 0xb8,0x9e,0x3c, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0xcc,0xa5,0x4e, 0xa6,0x8f,0x3c, 0xb2,0x99,0x3f, 0xb4,0x9c,0x48, 0xa8,0x90,0x36, 0x9f,0x88,0x35, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, }; ================================================ FILE: fbdoom/info.c ================================================ // // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // 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. // // DESCRIPTION: // Thing frame/state LUT, // generated by multigen utilitiy. // This one is the original DOOM version, preserved. // #include #include // Data. #include "sounds.h" #include "m_fixed.h" #include "info.h" #include "p_mobj.h" char *sprnames[] = { "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", NULL }; // Doesn't work with g++, needs actionf_p1 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(); 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(); 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 }; 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, // flags 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, // flags 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, // flags 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, // flags 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, // flags 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, // flags 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, // flags 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, // 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, // flags 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, // flags 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, // flags 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, // flags 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, // flags 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, // flags 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, // flags 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, // flags 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, // flags 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, // flags 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 } }; ================================================ FILE: fbdoom/info.h ================================================ // // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // 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. // // 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" 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, NUMSPRITES } spritenum_t; 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, NUMSTATES } statenum_t; typedef struct { spritenum_t sprite; int frame; int tics; // void (*action) (); actionf_t action; statenum_t nextstate; int misc1; int misc2; } state_t; extern state_t states[NUMSTATES]; extern char *sprnames[]; 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, NUMMOBJTYPES } mobjtype_t; typedef struct { int doomednum; int spawnstate; int spawnhealth; int seestate; int seesound; int reactiontime; int attacksound; int painstate; int painchance; int painsound; int meleestate; int missilestate; int deathstate; int xdeathstate; int deathsound; int speed; int radius; int height; int mass; int damage; int activesound; int flags; int raisestate; } mobjinfo_t; extern mobjinfo_t mobjinfo[NUMMOBJTYPES]; #endif ================================================ FILE: fbdoom/m_argv.c ================================================ // // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // 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. // // DESCRIPTION: // #include #include #include #include #include "doomtype.h" #include "i_system.h" #include "m_misc.h" #include "m_argv.h" // haleyjd 20110212: warning fix int myargc; char** myargv; // // 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_CheckParmWithArgs(char *check, int num_args) { int i; for (i = 1; i < myargc - num_args; i++) { if (!strcasecmp(check, myargv[i])) return i; } return 0; } // // M_ParmExists // // Returns true if the given parameter exists in the program's command // line arguments, false if not. // boolean M_ParmExists(char *check) { return M_CheckParm(check) != 0; } int M_CheckParm(char *check) { return M_CheckParmWithArgs(check, 0); } #define MAXARGVS 100 static void LoadResponseFile(int argv_index) { #if ORIGCODE FILE *handle; int size; char *infile; char *file; char *response_filename; char **newargv; int newargc; int i, k; response_filename = myargv[argv_index] + 1; // Read the response file into memory handle = fopen(response_filename, "rb"); if (handle == NULL) { printf ("\nNo such response file!"); #if ORIGCODE exit(1); #endif } printf("Found response file %s!\n", response_filename); size = M_FileLength(handle); // Read in the entire file // Allocate one byte extra - this is in case there is an argument // at the end of the response file, in which case a '\0' will be // needed. file = malloc(size + 1); i = 0; while (i < size) { k = fread(file + i, 1, size - i, handle); if (k < 0) { I_Error("Failed to read full contents of '%s'", response_filename); } i += k; } fclose(handle); // Create new arguments list array newargv = malloc(sizeof(char *) * MAXARGVS); newargc = 0; memset(newargv, 0, sizeof(char *) * MAXARGVS); // Copy all the arguments in the list up to the response file for (i=0; i= size) { break; } // If the next argument is enclosed in quote marks, treat // the contents as a single argument. This allows long filenames // to be specified. if (infile[k] == '\"') { // Skip the first character(") ++k; newargv[newargc++] = &infile[k]; // Read all characters between quotes while (k < size && infile[k] != '\"' && infile[k] != '\n') { ++k; } if (k >= size || infile[k] == '\n') { I_Error("Quotes unclosed in response file '%s'", response_filename); } // Cut off the string at the closing quote infile[k] = '\0'; ++k; } else { // Read in the next argument until a space is reached newargv[newargc++] = &infile[k]; while(k < size && !isspace((int)infile[k])) { ++k; } // Cut off the end of the argument at the first space infile[k] = '\0'; ++k; } } // Add arguments following the response file argument for (i=argv_index + 1; ibox[BOXRIGHT]) box[BOXRIGHT] = x; if (ybox[BOXTOP]) box[BOXTOP] = y; } ================================================ FILE: fbdoom/m_bbox.h ================================================ // // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // 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. // // DESCRIPTION: // Nil. // #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 ================================================ FILE: fbdoom/m_cheat.c ================================================ // // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // 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. // // DESCRIPTION: // Cheat sequence checking. // #include #include "doomtype.h" #include "m_cheat.h" // // CHEAT SEQUENCE PACKAGE // // // Called in st_stuff module, which handles the input. // Returns a 1 if the cheat was successful, 0 if failed. // int cht_CheckCheat ( cheatseq_t* cht, char key ) { // if we make a short sequence on a cheat with parameters, this // will not work in vanilla doom. behave the same. if (cht->parameter_chars > 0 && strlen(cht->sequence) < cht->sequence_len) return false; if (cht->chars_read < strlen(cht->sequence)) { // still reading characters from the cheat code // and verifying. reset back to the beginning // if a key is wrong if (key == cht->sequence[cht->chars_read]) ++cht->chars_read; else cht->chars_read = 0; cht->param_chars_read = 0; } else if (cht->param_chars_read < cht->parameter_chars) { // we have passed the end of the cheat sequence and are // entering parameters now cht->parameter_buf[cht->param_chars_read] = key; ++cht->param_chars_read; } if (cht->chars_read >= strlen(cht->sequence) && cht->param_chars_read >= cht->parameter_chars) { cht->chars_read = cht->param_chars_read = 0; return true; } // cheat not matched yet return false; } void cht_GetParam ( cheatseq_t* cht, char* buffer ) { memcpy(buffer, cht->parameter_buf, cht->parameter_chars); } ================================================ FILE: fbdoom/m_cheat.h ================================================ // // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // 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. // // DESCRIPTION: // Cheat code checking. // #ifndef __M_CHEAT__ #define __M_CHEAT__ // // CHEAT SEQUENCE PACKAGE // // declaring a cheat #define CHEAT(value, parameters) \ { value, sizeof(value) - 1, parameters, 0, 0, "" } #define MAX_CHEAT_LEN 25 #define MAX_CHEAT_PARAMS 5 typedef struct { // settings for this cheat char sequence[MAX_CHEAT_LEN]; size_t sequence_len; int parameter_chars; // state used during the game size_t chars_read; int param_chars_read; char parameter_buf[MAX_CHEAT_PARAMS]; } cheatseq_t; int cht_CheckCheat ( cheatseq_t* cht, char key ); void cht_GetParam ( cheatseq_t* cht, char* buffer ); #endif ================================================ FILE: fbdoom/m_config.c ================================================ // // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 1993-2008 Raven Software // Copyright(C) 2005-2014 Simon Howard // // 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. // // DESCRIPTION: // Configuration file interface. // #include #include #include #include #include #include "config.h" #include "doomtype.h" #include "doomkeys.h" #include "doomfeatures.h" #include "i_system.h" #include "m_argv.h" #include "m_misc.h" #include "z_zone.h" // // DEFAULTS // // Location where all configuration data is stored - // default.cfg, savegames, etc. char *configdir; // Default filenames for configuration files. static char *default_main_config; static char *default_extra_config; typedef enum { DEFAULT_INT, DEFAULT_INT_HEX, DEFAULT_STRING, DEFAULT_FLOAT, DEFAULT_KEY, } default_type_t; typedef struct { // Name of the variable char *name; // Pointer to the location in memory of the variable void *location; // Type of the variable default_type_t type; // If this is a key value, the original integer scancode we read from // the config file before translating it to the internal key value. // If zero, we didn't read this value from a config file. int untranslated; // The value we translated the scancode into when we read the // config file on startup. If the variable value is different from // this, it has been changed and needs to be converted; otherwise, // use the 'untranslated' value. int original_translated; // If true, this config variable has been bound to a variable // and is being used. boolean bound; } default_t; typedef struct { default_t *defaults; int numdefaults; char *filename; } default_collection_t; #define CONFIG_VARIABLE_GENERIC(name, type) \ { #name, NULL, type, 0, 0, false } #define CONFIG_VARIABLE_KEY(name) \ CONFIG_VARIABLE_GENERIC(name, DEFAULT_KEY) #define CONFIG_VARIABLE_INT(name) \ CONFIG_VARIABLE_GENERIC(name, DEFAULT_INT) #define CONFIG_VARIABLE_INT_HEX(name) \ CONFIG_VARIABLE_GENERIC(name, DEFAULT_INT_HEX) #define CONFIG_VARIABLE_FLOAT(name) \ CONFIG_VARIABLE_GENERIC(name, DEFAULT_FLOAT) #define CONFIG_VARIABLE_STRING(name) \ CONFIG_VARIABLE_GENERIC(name, DEFAULT_STRING) //! @begin_config_file default static default_t doom_defaults_list[] = { //! // Mouse sensitivity. This value is used to multiply input mouse // movement to control the effect of moving the mouse. // // The "normal" maximum value available for this through the // in-game options menu is 9. A value of 31 or greater will cause // the game to crash when entering the options menu. // CONFIG_VARIABLE_INT(mouse_sensitivity), //! // Volume of sound effects, range 0-15. // CONFIG_VARIABLE_INT(sfx_volume), //! // Volume of in-game music, range 0-15. // CONFIG_VARIABLE_INT(music_volume), //! // @game strife // // If non-zero, dialogue text is displayed over characters' pictures // when engaging actors who have voices. // CONFIG_VARIABLE_INT(show_talk), //! // @game strife // // Volume of voice sound effects, range 0-15. // CONFIG_VARIABLE_INT(voice_volume), //! // @game doom // // If non-zero, messages are displayed on the heads-up display // in the game ("picked up a clip", etc). If zero, these messages // are not displayed. // CONFIG_VARIABLE_INT(show_messages), //! // Keyboard key to turn right. // CONFIG_VARIABLE_KEY(key_right), //! // Keyboard key to turn left. // CONFIG_VARIABLE_KEY(key_left), //! // Keyboard key to move forward. // CONFIG_VARIABLE_KEY(key_up), //! // Keyboard key to move backward. // CONFIG_VARIABLE_KEY(key_down), //! // Keyboard key to strafe left. // CONFIG_VARIABLE_KEY(key_strafeleft), //! // Keyboard key to strafe right. // CONFIG_VARIABLE_KEY(key_straferight), //! // @game strife // // Keyboard key to use health. // CONFIG_VARIABLE_KEY(key_useHealth), //! // @game hexen // // Keyboard key to jump. // CONFIG_VARIABLE_KEY(key_jump), //! // @game heretic hexen // // Keyboard key to fly upward. // CONFIG_VARIABLE_KEY(key_flyup), //! // @game heretic hexen // // Keyboard key to fly downwards. // CONFIG_VARIABLE_KEY(key_flydown), //! // @game heretic hexen // // Keyboard key to center flying. // CONFIG_VARIABLE_KEY(key_flycenter), //! // @game heretic hexen // // Keyboard key to look up. // CONFIG_VARIABLE_KEY(key_lookup), //! // @game heretic hexen // // Keyboard key to look down. // CONFIG_VARIABLE_KEY(key_lookdown), //! // @game heretic hexen // // Keyboard key to center the view. // CONFIG_VARIABLE_KEY(key_lookcenter), //! // @game strife // // Keyboard key to query inventory. // CONFIG_VARIABLE_KEY(key_invquery), //! // @game strife // // Keyboard key to display mission objective. // CONFIG_VARIABLE_KEY(key_mission), //! // @game strife // // Keyboard key to display inventory popup. // CONFIG_VARIABLE_KEY(key_invPop), //! // @game strife // // Keyboard key to display keys popup. // CONFIG_VARIABLE_KEY(key_invKey), //! // @game strife // // Keyboard key to jump to start of inventory. // CONFIG_VARIABLE_KEY(key_invHome), //! // @game strife // // Keyboard key to jump to end of inventory. // CONFIG_VARIABLE_KEY(key_invEnd), //! // @game heretic hexen // // Keyboard key to scroll left in the inventory. // CONFIG_VARIABLE_KEY(key_invleft), //! // @game heretic hexen // // Keyboard key to scroll right in the inventory. // CONFIG_VARIABLE_KEY(key_invright), //! // @game strife // // Keyboard key to scroll left in the inventory. // CONFIG_VARIABLE_KEY(key_invLeft), //! // @game strife // // Keyboard key to scroll right in the inventory. // CONFIG_VARIABLE_KEY(key_invRight), //! // @game heretic hexen // // Keyboard key to use the current item in the inventory. // CONFIG_VARIABLE_KEY(key_useartifact), //! // @game strife // // Keyboard key to use inventory item. // CONFIG_VARIABLE_KEY(key_invUse), //! // @game strife // // Keyboard key to drop an inventory item. // CONFIG_VARIABLE_KEY(key_invDrop), //! // @game strife // // Keyboard key to look up. // CONFIG_VARIABLE_KEY(key_lookUp), //! // @game strife // // Keyboard key to look down. // CONFIG_VARIABLE_KEY(key_lookDown), //! // Keyboard key to fire the currently selected weapon. // CONFIG_VARIABLE_KEY(key_fire), //! // Keyboard key to "use" an object, eg. a door or switch. // CONFIG_VARIABLE_KEY(key_use), //! // Keyboard key to turn on strafing. When held down, pressing the // key to turn left or right causes the player to strafe left or // right instead. // CONFIG_VARIABLE_KEY(key_strafe), //! // Keyboard key to make the player run. // CONFIG_VARIABLE_KEY(key_speed), //! // If non-zero, mouse input is enabled. If zero, mouse input is // disabled. // CONFIG_VARIABLE_INT(use_mouse), //! // Mouse button to fire the currently selected weapon. // CONFIG_VARIABLE_INT(mouseb_fire), //! // Mouse button to turn on strafing. When held down, the player // will strafe left and right instead of turning left and right. // CONFIG_VARIABLE_INT(mouseb_strafe), //! // Mouse button to move forward. // CONFIG_VARIABLE_INT(mouseb_forward), //! // @game hexen strife // // Mouse button to jump. // CONFIG_VARIABLE_INT(mouseb_jump), //! // If non-zero, joystick input is enabled. // CONFIG_VARIABLE_INT(use_joystick), //! // Joystick virtual button that fires the current weapon. // CONFIG_VARIABLE_INT(joyb_fire), //! // Joystick virtual button that makes the player strafe while // held down. // CONFIG_VARIABLE_INT(joyb_strafe), //! // Joystick virtual button to "use" an object, eg. a door or switch. // CONFIG_VARIABLE_INT(joyb_use), //! // Joystick virtual button that makes the player run while held // down. // // If this has a value of 20 or greater, the player will always run, // even if use_joystick is 0. // CONFIG_VARIABLE_INT(joyb_speed), //! // @game hexen strife // // Joystick virtual button that makes the player jump. // CONFIG_VARIABLE_INT(joyb_jump), //! // @game doom heretic hexen // // Screen size, range 3-11. // // A value of 11 gives a full-screen view with the status bar not // displayed. A value of 10 gives a full-screen view with the // status bar displayed. // CONFIG_VARIABLE_INT(screenblocks), //! // @game strife // // Screen size, range 3-11. // // A value of 11 gives a full-screen view with the status bar not // displayed. A value of 10 gives a full-screen view with the // status bar displayed. // CONFIG_VARIABLE_INT(screensize), //! // @game doom // // Screen detail. Zero gives normal "high detail" mode, while // a non-zero value gives "low detail" mode. // CONFIG_VARIABLE_INT(detaillevel), //! // Number of sounds that will be played simultaneously. // CONFIG_VARIABLE_INT(snd_channels), //! // Music output device. A non-zero value gives MIDI sound output, // while a value of zero disables music. // CONFIG_VARIABLE_INT(snd_musicdevice), //! // Sound effects device. A value of zero disables in-game sound // effects, a value of 1 enables PC speaker sound effects, while // a value in the range 2-9 enables the "normal" digital sound // effects. // CONFIG_VARIABLE_INT(snd_sfxdevice), //! // SoundBlaster I/O port. Unused. // CONFIG_VARIABLE_INT(snd_sbport), //! // SoundBlaster IRQ. Unused. // CONFIG_VARIABLE_INT(snd_sbirq), //! // SoundBlaster DMA channel. Unused. // CONFIG_VARIABLE_INT(snd_sbdma), //! // Output port to use for OPL MIDI playback. Unused. // CONFIG_VARIABLE_INT(snd_mport), //! // Gamma correction level. A value of zero disables gamma // correction, while a value in the range 1-4 gives increasing // levels of gamma correction. // CONFIG_VARIABLE_INT(usegamma), //! // @game hexen // // Directory in which to store savegames. // CONFIG_VARIABLE_STRING(savedir), //! // @game hexen // // Controls whether messages are displayed in the heads-up display. // If this has a non-zero value, messages are displayed. // CONFIG_VARIABLE_INT(messageson), //! // @game strife // // Name of background flat used by view border. // CONFIG_VARIABLE_STRING(back_flat), //! // @game strife // // Multiplayer nickname (?). // CONFIG_VARIABLE_STRING(nickname), //! // Multiplayer chat macro: message to send when alt+0 is pressed. // CONFIG_VARIABLE_STRING(chatmacro0), //! // Multiplayer chat macro: message to send when alt+1 is pressed. // CONFIG_VARIABLE_STRING(chatmacro1), //! // Multiplayer chat macro: message to send when alt+2 is pressed. // CONFIG_VARIABLE_STRING(chatmacro2), //! // Multiplayer chat macro: message to send when alt+3 is pressed. // CONFIG_VARIABLE_STRING(chatmacro3), //! // Multiplayer chat macro: message to send when alt+4 is pressed. // CONFIG_VARIABLE_STRING(chatmacro4), //! // Multiplayer chat macro: message to send when alt+5 is pressed. // CONFIG_VARIABLE_STRING(chatmacro5), //! // Multiplayer chat macro: message to send when alt+6 is pressed. // CONFIG_VARIABLE_STRING(chatmacro6), //! // Multiplayer chat macro: message to send when alt+7 is pressed. // CONFIG_VARIABLE_STRING(chatmacro7), //! // Multiplayer chat macro: message to send when alt+8 is pressed. // CONFIG_VARIABLE_STRING(chatmacro8), //! // Multiplayer chat macro: message to send when alt+9 is pressed. // CONFIG_VARIABLE_STRING(chatmacro9), //! // @game strife // // Serial port number to use for SERSETUP.EXE (unused). // CONFIG_VARIABLE_INT(comport), }; static default_collection_t doom_defaults = { doom_defaults_list, arrlen(doom_defaults_list), NULL, }; //! @begin_config_file extended static default_t extra_defaults_list[] = { //! // @game heretic hexen strife // // If non-zero, display the graphical startup screen. // CONFIG_VARIABLE_INT(graphical_startup), //! // If non-zero, video settings will be autoadjusted to a valid // configuration when the screen_width and screen_height variables // do not match any valid configuration. // CONFIG_VARIABLE_INT(autoadjust_video_settings), //! // If non-zero, the game will run in full screen mode. If zero, // the game will run in a window. // CONFIG_VARIABLE_INT(fullscreen), //! // If non-zero, the screen will be stretched vertically to display // correctly on a square pixel video mode. // CONFIG_VARIABLE_INT(aspect_ratio_correct), //! // Number of milliseconds to wait on startup after the video mode // has been set, before the game will start. This allows the // screen to settle on some monitors that do not display an image // for a brief interval after changing video modes. // CONFIG_VARIABLE_INT(startup_delay), //! // Screen width in pixels. If running in full screen mode, this is // the X dimension of the video mode to use. If running in // windowed mode, this is the width of the window in which the game // will run. // CONFIG_VARIABLE_INT(screen_width), //! // Screen height in pixels. If running in full screen mode, this is // the Y dimension of the video mode to use. If running in // windowed mode, this is the height of the window in which the game // will run. // CONFIG_VARIABLE_INT(screen_height), //! // Color depth of the screen, in bits. // If this is set to zero, the color depth will be automatically set // on startup to the machine's default/native color depth. // CONFIG_VARIABLE_INT(screen_bpp), //! // If this is non-zero, the mouse will be "grabbed" when running // in windowed mode so that it can be used as an input device. // When running full screen, this has no effect. // CONFIG_VARIABLE_INT(grabmouse), //! // If non-zero, all vertical mouse movement is ignored. This // emulates the behavior of the "novert" tool available under DOS // that performs the same function. // CONFIG_VARIABLE_INT(novert), //! // Mouse acceleration factor. When the speed of mouse movement // exceeds the threshold value (mouse_threshold), the speed is // multiplied by this value. // CONFIG_VARIABLE_FLOAT(mouse_acceleration), //! // Mouse acceleration threshold. When the speed of mouse movement // exceeds this threshold value, the speed is multiplied by an // acceleration factor (mouse_acceleration). // CONFIG_VARIABLE_INT(mouse_threshold), //! // Sound output sample rate, in Hz. Typical values to use are // 11025, 22050, 44100 and 48000. // CONFIG_VARIABLE_INT(snd_samplerate), //! // Maximum number of bytes to allocate for caching converted sound // effects in memory. If set to zero, there is no limit applied. // CONFIG_VARIABLE_INT(snd_cachesize), //! // Maximum size of the output sound buffer size in milliseconds. // Sound output is generated periodically in slices. Higher values // might be more efficient but will introduce latency to the // sound output. The default is 28ms (one slice per tic with the // 35fps timer). CONFIG_VARIABLE_INT(snd_maxslicetime_ms), //! // External command to invoke to perform MIDI playback. If set to // the empty string, SDL_mixer's internal MIDI playback is used. // This only has any effect when snd_musicdevice is set to General // MIDI output. CONFIG_VARIABLE_STRING(snd_musiccmd), //! // The I/O port to use to access the OPL chip. Only relevant when // using native OPL music playback. // CONFIG_VARIABLE_INT_HEX(opl_io_port), //! // @game doom heretic strife // // If non-zero, the ENDOOM text screen is displayed when exiting the // game. If zero, the ENDOOM screen is not displayed. // CONFIG_VARIABLE_INT(show_endoom), //! // If non-zero, save screenshots in PNG format. // CONFIG_VARIABLE_INT(png_screenshots), //! // @game doom strife // // If non-zero, the Vanilla savegame limit is enforced; if the // savegame exceeds 180224 bytes in size, the game will exit with // an error. If this has a value of zero, there is no limit to // the size of savegames. // CONFIG_VARIABLE_INT(vanilla_savegame_limit), //! // @game doom strife // // If non-zero, the Vanilla demo size limit is enforced; the game // exits with an error when a demo exceeds the demo size limit // (128KiB by default). If this has a value of zero, there is no // limit to the size of demos. // CONFIG_VARIABLE_INT(vanilla_demo_limit), //! // If non-zero, the game behaves like Vanilla Doom, always assuming // an American keyboard mapping. If this has a value of zero, the // native keyboard mapping of the keyboard is used. // CONFIG_VARIABLE_INT(vanilla_keyboard_mapping), //! // Name of the SDL video driver to use. If this is an empty string, // the default video driver is used. // CONFIG_VARIABLE_STRING(video_driver), //! // Position of the window on the screen when running in windowed // mode. Accepted values are: "" (empty string) - don't care, // "center" - place window at center of screen, "x,y" - place // window at the specified coordinates. CONFIG_VARIABLE_STRING(window_position), #ifdef FEATURE_MULTIPLAYER //! // Name to use in network games for identification. This is only // used on the "waiting" screen while waiting for the game to start. // CONFIG_VARIABLE_STRING(player_name), #endif //! // Joystick number to use; '0' is the first joystick. A negative // value ('-1') indicates that no joystick is configured. // CONFIG_VARIABLE_INT(joystick_index), //! // Joystick axis to use to for horizontal (X) movement. // CONFIG_VARIABLE_INT(joystick_x_axis), //! // If non-zero, movement on the horizontal joystick axis is inverted. // CONFIG_VARIABLE_INT(joystick_x_invert), //! // Joystick axis to use to for vertical (Y) movement. // CONFIG_VARIABLE_INT(joystick_y_axis), //! // If non-zero, movement on the vertical joystick axis is inverted. // CONFIG_VARIABLE_INT(joystick_y_invert), //! // Joystick axis to use to for strafing movement. // CONFIG_VARIABLE_INT(joystick_strafe_axis), //! // If non-zero, movement on the joystick axis used for strafing // is inverted. // CONFIG_VARIABLE_INT(joystick_strafe_invert), //! // The physical joystick button that corresponds to joystick // virtual button #0. // CONFIG_VARIABLE_INT(joystick_physical_button0), //! // The physical joystick button that corresponds to joystick // virtual button #1. // CONFIG_VARIABLE_INT(joystick_physical_button1), //! // The physical joystick button that corresponds to joystick // virtual button #2. // CONFIG_VARIABLE_INT(joystick_physical_button2), //! // The physical joystick button that corresponds to joystick // virtual button #3. // CONFIG_VARIABLE_INT(joystick_physical_button3), //! // The physical joystick button that corresponds to joystick // virtual button #4. // CONFIG_VARIABLE_INT(joystick_physical_button4), //! // The physical joystick button that corresponds to joystick // virtual button #5. // CONFIG_VARIABLE_INT(joystick_physical_button5), //! // The physical joystick button that corresponds to joystick // virtual button #6. // CONFIG_VARIABLE_INT(joystick_physical_button6), //! // The physical joystick button that corresponds to joystick // virtual button #7. // CONFIG_VARIABLE_INT(joystick_physical_button7), //! // The physical joystick button that corresponds to joystick // virtual button #8. // CONFIG_VARIABLE_INT(joystick_physical_button8), //! // The physical joystick button that corresponds to joystick // virtual button #9. // CONFIG_VARIABLE_INT(joystick_physical_button9), //! // Joystick virtual button to make the player strafe left. // CONFIG_VARIABLE_INT(joyb_strafeleft), //! // Joystick virtual button to make the player strafe right. // CONFIG_VARIABLE_INT(joyb_straferight), //! // Joystick virtual button to activate the menu. // CONFIG_VARIABLE_INT(joyb_menu_activate), //! // Joystick virtual button that cycles to the previous weapon. // CONFIG_VARIABLE_INT(joyb_prevweapon), //! // Joystick virtual button that cycles to the next weapon. // CONFIG_VARIABLE_INT(joyb_nextweapon), //! // Mouse button to strafe left. // CONFIG_VARIABLE_INT(mouseb_strafeleft), //! // Mouse button to strafe right. // CONFIG_VARIABLE_INT(mouseb_straferight), //! // Mouse button to "use" an object, eg. a door or switch. // CONFIG_VARIABLE_INT(mouseb_use), //! // Mouse button to move backwards. // CONFIG_VARIABLE_INT(mouseb_backward), //! // Mouse button to cycle to the previous weapon. // CONFIG_VARIABLE_INT(mouseb_prevweapon), //! // Mouse button to cycle to the next weapon. // CONFIG_VARIABLE_INT(mouseb_nextweapon), //! // If non-zero, double-clicking a mouse button acts like pressing // the "use" key to use an object in-game, eg. a door or switch. // CONFIG_VARIABLE_INT(dclick_use), #ifdef FEATURE_SOUND //! // Controls whether libsamplerate support is used for performing // sample rate conversions of sound effects. Support for this // must be compiled into the program. // // If zero, libsamplerate support is disabled. If non-zero, // libsamplerate is enabled. Increasing values roughly correspond // to higher quality conversion; the higher the quality, the // slower the conversion process. Linear conversion = 1; // Zero order hold = 2; Fast Sinc filter = 3; Medium quality // Sinc filter = 4; High quality Sinc filter = 5. // CONFIG_VARIABLE_INT(use_libsamplerate), //! // Scaling factor used by libsamplerate. This is used when converting // sounds internally back into integer form; normally it should not // be necessary to change it from the default value. The only time // it might be needed is if a PWAD file is loaded that contains very // loud sounds, in which case the conversion may cause sound clipping // and the scale factor should be reduced. The lower the value, the // quieter the sound effects become, so it should be set as high as is // possible without clipping occurring. CONFIG_VARIABLE_FLOAT(libsamplerate_scale), //! // Full path to a Timidity configuration file to use for MIDI // playback. The file will be evaluated from the directory where // it is evaluated, so there is no need to add "dir" commands // into it. // CONFIG_VARIABLE_STRING(timidity_cfg_path), //! // Path to GUS patch files to use when operating in GUS emulation // mode. // CONFIG_VARIABLE_STRING(gus_patch_path), //! // Number of kilobytes of RAM to use in GUS emulation mode. Valid // values are 256, 512, 768 or 1024. // CONFIG_VARIABLE_INT(gus_ram_kb), #endif //! // Key to pause or unpause the game. // CONFIG_VARIABLE_KEY(key_pause), //! // Key that activates the menu when pressed. // CONFIG_VARIABLE_KEY(key_menu_activate), //! // Key that moves the cursor up on the menu. // CONFIG_VARIABLE_KEY(key_menu_up), //! // Key that moves the cursor down on the menu. // CONFIG_VARIABLE_KEY(key_menu_down), //! // Key that moves the currently selected slider on the menu left. // CONFIG_VARIABLE_KEY(key_menu_left), //! // Key that moves the currently selected slider on the menu right. // CONFIG_VARIABLE_KEY(key_menu_right), //! // Key to go back to the previous menu. // CONFIG_VARIABLE_KEY(key_menu_back), //! // Key to activate the currently selected menu item. // CONFIG_VARIABLE_KEY(key_menu_forward), //! // Key to answer 'yes' to a question in the menu. // CONFIG_VARIABLE_KEY(key_menu_confirm), //! // Key to answer 'no' to a question in the menu. // CONFIG_VARIABLE_KEY(key_menu_abort), //! // Keyboard shortcut to bring up the help screen. // CONFIG_VARIABLE_KEY(key_menu_help), //! // Keyboard shortcut to bring up the save game menu. // CONFIG_VARIABLE_KEY(key_menu_save), //! // Keyboard shortcut to bring up the load game menu. // CONFIG_VARIABLE_KEY(key_menu_load), //! // Keyboard shortcut to bring up the sound volume menu. // CONFIG_VARIABLE_KEY(key_menu_volume), //! // Keyboard shortcut to toggle the detail level. // CONFIG_VARIABLE_KEY(key_menu_detail), //! // Keyboard shortcut to quicksave the current game. // CONFIG_VARIABLE_KEY(key_menu_qsave), //! // Keyboard shortcut to end the game. // CONFIG_VARIABLE_KEY(key_menu_endgame), //! // Keyboard shortcut to toggle heads-up messages. // CONFIG_VARIABLE_KEY(key_menu_messages), //! // Keyboard shortcut to load the last quicksave. // CONFIG_VARIABLE_KEY(key_menu_qload), //! // Keyboard shortcut to quit the game. // CONFIG_VARIABLE_KEY(key_menu_quit), //! // Keyboard shortcut to toggle the gamma correction level. // CONFIG_VARIABLE_KEY(key_menu_gamma), //! // Keyboard shortcut to switch view in multiplayer. // CONFIG_VARIABLE_KEY(key_spy), //! // Keyboard shortcut to increase the screen size. // CONFIG_VARIABLE_KEY(key_menu_incscreen), //! // Keyboard shortcut to decrease the screen size. // CONFIG_VARIABLE_KEY(key_menu_decscreen), //! // Keyboard shortcut to save a screenshot. // CONFIG_VARIABLE_KEY(key_menu_screenshot), //! // Key to toggle the map view. // CONFIG_VARIABLE_KEY(key_map_toggle), //! // Key to pan north when in the map view. // CONFIG_VARIABLE_KEY(key_map_north), //! // Key to pan south when in the map view. // CONFIG_VARIABLE_KEY(key_map_south), //! // Key to pan east when in the map view. // CONFIG_VARIABLE_KEY(key_map_east), //! // Key to pan west when in the map view. // CONFIG_VARIABLE_KEY(key_map_west), //! // Key to zoom in when in the map view. // CONFIG_VARIABLE_KEY(key_map_zoomin), //! // Key to zoom out when in the map view. // CONFIG_VARIABLE_KEY(key_map_zoomout), //! // Key to zoom out the maximum amount when in the map view. // CONFIG_VARIABLE_KEY(key_map_maxzoom), //! // Key to toggle follow mode when in the map view. // CONFIG_VARIABLE_KEY(key_map_follow), //! // Key to toggle the grid display when in the map view. // CONFIG_VARIABLE_KEY(key_map_grid), //! // Key to set a mark when in the map view. // CONFIG_VARIABLE_KEY(key_map_mark), //! // Key to clear all marks when in the map view. // CONFIG_VARIABLE_KEY(key_map_clearmark), //! // Key to select weapon 1. // CONFIG_VARIABLE_KEY(key_weapon1), //! // Key to select weapon 2. // CONFIG_VARIABLE_KEY(key_weapon2), //! // Key to select weapon 3. // CONFIG_VARIABLE_KEY(key_weapon3), //! // Key to select weapon 4. // CONFIG_VARIABLE_KEY(key_weapon4), //! // Key to select weapon 5. // CONFIG_VARIABLE_KEY(key_weapon5), //! // Key to select weapon 6. // CONFIG_VARIABLE_KEY(key_weapon6), //! // Key to select weapon 7. // CONFIG_VARIABLE_KEY(key_weapon7), //! // Key to select weapon 8. // CONFIG_VARIABLE_KEY(key_weapon8), //! // Key to cycle to the previous weapon. // CONFIG_VARIABLE_KEY(key_prevweapon), //! // Key to cycle to the next weapon. // CONFIG_VARIABLE_KEY(key_nextweapon), //! // @game hexen // // Key to use one of each artifact. // CONFIG_VARIABLE_KEY(key_arti_all), //! // @game hexen // // Key to use "quartz flask" artifact. // CONFIG_VARIABLE_KEY(key_arti_health), //! // @game hexen // // Key to use "flechette" artifact. // CONFIG_VARIABLE_KEY(key_arti_poisonbag), //! // @game hexen // // Key to use "disc of repulsion" artifact. // CONFIG_VARIABLE_KEY(key_arti_blastradius), //! // @game hexen // // Key to use "chaos device" artifact. // CONFIG_VARIABLE_KEY(key_arti_teleport), //! // @game hexen // // Key to use "banishment device" artifact. // CONFIG_VARIABLE_KEY(key_arti_teleportother), //! // @game hexen // // Key to use "porkalator" artifact. // CONFIG_VARIABLE_KEY(key_arti_egg), //! // @game hexen // // Key to use "icon of the defender" artifact. // CONFIG_VARIABLE_KEY(key_arti_invulnerability), //! // Key to re-display last message. // CONFIG_VARIABLE_KEY(key_message_refresh), //! // Key to quit the game when recording a demo. // CONFIG_VARIABLE_KEY(key_demo_quit), //! // Key to send a message during multiplayer games. // CONFIG_VARIABLE_KEY(key_multi_msg), //! // Key to send a message to player 1 during multiplayer games. // CONFIG_VARIABLE_KEY(key_multi_msgplayer1), //! // Key to send a message to player 2 during multiplayer games. // CONFIG_VARIABLE_KEY(key_multi_msgplayer2), //! // Key to send a message to player 3 during multiplayer games. // CONFIG_VARIABLE_KEY(key_multi_msgplayer3), //! // Key to send a message to player 4 during multiplayer games. // CONFIG_VARIABLE_KEY(key_multi_msgplayer4), //! // @game hexen strife // // Key to send a message to player 5 during multiplayer games. // CONFIG_VARIABLE_KEY(key_multi_msgplayer5), //! // @game hexen strife // // Key to send a message to player 6 during multiplayer games. // CONFIG_VARIABLE_KEY(key_multi_msgplayer6), //! // @game hexen strife // // Key to send a message to player 7 during multiplayer games. // CONFIG_VARIABLE_KEY(key_multi_msgplayer7), //! // @game hexen strife // // Key to send a message to player 8 during multiplayer games. // CONFIG_VARIABLE_KEY(key_multi_msgplayer8), }; static default_collection_t extra_defaults = { extra_defaults_list, arrlen(extra_defaults_list), NULL, }; // Search a collection for a variable static default_t *SearchCollection(default_collection_t *collection, char *name) { int i; for (i=0; inumdefaults; ++i) { if (!strcmp(name, collection->defaults[i].name)) { return &collection->defaults[i]; } } return NULL; } // Mapping from DOS keyboard scan code to internal key code (as defined // in doomkey.h). I think I (fraggle) reused this from somewhere else // but I can't find where. Anyway, notes: // * KEY_PAUSE is wrong - it's in the KEY_NUMLOCK spot. This shouldn't // matter in terms of Vanilla compatibility because neither of // those were valid for key bindings. // * There is no proper scan code for PrintScreen (on DOS machines it // sends an interrupt). So I added a fake scan code of 126 for it. // The presence of this is important so we can bind PrintScreen as // a screenshot key. static const int scantokey[128] = { 0 , 27, '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '-', '=', KEY_BACKSPACE, 9, 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p', '[', ']', 13, KEY_RCTRL, 'a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', '\'', '`', KEY_RSHIFT,'\\', 'z', 'x', 'c', 'v', 'b', 'n', 'm', ',', '.', '/', KEY_RSHIFT,KEYP_MULTIPLY, KEY_RALT, ' ', KEY_CAPSLOCK,KEY_F1, KEY_F2, KEY_F3, KEY_F4, KEY_F5, KEY_F6, KEY_F7, KEY_F8, KEY_F9, KEY_F10, /*KEY_NUMLOCK?*/KEY_PAUSE,KEY_SCRLCK,KEY_HOME, KEY_UPARROW,KEY_PGUP,KEY_MINUS,KEY_LEFTARROW,KEYP_5,KEY_RIGHTARROW,KEYP_PLUS,KEY_END, KEY_DOWNARROW,KEY_PGDN,KEY_INS,KEY_DEL,0, 0, 0, KEY_F11, KEY_F12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KEY_PRTSCR, 0 }; static void SaveDefaultCollection(default_collection_t *collection) { #if ORIGCODE default_t *defaults; int i, v; FILE *f; f = fopen (collection->filename, "w"); if (!f) return; // can't write the file, but don't complain defaults = collection->defaults; for (i=0 ; inumdefaults ; i++) { int chars_written; // Ignore unbound variables if (!defaults[i].bound) { continue; } // Print the name and line up all values at 30 characters chars_written = fprintf(f, "%s ", defaults[i].name); for (; chars_written < 30; ++chars_written) fprintf(f, " "); // Print the value switch (defaults[i].type) { case DEFAULT_KEY: // use the untranslated version if we can, to reduce // the possibility of screwing up the user's config // file v = * (int *) defaults[i].location; if (v == KEY_RSHIFT) { // Special case: for shift, force scan code for // right shift, as this is what Vanilla uses. // This overrides the change check below, to fix // configuration files made by old versions that // mistakenly used the scan code for left shift. v = 54; } else if (defaults[i].untranslated && v == defaults[i].original_translated) { // Has not been changed since the last time we // read the config file. v = defaults[i].untranslated; } else { // search for a reverse mapping back to a scancode // in the scantokey table int s; for (s=0; s<128; ++s) { if (scantokey[s] == v) { v = s; break; } } } fprintf(f, "%i", v); break; case DEFAULT_INT: fprintf(f, "%i", * (int *) defaults[i].location); break; case DEFAULT_INT_HEX: fprintf(f, "0x%x", * (int *) defaults[i].location); break; case DEFAULT_FLOAT: fprintf(f, "%f", * (float *) defaults[i].location); break; case DEFAULT_STRING: fprintf(f,"\"%s\"", * (char **) (defaults[i].location)); break; } fprintf(f, "\n"); } fclose (f); #endif } // Parses integer values in the configuration file static int ParseIntParameter(char *strparm) { int parm; if (strparm[0] == '0' && strparm[1] == 'x') sscanf(strparm+2, "%x", &parm); else sscanf(strparm, "%i", &parm); return parm; } static void SetVariable(default_t *def, char *value) { int intparm; // parameter found switch (def->type) { case DEFAULT_STRING: * (char **) def->location = strdup(value); break; case DEFAULT_INT: case DEFAULT_INT_HEX: * (int *) def->location = ParseIntParameter(value); break; case DEFAULT_KEY: // translate scancodes read from config // file (save the old value in untranslated) intparm = ParseIntParameter(value); def->untranslated = intparm; if (intparm >= 0 && intparm < 128) { intparm = scantokey[intparm]; } else { intparm = 0; } def->original_translated = intparm; * (int *) def->location = intparm; break; case DEFAULT_FLOAT: * (float *) def->location = (float) atof(value); break; } } static void LoadDefaultCollection(default_collection_t *collection) { #if ORIGCODE FILE *f; default_t *def; char defname[80]; char strparm[100]; // read the file in, overriding any set defaults f = fopen(collection->filename, "r"); if (f == NULL) { // File not opened, but don't complain. // It's probably just the first time they ran the game. return; } while (!feof(f)) { if (fscanf(f, "%79s %99[^\n]\n", defname, strparm) != 2) { // This line doesn't match continue; } // Find the setting in the list def = SearchCollection(collection, defname); if (def == NULL || !def->bound) { // Unknown variable? Unbound variables are also treated // as unknown. continue; } // Strip off trailing non-printable characters (\r characters // from DOS text files) while (strlen(strparm) > 0 && !isprint(strparm[strlen(strparm)-1])) { strparm[strlen(strparm)-1] = '\0'; } // Surrounded by quotes? If so, remove them. if (strlen(strparm) >= 2 && strparm[0] == '"' && strparm[strlen(strparm) - 1] == '"') { strparm[strlen(strparm) - 1] = '\0'; memmove(strparm, strparm + 1, sizeof(strparm) - 1); } SetVariable(def, strparm); } fclose (f); #endif } // Set the default filenames to use for configuration files. void M_SetConfigFilenames(char *main_config, char *extra_config) { default_main_config = main_config; default_extra_config = extra_config; } // // M_SaveDefaults // void M_SaveDefaults (void) { SaveDefaultCollection(&doom_defaults); SaveDefaultCollection(&extra_defaults); } // // Save defaults to alternate filenames // void M_SaveDefaultsAlternate(char *main, char *extra) { char *orig_main; char *orig_extra; // Temporarily change the filenames orig_main = doom_defaults.filename; orig_extra = extra_defaults.filename; doom_defaults.filename = main; extra_defaults.filename = extra; M_SaveDefaults(); // Restore normal filenames doom_defaults.filename = orig_main; extra_defaults.filename = orig_extra; } // // M_LoadDefaults // void M_LoadDefaults (void) { int i; // check for a custom default file //! // @arg // @vanilla // // Load main configuration from the specified file, instead of the // default. // i = M_CheckParmWithArgs("-config", 1); if (i) { doom_defaults.filename = myargv[i+1]; printf (" default file: %s\n",doom_defaults.filename); } else { doom_defaults.filename = M_StringJoin(configdir, default_main_config, NULL); } printf("saving config in %s\n", doom_defaults.filename); //! // @arg // // Load additional configuration from the specified file, instead of // the default. // i = M_CheckParmWithArgs("-extraconfig", 1); if (i) { extra_defaults.filename = myargv[i+1]; printf(" extra configuration file: %s\n", extra_defaults.filename); } else { extra_defaults.filename = M_StringJoin(configdir, default_extra_config, NULL); } LoadDefaultCollection(&doom_defaults); LoadDefaultCollection(&extra_defaults); } // Get a configuration file variable by its name static default_t *GetDefaultForName(char *name) { default_t *result; // Try the main list and the extras result = SearchCollection(&doom_defaults, name); if (result == NULL) { result = SearchCollection(&extra_defaults, name); } // Not found? Internal error. if (result == NULL) { I_Error("Unknown configuration variable: '%s'", name); } return result; } // // Bind a variable to a given configuration file variable, by name. // void M_BindVariable(char *name, void *location) { default_t *variable; variable = GetDefaultForName(name); variable->location = location; variable->bound = true; } // Set the value of a particular variable; an API function for other // parts of the program to assign values to config variables by name. boolean M_SetVariable(char *name, char *value) { default_t *variable; variable = GetDefaultForName(name); if (variable == NULL || !variable->bound) { return false; } SetVariable(variable, value); return true; } // Get the value of a variable. int M_GetIntVariable(char *name) { default_t *variable; variable = GetDefaultForName(name); if (variable == NULL || !variable->bound || (variable->type != DEFAULT_INT && variable->type != DEFAULT_INT_HEX)) { return 0; } return *((int *) variable->location); } const char *M_GetStrVariable(char *name) { default_t *variable; variable = GetDefaultForName(name); if (variable == NULL || !variable->bound || variable->type != DEFAULT_STRING) { return NULL; } return *((const char **) variable->location); } float M_GetFloatVariable(char *name) { default_t *variable; variable = GetDefaultForName(name); if (variable == NULL || !variable->bound || variable->type != DEFAULT_FLOAT) { return 0; } return *((float *) variable->location); } // Get the path to the default configuration dir to use, if NULL // is passed to M_SetConfigDir. static char *GetDefaultConfigDir(void) { #if !defined(_WIN32) || defined(_WIN32_WCE) // Configuration settings are stored in ~/.chocolate-doom/, // except on Windows, where we behave like Vanilla Doom and // save in the current directory. char *homedir; char *result; // frosted HACK - homedir = getenv("HOME"); homedir = "/mnt"; if (homedir != NULL) { // put all configuration in a config directory off the // homedir result = M_StringJoin(homedir, DIR_SEPARATOR_S, "." PACKAGE_TARNAME, DIR_SEPARATOR_S, NULL); return result; } else #endif /* #ifndef _WIN32 */ { return strdup(FILES_DIR"/"); } } // // SetConfigDir: // // Sets the location of the configuration directory, where configuration // files are stored - default.cfg, chocolate-doom.cfg, savegames, etc. // void M_SetConfigDir(char *dir) { // Use the directory that was passed, or find the default. if (dir != NULL) { configdir = dir; } else { configdir = GetDefaultConfigDir(); } if (strcmp(configdir, "") != 0) { printf("Using %s for configuration and saves\n", configdir); } // Make the directory if it doesn't already exist: M_MakeDirectory(configdir); } // // Calculate the path to the directory to use to store save games. // Creates the directory as necessary. // char *M_GetSaveGameDir(char *iwadname) { char *savegamedir; #if ORIGCODE char *topdir; #endif // If not "doing" a configuration directory (Windows), don't "do" // a savegame directory, either. if (!strcmp(configdir, "")) { savegamedir = strdup(""); } else { #if ORIGCODE // ~/.chocolate-doom/savegames topdir = M_StringJoin(configdir, "savegame", NULL); M_MakeDirectory(topdir); // eg. ~/.chocolate-doom/savegames/doom2.wad/ savegamedir = M_StringJoin(topdir, DIR_SEPARATOR_S, iwadname, DIR_SEPARATOR_S, NULL); M_MakeDirectory(savegamedir); free(topdir); #else savegamedir = M_StringJoin(configdir, "savegame/", NULL); M_MakeDirectory(savegamedir); printf ("Using %s for savegames\n", savegamedir); #endif } return savegamedir; } ================================================ FILE: fbdoom/m_config.h ================================================ // // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // 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. // // DESCRIPTION: // Configuration file interface. // #ifndef __M_CONFIG__ #define __M_CONFIG__ #include "doomtype.h" void M_LoadDefaults(void); void M_SaveDefaults(void); void M_SaveDefaultsAlternate(char *main, char *extra); void M_SetConfigDir(char *dir); void M_BindVariable(char *name, void *variable); boolean M_SetVariable(char *name, char *value); int M_GetIntVariable(char *name); const char *M_GetStrVariable(char *name); float M_GetFloatVariable(char *name); void M_SetConfigFilenames(char *main_config, char *extra_config); char *M_GetSaveGameDir(char *iwadname); extern char *configdir; #endif ================================================ FILE: fbdoom/m_controls.c ================================================ // // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 1993-2008 Raven Software // Copyright(C) 2005-2014 Simon Howard // // 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. // #include #include "doomtype.h" #include "doomkeys.h" #include "m_config.h" #include "m_misc.h" // // Keyboard controls // int key_right = KEY_RIGHTARROW; int key_left = KEY_LEFTARROW; int key_up = KEY_UPARROW; int key_down = KEY_DOWNARROW; int key_strafeleft = KEY_STRAFE_L; int key_straferight = KEY_STRAFE_R; int key_fire = KEY_FIRE; int key_use = KEY_USE; int key_strafe = KEY_RALT; int key_speed = KEY_RSHIFT; // // Heretic keyboard controls // int key_flyup = KEY_PGUP; int key_flydown = KEY_INS; int key_flycenter = KEY_HOME; int key_lookup = KEY_PGDN; int key_lookdown = KEY_DEL; int key_lookcenter = KEY_END; int key_invleft = '['; int key_invright = ']'; int key_useartifact = KEY_ENTER; // // Hexen key controls // int key_jump = '/'; int key_arti_all = KEY_BACKSPACE; int key_arti_health = '\\'; int key_arti_poisonbag = '0'; int key_arti_blastradius = '9'; int key_arti_teleport = '8'; int key_arti_teleportother = '7'; int key_arti_egg = '6'; int key_arti_invulnerability = '5'; // // Strife key controls // // haleyjd 09/01/10 // // Note: Strife also uses key_invleft, key_invright, key_jump, key_lookup, and // key_lookdown, but with different default values. int key_usehealth = 'h'; int key_invquery = 'q'; int key_mission = 'w'; int key_invpop = 'z'; int key_invkey = 'k'; int key_invhome = KEY_HOME; int key_invend = KEY_END; int key_invuse = KEY_ENTER; int key_invdrop = KEY_BACKSPACE; // // Mouse controls // int mousebfire = 0; int mousebstrafe = 1; int mousebforward = 2; int mousebjump = -1; int mousebstrafeleft = -1; int mousebstraferight = -1; int mousebbackward = -1; int mousebuse = -1; int mousebprevweapon = -1; int mousebnextweapon = -1; int key_message_refresh = KEY_ENTER; int key_pause = KEY_PAUSE; int key_demo_quit = 'q'; int key_spy = KEY_F12; // Multiplayer chat keys: int key_multi_msg = 't'; int key_multi_msgplayer[8]; // Weapon selection keys: int key_weapon1 = '1'; int key_weapon2 = '2'; int key_weapon3 = '3'; int key_weapon4 = '4'; int key_weapon5 = '5'; int key_weapon6 = '6'; int key_weapon7 = '7'; int key_weapon8 = '8'; int key_prevweapon = 0; int key_nextweapon = 0; // Map control keys: int key_map_north = KEY_UPARROW; int key_map_south = KEY_DOWNARROW; int key_map_east = KEY_RIGHTARROW; int key_map_west = KEY_LEFTARROW; int key_map_zoomin = '='; int key_map_zoomout = '-'; int key_map_toggle = KEY_TAB; int key_map_maxzoom = '0'; int key_map_follow = 'f'; int key_map_grid = 'g'; int key_map_mark = 'm'; int key_map_clearmark = 'c'; // menu keys: int key_menu_activate = KEY_ESCAPE; int key_menu_up = KEY_UPARROW; int key_menu_down = KEY_DOWNARROW; int key_menu_left = KEY_LEFTARROW; int key_menu_right = KEY_RIGHTARROW; int key_menu_back = KEY_BACKSPACE; int key_menu_forward = KEY_ENTER; int key_menu_confirm = 'y'; int key_menu_abort = 'n'; int key_menu_help = KEY_F1; int key_menu_save = KEY_F2; int key_menu_load = KEY_F3; int key_menu_volume = KEY_F4; int key_menu_detail = KEY_F5; int key_menu_qsave = KEY_F6; int key_menu_endgame = KEY_F7; int key_menu_messages = KEY_F8; int key_menu_qload = KEY_F9; int key_menu_quit = KEY_F10; int key_menu_gamma = KEY_F11; int key_menu_incscreen = KEY_EQUALS; int key_menu_decscreen = KEY_MINUS; int key_menu_screenshot = 0; // // Joystick controls // int joybfire = 0; int joybstrafe = 1; int joybuse = 3; int joybspeed = 2; int joybstrafeleft = -1; int joybstraferight = -1; int joybjump = -1; int joybprevweapon = -1; int joybnextweapon = -1; int joybmenu = -1; // Control whether if a mouse button is double clicked, it acts like // "use" has been pressed int dclick_use = 1; // // Bind all of the common controls used by Doom and all other games. // void M_BindBaseControls(void) { M_BindVariable("key_right", &key_right); M_BindVariable("key_left", &key_left); M_BindVariable("key_up", &key_up); M_BindVariable("key_down", &key_down); M_BindVariable("key_strafeleft", &key_strafeleft); M_BindVariable("key_straferight", &key_straferight); M_BindVariable("key_fire", &key_fire); M_BindVariable("key_use", &key_use); M_BindVariable("key_strafe", &key_strafe); M_BindVariable("key_speed", &key_speed); M_BindVariable("mouseb_fire", &mousebfire); M_BindVariable("mouseb_strafe", &mousebstrafe); M_BindVariable("mouseb_forward", &mousebforward); M_BindVariable("joyb_fire", &joybfire); M_BindVariable("joyb_strafe", &joybstrafe); M_BindVariable("joyb_use", &joybuse); M_BindVariable("joyb_speed", &joybspeed); M_BindVariable("joyb_menu_activate", &joybmenu); // Extra controls that are not in the Vanilla versions: M_BindVariable("joyb_strafeleft", &joybstrafeleft); M_BindVariable("joyb_straferight", &joybstraferight); M_BindVariable("mouseb_strafeleft", &mousebstrafeleft); M_BindVariable("mouseb_straferight", &mousebstraferight); M_BindVariable("mouseb_use", &mousebuse); M_BindVariable("mouseb_backward", &mousebbackward); M_BindVariable("dclick_use", &dclick_use); M_BindVariable("key_pause", &key_pause); M_BindVariable("key_message_refresh", &key_message_refresh); } void M_BindHereticControls(void) { M_BindVariable("key_flyup", &key_flyup); M_BindVariable("key_flydown", &key_flydown); M_BindVariable("key_flycenter", &key_flycenter); M_BindVariable("key_lookup", &key_lookup); M_BindVariable("key_lookdown", &key_lookdown); M_BindVariable("key_lookcenter", &key_lookcenter); M_BindVariable("key_invleft", &key_invleft); M_BindVariable("key_invright", &key_invright); M_BindVariable("key_useartifact", &key_useartifact); } void M_BindHexenControls(void) { M_BindVariable("key_jump", &key_jump); M_BindVariable("mouseb_jump", &mousebjump); M_BindVariable("joyb_jump", &joybjump); M_BindVariable("key_arti_all", &key_arti_all); M_BindVariable("key_arti_health", &key_arti_health); M_BindVariable("key_arti_poisonbag", &key_arti_poisonbag); M_BindVariable("key_arti_blastradius", &key_arti_blastradius); M_BindVariable("key_arti_teleport", &key_arti_teleport); M_BindVariable("key_arti_teleportother", &key_arti_teleportother); M_BindVariable("key_arti_egg", &key_arti_egg); M_BindVariable("key_arti_invulnerability", &key_arti_invulnerability); } void M_BindStrifeControls(void) { // These are shared with all games, but have different defaults: key_message_refresh = '/'; // These keys are shared with Heretic/Hexen but have different defaults: key_jump = 'a'; key_lookup = KEY_PGUP; key_lookdown = KEY_PGDN; key_invleft = KEY_INS; key_invright = KEY_DEL; M_BindVariable("key_jump", &key_jump); M_BindVariable("key_lookUp", &key_lookup); M_BindVariable("key_lookDown", &key_lookdown); M_BindVariable("key_invLeft", &key_invleft); M_BindVariable("key_invRight", &key_invright); // Custom Strife-only Keys: M_BindVariable("key_useHealth", &key_usehealth); M_BindVariable("key_invquery", &key_invquery); M_BindVariable("key_mission", &key_mission); M_BindVariable("key_invPop", &key_invpop); M_BindVariable("key_invKey", &key_invkey); M_BindVariable("key_invHome", &key_invhome); M_BindVariable("key_invEnd", &key_invend); M_BindVariable("key_invUse", &key_invuse); M_BindVariable("key_invDrop", &key_invdrop); // Strife also supports jump on mouse and joystick, and in the exact same // manner as Hexen! M_BindVariable("mouseb_jump", &mousebjump); M_BindVariable("joyb_jump", &joybjump); } void M_BindWeaponControls(void) { M_BindVariable("key_weapon1", &key_weapon1); M_BindVariable("key_weapon2", &key_weapon2); M_BindVariable("key_weapon3", &key_weapon3); M_BindVariable("key_weapon4", &key_weapon4); M_BindVariable("key_weapon5", &key_weapon5); M_BindVariable("key_weapon6", &key_weapon6); M_BindVariable("key_weapon7", &key_weapon7); M_BindVariable("key_weapon8", &key_weapon8); M_BindVariable("key_prevweapon", &key_prevweapon); M_BindVariable("key_nextweapon", &key_nextweapon); M_BindVariable("joyb_prevweapon", &joybprevweapon); M_BindVariable("joyb_nextweapon", &joybnextweapon); M_BindVariable("mouseb_prevweapon", &mousebprevweapon); M_BindVariable("mouseb_nextweapon", &mousebnextweapon); } void M_BindMapControls(void) { M_BindVariable("key_map_north", &key_map_north); M_BindVariable("key_map_south", &key_map_south); M_BindVariable("key_map_east", &key_map_east); M_BindVariable("key_map_west", &key_map_west); M_BindVariable("key_map_zoomin", &key_map_zoomin); M_BindVariable("key_map_zoomout", &key_map_zoomout); M_BindVariable("key_map_toggle", &key_map_toggle); M_BindVariable("key_map_maxzoom", &key_map_maxzoom); M_BindVariable("key_map_follow", &key_map_follow); M_BindVariable("key_map_grid", &key_map_grid); M_BindVariable("key_map_mark", &key_map_mark); M_BindVariable("key_map_clearmark", &key_map_clearmark); } void M_BindMenuControls(void) { M_BindVariable("key_menu_activate", &key_menu_activate); M_BindVariable("key_menu_up", &key_menu_up); M_BindVariable("key_menu_down", &key_menu_down); M_BindVariable("key_menu_left", &key_menu_left); M_BindVariable("key_menu_right", &key_menu_right); M_BindVariable("key_menu_back", &key_menu_back); M_BindVariable("key_menu_forward", &key_menu_forward); M_BindVariable("key_menu_confirm", &key_menu_confirm); M_BindVariable("key_menu_abort", &key_menu_abort); M_BindVariable("key_menu_help", &key_menu_help); M_BindVariable("key_menu_save", &key_menu_save); M_BindVariable("key_menu_load", &key_menu_load); M_BindVariable("key_menu_volume", &key_menu_volume); M_BindVariable("key_menu_detail", &key_menu_detail); M_BindVariable("key_menu_qsave", &key_menu_qsave); M_BindVariable("key_menu_endgame", &key_menu_endgame); M_BindVariable("key_menu_messages", &key_menu_messages); M_BindVariable("key_menu_qload", &key_menu_qload); M_BindVariable("key_menu_quit", &key_menu_quit); M_BindVariable("key_menu_gamma", &key_menu_gamma); M_BindVariable("key_menu_incscreen", &key_menu_incscreen); M_BindVariable("key_menu_decscreen", &key_menu_decscreen); M_BindVariable("key_menu_screenshot",&key_menu_screenshot); M_BindVariable("key_demo_quit", &key_demo_quit); M_BindVariable("key_spy", &key_spy); } void M_BindChatControls(unsigned int num_players) { char name[32]; // haleyjd: 20 not large enough - Thank you, come again! unsigned int i; // haleyjd: signedness conflict M_BindVariable("key_multi_msg", &key_multi_msg); for (i=0; i> FRACBITS; } // // FixedDiv, C version. // fixed_t FixedDiv(fixed_t a, fixed_t b) { if ((abs(a) >> 14) >= abs(b)) { return (a^b) < 0 ? INT_MIN : INT_MAX; } else { int64_t result; result = ((int64_t) a << 16) / b; return (fixed_t) result; } } ================================================ FILE: fbdoom/m_fixed.h ================================================ // // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // 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. // // DESCRIPTION: // Fixed point arithemtics, implementation. // #ifndef __M_FIXED__ #define __M_FIXED__ // // Fixed point, 32bit as 16.16. // #define FRACBITS 16 #define FRACUNIT (1< #include #include "doomdef.h" #include "doomkeys.h" #include "dstrings.h" #include "d_main.h" #include "deh_main.h" #include "i_input_tty.h" #include "i_swap.h" #include "i_system.h" #include "i_timer.h" #include "i_video.h" #include "m_misc.h" #include "v_video.h" #include "w_wad.h" #include "z_zone.h" #include "r_local.h" #include "hu_stuff.h" #include "g_game.h" #include "m_argv.h" #include "m_controls.h" #include "p_saveg.h" #include "s_sound.h" #include "doomstat.h" // Data. #include "sounds.h" #include "m_menu.h" extern patch_t* hu_font[HU_FONTSIZE]; extern boolean message_dontfuckwithme; extern boolean chat_on; // in heads-up code // // defaulted values // int mouseSensitivity = 5; // Show messages has default, 0 = off, 1 = on int showMessages = 1; // Blocky mode, has default, 0 = high, 1 = normal int detailLevel = 0; int screenblocks = 9; // temp for screenblocks (0-9) int screenSize; // -1 = no quicksave slot picked! int quickSaveSlot; // 1 = message to be printed int messageToPrint; // ...and here is the message string! char* messageString; // message x & y int messx; int messy; int messageLastMenuActive; // timed message = no input from user boolean messageNeedsInput; void (*messageRoutine)(int response); char gammamsg[5][26] = { GAMMALVL0, GAMMALVL1, GAMMALVL2, GAMMALVL3, GAMMALVL4 }; // 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; boolean menuactive; #define SKULLXOFF -32 #define LINEHEIGHT 16 extern boolean sendpause; char savegamestrings[10][SAVESTRINGSIZE]; char endstring[160]; //static boolean opldev; // // MENU TYPEDEFS // typedef struct { // 0 = no cursor here, 1 = ok, 2 = arrows ok short status; char name[10]; // choice = menu item #. // if status = 2, // choice=0:leftarrow,1:rightarrow void (*routine)(int choice); // hotkey in menu char alphaKey; } 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 short skullAnimCounter; // skull animation counter short whichSkull; // which skull to draw // graphic name of skulls // warning: initializer-string for array of chars is too long char *skullName[2] = {"M_SKULL1","M_SKULL2"}; // current menudef menu_t* currentMenu; // // 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); void M_SizeDisplay(int choice); void M_StartGame(int choice); void M_Sound(int choice); void M_FinishReadThis(int choice); 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_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, char *string); int M_StringWidth(char *string); int M_StringHeight(char *string); void M_StartMessage(char *string,void *routine,boolean input); void M_StopMessage(void); void M_ClearMenus (void); // // DOOM MENU // enum { newgame = 0, options, loadgame, savegame, readthis, quitdoom, main_end } main_e; 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, NULL, MainMenu, M_DrawMainMenu, 97,64, 0 }; // // EPISODE SELECT // enum { ep1, ep2, ep3, ep4, ep_end } episodes_e; 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 }; // // NEW GAME // enum { killthings, toorough, hurtme, violence, nightmare, newg_end } newgame_e; 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 }; // // OPTIONS MENU // enum { endgame, messages, detail, scrnsize, option_empty1, mousesens, option_empty2, soundvol, opt_end } options_e; menuitem_t OptionsMenu[]= { {1,"M_ENDGAM", M_EndGame,'e'}, {1,"M_MESSG", M_ChangeMessages,'m'}, {1,"M_DETAIL", M_ChangeDetail,'g'}, {2,"M_SCRNSZ", M_SizeDisplay,'s'}, {-1,"",0,'\0'}, {2,"M_MSENS", M_ChangeSensitivity,'m'}, {-1,"",0,'\0'}, {1,"M_SVOL", M_Sound,'s'} }; menu_t OptionsDef = { opt_end, &MainDef, OptionsMenu, M_DrawOptions, 60,37, 0 }; // // Read This! MENU 1 & 2 // enum { rdthsempty1, read1_end } read_e; menuitem_t ReadMenu1[] = { {1,"",M_ReadThis2,0} }; menu_t ReadDef1 = { read1_end, &MainDef, ReadMenu1, M_DrawReadThis1, 280,185, 0 }; enum { rdthsempty2, read2_end } read_e2; menuitem_t ReadMenu2[]= { {1,"",M_FinishReadThis,0} }; menu_t ReadDef2 = { read2_end, &ReadDef1, ReadMenu2, M_DrawReadThis2, 330,175, 0 }; // // SOUND VOLUME MENU // enum { sfx_vol, sfx_empty1, music_vol, sfx_empty2, sound_end } sound_e; menuitem_t SoundMenu[]= { {2,"M_SFXVOL",M_SfxVol,'s'}, {-1,"",0,'\0'}, {2,"M_MUSVOL",M_MusicVol,'m'}, {-1,"",0,'\0'} }; menu_t SoundDef = { sound_end, &OptionsDef, SoundMenu, M_DrawSound, 80,64, 0 }; // // LOAD GAME MENU // enum { load1, load2, load3, load4, load5, load6, load_end } load_e; menuitem_t LoadMenu[]= { {1,"", M_LoadSelect,'1'}, {1,"", M_LoadSelect,'2'}, {1,"", M_LoadSelect,'3'}, {1,"", M_LoadSelect,'4'}, {1,"", M_LoadSelect,'5'}, {1,"", M_LoadSelect,'6'} }; menu_t LoadDef = { load_end, &MainDef, LoadMenu, M_DrawLoad, 80,54, 0 }; // // SAVE GAME MENU // 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'} }; menu_t SaveDef = { load_end, &MainDef, SaveMenu, M_DrawSave, 80,54, 0 }; // // M_ReadSaveStrings // read the strings from the savegame files // void M_ReadSaveStrings(void) { FILE *handle; int i; char name[256]; for (i = 0;i < load_end;i++) { M_StringCopy(name, P_SaveGameFile(i), sizeof(name)); handle = fopen(name, "rb"); if (handle == NULL) { M_StringCopy(savegamestrings[i], EMPTYSTRING, SAVESTRINGSIZE); LoadMenu[i].status = 0; continue; } fread(&savegamestrings[i], 1, SAVESTRINGSIZE, handle); fclose(handle); LoadMenu[i].status = 1; } } // // M_LoadGame & Cie. // void M_DrawLoad(void) { int i; V_DrawPatchDirect(72, 28, W_CacheLumpName(DEH_String("M_LOADG"), PU_CACHE)); 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_DrawPatchDirect(x - 8, y + 7, W_CacheLumpName(DEH_String("M_LSLEFT"), PU_CACHE)); for (i = 0;i < 24;i++) { V_DrawPatchDirect(x, y + 7, W_CacheLumpName(DEH_String("M_LSCNTR"), PU_CACHE)); x += 8; } V_DrawPatchDirect(x, y + 7, W_CacheLumpName(DEH_String("M_LSRGHT"), PU_CACHE)); } // // User wants to load this game // void M_LoadSelect(int choice) { char name[256]; M_StringCopy(name, P_SaveGameFile(choice), sizeof(name)); G_LoadGame (name); M_ClearMenus (); } // // Selected from DOOM menu // void M_LoadGame (int choice) { if (netgame) { M_StartMessage(DEH_String(LOADNET),NULL,false); return; } M_SetupNextMenu(&LoadDef); M_ReadSaveStrings(); } // // M_SaveGame & Cie. // void M_DrawSave(void) { int i; V_DrawPatchDirect(72, 28, W_CacheLumpName(DEH_String("M_SAVEG"), PU_CACHE)); 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 // 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; M_StringCopy(saveOldString,savegamestrings[choice], SAVESTRINGSIZE); if (!strcmp(savegamestrings[choice], EMPTYSTRING)) savegamestrings[choice][0] = 0; saveCharIndex = strlen(savegamestrings[choice]); } // // Selected from DOOM menu // void M_SaveGame (int choice) { if (!usergame) { M_StartMessage(DEH_String(SAVEDEAD),NULL,false); return; } if (gamestate != GS_LEVEL) return; M_SetupNextMenu(&SaveDef); M_ReadSaveStrings(); } // // M_QuickSave // char tempstring[80]; void M_QuickSaveResponse(int key) { if (key == key_menu_confirm) { M_DoSave(quickSaveSlot); S_StartSound(NULL,sfx_swtchx); } } void M_QuickSave(void) { if (!usergame) { 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; } DEH_snprintf(tempstring, 80, QSPROMPT, savegamestrings[quickSaveSlot]); M_StartMessage(tempstring,M_QuickSaveResponse,true); } // // M_QuickLoad // void M_QuickLoadResponse(int key) { if (key == key_menu_confirm) { M_LoadSelect(quickSaveSlot); S_StartSound(NULL,sfx_swtchx); } } void M_QuickLoad(void) { if (netgame) { M_StartMessage(DEH_String(QLOADNET),NULL,false); return; } if (quickSaveSlot < 0) { M_StartMessage(DEH_String(QSAVESPOT),NULL,false); return; } DEH_snprintf(tempstring, 80, QLPROMPT, savegamestrings[quickSaveSlot]); M_StartMessage(tempstring,M_QuickLoadResponse,true); } // // Read This Menus // Had a "quick hack to fix romero bug" // void M_DrawReadThis1(void) { char *lumpname = "CREDIT"; int skullx = 330, skully = 175; inhelpscreens = true; // Different versions of Doom 1.9 work differently switch (gameversion) { case exe_doom_1_666: case exe_doom_1_7: case exe_doom_1_8: case exe_doom_1_9: case exe_hacx: if (gamemode == commercial) { // Doom 2 lumpname = "HELP"; skullx = 330; skully = 165; } else { // Doom 1 // HELP2 is the first screen shown in Doom 1 lumpname = "HELP2"; skullx = 280; skully = 185; } break; case exe_ultimate: case exe_chex: // Ultimate Doom always displays "HELP1". // Chex Quest version also uses "HELP1", even though it is based // on Final Doom. lumpname = "HELP1"; break; case exe_final: case exe_final2: // Final Doom always displays "HELP". lumpname = "HELP"; break; default: I_Error("Unhandled game version"); break; } lumpname = DEH_String(lumpname); V_DrawPatchDirect (0, 0, W_CacheLumpName(lumpname, PU_CACHE)); ReadDef1.x = skullx; ReadDef1.y = skully; } // // Read This Menus - optional second page. // void M_DrawReadThis2(void) { inhelpscreens = true; // We only ever draw the second page if this is // gameversion == exe_doom_1_9 and gamemode == registered V_DrawPatchDirect(0, 0, W_CacheLumpName(DEH_String("HELP1"), PU_CACHE)); } // // Change Sfx & Music volumes // void M_DrawSound(void) { V_DrawPatchDirect (60, 38, W_CacheLumpName(DEH_String("M_SVOL"), PU_CACHE)); M_DrawThermo(SoundDef.x,SoundDef.y+LINEHEIGHT*(sfx_vol+1), 16,sfxVolume); M_DrawThermo(SoundDef.x,SoundDef.y+LINEHEIGHT*(music_vol+1), 16,musicVolume); } void M_Sound(int choice) { M_SetupNextMenu(&SoundDef); } void M_SfxVol(int choice) { switch(choice) { case 0: if (sfxVolume) sfxVolume--; break; case 1: if (sfxVolume < 15) sfxVolume++; break; } S_SetSfxVolume(sfxVolume * 8); } void M_MusicVol(int choice) { switch(choice) { case 0: if (musicVolume) musicVolume--; break; case 1: if (musicVolume < 15) musicVolume++; break; } S_SetMusicVolume(musicVolume * 8); } // // M_DrawMainMenu // void M_DrawMainMenu(void) { V_DrawPatchDirect(94, 2, W_CacheLumpName(DEH_String("M_DOOM"), PU_CACHE)); } // // M_NewGame // void M_DrawNewGame(void) { V_DrawPatchDirect(96, 14, W_CacheLumpName(DEH_String("M_NEWG"), PU_CACHE)); V_DrawPatchDirect(54, 38, W_CacheLumpName(DEH_String("M_SKILL"), PU_CACHE)); } void M_NewGame(int choice) { if (netgame && !demoplayback) { M_StartMessage(DEH_String(NEWGAME),NULL,false); return; } // Chex Quest disabled the episode select screen, as did Doom II. if (gamemode == commercial || gameversion == exe_chex) M_SetupNextMenu(&NewDef); else M_SetupNextMenu(&EpiDef); } // // M_Episode // int epi; void M_DrawEpisode(void) { V_DrawPatchDirect(54, 38, W_CacheLumpName(DEH_String("M_EPISOD"), PU_CACHE)); } void M_VerifyNightmare(int key) { if (key != key_menu_confirm) return; G_DeferedInitNew(nightmare,epi+1,1); M_ClearMenus (); } void M_ChooseSkill(int choice) { if (choice == nightmare) { M_StartMessage(DEH_String(NIGHTMARE),M_VerifyNightmare,true); return; } G_DeferedInitNew(choice,epi+1,1); M_ClearMenus (); } void M_Episode(int choice) { if ( (gamemode == shareware) && choice) { M_StartMessage(DEH_String(SWSTRING),NULL,false); M_SetupNextMenu(&ReadDef1); return; } // Yet another hack... if ( (gamemode == registered) && (choice > 2)) { fprintf( stderr, "M_Episode: 4th episode requires UltimateDOOM\n"); choice = 0; } epi = choice; M_SetupNextMenu(&NewDef); } // // M_Options // static char *detailNames[2] = {"M_GDHIGH","M_GDLOW"}; static char *msgNames[2] = {"M_MSGOFF","M_MSGON"}; void M_DrawOptions(void) { V_DrawPatchDirect(108, 15, W_CacheLumpName(DEH_String("M_OPTTTL"), PU_CACHE)); V_DrawPatchDirect(OptionsDef.x + 175, OptionsDef.y + LINEHEIGHT * detail, W_CacheLumpName(DEH_String(detailNames[detailLevel]), PU_CACHE)); V_DrawPatchDirect(OptionsDef.x + 120, OptionsDef.y + LINEHEIGHT * messages, W_CacheLumpName(DEH_String(msgNames[showMessages]), PU_CACHE)); M_DrawThermo(OptionsDef.x, OptionsDef.y + LINEHEIGHT * (mousesens + 1), 10, mouseSensitivity); M_DrawThermo(OptionsDef.x,OptionsDef.y+LINEHEIGHT*(scrnsize+1), 9,screenSize); } void M_Options(int choice) { M_SetupNextMenu(&OptionsDef); } // // Toggle messages on/off // void M_ChangeMessages(int choice) { // warning: unused parameter `int choice' choice = 0; showMessages = 1 - showMessages; if (!showMessages) players[consoleplayer].message = DEH_String(MSGOFF); else players[consoleplayer].message = DEH_String(MSGON); message_dontfuckwithme = true; } // // M_EndGame // void M_EndGameResponse(int key) { if (key != key_menu_confirm) return; currentMenu->lastOn = itemOn; M_ClearMenus (); D_StartTitle (); } void M_EndGame(int choice) { choice = 0; if (!usergame) { S_StartSound(NULL,sfx_oof); return; } if (netgame) { M_StartMessage(DEH_String(NETEND),NULL,false); return; } M_StartMessage(DEH_String(ENDGAME),M_EndGameResponse,true); } // // M_ReadThis // void M_ReadThis(int choice) { choice = 0; M_SetupNextMenu(&ReadDef1); } void M_ReadThis2(int choice) { // Doom 1.9 had two menus when playing Doom 1 // All others had only one if (gameversion <= exe_doom_1_9 && gamemode != commercial) { choice = 0; M_SetupNextMenu(&ReadDef2); } else { // Close the menu M_FinishReadThis(0); } } void M_FinishReadThis(int choice) { choice = 0; M_SetupNextMenu(&MainDef); } // // 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 }; void M_QuitResponse(int key) { if (key != key_menu_confirm) return; if (!netgame) { if (gamemode == commercial) S_StartSound(NULL,quitsounds2[(gametic>>2)&7]); else S_StartSound(NULL,quitsounds[(gametic>>2)&7]); I_WaitVBL(105); } I_Quit (); kbd_shutdown(); } static char *M_SelectEndMessage(void) { char **endmsg; if (logical_gamemission == doom) { // Doom 1 endmsg = doom1_endmsg; } else { // Doom 2 endmsg = doom2_endmsg; } return endmsg[gametic % NUM_QUITMESSAGES]; } void M_QuitDOOM(int choice) { DEH_snprintf(endstring, sizeof(endstring), "%s\n\n" DOSY, DEH_String(M_SelectEndMessage())); M_StartMessage(endstring,M_QuitResponse,true); } void M_ChangeSensitivity(int choice) { switch(choice) { case 0: if (mouseSensitivity) mouseSensitivity--; break; case 1: if (mouseSensitivity < 9) mouseSensitivity++; break; } } void M_ChangeDetail(int choice) { choice = 0; detailLevel = 1 - detailLevel; R_SetViewSize (screenblocks, detailLevel); if (!detailLevel) players[consoleplayer].message = DEH_String(DETAILHI); else players[consoleplayer].message = DEH_String(DETAILLO); } void M_SizeDisplay(int choice) { switch(choice) { case 0: if (screenSize > 0) { screenblocks--; screenSize--; } break; case 1: if (screenSize < 8) { screenblocks++; screenSize++; } break; } R_SetViewSize (screenblocks, detailLevel); } // // Menu Functions // void M_DrawThermo ( int x, int y, int thermWidth, int thermDot ) { int xx; int i; xx = x; V_DrawPatchDirect(xx, y, W_CacheLumpName(DEH_String("M_THERML"), PU_CACHE)); xx += 8; for (i=0;ix - 10, menu->y + item * LINEHEIGHT - 1, W_CacheLumpName(DEH_String("M_CELL1"), PU_CACHE)); } void M_DrawSelCell ( menu_t* menu, int item ) { V_DrawPatchDirect(menu->x - 10, menu->y + item * LINEHEIGHT - 1, W_CacheLumpName(DEH_String("M_CELL2"), PU_CACHE)); } void M_StartMessage ( 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; } // // Find string width from hu_font chars // int M_StringWidth(char* string) { size_t i; int w = 0; int c; for (i = 0;i < strlen(string);i++) { c = toupper(string[i]) - HU_FONTSTART; if (c < 0 || c >= HU_FONTSIZE) w += 4; else w += SHORT (hu_font[c]->width); } return w; } // // Find string height from hu_font chars // int M_StringHeight(char* string) { size_t i; int h; int height = SHORT(hu_font[0]->height); h = height; for (i = 0;i < strlen(string);i++) if (string[i] == '\n') h += height; return h; } // // Write a string using the hu_font // void M_WriteText ( int x, int y, char* string) { int w; 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 = SHORT (hu_font[c]->width); if (cx+w > SCREENWIDTH) break; V_DrawPatchDirect(cx, cy, hu_font[c]); cx+=w; } } // These keys evaluate to a "null" key in Vanilla Doom that allows weird // jumping in the menus. Preserve this behavior for accuracy. static boolean IsNullKey(int key) { return key == KEY_PAUSE || key == KEY_CAPSLOCK || key == KEY_SCRLCK || key == KEY_NUMLOCK; } // // CONTROL PANEL // // // M_Responder // boolean M_Responder (event_t* ev) { int ch; int key; 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; // In testcontrols mode, none of the function keys should do anything // - the only key is escape to quit. if (testcontrols) { if (ev->type == ev_quit || (ev->type == ev_keydown && (ev->data1 == key_menu_activate || ev->data1 == key_menu_quit))) { I_Quit(); return true; } return false; } // "close" button pressed on window? if (ev->type == ev_quit) { // First click on close button = bring up quit confirm message. // Second click on close button = confirm quit if (menuactive && messageToPrint && messageRoutine == M_QuitResponse) { M_QuitResponse(key_menu_confirm); } else { S_StartSound(NULL,sfx_swtchn); M_QuitDOOM(0); } return true; } // key is the key pressed, ch is the actual character typed ch = 0; key = -1; if (ev->type == ev_joystick && joywait < I_GetTime()) { if (ev->data3 < 0) { key = key_menu_up; joywait = I_GetTime() + 5; } else if (ev->data3 > 0) { key = key_menu_down; joywait = I_GetTime() + 5; } if (ev->data2 < 0) { key = key_menu_left; joywait = I_GetTime() + 2; } else if (ev->data2 > 0) { key = key_menu_right; joywait = I_GetTime() + 2; } if (ev->data1&1) { key = key_menu_forward; joywait = I_GetTime() + 5; } if (ev->data1&2) { key = key_menu_back; joywait = I_GetTime() + 5; } if (joybmenu >= 0 && (ev->data1 & (1 << joybmenu)) != 0) { key = key_menu_activate; joywait = I_GetTime() + 5; } } else { if (ev->type == ev_mouse && mousewait < I_GetTime()) { mousey += ev->data3; if (mousey < lasty-30) { key = key_menu_down; mousewait = I_GetTime() + 5; mousey = lasty -= 30; } else if (mousey > lasty+30) { key = key_menu_up; mousewait = I_GetTime() + 5; mousey = lasty += 30; } mousex += ev->data2; if (mousex < lastx-30) { key = key_menu_left; mousewait = I_GetTime() + 5; mousex = lastx -= 30; } else if (mousex > lastx+30) { key = key_menu_right; mousewait = I_GetTime() + 5; mousex = lastx += 30; } if (ev->data1&1) { key = key_menu_forward; mousewait = I_GetTime() + 15; } if (ev->data1&2) { key = key_menu_back; mousewait = I_GetTime() + 15; } } else { if (ev->type == ev_keydown) { key = ev->data1; ch = ev->data2; } } } if (key == -1) return false; // Save Game string input if (saveStringEnter) { switch(key) { case KEY_BACKSPACE: if (saveCharIndex > 0) { saveCharIndex--; savegamestrings[saveSlot][saveCharIndex] = 0; } break; case KEY_ESCAPE: saveStringEnter = 0; M_StringCopy(savegamestrings[saveSlot], saveOldString, SAVESTRINGSIZE); break; case KEY_ENTER: saveStringEnter = 0; if (savegamestrings[saveSlot][0]) M_DoSave(saveSlot); break; default: // This is complicated. // Vanilla has a bug where the shift key is ignored when entering // a savegame name. If vanilla_keyboard_mapping is on, we want // to emulate this bug by using 'data1'. But if it's turned off, // it implies the user doesn't care about Vanilla emulation: just // use the correct 'data2'. if (vanilla_keyboard_mapping) { ch = key; } ch = toupper(ch); if (ch != ' ' && (ch - HU_FONTSTART < 0 || ch - HU_FONTSTART >= HU_FONTSIZE)) { break; } if (ch >= 32 && ch <= 127 && saveCharIndex < SAVESTRINGSIZE-1 && M_StringWidth(savegamestrings[saveSlot]) < (SAVESTRINGSIZE-2)*8) { savegamestrings[saveSlot][saveCharIndex++] = ch; savegamestrings[saveSlot][saveCharIndex] = 0; } break; } return true; } // Take care of any messages that need input if (messageToPrint) { if (messageNeedsInput) { if (key != ' ' && key != KEY_ESCAPE && key != key_menu_confirm && key != key_menu_abort) { return false; } } menuactive = messageLastMenuActive; messageToPrint = 0; if (messageRoutine) messageRoutine(key); menuactive = false; S_StartSound(NULL,sfx_swtchx); return true; } if ((devparm && key == key_menu_help) || (key != 0 && key == key_menu_screenshot)) { G_ScreenShot (); return true; } // F-Keys if (!menuactive) { if (key == key_menu_decscreen) // Screen size down { if (automapactive || chat_on) return false; M_SizeDisplay(0); S_StartSound(NULL,sfx_stnmov); return true; } else if (key == key_menu_incscreen) // Screen size up { if (automapactive || chat_on) return false; M_SizeDisplay(1); S_StartSound(NULL,sfx_stnmov); return true; } else if (key == key_menu_help) // Help key { M_StartControlPanel (); if ( gamemode == retail ) currentMenu = &ReadDef2; else currentMenu = &ReadDef1; itemOn = 0; S_StartSound(NULL,sfx_swtchn); return true; } else if (key == key_menu_save) // Save { M_StartControlPanel(); S_StartSound(NULL,sfx_swtchn); M_SaveGame(0); return true; } else if (key == key_menu_load) // Load { M_StartControlPanel(); S_StartSound(NULL,sfx_swtchn); M_LoadGame(0); return true; } else if (key == key_menu_volume) // Sound Volume { M_StartControlPanel (); currentMenu = &SoundDef; itemOn = sfx_vol; S_StartSound(NULL,sfx_swtchn); return true; } else if (key == key_menu_detail) // Detail toggle { M_ChangeDetail(0); S_StartSound(NULL,sfx_swtchn); return true; } else if (key == key_menu_qsave) // Quicksave { S_StartSound(NULL,sfx_swtchn); M_QuickSave(); return true; } else if (key == key_menu_endgame) // End game { S_StartSound(NULL,sfx_swtchn); M_EndGame(0); return true; } else if (key == key_menu_messages) // Toggle messages { M_ChangeMessages(0); S_StartSound(NULL,sfx_swtchn); return true; } else if (key == key_menu_qload) // Quickload { S_StartSound(NULL,sfx_swtchn); M_QuickLoad(); return true; } else if (key == key_menu_quit) // Quit DOOM { S_StartSound(NULL,sfx_swtchn); M_QuitDOOM(0); return true; } else if (key == key_menu_gamma) // gamma toggle { usegamma++; if (usegamma > 4) usegamma = 0; players[consoleplayer].message = DEH_String(gammamsg[usegamma]); I_SetPalette (W_CacheLumpName (DEH_String("PLAYPAL"),PU_CACHE)); return true; } } // Pop-up menu? if (!menuactive) { if (key == key_menu_activate) { M_StartControlPanel (); S_StartSound(NULL,sfx_swtchn); return true; } return false; } // Keys usable within menu if (key == key_menu_down) { // Move down to next item do { if (itemOn+1 > currentMenu->numitems-1) itemOn = 0; else itemOn++; S_StartSound(NULL,sfx_pstop); } while(currentMenu->menuitems[itemOn].status==-1); return true; } else if (key == key_menu_up) { // Move back up to previous item do { if (!itemOn) itemOn = currentMenu->numitems-1; else itemOn--; S_StartSound(NULL,sfx_pstop); } while(currentMenu->menuitems[itemOn].status==-1); return true; } else if (key == key_menu_left) { // Slide slider left if (currentMenu->menuitems[itemOn].routine && currentMenu->menuitems[itemOn].status == 2) { S_StartSound(NULL,sfx_stnmov); currentMenu->menuitems[itemOn].routine(0); } return true; } else if (key == key_menu_right) { // Slide slider right if (currentMenu->menuitems[itemOn].routine && currentMenu->menuitems[itemOn].status == 2) { S_StartSound(NULL,sfx_stnmov); currentMenu->menuitems[itemOn].routine(1); } return true; } else if (key == key_menu_forward) { // Activate menu item 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); } } return true; } else if (key == key_menu_activate) { // Deactivate menu currentMenu->lastOn = itemOn; M_ClearMenus (); S_StartSound(NULL,sfx_swtchx); return true; } else if (key == key_menu_back) { // Go back to previous menu currentMenu->lastOn = itemOn; if (currentMenu->prevMenu) { currentMenu = currentMenu->prevMenu; itemOn = currentMenu->lastOn; S_StartSound(NULL,sfx_swtchn); } return true; } // Keyboard shortcut? // Vanilla Doom has a weird behavior where it jumps to the scroll bars // when the certain keys are pressed, so emulate this. else if (ch != 0 || IsNullKey(key)) { 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; } // // M_StartControlPanel // void M_StartControlPanel (void) { // intro might call this repeatedly if (menuactive) return; menuactive = 1; currentMenu = &MainDef; // JDC itemOn = currentMenu->lastOn; // JDC } // Display OPL debug messages - hack for GENMIDI development. #if 0 static void M_DrawOPLDev(void) { extern void I_OPL_DevMessages(char *, size_t); char debug[1024]; char *curr, *p; int line; //XXX I_OPL_DevMessages(debug, sizeof(debug)); curr = debug; line = 0; for (;;) { p = strchr(curr, '\n'); if (p != NULL) { *p = '\0'; } M_WriteText(0, line * 8, curr); ++line; if (p == NULL) { break; } curr = p + 1; } } #endif // // M_Drawer // Called after the view has been rendered, // but before it has been blitted. // void M_Drawer (void) { static short x; static short y; unsigned int i; unsigned int max; char string[80]; char *name; int start; inhelpscreens = false; // Horiz. & Vertically center string and print it. if (messageToPrint) { start = 0; y = SCREENHEIGHT/2 - M_StringHeight(messageString) / 2; while (messageString[start] != '\0') { int foundnewline = 0; for (i = 0; i < strlen(messageString + start); i++) { if (messageString[start + i] == '\n') { M_StringCopy(string, messageString + start, sizeof(string)); if (i < sizeof(string)) { string[i] = '\0'; } foundnewline = 1; start += i + 1; break; } } if (!foundnewline) { M_StringCopy(string, messageString + start, sizeof(string)); start += strlen(string); } x = SCREENWIDTH/2 - M_StringWidth(string) / 2; M_WriteText(x, y, string); y += SHORT(hu_font[0]->height); } return; } //if (opldev) //{ // M_DrawOPLDev(); //} if (!menuactive) return; 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); if (name[0]) { V_DrawPatchDirect (x, y, W_CacheLumpName(name, PU_CACHE)); } y += LINEHEIGHT; } // DRAW SKULL V_DrawPatchDirect(x + SKULLXOFF, currentMenu->y - 5 + itemOn*LINEHEIGHT, W_CacheLumpName(DEH_String(skullName[whichSkull]), PU_CACHE)); } // // M_ClearMenus // void M_ClearMenus (void) { menuactive = 0; // 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; } } // // M_Init // void M_Init (void) { 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: // Commercial has no "read this" entry. MainMenu[readthis] = MainMenu[quitdoom]; MainDef.numitems--; MainDef.y += 8; NewDef.prevMenu = &MainDef; break; case shareware: // Episode 2 and 3 are handled, // branching to an ad screen. case registered: break; case retail: // We are fine. default: break; } // Versions of doom.exe before the Ultimate Doom release only had // three episodes; if we're emulating one of those then don't try // to show episode four. If we are, then do show episode four // (should crash if missing). if (gameversion < exe_ultimate) { EpiDef.numitems--; } //opldev = M_CheckParm("-opldev") > 0; } ================================================ FILE: fbdoom/m_menu.h ================================================ // // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // 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. // // 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); extern int detailLevel; extern int screenblocks; #endif ================================================ FILE: fbdoom/m_misc.c ================================================ // // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 1993-2008 Raven Software // Copyright(C) 2005-2014 Simon Howard // // 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. // // DESCRIPTION: // Miscellaneous. // #include #include #include #include #include #ifdef _WIN32 #define WIN32_LEAN_AND_MEAN #include #include #ifdef _MSC_VER #include #endif #else #include #include #endif #include "doomtype.h" #include "deh_str.h" #include "i_swap.h" #include "i_system.h" #include "i_video.h" #include "m_misc.h" #include "v_video.h" #include "w_wad.h" #include "z_zone.h" // // Create a directory // void M_MakeDirectory(char *path) { #ifdef _WIN32 mkdir(path); #else mkdir(path, 0755); #endif } // Check if a file exists boolean M_FileExists(char *filename) { FILE *fstream; fstream = fopen(filename, "r"); if (fstream != NULL) { fclose(fstream); return true; } else { // If we can't open because the file is a directory, the // "file" exists at least! return errno == EISDIR; } } // // Determine the length of an open file. // long M_FileLength(FILE *handle) { long savedpos; long length; // save the current position in the file savedpos = ftell(handle); // jump to the end and find the length fseek(handle, 0, SEEK_END); length = ftell(handle); // go back to the old location fseek(handle, savedpos, SEEK_SET); return length; } // // M_WriteFile // boolean M_WriteFile(char *name, void *source, int length) { FILE *handle; int count; handle = fopen(name, "wb"); if (handle == NULL) return false; count = fwrite(source, 1, length, handle); fclose(handle); if (count < length) return false; return true; } // // M_ReadFile // int M_ReadFile(char *name, byte **buffer) { FILE *handle; int count, length; byte *buf; handle = fopen(name, "rb"); if (handle == NULL) I_Error ("Couldn't read file %s", name); // find the size of the file by seeking to the end and // reading the current position length = M_FileLength(handle); buf = Z_Malloc (length, PU_STATIC, NULL); count = fread(buf, 1, length, handle); fclose (handle); if (count < length) I_Error ("Couldn't read file %s", name); *buffer = buf; return length; } // Returns the path to a temporary file of the given name, stored // inside the system temporary directory. // // The returned value must be freed with Z_Free after use. char *M_TempFile(char *s) { char *tempdir; #ifdef _WIN32 // Check the TEMP environment variable to find the location. tempdir = getenv("TEMP"); if (tempdir == NULL) { tempdir = "."; } #else // In Unix, just use /tmp. tempdir = "/tmp"; #endif return M_StringJoin(tempdir, DIR_SEPARATOR_S, s, NULL); } boolean M_StrToInt(const char *str, int *result) { return sscanf(str, " 0x%x", result) == 1 || sscanf(str, " 0X%x", result) == 1 || sscanf(str, " 0%o", result) == 1 || sscanf(str, " %d", result) == 1; } void M_ExtractFileBase(char *path, char *dest) { char *src; char *filename; int length; src = path + strlen(path) - 1; // back up until a \ or the start while (src != path && *(src - 1) != DIR_SEPARATOR) { src--; } filename = src; // Copy up to eight characters // Note: Vanilla Doom exits with an error if a filename is specified // with a base of more than eight characters. To remove the 8.3 // filename limit, instead we simply truncate the name. length = 0; memset(dest, 0, 8); while (*src != '\0' && *src != '.') { if (length >= 8) { printf("Warning: Truncated '%s' lump name to '%.8s'.\n", filename, dest); break; } dest[length++] = toupper((int)*src++); } } //--------------------------------------------------------------------------- // // PROC M_ForceUppercase // // Change string to uppercase. // //--------------------------------------------------------------------------- void M_ForceUppercase(char *text) { char *p; for (p = text; *p != '\0'; ++p) { *p = toupper(*p); } } // // M_StrCaseStr // // Case-insensitive version of strstr() // char *M_StrCaseStr(char *haystack, char *needle) { unsigned int haystack_len; unsigned int needle_len; unsigned int len; unsigned int i; haystack_len = strlen(haystack); needle_len = strlen(needle); if (haystack_len < needle_len) { return NULL; } len = haystack_len - needle_len; for (i = 0; i <= len; ++i) { if (!strncasecmp(haystack + i, needle, needle_len)) { return haystack + i; } } return NULL; } // // Safe version of strdup() that checks the string was successfully // allocated. // char *M_StringDuplicate(const char *orig) { char *result; result = strdup(orig); if (result == NULL) { I_Error("Failed to duplicate string (length %i)\n", strlen(orig)); } return result; } // // String replace function. // char *M_StringReplace(const char *haystack, const char *needle, const char *replacement) { char *result, *dst; const char *p; size_t needle_len = strlen(needle); size_t result_len, dst_len; // Iterate through occurrences of 'needle' and calculate the size of // the new string. result_len = strlen(haystack) + 1; p = haystack; for (;;) { p = strstr(p, needle); if (p == NULL) { break; } p += needle_len; result_len += strlen(replacement) - needle_len; } // Construct new string. result = malloc(result_len); if (result == NULL) { I_Error("M_StringReplace: Failed to allocate new string"); return NULL; } dst = result; dst_len = result_len; p = haystack; while (*p != '\0') { if (!strncmp(p, needle, needle_len)) { M_StringCopy(dst, replacement, dst_len); p += needle_len; dst += strlen(replacement); dst_len -= strlen(replacement); } else { *dst = *p; ++dst; --dst_len; ++p; } } *dst = '\0'; return result; } // Safe string copy function that works like OpenBSD's strlcpy(). // Returns true if the string was not truncated. boolean M_StringCopy(char *dest, const char *src, size_t dest_size) { size_t len; if (dest_size >= 1) { dest[dest_size - 1] = '\0'; strncpy(dest, src, dest_size - 1); } else { return false; } len = strlen(dest); return src[len] == '\0'; } // Safe string concat function that works like OpenBSD's strlcat(). // Returns true if string not truncated. boolean M_StringConcat(char *dest, const char *src, size_t dest_size) { size_t offset; offset = strlen(dest); if (offset > dest_size) { offset = dest_size; } return M_StringCopy(dest + offset, src, dest_size - offset); } // Returns true if 's' begins with the specified prefix. boolean M_StringStartsWith(const char *s, const char *prefix) { return strlen(s) > strlen(prefix) && strncmp(s, prefix, strlen(prefix)) == 0; } // Returns true if 's' ends with the specified suffix. boolean M_StringEndsWith(const char *s, const char *suffix) { return strlen(s) >= strlen(suffix) && strcmp(s + strlen(s) - strlen(suffix), suffix) == 0; } // Return a newly-malloced string with all the strings given as arguments // concatenated together. char *M_StringJoin(const char *s, ...) { char *result; const char *v; va_list args; size_t result_len; result_len = strlen(s) + 1; va_start(args, s); for (;;) { v = va_arg(args, const char *); if (v == NULL) { break; } result_len += strlen(v); } va_end(args); result = malloc(result_len); if (result == NULL) { I_Error("M_StringJoin: Failed to allocate new string."); return NULL; } M_StringCopy(result, s, result_len); va_start(args, s); for (;;) { v = va_arg(args, const char *); if (v == NULL) { break; } M_StringConcat(result, v, result_len); } va_end(args); return result; } // On Windows, vsnprintf() is _vsnprintf(). #ifdef _WIN32 #if _MSC_VER < 1400 /* not needed for Visual Studio 2008 */ #define vsnprintf _vsnprintf #endif #endif // Safe, portable vsnprintf(). int M_vsnprintf(char *buf, size_t buf_len, const char *s, va_list args) { int result; if (buf_len < 1) { return 0; } // Windows (and other OSes?) has a vsnprintf() that doesn't always // append a trailing \0. So we must do it, and write into a buffer // that is one byte shorter; otherwise this function is unsafe. result = vsnprintf(buf, buf_len, s, args); // If truncated, change the final char in the buffer to a \0. // A negative result indicates a truncated buffer on Windows. if (result < 0 || result >= buf_len) { buf[buf_len - 1] = '\0'; result = buf_len - 1; } return result; } // Safe, portable snprintf(). int M_snprintf(char *buf, size_t buf_len, const char *s, ...) { va_list args; int result; va_start(args, s); result = M_vsnprintf(buf, buf_len, s, args); va_end(args); return result; } #ifdef _WIN32 char *M_OEMToUTF8(const char *oem) { unsigned int len = strlen(oem) + 1; wchar_t *tmp; char *result; tmp = malloc(len * sizeof(wchar_t)); MultiByteToWideChar(CP_OEMCP, 0, oem, len, tmp, len); result = malloc(len * 4); WideCharToMultiByte(CP_UTF8, 0, tmp, len, result, len * 4, NULL, NULL); free(tmp); return result; } #endif ================================================ FILE: fbdoom/m_misc.h ================================================ // // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // 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. // // DESCRIPTION: // Miscellaneous. // #ifndef __M_MISC__ #define __M_MISC__ #include #include #include "doomtype.h" boolean M_WriteFile(char *name, void *source, int length); int M_ReadFile(char *name, byte **buffer); void M_MakeDirectory(char *dir); char *M_TempFile(char *s); boolean M_FileExists(char *file); long M_FileLength(FILE *handle); boolean M_StrToInt(const char *str, int *result); void M_ExtractFileBase(char *path, char *dest); void M_ForceUppercase(char *text); char *M_StrCaseStr(char *haystack, char *needle); char *M_StringDuplicate(const char *orig); boolean M_StringCopy(char *dest, const char *src, size_t dest_size); boolean M_StringConcat(char *dest, const char *src, size_t dest_size); char *M_StringReplace(const char *haystack, const char *needle, const char *replacement); char *M_StringJoin(const char *s, ...); boolean M_StringStartsWith(const char *s, const char *prefix); boolean M_StringEndsWith(const char *s, const char *suffix); int M_vsnprintf(char *buf, size_t buf_len, const char *s, va_list args); int M_snprintf(char *buf, size_t buf_len, const char *s, ...); char *M_OEMToUTF8(const char *ansi); #endif ================================================ FILE: fbdoom/m_random.c ================================================ // // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // 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. // // DESCRIPTION: // Random number LUT. // // // M_Random // Returns a 0-255 number // static const unsigned char rndtable[256] = { 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 }; int rndindex = 0; int prndindex = 0; // Which one is deterministic? int P_Random (void) { prndindex = (prndindex+1)&0xff; return rndtable[prndindex]; } int M_Random (void) { rndindex = (rndindex+1)&0xff; return rndtable[rndindex]; } void M_ClearRandom (void) { rndindex = prndindex = 0; } ================================================ FILE: fbdoom/m_random.h ================================================ // // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // 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. // // DESCRIPTION: // // #ifndef __M_RANDOM__ #define __M_RANDOM__ #include "doomtype.h" // Returns a number from 0 to 255, // from a lookup table. int M_Random (void); // As M_Random, but used only by the play simulation. int P_Random (void); // Fix randoms for demos. void M_ClearRandom (void); #endif ================================================ FILE: fbdoom/memio.c ================================================ // // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // 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. // // Emulates the IO functions in C stdio.h reading and writing to // memory. // #include #include #include #include "memio.h" #include "z_zone.h" typedef enum { MODE_READ, MODE_WRITE, } memfile_mode_t; struct _MEMFILE { unsigned char *buf; size_t buflen; size_t alloced; unsigned int position; memfile_mode_t mode; }; // Open a memory area for reading MEMFILE *mem_fopen_read(void *buf, size_t buflen) { MEMFILE *file; file = Z_Malloc(sizeof(MEMFILE), PU_STATIC, 0); file->buf = (unsigned char *) buf; file->buflen = buflen; file->position = 0; file->mode = MODE_READ; return file; } // Read bytes size_t mem_fread(void *buf, size_t size, size_t nmemb, MEMFILE *stream) { size_t items; if (stream->mode != MODE_READ) { printf("not a read stream\n"); return -1; } // Trying to read more bytes than we have left? items = nmemb; if (items * size > stream->buflen - stream->position) { items = (stream->buflen - stream->position) / size; } // Copy bytes to buffer memcpy(buf, stream->buf + stream->position, items * size); // Update position stream->position += items * size; return items; } // Open a memory area for writing MEMFILE *mem_fopen_write(void) { MEMFILE *file; file = Z_Malloc(sizeof(MEMFILE), PU_STATIC, 0); file->alloced = 1024; file->buf = Z_Malloc(file->alloced, PU_STATIC, 0); file->buflen = 0; file->position = 0; file->mode = MODE_WRITE; return file; } // Write bytes to stream size_t mem_fwrite(const void *ptr, size_t size, size_t nmemb, MEMFILE *stream) { size_t bytes; if (stream->mode != MODE_WRITE) { return -1; } // More bytes than can fit in the buffer? // If so, reallocate bigger. bytes = size * nmemb; while (bytes > stream->alloced - stream->position) { unsigned char *newbuf; newbuf = Z_Malloc(stream->alloced * 2, PU_STATIC, 0); memcpy(newbuf, stream->buf, stream->alloced); Z_Free(stream->buf); stream->buf = newbuf; stream->alloced *= 2; } // Copy into buffer memcpy(stream->buf + stream->position, ptr, bytes); stream->position += bytes; if (stream->position > stream->buflen) stream->buflen = stream->position; return nmemb; } void mem_get_buf(MEMFILE *stream, void **buf, size_t *buflen) { *buf = stream->buf; *buflen = stream->buflen; } void mem_fclose(MEMFILE *stream) { if (stream->mode == MODE_WRITE) { Z_Free(stream->buf); } Z_Free(stream); } long mem_ftell(MEMFILE *stream) { return stream->position; } int mem_fseek(MEMFILE *stream, signed long position, mem_rel_t whence) { unsigned int newpos; switch (whence) { case MEM_SEEK_SET: newpos = (int) position; break; case MEM_SEEK_CUR: newpos = (int) (stream->position + position); break; case MEM_SEEK_END: newpos = (int) (stream->buflen + position); break; default: return -1; } if (newpos < stream->buflen) { stream->position = newpos; return 0; } else { printf("Error seeking to %i\n", newpos); return -1; } } ================================================ FILE: fbdoom/memio.h ================================================ // // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // 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. // #ifndef MEMIO_H #define MEMIO_H typedef struct _MEMFILE MEMFILE; typedef enum { MEM_SEEK_SET, MEM_SEEK_CUR, MEM_SEEK_END, } mem_rel_t; MEMFILE *mem_fopen_read(void *buf, size_t buflen); size_t mem_fread(void *buf, size_t size, size_t nmemb, MEMFILE *stream); MEMFILE *mem_fopen_write(void); size_t mem_fwrite(const void *ptr, size_t size, size_t nmemb, MEMFILE *stream); void mem_get_buf(MEMFILE *stream, void **buf, size_t *buflen); void mem_fclose(MEMFILE *stream); long mem_ftell(MEMFILE *stream); int mem_fseek(MEMFILE *stream, signed long offset, mem_rel_t whence); #endif /* #ifndef MEMIO_H */ ================================================ FILE: fbdoom/net_client.h ================================================ // // Copyright(C) 2005-2014 Simon Howard // // 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. // // Network client code // #ifndef NET_CLIENT_H #define NET_CLIENT_H #include "doomtype.h" #include "d_ticcmd.h" #include "sha1.h" #include "net_defs.h" boolean NET_CL_Connect(net_addr_t *addr, net_connect_data_t *data); void NET_CL_Disconnect(void); void NET_CL_Run(void); void NET_CL_Init(void); void NET_CL_LaunchGame(void); void NET_CL_StartGame(net_gamesettings_t *settings); void NET_CL_SendTiccmd(ticcmd_t *ticcmd, int maketic); boolean NET_CL_GetSettings(net_gamesettings_t *_settings); void NET_Init(void); void NET_BindVariables(void); extern boolean net_client_connected; extern boolean net_client_received_wait_data; extern net_waitdata_t net_client_wait_data; extern boolean net_waiting_for_launch; extern char *net_player_name; extern sha1_digest_t net_server_wad_sha1sum; extern sha1_digest_t net_server_deh_sha1sum; extern unsigned int net_server_is_freedoom; extern sha1_digest_t net_local_wad_sha1sum; extern sha1_digest_t net_local_deh_sha1sum; extern unsigned int net_local_is_freedoom; extern boolean drone; #endif /* #ifndef NET_CLIENT_H */ ================================================ FILE: fbdoom/net_dedicated.h ================================================ // // Copyright(C) 2005-2014 Simon Howard // // 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. // // // Dedicated server code. // #ifndef NET_DEDICATED_H #define NET_DEDICATED_H void NET_DedicatedServer(void); #endif /* #ifndef NET_DEDICATED_H */ ================================================ FILE: fbdoom/net_defs.h ================================================ // // Copyright(C) 2005-2014 Simon Howard // // 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. // // DESCRIPTION: // Definitions for use in networking code. // #ifndef NET_DEFS_H #define NET_DEFS_H #include #include "doomtype.h" #include "d_ticcmd.h" #include "sha1.h" // Absolute maximum number of "nodes" in the game. This is different to // NET_MAXPLAYERS, as there may be observers that are not participating // (eg. left/right monitors) #define MAXNETNODES 16 // The maximum number of players, multiplayer/networking. // This is the maximum supported by the networking code; individual games // have their own values for MAXPLAYERS that can be smaller. #define NET_MAXPLAYERS 8 // Maximum length of a player's name. #define MAXPLAYERNAME 30 // Networking and tick handling related. #define BACKUPTICS 128 typedef struct _net_module_s net_module_t; typedef struct _net_packet_s net_packet_t; typedef struct _net_addr_s net_addr_t; typedef struct _net_context_s net_context_t; struct _net_packet_s { byte *data; size_t len; size_t alloced; unsigned int pos; }; struct _net_module_s { // Initialize this module for use as a client boolean (*InitClient)(void); // Initialize this module for use as a server boolean (*InitServer)(void); // Send a packet void (*SendPacket)(net_addr_t *addr, net_packet_t *packet); // Check for new packets to receive // // Returns true if packet received boolean (*RecvPacket)(net_addr_t **addr, net_packet_t **packet); // Converts an address to a string void (*AddrToString)(net_addr_t *addr, char *buffer, int buffer_len); // Free back an address when no longer in use void (*FreeAddress)(net_addr_t *addr); // Try to resolve a name to an address net_addr_t *(*ResolveAddress)(char *addr); }; // net_addr_t struct _net_addr_s { net_module_t *module; void *handle; }; // magic number sent when connecting to check this is a valid client #define NET_MAGIC_NUMBER 3436803284U // header field value indicating that the packet is a reliable packet #define NET_RELIABLE_PACKET (1 << 15) // packet types typedef enum { NET_PACKET_TYPE_SYN, NET_PACKET_TYPE_ACK, NET_PACKET_TYPE_REJECTED, NET_PACKET_TYPE_KEEPALIVE, NET_PACKET_TYPE_WAITING_DATA, NET_PACKET_TYPE_GAMESTART, NET_PACKET_TYPE_GAMEDATA, NET_PACKET_TYPE_GAMEDATA_ACK, NET_PACKET_TYPE_DISCONNECT, NET_PACKET_TYPE_DISCONNECT_ACK, NET_PACKET_TYPE_RELIABLE_ACK, NET_PACKET_TYPE_GAMEDATA_RESEND, NET_PACKET_TYPE_CONSOLE_MESSAGE, NET_PACKET_TYPE_QUERY, NET_PACKET_TYPE_QUERY_RESPONSE, NET_PACKET_TYPE_LAUNCH, } net_packet_type_t; typedef enum { NET_MASTER_PACKET_TYPE_ADD, NET_MASTER_PACKET_TYPE_ADD_RESPONSE, NET_MASTER_PACKET_TYPE_QUERY, NET_MASTER_PACKET_TYPE_QUERY_RESPONSE, NET_MASTER_PACKET_TYPE_GET_METADATA, NET_MASTER_PACKET_TYPE_GET_METADATA_RESPONSE, NET_MASTER_PACKET_TYPE_SIGN_START, NET_MASTER_PACKET_TYPE_SIGN_START_RESPONSE, NET_MASTER_PACKET_TYPE_SIGN_END, NET_MASTER_PACKET_TYPE_SIGN_END_RESPONSE, } net_master_packet_type_t; // Settings specified when the client connects to the server. typedef struct { int gamemode; int gamemission; int lowres_turn; int drone; int max_players; int is_freedoom; sha1_digest_t wad_sha1sum; sha1_digest_t deh_sha1sum; int player_class; } net_connect_data_t; // Game settings sent by client to server when initiating game start, // and received from the server by clients when the game starts. typedef struct { int ticdup; int extratics; int deathmatch; int episode; int nomonsters; int fast_monsters; int respawn_monsters; int map; int skill; int gameversion; int lowres_turn; int new_sync; int timelimit; int loadgame; int random; // [Strife only] // These fields are only used by the server when sending a game // start message: int num_players; int consoleplayer; // Hexen player classes: int player_classes[NET_MAXPLAYERS]; } net_gamesettings_t; #define NET_TICDIFF_FORWARD (1 << 0) #define NET_TICDIFF_SIDE (1 << 1) #define NET_TICDIFF_TURN (1 << 2) #define NET_TICDIFF_BUTTONS (1 << 3) #define NET_TICDIFF_CONSISTANCY (1 << 4) #define NET_TICDIFF_CHATCHAR (1 << 5) #define NET_TICDIFF_RAVEN (1 << 6) #define NET_TICDIFF_STRIFE (1 << 7) typedef struct { unsigned int diff; ticcmd_t cmd; } net_ticdiff_t; // Complete set of ticcmds from all players typedef struct { signed int latency; unsigned int seq; boolean playeringame[NET_MAXPLAYERS]; net_ticdiff_t cmds[NET_MAXPLAYERS]; } net_full_ticcmd_t; // Data sent in response to server queries typedef struct { char *version; int server_state; int num_players; int max_players; int gamemode; int gamemission; char *description; } net_querydata_t; // Data sent by the server while waiting for the game to start. typedef struct { int num_players; int num_drones; int ready_players; int max_players; int is_controller; int consoleplayer; char player_names[NET_MAXPLAYERS][MAXPLAYERNAME]; char player_addrs[NET_MAXPLAYERS][MAXPLAYERNAME]; sha1_digest_t wad_sha1sum; sha1_digest_t deh_sha1sum; int is_freedoom; } net_waitdata_t; #endif /* #ifndef NET_DEFS_H */ ================================================ FILE: fbdoom/net_gui.h ================================================ // // Copyright(C) 2005-2014 Simon Howard // // 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. // // Graphical stuff related to the networking code: // // * The client waiting screen when we are waiting for the server to // start the game. // #ifndef NET_GUI_H #define NET_GUI_H #include "doomtype.h" extern void NET_WaitForLaunch(void); #endif /* #ifndef NET_GUI_H */ ================================================ FILE: fbdoom/net_io.h ================================================ // // Copyright(C) 2005-2014 Simon Howard // // 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. // // DESCRIPTION: // Network packet manipulation (net_packet_t) // #ifndef NET_IO_H #define NET_IO_H #include "net_defs.h" extern net_addr_t net_broadcast_addr; net_context_t *NET_NewContext(void); void NET_AddModule(net_context_t *context, net_module_t *module); void NET_SendPacket(net_addr_t *addr, net_packet_t *packet); void NET_SendBroadcast(net_context_t *context, net_packet_t *packet); boolean NET_RecvPacket(net_context_t *context, net_addr_t **addr, net_packet_t **packet); char *NET_AddrToString(net_addr_t *addr); void NET_FreeAddress(net_addr_t *addr); net_addr_t *NET_ResolveAddress(net_context_t *context, char *address); #endif /* #ifndef NET_IO_H */ ================================================ FILE: fbdoom/net_loop.h ================================================ // // Copyright(C) 2005-2014 Simon Howard // // 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. // // DESCRIPTION: // Loopback network module for server compiled into the client // #ifndef NET_LOOP_H #define NET_LOOP_H #include "net_defs.h" extern net_module_t net_loop_client_module; extern net_module_t net_loop_server_module; #endif /* #ifndef NET_LOOP_H */ ================================================ FILE: fbdoom/net_packet.h ================================================ // // Copyright(C) 2005-2014 Simon Howard // // 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. // // DESCRIPTION: // Definitions for use in networking code. // #ifndef NET_PACKET_H #define NET_PACKET_H #include "net_defs.h" net_packet_t *NET_NewPacket(int initial_size); net_packet_t *NET_PacketDup(net_packet_t *packet); void NET_FreePacket(net_packet_t *packet); boolean NET_ReadInt8(net_packet_t *packet, unsigned int *data); boolean NET_ReadInt16(net_packet_t *packet, unsigned int *data); boolean NET_ReadInt32(net_packet_t *packet, unsigned int *data); boolean NET_ReadSInt8(net_packet_t *packet, signed int *data); boolean NET_ReadSInt16(net_packet_t *packet, signed int *data); boolean NET_ReadSInt32(net_packet_t *packet, signed int *data); char *NET_ReadString(net_packet_t *packet); void NET_WriteInt8(net_packet_t *packet, unsigned int i); void NET_WriteInt16(net_packet_t *packet, unsigned int i); void NET_WriteInt32(net_packet_t *packet, unsigned int i); void NET_WriteString(net_packet_t *packet, char *string); #endif /* #ifndef NET_PACKET_H */ ================================================ FILE: fbdoom/net_query.h ================================================ // // Copyright(C) 2005-2014 Simon Howard // // 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. // // DESCRIPTION: // Querying servers to find their current status. // #ifndef NET_QUERY_H #define NET_QUERY_H #include "net_defs.h" typedef void (*net_query_callback_t)(net_addr_t *addr, net_querydata_t *querydata, unsigned int ping_time, void *user_data); extern int NET_StartLANQuery(void); extern int NET_StartMasterQuery(void); extern void NET_LANQuery(void); extern void NET_MasterQuery(void); extern void NET_QueryAddress(char *addr); extern net_addr_t *NET_FindLANServer(void); extern int NET_Query_Poll(net_query_callback_t callback, void *user_data); extern net_addr_t *NET_Query_ResolveMaster(net_context_t *context); extern void NET_Query_AddToMaster(net_addr_t *master_addr); extern boolean NET_Query_CheckAddedToMaster(boolean *result); extern void NET_Query_MasterResponse(net_packet_t *packet); #endif /* #ifndef NET_QUERY_H */ ================================================ FILE: fbdoom/net_sdl.h ================================================ // // Copyright(C) 2005-2014 Simon Howard // // 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. // // DESCRIPTION: // Networking module which uses SDL_net // #ifndef NET_SDL_H #define NET_SDL_H #include "net_defs.h" extern net_module_t net_sdl_module; #endif /* #ifndef NET_SDL_H */ ================================================ FILE: fbdoom/net_server.h ================================================ // // Copyright(C) 2005-2014 Simon Howard // // 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. // // Network server code // #ifndef NET_SERVER_H #define NET_SERVER_H // initialize server and wait for connections void NET_SV_Init(void); // run server: check for new packets received etc. void NET_SV_Run(void); // Shut down the server // Blocks until all clients disconnect, or until a 5 second timeout void NET_SV_Shutdown(void); // Add a network module to the context used by the server void NET_SV_AddModule(net_module_t *module); // Register server with master server. void NET_SV_RegisterWithMaster(void); #endif /* #ifndef NET_SERVER_H */ ================================================ FILE: fbdoom/p_ceilng.c ================================================ // // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // 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. // // DESCRIPTION: Ceiling aninmation (lowering, crushing, raising) // #include "z_zone.h" #include "doomdef.h" #include "p_local.h" #include "s_sound.h" // State. #include "doomstat.h" #include "r_state.h" // Data. #include "sounds.h" // // CEILINGS // ceiling_t* activeceilings[MAXCEILINGS]; // // T_MoveCeiling // void T_MoveCeiling (ceiling_t* ceiling) { result_e res; switch(ceiling->direction) { case 0: // IN STASIS break; case 1: // UP res = T_MovePlane(ceiling->sector, ceiling->speed, ceiling->topheight, false,1,ceiling->direction); if (!(leveltime&7)) { switch(ceiling->type) { case silentCrushAndRaise: break; default: S_StartSound(&ceiling->sector->soundorg, sfx_stnmov); // ? break; } } if (res == pastdest) { switch(ceiling->type) { case raiseToHighest: P_RemoveActiveCeiling(ceiling); break; case silentCrushAndRaise: S_StartSound(&ceiling->sector->soundorg, sfx_pstop); case fastCrushAndRaise: case crushAndRaise: ceiling->direction = -1; break; default: break; } } break; case -1: // DOWN res = T_MovePlane(ceiling->sector, ceiling->speed, ceiling->bottomheight, ceiling->crush,1,ceiling->direction); if (!(leveltime&7)) { switch(ceiling->type) { case silentCrushAndRaise: break; default: S_StartSound(&ceiling->sector->soundorg, sfx_stnmov); } } if (res == pastdest) { switch(ceiling->type) { case silentCrushAndRaise: S_StartSound(&ceiling->sector->soundorg, sfx_pstop); case crushAndRaise: ceiling->speed = CEILSPEED; case fastCrushAndRaise: ceiling->direction = 1; break; case lowerAndCrush: case lowerToFloor: P_RemoveActiveCeiling(ceiling); break; default: break; } } else // ( res != pastdest ) { if (res == crushed) { switch(ceiling->type) { case silentCrushAndRaise: case crushAndRaise: case lowerAndCrush: ceiling->speed = CEILSPEED / 8; break; default: break; } } } break; } } // // EV_DoCeiling // Move a ceiling up/down and all around! // 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. switch(type) { case fastCrushAndRaise: case silentCrushAndRaise: case crushAndRaise: P_ActivateInStasisCeiling(line); default: break; } while ((secnum = P_FindSectorFromLineTag(line,secnum)) >= 0) { sec = §ors[secnum]; if (sec->specialdata) continue; // new door thinker rtn = 1; ceiling = Z_Malloc (sizeof(*ceiling), PU_LEVSPEC, 0); P_AddThinker (&ceiling->thinker); sec->specialdata = ceiling; ceiling->thinker.function.acp1 = (actionf_p1)T_MoveCeiling; ceiling->sector = sec; ceiling->crush = false; 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; } ceiling->tag = sec->tag; ceiling->type = type; P_AddActiveCeiling(ceiling); } return rtn; } // // Add an active ceiling // void P_AddActiveCeiling(ceiling_t* c) { int i; for (i = 0; i < MAXCEILINGS;i++) { if (activeceilings[i] == NULL) { activeceilings[i] = c; return; } } } // // Remove a ceiling's thinker // void P_RemoveActiveCeiling(ceiling_t* c) { int i; for (i = 0;i < MAXCEILINGS;i++) { if (activeceilings[i] == c) { activeceilings[i]->sector->specialdata = NULL; P_RemoveThinker (&activeceilings[i]->thinker); activeceilings[i] = NULL; break; } } } // // Restart a ceiling that's in-stasis // void P_ActivateInStasisCeiling(line_t* line) { int i; for (i = 0;i < MAXCEILINGS;i++) { if (activeceilings[i] && (activeceilings[i]->tag == line->tag) && (activeceilings[i]->direction == 0)) { activeceilings[i]->direction = activeceilings[i]->olddirection; activeceilings[i]->thinker.function.acp1 = (actionf_p1)T_MoveCeiling; } } } // // EV_CeilingCrushStop // Stop a ceiling from crushing! // int EV_CeilingCrushStop(line_t *line) { int i; int rtn; rtn = 0; for (i = 0;i < MAXCEILINGS;i++) { if (activeceilings[i] && (activeceilings[i]->tag == line->tag) && (activeceilings[i]->direction != 0)) { activeceilings[i]->olddirection = activeceilings[i]->direction; activeceilings[i]->thinker.function.acv = (actionf_v)NULL; activeceilings[i]->direction = 0; // in-stasis rtn = 1; } } return rtn; } ================================================ FILE: fbdoom/p_doors.c ================================================ // // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // 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. // // DESCRIPTION: Door animation code (opening/closing) // #include "z_zone.h" #include "doomdef.h" #include "deh_main.h" #include "p_local.h" #include "s_sound.h" // State. #include "doomstat.h" #include "r_state.h" // Data. #include "dstrings.h" #include "sounds.h" #if 0 // // Sliding door frame information // slidename_t slideFrameNames[MAXSLIDEDOORS] = { {"GDOORF1","GDOORF2","GDOORF3","GDOORF4", // front "GDOORB1","GDOORB2","GDOORB3","GDOORB4"}, // back {"\0","\0","\0","\0"} }; #endif // // VERTICAL DOORS // // // T_VerticalDoor // void T_VerticalDoor (vldoor_t* door) { result_e res; switch(door->direction) { case 0: // WAITING if (!--door->topcountdown) { switch(door->type) { case vld_blazeRaise: door->direction = -1; // time to go back down S_StartSound(&door->sector->soundorg, sfx_bdcls); break; case vld_normal: door->direction = -1; // time to go back down S_StartSound(&door->sector->soundorg, sfx_dorcls); break; case vld_close30ThenOpen: door->direction = 1; S_StartSound(&door->sector->soundorg, sfx_doropn); break; default: break; } } break; case 2: // INITIAL WAIT if (!--door->topcountdown) { switch(door->type) { case vld_raiseIn5Mins: door->direction = 1; door->type = vld_normal; S_StartSound(&door->sector->soundorg, sfx_doropn); break; default: break; } } break; case -1: // DOWN res = T_MovePlane(door->sector, door->speed, door->sector->floorheight, false,1,door->direction); if (res == pastdest) { switch(door->type) { case vld_blazeRaise: case vld_blazeClose: door->sector->specialdata = NULL; P_RemoveThinker (&door->thinker); // unlink and free S_StartSound(&door->sector->soundorg, sfx_bdcls); break; case vld_normal: case vld_close: door->sector->specialdata = NULL; P_RemoveThinker (&door->thinker); // unlink and free break; case vld_close30ThenOpen: door->direction = 0; door->topcountdown = TICRATE*30; break; default: break; } } else if (res == crushed) { switch(door->type) { case vld_blazeClose: case vld_close: // DO NOT GO BACK UP! break; default: door->direction = 1; S_StartSound(&door->sector->soundorg, sfx_doropn); break; } } break; case 1: // UP res = T_MovePlane(door->sector, door->speed, door->topheight, false,1,door->direction); if (res == pastdest) { switch(door->type) { case vld_blazeRaise: case vld_normal: door->direction = 0; // wait at top door->topcountdown = door->topwait; break; case vld_close30ThenOpen: case vld_blazeOpen: case vld_open: door->sector->specialdata = NULL; P_RemoveThinker (&door->thinker); // unlink and free break; default: break; } } break; } } // // EV_DoLockedDoor // Move a locked door up/down // int EV_DoLockedDoor ( line_t* line, vldoor_e type, mobj_t* thing ) { player_t* p; p = thing->player; if (!p) return 0; switch(line->special) { case 99: // Blue Lock case 133: if ( !p ) return 0; if (!p->cards[it_bluecard] && !p->cards[it_blueskull]) { p->message = DEH_String(PD_BLUEO); S_StartSound(NULL,sfx_oof); return 0; } break; case 134: // Red Lock case 135: if ( !p ) return 0; if (!p->cards[it_redcard] && !p->cards[it_redskull]) { p->message = DEH_String(PD_REDO); S_StartSound(NULL,sfx_oof); return 0; } break; case 136: // Yellow Lock case 137: if ( !p ) return 0; if (!p->cards[it_yellowcard] && !p->cards[it_yellowskull]) { p->message = DEH_String(PD_YELLOWO); S_StartSound(NULL,sfx_oof); return 0; } break; } return EV_DoDoor(line,type); } int EV_DoDoor ( line_t* line, vldoor_e type ) { int secnum,rtn; sector_t* sec; vldoor_t* door; secnum = -1; rtn = 0; while ((secnum = P_FindSectorFromLineTag(line,secnum)) >= 0) { sec = §ors[secnum]; if (sec->specialdata) continue; // new door thinker rtn = 1; door = Z_Malloc (sizeof(*door), PU_LEVSPEC, 0); P_AddThinker (&door->thinker); sec->specialdata = door; door->thinker.function.acp1 = (actionf_p1) T_VerticalDoor; door->sector = sec; door->type = type; door->topwait = VDOORWAIT; door->speed = VDOORSPEED; switch(type) { case vld_blazeClose: door->topheight = P_FindLowestCeilingSurrounding(sec); door->topheight -= 4*FRACUNIT; door->direction = -1; door->speed = VDOORSPEED * 4; S_StartSound(&door->sector->soundorg, sfx_bdcls); break; case vld_close: door->topheight = P_FindLowestCeilingSurrounding(sec); door->topheight -= 4*FRACUNIT; door->direction = -1; S_StartSound(&door->sector->soundorg, sfx_dorcls); break; case vld_close30ThenOpen: door->topheight = sec->ceilingheight; door->direction = -1; S_StartSound(&door->sector->soundorg, sfx_dorcls); break; case vld_blazeRaise: case vld_blazeOpen: door->direction = 1; door->topheight = P_FindLowestCeilingSurrounding(sec); door->topheight -= 4*FRACUNIT; door->speed = VDOORSPEED * 4; if (door->topheight != sec->ceilingheight) S_StartSound(&door->sector->soundorg, sfx_bdopn); break; case vld_normal: case vld_open: door->direction = 1; door->topheight = P_FindLowestCeilingSurrounding(sec); door->topheight -= 4*FRACUNIT; if (door->topheight != sec->ceilingheight) S_StartSound(&door->sector->soundorg, sfx_doropn); break; default: break; } } return rtn; } // // EV_VerticalDoor : open a door manually, no tag value // void EV_VerticalDoor ( line_t* line, mobj_t* thing ) { player_t* player; sector_t* sec; vldoor_t* door; int side; side = 0; // only front sides can be used // Check for locks player = thing->player; switch(line->special) { case 26: // Blue Lock case 32: if ( !player ) return; if (!player->cards[it_bluecard] && !player->cards[it_blueskull]) { player->message = DEH_String(PD_BLUEK); S_StartSound(NULL,sfx_oof); return; } break; case 27: // Yellow Lock case 34: if ( !player ) return; if (!player->cards[it_yellowcard] && !player->cards[it_yellowskull]) { player->message = DEH_String(PD_YELLOWK); S_StartSound(NULL,sfx_oof); return; } break; case 28: // Red Lock case 33: if ( !player ) return; if (!player->cards[it_redcard] && !player->cards[it_redskull]) { player->message = DEH_String(PD_REDK); S_StartSound(NULL,sfx_oof); return; } break; } // if the sector has an active thinker, use it sec = sides[ line->sidenum[side^1]] .sector; if (sec->specialdata) { door = sec->specialdata; switch(line->special) { case 1: // ONLY FOR "RAISE" DOORS, NOT "OPEN"s case 26: case 27: case 28: case 117: if (door->direction == -1) door->direction = 1; // go back up else { if (!thing->player) return; // JDC: bad guys never close doors // When is a door not a door? // In Vanilla, door->direction is set, even though // "specialdata" might not actually point at a door. if (door->thinker.function.acp1 == (actionf_p1) T_VerticalDoor) { door->direction = -1; // start going down immediately } else if (door->thinker.function.acp1 == (actionf_p1) T_PlatRaise) { // Erm, this is a plat, not a door. // This notably causes a problem in ep1-0500.lmp where // a plat and a door are cross-referenced; the door // doesn't open on 64-bit. // The direction field in vldoor_t corresponds to the wait // field in plat_t. Let's set that to -1 instead. plat_t *plat; plat = (plat_t *) door; plat->wait = -1; } else { // This isn't a door OR a plat. Now we're in trouble. fprintf(stderr, "EV_VerticalDoor: Tried to close " "something that wasn't a door.\n"); // Try closing it anyway. At least it will work on 32-bit // machines. door->direction = -1; } } return; } } // for proper sound switch(line->special) { case 117: // BLAZING DOOR RAISE case 118: // BLAZING DOOR OPEN S_StartSound(&sec->soundorg,sfx_bdopn); break; case 1: // NORMAL DOOR SOUND case 31: S_StartSound(&sec->soundorg,sfx_doropn); break; default: // LOCKED DOOR SOUND S_StartSound(&sec->soundorg,sfx_doropn); break; } // new door thinker door = Z_Malloc (sizeof(*door), PU_LEVSPEC, 0); P_AddThinker (&door->thinker); sec->specialdata = door; door->thinker.function.acp1 = (actionf_p1) T_VerticalDoor; door->sector = sec; door->direction = 1; door->speed = VDOORSPEED; door->topwait = VDOORWAIT; switch(line->special) { case 1: case 26: case 27: case 28: door->type = vld_normal; break; case 31: case 32: case 33: case 34: door->type = vld_open; line->special = 0; break; case 117: // blazing door raise door->type = vld_blazeRaise; door->speed = VDOORSPEED*4; break; case 118: // blazing door open door->type = vld_blazeOpen; line->special = 0; door->speed = VDOORSPEED*4; break; } // find the top and bottom of the movement range door->topheight = P_FindLowestCeilingSurrounding(sec); door->topheight -= 4*FRACUNIT; } // // Spawn a door that closes after 30 seconds // void P_SpawnDoorCloseIn30 (sector_t* sec) { vldoor_t* door; door = Z_Malloc ( sizeof(*door), PU_LEVSPEC, 0); P_AddThinker (&door->thinker); sec->specialdata = door; sec->special = 0; door->thinker.function.acp1 = (actionf_p1)T_VerticalDoor; door->sector = sec; door->direction = 0; door->type = vld_normal; door->speed = VDOORSPEED; door->topcountdown = 30 * TICRATE; } // // Spawn a door that opens after 5 minutes // void P_SpawnDoorRaiseIn5Mins ( sector_t* sec, int secnum ) { vldoor_t* door; door = Z_Malloc ( sizeof(*door), PU_LEVSPEC, 0); P_AddThinker (&door->thinker); sec->specialdata = door; sec->special = 0; door->thinker.function.acp1 = (actionf_p1)T_VerticalDoor; door->sector = sec; door->direction = 2; door->type = vld_raiseIn5Mins; door->speed = VDOORSPEED; door->topheight = P_FindLowestCeilingSurrounding(sec); door->topheight -= 4*FRACUNIT; door->topwait = VDOORWAIT; door->topcountdown = 5 * 60 * TICRATE; } // UNUSED // Separate into p_slidoor.c? #if 0 // ABANDONED TO THE MISTS OF TIME!!! // // EV_SlidingDoor : slide a door horizontally // (animate midtexture, then set noblocking line) // slideframe_t slideFrames[MAXSLIDEDOORS]; void P_InitSlidingDoorFrames(void) { int i; int f1; int f2; int f3; int f4; // DOOM II ONLY... if ( gamemode != commercial) return; for (i = 0;i < MAXSLIDEDOORS; i++) { if (!slideFrameNames[i].frontFrame1[0]) break; f1 = R_TextureNumForName(slideFrameNames[i].frontFrame1); f2 = R_TextureNumForName(slideFrameNames[i].frontFrame2); f3 = R_TextureNumForName(slideFrameNames[i].frontFrame3); f4 = R_TextureNumForName(slideFrameNames[i].frontFrame4); slideFrames[i].frontFrames[0] = f1; slideFrames[i].frontFrames[1] = f2; slideFrames[i].frontFrames[2] = f3; slideFrames[i].frontFrames[3] = f4; f1 = R_TextureNumForName(slideFrameNames[i].backFrame1); f2 = R_TextureNumForName(slideFrameNames[i].backFrame2); f3 = R_TextureNumForName(slideFrameNames[i].backFrame3); f4 = R_TextureNumForName(slideFrameNames[i].backFrame4); slideFrames[i].backFrames[0] = f1; slideFrames[i].backFrames[1] = f2; slideFrames[i].backFrames[2] = f3; slideFrames[i].backFrames[3] = f4; } } // // Return index into "slideFrames" array // for which door type to use // int P_FindSlidingDoorType(line_t* line) { int i; int val; for (i = 0;i < MAXSLIDEDOORS;i++) { val = sides[line->sidenum[0]].midtexture; if (val == slideFrames[i].frontFrames[0]) return i; } return -1; } void T_SlidingDoor (slidedoor_t* door) { switch(door->status) { case sd_opening: if (!door->timer--) { if (++door->frame == SNUMFRAMES) { // IF DOOR IS DONE OPENING... sides[door->line->sidenum[0]].midtexture = 0; sides[door->line->sidenum[1]].midtexture = 0; door->line->flags &= ML_BLOCKING^0xff; if (door->type == sdt_openOnly) { door->frontsector->specialdata = NULL; P_RemoveThinker (&door->thinker); break; } door->timer = SDOORWAIT; door->status = sd_waiting; } else { // IF DOOR NEEDS TO ANIMATE TO NEXT FRAME... door->timer = SWAITTICS; sides[door->line->sidenum[0]].midtexture = slideFrames[door->whichDoorIndex]. frontFrames[door->frame]; sides[door->line->sidenum[1]].midtexture = slideFrames[door->whichDoorIndex]. backFrames[door->frame]; } } break; case sd_waiting: // IF DOOR IS DONE WAITING... if (!door->timer--) { // CAN DOOR CLOSE? if (door->frontsector->thinglist != NULL || door->backsector->thinglist != NULL) { door->timer = SDOORWAIT; break; } //door->frame = SNUMFRAMES-1; door->status = sd_closing; door->timer = SWAITTICS; } break; case sd_closing: if (!door->timer--) { if (--door->frame < 0) { // IF DOOR IS DONE CLOSING... door->line->flags |= ML_BLOCKING; door->frontsector->specialdata = NULL; P_RemoveThinker (&door->thinker); break; } else { // IF DOOR NEEDS TO ANIMATE TO NEXT FRAME... door->timer = SWAITTICS; sides[door->line->sidenum[0]].midtexture = slideFrames[door->whichDoorIndex]. frontFrames[door->frame]; sides[door->line->sidenum[1]].midtexture = slideFrames[door->whichDoorIndex]. backFrames[door->frame]; } } break; } } void EV_SlidingDoor ( line_t* line, mobj_t* thing ) { sector_t* sec; slidedoor_t* door; // DOOM II ONLY... if (gamemode != commercial) return; // Make sure door isn't already being animated sec = line->frontsector; door = NULL; if (sec->specialdata) { if (!thing->player) return; door = sec->specialdata; if (door->type == sdt_openAndClose) { if (door->status == sd_waiting) door->status = sd_closing; } else return; } // Init sliding door vars if (!door) { door = Z_Malloc (sizeof(*door), PU_LEVSPEC, 0); P_AddThinker (&door->thinker); sec->specialdata = door; door->type = sdt_openAndClose; door->status = sd_opening; door->whichDoorIndex = P_FindSlidingDoorType(line); if (door->whichDoorIndex < 0) I_Error("EV_SlidingDoor: Can't use texture for sliding door!"); door->frontsector = sec; door->backsector = line->backsector; door->thinker.function = T_SlidingDoor; door->timer = SWAITTICS; door->frame = 0; door->line = line; } } #endif ================================================ FILE: fbdoom/p_enemy.c ================================================ // // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // 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. // // DESCRIPTION: // Enemy thinking, AI. // Action Pointer Functions // that are associated with states/frames. // #include #include #include "m_random.h" #include "i_system.h" #include "doomdef.h" #include "p_local.h" #include "s_sound.h" #include "g_game.h" // State. #include "doomstat.h" #include "r_state.h" // Data. #include "sounds.h" typedef enum { DI_EAST, DI_NORTHEAST, DI_NORTH, DI_NORTHWEST, DI_WEST, DI_SOUTHWEST, DI_SOUTH, DI_SOUTHEAST, DI_NODIR, NUMDIRS } dirtype_t; // // P_NewChaseDir related LUT. // dirtype_t opposite[] = { DI_WEST, DI_SOUTHWEST, DI_SOUTH, DI_SOUTHEAST, DI_EAST, DI_NORTHEAST, DI_NORTH, DI_NORTHWEST, DI_NODIR }; dirtype_t diags[] = { DI_NORTHWEST, DI_NORTHEAST, DI_SOUTHWEST, DI_SOUTHEAST }; void A_Fall (mobj_t *actor); // // 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. // mobj_t* soundtarget; void P_RecursiveSound ( sector_t* sec, int soundblocks ) { int i; line_t* check; sector_t* other; // 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; sec->soundtarget = soundtarget; for (i=0 ;ilinecount ; i++) { check = sec->lines[i]; if (! (check->flags & ML_TWOSIDED) ) continue; P_LineOpening (check); if (openrange <= 0) continue; // closed door if ( sides[ check->sidenum[0] ].sector == sec) other = sides[ check->sidenum[1] ] .sector; else other = sides[ check->sidenum[0] ].sector; if (check->flags & ML_SOUNDBLOCK) { if (!soundblocks) P_RecursiveSound (other, 1); } else P_RecursiveSound (other, soundblocks); } } // // 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* emmiter ) { soundtarget = target; validcount++; P_RecursiveSound (emmiter->subsector->sector, 0); } // // P_CheckMeleeRange // boolean P_CheckMeleeRange (mobj_t* actor) { mobj_t* pl; fixed_t dist; if (!actor->target) return false; pl = actor->target; dist = P_AproxDistance (pl->x-actor->x, pl->y-actor->y); if (dist >= MELEERANGE-20*FRACUNIT+pl->info->radius) return false; if (! P_CheckSight (actor, actor->target) ) return false; return true; } // // P_CheckMissileRange // 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; return true; } 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 >>= 16; 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 () < dist) return false; return true; } // // P_Move // Move in the current direction, // returns false if the move is blocked. // fixed_t xspeed[8] = {FRACUNIT,47000,0,-47000,-FRACUNIT,-47000,0,47000}; fixed_t yspeed[8] = {0,47000,FRACUNIT,47000,0,-47000,-FRACUNIT,-47000}; boolean P_Move (mobj_t* actor) { fixed_t tryx; fixed_t tryy; line_t* ld; // warning: 'catch', 'throw', and 'try' // are all C++ reserved words boolean try_ok; boolean good; if (actor->movedir == DI_NODIR) return false; if ((unsigned)actor->movedir >= 8) I_Error ("Weird actor->movedir!"); tryx = actor->x + actor->info->speed*xspeed[actor->movedir]; tryy = actor->y + actor->info->speed*yspeed[actor->movedir]; try_ok = P_TryMove (actor, tryx, tryy); if (!try_ok) { // open any specials if (actor->flags & MF_FLOAT && floatok) { // must adjust height if (actor->z < tmfloorz) actor->z += FLOATSPEED; else actor->z -= FLOATSPEED; actor->flags |= MF_INFLOAT; return true; } if (!numspechit) return false; actor->movedir = DI_NODIR; good = false; while (numspechit--) { ld = spechit[numspechit]; // if the special is not a door // that can be opened, // return false if (P_UseSpecialLine (actor, ld,0)) good = true; } return good; } else { actor->flags &= ~MF_INFLOAT; } if (! (actor->flags & MF_FLOAT) ) actor->z = actor->floorz; 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. // boolean P_TryWalk (mobj_t* actor) { if (!P_Move (actor)) { return false; } actor->movecount = P_Random()&15; return true; } void P_NewChaseDir (mobj_t* actor) { fixed_t deltax; fixed_t deltay; dirtype_t d[3]; int tdir; dirtype_t olddir; dirtype_t turnaround; if (!actor->target) I_Error ("P_NewChaseDir: called with no target"); olddir = actor->movedir; turnaround=opposite[olddir]; deltax = actor->target->x - actor->x; deltay = actor->target->y - actor->y; if (deltax>10*FRACUNIT) d[1]= DI_EAST; else if (deltax<-10*FRACUNIT) d[1]= DI_WEST; else d[1]=DI_NODIR; if (deltay<-10*FRACUNIT) d[2]= DI_SOUTH; else if (deltay>10*FRACUNIT) d[2]= DI_NORTH; else d[2]=DI_NODIR; // try direct route if (d[1] != DI_NODIR && d[2] != DI_NODIR) { actor->movedir = diags[((deltay<0)<<1)+(deltax>0)]; if (actor->movedir != (int) turnaround && P_TryWalk(actor)) return; } // try other directions if (P_Random() > 200 || abs(deltay)>abs(deltax)) { tdir=d[1]; d[1]=d[2]; d[2]=tdir; } if (d[1]==turnaround) d[1]=DI_NODIR; if (d[2]==turnaround) d[2]=DI_NODIR; if (d[1]!=DI_NODIR) { actor->movedir = d[1]; if (P_TryWalk(actor)) { // either moved forward or attacked return; } } if (d[2]!=DI_NODIR) { actor->movedir =d[2]; if (P_TryWalk(actor)) return; } // there is no direct path to the player, // so pick another direction. if (olddir!=DI_NODIR) { actor->movedir =olddir; if (P_TryWalk(actor)) return; } // randomly determine direction of search if (P_Random()&1) { for ( tdir=DI_EAST; tdir<=DI_SOUTHEAST; tdir++ ) { if (tdir != (int) turnaround) { actor->movedir =tdir; if ( P_TryWalk(actor) ) return; } } } else { for ( tdir=DI_SOUTHEAST; tdir != (DI_EAST-1); tdir-- ) { if (tdir != (int) turnaround) { actor->movedir = tdir; if ( P_TryWalk(actor) ) return; } } } if (turnaround != DI_NODIR) { actor->movedir =turnaround; if ( P_TryWalk(actor) ) return; } actor->movedir = DI_NODIR; // can not move } // // P_LookForPlayers // If allaround is false, only look 180 degrees in front. // Returns true if a player is targeted. // boolean P_LookForPlayers ( mobj_t* actor, boolean allaround ) { int c; int stop; player_t* player; angle_t an; fixed_t dist; c = 0; stop = (actor->lastlook-1)&3; for ( ; ; actor->lastlook = (actor->lastlook+1)&3 ) { if (!playeringame[actor->lastlook]) continue; if (c++ == 2 || actor->lastlook == stop) { // done looking return false; } player = &players[actor->lastlook]; if (player->health <= 0) continue; // dead if (!P_CheckSight (actor, player->mo)) continue; // out of sight if (!allaround) { an = R_PointToAngle2 (actor->x, actor->y, player->mo->x, player->mo->y) - actor->angle; if (an > ANG90 && an < ANG270) { dist = P_AproxDistance (player->mo->x - actor->x, player->mo->y - actor->y); // if real close, react anyway if (dist > MELEERANGE) continue; // behind back } } actor->target = player->mo; return true; } return false; } // // A_KeenDie // DOOM II special, map 32. // Uses special tag 666. // void A_KeenDie (mobj_t* mo) { thinker_t* th; mobj_t* mo2; 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.acp1 != (actionf_p1)P_MobjThinker) continue; mo2 = (mobj_t *)th; if (mo2 != mo && mo2->type == mo->type && mo2->health > 0) { // other Keen not dead return; } } junk.tag = 666; EV_DoDoor(&junk, vld_open); } // // ACTION ROUTINES // // // A_Look // Stay in state until a player is sighted. // void A_Look (mobj_t* actor) { mobj_t* targ; actor->threshold = 0; // any shot will wake up targ = actor->subsector->sector->soundtarget; if (targ && (targ->flags & MF_SHOOTABLE) ) { actor->target = targ; if ( actor->flags & MF_AMBUSH ) { if (P_CheckSight (actor, actor->target)) goto seeyou; } else goto seeyou; } if (!P_LookForPlayers (actor, false) ) return; // go into chase state seeyou: if (actor->info->seesound) { int sound; switch (actor->info->seesound) { case sfx_posit1: case sfx_posit2: case sfx_posit3: sound = sfx_posit1+P_Random()%3; break; case sfx_bgsit1: case sfx_bgsit2: sound = sfx_bgsit1+P_Random()%2; break; default: sound = actor->info->seesound; break; } if (actor->type==MT_SPIDER || actor->type == MT_CYBORG) { // full volume S_StartSound (NULL, sound); } else S_StartSound (actor, sound); } P_SetMobjState (actor, actor->info->seestate); } // // A_Chase // Actor has a melee attack, // so it tries to close as fast as possible // void A_Chase (mobj_t* actor) { int delta; if (actor->reactiontime) actor->reactiontime--; // modify target threshold if (actor->threshold) { if (!actor->target || actor->target->health <= 0) { actor->threshold = 0; } else actor->threshold--; } // turn towards movement direction if not there yet if (actor->movedir < 8) { actor->angle &= (7<<29); delta = actor->angle - (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)) { // look for a new target if (P_LookForPlayers(actor,true)) return; // got a new target P_SetMobjState (actor, actor->info->spawnstate); 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); return; } // check for missile attack if (actor->info->missilestate) { if (gameskill < sk_nightmare && !fastparm && actor->movecount) { goto nomissile; } if (!P_CheckMissileRange (actor)) goto nomissile; P_SetMobjState (actor, actor->info->missilestate); actor->flags |= MF_JUSTATTACKED; return; } // ? nomissile: // possibly choose another target if (netgame && !actor->threshold && !P_CheckSight (actor, actor->target) ) { if (P_LookForPlayers(actor,true)) return; // got a new target } // chase towards player if (--actor->movecount<0 || !P_Move (actor)) { P_NewChaseDir (actor); } // make active sound if (actor->info->activesound && P_Random () < 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) actor->angle += (P_Random()-P_Random())<<21; } // // A_PosAttack // void A_PosAttack (mobj_t* actor) { int angle; int damage; int slope; if (!actor->target) return; A_FaceTarget (actor); angle = actor->angle; slope = P_AimLineAttack (actor, angle, MISSILERANGE); S_StartSound (actor, sfx_pistol); angle += (P_Random()-P_Random())<<20; damage = ((P_Random()%5)+1)*3; P_LineAttack (actor, angle, MISSILERANGE, slope, damage); } void A_SPosAttack (mobj_t* actor) { int i; int angle; int bangle; int damage; int slope; if (!actor->target) return; S_StartSound (actor, sfx_shotgn); A_FaceTarget (actor); bangle = actor->angle; slope = P_AimLineAttack (actor, bangle, MISSILERANGE); for (i=0 ; i<3 ; i++) { angle = bangle + ((P_Random()-P_Random())<<20); damage = ((P_Random()%5)+1)*3; P_LineAttack (actor, angle, MISSILERANGE, slope, damage); } } void A_CPosAttack (mobj_t* actor) { int angle; int bangle; int damage; int slope; if (!actor->target) return; S_StartSound (actor, sfx_shotgn); A_FaceTarget (actor); bangle = actor->angle; slope = P_AimLineAttack (actor, bangle, MISSILERANGE); angle = bangle + ((P_Random()-P_Random())<<20); damage = ((P_Random()%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); if (P_Random () < 40) return; if (!actor->target || actor->target->health <= 0 || !P_CheckSight (actor, actor->target) ) { P_SetMobjState (actor, actor->info->seestate); } } void A_SpidRefire (mobj_t* actor) { // keep firing unless target got out of sight A_FaceTarget (actor); if (P_Random () < 10) return; if (!actor->target || actor->target->health <= 0 || !P_CheckSight (actor, actor->target) ) { P_SetMobjState (actor, actor->info->seestate); } } void A_BspiAttack (mobj_t *actor) { if (!actor->target) return; A_FaceTarget (actor); // launch a missile P_SpawnMissile (actor, actor->target, MT_ARACHPLAZ); } // // A_TroopAttack // void A_TroopAttack (mobj_t* actor) { int damage; if (!actor->target) return; A_FaceTarget (actor); if (P_CheckMeleeRange (actor)) { S_StartSound (actor, sfx_claw); damage = (P_Random()%8+1)*3; P_DamageMobj (actor->target, actor, actor, damage); return; } // launch a missile P_SpawnMissile (actor, actor->target, MT_TROOPSHOT); } void A_SargAttack (mobj_t* actor) { int damage; if (!actor->target) return; A_FaceTarget (actor); if (P_CheckMeleeRange (actor)) { damage = ((P_Random()%10)+1)*4; P_DamageMobj (actor->target, actor, actor, damage); } } void A_HeadAttack (mobj_t* actor) { int damage; if (!actor->target) return; A_FaceTarget (actor); if (P_CheckMeleeRange (actor)) { damage = (P_Random()%6+1)*10; P_DamageMobj (actor->target, actor, actor, damage); return; } // launch a missile P_SpawnMissile (actor, actor->target, MT_HEADSHOT); } 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) { int damage; if (!actor->target) return; if (P_CheckMeleeRange (actor)) { S_StartSound (actor, sfx_claw); damage = (P_Random()%8+1)*10; P_DamageMobj (actor->target, actor, actor, damage); return; } // launch a missile P_SpawnMissile (actor, actor->target, MT_BRUISERSHOT); } // // 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; 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; if (gametic & 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()&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) { int damage; if (!actor->target) return; A_FaceTarget (actor); if (P_CheckMeleeRange (actor)) { damage = ((P_Random()%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; 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 ( abs(thing->x - viletryx) > maxdist || abs(thing->y - viletryy) > maxdist ) return true; // not actually touching corpsehit = thing; corpsehit->momx = corpsehit->momy = 0; corpsehit->height <<= 2; check = P_CheckPosition (corpsehit, corpsehit->x, corpsehit->y); corpsehit->height >>= 2; 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; int xh; int yl; int yh; int bx; int by; mobjinfo_t* info; mobj_t* temp; 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)) { // got one! 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); corpsehit->height <<= 2; corpsehit->flags = info->flags; corpsehit->health = info->spawnhealth; corpsehit->target = NULL; return; } } } } // Return to normal attack. A_Chase (actor); } // // 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_Fire (mobj_t* actor); 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) { mobj_t* dest; mobj_t* target; unsigned an; dest = actor->tracer; if (!dest) return; target = P_SubstNullMobj(actor->target); // don't move it if the vile lost sight if (!P_CheckSight (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); fog = P_SpawnMobj (actor->target->x, actor->target->x, actor->target->z, MT_FIRE); actor->tracer = fog; fog->target = actor; 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; mobj_t* target; int an; A_FaceTarget (actor); // Change direction to ... actor->angle += FATSPREAD; target = P_SubstNullMobj(actor->target); P_SpawnMissile (actor, target, MT_FATSHOT); mo = P_SpawnMissile (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; mobj_t* target; int an; A_FaceTarget (actor); // Now here choose opposite deviation. actor->angle -= FATSPREAD; target = P_SubstNullMobj(actor->target); P_SpawnMissile (actor, target, MT_FATSHOT); mo = P_SpawnMissile (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; mobj_t* target; int an; A_FaceTarget (actor); target = P_SubstNullMobj(actor->target); mo = P_SpawnMissile (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, 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 // void A_PainShootSkull ( mobj_t* actor, angle_t angle ) { fixed_t x; fixed_t y; fixed_t z; mobj_t* newmobj; angle_t an; int prestep; int count; thinker_t* currentthinker; // count total number of skull currently on the level count = 0; currentthinker = thinkercap.next; while (currentthinker != &thinkercap) { if ( (currentthinker->function.acp1 == (actionf_p1)P_MobjThinker) && ((mobj_t *)currentthinker)->type == MT_SKULL) count++; currentthinker = currentthinker->next; } // if there are allready 20 skulls on the level, // don't spit another one if (count > 20) return; // okay, there's playe 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; newmobj = P_SpawnMobj (x , y, z, MT_SKULL); // Check for movements. if (!P_TryMove (newmobj, newmobj->x, newmobj->y)) { // kill it immediately P_DamageMobj (newmobj,actor,actor,10000); return; } 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 ()%3; break; case sfx_bgdth1: case sfx_bgdth2: sound = sfx_bgdth1 + P_Random ()%2; break; default: sound = actor->info->deathsound; break; } // Check for bosses. if (actor->type==MT_SPIDER || actor->type == MT_CYBORG) { // full volume S_StartSound (NULL, sound); } 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; // So change this if corpse objects // are meant to be obstacles. } // // A_Explode // void A_Explode (mobj_t* thingy) { P_RadiusAttack(thingy, thingy->target, 128); } // Check whether the death of the specified monster type is allowed // to trigger the end of episode special action. // // This behavior changed in v1.9, the most notable effect of which // was to break uac_dead.wad static boolean CheckBossEnd(mobjtype_t motype) { if (gameversion < exe_ultimate) { if (gamemap != 8) { return false; } // Baron death on later episodes is nothing special. if (motype == MT_BRUISER && gameepisode != 1) { return false; } return true; } else { // New logic that appeared in Ultimate Doom. // Looks like the logic was overhauled while adding in the // episode 4 support. Now bosses only trigger on their // specific episode. switch(gameepisode) { case 1: return gamemap == 8 && motype == MT_BRUISER; case 2: return gamemap == 8 && motype == MT_CYBORG; case 3: return gamemap == 8 && motype == MT_SPIDER; case 4: return (gamemap == 6 && motype == MT_CYBORG) || (gamemap == 8 && motype == MT_SPIDER); default: return gamemap == 8; } } } // // A_BossDeath // Possibly trigger special effects // if on first boss level // void A_BossDeath (mobj_t* mo) { thinker_t* th; mobj_t* mo2; line_t junk; int i; if ( gamemode == commercial) { if (gamemap != 7) return; if ((mo->type != MT_FATSO) && (mo->type != MT_BABY)) return; } else { if (!CheckBossEnd(mo->type)) { return; } } // 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.acp1 != (actionf_p1)P_MobjThinker) continue; mo2 = (mobj_t *)th; if (mo2 != mo && mo2->type == mo->type && mo2->health > 0) { // other boss not dead return; } } // 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, vld_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_ReFire ( player_t* player, pspdef_t* psp ); void A_CloseShotgun2 ( player_t* player, pspdef_t* psp ) { S_StartSound (player->mo, sfx_dbcls); A_ReFire(player,psp); } mobj_t* braintargets[32]; int numbraintargets; int braintargeton = 0; void A_BrainAwake (mobj_t* mo) { thinker_t* thinker; mobj_t* m; // find all the target spots numbraintargets = 0; braintargeton = 0; thinker = thinkercap.next; for (thinker = thinkercap.next ; thinker != &thinkercap ; thinker = thinker->next) { if (thinker->function.acp1 != (actionf_p1)P_MobjThinker) continue; // not a mobj m = (mobj_t *)thinker; if (m->type == MT_BOSSTARGET ) { braintargets[numbraintargets] = m; numbraintargets++; } } S_StartSound (NULL,sfx_bossit); } void A_BrainPain (mobj_t* mo) { S_StartSound (NULL,sfx_bospn); } void A_BrainScream (mobj_t* mo) { int x; int y; int z; mobj_t* th; for (x=mo->x - 196*FRACUNIT ; x< mo->x + 320*FRACUNIT ; x+= FRACUNIT*8) { y = mo->y - 320*FRACUNIT; z = 128 + P_Random()*2*FRACUNIT; th = P_SpawnMobj (x,y,z, MT_ROCKET); th->momz = P_Random()*512; P_SetMobjState (th, S_BRAINEXPLODE1); th->tics -= P_Random()&7; if (th->tics < 1) th->tics = 1; } S_StartSound (NULL,sfx_bosdth); } void A_BrainExplode (mobj_t* mo) { int x; int y; int z; mobj_t* th; x = mo->x + (P_Random () - P_Random ())*2048; y = mo->y; z = 128 + P_Random()*2*FRACUNIT; th = P_SpawnMobj (x,y,z, MT_ROCKET); th->momz = P_Random()*512; P_SetMobjState (th, S_BRAINEXPLODE1); th->tics -= P_Random()&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; mobj_t* newmobj; static int easy = 0; easy ^= 1; if (gameskill <= sk_easy && (!easy)) return; // shoot a cube at current target targ = braintargets[braintargeton]; braintargeton = (braintargeton+1)%numbraintargets; // spawn brain missile newmobj = P_SpawnMissile (mo, targ, MT_SPAWNSHOT); newmobj->target = targ; newmobj->reactiontime = ((targ->y - mo->y)/newmobj->momy) / newmobj->state->tics; S_StartSound(NULL, sfx_bospit); } void A_SpawnFly (mobj_t* mo); // 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 = P_SubstNullMobj(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 (); // 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); if (P_LookForPlayers (newmobj, true) ) P_SetMobjState (newmobj, newmobj->info->seestate); // telefrag anything in this spot P_TeleportMove (newmobj, newmobj->x, newmobj->y); // remove self (i.e., cube). P_RemoveMobj (mo); } void A_PlayerScream (mobj_t* mo) { // Default death sound. int sound = sfx_pldeth; if ( (gamemode == commercial) && (mo->health < -50)) { // IF THE PLAYER DIES // LESS THAN -50% WITHOUT GIBBING sound = sfx_pdiehi; } S_StartSound (mo, sound); } ================================================ FILE: fbdoom/p_floor.c ================================================ // // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // 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. // // DESCRIPTION: // Floor animation: raising stairs. // #include "z_zone.h" #include "doomdef.h" #include "p_local.h" #include "s_sound.h" // State. #include "doomstat.h" #include "r_state.h" // Data. #include "sounds.h" // // FLOORS // // // Move a plane (floor or ceiling) and check for crushing // result_e T_MovePlane ( sector_t* sector, fixed_t speed, fixed_t dest, boolean crush, int floorOrCeiling, int direction ) { boolean flag; fixed_t lastpos; switch(floorOrCeiling) { case 0: // FLOOR switch(direction) { case -1: // DOWN if (sector->floorheight - speed < dest) { lastpos = sector->floorheight; sector->floorheight = dest; flag = P_ChangeSector(sector,crush); if (flag == true) { sector->floorheight =lastpos; P_ChangeSector(sector,crush); //return crushed; } return pastdest; } else { lastpos = sector->floorheight; sector->floorheight -= speed; flag = P_ChangeSector(sector,crush); if (flag == true) { sector->floorheight = lastpos; P_ChangeSector(sector,crush); return crushed; } } break; case 1: // UP if (sector->floorheight + speed > dest) { lastpos = sector->floorheight; sector->floorheight = dest; flag = P_ChangeSector(sector,crush); if (flag == true) { sector->floorheight = lastpos; P_ChangeSector(sector,crush); //return crushed; } return pastdest; } else { // COULD GET CRUSHED lastpos = sector->floorheight; sector->floorheight += speed; flag = P_ChangeSector(sector,crush); if (flag == true) { if (crush == true) return crushed; sector->floorheight = lastpos; P_ChangeSector(sector,crush); return crushed; } } break; } break; case 1: // CEILING switch(direction) { case -1: // DOWN if (sector->ceilingheight - speed < dest) { lastpos = sector->ceilingheight; sector->ceilingheight = dest; flag = P_ChangeSector(sector,crush); if (flag == true) { sector->ceilingheight = lastpos; P_ChangeSector(sector,crush); //return crushed; } return pastdest; } else { // COULD GET CRUSHED lastpos = sector->ceilingheight; sector->ceilingheight -= speed; flag = P_ChangeSector(sector,crush); if (flag == true) { if (crush == true) return crushed; sector->ceilingheight = lastpos; P_ChangeSector(sector,crush); return crushed; } } break; case 1: // UP if (sector->ceilingheight + speed > dest) { lastpos = sector->ceilingheight; sector->ceilingheight = dest; flag = P_ChangeSector(sector,crush); if (flag == true) { sector->ceilingheight = lastpos; P_ChangeSector(sector,crush); //return crushed; } return pastdest; } else { lastpos = sector->ceilingheight; sector->ceilingheight += speed; flag = P_ChangeSector(sector,crush); // UNUSED #if 0 if (flag == true) { sector->ceilingheight = lastpos; P_ChangeSector(sector,crush); return crushed; } #endif } break; } break; } return ok; } // // MOVE A FLOOR TO IT'S DESTINATION (UP OR DOWN) // void T_MoveFloor(floormove_t* floor) { result_e res; res = T_MovePlane(floor->sector, floor->speed, floor->floordestheight, floor->crush,0,floor->direction); if (!(leveltime&7)) S_StartSound(&floor->sector->soundorg, sfx_stnmov); if (res == pastdest) { floor->sector->specialdata = NULL; if (floor->direction == 1) { switch(floor->type) { case donutRaise: floor->sector->special = floor->newspecial; floor->sector->floorpic = floor->texture; default: break; } } else if (floor->direction == -1) { switch(floor->type) { case lowerAndChange: floor->sector->special = floor->newspecial; floor->sector->floorpic = floor->texture; default: break; } } P_RemoveThinker(&floor->thinker); S_StartSound(&floor->sector->soundorg, sfx_pstop); } } // // HANDLE FLOOR TYPES // 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; while ((secnum = P_FindSectorFromLineTag(line,secnum)) >= 0) { sec = §ors[secnum]; // ALREADY MOVING? IF SO, KEEP GOING... if (sec->specialdata) continue; // new floor thinker rtn = 1; floor = Z_Malloc (sizeof(*floor), PU_LEVSPEC, 0); P_AddThinker (&floor->thinker); sec->specialdata = floor; floor->thinker.function.acp1 = (actionf_p1) T_MoveFloor; floor->type = floortype; floor->crush = false; switch(floortype) { case lowerFloor: floor->direction = -1; floor->sector = sec; floor->speed = FLOORSPEED; floor->floordestheight = P_FindHighestFloorSurrounding(sec); break; case lowerFloorToLowest: floor->direction = -1; floor->sector = sec; floor->speed = FLOORSPEED; floor->floordestheight = P_FindLowestFloorSurrounding(sec); 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; 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; break; case raiseToTexture: { int minsize = INT_MAX; side_t* side; floor->direction = 1; floor->sector = sec; floor->speed = FLOORSPEED; for (i = 0; i < sec->linecount; i++) { if (twoSided (secnum, i) ) { side = getSide(secnum,i,0); if (side->bottomtexture >= 0) if (textureheight[side->bottomtexture] < minsize) minsize = textureheight[side->bottomtexture]; side = getSide(secnum,i,1); if (side->bottomtexture >= 0) if (textureheight[side->bottomtexture] < minsize) minsize = textureheight[side->bottomtexture]; } } floor->floordestheight = floor->sector->floorheight + minsize; } break; case lowerAndChange: floor->direction = -1; floor->sector = sec; floor->speed = FLOORSPEED; floor->floordestheight = P_FindLowestFloorSurrounding(sec); floor->texture = sec->floorpic; for (i = 0; i < sec->linecount; i++) { if ( twoSided(secnum, i) ) { if (getSide(secnum,i,0)->sector-sectors == secnum) { sec = getSector(secnum,i,1); if (sec->floorheight == floor->floordestheight) { floor->texture = sec->floorpic; floor->newspecial = sec->special; break; } } else { sec = getSector(secnum,i,0); if (sec->floorheight == floor->floordestheight) { floor->texture = sec->floorpic; floor->newspecial = sec->special; break; } } } } default: break; } } return rtn; } // // BUILD A STAIRCASE! // int EV_BuildStairs ( line_t* line, stair_e type ) { int secnum; int height; int i; int newsecnum; int texture; int ok; int rtn; sector_t* sec; sector_t* tsec; floormove_t* floor; fixed_t stairsize = 0; fixed_t speed = 0; secnum = -1; rtn = 0; while ((secnum = P_FindSectorFromLineTag(line,secnum)) >= 0) { sec = §ors[secnum]; // ALREADY MOVING? IF SO, KEEP GOING... if (sec->specialdata) continue; // new floor thinker rtn = 1; floor = Z_Malloc (sizeof(*floor), PU_LEVSPEC, 0); P_AddThinker (&floor->thinker); sec->specialdata = floor; floor->thinker.function.acp1 = (actionf_p1) T_MoveFloor; floor->direction = 1; floor->sector = sec; switch(type) { case build8: speed = FLOORSPEED/4; stairsize = 8*FRACUNIT; break; case turbo16: speed = FLOORSPEED*4; stairsize = 16*FRACUNIT; 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] // 2. Other side is the next sector to raise do { ok = 0; for (i = 0;i < sec->linecount;i++) { if ( !((sec->lines[i])->flags & ML_TWOSIDED) ) continue; tsec = (sec->lines[i])->frontsector; newsecnum = tsec-sectors; if (secnum != newsecnum) continue; tsec = (sec->lines[i])->backsector; newsecnum = tsec - sectors; if (tsec->floorpic != texture) continue; height += stairsize; if (tsec->specialdata) continue; sec = tsec; secnum = newsecnum; floor = Z_Malloc (sizeof(*floor), PU_LEVSPEC, 0); P_AddThinker (&floor->thinker); sec->specialdata = floor; floor->thinker.function.acp1 = (actionf_p1) T_MoveFloor; floor->direction = 1; floor->sector = sec; floor->speed = speed; floor->floordestheight = height; ok = 1; break; } } while(ok); } return rtn; } ================================================ FILE: fbdoom/p_inter.c ================================================ // // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // 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. // // DESCRIPTION: // Handling interactions (i.e., collisions). // // Data. #include "doomdef.h" #include "dstrings.h" #include "sounds.h" #include "deh_main.h" #include "deh_misc.h" #include "doomstat.h" #include "m_random.h" #include "i_system.h" #include "am_map.h" #include "p_local.h" #include "s_sound.h" #include "p_inter.h" #define BONUSADD 6 // 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 // boolean P_GiveAmmo ( player_t* player, ammotype_t ammo, int num ) { int oldammo; if (ammo == am_noammo) return false; if (ammo > NUMAMMO) I_Error ("P_GiveAmmo: bad type %i", ammo); if ( player->ammo[ammo] == player->maxammo[ammo] ) return false; if (num) num *= clipammo[ammo]; else num = clipammo[ammo]/2; if (gameskill == sk_baby || gameskill == sk_nightmare) { // give double ammo in trainer mode, // you'll need in 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. // 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; if (deathmatch) P_GiveAmmo (player, weaponinfo[weapon].ammo, 5); else P_GiveAmmo (player, weaponinfo[weapon].ammo, 2); player->pendingweapon = weapon; if (player == &players[consoleplayer]) S_StartSound (NULL, sfx_wpnup); return false; } if (weaponinfo[weapon].ammo != am_noammo) { // give one clip with a dropped weapon, // two clips with a found weapon if (dropped) gaveammo = P_GiveAmmo (player, weaponinfo[weapon].ammo, 1); else gaveammo = P_GiveAmmo (player, weaponinfo[weapon].ammo, 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 // boolean P_GiveBody ( player_t* player, int num ) { if (player->health >= MAXHEALTH) return false; 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. // boolean P_GiveArmor ( player_t* player, int armortype ) { int hits; hits = armortype*100; if (player->armorpoints >= hits) return false; // don't pick up player->armortype = armortype; player->armorpoints = hits; return true; } // // P_GiveCard // void P_GiveCard ( player_t* player, card_t card ) { if (player->cards[card]) return; player->bonuscount = BONUSADD; player->cards[card] = 1; } // // P_GivePower // boolean P_GivePower ( player_t* player, int /*powertype_t*/ power ) { if (power == pw_invulnerability) { player->powers[power] = INVULNTICS; return true; } if (power == pw_invisibility) { player->powers[power] = INVISTICS; player->mo->flags |= MF_SHADOW; return true; } if (power == pw_infrared) { player->powers[power] = INFRATICS; return true; } if (power == pw_ironfeet) { player->powers[power] = IRONTICS; return true; } if (power == pw_strength) { P_GiveBody (player, 100); player->powers[power] = 1; return true; } if (player->powers[power]) return false; // already got it player->powers[power] = 1; return true; } // // P_TouchSpecialThing // void P_TouchSpecialThing ( mobj_t* special, mobj_t* toucher ) { player_t* player; int i; fixed_t delta; int sound; delta = special->z - toucher->z; if (delta > toucher->height || delta < -8*FRACUNIT) { // out of reach return; } 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, deh_green_armor_class)) return; player->message = DEH_String(GOTARMOR); break; case SPR_ARM2: if (!P_GiveArmor (player, deh_blue_armor_class)) return; player->message = DEH_String(GOTMEGA); break; // bonus items case SPR_BON1: player->health++; // can go over 100% if (player->health > deh_max_health) player->health = deh_max_health; player->mo->health = player->health; player->message = DEH_String(GOTHTHBONUS); break; case SPR_BON2: player->armorpoints++; // can go over 100% if (player->armorpoints > deh_max_armor) player->armorpoints = deh_max_armor; // deh_green_armor_class only applies to the green armor shirt; // for the armor helmets, armortype 1 is always used. if (!player->armortype) player->armortype = 1; player->message = DEH_String(GOTARMBONUS); break; case SPR_SOUL: player->health += deh_soulsphere_health; if (player->health > deh_max_soulsphere) player->health = deh_max_soulsphere; player->mo->health = player->health; player->message = DEH_String(GOTSUPER); sound = sfx_getpow; break; case SPR_MEGA: if (gamemode != commercial) return; player->health = deh_megasphere_health; player->mo->health = player->health; // We always give armor type 2 for the megasphere; dehacked only // affects the MegaArmor. P_GiveArmor (player, 2); player->message = DEH_String(GOTMSPHERE); sound = sfx_getpow; break; // cards // leave cards for everyone case SPR_BKEY: if (!player->cards[it_bluecard]) player->message = DEH_String(GOTBLUECARD); P_GiveCard (player, it_bluecard); if (!netgame) break; return; case SPR_YKEY: if (!player->cards[it_yellowcard]) player->message = DEH_String(GOTYELWCARD); P_GiveCard (player, it_yellowcard); if (!netgame) break; return; case SPR_RKEY: if (!player->cards[it_redcard]) player->message = DEH_String(GOTREDCARD); P_GiveCard (player, it_redcard); if (!netgame) break; return; case SPR_BSKU: if (!player->cards[it_blueskull]) player->message = DEH_String(GOTBLUESKUL); P_GiveCard (player, it_blueskull); if (!netgame) break; return; case SPR_YSKU: if (!player->cards[it_yellowskull]) player->message = DEH_String(GOTYELWSKUL); P_GiveCard (player, it_yellowskull); if (!netgame) break; return; case SPR_RSKU: if (!player->cards[it_redskull]) player->message = DEH_String(GOTREDSKULL); P_GiveCard (player, it_redskull); if (!netgame) break; return; // medikits, heals case SPR_STIM: if (!P_GiveBody (player, 10)) return; player->message = DEH_String(GOTSTIM); break; case SPR_MEDI: if (!P_GiveBody (player, 25)) return; if (player->health < 25) player->message = DEH_String(GOTMEDINEED); else player->message = DEH_String(GOTMEDIKIT); break; // power ups case SPR_PINV: if (!P_GivePower (player, pw_invulnerability)) return; player->message = DEH_String(GOTINVUL); sound = sfx_getpow; break; case SPR_PSTR: if (!P_GivePower (player, pw_strength)) return; player->message = DEH_String(GOTBERSERK); 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 = DEH_String(GOTINVIS); sound = sfx_getpow; break; case SPR_SUIT: if (!P_GivePower (player, pw_ironfeet)) return; player->message = DEH_String(GOTSUIT); sound = sfx_getpow; break; case SPR_PMAP: if (!P_GivePower (player, pw_allmap)) return; player->message = DEH_String(GOTMAP); sound = sfx_getpow; break; case SPR_PVIS: if (!P_GivePower (player, pw_infrared)) return; player->message = DEH_String(GOTVISOR); 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 = DEH_String(GOTCLIP); break; case SPR_AMMO: if (!P_GiveAmmo (player, am_clip,5)) return; player->message = DEH_String(GOTCLIPBOX); break; case SPR_ROCK: if (!P_GiveAmmo (player, am_misl,1)) return; player->message = DEH_String(GOTROCKET); break; case SPR_BROK: if (!P_GiveAmmo (player, am_misl,5)) return; player->message = DEH_String(GOTROCKBOX); break; case SPR_CELL: if (!P_GiveAmmo (player, am_cell,1)) return; player->message = DEH_String(GOTCELL); break; case SPR_CELP: if (!P_GiveAmmo (player, am_cell,5)) return; player->message = DEH_String(GOTCELLBOX); break; case SPR_SHEL: if (!P_GiveAmmo (player, am_shell,1)) return; player->message = DEH_String(GOTSHELLS); break; case SPR_SBOX: if (!P_GiveAmmo (player, am_shell,5)) return; player->message = DEH_String(GOTSHELLBOX); break; case SPR_BPAK: if (!player->backpack) { for (i=0 ; imaxammo[i] *= 2; player->backpack = true; } for (i=0 ; imessage = DEH_String(GOTBACKPACK); break; // weapons case SPR_BFUG: if (!P_GiveWeapon (player, wp_bfg, false) ) return; player->message = DEH_String(GOTBFG9000); sound = sfx_wpnup; break; case SPR_MGUN: if (!P_GiveWeapon (player, wp_chaingun, (special->flags&MF_DROPPED) != 0) ) return; player->message = DEH_String(GOTCHAINGUN); sound = sfx_wpnup; break; case SPR_CSAW: if (!P_GiveWeapon (player, wp_chainsaw, false) ) return; player->message = DEH_String(GOTCHAINSAW); sound = sfx_wpnup; break; case SPR_LAUN: if (!P_GiveWeapon (player, wp_missile, false) ) return; player->message = DEH_String(GOTLAUNCHER); sound = sfx_wpnup; break; case SPR_PLAS: if (!P_GiveWeapon (player, wp_plasma, false) ) return; player->message = DEH_String(GOTPLASMA); sound = sfx_wpnup; break; case SPR_SHOT: if (!P_GiveWeapon (player, wp_shotgun, (special->flags&MF_DROPPED) != 0 ) ) return; player->message = DEH_String(GOTSHOTGUN); sound = sfx_wpnup; break; case SPR_SGN2: if (!P_GiveWeapon (player, wp_supershotgun, (special->flags&MF_DROPPED) != 0 ) ) return; player->message = DEH_String(GOTSHOTGUN2); 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; if (player == &players[consoleplayer]) S_StartSound (NULL, sound); } // // KillMobj // 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 (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 (!netgame && (target->flags & MF_COUNTKILL) ) { // count all monster deaths, // even those caused by other monsters players[0].killcount++; } if (target->player) { // 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] && automapactive) { // don't die in auto map, // switch view prior to dying AM_Stop (); } } 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()&3; if (target->tics < 1) target->tics = 1; // I_StartSound (&actor->r, actor->info->deathsound); // In Chex Quest, monsters don't drop items. if (gameversion == exe_chex) { return; } // 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 ) { unsigned ang; int saved; player_t* player; fixed_t thrust; int temp; if ( !(target->flags & MF_SHOOTABLE) ) 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)) { ang = R_PointToAngle2 ( inflictor->x, inflictor->y, target->x, target->y); 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 ()&1) ) { ang += ANG180; thrust *= 4; } ang >>= ANGLETOFINESHIFT; target->momx += FixedMul (thrust, finecosine[ang]); target->momy += FixedMul (thrust, finesine[ang]); } // 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. if ( damage < 1000 && ( (player->cheats&CF_GODMODE) || player->powers[pw_invulnerability] ) ) { return; } if (player->armortype) { if (player->armortype == 1) saved = damage/3; else saved = 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... temp = damage < 100 ? damage : 100; if (player == &players[consoleplayer]) I_Tactile (40,10,40+temp*2); } // do the damage target->health -= damage; if (target->health <= 0) { P_KillMobj (source, target); return; } if ( (P_Random () < target->info->painchance) && !(target->flags&MF_SKULLFLY) ) { target->flags |= MF_JUSTHIT; // fight back! P_SetMobjState (target, target->info->painstate); } target->reactiontime = 0; // we're awake now... if ( (!target->threshold || target->type == MT_VILE) && source && source != target && source->type != MT_VILE) { // if not intent on another player, // chase after this one target->target = source; target->threshold = BASETHRESHOLD; if (target->state == &states[target->info->spawnstate] && target->info->seestate != S_NULL) P_SetMobjState (target, target->info->seestate); } } ================================================ FILE: fbdoom/p_inter.h ================================================ // // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // 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. // // DESCRIPTION: // // #ifndef __P_INTER__ #define __P_INTER__ boolean P_GivePower(player_t*, int); #endif ================================================ FILE: fbdoom/p_lights.c ================================================ // // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // 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. // // DESCRIPTION: // Handle Sector base lighting effects. // Muzzle flash? // #include "z_zone.h" #include "m_random.h" #include "doomdef.h" #include "p_local.h" // State. #include "r_state.h" // // FIRELIGHT FLICKER // // // T_FireFlicker // void T_FireFlicker (fireflicker_t* flick) { int amount; if (--flick->count) return; amount = (P_Random()&3)*16; if (flick->sector->lightlevel - amount < flick->minlight) flick->sector->lightlevel = flick->minlight; else flick->sector->lightlevel = flick->maxlight - amount; flick->count = 4; } // // P_SpawnFireFlicker // void P_SpawnFireFlicker (sector_t* sector) { fireflicker_t* flick; // Note that we are resetting sector attributes. // Nothing special about it during gameplay. sector->special = 0; flick = Z_Malloc ( sizeof(*flick), PU_LEVSPEC, 0); P_AddThinker (&flick->thinker); flick->thinker.function.acp1 = (actionf_p1) T_FireFlicker; flick->sector = sector; flick->maxlight = sector->lightlevel; flick->minlight = P_FindMinSurroundingLight(sector,sector->lightlevel)+16; flick->count = 4; } // // BROKEN LIGHT FLASHING // // // T_LightFlash // Do flashing lights. // 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()&flash->mintime)+1; } else { flash-> sector->lightlevel = flash->maxlight; flash->count = (P_Random()&flash->maxtime)+1; } } // // P_SpawnLightFlash // After the map has been loaded, scan each sector // for specials that spawn thinkers // void P_SpawnLightFlash (sector_t* sector) { lightflash_t* flash; // nothing special about it during gameplay sector->special = 0; flash = Z_Malloc ( sizeof(*flash), PU_LEVSPEC, 0); P_AddThinker (&flash->thinker); flash->thinker.function.acp1 = (actionf_p1) 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()&flash->maxtime)+1; } // // STROBE LIGHT FLASHING // // // T_StrobeFlash // 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; } } // // P_SpawnStrobeFlash // After the map has been loaded, scan each sector // for specials that spawn thinkers // void P_SpawnStrobeFlash ( sector_t* sector, int fastOrSlow, int inSync ) { strobe_t* flash; flash = Z_Malloc ( sizeof(*flash), PU_LEVSPEC, 0); P_AddThinker (&flash->thinker); flash->sector = sector; flash->darktime = fastOrSlow; flash->brighttime = STROBEBRIGHT; flash->thinker.function.acp1 = (actionf_p1) 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 = 0; if (!inSync) flash->count = (P_Random()&7)+1; else flash->count = 1; } // // Start strobing lights (usually from a trigger) // void EV_StartLightStrobing(line_t* line) { int secnum; sector_t* sec; secnum = -1; while ((secnum = P_FindSectorFromLineTag(line,secnum)) >= 0) { sec = §ors[secnum]; if (sec->specialdata) continue; P_SpawnStrobeFlash (sec,SLOWDARK, 0); } } // // TURN LINE'S TAG LIGHTS OFF // void EV_TurnTagLightsOff(line_t* line) { int i; int j; int min; sector_t* sector; sector_t* tsec; line_t* templine; sector = sectors; for (j = 0;j < numsectors; j++, sector++) { if (sector->tag == line->tag) { min = sector->lightlevel; for (i = 0;i < sector->linecount; i++) { templine = sector->lines[i]; tsec = getNextSector(templine,sector); if (!tsec) continue; if (tsec->lightlevel < min) min = tsec->lightlevel; } sector->lightlevel = min; } } } // // TURN LINE'S TAG LIGHTS ON // void EV_LightTurnOn ( line_t* line, int bright ) { int i; int j; sector_t* sector; sector_t* temp; line_t* templine; sector = sectors; for (i=0;itag == line->tag) { // bright = 0 means to search // for highest light level // surrounding sector if (!bright) { for (j = 0;j < sector->linecount; j++) { templine = sector->lines[j]; temp = getNextSector(templine,sector); if (!temp) continue; if (temp->lightlevel > bright) bright = temp->lightlevel; } } sector-> lightlevel = bright; } } } // // Spawn glowing light // void T_Glow(glow_t* g) { switch(g->direction) { case -1: // DOWN g->sector->lightlevel -= GLOWSPEED; if (g->sector->lightlevel <= g->minlight) { g->sector->lightlevel += GLOWSPEED; g->direction = 1; } break; case 1: // UP g->sector->lightlevel += GLOWSPEED; if (g->sector->lightlevel >= g->maxlight) { g->sector->lightlevel -= GLOWSPEED; g->direction = -1; } break; } } void P_SpawnGlowingLight(sector_t* sector) { glow_t* g; g = Z_Malloc( sizeof(*g), PU_LEVSPEC, 0); P_AddThinker(&g->thinker); g->sector = sector; g->minlight = P_FindMinSurroundingLight(sector,sector->lightlevel); g->maxlight = sector->lightlevel; g->thinker.function.acp1 = (actionf_p1) T_Glow; g->direction = -1; sector->special = 0; } ================================================ FILE: fbdoom/p_local.h ================================================ // // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // 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. // // DESCRIPTION: // Play functions, animation, global header. // #ifndef __P_LOCAL__ #define __P_LOCAL__ #ifndef __R_LOCAL__ #include "r_local.h" #endif #define FLOATSPEED (FRACUNIT*4) #define MAXHEALTH 100 #define VIEWHEIGHT (41*FRACUNIT) // 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) // player radius for movement checking #define PLAYERRADIUS 16*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 #define GRAVITY FRACUNIT #define MAXMOVE (30*FRACUNIT) #define USERANGE (64*FRACUNIT) #define MELEERANGE (64*FRACUNIT) #define MISSILERANGE (32*64*FRACUNIT) // follow a player exlusively for 3 seconds #define BASETHRESHOLD 100 // // P_TICK // // both the head and tail of the thinker list extern thinker_t thinkercap; void P_InitThinkers (void); void P_AddThinker (thinker_t* thinker); void P_RemoveThinker (thinker_t* thinker); // // P_PSPR // void P_SetupPsprites (player_t* curplayer); void P_MovePsprites (player_t* curplayer); void P_DropWeapon (player_t* player); // // P_USER // void P_PlayerThink (player_t* player); // // P_MOBJ // #define ONFLOORZ INT_MIN #define ONCEILINGZ INT_MAX // Time interval for item respawning. #define ITEMQUESIZE 128 extern mapthing_t itemrespawnque[ITEMQUESIZE]; extern int itemrespawntime[ITEMQUESIZE]; 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); mobj_t* P_SubstNullMobj (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); // // P_ENEMY // void P_NoiseAlert (mobj_t* target, mobj_t* emmiter); // // P_MAPUTL // 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; // Extended MAXINTERCEPTS, to allow for intercepts overrun emulation. #define MAXINTERCEPTS_ORIGINAL 128 #define MAXINTERCEPTS (MAXINTERCEPTS_ORIGINAL + 61) extern intercept_t intercepts[MAXINTERCEPTS]; extern intercept_t* intercept_p; typedef boolean (*traverser_t) (intercept_t *in); fixed_t P_AproxDistance (fixed_t dx, fixed_t dy); int P_PointOnLineSide (fixed_t x, fixed_t y, line_t* line); int P_PointOnDivlineSide (fixed_t x, fixed_t y, divline_t* line); void P_MakeDivline (line_t* li, divline_t* dl); fixed_t P_InterceptVector (divline_t* v2, divline_t* v1); int P_BoxOnLineSide (fixed_t* tmbox, line_t* ld); extern fixed_t opentop; extern fixed_t openbottom; extern fixed_t openrange; extern fixed_t lowfloor; void P_LineOpening (line_t* linedef); boolean P_BlockLinesIterator (int x, int y, boolean(*func)(line_t*) ); boolean P_BlockThingsIterator (int x, int y, boolean(*func)(mobj_t*) ); #define PT_ADDLINES 1 #define PT_ADDTHINGS 2 #define PT_EARLYOUT 4 extern divline_t trace; boolean P_PathTraverse ( fixed_t x1, fixed_t y1, fixed_t x2, fixed_t y2, int flags, boolean (*trav) (intercept_t *)); void P_UnsetThingPosition (mobj_t* thing); void P_SetThingPosition (mobj_t* thing); // // P_MAP // // If "floatok" true, move would be ok // if within "tmfloorz - tmceilingz". extern boolean floatok; extern fixed_t tmfloorz; extern fixed_t tmceilingz; extern line_t* ceilingline; // fraggle: I have increased the size of this buffer. In the original Doom, // overrunning past this limit caused other bits of memory to be overwritten, // affecting demo playback. However, in doing so, the limit was still // exceeded. So we have to support more than 8 specials. // // We keep the original limit, to detect what variables in memory were // overwritten (see SpechitOverrun()) #define MAXSPECIALCROSS 20 #define MAXSPECIALCROSS_ORIGINAL 8 extern line_t* spechit[MAXSPECIALCROSS]; extern int numspechit; boolean P_CheckPosition (mobj_t *thing, fixed_t x, fixed_t y); boolean P_TryMove (mobj_t* thing, fixed_t x, fixed_t y); boolean P_TeleportMove (mobj_t* thing, fixed_t x, fixed_t y); void P_SlideMove (mobj_t* mo); boolean P_CheckSight (mobj_t* t1, mobj_t* t2); void P_UseLines (player_t* player); boolean P_ChangeSector (sector_t* sector, boolean crunch); extern mobj_t* linetarget; // who got hit (or NULL) fixed_t P_AimLineAttack ( mobj_t* t1, angle_t angle, fixed_t distance ); 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 ); // // P_SETUP // extern byte* rejectmatrix; // for fast sight rejection extern short* blockmaplump; // offsets in blockmap are from here extern short* blockmap; extern int bmapwidth; extern int bmapheight; // in mapblocks extern fixed_t bmaporgx; extern fixed_t bmaporgy; // origin of block map extern mobj_t** blocklinks; // for thing chains // // P_INTER // extern int maxammo[NUMAMMO]; extern int clipammo[NUMAMMO]; void P_TouchSpecialThing ( mobj_t* special, mobj_t* toucher ); void P_DamageMobj ( mobj_t* target, mobj_t* inflictor, mobj_t* source, int damage ); // // P_SPEC // #include "p_spec.h" #endif // __P_LOCAL__ ================================================ FILE: fbdoom/p_map.c ================================================ // // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard, 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. // // DESCRIPTION: // Movement, collision handling. // Shooting and aiming. // #include #include #include "deh_misc.h" #include "m_bbox.h" #include "m_random.h" #include "i_system.h" #include "doomdef.h" #include "m_argv.h" #include "m_misc.h" #include "p_local.h" #include "s_sound.h" // State. #include "doomstat.h" #include "r_state.h" // Data. #include "sounds.h" // Spechit overrun magic value. // // This is the value used by PrBoom-plus. I think the value below is // actually better and works with more demos. However, I think // it's better for the spechits emulation to be compatible with // PrBoom-plus, at least so that the big spechits emulation list // on Doomworld can also be used with Chocolate Doom. #define DEFAULT_SPECHIT_MAGIC 0x01C09C98 // This is from a post by myk on the Doomworld forums, // outputted from entryway's spechit_magic generator for // s205n546.lmp. The _exact_ value of this isn't too // important; as long as it is in the right general // range, it will usually work. Otherwise, we can use // the generator (hacked doom2.exe) and provide it // with -spechit. //#define DEFAULT_SPECHIT_MAGIC 0x84f968e8 fixed_t tmbbox[4]; mobj_t* tmthing; int tmflags; fixed_t tmx; fixed_t tmy; // If "floatok" true, move would be ok // if within "tmfloorz - tmceilingz". boolean floatok; fixed_t tmfloorz; fixed_t tmceilingz; fixed_t tmdropoffz; // keep track of the line that lowers the ceiling, // so missiles don't explode against sky hack walls line_t* ceilingline; // keep track of special lines as they are hit, // but don't process them until the move is proven valid line_t* spechit[MAXSPECIALCROSS]; int numspechit; // // TELEPORT MOVE // // // PIT_StompThing // boolean PIT_StompThing (mobj_t* thing) { fixed_t blockdist; if (!(thing->flags & MF_SHOOTABLE) ) return true; blockdist = thing->radius + tmthing->radius; if ( abs(thing->x - tmx) >= blockdist || abs(thing->y - tmy) >= blockdist ) { // didn't hit it return true; } // don't clip against self if (thing == tmthing) return true; // monsters don't stomp things except on boss level if ( !tmthing->player && gamemap != 30) return false; P_DamageMobj (thing, tmthing, tmthing, 10000); return true; } // // P_TeleportMove // boolean P_TeleportMove ( mobj_t* thing, fixed_t x, fixed_t y ) { int xl; int xh; int yl; int yh; int bx; int by; subsector_t* newsubsec; // kill anything occupying the position tmthing = thing; tmflags = thing->flags; 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 link the thing into its new position P_UnsetThingPosition (thing); thing->floorz = tmfloorz; thing->ceilingz = tmceilingz; thing->x = x; thing->y = y; P_SetThingPosition (thing); return true; } // // MOVEMENT ITERATOR FUNCTIONS // static void SpechitOverrun(line_t *ld); // // PIT_CheckLine // Adjusts tmfloorz and tmceilingz as lines are contacted // 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; if (P_BoxOnLineSide (tmbbox, ld) != -1) return true; // 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. if (!ld->backsector) return false; // one sided line if (!(tmthing->flags & MF_MISSILE) ) { if ( ld->flags & ML_BLOCKING ) return false; // explicitly blocking everything if ( !tmthing->player && ld->flags & ML_BLOCKMONSTERS ) return false; // block monsters only } // set openrange, opentop, openbottom P_LineOpening (ld); // adjust floor / ceiling heights if (opentop < tmceilingz) { tmceilingz = opentop; ceilingline = ld; } if (openbottom > tmfloorz) tmfloorz = openbottom; if (lowfloor < tmdropoffz) tmdropoffz = lowfloor; // if contacted a special line, add it to the list if (ld->special) { spechit[numspechit] = ld; numspechit++; // fraggle: spechits overrun emulation code from prboom-plus if (numspechit > MAXSPECIALCROSS_ORIGINAL) { SpechitOverrun(ld); } } return true; } // // PIT_CheckThing // boolean PIT_CheckThing (mobj_t* thing) { fixed_t blockdist; boolean solid; int damage; if (!(thing->flags & (MF_SOLID|MF_SPECIAL|MF_SHOOTABLE) )) return true; blockdist = thing->radius + tmthing->radius; if ( abs(thing->x - tmx) >= blockdist || abs(thing->y - tmy) >= blockdist ) { // didn't hit it return true; } // don't clip against self if (thing == tmthing) return true; // check for skulls slamming into things if (tmthing->flags & MF_SKULLFLY) { damage = ((P_Random()%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 if (tmthing->flags & MF_MISSILE) { // 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) ) ) { // Don't hit same species as originator. if (thing == tmthing->target) return true; // sdh: Add deh_species_infighting here. We can override the // "monsters of the same species cant hurt each other" behavior // through dehacked patches if (thing->type != MT_PLAYER && !deh_species_infighting) { // Explode, but do no damage. // Let players missile other players. return false; } } if (! (thing->flags & MF_SHOOTABLE) ) { // didn't do any damage return !(thing->flags & MF_SOLID); } // damage / explode damage = ((P_Random()%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) { solid = thing->flags&MF_SOLID; if (tmflags&MF_PICKUP) { // can remove thing P_TouchSpecialThing (thing, tmthing); } return !solid; } return !(thing->flags & MF_SOLID); } // // 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; tmflags = thing->flags; 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; if ( tmflags & 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; 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 ) { fixed_t oldx; fixed_t oldy; int side; int oldside; line_t* ld; floatok = false; if (!P_CheckPosition (thing, x, y)) return false; // solid wall or thing if ( !(thing->flags & MF_NOCLIP) ) { if (tmceilingz - tmfloorz < thing->height) return false; // doesn't fit floatok = true; if ( !(thing->flags&MF_TELEPORT) &&tmceilingz - thing->z < thing->height) return false; // mobj must lower itself to fit if ( !(thing->flags&MF_TELEPORT) && tmfloorz - thing->z > 24*FRACUNIT ) return false; // too big a step up if ( !(thing->flags&(MF_DROPOFF|MF_FLOAT)) && tmfloorz - tmdropoffz > 24*FRACUNIT ) return false; // don't stand over a dropoff } // the move is ok, // so link the thing into its new position P_UnsetThingPosition (thing); oldx = thing->x; oldy = thing->y; thing->floorz = tmfloorz; thing->ceilingz = tmceilingz; 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--) { // see if the line was crossed ld = spechit[numspechit]; side = P_PointOnLineSide (thing->x, thing->y, ld); oldside = P_PointOnLineSide (oldx, oldy, ld); if (side != oldside) { if (ld->special) P_CrossSpecialLine (ld-lines, oldside, thing); } } } return true; } // // 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? thing->floorz = tmfloorz; thing->ceilingz = tmceilingz; if (onfloor) { // walking monsters rise and fall with the floor thing->z = thing->floorz; } else { // don't adjust a floating monster unless forced to if (thing->z+thing->height > thing->ceilingz) thing->z = thing->ceilingz - thing->height; } if (thing->ceilingz - thing->floorz < thing->height) return false; return true; } // // SLIDE MOVE // Allows the player to slide along any angled walls. // fixed_t bestslidefrac; fixed_t secondslidefrac; line_t* bestslideline; line_t* secondslideline; mobj_t* slidemo; fixed_t tmxmove; fixed_t tmymove; // // P_HitSlideLine // Adjusts the xmove / ymove // so that the next move will slide along the wall. // void P_HitSlideLine (line_t* ld) { int side; angle_t lineangle; angle_t moveangle; angle_t deltaangle; fixed_t movelen; fixed_t newlen; if (ld->slopetype == ST_HORIZONTAL) { tmymove = 0; return; } if (ld->slopetype == ST_VERTICAL) { tmxmove = 0; return; } 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); deltaangle = moveangle-lineangle; if (deltaangle > ANG180) deltaangle += ANG180; // I_Error ("SlideLine: ang>ANG180"); lineangle >>= ANGLETOFINESHIFT; deltaangle >>= ANGLETOFINESHIFT; movelen = P_AproxDistance (tmxmove, tmymove); newlen = FixedMul (movelen, finecosine[deltaangle]); tmxmove = FixedMul (newlen, finecosine[lineangle]); tmymove = FixedMul (newlen, finesine[lineangle]); } // // 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)) { // don't hit the back side return true; } goto isblocking; } // set openrange, opentop, openbottom 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) { secondslidefrac = bestslidefrac; secondslideline = bestslideline; 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. // void P_SlideMove (mobj_t* mo) { fixed_t leadx; fixed_t leady; fixed_t trailx; fixed_t traily; fixed_t newx; fixed_t newy; int hitcount; slidemo = mo; hitcount = 0; retry: if (++hitcount == 3) 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 most have hit the middle, so stairstep stairstep: if (!P_TryMove (mo, mo->x, mo->y + mo->momy)) P_TryMove (mo, mo->x + mo->momx, mo->y); return; } // fudge a bit to make sure it doesn't hit bestslidefrac -= 0x800; if (bestslidefrac > 0) { newx = FixedMul (mo->momx, bestslidefrac); newy = FixedMul (mo->momy, bestslidefrac); if (!P_TryMove (mo, mo->x+newx, mo->y+newy)) goto stairstep; } // Now continue along the wall. // First calculate remainder. bestslidefrac = FRACUNIT-(bestslidefrac+0x800); if (bestslidefrac > FRACUNIT) bestslidefrac = FRACUNIT; if (bestslidefrac <= 0) return; tmxmove = FixedMul (mo->momx, bestslidefrac); tmymove = FixedMul (mo->momy, bestslidefrac); P_HitSlideLine (bestslideline); // clip the moves mo->momx = tmxmove; mo->momy = tmymove; if (!P_TryMove (mo, mo->x+tmxmove, mo->y+tmymove)) { goto retry; } } // // P_LineAttack // mobj_t* linetarget; // who got hit (or NULL) mobj_t* shootthing; // Height if not aiming up or down // ???: use slope for monsters? fixed_t shootz; int la_damage; fixed_t attackrange; fixed_t aimslope; // slopes to top and bottom of target extern fixed_t topslope; extern 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->backsector == NULL || li->frontsector->floorheight != li->backsector->floorheight) { slope = FixedDiv (openbottom - shootz , dist); if (slope > bottomslope) bottomslope = slope; } if (li->backsector == NULL || 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 // 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; line_t* li; mobj_t* th; fixed_t slope; fixed_t dist; fixed_t thingtopslope; fixed_t thingbottomslope; if (in->isaline) { li = in->d.line; if (li->special) P_ShootSpecialLine (shootthing, li); if ( !(li->flags & ML_TWOSIDED) ) goto hitline; // crosses a two sided line P_LineOpening (li); dist = FixedMul (attackrange, in->frac); // e6y: emulation of missed back side on two-sided lines. // backsector can be NULL when emulating missing back side. if (li->backsector == NULL) { slope = FixedDiv (openbottom - shootz , dist); if (slope > aimslope) goto hitline; slope = FixedDiv (opentop - shootz , dist); if (slope < aimslope) goto hitline; } else { if (li->frontsector->floorheight != li->backsector->floorheight) { slope = FixedDiv (openbottom - shootz , dist); if (slope > aimslope) goto hitline; } if (li->frontsector->ceilingheight != li->backsector->ceilingheight) { slope = FixedDiv (opentop - shootz , dist); if (slope < aimslope) goto hitline; } } // shot continues return true; // hit line hitline: // 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) 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 ) { fixed_t x2; fixed_t y2; t1 = P_SubstNullMobj(t1); 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; 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); // can't use for than one special line in a row return false; } // // 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]; P_PathTraverse ( x1, y1, x2, y2, PT_ADDLINES, PTR_UseTraverse ); } // // RADIUS ATTACK // mobj_t* bombsource; mobj_t* bombspot; 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; if (!(thing->flags & MF_SHOOTABLE) ) return true; // Boss spider and cyborg // take no damage from concussion. if (thing->type == MT_CYBORG || thing->type == MT_SPIDER) return true; dx = abs(thing->x - bombspot->x); dy = 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. // boolean crushchange; boolean nofit; // // PIT_ChangeSector // boolean PIT_ChangeSector (mobj_t* thing) { mobj_t* mo; if (P_ThingHeightClip (thing)) { // keep checking return true; } // crunch bodies to giblets if (thing->health <= 0) { P_SetMobjState (thing, S_GIBS); thing->flags &= ~MF_SOLID; thing->height = 0; thing->radius = 0; // keep checking return true; } // crunch dropped items if (thing->flags & MF_DROPPED) { P_RemoveMobj (thing); // keep checking return true; } if (! (thing->flags & MF_SHOOTABLE) ) { // assume it is bloody gibs or something return true; } nofit = true; if (crushchange && !(leveltime&3) ) { 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); mo->momx = (P_Random() - P_Random ())<<12; mo->momy = (P_Random() - P_Random ())<<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; // 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; } // Code to emulate the behavior of Vanilla Doom when encountering an overrun // of the spechit array. This is by Andrey Budko (e6y) and comes from his // PrBoom plus port. A big thanks to Andrey for this. static void SpechitOverrun(line_t *ld) { static unsigned int baseaddr = 0; unsigned int addr; if (baseaddr == 0) { int p; // This is the first time we have had an overrun. Work out // what base address we are going to use. // Allow a spechit value to be specified on the command line. //! // @category compat // @arg // // Use the specified magic value when emulating spechit overruns. // p = M_CheckParmWithArgs("-spechit", 1); if (p > 0) { M_StrToInt(myargv[p+1], (int *) &baseaddr); } else { baseaddr = DEFAULT_SPECHIT_MAGIC; } } // Calculate address used in doom2.exe addr = baseaddr + (ld - lines) * 0x3E; switch(numspechit) { case 9: case 10: case 11: case 12: tmbbox[numspechit-9] = addr; break; case 13: crushchange = addr; break; case 14: nofit = addr; break; default: fprintf(stderr, "SpechitOverrun: Warning: unable to emulate" "an overrun where numspechit=%i\n", numspechit); break; } } ================================================ FILE: fbdoom/p_maputl.c ================================================ // // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // Copyright(C) 2005, 2006 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. // // 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 #include "m_bbox.h" #include "doomdef.h" #include "doomstat.h" #include "p_local.h" // State. #include "r_state.h" // // P_AproxDistance // Gives an estimation of distance (not exact) // fixed_t P_AproxDistance ( fixed_t dx, fixed_t dy ) { dx = abs(dx); dy = abs(dy); if (dx < dy) return dx+dy-(dx>>1); return dx+dy-(dy>>1); } // // P_PointOnLineSide // Returns 0 or 1 // int P_PointOnLineSide ( fixed_t x, fixed_t y, line_t* line ) { fixed_t dx; fixed_t dy; fixed_t left; fixed_t right; if (!line->dx) { if (x <= line->v1->x) return line->dy > 0; return line->dy < 0; } if (!line->dy) { if (y <= line->v1->y) return line->dx < 0; return line->dx > 0; } dx = (x - line->v1->x); dy = (y - line->v1->y); left = FixedMul ( line->dy>>FRACBITS , dx ); right = FixedMul ( dy , line->dx>>FRACBITS ); if (right < left) return 0; // front side return 1; // back side } // // P_BoxOnLineSide // Considers the line to be infinite // Returns side 0 or 1, -1 if box crosses the line. // int P_BoxOnLineSide ( fixed_t* tmbox, line_t* ld ) { int p1 = 0; int p2 = 0; switch (ld->slopetype) { case ST_HORIZONTAL: p1 = tmbox[BOXTOP] > ld->v1->y; p2 = tmbox[BOXBOTTOM] > ld->v1->y; if (ld->dx < 0) { p1 ^= 1; p2 ^= 1; } break; case ST_VERTICAL: p1 = tmbox[BOXRIGHT] < ld->v1->x; p2 = tmbox[BOXLEFT] < ld->v1->x; if (ld->dy < 0) { p1 ^= 1; p2 ^= 1; } break; case ST_POSITIVE: p1 = P_PointOnLineSide (tmbox[BOXLEFT], tmbox[BOXTOP], ld); p2 = P_PointOnLineSide (tmbox[BOXRIGHT], tmbox[BOXBOTTOM], ld); break; case ST_NEGATIVE: p1 = P_PointOnLineSide (tmbox[BOXRIGHT], tmbox[BOXTOP], ld); p2 = P_PointOnLineSide (tmbox[BOXLEFT], tmbox[BOXBOTTOM], ld); break; } if (p1 == p2) return p1; return -1; } // // P_PointOnDivlineSide // Returns 0 or 1. // int P_PointOnDivlineSide ( fixed_t x, fixed_t y, divline_t* line ) { fixed_t dx; fixed_t dy; fixed_t left; fixed_t right; if (!line->dx) { if (x <= line->x) return line->dy > 0; return line->dy < 0; } if (!line->dy) { if (y <= line->y) return line->dx < 0; return line->dx > 0; } dx = (x - line->x); dy = (y - line->y); // try to quickly decide by looking at sign bits if ( (line->dy ^ line->dx ^ dx ^ dy)&0x80000000 ) { if ( (line->dy ^ dx) & 0x80000000 ) return 1; // (left is negative) return 0; } left = FixedMul ( line->dy>>8, dx>>8 ); right = FixedMul ( dy>>8 , line->dx>>8 ); if (right < left) return 0; // front side return 1; // back side } // // P_MakeDivline // void P_MakeDivline ( 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. // fixed_t P_InterceptVector ( divline_t* v2, divline_t* v1 ) { #if 1 fixed_t frac; fixed_t num; fixed_t den; den = FixedMul (v1->dy>>8,v2->dx) - FixedMul(v1->dx>>8,v2->dy); if (den == 0) return 0; // I_Error ("P_InterceptVector: parallel"); num = FixedMul ( (v1->x - v2->x)>>8 ,v1->dy ) +FixedMul ( (v2->y - v1->y)>>8, v1->dx ); frac = FixedDiv (num , den); return frac; #else // UNUSED, float debug. float frac; float num; float den; float v1x; float v1y; float v1dx; float v1dy; float v2x; float v2y; float v2dx; float v2dy; v1x = (float)v1->x/FRACUNIT; v1y = (float)v1->y/FRACUNIT; v1dx = (float)v1->dx/FRACUNIT; v1dy = (float)v1->dy/FRACUNIT; v2x = (float)v2->x/FRACUNIT; v2y = (float)v2->y/FRACUNIT; v2dx = (float)v2->dx/FRACUNIT; v2dy = (float)v2->dy/FRACUNIT; den = v1dy*v2dx - v1dx*v2dy; if (den == 0) return 0; // parallel num = (v1x - v2x)*v1dy + (v2y - v1y)*v1dx; frac = num / den; return frac*FRACUNIT; #endif } // // 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; void P_LineOpening (line_t* linedef) { sector_t* front; sector_t* back; if (linedef->sidenum[1] == -1) { // single sided line openrange = 0; return; } front = linedef->frontsector; back = linedef->backsector; if (front->ceilingheight < back->ceilingheight) opentop = front->ceilingheight; else opentop = back->ceilingheight; if (front->floorheight > back->floorheight) { openbottom = front->floorheight; lowfloor = back->floorheight; } else { openbottom = back->floorheight; lowfloor = front->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) { int blockx; int blocky; if ( ! (thing->flags & MF_NOSECTOR) ) { // inert things don't need to be in blockmap? // unlink from subsector if (thing->snext) thing->snext->sprev = thing->sprev; if (thing->sprev) thing->sprev->snext = thing->snext; else thing->subsector->sector->thinglist = thing->snext; } if ( ! (thing->flags & MF_NOBLOCKMAP) ) { // inert things don't need to be in blockmap // unlink from block map if (thing->bnext) thing->bnext->bprev = thing->bprev; if (thing->bprev) thing->bprev->bnext = thing->bnext; else { blockx = (thing->x - bmaporgx)>>MAPBLOCKSHIFT; blocky = (thing->y - bmaporgy)>>MAPBLOCKSHIFT; if (blockx>=0 && blockx < bmapwidth && blocky>=0 && blocky bnext; } } } } // // P_SetThingPosition // Links a thing into both a block and a subsector // based on it's x y. // Sets thing->subsector properly // void P_SetThingPosition (mobj_t* thing) { subsector_t* ss; sector_t* sec; int blockx; int blocky; mobj_t** link; // link into subsector ss = R_PointInSubsector (thing->x,thing->y); thing->subsector = ss; if ( ! (thing->flags & MF_NOSECTOR) ) { // invisible things don't go into the sector links sec = ss->sector; thing->sprev = NULL; thing->snext = sec->thinglist; if (sec->thinglist) sec->thinglist->sprev = thing; sec->thinglist = thing; } // link into blockmap if ( ! (thing->flags & MF_NOBLOCKMAP) ) { // inert things don't need to be in blockmap blockx = (thing->x - bmaporgx)>>MAPBLOCKSHIFT; blocky = (thing->y - bmaporgy)>>MAPBLOCKSHIFT; if (blockx>=0 && blockx < bmapwidth && blocky>=0 && blocky < bmapheight) { link = &blocklinks[blocky*bmapwidth+blockx]; thing->bprev = NULL; thing->bnext = *link; if (*link) (*link)->bprev = thing; *link = thing; } else { // thing is off the map thing->bnext = 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. // boolean P_BlockLinesIterator ( int x, int y, boolean(*func)(line_t*) ) { int offset; short* list; line_t* ld; if (x<0 || y<0 || x>=bmapwidth || y>=bmapheight) { return true; } offset = y*bmapwidth+x; offset = *(blockmap+offset); for ( list = blockmaplump+offset ; *list != -1 ; list++) { 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 // boolean P_BlockThingsIterator ( int x, int y, boolean(*func)(mobj_t*) ) { mobj_t* mobj; if ( x<0 || y<0 || x>=bmapwidth || y>=bmapheight) { return true; } for (mobj = blocklinks[y*bmapwidth+x] ; mobj ; mobj = mobj->bnext) { if (!func( mobj ) ) return false; } return true; } // // INTERCEPT ROUTINES // intercept_t intercepts[MAXINTERCEPTS]; intercept_t* intercept_p; divline_t trace; boolean earlyout; int ptflags; static void InterceptsOverrun(int num_intercepts, intercept_t *intercept); // // 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. // Returns true if earlyout and a solid line hit. // 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 // try to early out the check if (earlyout && frac < FRACUNIT && !ld->backsector) { return false; // stop checking } intercept_p->frac = frac; intercept_p->isaline = true; intercept_p->d.line = ld; InterceptsOverrun(intercept_p - intercepts, intercept_p); intercept_p++; return true; // continue } // // PIT_AddThingIntercepts // boolean PIT_AddThingIntercepts (mobj_t* thing) { fixed_t x1; fixed_t y1; fixed_t x2; fixed_t y2; int s1; int s2; boolean tracepositive; divline_t dl; fixed_t frac; tracepositive = (trace.dx ^ trace.dy)>0; // check a corner to corner crossection for hit if (tracepositive) { 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 intercept_p->frac = frac; intercept_p->isaline = false; intercept_p->d.thing = thing; InterceptsOverrun(intercept_p - intercepts, intercept_p); intercept_p++; return true; // keep going } // // P_TraverseIntercepts // Returns true if the traverser function returns true // for all lines. // boolean P_TraverseIntercepts ( traverser_t func, fixed_t maxfrac ) { int count; fixed_t dist; intercept_t* scan; intercept_t* in; count = intercept_p - intercepts; in = 0; // shut up compiler warning while (count--) { dist = INT_MAX; for (scan = intercepts ; scanfrac < dist) { dist = scan->frac; in = scan; } } if (dist > maxfrac) return true; // checked everything in range #if 0 // UNUSED { // don't check these yet, there may be others inserted in = scan = intercepts; for ( scan = intercepts ; scanfrac > maxfrac) *in++ = *scan; intercept_p = in; return false; } #endif if ( !func (in) ) return false; // don't bother going farther in->frac = INT_MAX; } return true; // everything was traversed } extern fixed_t bulletslope; // Intercepts Overrun emulation, from PrBoom-plus. // Thanks to Andrey Budko (entryway) for researching this and his // implementation of Intercepts Overrun emulation in PrBoom-plus // which this is based on. typedef struct { int len; void *addr; boolean int16_array; } intercepts_overrun_t; // Intercepts memory table. This is where various variables are located // in memory in Vanilla Doom. When the intercepts table overflows, we // need to write to them. // // Almost all of the values to overwrite are 32-bit integers, except for // playerstarts, which is effectively an array of 16-bit integers and // must be treated differently. static intercepts_overrun_t intercepts_overrun[] = { {4, NULL, false}, {4, NULL, /* &earlyout, */ false}, {4, NULL, /* &intercept_p, */ false}, {4, &lowfloor, false}, {4, &openbottom, false}, {4, &opentop, false}, {4, &openrange, false}, {4, NULL, false}, {120, NULL, /* &activeplats, */ false}, {8, NULL, false}, {4, &bulletslope, false}, {4, NULL, /* &swingx, */ false}, {4, NULL, /* &swingy, */ false}, {4, NULL, false}, {40, &playerstarts, true}, {4, NULL, /* &blocklinks, */ false}, {4, &bmapwidth, false}, {4, NULL, /* &blockmap, */ false}, {4, &bmaporgx, false}, {4, &bmaporgy, false}, {4, NULL, /* &blockmaplump, */ false}, {4, &bmapheight, false}, {0, NULL, false}, }; // Overwrite a specific memory location with a value. static void InterceptsMemoryOverrun(int location, int value) { int i, offset; int index; void *addr; i = 0; offset = 0; // Search down the array until we find the right entry while (intercepts_overrun[i].len != 0) { if (offset + intercepts_overrun[i].len > location) { addr = intercepts_overrun[i].addr; // Write the value to the memory location. // 16-bit and 32-bit values are written differently. if (addr != NULL) { if (intercepts_overrun[i].int16_array) { index = (location - offset) / 2; ((short *) addr)[index] = value & 0xffff; ((short *) addr)[index + 1] = (value >> 16) & 0xffff; } else { index = (location - offset) / 4; ((int *) addr)[index] = value; } } break; } offset += intercepts_overrun[i].len; ++i; } } // Emulate overruns of the intercepts[] array. static void InterceptsOverrun(int num_intercepts, intercept_t *intercept) { int location; if (num_intercepts <= MAXINTERCEPTS_ORIGINAL) { // No overrun return; } location = (num_intercepts - MAXINTERCEPTS_ORIGINAL - 1) * 12; // Overwrite memory that is overwritten in Vanilla Doom, using // the values from the intercept structure. // // Note: the ->d.{thing,line} member should really have its // address translated into the correct address value for // Vanilla Doom. InterceptsMemoryOverrun(location, intercept->frac); InterceptsMemoryOverrun(location + 4, intercept->isaline); InterceptsMemoryOverrun(location + 8, (int) intercept->d.thing); } // // 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. // boolean P_PathTraverse ( fixed_t x1, fixed_t y1, fixed_t x2, fixed_t y2, int flags, boolean (*trav) (intercept_t *)) { fixed_t xt1; fixed_t yt1; fixed_t xt2; fixed_t yt2; fixed_t xstep; fixed_t ystep; fixed_t partial; fixed_t xintercept; fixed_t yintercept; int mapx; int mapy; int mapxstep; int mapystep; int count; earlyout = flags & PT_EARLYOUT; validcount++; intercept_p = intercepts; if ( ((x1-bmaporgx)&(MAPBLOCKSIZE-1)) == 0) x1 += FRACUNIT; // don't side exactly on a line if ( ((y1-bmaporgy)&(MAPBLOCKSIZE-1)) == 0) 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,abs(x2-x1)); } else if (xt2 < xt1) { mapxstep = -1; partial = (x1>>MAPBTOFRAC)&(FRACUNIT-1); ystep = FixedDiv (y2-y1,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,abs(y2-y1)); } else if (yt2 < yt1) { mapystep = -1; partial = (y1>>MAPBTOFRAC)&(FRACUNIT-1); xstep = FixedDiv (x2-x1,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 ); } ================================================ FILE: fbdoom/p_mobj.c ================================================ // // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // 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. // // DESCRIPTION: // Moving object handling. Spawn functions. // #include #include "i_system.h" #include "z_zone.h" #include "m_random.h" #include "doomdef.h" #include "p_local.h" #include "sounds.h" #include "st_stuff.h" #include "hu_stuff.h" #include "s_sound.h" #include "doomstat.h" void G_PlayerReborn (int player); void P_SpawnMapThing (mapthing_t* mthing); // // P_SetMobjState // Returns true if the mobj is still present. // int test; boolean P_SetMobjState ( mobj_t* mobj, statenum_t state ) { state_t* st; do { if (state == S_NULL) { mobj->state = (state_t *) S_NULL; P_RemoveMobj (mobj); return false; } 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.acp1) st->action.acp1(mobj); state = st->nextstate; } while (!mobj->tics); return true; } // // 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()&3; if (mo->tics < 1) mo->tics = 1; mo->flags &= ~MF_MISSILE; if (mo->info->deathsound) S_StartSound (mo, mo->info->deathsound); } // // P_XYMovement // #define STOPSPEED 0x1000 #define FRICTION 0xe800 void P_XYMovement (mobj_t* mo) { fixed_t ptryx; fixed_t ptryy; player_t* player; fixed_t xmove; fixed_t ymove; if (!mo->momx && !mo->momy) { if (mo->flags & MF_SKULLFLY) { // the skull slammed into something mo->flags &= ~MF_SKULLFLY; mo->momx = mo->momy = 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; do { if (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; } if (!P_TryMove (mo, ptryx, ptryy)) { // blocked move if (mo->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) { // Hack to prevent missiles exploding // against the sky. // Does not handle sky floors. P_RemoveMobj (mo); return; } P_ExplodeMissile (mo); } else mo->momx = mo->momy = 0; } } while (xmove || ymove); // slow down if (player && player->cheats & CF_NOMOMENTUM) { // debug option for no sliding at all mo->momx = mo->momy = 0; return; } if (mo->flags & (MF_MISSILE | MF_SKULLFLY) ) return; // no friction for missiles ever if (mo->z > mo->floorz) return; // no friction when airborne if (mo->flags & MF_CORPSE) { // do not stop sliding // if halfway off a step with some momentum if (mo->momx > FRACUNIT/4 || mo->momx < -FRACUNIT/4 || mo->momy > FRACUNIT/4 || mo->momy < -FRACUNIT/4) { if (mo->floorz != mo->subsector->sector->floorheight) return; } } if (mo->momx > -STOPSPEED && mo->momx < STOPSPEED && mo->momy > -STOPSPEED && mo->momy < STOPSPEED && (!player || (player->cmd.forwardmove== 0 && player->cmd.sidemove == 0 ) ) ) { // if in a walking frame, stop moving if ( player&&(unsigned)((player->mo->state - states)- S_PLAY_RUN1) < 4) P_SetMobjState (player->mo, S_PLAY); mo->momx = 0; mo->momy = 0; } else { mo->momx = FixedMul (mo->momx, FRICTION); mo->momy = FixedMul (mo->momy, FRICTION); } } // // P_ZMovement // void P_ZMovement (mobj_t* mo) { fixed_t dist; fixed_t delta; // check for smooth step up if (mo->player && mo->z < mo->floorz) { mo->player->viewheight -= mo->floorz-mo->z; mo->player->deltaviewheight = (VIEWHEIGHT - mo->player->viewheight)>>3; } // adjust height mo->z += mo->momz; if ( mo->flags & MF_FLOAT && mo->target) { // float down towards target if too close if ( !(mo->flags & MF_SKULLFLY) && !(mo->flags & MF_INFLOAT) ) { dist = P_AproxDistance (mo->x - mo->target->x, mo->y - mo->target->y); delta =(mo->target->z + (mo->height>>1)) - mo->z; if (delta<0 && dist < -(delta*3) ) mo->z -= FLOATSPEED; else if (delta>0 && dist < (delta*3) ) mo->z += 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. // Note that this only applies to original Doom 1 or Doom2 demos - not // Final Doom and Ultimate Doom. So we test demo_compatibility *and* // gamemission. (Note we assume that Doom1 is always Ult Doom, which // seems to hold for most published demos.) // // fraggle - cph got the logic here slightly wrong. There are three // versions of Doom 1.9: // // * The version used in registered doom 1.9 + doom2 - no bounce // * The version used in ultimate doom - has bounce // * The version used in final doom - has bounce // // So we need to check that this is either retail or commercial // (but not doom2) int correct_lost_soul_bounce = gameversion >= exe_ultimate; if (correct_lost_soul_bounce && mo->flags & MF_SKULLFLY) { // the skull slammed into something mo->momz = -mo->momz; } if (mo->momz < 0) { if (mo->player && 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; S_StartSound (mo, sfx_oof); } mo->momz = 0; } mo->z = mo->floorz; // cph 2001/05/26 - // See lost soul bouncing comment above. We need this here for bug // compatibility with original Doom2 v1.9 - if a soul is charging and // hit by a raising floor this incorrectly reverses its Y momentum. // if (!correct_lost_soul_bounce && mo->flags & MF_SKULLFLY) mo->momz = -mo->momz; if ( (mo->flags & MF_MISSILE) && !(mo->flags & MF_NOCLIP) ) { P_ExplodeMissile (mo); return; } } else if (! (mo->flags & MF_NOGRAVITY) ) { if (mo->momz == 0) mo->momz = -GRAVITY*2; else mo->momz -= GRAVITY; } if (mo->z + mo->height > mo->ceilingz) { // hit the ceiling if (mo->momz > 0) mo->momz = 0; { mo->z = mo->ceilingz - mo->height; } if (mo->flags & MF_SKULLFLY) { // the skull slammed into something mo->momz = -mo->momz; } if ( (mo->flags & MF_MISSILE) && !(mo->flags & MF_NOCLIP) ) { P_ExplodeMissile (mo); return; } } } // // P_NightmareRespawn // 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; // somthing is occupying it's 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; // spawn it 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; mo->reactiontime = 18; // remove the old monster, P_RemoveMobj (mobj); } // // P_MobjThinker // void P_MobjThinker (mobj_t* mobj) { // momentum movement if (mobj->momx || mobj->momy || (mobj->flags&MF_SKULLFLY) ) { P_XYMovement (mobj); // FIXME: decent NOP/NULL/Nil function pointer please. if (mobj->thinker.function.acv == (actionf_v) (-1)) return; // mobj was removed } if ( (mobj->z != mobj->floorz) || mobj->momz ) { P_ZMovement (mobj); // FIXME: decent NOP/NULL/Nil function pointer please. if (mobj->thinker.function.acv == (actionf_v) (-1)) return; // mobj was removed } // 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*TICRATE) return; if ( leveltime&31 ) return; if (P_Random () > 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; mobj->flags = info->flags; mobj->health = info->spawnhealth; if (gameskill != sk_nightmare) mobj->reactiontime = info->reactiontime; mobj->lastlook = P_Random () % 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; // set subsector and/or block links P_SetThingPosition (mobj); mobj->floorz = mobj->subsector->sector->floorheight; mobj->ceilingz = mobj->subsector->sector->ceilingheight; if (z == ONFLOORZ) mobj->z = mobj->floorz; else if (z == ONCEILINGZ) mobj->z = mobj->ceilingz - mobj->info->height; else mobj->z = z; mobj->thinker.function.acp1 = (actionf_p1)P_MobjThinker; P_AddThinker (&mobj->thinker); return mobj; } // // P_RemoveMobj // mapthing_t itemrespawnque[ITEMQUESIZE]; int itemrespawntime[ITEMQUESIZE]; int iquehead; int iquetail; 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); // stop any playing sound S_StopSound (mobj); // free block P_RemoveThinker ((thinker_t*)mobj); } // // P_RespawnSpecials // void P_RespawnSpecials (void) { fixed_t x; fixed_t y; fixed_t z; subsector_t* ss; mobj_t* mo; mapthing_t* mthing; int i; // only respawn items in deathmatch if (deathmatch != 2) return; // // nothing left to respawn? if (iquehead == iquetail) return; // wait at least 30 seconds if (leveltime - itemrespawntime[iquetail] < 30*TICRATE) return; mthing = &itemrespawnque[iquetail]; x = mthing->x << 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 for (i=0 ; i< NUMMOBJTYPES ; i++) { if (mthing->type == mobjinfo[i].doomednum) break; } // 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 que 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. // void P_SpawnPlayer (mapthing_t* mthing) { player_t* p; fixed_t x; fixed_t y; fixed_t z; mobj_t* mobj; int i; if (mthing->type == 0) { return; } // not playing? if (!playeringame[mthing->type-1]) return; p = &players[mthing->type-1]; if (p->playerstate == PST_REBORN) G_PlayerReborn (mthing->type-1); x = mthing->x << FRACBITS; y = mthing->y << FRACBITS; z = ONFLOORZ; mobj = P_SpawnMobj (x,y,z, MT_PLAYER); // set color translations for player sprites if (mthing->type > 1) mobj->flags |= (mthing->type-1)<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; // setup gun psprite P_SetupPsprites (p); // give all cards in death match mode if (deathmatch) for (i=0 ; icards[i] = true; if (mthing->type-1 == consoleplayer) { // wake up the status bar ST_Start (); // wake up the heads up text HU_Start (); } } // // P_SpawnMapThing // The fields of the mapthing should // already be in host byte order. // void P_SpawnMapThing (mapthing_t* mthing) { int i; int bit; mobj_t* mobj; fixed_t x; fixed_t y; fixed_t z; // count deathmatch start positions if (mthing->type == 11) { if (deathmatch_p < &deathmatchstarts[10]) { memcpy (deathmatch_p, mthing, sizeof(*mthing)); deathmatch_p++; } return; } if (mthing->type <= 0) { // Thing type 0 is actually "player -1 start". // For some reason, Vanilla Doom accepts/ignores this. return; } // check for players specially if (mthing->type <= 4) { // save spots for respawning in network games playerstarts[mthing->type-1] = *mthing; if (!deathmatch) P_SpawnPlayer (mthing); return; } // check for apropriate skill level if (!netgame && (mthing->options & 16) ) return; if (gameskill == sk_baby) bit = 1; else if (gameskill == sk_nightmare) bit = 4; else bit = 1<<(gameskill-1); if (!(mthing->options & bit) ) return; // find which type to spawn for (i=0 ; i< NUMMOBJTYPES ; i++) if (mthing->type == mobjinfo[i].doomednum) break; if (i==NUMMOBJTYPES) I_Error ("P_SpawnMapThing: Unknown type %i at (%i, %i)", mthing->type, mthing->x, mthing->y); // 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 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 () % mobj->tics); if (mobj->flags & MF_COUNTKILL) totalkills++; if (mobj->flags & MF_COUNTITEM) totalitems++; mobj->angle = ANG45 * (mthing->angle/45); if (mthing->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; z += ((P_Random()-P_Random())<<10); th = P_SpawnMobj (x,y,z, MT_PUFF); th->momz = FRACUNIT; th->tics -= P_Random()&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; z += ((P_Random()-P_Random())<<10); th = P_SpawnMobj (x,y,z, MT_BLOOD); th->momz = FRACUNIT*2; th->tics -= P_Random()&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()&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); if (!P_TryMove (th, th->x, th->y)) P_ExplodeMissile (th); } // Certain functions assume that a mobj_t pointer is non-NULL, // causing a crash in some situations where it is NULL. Vanilla // Doom did not crash because of the lack of proper memory // protection. This function substitutes NULL pointers for // pointers to a dummy mobj, to avoid a crash. mobj_t *P_SubstNullMobj(mobj_t *mobj) { if (mobj == NULL) { static mobj_t dummy_mobj; dummy_mobj.x = 0; dummy_mobj.y = 0; dummy_mobj.z = 0; dummy_mobj.flags = 0; mobj = &dummy_mobj; } return mobj; } // // 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); 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) an += (P_Random()-P_Random())<<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; angle_t an; fixed_t x; fixed_t y; fixed_t z; fixed_t slope; // see which target is to be aimed at an = source->angle; slope = P_AimLineAttack (source, an, 16*64*FRACUNIT); if (!linetarget) { an += 1<<26; slope = P_AimLineAttack (source, an, 16*64*FRACUNIT); if (!linetarget) { an -= 2<<26; slope = P_AimLineAttack (source, an, 16*64*FRACUNIT); } if (!linetarget) { an = source->angle; slope = 0; } } 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); 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); } ================================================ FILE: fbdoom/p_mobj.h ================================================ // // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // 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. // // 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 // typedef enum { // Call P_SpecialThing when touched. MF_SPECIAL = 1, // Blocks. MF_SOLID = 2, // Can be hit. MF_SHOOTABLE = 4, // Don't use the sector links (invisible but touchable). MF_NOSECTOR = 8, // Don't use the blocklinks (inert but displayable) MF_NOBLOCKMAP = 16, // Not to be activated by sound, deaf monster. MF_AMBUSH = 32, // Will try to attack right back. MF_JUSTHIT = 64, // Will take at least one step before attacking. MF_JUSTATTACKED = 128, // On level spawning (initial position), // hang from ceiling instead of stand on floor. MF_SPAWNCEILING = 256, // Don't apply gravity (every tic), // that is, object will float, keeping current height // or changing it actively. MF_NOGRAVITY = 512, // Movement flags. // This allows jumps from high places. MF_DROPOFF = 0x400, // For players, will pick up items. MF_PICKUP = 0x800, // Player cheat. ??? MF_NOCLIP = 0x1000, // Player: keep info about sliding along walls. MF_SLIDE = 0x2000, // Allow moves to any height, no gravity. // For active floaters, e.g. cacodemons, pain elementals. MF_FLOAT = 0x4000, // Don't cross lines // ??? or look at heights on teleport. MF_TELEPORT = 0x8000, // Don't hit same species, explode on block. // Player missiles as well as fireballs of various kinds. MF_MISSILE = 0x10000, // Dropped by a demon, not level spawned. // E.g. ammo clips dropped by dying former humans. MF_DROPPED = 0x20000, // Use fuzzy draw (shadow demons or spectres), // temporary player invisibility powerup. MF_SHADOW = 0x40000, // Flag: don't bleed when shot (use puff), // barrels and shootable furniture shall not bleed. MF_NOBLOOD = 0x80000, // Don't stop moving halfway off a step, // that is, have dead bodies slide down all the way. MF_CORPSE = 0x100000, // Floating to a height for a move, ??? // don't auto float to target's height. MF_INFLOAT = 0x200000, // On kill, count this enemy object // towards intermission kill total. // Happy gathering. MF_COUNTKILL = 0x400000, // On picking up, count this item object // towards intermission item total. MF_COUNTITEM = 0x800000, // Special handling: skull in flight. // Neither a cacodemon nor a missile. MF_SKULLFLY = 0x1000000, // Don't spawn this object // in death match mode (e.g. key cards). MF_NOTDMATCH = 0x2000000, // 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 MF_TRANSLATION = 0xc000000, // Hmm ???. MF_TRANSSHIFT = 26 } mobjflag_t; // Map Object definition. 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; //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; struct subsector_s* subsector; // The closest interval over all contacted Sectors. fixed_t floorz; fixed_t ceilingz; // 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; int flags; int health; // Movement direction, movement generation (zig-zagging). int movedir; // 0-7 int movecount; // when 0, select a new dir // 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. int reactiontime; // If >0, the target will be chased // no matter what (even if shot) int threshold; // Additional info record for player avatars only. // Only valid if type == MT_PLAYER struct player_s* player; // Player number last looked for. int lastlook; // For nightmare respawn. mapthing_t spawnpoint; // Thing being chased/attacked for tracers. struct mobj_s* tracer; } mobj_t; #endif ================================================ FILE: fbdoom/p_plats.c ================================================ // // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // 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. // // DESCRIPTION: // Plats (i.e. elevator platforms) code, raising/lowering. // #include #include "i_system.h" #include "z_zone.h" #include "m_random.h" #include "doomdef.h" #include "p_local.h" #include "s_sound.h" // State. #include "doomstat.h" #include "r_state.h" // Data. #include "sounds.h" plat_t* activeplats[MAXPLATS]; // // Move a plat up and down // void T_PlatRaise(plat_t* plat) { result_e res; switch(plat->status) { case up: res = T_MovePlane(plat->sector, plat->speed, plat->high, plat->crush,0,1); if (plat->type == raiseAndChange || plat->type == raiseToNearestAndChange) { if (!(leveltime&7)) S_StartSound(&plat->sector->soundorg, sfx_stnmov); } if (res == crushed && (!plat->crush)) { plat->count = plat->wait; plat->status = down; S_StartSound(&plat->sector->soundorg, sfx_pstart); } else { if (res == pastdest) { plat->count = plat->wait; plat->status = waiting; S_StartSound(&plat->sector->soundorg, sfx_pstop); switch(plat->type) { case blazeDWUS: case downWaitUpStay: P_RemoveActivePlat(plat); break; case raiseAndChange: case raiseToNearestAndChange: P_RemoveActivePlat(plat); break; default: break; } } } break; case down: res = T_MovePlane(plat->sector,plat->speed,plat->low,false,0,-1); if (res == pastdest) { plat->count = plat->wait; plat->status = waiting; S_StartSound(&plat->sector->soundorg,sfx_pstop); } break; case waiting: if (!--plat->count) { if (plat->sector->floorheight == plat->low) plat->status = up; else plat->status = down; S_StartSound(&plat->sector->soundorg,sfx_pstart); } case in_stasis: break; } } // // Do Platforms // "amount" is only used for SOME platforms. // 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; default: break; } while ((secnum = P_FindSectorFromLineTag(line,secnum)) >= 0) { sec = §ors[secnum]; if (sec->specialdata) continue; // Find lowest & highest floors around sector rtn = 1; plat = Z_Malloc( sizeof(*plat), PU_LEVSPEC, 0); P_AddThinker(&plat->thinker); plat->type = type; plat->sector = sec; plat->sector->specialdata = plat; plat->thinker.function.acp1 = (actionf_p1) T_PlatRaise; plat->crush = false; plat->tag = line->tag; 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; // NO MORE DAMAGE, IF APPLICABLE sec->special = 0; S_StartSound(&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(&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 = TICRATE*PLATWAIT; plat->status = down; S_StartSound(&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 = TICRATE*PLATWAIT; plat->status = down; S_StartSound(&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 = TICRATE*PLATWAIT; plat->status = P_Random()&1; S_StartSound(&sec->soundorg,sfx_pstart); break; } P_AddActivePlat(plat); } return rtn; } void P_ActivateInStasis(int tag) { int i; for (i = 0;i < MAXPLATS;i++) if (activeplats[i] && (activeplats[i])->tag == tag && (activeplats[i])->status == in_stasis) { (activeplats[i])->status = (activeplats[i])->oldstatus; (activeplats[i])->thinker.function.acp1 = (actionf_p1) T_PlatRaise; } } void EV_StopPlat(line_t* line) { int j; for (j = 0;j < MAXPLATS;j++) if (activeplats[j] && ((activeplats[j])->status != in_stasis) && ((activeplats[j])->tag == line->tag)) { (activeplats[j])->oldstatus = (activeplats[j])->status; (activeplats[j])->status = in_stasis; (activeplats[j])->thinker.function.acv = (actionf_v)NULL; } } void P_AddActivePlat(plat_t* plat) { int i; for (i = 0;i < MAXPLATS;i++) if (activeplats[i] == NULL) { activeplats[i] = plat; return; } I_Error ("P_AddActivePlat: no more plats!"); } void P_RemoveActivePlat(plat_t* plat) { int i; for (i = 0;i < MAXPLATS;i++) if (plat == activeplats[i]) { (activeplats[i])->sector->specialdata = NULL; P_RemoveThinker(&(activeplats[i])->thinker); activeplats[i] = NULL; return; } I_Error ("P_RemoveActivePlat: can't find plat!"); } ================================================ FILE: fbdoom/p_pspr.c ================================================ // // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // 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. // // DESCRIPTION: // Weapon sprite animation, weapon objects. // Action functions for weapons. // #include "doomdef.h" #include "d_event.h" #include "deh_misc.h" #include "m_random.h" #include "p_local.h" #include "s_sound.h" // State. #include "doomstat.h" // Data. #include "sounds.h" #include "p_pspr.h" #define LOWERSPEED FRACUNIT*6 #define RAISESPEED FRACUNIT*6 #define WEAPONBOTTOM 128*FRACUNIT #define WEAPONTOP 32*FRACUNIT // // P_SetPsprite // void P_SetPsprite ( player_t* player, int position, statenum_t stnum ) { pspdef_t* psp; state_t* state; psp = &player->psprites[position]; do { 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.acp2) { state->action.acp2(player, psp); if (!psp->state) break; } stnum = psp->state->nextstate; } while (!psp->tics); // an initial state of 0 could cycle through } // // P_CalcSwing // fixed_t swingx; fixed_t swingy; void P_CalcSwing (player_t* player) { fixed_t swing; int angle; // OPTIMIZE: tablify this. // A LUT would allow for different modes, // and add flexibility. swing = player->bob; angle = (FINEANGLES/70*leveltime)&FINEMASK; swingx = FixedMul ( swing, finesine[angle]); angle = (FINEANGLES/70*leveltime+FINEANGLES/2)&FINEMASK; swingy = -FixedMul ( swingx, finesine[angle]); } // // P_BringUpWeapon // Starts bringing the pending weapon up // from the bottom of the screen. // Uses player // 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; player->psprites[ps_weapon].sy = WEAPONBOTTOM; P_SetPsprite (player, ps_weapon, newstate); } // // P_CheckAmmo // Returns true if there is enough ammo to shoot. // If not, selects the next weapon to use. // boolean P_CheckAmmo (player_t* player) { ammotype_t ammo; int count; ammo = weaponinfo[player->readyweapon].ammo; // Minimal amount for one shot varies. if (player->readyweapon == wp_bfg) count = deh_bfg_cells_per_shot; else if (player->readyweapon == wp_supershotgun) count = 2; // Double barrel. else count = 1; // Regular. // 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. // Preferences are set here. do { if (player->weaponowned[wp_plasma] && player->ammo[am_cell] && (gamemode != shareware) ) { player->pendingweapon = wp_plasma; } else if (player->weaponowned[wp_supershotgun] && player->ammo[am_shell]>2 && (gamemode == commercial) ) { player->pendingweapon = wp_supershotgun; } else if (player->weaponowned[wp_chaingun] && player->ammo[am_clip]) { player->pendingweapon = wp_chaingun; } else if (player->weaponowned[wp_shotgun] && player->ammo[am_shell]) { player->pendingweapon = wp_shotgun; } else if (player->ammo[am_clip]) { player->pendingweapon = wp_pistol; } else if (player->weaponowned[wp_chainsaw]) { player->pendingweapon = wp_chainsaw; } else if (player->weaponowned[wp_missile] && player->ammo[am_misl]) { player->pendingweapon = wp_missile; } else if (player->weaponowned[wp_bfg] && player->ammo[am_cell]>40 && (gamemode != shareware) ) { player->pendingweapon = wp_bfg; } else { // If everything fails. player->pendingweapon = wp_fist; } } while (player->pendingweapon == wp_nochange); // Now set appropriate weapon overlay. P_SetPsprite (player, ps_weapon, weaponinfo[player->readyweapon].downstate); return false; } // // P_FireWeapon. // 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 ) { statenum_t newstate; int angle; // 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 allready be validated) 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 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 ) { P_CheckAmmo (player); #if 0 if (player->ammo[am_shell]<2) P_SetPsprite (player, ps_weapon, S_DSNR1); #endif } // // 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; // don't bring weapon back up return; } // 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); } // // A_GunFlash // void A_GunFlash ( player_t* player, pspdef_t* psp ) { P_SetMobjState (player->mo, S_PLAY_ATK2); P_SetPsprite (player,ps_flash,weaponinfo[player->readyweapon].flashstate); } // // WEAPON ATTACKS // // // A_Punch // void A_Punch ( player_t* player, pspdef_t* psp ) { angle_t angle; int damage; int slope; damage = (P_Random ()%10+1)<<1; if (player->powers[pw_strength]) damage *= 10; angle = player->mo->angle; angle += (P_Random()-P_Random())<<18; slope = P_AimLineAttack (player->mo, angle, MELEERANGE); P_LineAttack (player->mo, angle, MELEERANGE, slope, damage); // turn to face target if (linetarget) { S_StartSound (player->mo, sfx_punch); player->mo->angle = R_PointToAngle2 (player->mo->x, player->mo->y, linetarget->x, linetarget->y); } } // // A_Saw // void A_Saw ( player_t* player, pspdef_t* psp ) { angle_t angle; int damage; int slope; damage = 2*(P_Random ()%10+1); angle = player->mo->angle; angle += (P_Random()-P_Random())<<18; // use meleerange + 1 se the puff doesn't skip the flash slope = P_AimLineAttack (player->mo, angle, MELEERANGE+1); 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 ((signed int) (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; } // Doom does not check the bounds of the ammo array. As a result, // it is possible to use an ammo type > 4 that overflows into the // maxammo array and affects that instead. Through dehacked, for // example, it is possible to make a weapon that decreases the max // number of ammo for another weapon. Emulate this. static void DecreaseAmmo(player_t *player, int ammonum, int amount) { if (ammonum < NUMAMMO) { player->ammo[ammonum] -= amount; } else { player->maxammo[ammonum - NUMAMMO] -= amount; } } // // A_FireMissile // void A_FireMissile ( player_t* player, pspdef_t* psp ) { DecreaseAmmo(player, weaponinfo[player->readyweapon].ammo, 1); P_SpawnPlayerMissile (player->mo, MT_ROCKET); } // // A_FireBFG // void A_FireBFG ( player_t* player, pspdef_t* psp ) { DecreaseAmmo(player, weaponinfo[player->readyweapon].ammo, deh_bfg_cells_per_shot); P_SpawnPlayerMissile (player->mo, MT_BFG); } // // A_FirePlasma // void A_FirePlasma ( player_t* player, pspdef_t* psp ) { DecreaseAmmo(player, weaponinfo[player->readyweapon].ammo, 1); P_SetPsprite (player, ps_flash, weaponinfo[player->readyweapon].flashstate+(P_Random ()&1) ); P_SpawnPlayerMissile (player->mo, MT_PLASMA); } // // P_BulletSlope // Sets a slope so a near miss is at aproximately // the height of the intended target // fixed_t bulletslope; void P_BulletSlope (mobj_t* mo) { angle_t an; // see which target is to be aimed at an = mo->angle; bulletslope = P_AimLineAttack (mo, an, 16*64*FRACUNIT); if (!linetarget) { an += 1<<26; bulletslope = P_AimLineAttack (mo, an, 16*64*FRACUNIT); if (!linetarget) { an -= 2<<26; bulletslope = P_AimLineAttack (mo, an, 16*64*FRACUNIT); } } } // // P_GunShot // void P_GunShot ( mobj_t* mo, boolean accurate ) { angle_t angle; int damage; damage = 5*(P_Random ()%3+1); angle = mo->angle; if (!accurate) angle += (P_Random()-P_Random())<<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); DecreaseAmmo(player, weaponinfo[player->readyweapon].ammo, 1); P_SetPsprite (player, ps_flash, weaponinfo[player->readyweapon].flashstate); 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); DecreaseAmmo(player, weaponinfo[player->readyweapon].ammo, 1); P_SetPsprite (player, ps_flash, weaponinfo[player->readyweapon].flashstate); 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; angle_t angle; int damage; S_StartSound (player->mo, sfx_dshtgn); P_SetMobjState (player->mo, S_PLAY_ATK2); DecreaseAmmo(player, weaponinfo[player->readyweapon].ammo, 2); P_SetPsprite (player, ps_flash, weaponinfo[player->readyweapon].flashstate); P_BulletSlope (player->mo); for (i=0 ; i<20 ; i++) { damage = 5*(P_Random ()%3+1); angle = player->mo->angle; angle += (P_Random()-P_Random())<<19; P_LineAttack (player->mo, angle, MISSILERANGE, bulletslope + ((P_Random()-P_Random())<<5), damage); } } // // A_FireCGun // void A_FireCGun ( player_t* player, pspdef_t* psp ) { S_StartSound (player->mo, sfx_pistol); if (!player->ammo[weaponinfo[player->readyweapon].ammo]) return; P_SetMobjState (player->mo, S_PLAY_ATK2); DecreaseAmmo(player, weaponinfo[player->readyweapon].ammo, 1); P_SetPsprite (player, ps_flash, weaponinfo[player->readyweapon].flashstate + psp->state - &states[S_CHAIN1] ); 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; int j; int damage; angle_t an; // offset angles from its attack angle for (i=0 ; i<40 ; i++) { an = mo->angle - ANG90/2 + ANG90/40*i; // mo->target is the originator (player) // of the missile P_AimLineAttack (mo->target, an, 16*64*FRACUNIT); if (!linetarget) continue; P_SpawnMobj (linetarget->x, linetarget->y, linetarget->z + (linetarget->height>>2), MT_EXTRABFG); damage = 0; for (j=0;j<15;j++) damage += (P_Random()&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) { int i; pspdef_t* psp; state_t* state; psp = &player->psprites[0]; for (i=0 ; istate) ) { // drop tic count and possibly change state // a -1 tic count never changes if (psp->tics != -1) { psp->tics--; if (!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; } ================================================ FILE: fbdoom/p_pspr.h ================================================ // // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // 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. // // 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" // // 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; #endif ================================================ FILE: fbdoom/p_saveg.c ================================================ // // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // 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. // // DESCRIPTION: // Archiving: SaveGame I/O. // #include #include #include "dstrings.h" #include "deh_main.h" #include "i_system.h" #include "z_zone.h" #include "p_local.h" #include "p_saveg.h" // State. #include "doomstat.h" #include "g_game.h" #include "m_misc.h" #include "r_state.h" #define SAVEGAME_EOF 0x1d #define VERSIONSIZE 16 FILE *save_stream; int savegamelength; boolean savegame_error; // Get the filename of a temporary file to write the savegame to. After // the file has been successfully saved, it will be renamed to the // real file. char *P_TempSaveGameFile(void) { static char *filename = NULL; if (filename == NULL) { filename = M_StringJoin(savegamedir, "temp.dsg", NULL); } return filename; } // Get the filename of the save game file to use for the specified slot. char *P_SaveGameFile(int slot) { static char *filename = NULL; static size_t filename_size = 0; char basename[32]; if (filename == NULL) { filename_size = strlen(savegamedir) + 32; filename = malloc(filename_size); } DEH_snprintf(basename, 32, SAVEGAMENAME "%d.dsg", slot); M_snprintf(filename, filename_size, "%s%s", savegamedir, basename); return filename; } // Endian-safe integer read/write functions static byte saveg_read8(void) { byte result; if (fread(&result, 1, 1, save_stream) < 1) { if (!savegame_error) { fprintf(stderr, "saveg_read8: Unexpected end of file while " "reading save game\n"); savegame_error = true; } } return result; } static void saveg_write8(byte value) { if (fwrite(&value, 1, 1, save_stream) < 1) { if (!savegame_error) { fprintf(stderr, "saveg_write8: Error while writing save game\n"); savegame_error = true; } } } static short saveg_read16(void) { int result; result = saveg_read8(); result |= saveg_read8() << 8; return result; } static void saveg_write16(short value) { saveg_write8(value & 0xff); saveg_write8((value >> 8) & 0xff); } static int saveg_read32(void) { int result; result = saveg_read8(); result |= saveg_read8() << 8; result |= saveg_read8() << 16; result |= saveg_read8() << 24; return result; } static void saveg_write32(int value) { saveg_write8(value & 0xff); saveg_write8((value >> 8) & 0xff); saveg_write8((value >> 16) & 0xff); saveg_write8((value >> 24) & 0xff); } // Pad to 4-byte boundaries static void saveg_read_pad(void) { unsigned long pos; int padding; int i; pos = ftell(save_stream); padding = (4 - (pos & 3)) & 3; for (i=0; ix = saveg_read16(); // short y; str->y = saveg_read16(); // short angle; str->angle = saveg_read16(); // short type; str->type = saveg_read16(); // short options; str->options = saveg_read16(); } static void saveg_write_mapthing_t(mapthing_t *str) { // short x; saveg_write16(str->x); // short y; saveg_write16(str->y); // short angle; saveg_write16(str->angle); // short type; saveg_write16(str->type); // short options; saveg_write16(str->options); } // // actionf_t // static void saveg_read_actionf_t(actionf_t *str) { // actionf_p1 acp1; str->acp1 = saveg_readp(); } static void saveg_write_actionf_t(actionf_t *str) { // actionf_p1 acp1; saveg_writep(str->acp1); } // // think_t // // This is just an actionf_t. // #define saveg_read_think_t saveg_read_actionf_t #define saveg_write_think_t saveg_write_actionf_t // // thinker_t // static void saveg_read_thinker_t(thinker_t *str) { // struct thinker_s* prev; str->prev = saveg_readp(); // struct thinker_s* next; str->next = saveg_readp(); // think_t function; saveg_read_think_t(&str->function); } static void saveg_write_thinker_t(thinker_t *str) { // struct thinker_s* prev; saveg_writep(str->prev); // struct thinker_s* next; saveg_writep(str->next); // think_t function; saveg_write_think_t(&str->function); } // // mobj_t // static void saveg_read_mobj_t(mobj_t *str) { int pl; // thinker_t thinker; saveg_read_thinker_t(&str->thinker); // fixed_t x; str->x = saveg_read32(); // fixed_t y; str->y = saveg_read32(); // fixed_t z; str->z = saveg_read32(); // struct mobj_s* snext; str->snext = saveg_readp(); // struct mobj_s* sprev; str->sprev = saveg_readp(); // angle_t angle; str->angle = saveg_read32(); // spritenum_t sprite; str->sprite = saveg_read_enum(); // int frame; str->frame = saveg_read32(); // struct mobj_s* bnext; str->bnext = saveg_readp(); // struct mobj_s* bprev; str->bprev = saveg_readp(); // struct subsector_s* subsector; str->subsector = saveg_readp(); // fixed_t floorz; str->floorz = saveg_read32(); // fixed_t ceilingz; str->ceilingz = saveg_read32(); // fixed_t radius; str->radius = saveg_read32(); // fixed_t height; str->height = saveg_read32(); // fixed_t momx; str->momx = saveg_read32(); // fixed_t momy; str->momy = saveg_read32(); // fixed_t momz; str->momz = saveg_read32(); // int validcount; str->validcount = saveg_read32(); // mobjtype_t type; str->type = saveg_read_enum(); // mobjinfo_t* info; str->info = saveg_readp(); // int tics; str->tics = saveg_read32(); // state_t* state; str->state = &states[saveg_read32()]; // int flags; str->flags = saveg_read32(); // int health; str->health = saveg_read32(); // int movedir; str->movedir = saveg_read32(); // int movecount; str->movecount = saveg_read32(); // struct mobj_s* target; str->target = saveg_readp(); // int reactiontime; str->reactiontime = saveg_read32(); // int threshold; str->threshold = saveg_read32(); // struct player_s* player; pl = saveg_read32(); if (pl > 0) { str->player = &players[pl - 1]; str->player->mo = str; } else { str->player = NULL; } // int lastlook; str->lastlook = saveg_read32(); // mapthing_t spawnpoint; saveg_read_mapthing_t(&str->spawnpoint); // struct mobj_s* tracer; str->tracer = saveg_readp(); } static void saveg_write_mobj_t(mobj_t *str) { // thinker_t thinker; saveg_write_thinker_t(&str->thinker); // fixed_t x; saveg_write32(str->x); // fixed_t y; saveg_write32(str->y); // fixed_t z; saveg_write32(str->z); // struct mobj_s* snext; saveg_writep(str->snext); // struct mobj_s* sprev; saveg_writep(str->sprev); // angle_t angle; saveg_write32(str->angle); // spritenum_t sprite; saveg_write_enum(str->sprite); // int frame; saveg_write32(str->frame); // struct mobj_s* bnext; saveg_writep(str->bnext); // struct mobj_s* bprev; saveg_writep(str->bprev); // struct subsector_s* subsector; saveg_writep(str->subsector); // fixed_t floorz; saveg_write32(str->floorz); // fixed_t ceilingz; saveg_write32(str->ceilingz); // fixed_t radius; saveg_write32(str->radius); // fixed_t height; saveg_write32(str->height); // fixed_t momx; saveg_write32(str->momx); // fixed_t momy; saveg_write32(str->momy); // fixed_t momz; saveg_write32(str->momz); // int validcount; saveg_write32(str->validcount); // mobjtype_t type; saveg_write_enum(str->type); // mobjinfo_t* info; saveg_writep(str->info); // int tics; saveg_write32(str->tics); // state_t* state; saveg_write32(str->state - states); // int flags; saveg_write32(str->flags); // int health; saveg_write32(str->health); // int movedir; saveg_write32(str->movedir); // int movecount; saveg_write32(str->movecount); // struct mobj_s* target; saveg_writep(str->target); // int reactiontime; saveg_write32(str->reactiontime); // int threshold; saveg_write32(str->threshold); // struct player_s* player; if (str->player) { saveg_write32(str->player - players + 1); } else { saveg_write32(0); } // int lastlook; saveg_write32(str->lastlook); // mapthing_t spawnpoint; saveg_write_mapthing_t(&str->spawnpoint); // struct mobj_s* tracer; saveg_writep(str->tracer); } // // ticcmd_t // static void saveg_read_ticcmd_t(ticcmd_t *str) { // signed char forwardmove; str->forwardmove = saveg_read8(); // signed char sidemove; str->sidemove = saveg_read8(); // short angleturn; str->angleturn = saveg_read16(); // short consistancy; str->consistancy = saveg_read16(); // byte chatchar; str->chatchar = saveg_read8(); // byte buttons; str->buttons = saveg_read8(); } static void saveg_write_ticcmd_t(ticcmd_t *str) { // signed char forwardmove; saveg_write8(str->forwardmove); // signed char sidemove; saveg_write8(str->sidemove); // short angleturn; saveg_write16(str->angleturn); // short consistancy; saveg_write16(str->consistancy); // byte chatchar; saveg_write8(str->chatchar); // byte buttons; saveg_write8(str->buttons); } // // pspdef_t // static void saveg_read_pspdef_t(pspdef_t *str) { int state; // state_t* state; state = saveg_read32(); if (state > 0) { str->state = &states[state]; } else { str->state = NULL; } // int tics; str->tics = saveg_read32(); // fixed_t sx; str->sx = saveg_read32(); // fixed_t sy; str->sy = saveg_read32(); } static void saveg_write_pspdef_t(pspdef_t *str) { // state_t* state; if (str->state) { saveg_write32(str->state - states); } else { saveg_write32(0); } // int tics; saveg_write32(str->tics); // fixed_t sx; saveg_write32(str->sx); // fixed_t sy; saveg_write32(str->sy); } // // player_t // static void saveg_read_player_t(player_t *str) { int i; // mobj_t* mo; str->mo = saveg_readp(); // playerstate_t playerstate; str->playerstate = saveg_read_enum(); // ticcmd_t cmd; saveg_read_ticcmd_t(&str->cmd); // fixed_t viewz; str->viewz = saveg_read32(); // fixed_t viewheight; str->viewheight = saveg_read32(); // fixed_t deltaviewheight; str->deltaviewheight = saveg_read32(); // fixed_t bob; str->bob = saveg_read32(); // int health; str->health = saveg_read32(); // int armorpoints; str->armorpoints = saveg_read32(); // int armortype; str->armortype = saveg_read32(); // int powers[NUMPOWERS]; for (i=0; ipowers[i] = saveg_read32(); } // boolean cards[NUMCARDS]; for (i=0; icards[i] = saveg_read32(); } // boolean backpack; str->backpack = saveg_read32(); // int frags[MAXPLAYERS]; for (i=0; ifrags[i] = saveg_read32(); } // weapontype_t readyweapon; str->readyweapon = saveg_read_enum(); // weapontype_t pendingweapon; str->pendingweapon = saveg_read_enum(); // boolean weaponowned[NUMWEAPONS]; for (i=0; iweaponowned[i] = saveg_read32(); } // int ammo[NUMAMMO]; for (i=0; iammo[i] = saveg_read32(); } // int maxammo[NUMAMMO]; for (i=0; imaxammo[i] = saveg_read32(); } // int attackdown; str->attackdown = saveg_read32(); // int usedown; str->usedown = saveg_read32(); // int cheats; str->cheats = saveg_read32(); // int refire; str->refire = saveg_read32(); // int killcount; str->killcount = saveg_read32(); // int itemcount; str->itemcount = saveg_read32(); // int secretcount; str->secretcount = saveg_read32(); // char* message; str->message = saveg_readp(); // int damagecount; str->damagecount = saveg_read32(); // int bonuscount; str->bonuscount = saveg_read32(); // mobj_t* attacker; str->attacker = saveg_readp(); // int extralight; str->extralight = saveg_read32(); // int fixedcolormap; str->fixedcolormap = saveg_read32(); // int colormap; str->colormap = saveg_read32(); // pspdef_t psprites[NUMPSPRITES]; for (i=0; ipsprites[i]); } // boolean didsecret; str->didsecret = saveg_read32(); } static void saveg_write_player_t(player_t *str) { int i; // mobj_t* mo; saveg_writep(str->mo); // playerstate_t playerstate; saveg_write_enum(str->playerstate); // ticcmd_t cmd; saveg_write_ticcmd_t(&str->cmd); // fixed_t viewz; saveg_write32(str->viewz); // fixed_t viewheight; saveg_write32(str->viewheight); // fixed_t deltaviewheight; saveg_write32(str->deltaviewheight); // fixed_t bob; saveg_write32(str->bob); // int health; saveg_write32(str->health); // int armorpoints; saveg_write32(str->armorpoints); // int armortype; saveg_write32(str->armortype); // int powers[NUMPOWERS]; for (i=0; ipowers[i]); } // boolean cards[NUMCARDS]; for (i=0; icards[i]); } // boolean backpack; saveg_write32(str->backpack); // int frags[MAXPLAYERS]; for (i=0; ifrags[i]); } // weapontype_t readyweapon; saveg_write_enum(str->readyweapon); // weapontype_t pendingweapon; saveg_write_enum(str->pendingweapon); // boolean weaponowned[NUMWEAPONS]; for (i=0; iweaponowned[i]); } // int ammo[NUMAMMO]; for (i=0; iammo[i]); } // int maxammo[NUMAMMO]; for (i=0; imaxammo[i]); } // int attackdown; saveg_write32(str->attackdown); // int usedown; saveg_write32(str->usedown); // int cheats; saveg_write32(str->cheats); // int refire; saveg_write32(str->refire); // int killcount; saveg_write32(str->killcount); // int itemcount; saveg_write32(str->itemcount); // int secretcount; saveg_write32(str->secretcount); // char* message; saveg_writep(str->message); // int damagecount; saveg_write32(str->damagecount); // int bonuscount; saveg_write32(str->bonuscount); // mobj_t* attacker; saveg_writep(str->attacker); // int extralight; saveg_write32(str->extralight); // int fixedcolormap; saveg_write32(str->fixedcolormap); // int colormap; saveg_write32(str->colormap); // pspdef_t psprites[NUMPSPRITES]; for (i=0; ipsprites[i]); } // boolean didsecret; saveg_write32(str->didsecret); } // // ceiling_t // static void saveg_read_ceiling_t(ceiling_t *str) { int sector; // thinker_t thinker; saveg_read_thinker_t(&str->thinker); // ceiling_e type; str->type = saveg_read_enum(); // sector_t* sector; sector = saveg_read32(); str->sector = §ors[sector]; // fixed_t bottomheight; str->bottomheight = saveg_read32(); // fixed_t topheight; str->topheight = saveg_read32(); // fixed_t speed; str->speed = saveg_read32(); // boolean crush; str->crush = saveg_read32(); // int direction; str->direction = saveg_read32(); // int tag; str->tag = saveg_read32(); // int olddirection; str->olddirection = saveg_read32(); } static void saveg_write_ceiling_t(ceiling_t *str) { // thinker_t thinker; saveg_write_thinker_t(&str->thinker); // ceiling_e type; saveg_write_enum(str->type); // sector_t* sector; saveg_write32(str->sector - sectors); // fixed_t bottomheight; saveg_write32(str->bottomheight); // fixed_t topheight; saveg_write32(str->topheight); // fixed_t speed; saveg_write32(str->speed); // boolean crush; saveg_write32(str->crush); // int direction; saveg_write32(str->direction); // int tag; saveg_write32(str->tag); // int olddirection; saveg_write32(str->olddirection); } // // vldoor_t // static void saveg_read_vldoor_t(vldoor_t *str) { int sector; // thinker_t thinker; saveg_read_thinker_t(&str->thinker); // vldoor_e type; str->type = saveg_read_enum(); // sector_t* sector; sector = saveg_read32(); str->sector = §ors[sector]; // fixed_t topheight; str->topheight = saveg_read32(); // fixed_t speed; str->speed = saveg_read32(); // int direction; str->direction = saveg_read32(); // int topwait; str->topwait = saveg_read32(); // int topcountdown; str->topcountdown = saveg_read32(); } static void saveg_write_vldoor_t(vldoor_t *str) { // thinker_t thinker; saveg_write_thinker_t(&str->thinker); // vldoor_e type; saveg_write_enum(str->type); // sector_t* sector; saveg_write32(str->sector - sectors); // fixed_t topheight; saveg_write32(str->topheight); // fixed_t speed; saveg_write32(str->speed); // int direction; saveg_write32(str->direction); // int topwait; saveg_write32(str->topwait); // int topcountdown; saveg_write32(str->topcountdown); } // // floormove_t // static void saveg_read_floormove_t(floormove_t *str) { int sector; // thinker_t thinker; saveg_read_thinker_t(&str->thinker); // floor_e type; str->type = saveg_read_enum(); // boolean crush; str->crush = saveg_read32(); // sector_t* sector; sector = saveg_read32(); str->sector = §ors[sector]; // int direction; str->direction = saveg_read32(); // int newspecial; str->newspecial = saveg_read32(); // short texture; str->texture = saveg_read16(); // fixed_t floordestheight; str->floordestheight = saveg_read32(); // fixed_t speed; str->speed = saveg_read32(); } static void saveg_write_floormove_t(floormove_t *str) { // thinker_t thinker; saveg_write_thinker_t(&str->thinker); // floor_e type; saveg_write_enum(str->type); // boolean crush; saveg_write32(str->crush); // sector_t* sector; saveg_write32(str->sector - sectors); // int direction; saveg_write32(str->direction); // int newspecial; saveg_write32(str->newspecial); // short texture; saveg_write16(str->texture); // fixed_t floordestheight; saveg_write32(str->floordestheight); // fixed_t speed; saveg_write32(str->speed); } // // plat_t // static void saveg_read_plat_t(plat_t *str) { int sector; // thinker_t thinker; saveg_read_thinker_t(&str->thinker); // sector_t* sector; sector = saveg_read32(); str->sector = §ors[sector]; // fixed_t speed; str->speed = saveg_read32(); // fixed_t low; str->low = saveg_read32(); // fixed_t high; str->high = saveg_read32(); // int wait; str->wait = saveg_read32(); // int count; str->count = saveg_read32(); // plat_e status; str->status = saveg_read_enum(); // plat_e oldstatus; str->oldstatus = saveg_read_enum(); // boolean crush; str->crush = saveg_read32(); // int tag; str->tag = saveg_read32(); // plattype_e type; str->type = saveg_read_enum(); } static void saveg_write_plat_t(plat_t *str) { // thinker_t thinker; saveg_write_thinker_t(&str->thinker); // sector_t* sector; saveg_write32(str->sector - sectors); // fixed_t speed; saveg_write32(str->speed); // fixed_t low; saveg_write32(str->low); // fixed_t high; saveg_write32(str->high); // int wait; saveg_write32(str->wait); // int count; saveg_write32(str->count); // plat_e status; saveg_write_enum(str->status); // plat_e oldstatus; saveg_write_enum(str->oldstatus); // boolean crush; saveg_write32(str->crush); // int tag; saveg_write32(str->tag); // plattype_e type; saveg_write_enum(str->type); } // // lightflash_t // static void saveg_read_lightflash_t(lightflash_t *str) { int sector; // thinker_t thinker; saveg_read_thinker_t(&str->thinker); // sector_t* sector; sector = saveg_read32(); str->sector = §ors[sector]; // int count; str->count = saveg_read32(); // int maxlight; str->maxlight = saveg_read32(); // int minlight; str->minlight = saveg_read32(); // int maxtime; str->maxtime = saveg_read32(); // int mintime; str->mintime = saveg_read32(); } static void saveg_write_lightflash_t(lightflash_t *str) { // thinker_t thinker; saveg_write_thinker_t(&str->thinker); // sector_t* sector; saveg_write32(str->sector - sectors); // int count; saveg_write32(str->count); // int maxlight; saveg_write32(str->maxlight); // int minlight; saveg_write32(str->minlight); // int maxtime; saveg_write32(str->maxtime); // int mintime; saveg_write32(str->mintime); } // // strobe_t // static void saveg_read_strobe_t(strobe_t *str) { int sector; // thinker_t thinker; saveg_read_thinker_t(&str->thinker); // sector_t* sector; sector = saveg_read32(); str->sector = §ors[sector]; // int count; str->count = saveg_read32(); // int minlight; str->minlight = saveg_read32(); // int maxlight; str->maxlight = saveg_read32(); // int darktime; str->darktime = saveg_read32(); // int brighttime; str->brighttime = saveg_read32(); } static void saveg_write_strobe_t(strobe_t *str) { // thinker_t thinker; saveg_write_thinker_t(&str->thinker); // sector_t* sector; saveg_write32(str->sector - sectors); // int count; saveg_write32(str->count); // int minlight; saveg_write32(str->minlight); // int maxlight; saveg_write32(str->maxlight); // int darktime; saveg_write32(str->darktime); // int brighttime; saveg_write32(str->brighttime); } // // glow_t // static void saveg_read_glow_t(glow_t *str) { int sector; // thinker_t thinker; saveg_read_thinker_t(&str->thinker); // sector_t* sector; sector = saveg_read32(); str->sector = §ors[sector]; // int minlight; str->minlight = saveg_read32(); // int maxlight; str->maxlight = saveg_read32(); // int direction; str->direction = saveg_read32(); } static void saveg_write_glow_t(glow_t *str) { // thinker_t thinker; saveg_write_thinker_t(&str->thinker); // sector_t* sector; saveg_write32(str->sector - sectors); // int minlight; saveg_write32(str->minlight); // int maxlight; saveg_write32(str->maxlight); // int direction; saveg_write32(str->direction); } // // Write the header for a savegame // void P_WriteSaveGameHeader(char *description) { char name[VERSIONSIZE]; int i; for (i=0; description[i] != '\0'; ++i) saveg_write8(description[i]); for (; i> 16) & 0xff); saveg_write8((leveltime >> 8) & 0xff); saveg_write8(leveltime & 0xff); } // // Read the header for a savegame // boolean P_ReadSaveGameHeader(void) { int i; byte a, b, c; char vcheck[VERSIONSIZE]; char read_vcheck[VERSIONSIZE]; // skip the description field for (i=0; ifloorheight >> FRACBITS); saveg_write16(sec->ceilingheight >> FRACBITS); saveg_write16(sec->floorpic); saveg_write16(sec->ceilingpic); saveg_write16(sec->lightlevel); saveg_write16(sec->special); // needed? saveg_write16(sec->tag); // needed? } // do lines for (i=0, li = lines ; iflags); saveg_write16(li->special); saveg_write16(li->tag); for (j=0 ; j<2 ; j++) { if (li->sidenum[j] == -1) continue; si = &sides[li->sidenum[j]]; saveg_write16(si->textureoffset >> FRACBITS); saveg_write16(si->rowoffset >> FRACBITS); saveg_write16(si->toptexture); saveg_write16(si->bottomtexture); saveg_write16(si->midtexture); } } } // // P_UnArchiveWorld // void P_UnArchiveWorld (void) { int i; int j; sector_t* sec; line_t* li; side_t* si; // do sectors for (i=0, sec = sectors ; ifloorheight = saveg_read16() << FRACBITS; sec->ceilingheight = saveg_read16() << FRACBITS; sec->floorpic = saveg_read16(); sec->ceilingpic = saveg_read16(); sec->lightlevel = saveg_read16(); sec->special = saveg_read16(); // needed? sec->tag = saveg_read16(); // needed? sec->specialdata = 0; sec->soundtarget = 0; } // do lines for (i=0, li = lines ; iflags = saveg_read16(); li->special = saveg_read16(); li->tag = saveg_read16(); for (j=0 ; j<2 ; j++) { if (li->sidenum[j] == -1) continue; si = &sides[li->sidenum[j]]; si->textureoffset = saveg_read16() << FRACBITS; si->rowoffset = saveg_read16() << FRACBITS; si->toptexture = saveg_read16(); si->bottomtexture = saveg_read16(); si->midtexture = saveg_read16(); } } } // // Thinkers // typedef enum { tc_end, tc_mobj } thinkerclass_t; // // P_ArchiveThinkers // void P_ArchiveThinkers (void) { thinker_t* th; // save off the current thinkers for (th = thinkercap.next ; th != &thinkercap ; th=th->next) { if (th->function.acp1 == (actionf_p1)P_MobjThinker) { saveg_write8(tc_mobj); saveg_write_pad(); saveg_write_mobj_t((mobj_t *) th); continue; } // I_Error ("P_ArchiveThinkers: Unknown thinker function"); } // add a terminating marker saveg_write8(tc_end); } // // P_UnArchiveThinkers // void P_UnArchiveThinkers (void) { byte tclass; thinker_t* currentthinker; thinker_t* next; mobj_t* mobj; // remove all the current thinkers currentthinker = thinkercap.next; while (currentthinker != &thinkercap) { next = currentthinker->next; if (currentthinker->function.acp1 == (actionf_p1)P_MobjThinker) P_RemoveMobj ((mobj_t *)currentthinker); else Z_Free (currentthinker); currentthinker = next; } P_InitThinkers (); // read in saved thinkers while (1) { tclass = saveg_read8(); switch (tclass) { case tc_end: return; // end of list case tc_mobj: saveg_read_pad(); mobj = Z_Malloc (sizeof(*mobj), PU_LEVEL, NULL); saveg_read_mobj_t(mobj); mobj->target = NULL; mobj->tracer = NULL; P_SetThingPosition (mobj); mobj->info = &mobjinfo[mobj->type]; mobj->floorz = mobj->subsector->sector->floorheight; mobj->ceilingz = mobj->subsector->sector->ceilingheight; mobj->thinker.function.acp1 = (actionf_p1)P_MobjThinker; P_AddThinker (&mobj->thinker); break; default: I_Error ("Unknown tclass %i in savegame",tclass); } } } // // P_ArchiveSpecials // enum { tc_ceiling, tc_door, tc_floor, tc_plat, tc_flash, tc_strobe, tc_glow, 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 // void P_ArchiveSpecials (void) { thinker_t* th; int i; // save off the current thinkers for (th = thinkercap.next ; th != &thinkercap ; th=th->next) { if (th->function.acv == (actionf_v)NULL) { for (i = 0; i < MAXCEILINGS;i++) if (activeceilings[i] == (ceiling_t *)th) break; if (ifunction.acp1 == (actionf_p1)T_MoveCeiling) { saveg_write8(tc_ceiling); saveg_write_pad(); saveg_write_ceiling_t((ceiling_t *) th); continue; } if (th->function.acp1 == (actionf_p1)T_VerticalDoor) { saveg_write8(tc_door); saveg_write_pad(); saveg_write_vldoor_t((vldoor_t *) th); continue; } if (th->function.acp1 == (actionf_p1)T_MoveFloor) { saveg_write8(tc_floor); saveg_write_pad(); saveg_write_floormove_t((floormove_t *) th); continue; } if (th->function.acp1 == (actionf_p1)T_PlatRaise) { saveg_write8(tc_plat); saveg_write_pad(); saveg_write_plat_t((plat_t *) th); continue; } if (th->function.acp1 == (actionf_p1)T_LightFlash) { saveg_write8(tc_flash); saveg_write_pad(); saveg_write_lightflash_t((lightflash_t *) th); continue; } if (th->function.acp1 == (actionf_p1)T_StrobeFlash) { saveg_write8(tc_strobe); saveg_write_pad(); saveg_write_strobe_t((strobe_t *) th); continue; } if (th->function.acp1 == (actionf_p1)T_Glow) { saveg_write8(tc_glow); saveg_write_pad(); saveg_write_glow_t((glow_t *) th); continue; } } // add a terminating marker saveg_write8(tc_endspecials); } // // P_UnArchiveSpecials // void P_UnArchiveSpecials (void) { byte tclass; ceiling_t* ceiling; vldoor_t* door; floormove_t* floor; plat_t* plat; lightflash_t* flash; strobe_t* strobe; glow_t* glow; // read in saved thinkers while (1) { tclass = saveg_read8(); switch (tclass) { case tc_endspecials: return; // end of list case tc_ceiling: saveg_read_pad(); ceiling = Z_Malloc (sizeof(*ceiling), PU_LEVEL, NULL); saveg_read_ceiling_t(ceiling); ceiling->sector->specialdata = ceiling; if (ceiling->thinker.function.acp1) ceiling->thinker.function.acp1 = (actionf_p1)T_MoveCeiling; P_AddThinker (&ceiling->thinker); P_AddActiveCeiling(ceiling); break; case tc_door: saveg_read_pad(); door = Z_Malloc (sizeof(*door), PU_LEVEL, NULL); saveg_read_vldoor_t(door); door->sector->specialdata = door; door->thinker.function.acp1 = (actionf_p1)T_VerticalDoor; P_AddThinker (&door->thinker); break; case tc_floor: saveg_read_pad(); floor = Z_Malloc (sizeof(*floor), PU_LEVEL, NULL); saveg_read_floormove_t(floor); floor->sector->specialdata = floor; floor->thinker.function.acp1 = (actionf_p1)T_MoveFloor; P_AddThinker (&floor->thinker); break; case tc_plat: saveg_read_pad(); plat = Z_Malloc (sizeof(*plat), PU_LEVEL, NULL); saveg_read_plat_t(plat); plat->sector->specialdata = plat; if (plat->thinker.function.acp1) plat->thinker.function.acp1 = (actionf_p1)T_PlatRaise; P_AddThinker (&plat->thinker); P_AddActivePlat(plat); break; case tc_flash: saveg_read_pad(); flash = Z_Malloc (sizeof(*flash), PU_LEVEL, NULL); saveg_read_lightflash_t(flash); flash->thinker.function.acp1 = (actionf_p1)T_LightFlash; P_AddThinker (&flash->thinker); break; case tc_strobe: saveg_read_pad(); strobe = Z_Malloc (sizeof(*strobe), PU_LEVEL, NULL); saveg_read_strobe_t(strobe); strobe->thinker.function.acp1 = (actionf_p1)T_StrobeFlash; P_AddThinker (&strobe->thinker); break; case tc_glow: saveg_read_pad(); glow = Z_Malloc (sizeof(*glow), PU_LEVEL, NULL); saveg_read_glow_t(glow); glow->thinker.function.acp1 = (actionf_p1)T_Glow; P_AddThinker (&glow->thinker); break; default: I_Error ("P_UnarchiveSpecials:Unknown tclass %i " "in savegame",tclass); } } } ================================================ FILE: fbdoom/p_saveg.h ================================================ // // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // 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. // // DESCRIPTION: // Savegame I/O, archiving, persistence. // #ifndef __P_SAVEG__ #define __P_SAVEG__ #include // maximum size of a savegame description #define SAVESTRINGSIZE 24 // temporary filename to use while saving. char *P_TempSaveGameFile(void); // filename to use for a savegame slot char *P_SaveGameFile(int slot); // Savegame file header read/write functions boolean P_ReadSaveGameHeader(void); void P_WriteSaveGameHeader(char *description); // Savegame end-of-file read/write functions boolean P_ReadSaveGameEOF(void); void P_WriteSaveGameEOF(void); // 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); extern FILE *save_stream; extern boolean savegame_error; #endif ================================================ FILE: fbdoom/p_setup.c ================================================ // // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // 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. // // DESCRIPTION: // Do all the WAD I/O, get map description, // set up initial state and misc. LUTs. // #include #include "z_zone.h" #include "deh_main.h" #include "i_swap.h" #include "m_argv.h" #include "m_bbox.h" #include "g_game.h" #include "i_system.h" #include "w_wad.h" #include "doomdef.h" #include "p_local.h" #include "s_sound.h" #include "doomstat.h" void P_SpawnMapThing (mapthing_t* mthing); // // 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; static int totallines; // 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; int bmapheight; // size in mapblocks short* blockmap; // int for larger maps // offsets in blockmap are from here short* blockmaplump; // origin of block map fixed_t bmaporgx; fixed_t bmaporgy; // for thing chains mobj_t** blocklinks; // REJECT // For fast sight rejection. // Speeds up enemy AI by skipping detailed // LineOf Sight calculation. // Without special effect, this could be // used as a PVS lookup as well. // byte* rejectmatrix; // Maintain single and multi player starting spots. #define MAX_DEATHMATCH_STARTS 10 mapthing_t deathmatchstarts[MAX_DEATHMATCH_STARTS]; mapthing_t* deathmatch_p; mapthing_t playerstarts[MAXPLAYERS]; // // P_LoadVertexes // void P_LoadVertexes (int lump) { byte* data; int i; mapvertex_t* ml; vertex_t* li; // 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. data = W_CacheLumpNum (lump, PU_STATIC); ml = (mapvertex_t *)data; li = vertexes; // Copy and convert vertex coordinates, // internal representation as fixed. for (i=0 ; ix = SHORT(ml->x)<y = SHORT(ml->y)<v1 = &vertexes[SHORT(ml->v1)]; li->v2 = &vertexes[SHORT(ml->v2)]; li->angle = (SHORT(ml->angle))<<16; li->offset = (SHORT(ml->offset))<<16; linedef = SHORT(ml->linedef); ldef = &lines[linedef]; li->linedef = ldef; side = SHORT(ml->side); li->sidedef = &sides[ldef->sidenum[side]]; li->frontsector = sides[ldef->sidenum[side]].sector; if (ldef-> flags & ML_TWOSIDED) { sidenum = ldef->sidenum[side ^ 1]; // If the sidenum is out of range, this may be a "glass hack" // impassible window. Point at side #0 (this may not be // the correct Vanilla behavior; however, it seems to work for // OTTAWAU.WAD, which is the one place I've seen this trick // used). if (sidenum < 0 || sidenum >= numsides) { li->backsector = GetSectorAtNullAddress(); } else { li->backsector = sides[sidenum].sector; } } else { li->backsector = 0; } } W_ReleaseLumpNum(lump); } // // P_LoadSubsectors // void P_LoadSubsectors (int lump) { byte* data; int i; mapsubsector_t* ms; subsector_t* ss; numsubsectors = W_LumpLength (lump) / sizeof(mapsubsector_t); subsectors = Z_Malloc (numsubsectors*sizeof(subsector_t),PU_LEVEL,0); data = W_CacheLumpNum (lump,PU_STATIC); ms = (mapsubsector_t *)data; memset (subsectors,0, numsubsectors*sizeof(subsector_t)); ss = subsectors; for (i=0 ; inumlines = SHORT(ms->numsegs); ss->firstline = SHORT(ms->firstseg); } W_ReleaseLumpNum(lump); } // // P_LoadSectors // void P_LoadSectors (int lump) { byte* data; int i; mapsector_t* ms; sector_t* ss; numsectors = W_LumpLength (lump) / sizeof(mapsector_t); sectors = Z_Malloc (numsectors*sizeof(sector_t),PU_LEVEL,0); memset (sectors, 0, numsectors*sizeof(sector_t)); data = W_CacheLumpNum (lump,PU_STATIC); ms = (mapsector_t *)data; ss = sectors; for (i=0 ; ifloorheight = 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->tag = SHORT(ms->tag); ss->thinglist = NULL; } W_ReleaseLumpNum(lump); } // // P_LoadNodes // void P_LoadNodes (int lump) { byte* data; int i; int j; int k; mapnode_t* mn; node_t* no; numnodes = W_LumpLength (lump) / sizeof(mapnode_t); nodes = Z_Malloc (numnodes*sizeof(node_t),PU_LEVEL,0); data = W_CacheLumpNum (lump,PU_STATIC); mn = (mapnode_t *)data; no = nodes; 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])<type)) { case 68: // Arachnotron case 64: // Archvile case 88: // Boss Brain case 89: // Boss Shooter case 69: // Hell Knight case 67: // Mancubus case 71: // Pain Elemental case 65: // Former Human Commando case 66: // Revenant case 84: // Wolf SS spawn = false; break; } } if (spawn == false) break; // Do spawn all other stuff. spawnthing.x = SHORT(mt->x); spawnthing.y = SHORT(mt->y); spawnthing.angle = SHORT(mt->angle); spawnthing.type = SHORT(mt->type); spawnthing.options = SHORT(mt->options); P_SpawnMapThing(&spawnthing); } W_ReleaseLumpNum(lump); } // // P_LoadLineDefs // Also counts secret lines for intermissions. // void P_LoadLineDefs (int lump) { byte* data; int i; maplinedef_t* mld; line_t* ld; vertex_t* v1; vertex_t* v2; numlines = W_LumpLength (lump) / sizeof(maplinedef_t); lines = Z_Malloc (numlines*sizeof(line_t),PU_LEVEL,0); memset (lines, 0, numlines*sizeof(line_t)); data = W_CacheLumpNum (lump,PU_STATIC); mld = (maplinedef_t *)data; ld = lines; for (i=0 ; iflags = SHORT(mld->flags); ld->special = SHORT(mld->special); ld->tag = SHORT(mld->tag); v1 = ld->v1 = &vertexes[SHORT(mld->v1)]; v2 = ld->v2 = &vertexes[SHORT(mld->v2)]; ld->dx = v2->x - v1->x; ld->dy = v2->y - v1->y; if (!ld->dx) ld->slopetype = ST_VERTICAL; else if (!ld->dy) ld->slopetype = ST_HORIZONTAL; else { if (FixedDiv (ld->dy , ld->dx) > 0) ld->slopetype = ST_POSITIVE; else ld->slopetype = 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; } ld->sidenum[0] = SHORT(mld->sidenum[0]); ld->sidenum[1] = SHORT(mld->sidenum[1]); if (ld->sidenum[0] != -1) ld->frontsector = sides[ld->sidenum[0]].sector; else ld->frontsector = 0; if (ld->sidenum[1] != -1) ld->backsector = sides[ld->sidenum[1]].sector; else ld->backsector = 0; } W_ReleaseLumpNum(lump); } // // P_LoadSideDefs // void P_LoadSideDefs (int lump) { byte* data; int i; mapsidedef_t* msd; side_t* sd; numsides = W_LumpLength (lump) / sizeof(mapsidedef_t); sides = Z_Malloc (numsides*sizeof(side_t),PU_LEVEL,0); memset (sides, 0, numsides*sizeof(side_t)); data = W_CacheLumpNum (lump,PU_STATIC); msd = (mapsidedef_t *)data; sd = sides; for (i=0 ; itextureoffset = SHORT(msd->textureoffset)<rowoffset = SHORT(msd->rowoffset)<toptexture = R_TextureNumForName(msd->toptexture); sd->bottomtexture = R_TextureNumForName(msd->bottomtexture); sd->midtexture = R_TextureNumForName(msd->midtexture); sd->sector = §ors[SHORT(msd->sector)]; } W_ReleaseLumpNum(lump); } // // P_LoadBlockMap // void P_LoadBlockMap (int lump) { int i; int count; int lumplen; lumplen = W_LumpLength(lump); count = lumplen / 2; blockmaplump = Z_Malloc(lumplen, PU_LEVEL, NULL); W_ReadLump(lump, blockmaplump); blockmap = blockmaplump + 4; // Swap all short integers to native byte ordering. for (i=0; ifirstline]; ss->sector = seg->sidedef->sector; } // count number of lines in each sector li = lines; totallines = 0; for (i=0 ; ifrontsector->linecount++; if (li->backsector && li->backsector != li->frontsector) { li->backsector->linecount++; totallines++; } } // build line tables for each sector linebuffer = Z_Malloc (totallines*sizeof(line_t *), PU_LEVEL, 0); for (i=0; ifrontsector != NULL) { sector = li->frontsector; sector->lines[sector->linecount] = li; ++sector->linecount; } if (li->backsector != NULL && li->frontsector != li->backsector) { sector = li->backsector; sector->lines[sector->linecount] = li; ++sector->linecount; } } // Generate bounding boxes for sectors sector = sectors; for (i=0 ; ilinecount; j++) { li = sector->lines[j]; M_AddToBox (bbox, li->v1->x, li->v1->y); M_AddToBox (bbox, li->v2->x, li->v2->y); } // set the degenmobj_t to the middle of the bounding box sector->soundorg.x = (bbox[BOXRIGHT]+bbox[BOXLEFT])/2; sector->soundorg.y = (bbox[BOXTOP]+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; } } // Pad the REJECT lump with extra data when the lump is too small, // to simulate a REJECT buffer overflow in Vanilla Doom. static void PadRejectArray(byte *array, unsigned int len) { unsigned int i; unsigned int byte_num; byte *dest; unsigned int padvalue; // Values to pad the REJECT array with: unsigned int rejectpad[4] = { ((totallines * 4 + 3) & ~3) + 24, // Size 0, // Part of z_zone block header 50, // PU_LEVEL 0x1d4a11 // DOOM_CONST_ZONEID }; // Copy values from rejectpad into the destination array. dest = array; for (i=0; i> (byte_num * 8)) & 0xff; ++dest; } // We only have a limited pad size. Print a warning if the // REJECT lump is too small. if (len > sizeof(rejectpad)) { fprintf(stderr, "PadRejectArray: REJECT lump too short to pad! (%i > %i)\n", len, (int) sizeof(rejectpad)); // Pad remaining space with 0 (or 0xff, if specified on command line). if (M_CheckParm("-reject_pad_with_ff")) { padvalue = 0xff; } else { padvalue = 0xf00; } memset(array + sizeof(rejectpad), padvalue, len - sizeof(rejectpad)); } } static void P_LoadReject(int lumpnum) { int minlength; int lumplen; // Calculate the size that the REJECT lump *should* be. minlength = (numsectors * numsectors + 7) / 8; // If the lump meets the minimum length, it can be loaded directly. // Otherwise, we need to allocate a buffer of the correct size // and pad it with appropriate data. lumplen = W_LumpLength(lumpnum); if (lumplen >= minlength) { rejectmatrix = W_CacheLumpNum(lumpnum, PU_LEVEL); } else { rejectmatrix = Z_Malloc(minlength, PU_LEVEL, &rejectmatrix); W_ReadLump(lumpnum, rejectmatrix); PadRejectArray(rejectmatrix + lumplen, minlength - lumplen); } } // // P_SetupLevel // void P_SetupLevel ( int episode, int map, int playermask, skill_t skill) { int i; char lumpname[9]; int lumpnum; totalkills = totalitems = totalsecret = wminfo.maxfrags = 0; wminfo.partime = 180; for (i=0 ; idx) { if (x==node->x) return 2; if (x <= node->x) return node->dy > 0; return node->dy < 0; } if (!node->dy) { if (x==node->y) return 2; if (y <= node->y) return node->dx < 0; return node->dx > 0; } dx = (x - node->x); dy = (y - node->y); left = (node->dy>>FRACBITS) * (dx>>FRACBITS); right = (dy>>FRACBITS) * (node->dx>>FRACBITS); if (right < left) return 0; // front side if (left == right) return 2; return 1; // back side } // // P_InterceptVector2 // Returns the fractional intercept point // along the first divline. // This is only called by the addthings and addlines traversers. // fixed_t P_InterceptVector2 ( divline_t* v2, divline_t* v1 ) { fixed_t frac; fixed_t num; fixed_t den; den = FixedMul (v1->dy>>8,v2->dx) - FixedMul(v1->dx>>8,v2->dy); if (den == 0) return 0; // I_Error ("P_InterceptVector: parallel"); num = FixedMul ( (v1->x - v2->x)>>8 ,v1->dy) + FixedMul ( (v2->y - v1->y)>>8 , v1->dx); frac = FixedDiv (num , den); return frac; } // // P_CrossSubsector // Returns true // if strace crosses the given subsector successfully. // boolean P_CrossSubsector (int num) { seg_t* seg; line_t* line; int s1; int s2; int count; subsector_t* sub; sector_t* front; sector_t* back; fixed_t opentop; fixed_t openbottom; divline_t divl; vertex_t* v1; vertex_t* v2; fixed_t frac; fixed_t slope; #ifdef RANGECHECK if (num>=numsubsectors) I_Error ("P_CrossSubsector: ss %i with numss = %i", num, numsubsectors); #endif sub = &subsectors[num]; // check lines count = sub->numlines; seg = &segs[sub->firstline]; for ( ; count ; seg++, count--) { line = seg->linedef; // allready checked other side? if (line->validcount == validcount) continue; line->validcount = validcount; v1 = line->v1; v2 = line->v2; s1 = P_DivlineSide (v1->x,v1->y, &strace); s2 = P_DivlineSide (v2->x, v2->y, &strace); // line isn't crossed? if (s1 == s2) continue; divl.x = v1->x; divl.y = v1->y; divl.dx = v2->x - v1->x; divl.dy = v2->y - v1->y; s1 = P_DivlineSide (strace.x, strace.y, &divl); s2 = P_DivlineSide (t2x, t2y, &divl); // line isn't crossed? if (s1 == s2) continue; // Backsector may be NULL if this is an "impassible // glass" hack line. if (line->backsector == NULL) { return false; } // stop because it is not two sided anyway // might do this after updating validcount? if ( !(line->flags & ML_TWOSIDED) ) return false; // crosses a two sided line front = seg->frontsector; back = seg->backsector; // no wall to block sight with? if (front->floorheight == back->floorheight && front->ceilingheight == back->ceilingheight) continue; // possible occluder // because of ceiling height differences if (front->ceilingheight < back->ceilingheight) opentop = front->ceilingheight; else opentop = back->ceilingheight; // because of ceiling height differences if (front->floorheight > back->floorheight) openbottom = front->floorheight; else openbottom = back->floorheight; // quick test for totally closed doors if (openbottom >= opentop) return false; // stop frac = P_InterceptVector2 (&strace, &divl); if (front->floorheight != back->floorheight) { slope = FixedDiv (openbottom - sightzstart , frac); if (slope > bottomslope) bottomslope = slope; } if (front->ceilingheight != back->ceilingheight) { slope = FixedDiv (opentop - sightzstart , frac); if (slope < topslope) topslope = slope; } if (topslope <= bottomslope) return false; // stop } // passed the subsector ok return true; } // // P_CrossBSPNode // Returns true // if strace crosses the given node successfully. // boolean P_CrossBSPNode (int bspnum) { node_t* bsp; int side; if (bspnum & NF_SUBSECTOR) { if (bspnum == -1) return P_CrossSubsector (0); else return P_CrossSubsector (bspnum&(~NF_SUBSECTOR)); } bsp = &nodes[bspnum]; // decide which side the start point is on side = P_DivlineSide (strace.x, strace.y, (divline_t *)bsp); if (side == 2) side = 0; // an "on" should cross both sides // cross the starting side if (!P_CrossBSPNode (bsp->children[side]) ) return false; // the partition plane is crossed here if (side == P_DivlineSide (t2x, t2y,(divline_t *)bsp)) { // the line doesn't touch the other side return true; } // cross the ending side return P_CrossBSPNode (bsp->children[side^1]); } // // P_CheckSight // Returns true // if a straight line between t1 and t2 is unobstructed. // Uses REJECT. // boolean P_CheckSight ( mobj_t* t1, mobj_t* t2 ) { int s1; int s2; int pnum; int bytenum; int bitnum; // First check for trivial rejection. // Determine subsector entries in REJECT table. s1 = (t1->subsector->sector - sectors); s2 = (t2->subsector->sector - sectors); pnum = s1*numsectors + s2; bytenum = pnum>>3; bitnum = 1 << (pnum&7); // Check in REJECT table. if (rejectmatrix[bytenum]&bitnum) { sightcounts[0]++; // can't possibly be connected return false; } // An unobstructed LOS is possible. // Now look from eyes of t1 to any part of t2. sightcounts[1]++; validcount++; sightzstart = t1->z + t1->height - (t1->height>>2); topslope = (t2->z+t2->height) - sightzstart; bottomslope = (t2->z) - sightzstart; strace.x = t1->x; strace.y = t1->y; t2x = t2->x; t2y = t2->y; strace.dx = t2->x - t1->x; strace.dy = t2->y - t1->y; // the head node is the last node output return P_CrossBSPNode (numnodes-1); } ================================================ FILE: fbdoom/p_spec.c ================================================ // // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // 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. // // DESCRIPTION: // Implements special effects: // Texture animation, height or lighting changes // according to adjacent sectors, respective // utility functions, etc. // Line Tag handling. Line and Sector triggers. // #include #include "doomdef.h" #include "doomstat.h" #include "deh_main.h" #include "i_system.h" #include "z_zone.h" #include "m_argv.h" #include "m_misc.h" #include "m_random.h" #include "w_wad.h" #include "r_local.h" #include "p_local.h" #include "g_game.h" #include "s_sound.h" // State. #include "r_state.h" // Data. #include "sounds.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 // typedef struct { int istexture; // if false, it is a flat char endname[9]; char startname[9]; int speed; } animdef_t; #define MAXANIMS 32 extern anim_t anims[MAXANIMS]; extern anim_t* lastanim; // // P_InitPicAnims // // Floor/ceiling animation sequences, // defined by first and last frame, // i.e. the flat (64x64 tile) name to // be used. // The full animation sequence is given // using all the flats between the start // and end entry, in the order found in // the WAD file. // animdef_t animdefs[] = { {false, "NUKAGE3", "NUKAGE1", 8}, {false, "FWATER4", "FWATER1", 8}, {false, "SWATER4", "SWATER1", 8}, {false, "LAVA4", "LAVA1", 8}, {false, "BLOOD3", "BLOOD1", 8}, // DOOM II flat animations. {false, "RROCK08", "RROCK05", 8}, {false, "SLIME04", "SLIME01", 8}, {false, "SLIME08", "SLIME05", 8}, {false, "SLIME12", "SLIME09", 8}, {true, "BLODGR4", "BLODGR1", 8}, {true, "SLADRIP3", "SLADRIP1", 8}, {true, "BLODRIP4", "BLODRIP1", 8}, {true, "FIREWALL", "FIREWALA", 8}, {true, "GSTFONT3", "GSTFONT1", 8}, {true, "FIRELAVA", "FIRELAV3", 8}, {true, "FIREMAG3", "FIREMAG1", 8}, {true, "FIREBLU2", "FIREBLU1", 8}, {true, "ROCKRED3", "ROCKRED1", 8}, {true, "BFALL4", "BFALL1", 8}, {true, "SFALL4", "SFALL1", 8}, {true, "WFALL4", "WFALL1", 8}, {true, "DBRAIN4", "DBRAIN1", 8}, {-1, "", "", 0}, }; anim_t anims[MAXANIMS]; anim_t* lastanim; // // Animating line specials // #define MAXLINEANIMS 64 extern short numlinespecials; extern line_t* linespeciallist[MAXLINEANIMS]; void P_InitPicAnims (void) { int i; // Init animation lastanim = anims; for (i=0 ; animdefs[i].istexture != -1 ; i++) { char *startname, *endname; startname = DEH_String(animdefs[i].startname); endname = DEH_String(animdefs[i].endname); if (animdefs[i].istexture) { // different episode ? if (R_CheckTextureNumForName(startname) == -1) continue; lastanim->picnum = R_TextureNumForName(endname); lastanim->basepic = R_TextureNumForName(startname); } else { if (W_CheckNumForName(startname) == -1) continue; lastanim->picnum = R_FlatNumForName(endname); lastanim->basepic = R_FlatNumForName(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", startname, endname); lastanim->speed = animdefs[i].speed; lastanim++; } } // // UTILITIES // // // getSide() // Will return a side_t* // given the number of the current sector, // the line number, and the side (0/1) that you want. // 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. // 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. // int twoSided ( int sector, int line ) { return (sectors[sector].lines[line])->flags & ML_TWOSIDED; } // // getNextSector() // Return sector_t * of sector next to current. // NULL if not two-sided line // sector_t* getNextSector ( line_t* line, sector_t* sec ) { if (!(line->flags & ML_TWOSIDED)) return NULL; if (line->frontsector == sec) return line->backsector; return line->frontsector; } // // P_FindLowestFloorSurrounding() // FIND LOWEST FLOOR HEIGHT IN 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() // FIND HIGHEST FLOOR HEIGHT IN SURROUNDING SECTORS // fixed_t P_FindHighestFloorSurrounding(sector_t *sec) { int i; line_t* check; sector_t* other; fixed_t floor = -500*FRACUNIT; 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 // FIND NEXT HIGHEST FLOOR IN SURROUNDING SECTORS // Note: this should be doable w/o a fixed array. // Thanks to entryway for the Vanilla overflow emulation. // 20 adjoining sectors max! #define MAX_ADJOINING_SECTORS 20 fixed_t P_FindNextHighestFloor ( sector_t* sec, int currentheight ) { int i; int h; int min; line_t* check; sector_t* other; fixed_t height = currentheight; fixed_t heightlist[MAX_ADJOINING_SECTORS + 2]; for (i=0, h=0; i < sec->linecount; i++) { check = sec->lines[i]; other = getNextSector(check,sec); if (!other) continue; if (other->floorheight > height) { // Emulation of memory (stack) overflow if (h == MAX_ADJOINING_SECTORS + 1) { height = other->floorheight; } else if (h == MAX_ADJOINING_SECTORS + 2) { // Fatal overflow: game crashes at 22 textures I_Error("Sector with more than 22 adjoining sectors. " "Vanilla will crash here"); } heightlist[h++] = other->floorheight; } } // Find lowest height in list if (!h) { return currentheight; } min = heightlist[0]; // Range checking? for (i = 1; i < h; i++) { if (heightlist[i] < min) { min = heightlist[i]; } } return min; } // // FIND LOWEST CEILING IN THE SURROUNDING SECTORS // fixed_t P_FindLowestCeilingSurrounding(sector_t* sec) { int i; line_t* check; sector_t* other; fixed_t height = INT_MAX; 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; } // // FIND HIGHEST CEILING IN THE SURROUNDING SECTORS // fixed_t P_FindHighestCeilingSurrounding(sector_t* sec) { int i; line_t* check; sector_t* other; fixed_t height = 0; 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; } // // RETURN NEXT SECTOR # THAT LINE TAG REFERS TO // int P_FindSectorFromLineTag ( line_t* line, int start ) { int i; for (i=start+1;itag) return i; return -1; } // // Find minimum light from an adjacent sector // 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; } // // EVENTS // Events are operations triggered by using, crossing, // or shooting special lines, or by timed thinkers. // // // P_CrossSpecialLine - TRIGGER // Called every time a thing origin is about // to cross a line with a non 0 special. // void P_CrossSpecialLine ( int linenum, int side, mobj_t* thing ) { line_t* line; int ok; line = &lines[linenum]; // Triggers that other things can activate 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; } 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 ok = 1; break; } if (!ok) return; } // Note: could use some const's here. switch (line->special) { // TRIGGERS. // All from here to RETRIGGERS. case 2: // Open Door EV_DoDoor(line,vld_open); line->special = 0; break; case 3: // Close Door EV_DoDoor(line,vld_close); line->special = 0; break; case 4: // Raise Door EV_DoDoor(line,vld_normal); line->special = 0; break; case 5: // Raise Floor EV_DoFloor(line,raiseFloor); line->special = 0; break; case 6: // Fast Ceiling Crush & Raise EV_DoCeiling(line,fastCrushAndRaise); line->special = 0; break; case 8: // Build Stairs EV_BuildStairs(line,build8); line->special = 0; break; case 10: // PlatDownWaitUp EV_DoPlat(line,downWaitUpStay,0); line->special = 0; break; case 12: // Light Turn On - brightest near EV_LightTurnOn(line,0); line->special = 0; break; case 13: // Light Turn On 255 EV_LightTurnOn(line,255); line->special = 0; break; case 16: // Close Door 30 EV_DoDoor(line,vld_close30ThenOpen); line->special = 0; break; case 17: // Start Light Strobing EV_StartLightStrobing(line); line->special = 0; break; case 19: // Lower Floor EV_DoFloor(line,lowerFloor); line->special = 0; break; case 22: // Raise floor to nearest height and change texture EV_DoPlat(line,raiseToNearestAndChange,0); line->special = 0; break; case 25: // Ceiling Crush and Raise EV_DoCeiling(line,crushAndRaise); line->special = 0; break; case 30: // Raise floor to shortest texture height // on either side of lines. EV_DoFloor(line,raiseToTexture); line->special = 0; break; case 35: // Lights Very Dark EV_LightTurnOn(line,35); line->special = 0; break; case 36: // Lower Floor (TURBO) EV_DoFloor(line,turboLower); line->special = 0; break; case 37: // LowerAndChange EV_DoFloor(line,lowerAndChange); line->special = 0; break; case 38: // Lower Floor To Lowest EV_DoFloor( line, lowerFloorToLowest ); line->special = 0; break; case 39: // TELEPORT! EV_Teleport( line, side, thing ); line->special = 0; break; case 40: // RaiseCeilingLowerFloor EV_DoCeiling( line, raiseToHighest ); EV_DoFloor( line, lowerFloorToLowest ); line->special = 0; break; case 44: // Ceiling Crush EV_DoCeiling( line, lowerAndCrush ); line->special = 0; break; case 52: // EXIT! G_ExitLevel (); break; case 53: // Perpetual Platform Raise EV_DoPlat(line,perpetualRaise,0); line->special = 0; break; case 54: // Platform Stop EV_StopPlat(line); line->special = 0; break; case 56: // Raise Floor Crush EV_DoFloor(line,raiseFloorCrush); line->special = 0; break; case 57: // Ceiling Crush Stop EV_CeilingCrushStop(line); line->special = 0; break; case 58: // Raise Floor 24 EV_DoFloor(line,raiseFloor24); line->special = 0; break; case 59: // Raise Floor 24 And Change EV_DoFloor(line,raiseFloor24AndChange); line->special = 0; break; case 104: // Turn lights off in sector(tag) EV_TurnTagLightsOff(line); line->special = 0; break; case 108: // Blazing Door Raise (faster than TURBO!) EV_DoDoor (line,vld_blazeRaise); line->special = 0; break; case 109: // Blazing Door Open (faster than TURBO!) EV_DoDoor (line,vld_blazeOpen); line->special = 0; break; case 100: // Build Stairs Turbo 16 EV_BuildStairs(line,turbo16); line->special = 0; break; case 110: // Blazing Door Close (faster than TURBO!) EV_DoDoor (line,vld_blazeClose); line->special = 0; break; case 119: // Raise floor to nearest surr. floor EV_DoFloor(line,raiseFloorToNearest); line->special = 0; break; case 121: // Blazing PlatDownWaitUpStay EV_DoPlat(line,blazeDWUS,0); line->special = 0; break; case 124: // Secret EXIT G_SecretExitLevel (); break; case 125: // TELEPORT MonsterONLY if (!thing->player) { EV_Teleport( line, side, thing ); line->special = 0; } break; case 130: // Raise Floor Turbo EV_DoFloor(line,raiseFloorTurbo); line->special = 0; break; case 141: // Silent Ceiling Crush & Raise EV_DoCeiling(line,silentCrushAndRaise); line->special = 0; break; // RETRIGGERS. All from here till end. 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,vld_close); break; case 76: // Close Door 30 EV_DoDoor(line,vld_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,vld_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,vld_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,vld_blazeRaise); break; case 106: // Blazing Door Open (faster than TURBO!) EV_DoDoor (line,vld_blazeOpen); break; case 107: // Blazing Door Close (faster than TURBO!) EV_DoDoor (line,vld_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; } } // // P_ShootSpecialLine - IMPACT SPECIALS // Called when a thing shoots a special line. // void P_ShootSpecialLine ( mobj_t* thing, line_t* line ) { int ok; // Impacts that other things can activate. if (!thing->player) { ok = 0; switch(line->special) { case 46: // OPEN DOOR IMPACT ok = 1; break; } if (!ok) return; } switch(line->special) { case 24: // RAISE FLOOR EV_DoFloor(line,raiseFloor); P_ChangeSwitchTexture(line,0); break; case 46: // OPEN DOOR EV_DoDoor(line,vld_open); P_ChangeSwitchTexture(line,1); break; case 47: // RAISE FLOOR NEAR AND CHANGE EV_DoPlat(line,raiseToNearestAndChange,0); P_ChangeSwitchTexture(line,0); break; } } // // P_PlayerInSpecialSector // Called every tic frame // that the player origin is in a special sector // void P_PlayerInSpecialSector (player_t* player) { sector_t* sector; sector = player->mo->subsector->sector; // Falling, not all the way down yet? if (player->mo->z != sector->floorheight) return; // Has hitten ground. switch (sector->special) { case 5: // HELLSLIME DAMAGE if (!player->powers[pw_ironfeet]) if (!(leveltime&0x1f)) P_DamageMobj (player->mo, NULL, NULL, 10); break; case 7: // NUKAGE DAMAGE if (!player->powers[pw_ironfeet]) if (!(leveltime&0x1f)) P_DamageMobj (player->mo, NULL, NULL, 5); break; case 16: // SUPER HELLSLIME DAMAGE case 4: // STROBE HURT if (!player->powers[pw_ironfeet] || (P_Random()<5) ) { if (!(leveltime&0x1f)) P_DamageMobj (player->mo, NULL, NULL, 20); } break; case 9: // SECRET SECTOR player->secretcount++; sector->special = 0; break; case 11: // EXIT SUPER DAMAGE! (for E1M8 finale) player->cheats &= ~CF_GODMODE; if (!(leveltime&0x1f)) P_DamageMobj (player->mo, NULL, NULL, 20); if (player->health <= 10) G_ExitLevel(); break; default: I_Error ("P_PlayerInSpecialSector: " "unknown special %i", sector->special); break; }; } // // P_UpdateSpecials // Animate planes, scroll walls, etc. // boolean levelTimer; int levelTimeCount; void P_UpdateSpecials (void) { anim_t* anim; int pic; int i; line_t* line; // LEVEL TIMER if (levelTimer == true) { levelTimeCount--; if (!levelTimeCount) 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; } } // ANIMATE LINE SPECIALS for (i = 0; i < numlinespecials; i++) { line = linespeciallist[i]; switch(line->special) { case 48: // EFFECT FIRSTCOL SCROLL + sides[line->sidenum[0]].textureoffset += FRACUNIT; break; } } // DO BUTTONS 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; } S_StartSound(&buttonlist[i].soundorg,sfx_swtchn); memset(&buttonlist[i],0,sizeof(button_t)); } } } // // Donut overrun emulation // // Derived from the code from PrBoom+. Thanks go to Andrey Budko (entryway) // as usual :-) // #define DONUT_FLOORHEIGHT_DEFAULT 0x00000000 #define DONUT_FLOORPIC_DEFAULT 0x16 static void DonutOverrun(fixed_t *s3_floorheight, short *s3_floorpic, line_t *line, sector_t *pillar_sector) { static int first = 1; static int tmp_s3_floorheight; static int tmp_s3_floorpic; extern int numflats; if (first) { int p; // This is the first time we have had an overrun. first = 0; // Default values tmp_s3_floorheight = DONUT_FLOORHEIGHT_DEFAULT; tmp_s3_floorpic = DONUT_FLOORPIC_DEFAULT; //! // @category compat // @arg // // Use the specified magic values when emulating behavior caused // by memory overruns from improperly constructed donuts. // In Vanilla Doom this can differ depending on the operating // system. The default (if this option is not specified) is to // emulate the behavior when running under Windows 98. p = M_CheckParmWithArgs("-donut", 2); if (p > 0) { // Dump of needed memory: (fixed_t)0000:0000 and (short)0000:0008 // // C:\>debug // -d 0:0 // // DOS 6.22: // 0000:0000 (57 92 19 00) F4 06 70 00-(16 00) // DOS 7.1: // 0000:0000 (9E 0F C9 00) 65 04 70 00-(16 00) // Win98: // 0000:0000 (00 00 00 00) 65 04 70 00-(16 00) // DOSBox under XP: // 0000:0000 (00 00 00 F1) ?? ?? ?? 00-(07 00) M_StrToInt(myargv[p + 1], &tmp_s3_floorheight); M_StrToInt(myargv[p + 2], &tmp_s3_floorpic); if (tmp_s3_floorpic >= numflats) { fprintf(stderr, "DonutOverrun: The second parameter for \"-donut\" " "switch should be greater than 0 and less than number " "of flats (%d). Using default value (%d) instead. \n", numflats, DONUT_FLOORPIC_DEFAULT); tmp_s3_floorpic = DONUT_FLOORPIC_DEFAULT; } } } /* fprintf(stderr, "Linedef: %d; Sector: %d; " "New floor height: %d; New floor pic: %d\n", line->iLineID, pillar_sector->iSectorID, tmp_s3_floorheight >> 16, tmp_s3_floorpic); */ *s3_floorheight = (fixed_t) tmp_s3_floorheight; *s3_floorpic = (short) tmp_s3_floorpic; } // // Special Stuff that can not be categorized // int EV_DoDonut(line_t* line) { sector_t* s1; sector_t* s2; sector_t* s3; int secnum; int rtn; int i; floormove_t* floor; fixed_t s3_floorheight; short s3_floorpic; secnum = -1; rtn = 0; while ((secnum = P_FindSectorFromLineTag(line,secnum)) >= 0) { s1 = §ors[secnum]; // ALREADY MOVING? IF SO, KEEP GOING... if (s1->specialdata) continue; rtn = 1; s2 = getNextSector(s1->lines[0],s1); // Vanilla Doom does not check if the linedef is one sided. The // game does not crash, but reads invalid memory and causes the // sector floor to move "down" to some unknown height. // DOSbox prints a warning about an invalid memory access. // // I'm not sure exactly what invalid memory is being read. This // isn't something that should be done, anyway. // Just print a warning and return. if (s2 == NULL) { fprintf(stderr, "EV_DoDonut: linedef had no second sidedef! " "Unexpected behavior may occur in Vanilla Doom. \n"); break; } for (i = 0; i < s2->linecount; i++) { s3 = s2->lines[i]->backsector; if (s3 == s1) continue; if (s3 == NULL) { // e6y // s3 is NULL, so // s3->floorheight is an int at 0000:0000 // s3->floorpic is a short at 0000:0008 // Trying to emulate fprintf(stderr, "EV_DoDonut: WARNING: emulating buffer overrun due to " "NULL back sector. " "Unexpected behavior may occur in Vanilla Doom.\n"); DonutOverrun(&s3_floorheight, &s3_floorpic, line, s1); } else { s3_floorheight = s3->floorheight; s3_floorpic = s3->floorpic; } // Spawn rising slime floor = Z_Malloc (sizeof(*floor), PU_LEVSPEC, 0); P_AddThinker (&floor->thinker); s2->specialdata = floor; floor->thinker.function.acp1 = (actionf_p1) 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 floor = Z_Malloc (sizeof(*floor), PU_LEVSPEC, 0); P_AddThinker (&floor->thinker); s1->specialdata = floor; floor->thinker.function.acp1 = (actionf_p1) 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; } // // SPECIAL SPAWNING // // // P_SpawnSpecials // After the map has been loaded, scan for specials // that spawn thinkers // short numlinespecials; line_t* linespeciallist[MAXLINEANIMS]; // Parses command line parameters. void P_SpawnSpecials (void) { sector_t* sector; int i; // See if -TIMER was specified. if (timelimit > 0 && deathmatch) { levelTimer = true; levelTimeCount = timelimit * 60 * TICRATE; } else { levelTimer = false; } // Init special SECTORs. sector = sectors; for (i=0 ; ispecial) continue; switch (sector->special) { case 1: // FLICKERING LIGHTS 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 = 4; break; case 8: // GLOWING LIGHT P_SpawnGlowingLight(sector); break; case 9: // SECRET SECTOR totalsecret++; 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: P_SpawnFireFlicker(sector); break; } } // Init line EFFECTs numlinespecials = 0; for (i = 0;i < numlines; i++) { switch(lines[i].special) { case 48: if (numlinespecials >= MAXLINEANIMS) { I_Error("Too many scrolling wall linedefs! " "(Vanilla limit is 64)"); } // EFFECT FIRSTCOL SCROLL+ linespeciallist[numlinespecials] = &lines[i]; numlinespecials++; break; } } // Init other misc stuff for (i = 0;i < MAXCEILINGS;i++) activeceilings[i] = NULL; for (i = 0;i < MAXPLATS;i++) activeplats[i] = NULL; for (i = 0;i < MAXBUTTONS;i++) memset(&buttonlist[i],0,sizeof(button_t)); // UNUSED: no horizonal sliders. // P_InitSlidingDoorFrames(); } ================================================ FILE: fbdoom/p_spec.h ================================================ // // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // 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. // // DESCRIPTION: none // Implements special effects: // Texture animation, height or lighting changes // according to adjacent sectors, respective // utility functions, etc. // #ifndef __P_SPEC__ #define __P_SPEC__ // // End-level timer (-TIMER option) // extern boolean levelTimer; extern int levelTimeCount; // Define values for map objects #define MO_TELEPORTMAN 14 // at game start void P_InitPicAnims (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 ( int linenum, int side, mobj_t* thing ); void P_PlayerInSpecialSector (player_t* player); 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_FindLowestCeilingSurrounding(sector_t* sec); fixed_t P_FindHighestCeilingSurrounding(sector_t* sec); int P_FindSectorFromLineTag ( line_t* line, int start ); int P_FindMinSurroundingLight ( sector_t* sector, int max ); sector_t* getNextSector ( line_t* line, sector_t* sec ); // // SPECIAL // int EV_DoDonut(line_t* line); // // 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; #define GLOWSPEED 8 #define STROBEBRIGHT 5 #define FASTDARK 15 #define SLOWDARK 35 void P_SpawnFireFlicker (sector_t* sector); void T_LightFlash (lightflash_t* flash); void P_SpawnLightFlash (sector_t* sector); void T_StrobeFlash (strobe_t* flash); void P_SpawnStrobeFlash ( sector_t* sector, int fastOrSlow, int inSync ); void EV_StartLightStrobing(line_t* line); void EV_TurnTagLightsOff(line_t* line); void EV_LightTurnOn ( line_t* line, int bright ); void T_Glow(glow_t* g); void P_SpawnGlowingLight(sector_t* sector); // // P_SWITCH // typedef struct { char name1[9]; char name2[9]; short episode; } switchlist_t; typedef enum { top, middle, bottom } bwhere_e; typedef struct { line_t* line; bwhere_e where; int btexture; int btimer; degenmobj_t *soundorg; } button_t; // max # of wall switches in a level #define MAXSWITCHES 50 // 4 players, 4 buttons each at once, max. #define MAXBUTTONS 16 // 1 second, in ticks. #define BUTTONTIME 35 extern button_t buttonlist[MAXBUTTONS]; void P_ChangeSwitchTexture ( line_t* line, int useAgain ); void P_InitSwitchList(void); // // P_PLATS // typedef enum { up, down, waiting, in_stasis } plat_e; typedef enum { perpetualRaise, downWaitUpStay, raiseAndChange, raiseToNearestAndChange, blazeDWUS } plattype_e; 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; } plat_t; #define PLATWAIT 3 #define PLATSPEED FRACUNIT #define MAXPLATS 30 extern plat_t* activeplats[MAXPLATS]; void T_PlatRaise(plat_t* plat); int EV_DoPlat ( line_t* line, plattype_e type, int amount ); void P_AddActivePlat(plat_t* plat); void P_RemoveActivePlat(plat_t* plat); void EV_StopPlat(line_t* line); void P_ActivateInStasis(int tag); // // P_DOORS // typedef enum { vld_normal, vld_close30ThenOpen, vld_close, vld_open, vld_raiseIn5Mins, vld_blazeRaise, vld_blazeOpen, vld_blazeClose } vldoor_e; 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; } vldoor_t; #define VDOORSPEED FRACUNIT*2 #define VDOORWAIT 150 void 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 ); void T_VerticalDoor (vldoor_t* door); void P_SpawnDoorCloseIn30 (sector_t* sec); void P_SpawnDoorRaiseIn5Mins ( sector_t* sec, int secnum ); #if 0 // UNUSED // // Sliding doors... // typedef enum { sd_opening, sd_waiting, sd_closing } sd_e; typedef enum { sdt_openOnly, sdt_closeOnly, sdt_openAndClose } sdt_e; typedef struct { thinker_t thinker; sdt_e type; line_t* line; int frame; int whichDoorIndex; int timer; sector_t* frontsector; sector_t* backsector; sd_e status; } slidedoor_t; typedef struct { char frontFrame1[9]; char frontFrame2[9]; char frontFrame3[9]; char frontFrame4[9]; char backFrame1[9]; char backFrame2[9]; char backFrame3[9]; char backFrame4[9]; } slidename_t; typedef struct { int frontFrames[4]; int backFrames[4]; } slideframe_t; // how many frames of animation #define SNUMFRAMES 4 #define SDOORWAIT 35*3 #define SWAITTICS 4 // how many diff. types of anims #define MAXSLIDEDOORS 5 void P_InitSlidingDoorFrames(void); void EV_SlidingDoor ( line_t* line, mobj_t* thing ); #endif // // P_CEILNG // typedef enum { lowerToFloor, raiseToHighest, lowerAndCrush, crushAndRaise, fastCrushAndRaise, silentCrushAndRaise } ceiling_e; typedef struct { thinker_t thinker; ceiling_e type; sector_t* sector; fixed_t bottomheight; fixed_t topheight; fixed_t speed; boolean crush; // 1 = up, 0 = waiting, -1 = down int direction; // ID int tag; int olddirection; } ceiling_t; #define CEILSPEED FRACUNIT #define CEILWAIT 150 #define MAXCEILINGS 30 extern ceiling_t* activeceilings[MAXCEILINGS]; int EV_DoCeiling ( line_t* line, ceiling_e type ); void T_MoveCeiling (ceiling_t* ceiling); void P_AddActiveCeiling(ceiling_t* c); void P_RemoveActiveCeiling(ceiling_t* c); int EV_CeilingCrushStop(line_t* line); void P_ActivateInStasisCeiling(line_t* line); // // 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, // raise floor to shortest height texture around it raiseToTexture, // lower floor to lowest surrounding floor // and change floorpic lowerAndChange, raiseFloor24, raiseFloor24AndChange, raiseFloorCrush, // raise to next highest floor, turbo-speed raiseFloorTurbo, donutRaise, raiseFloor512 } floor_e; typedef enum { build8, // slowly build by 8 turbo16 // quickly build by 16 } stair_e; typedef struct { thinker_t thinker; floor_e type; boolean crush; sector_t* sector; int direction; int newspecial; short texture; fixed_t floordestheight; fixed_t speed; } floormove_t; #define FLOORSPEED FRACUNIT typedef enum { ok, crushed, pastdest } result_e; result_e T_MovePlane ( sector_t* sector, fixed_t speed, fixed_t dest, boolean crush, int floorOrCeiling, int direction ); int EV_BuildStairs ( line_t* line, stair_e type ); int EV_DoFloor ( line_t* line, floor_e floortype ); void T_MoveFloor( floormove_t* floor); // // P_TELEPT // int EV_Teleport ( line_t* line, int side, mobj_t* thing ); #endif ================================================ FILE: fbdoom/p_switch.c ================================================ // // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // 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. // // // DESCRIPTION: // Switches, buttons. Two-state animation. Exits. // #include #include "i_system.h" #include "deh_main.h" #include "doomdef.h" #include "p_local.h" #include "g_game.h" #include "s_sound.h" // Data. #include "sounds.h" // State. #include "doomstat.h" #include "r_state.h" // // CHANGE THE TEXTURE OF A WALL SWITCH TO ITS OPPOSITE // switchlist_t alphSwitchList[] = { // Doom shareware episode 1 switches {"SW1BRCOM", "SW2BRCOM", 1}, {"SW1BRN1", "SW2BRN1", 1}, {"SW1BRN2", "SW2BRN2", 1}, {"SW1BRNGN", "SW2BRNGN", 1}, {"SW1BROWN", "SW2BROWN", 1}, {"SW1COMM", "SW2COMM", 1}, {"SW1COMP", "SW2COMP", 1}, {"SW1DIRT", "SW2DIRT", 1}, {"SW1EXIT", "SW2EXIT", 1}, {"SW1GRAY", "SW2GRAY", 1}, {"SW1GRAY1", "SW2GRAY1", 1}, {"SW1METAL", "SW2METAL", 1}, {"SW1PIPE", "SW2PIPE", 1}, {"SW1SLAD", "SW2SLAD", 1}, {"SW1STARG", "SW2STARG", 1}, {"SW1STON1", "SW2STON1", 1}, {"SW1STON2", "SW2STON2", 1}, {"SW1STONE", "SW2STONE", 1}, {"SW1STRTN", "SW2STRTN", 1}, // Doom registered episodes 2&3 switches {"SW1BLUE", "SW2BLUE", 2}, {"SW1CMT", "SW2CMT", 2}, {"SW1GARG", "SW2GARG", 2}, {"SW1GSTON", "SW2GSTON", 2}, {"SW1HOT", "SW2HOT", 2}, {"SW1LION", "SW2LION", 2}, {"SW1SATYR", "SW2SATYR", 2}, {"SW1SKIN", "SW2SKIN", 2}, {"SW1VINE", "SW2VINE", 2}, {"SW1WOOD", "SW2WOOD", 2}, // Doom II switches {"SW1PANEL", "SW2PANEL", 3}, {"SW1ROCK", "SW2ROCK", 3}, {"SW1MET2", "SW2MET2", 3}, {"SW1WDMET", "SW2WDMET", 3}, {"SW1BRIK", "SW2BRIK", 3}, {"SW1MOD1", "SW2MOD1", 3}, {"SW1ZIM", "SW2ZIM", 3}, {"SW1STON6", "SW2STON6", 3}, {"SW1TEK", "SW2TEK", 3}, {"SW1MARB", "SW2MARB", 3}, {"SW1SKULL", "SW2SKULL", 3}, {"\0", "\0", 0} }; int switchlist[MAXSWITCHES * 2]; int numswitches; button_t buttonlist[MAXBUTTONS]; // // P_InitSwitchList // Only called at game initialization. // void P_InitSwitchList(void) { int i; int index; int episode; episode = 1; if (gamemode == registered || gamemode == retail) episode = 2; else if ( gamemode == commercial ) episode = 3; for (index = 0,i = 0;i < MAXSWITCHES;i++) { if (!alphSwitchList[i].episode) { numswitches = index/2; switchlist[index] = -1; break; } if (alphSwitchList[i].episode <= episode) { #if 0 // UNUSED - debug? int value; if (R_CheckTextureNumForName(alphSwitchList[i].name1) < 0) { I_Error("Can't find switch texture '%s'!", alphSwitchList[i].name1); continue; } value = R_TextureNumForName(alphSwitchList[i].name1); #endif switchlist[index++] = R_TextureNumForName(DEH_String(alphSwitchList[i].name1)); switchlist[index++] = R_TextureNumForName(DEH_String(alphSwitchList[i].name2)); } } } // // Start a button counting down till it turns off. // 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) { buttonlist[i].line = line; buttonlist[i].where = w; buttonlist[i].btexture = texture; buttonlist[i].btimer = time; buttonlist[i].soundorg = &line->frontsector->soundorg; return; } } I_Error("P_StartButton: no button slots left!"); } // // Function that changes wall texture. // Tell it if switch is ok to use again (1=yes, it's a button). // void P_ChangeSwitchTexture ( line_t* line, int useAgain ) { int texTop; int texMid; int texBot; int i; int sound; if (!useAgain) line->special = 0; texTop = sides[line->sidenum[0]].toptexture; texMid = sides[line->sidenum[0]].midtexture; texBot = sides[line->sidenum[0]].bottomtexture; sound = sfx_swtchn; // EXIT SWITCH? if (line->special == 11) sound = sfx_swtchx; for (i = 0;i < numswitches*2;i++) { if (switchlist[i] == texTop) { S_StartSound(buttonlist->soundorg,sound); sides[line->sidenum[0]].toptexture = switchlist[i^1]; if (useAgain) P_StartButton(line,top,switchlist[i],BUTTONTIME); return; } else { if (switchlist[i] == texMid) { S_StartSound(buttonlist->soundorg,sound); sides[line->sidenum[0]].midtexture = switchlist[i^1]; if (useAgain) P_StartButton(line, middle,switchlist[i],BUTTONTIME); return; } else { if (switchlist[i] == texBot) { S_StartSound(buttonlist->soundorg,sound); sides[line->sidenum[0]].bottomtexture = switchlist[i^1]; if (useAgain) P_StartButton(line, bottom,switchlist[i],BUTTONTIME); return; } } } } } // // P_UseSpecialLine // Called when a thing uses a special line. // Only the front sides of lines are usable. // boolean P_UseSpecialLine ( mobj_t* thing, line_t* line, int side ) { // Err... // Use the back sides of VERY SPECIAL lines... if (side) { switch(line->special) { case 124: // Sliding door open&close // UNUSED? break; default: return false; break; } } // 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 break; default: return false; break; } } // do something switch (line->special) { // MANUALS 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; //UNUSED - Door Slide Open&Close // case 124: // EV_SlidingDoor (line, thing); // break; // SWITCHES 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 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,vld_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,vld_close)) P_ChangeSwitchTexture(line,0); break; case 51: // Secret EXIT 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,vld_open)) P_ChangeSwitchTexture(line,0); break; case 111: // Blazing Door Raise (faster than TURBO!) if (EV_DoDoor (line,vld_blazeRaise)) P_ChangeSwitchTexture(line,0); break; case 112: // Blazing Door Open (faster than TURBO!) if (EV_DoDoor (line,vld_blazeOpen)) P_ChangeSwitchTexture(line,0); break; case 113: // Blazing Door Close (faster than TURBO!) if (EV_DoDoor (line,vld_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,vld_blazeOpen,thing)) P_ChangeSwitchTexture(line,0); break; case 140: // Raise Floor 512 if (EV_DoFloor(line,raiseFloor512)) P_ChangeSwitchTexture(line,0); break; // BUTTONS case 42: // Close Door if (EV_DoDoor(line,vld_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,vld_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,vld_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,vld_blazeRaise)) P_ChangeSwitchTexture(line,1); break; case 115: // Blazing Door Open (faster than TURBO!) if (EV_DoDoor (line,vld_blazeOpen)) P_ChangeSwitchTexture(line,1); break; case 116: // Blazing Door Close (faster than TURBO!) if (EV_DoDoor (line,vld_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,vld_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; } ================================================ FILE: fbdoom/p_telept.c ================================================ // // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // 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. // // DESCRIPTION: // Teleportation. // #include "doomdef.h" #include "doomstat.h" #include "s_sound.h" #include "p_local.h" // Data. #include "sounds.h" // State. #include "r_state.h" // // TELEPORTATION // int EV_Teleport ( line_t* line, int side, mobj_t* thing ) { int i; int tag; mobj_t* m; mobj_t* fog; unsigned an; thinker_t* thinker; sector_t* sector; fixed_t oldx; fixed_t oldy; fixed_t oldz; // don't teleport missiles if (thing->flags & MF_MISSILE) return 0; // Don't teleport if hit back of line, // so you can get out of teleporter. if (side == 1) return 0; tag = line->tag; for (i = 0; i < numsectors; i++) { if (sectors[ i ].tag == tag ) { thinker = thinkercap.next; for (thinker = thinkercap.next; thinker != &thinkercap; thinker = thinker->next) { // not a mobj if (thinker->function.acp1 != (actionf_p1)P_MobjThinker) continue; m = (mobj_t *)thinker; // not a teleportman if (m->type != MT_TELEPORTMAN ) continue; sector = m->subsector->sector; // wrong sector if (sector-sectors != i ) continue; oldx = thing->x; oldy = thing->y; oldz = thing->z; if (!P_TeleportMove (thing, m->x, m->y)) return 0; // The first Final Doom executable does not set thing->z // when teleporting. This quirk is unique to this // particular version; the later version included in // some versions of the Id Anthology fixed this. if (gameversion != exe_final) thing->z = thing->floorz; if (thing->player) thing->player->viewz = thing->z+thing->player->viewheight; // spawn teleport fog at source and destination fog = P_SpawnMobj (oldx, oldy, oldz, MT_TFOG); S_StartSound (fog, sfx_telept); an = m->angle >> ANGLETOFINESHIFT; fog = P_SpawnMobj (m->x+20*finecosine[an], m->y+20*finesine[an] , thing->z, MT_TFOG); // emit sound, where? S_StartSound (fog, sfx_telept); // don't move for a bit if (thing->player) thing->reactiontime = 18; thing->angle = m->angle; thing->momx = thing->momy = thing->momz = 0; return 1; } } } return 0; } ================================================ FILE: fbdoom/p_tick.c ================================================ // // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // 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. // // DESCRIPTION: // Archiving: SaveGame I/O. // Thinker, Ticker. // #include "z_zone.h" #include "p_local.h" #include "doomstat.h" int leveltime; // // 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. // // Both the head and tail of the thinker list. thinker_t thinkercap; // // P_InitThinkers // void P_InitThinkers (void) { thinkercap.prev = thinkercap.next = &thinkercap; } // // 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; } // // P_RemoveThinker // Deallocation is lazy -- it will not actually be freed // until its thinking turn comes up. // void P_RemoveThinker (thinker_t* thinker) { // FIXME: NOP. thinker->function.acv = (actionf_v)(-1); } // // P_AllocateThinker // Allocates memory and adds a new thinker at the end of the list. // void P_AllocateThinker (thinker_t* thinker) { } // // P_RunThinkers // void P_RunThinkers (void) { thinker_t* currentthinker; currentthinker = thinkercap.next; while (currentthinker != &thinkercap) { if ( currentthinker->function.acv == (actionf_v)(-1) ) { // time to remove it currentthinker->next->prev = currentthinker->prev; currentthinker->prev->next = currentthinker->next; Z_Free (currentthinker); } else { if (currentthinker->function.acp1) currentthinker->function.acp1 (currentthinker); } currentthinker = currentthinker->next; } } // // P_Ticker // void P_Ticker (void) { int i; // run the tic if (paused) return; // pause if in menu and at least one tic has been run if ( !netgame && menuactive && !demoplayback && players[consoleplayer].viewz != 1) { return; } for (i=0 ; i>= ANGLETOFINESHIFT; player->mo->momx += FixedMul(move,finecosine[angle]); player->mo->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. player->bob = FixedMul (player->mo->momx, player->mo->momx) + FixedMul (player->mo->momy,player->mo->momy); player->bob >>= 2; if (player->bob>MAXBOB) player->bob = MAXBOB; if ((player->cheats & CF_NOMOMENTUM) || !onground) { player->viewz = player->mo->z + VIEWHEIGHT; if (player->viewz > player->mo->ceilingz-4*FRACUNIT) player->viewz = player->mo->ceilingz-4*FRACUNIT; 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 // void P_MovePlayer (player_t* player) { ticcmd_t* cmd; cmd = &player->cmd; player->mo->angle += (cmd->angleturn<<16); // Do not let the player control movement // if not onground. onground = (player->mo->z <= player->mo->floorz); if (cmd->forwardmove && onground) P_Thrust (player, player->mo->angle, cmd->forwardmove*2048); if (cmd->sidemove && onground) P_Thrust (player, player->mo->angle-ANG90, cmd->sidemove*2048); if ( (cmd->forwardmove || cmd->sidemove) && player->mo->state == &states[S_PLAY] ) { P_SetMobjState (player->mo, S_PLAY_RUN1); } } // // P_DeathThink // Fall on your face when dying. // Decrease POV height to floor height. // #define ANG5 (ANG90/18) 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; } // // P_PlayerThink // void P_PlayerThink (player_t* player) { ticcmd_t* cmd; weapontype_t newweapon; // fixme: do this in the cheat code 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); if (player->mo->subsector->sector->special) P_PlayerInSpecialSector (player); // Check for weapon change. // A special event has no other buttons. if (cmd->buttons & BT_SPECIAL) cmd->buttons = 0; 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; 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; } 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 dependend power ups. // Strength counts up to diminish fade. if (player->powers[pw_strength]) player->powers[pw_strength]++; if (player->powers[pw_invulnerability]) player->powers[pw_invulnerability]--; if (player->powers[pw_invisibility]) if (! --player->powers[pw_invisibility] ) player->mo->flags &= ~MF_SHADOW; if (player->powers[pw_infrared]) player->powers[pw_infrared]--; if (player->powers[pw_ironfeet]) player->powers[pw_ironfeet]--; if (player->damagecount) player->damagecount--; if (player->bonuscount) player->bonuscount--; // Handling colormaps. if (player->powers[pw_invulnerability]) { if (player->powers[pw_invulnerability] > 4*32 || (player->powers[pw_invulnerability]&8) ) player->fixedcolormap = INVERSECOLORMAP; else player->fixedcolormap = 0; } else if (player->powers[pw_infrared]) { if (player->powers[pw_infrared] > 4*32 || (player->powers[pw_infrared]&8) ) { // almost full bright player->fixedcolormap = 1; } else player->fixedcolormap = 0; } else player->fixedcolormap = 0; } ================================================ FILE: fbdoom/r_bsp.c ================================================ // // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // 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. // // DESCRIPTION: // BSP traversal, handling of LineSegs for rendering. // #include "doomdef.h" #include "m_bbox.h" #include "i_system.h" #include "r_main.h" #include "r_plane.h" #include "r_things.h" // State. #include "doomstat.h" #include "r_state.h" //#include "r_local.h" seg_t* curline; side_t* sidedef; line_t* linedef; sector_t* frontsector; sector_t* backsector; drawseg_t drawsegs[MAXDRAWSEGS]; drawseg_t* ds_p; void R_StoreWallRange ( int start, int stop ); // // R_ClearDrawSegs // void R_ClearDrawSegs (void) { ds_p = drawsegs; } // // ClipWallSegment // Clips the given range of columns // and includes it in the new clip list. // typedef struct { int first; int last; } cliprange_t; #define MAXSEGS 32 // newend is one past the last valid seg cliprange_t* newend; cliprange_t solidsegs[MAXSEGS]; // // R_ClipSolidWallSegment // Does handle solid walls, // e.g. single sided LineDefs (middle texture) // that entirely block the view. // void R_ClipSolidWallSegment ( int first, int last ) { cliprange_t* next; cliprange_t* start; // Find the first range that touches the range // (adjacent pixels are touching). start = solidsegs; while (start->last < first-1) start++; if (first < start->first) { if (last < start->first-1) { // Post is entirely visible (above start), // so insert a new clippost. R_StoreWallRange (first, last); next = newend; newend++; while (next != start) { *next = *(next-1); next--; } next->first = first; next->last = last; return; } // There is a fragment above *start. R_StoreWallRange (first, start->first - 1); // Now adjust the clip size. start->first = first; } // Bottom contained in start? if (last <= start->last) return; next = start; while (last >= (next+1)->first-1) { // There is a fragment between two posts. R_StoreWallRange (next->last + 1, (next+1)->first - 1); next++; if (last <= next->last) { // Bottom is contained in next. // Adjust the clip size. start->last = next->last; goto crunch; } } // There is a fragment after *next. R_StoreWallRange (next->last + 1, last); // Adjust the clip size. start->last = last; // Remove start+1 to next from the clip list, // because start now covers their area. crunch: if (next == start) { // Post just extended past the bottom of one post. return; } while (next++ != newend) { // Remove a post. *++start = *next; } newend = start+1; } // // R_ClipPassWallSegment // Clips the given range of columns, // but does not includes it in the clip list. // Does handle windows, // e.g. LineDefs with upper and lower texture. // void R_ClipPassWallSegment ( int first, int last ) { cliprange_t* start; // Find the first range that touches the range // (adjacent pixels are touching). start = solidsegs; while (start->last < first-1) start++; if (first < start->first) { if (last < start->first-1) { // Post is entirely visible (above start). R_StoreWallRange (first, last); return; } // There is a fragment above *start. R_StoreWallRange (first, start->first - 1); } // Bottom contained in start? if (last <= start->last) return; while (last >= (start+1)->first-1) { // There is a fragment between two posts. R_StoreWallRange (start->last + 1, (start+1)->first - 1); start++; if (last <= start->last) return; } // There is a fragment after *next. R_StoreWallRange (start->last + 1, last); } // // R_ClearClipSegs // void R_ClearClipSegs (void) { solidsegs[0].first = -0x7fffffff; solidsegs[0].last = -1; solidsegs[1].first = viewwidth; solidsegs[1].last = 0x7fffffff; newend = solidsegs+2; } // // R_AddLine // Clips the given segment // and adds any visible pieces to the line list. // void R_AddLine (seg_t* line) { int x1; int x2; angle_t angle1; angle_t angle2; angle_t span; angle_t tspan; curline = line; // OPTIMIZE: quickly reject orthogonal back sides. angle1 = R_PointToAngle (line->v1->x, line->v1->y); angle2 = R_PointToAngle (line->v2->x, line->v2->y); // Clip to view edges. // OPTIMIZE: make constant out of 2*clipangle (FIELDOFVIEW). 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 = -clipangle; } // The seg is in the view range, // but not necessarily visible. angle1 = (angle1+ANG90)>>ANGLETOFINESHIFT; angle2 = (angle2+ANG90)>>ANGLETOFINESHIFT; x1 = viewangletox[angle1]; x2 = viewangletox[angle2]; // Does not cross a pixel? if (x1 == x2) return; backsector = line->backsector; // Single sided line? if (!backsector) goto clipsolid; // Closed door. if (backsector->ceilingheight <= frontsector->floorheight || backsector->floorheight >= frontsector->ceilingheight) goto clipsolid; // Window. if (backsector->ceilingheight != frontsector->ceilingheight || backsector->floorheight != frontsector->floorheight) goto clippass; // 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. if (backsector->ceilingpic == frontsector->ceilingpic && backsector->floorpic == frontsector->floorpic && backsector->lightlevel == frontsector->lightlevel && curline->sidedef->midtexture == 0) { return; } clippass: R_ClipPassWallSegment (x1, x2-1); return; clipsolid: R_ClipSolidWallSegment (x1, x2-1); } // // R_CheckBBox // Checks BSP node/subtree bounding box. // Returns true // if some part of the bbox might be visible. // int checkcoord[12][4] = { {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} }; boolean R_CheckBBox (fixed_t* bspcoord) { int boxx; int boxy; int boxpos; fixed_t x1; fixed_t y1; fixed_t x2; fixed_t y2; angle_t angle1; angle_t angle2; angle_t span; angle_t tspan; cliprange_t* start; int sx1; int sx2; // Find the corners of the box // that define the edges from current viewpoint. if (viewx <= bspcoord[BOXLEFT]) boxx = 0; else if (viewx < bspcoord[BOXRIGHT]) boxx = 1; else boxx = 2; if (viewy >= bspcoord[BOXTOP]) boxy = 0; else if (viewy > bspcoord[BOXBOTTOM]) boxy = 1; else boxy = 2; boxpos = (boxy<<2)+boxx; if (boxpos == 5) return true; x1 = bspcoord[checkcoord[boxpos][0]]; y1 = bspcoord[checkcoord[boxpos][1]]; x2 = bspcoord[checkcoord[boxpos][2]]; y2 = bspcoord[checkcoord[boxpos][3]]; // check clip list for an open space angle1 = R_PointToAngle (x1, y1) - viewangle; angle2 = R_PointToAngle (x2, y2) - viewangle; span = angle1 - angle2; // Sitting on a line? if (span >= ANG180) return true; tspan = angle1 + clipangle; if (tspan > 2*clipangle) { tspan -= 2*clipangle; // Totally off the left edge? if (tspan >= span) return false; angle1 = clipangle; } tspan = clipangle - angle2; if (tspan > 2*clipangle) { tspan -= 2*clipangle; // Totally off the left edge? if (tspan >= span) return false; angle2 = -clipangle; } // Find the first clippost // that touches the source post // (adjacent pixels are touching). angle1 = (angle1+ANG90)>>ANGLETOFINESHIFT; angle2 = (angle2+ANG90)>>ANGLETOFINESHIFT; sx1 = viewangletox[angle1]; sx2 = viewangletox[angle2]; // Does not cross a pixel. if (sx1 == sx2) return false; sx2--; start = solidsegs; while (start->last < sx2) start++; if (sx1 >= start->first && sx2 <= start->last) { // The clippost contains the new span. return false; } return true; } // // R_Subsector // Determine floor/ceiling planes. // Add sprites of things in sector. // Draw one or more line segments. // void R_Subsector (int num) { int count; seg_t* line; subsector_t* sub; #ifdef RANGECHECK if (num>=numsubsectors) I_Error ("R_Subsector: ss %i with numss = %i", num, numsubsectors); #endif sscount++; sub = &subsectors[num]; frontsector = sub->sector; count = sub->numlines; line = &segs[sub->firstline]; if (frontsector->floorheight < viewz) { floorplane = R_FindPlane (frontsector->floorheight, frontsector->floorpic, frontsector->lightlevel); } else floorplane = NULL; if (frontsector->ceilingheight > viewz || frontsector->ceilingpic == skyflatnum) { ceilingplane = R_FindPlane (frontsector->ceilingheight, frontsector->ceilingpic, frontsector->lightlevel); } else ceilingplane = NULL; R_AddSprites (frontsector); while (count--) { R_AddLine (line); line++; } } // // RenderBSPNode // Renders all subsectors below a given node, // traversing subtree recursively. // Just call with BSP root. void R_RenderBSPNode (int bspnum) { node_t* bsp; int side; // Found a subsector? if (bspnum & NF_SUBSECTOR) { if (bspnum == -1) R_Subsector (0); else R_Subsector (bspnum&(~NF_SUBSECTOR)); return; } bsp = &nodes[bspnum]; // Decide which side the view point is on. 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])) R_RenderBSPNode (bsp->children[side^1]); } ================================================ FILE: fbdoom/r_bsp.h ================================================ // // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // 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. // // DESCRIPTION: // Refresh module, BSP traversal and handling. // #ifndef __R_BSP__ #define __R_BSP__ extern seg_t* curline; extern side_t* sidedef; extern line_t* linedef; extern sector_t* frontsector; extern sector_t* backsector; extern int rw_x; extern int rw_stopx; extern boolean segtextured; // false if the back side is the same plane extern boolean markfloor; extern boolean markceiling; extern boolean skymap; extern drawseg_t drawsegs[MAXDRAWSEGS]; extern drawseg_t* ds_p; extern lighttable_t** hscalelight; extern lighttable_t** vscalelight; extern lighttable_t** dscalelight; typedef void (*drawfunc_t) (int start, int stop); // BSP? void R_ClearClipSegs (void); void R_ClearDrawSegs (void); void R_RenderBSPNode (int bspnum); #endif ================================================ FILE: fbdoom/r_data.c ================================================ // // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // 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. // // DESCRIPTION: // Preparation of data for rendering, // generation of lookups, caching, retrieval by name. // #include #include "deh_main.h" #include "i_swap.h" #include "i_system.h" #include "z_zone.h" #include "w_wad.h" #include "doomdef.h" #include "m_misc.h" #include "r_local.h" #include "p_local.h" #include "doomstat.h" #include "r_sky.h" #include "r_data.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; short colormap; } PACKEDATTR mappatch_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]; int masked; short width; short height; int obsolete; short patchcount; mappatch_t patches[1]; } PACKEDATTR maptexture_t; // A single patch from a texture definition, // basically a rectangular area within // the texture rectangle. typedef struct { // Block origin (allways UL), // which has allready accounted // for the internal origin of the patch. short originx; short originy; int patch; } texpatch_t; // A maptexturedef_t describes a rectangular texture, // which is composed of one or more mappatch_t structures // that arrange graphic patches. typedef struct texture_s texture_t; struct texture_s { // Keep name for switch changing, etc. char name[8]; short width; short height; // Index in textures list int index; // Next in hash table chain texture_t *next; // All the patches[patchcount] // are drawn back to front into the cached texture. short patchcount; texpatch_t patches[1]; }; int firstflat; int lastflat; int numflats; int firstpatch; int lastpatch; int numpatches; int firstspritelump; int lastspritelump; int numspritelumps; int numtextures; texture_t** textures; texture_t** textures_hashtable; int* texturewidthmask; // needed for texture pegging fixed_t* textureheight; int* texturecompositesize; short** texturecolumnlump; unsigned short** texturecolumnofs; byte** texturecomposite; // for global animation int* flattranslation; int* texturetranslation; // needed for pre rendering fixed_t* spritewidth; fixed_t* spriteoffset; fixed_t* spritetopoffset; lighttable_t *colormaps; // // MAPTEXTURE_T CACHING // When a texture is first needed, // it counts the number of composite columns // required in the texture and allocates space // for a column directory and any new columns. // The directory will simply point inside other patches // if there is only one patch in a given column, // but any columns with multiple patches // will have new column_ts generated. // // // R_DrawColumnInCache // Clip and draw a column // from a patch into a cached post. // void R_DrawColumnInCache ( column_t* patch, byte* cache, int originy, int cacheheight ) { int count; int position; byte* source; while (patch->topdelta != 0xff) { source = (byte *)patch + 3; count = patch->length; position = originy + patch->topdelta; if (position < 0) { count += position; position = 0; } if (position + count > cacheheight) count = cacheheight - position; if (count > 0) memcpy (cache + position, source, count); patch = (column_t *)( (byte *)patch + patch->length + 4); } } // // R_GenerateComposite // Using the texture definition, // the composite texture is created from the patches, // and each column is cached. // void R_GenerateComposite (int texnum) { byte* block; texture_t* texture; texpatch_t* patch; patch_t* realpatch; int x; int x1; int x2; int i; column_t* patchcol; short* collump; unsigned short* colofs; texture = textures[texnum]; block = Z_Malloc (texturecompositesize[texnum], PU_STATIC, &texturecomposite[texnum]); collump = texturecolumnlump[texnum]; colofs = texturecolumnofs[texnum]; // Composite the columns together. patch = texture->patches; for (i=0 , patch = texture->patches; ipatchcount; i++, patch++) { realpatch = W_CacheLumpNum (patch->patch, PU_CACHE); x1 = patch->originx; x2 = x1 + SHORT(realpatch->width); if (x1<0) x = 0; else x = x1; if (x2 > texture->width) x2 = texture->width; for ( ; x= 0) continue; patchcol = (column_t *)((byte *)realpatch + LONG(realpatch->columnofs[x-x1])); R_DrawColumnInCache (patchcol, block + colofs[x], patch->originy, texture->height); } } // Now that the texture has been built in column cache, // it is purgable from zone memory. Z_ChangeTag (block, PU_CACHE); } // // R_GenerateLookup // void R_GenerateLookup (int texnum) { texture_t* texture; byte* patchcount; // patchcount[texture->width] texpatch_t* patch; patch_t* realpatch; int x; int x1; int x2; int i; short* collump; unsigned short* colofs; texture = textures[texnum]; // Composited texture not created yet. texturecomposite[texnum] = 0; texturecompositesize[texnum] = 0; collump = texturecolumnlump[texnum]; colofs = texturecolumnofs[texnum]; // Now count the number of columns // that are covered by more than one patch. // Fill in the lump / offset, so columns // with only a single patch are all done. patchcount = (byte *) Z_Malloc(texture->width, PU_STATIC, &patchcount); memset (patchcount, 0, texture->width); patch = texture->patches; for (i=0 , patch = texture->patches; ipatchcount; i++, patch++) { realpatch = W_CacheLumpNum (patch->patch, PU_CACHE); x1 = patch->originx; x2 = x1 + SHORT(realpatch->width); if (x1 < 0) x = 0; else x = x1; if (x2 > texture->width) x2 = texture->width; for ( ; xpatch; colofs[x] = LONG(realpatch->columnofs[x-x1])+3; } } for (x=0 ; xwidth ; x++) { if (!patchcount[x]) { printf ("R_GenerateLookup: column without a patch (%s)\n", texture->name); return; } // I_Error ("R_GenerateLookup: column without a patch"); if (patchcount[x] > 1) { // Use the cached block. collump[x] = -1; colofs[x] = texturecompositesize[texnum]; if (texturecompositesize[texnum] > 0x10000-texture->height) { I_Error ("R_GenerateLookup: texture %i is >64k", texnum); } texturecompositesize[texnum] += texture->height; } } Z_Free(patchcount); } // // R_GetColumn // byte* R_GetColumn ( int tex, int col ) { int lump; int ofs; col &= texturewidthmask[tex]; lump = texturecolumnlump[tex][col]; ofs = texturecolumnofs[tex][col]; if (lump > 0) return (byte *)W_CacheLumpNum(lump,PU_CACHE)+ofs; if (!texturecomposite[tex]) R_GenerateComposite (tex); return texturecomposite[tex] + ofs; } static void GenerateTextureHashTable(void) { texture_t **rover; int i; int key; textures_hashtable = Z_Malloc(sizeof(texture_t *) * numtextures, PU_STATIC, 0); memset(textures_hashtable, 0, sizeof(texture_t *) * numtextures); // Add all textures to hash table for (i=0; iindex = i; // Vanilla Doom does a linear search of the texures array // and stops at the first entry it finds. If there are two // entries with the same name, the first one in the array // wins. The new entry must therefore be added at the end // of the hash chain, so that earlier entries win. key = W_LumpNameHash(textures[i]->name) % numtextures; rover = &textures_hashtable[key]; while (*rover != NULL) { rover = &(*rover)->next; } // Hook into hash table textures[i]->next = NULL; *rover = textures[i]; } } // // R_InitTextures // Initializes the texture list // with the textures from the world map. // void R_InitTextures (void) { maptexture_t* mtexture; texture_t* texture; mappatch_t* mpatch; texpatch_t* patch; int i; int j; int* maptex; int* maptex2; int* maptex1; char name[9]; char* names; char* name_p; int* patchlookup; int totalwidth; int nummappatches; int offset; int maxoff; int maxoff2; int numtextures1; int numtextures2; int* directory; int temp1; int temp2; int temp3; // Load the patch names from pnames.lmp. name[8] = 0; names = W_CacheLumpName (DEH_String("PNAMES"), PU_STATIC); nummappatches = LONG ( *((int *)names) ); name_p = names + 4; patchlookup = Z_Malloc(nummappatches*sizeof(*patchlookup), PU_STATIC, NULL); for (i = 0; i < nummappatches; i++) { M_StringCopy(name, name_p + i * 8, sizeof(name)); patchlookup[i] = W_CheckNumForName(name); } W_ReleaseLumpName(DEH_String("PNAMES")); // Load the map texture definitions from textures.lmp. // The data is contained in one or two lumps, // TEXTURE1 for shareware, plus TEXTURE2 for commercial. maptex = maptex1 = W_CacheLumpName (DEH_String("TEXTURE1"), PU_STATIC); numtextures1 = LONG(*maptex); maxoff = W_LumpLength (W_GetNumForName (DEH_String("TEXTURE1"))); directory = maptex+1; if (W_CheckNumForName (DEH_String("TEXTURE2")) != -1) { maptex2 = W_CacheLumpName (DEH_String("TEXTURE2"), PU_STATIC); numtextures2 = LONG(*maptex2); maxoff2 = W_LumpLength (W_GetNumForName (DEH_String("TEXTURE2"))); } else { maptex2 = NULL; numtextures2 = 0; maxoff2 = 0; } numtextures = numtextures1 + numtextures2; textures = Z_Malloc (numtextures * sizeof(*textures), PU_STATIC, 0); texturecolumnlump = Z_Malloc (numtextures * sizeof(*texturecolumnlump), PU_STATIC, 0); texturecolumnofs = Z_Malloc (numtextures * sizeof(*texturecolumnofs), PU_STATIC, 0); texturecomposite = Z_Malloc (numtextures * sizeof(*texturecomposite), PU_STATIC, 0); texturecompositesize = Z_Malloc (numtextures * sizeof(*texturecompositesize), PU_STATIC, 0); texturewidthmask = Z_Malloc (numtextures * sizeof(*texturewidthmask), PU_STATIC, 0); textureheight = Z_Malloc (numtextures * sizeof(*textureheight), PU_STATIC, 0); totalwidth = 0; // Really complex printing shit... temp1 = W_GetNumForName (DEH_String("S_START")); // P_??????? temp2 = W_GetNumForName (DEH_String("S_END")) - 1; temp3 = ((temp2-temp1+63)/64) + ((numtextures+63)/64); // If stdout is a real console, use the classic vanilla "filling // up the box" effect, which uses backspace to "step back" inside // the box. If stdout is a file, don't draw the box. if (I_ConsoleStdout()) { printf("["); for (i = 0; i < temp3 + 9; i++) printf(" "); printf("]"); for (i = 0; i < temp3 + 10; i++) printf("\b"); } for (i=0 ; i maxoff) I_Error ("R_InitTextures: bad texture directory"); mtexture = (maptexture_t *) ( (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); memcpy (texture->name, mtexture->name, sizeof(texture->name)); mpatch = &mtexture->patches[0]; patch = &texture->patches[0]; 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) { I_Error ("R_InitTextures: Missing patch in texture %s", texture->name); } } texturecolumnlump[i] = Z_Malloc (texture->width*sizeof(**texturecolumnlump), PU_STATIC,0); texturecolumnofs[i] = Z_Malloc (texture->width*sizeof(**texturecolumnofs), PU_STATIC,0); j = 1; while (j*2 <= texture->width) j<<=1; texturewidthmask[i] = j-1; textureheight[i] = texture->height<width; } Z_Free(patchlookup); W_ReleaseLumpName(DEH_String("TEXTURE1")); if (maptex2) W_ReleaseLumpName(DEH_String("TEXTURE2")); // Precalculate whatever possible. for (i=0 ; iwidth)<leftoffset)<topoffset)<name, name, 8) ) return texture->index; texture = texture->next; } return -1; } // // R_TextureNumForName // Calls R_CheckTextureNumForName, // aborts with error message. // int R_TextureNumForName (char* name) { int i; i = R_CheckTextureNumForName (name); if (i==-1) { I_Error ("R_TextureNumForName: %s not found", name); } return i; } // // R_PrecacheLevel // Preloads all relevant graphics for the level. // int flatmemory; int texturememory; int spritememory; void R_PrecacheLevel (void) { char* flatpresent; char* texturepresent; char* spritepresent; int i; int j; int k; int lump; texture_t* texture; thinker_t* th; spriteframe_t* sf; if (demoplayback) return; // Precache flats. flatpresent = Z_Malloc(numflats, PU_STATIC, NULL); memset (flatpresent,0,numflats); for (i=0 ; ipatchcount ; j++) { lump = texture->patches[j].patch; texturememory += lumpinfo[lump].size; W_CacheLumpNum(lump , PU_CACHE); } } Z_Free(texturepresent); // Precache sprites. spritepresent = Z_Malloc(numsprites, PU_STATIC, NULL); memset (spritepresent,0, numsprites); for (th = thinkercap.next ; th != &thinkercap ; th=th->next) { if (th->function.acp1 == (actionf_p1)P_MobjThinker) spritepresent[((mobj_t *)th)->sprite] = 1; } spritememory = 0; for (i=0 ; ilump[k]; spritememory += lumpinfo[lump].size; W_CacheLumpNum(lump , PU_CACHE); } } } Z_Free(spritepresent); } ================================================ FILE: fbdoom/r_data.h ================================================ // // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // 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. // // 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" // Retrieve column data for span blitting. byte* R_GetColumn ( int tex, 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 (char* name); // Called by P_Ticker for switches and animations, // returns the texture number for the texture name. int R_TextureNumForName (char *name); int R_CheckTextureNumForName (char *name); #endif ================================================ FILE: fbdoom/r_defs.h ================================================ // // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // 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. // // 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" #include "i_video.h" #include "v_patch.h" // 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") did. // typedef struct { fixed_t x; fixed_t y; } vertex_t; // Forward of LineDefs, for Sectors. struct line_s; // Each sector has a degenmobj_t in its center // for sound origin purposes. // I suppose this does not handle sound from // moving objects (doppler), because // position is prolly just buffered, not // updated. typedef struct { thinker_t thinker; // not used for anything fixed_t x; fixed_t y; fixed_t z; } degenmobj_t; // // The SECTORS record, at runtime. // Stores things/mobjs. // typedef struct { fixed_t floorheight; fixed_t ceilingheight; short floorpic; short ceilingpic; short lightlevel; short special; short tag; // 0 = untraversed, 1,2 = sndlines -1 int soundtraversed; // thing that made a sound (or null) mobj_t* soundtarget; // mapblock bounding box for height changes int blockbox[4]; // origin for any sounds played by the sector degenmobj_t soundorg; // if == validcount, already checked int validcount; // list of mobjs in sector mobj_t* thinglist; // thinker_t for reversable actions void* specialdata; int linecount; struct line_s** lines; // [linecount] size } sector_t; // // The SideDef. // typedef struct { // add this to the calculated texture column fixed_t textureoffset; // add this to the calculated texture top fixed_t rowoffset; // Texture indices. // We do not maintain names here. short toptexture; short bottomtexture; short midtexture; // Sector the SideDef is facing. sector_t* sector; } side_t; // // Move clipping aid for LineDefs. // typedef enum { ST_HORIZONTAL, ST_VERTICAL, ST_POSITIVE, ST_NEGATIVE } slopetype_t; typedef struct line_s { // Vertices, from v1 to v2. vertex_t* v1; vertex_t* v2; // Precalculated v2 - v1 for side checking. fixed_t dx; fixed_t dy; // Animation related. short flags; short special; short tag; // Visual appearance: SideDefs. // sidenum[1] will be -1 if one sided short sidenum[2]; // Neat. Another bounding box, for the extent // of the LineDef. fixed_t bbox[4]; // To aid move clipping. slopetype_t slopetype; // Front and back sector. // Note: redundant? Can be retrieved from SideDefs. sector_t* frontsector; sector_t* backsector; // if == validcount, already checked int validcount; // thinker_t for reversable actions void* specialdata; } line_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; short numlines; short firstline; } subsector_t; // // The LineSeg. // typedef struct { vertex_t* v1; vertex_t* v2; fixed_t offset; angle_t angle; side_t* sidedef; line_t* linedef; // Sector references. // Could be retrieved from linedef, too. // backsector is NULL for one sided lines sector_t* frontsector; sector_t* backsector; } seg_t; // // BSP node. // typedef struct { // Partition line. fixed_t x; fixed_t y; fixed_t dx; fixed_t dy; // Bounding box for each child. fixed_t bbox[2][4]; // If NF_SUBSECTOR its a subsector. unsigned short children[2]; } node_t; // PC direct to screen pointers //B UNUSED - keep till detailshift in r_draw.c resolved //extern byte* destview; //extern byte* destscreen; // // 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 even us emore than 32 levels. typedef byte lighttable_t; // // ? // typedef struct drawseg_s { seg_t* curline; int x1; int x2; fixed_t scale1; fixed_t scale2; fixed_t scalestep; // 0=none, 1=bottom, 2=top, 3=both int silhouette; // do not clip sprites above this fixed_t bsilheight; // do not clip sprites below this fixed_t tsilheight; // Pointers to lists for sprite clipping, // all three adjusted so [x1] is first value. short* sprtopclip; short* sprbottomclip; short* maskedtexturecol; } drawseg_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 { // Doubly linked list. struct vissprite_s* prev; struct vissprite_s* next; int x1; int x2; // for line side calculation fixed_t gx; fixed_t gy; // global bottom / top for silhouette clipping fixed_t gz; fixed_t gzt; // horizontal position of x1 fixed_t startfrac; fixed_t scale; // negative if flipped fixed_t xiscale; fixed_t texturemid; int patch; // for color translation and shadow draw, // maxbright frames as well lighttable_t* colormap; int mobjflags; } 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? // typedef struct { fixed_t height; int picnum; int lightlevel; int minx; int maxx; // leave pads for [minx-1]/[maxx+1] byte pad1; // Here lies the rub for all // dynamic resize/change of resolution. byte top[SCREENWIDTH]; byte pad2; byte pad3; // See above. byte bottom[SCREENWIDTH]; byte pad4; } visplane_t; #endif ================================================ FILE: fbdoom/r_draw.c ================================================ // // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // 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. // // DESCRIPTION: // The actual span/column drawing functions. // Here find the main potential for optimization, // e.g. inline assembly, different algorithms. // #include "doomdef.h" #include "deh_main.h" #include "i_system.h" #include "z_zone.h" #include "w_wad.h" #include "r_local.h" // Needs access to LFB (guess what). #include "v_video.h" // State. #include "doomstat.h" // ? #define MAXWIDTH 1120 #define MAXHEIGHT 832 // status bar height at bottom of screen #define SBARHEIGHT 32 // // 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; byte* ylookup[MAXHEIGHT]; int columnofs[MAXWIDTH]; // Color tables for different players, // translate a limited part to another // (color ramps used for suit colors). // byte translations[3][256]; // Backing buffer containing the bezel drawn around the screen and // surrounding background. static byte *background_buffer = NULL; // // R_DrawColumn // Source is the top of the column to scale. // lighttable_t* dc_colormap; int dc_x; int dc_yl; int dc_yh; fixed_t dc_iscale; fixed_t dc_texturemid; // first pixel in a column (possibly virtual) byte* dc_source; // just for profiling int dccount; // // 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. // void R_DrawColumn (void) { int count; byte* dest; fixed_t frac; fixed_t fracstep; count = dc_yh - dc_yl; // Zero length, column does not exceed a pixel. if (count < 0) return; #ifdef RANGECHECK if ((unsigned)dc_x >= SCREENWIDTH || dc_yl < 0 || dc_yh >= SCREENHEIGHT) I_Error ("R_DrawColumn: %i to %i at %i", dc_yl, dc_yh, dc_x); #endif // Framebuffer destination address. // Use ylookup LUT to avoid multiply with ScreenWidth. // Use columnofs LUT for subwindows? dest = ylookup[dc_yl] + columnofs[dc_x]; // Determine scaling, // which is the only mapping to be done. fracstep = dc_iscale; frac = dc_texturemid + (dc_yl-centery)*fracstep; // Inner loop that does the actual texture mapping, // e.g. a DDA-lile scaling. // This is as fast as it gets. do { // Re-map color indices from wall texture column // using a lighting/special effects LUT. *dest = dc_colormap[dc_source[(frac>>FRACBITS)&127]]; dest += SCREENWIDTH; frac += fracstep; } while (count--); } // UNUSED. // Loop unrolled. #if 0 void R_DrawColumn (void) { int count; byte* source; byte* dest; byte* colormap; unsigned frac; unsigned fracstep; unsigned fracstep2; unsigned fracstep3; unsigned fracstep4; count = dc_yh - dc_yl + 1; source = dc_source; colormap = dc_colormap; dest = ylookup[dc_yl] + columnofs[dc_x]; fracstep = dc_iscale<<9; frac = (dc_texturemid + (dc_yl-centery)*dc_iscale)<<9; fracstep2 = fracstep+fracstep; fracstep3 = fracstep2+fracstep; fracstep4 = fracstep3+fracstep; while (count >= 8) { dest[0] = colormap[source[frac>>25]]; dest[SCREENWIDTH] = colormap[source[(frac+fracstep)>>25]]; dest[SCREENWIDTH*2] = colormap[source[(frac+fracstep2)>>25]]; dest[SCREENWIDTH*3] = colormap[source[(frac+fracstep3)>>25]]; frac += fracstep4; dest[SCREENWIDTH*4] = colormap[source[frac>>25]]; dest[SCREENWIDTH*5] = colormap[source[(frac+fracstep)>>25]]; dest[SCREENWIDTH*6] = colormap[source[(frac+fracstep2)>>25]]; dest[SCREENWIDTH*7] = colormap[source[(frac+fracstep3)>>25]]; frac += fracstep4; dest += SCREENWIDTH*8; count -= 8; } while (count > 0) { *dest = colormap[source[frac>>25]]; dest += SCREENWIDTH; frac += fracstep; count--; } } #endif void R_DrawColumnLow (void) { int count; byte* dest; byte* dest2; fixed_t frac; fixed_t fracstep; int x; count = dc_yh - dc_yl; // Zero length. if (count < 0) return; #ifdef RANGECHECK if ((unsigned)dc_x >= SCREENWIDTH || dc_yl < 0 || dc_yh >= SCREENHEIGHT) { I_Error ("R_DrawColumn: %i to %i at %i", dc_yl, dc_yh, dc_x); } // dccount++; #endif // Blocky mode, need to multiply by 2. x = dc_x << 1; dest = ylookup[dc_yl] + columnofs[x]; dest2 = ylookup[dc_yl] + columnofs[x+1]; fracstep = dc_iscale; frac = dc_texturemid + (dc_yl-centery)*fracstep; do { // Hack. Does not work corretly. *dest2 = *dest = dc_colormap[dc_source[(frac>>FRACBITS)&127]]; dest += SCREENWIDTH; dest2 += SCREENWIDTH; frac += fracstep; } while (count--); } // // Spectre/Invisibility. // #define FUZZTABLE 50 #define FUZZOFF (SCREENWIDTH) int fuzzoffset[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 }; int fuzzpos = 0; // // 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. // void R_DrawFuzzColumn (void) { int count; byte* dest; fixed_t frac; fixed_t fracstep; // Adjust borders. Low... if (!dc_yl) dc_yl = 1; // .. and high. if (dc_yh == viewheight-1) dc_yh = viewheight - 2; count = dc_yh - dc_yl; // Zero length. if (count < 0) return; #ifdef RANGECHECK if ((unsigned)dc_x >= SCREENWIDTH || dc_yl < 0 || dc_yh >= SCREENHEIGHT) { I_Error ("R_DrawFuzzColumn: %i to %i at %i", dc_yl, dc_yh, dc_x); } #endif dest = ylookup[dc_yl] + columnofs[dc_x]; // Looks familiar. fracstep = dc_iscale; frac = dc_texturemid + (dc_yl-centery)*fracstep; // Looks like an attempt at dithering, // using the colormap #6 (of 0-31, a bit // brighter than average). do { // Lookup framebuffer, and retrieve // a pixel that is either one column // left or right of the current one. // Add index from colormap to index. *dest = colormaps[6*256+dest[fuzzoffset[fuzzpos]]]; // Clamp table lookup index. if (++fuzzpos == FUZZTABLE) fuzzpos = 0; dest += SCREENWIDTH; frac += fracstep; } while (count--); } // low detail mode version void R_DrawFuzzColumnLow (void) { int count; byte* dest; byte* dest2; fixed_t frac; fixed_t fracstep; int x; // Adjust borders. Low... if (!dc_yl) dc_yl = 1; // .. and high. if (dc_yh == viewheight-1) dc_yh = viewheight - 2; count = dc_yh - dc_yl; // Zero length. if (count < 0) return; // low detail mode, need to multiply by 2 x = dc_x << 1; #ifdef RANGECHECK if ((unsigned)x >= SCREENWIDTH || dc_yl < 0 || dc_yh >= SCREENHEIGHT) { I_Error ("R_DrawFuzzColumn: %i to %i at %i", dc_yl, dc_yh, dc_x); } #endif dest = ylookup[dc_yl] + columnofs[x]; dest2 = ylookup[dc_yl] + columnofs[x+1]; // Looks familiar. fracstep = dc_iscale; frac = dc_texturemid + (dc_yl-centery)*fracstep; // Looks like an attempt at dithering, // using the colormap #6 (of 0-31, a bit // brighter than average). do { // Lookup framebuffer, and retrieve // a pixel that is either one column // left or right of the current one. // Add index from colormap to index. *dest = colormaps[6*256+dest[fuzzoffset[fuzzpos]]]; *dest2 = colormaps[6*256+dest2[fuzzoffset[fuzzpos]]]; // Clamp table lookup index. if (++fuzzpos == FUZZTABLE) fuzzpos = 0; dest += SCREENWIDTH; dest2 += SCREENWIDTH; frac += fracstep; } while (count--); } // // 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. // byte* dc_translation; byte* translationtables; void R_DrawTranslatedColumn (void) { int count; byte* dest; fixed_t frac; fixed_t fracstep; count = dc_yh - dc_yl; if (count < 0) return; #ifdef RANGECHECK if ((unsigned)dc_x >= SCREENWIDTH || dc_yl < 0 || dc_yh >= SCREENHEIGHT) { I_Error ( "R_DrawColumn: %i to %i at %i", dc_yl, dc_yh, dc_x); } #endif dest = ylookup[dc_yl] + columnofs[dc_x]; // Looks familiar. fracstep = dc_iscale; frac = dc_texturemid + (dc_yl-centery)*fracstep; // Here we do an additional index re-mapping. do { // Translation tables are used // to map certain colorramps to other ones, // used with PLAY sprites. // Thus the "green" ramp of the player 0 sprite // is mapped to gray, red, black/indigo. *dest = dc_colormap[dc_translation[dc_source[frac>>FRACBITS]]]; dest += SCREENWIDTH; frac += fracstep; } while (count--); } void R_DrawTranslatedColumnLow (void) { int count; byte* dest; byte* dest2; fixed_t frac; fixed_t fracstep; int x; count = dc_yh - dc_yl; if (count < 0) return; // low detail, need to scale by 2 x = dc_x << 1; #ifdef RANGECHECK if ((unsigned)x >= SCREENWIDTH || dc_yl < 0 || dc_yh >= SCREENHEIGHT) { I_Error ( "R_DrawColumn: %i to %i at %i", dc_yl, dc_yh, x); } #endif dest = ylookup[dc_yl] + columnofs[x]; dest2 = ylookup[dc_yl] + columnofs[x+1]; // Looks familiar. fracstep = dc_iscale; frac = dc_texturemid + (dc_yl-centery)*fracstep; // Here we do an additional index re-mapping. do { // Translation tables are used // to map certain colorramps to other ones, // used with PLAY sprites. // Thus the "green" ramp of the player 0 sprite // is mapped to gray, red, black/indigo. *dest = dc_colormap[dc_translation[dc_source[frac>>FRACBITS]]]; *dest2 = dc_colormap[dc_translation[dc_source[frac>>FRACBITS]]]; dest += SCREENWIDTH; dest2 += SCREENWIDTH; frac += fracstep; } while (count--); } // // 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. // void R_InitTranslationTables (void) { int i; translationtables = Z_Malloc (256*3, PU_STATIC, 0); // translate just the 16 green colors for (i=0 ; i<256 ; i++) { if (i >= 0x70 && i<= 0x7f) { // map green ramp to gray, brown, red translationtables[i] = 0x60 + (i&0xf); translationtables [i+256] = 0x40 + (i&0xf); translationtables [i+512] = 0x20 + (i&0xf); } 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. // int ds_y; int ds_x1; int ds_x2; lighttable_t* ds_colormap; fixed_t ds_xfrac; fixed_t ds_yfrac; fixed_t ds_xstep; fixed_t ds_ystep; // start of a 64*64 tile image byte* ds_source; // just for profiling int dscount; // // Draws the actual span. void R_DrawSpan (void) { unsigned int position, step; byte *dest; int count; int spot; unsigned int xtemp, ytemp; #ifdef RANGECHECK if (ds_x2 < ds_x1 || ds_x1<0 || ds_x2>=SCREENWIDTH || (unsigned)ds_y>SCREENHEIGHT) { I_Error( "R_DrawSpan: %i to %i at %i", ds_x1,ds_x2,ds_y); } // dscount++; #endif // Pack position and step variables into a single 32-bit integer, // with x in the top 16 bits and y in the bottom 16 bits. For // each 16-bit part, the top 6 bits are the integer part and the // bottom 10 bits are the fractional part of the pixel position. position = ((ds_xfrac << 10) & 0xffff0000) | ((ds_yfrac >> 6) & 0x0000ffff); step = ((ds_xstep << 10) & 0xffff0000) | ((ds_ystep >> 6) & 0x0000ffff); dest = ylookup[ds_y] + columnofs[ds_x1]; // We do not check for zero spans here? count = ds_x2 - ds_x1; do { // Calculate current texture index in u,v. ytemp = (position >> 4) & 0x0fc0; xtemp = (position >> 26); spot = xtemp | ytemp; // Lookup pixel from flat texture tile, // re-index using light/colormap. *dest++ = ds_colormap[ds_source[spot]]; position += step; } while (count--); } // UNUSED. // Loop unrolled by 4. #if 0 void R_DrawSpan (void) { unsigned position, step; byte* source; byte* colormap; byte* dest; unsigned count; usingned spot; unsigned value; unsigned temp; unsigned xtemp; unsigned ytemp; position = ((ds_xfrac<<10)&0xffff0000) | ((ds_yfrac>>6)&0xffff); step = ((ds_xstep<<10)&0xffff0000) | ((ds_ystep>>6)&0xffff); source = ds_source; colormap = ds_colormap; dest = ylookup[ds_y] + columnofs[ds_x1]; count = ds_x2 - ds_x1 + 1; while (count >= 4) { ytemp = position>>4; ytemp = ytemp & 4032; xtemp = position>>26; spot = xtemp | ytemp; position += step; dest[0] = colormap[source[spot]]; ytemp = position>>4; ytemp = ytemp & 4032; xtemp = position>>26; spot = xtemp | ytemp; position += step; dest[1] = colormap[source[spot]]; ytemp = position>>4; ytemp = ytemp & 4032; xtemp = position>>26; spot = xtemp | ytemp; position += step; dest[2] = colormap[source[spot]]; ytemp = position>>4; ytemp = ytemp & 4032; xtemp = position>>26; spot = xtemp | ytemp; position += step; dest[3] = colormap[source[spot]]; count -= 4; dest += 4; } while (count > 0) { ytemp = position>>4; ytemp = ytemp & 4032; xtemp = position>>26; spot = xtemp | ytemp; position += step; *dest++ = colormap[source[spot]]; count--; } } #endif // // Again.. // void R_DrawSpanLow (void) { unsigned int position, step; unsigned int xtemp, ytemp; byte *dest; int count; int spot; #ifdef RANGECHECK if (ds_x2 < ds_x1 || ds_x1<0 || ds_x2>=SCREENWIDTH || (unsigned)ds_y>SCREENHEIGHT) { I_Error( "R_DrawSpan: %i to %i at %i", ds_x1,ds_x2,ds_y); } // dscount++; #endif position = ((ds_xfrac << 10) & 0xffff0000) | ((ds_yfrac >> 6) & 0x0000ffff); step = ((ds_xstep << 10) & 0xffff0000) | ((ds_ystep >> 6) & 0x0000ffff); count = (ds_x2 - ds_x1); // Blocky mode, need to multiply by 2. ds_x1 <<= 1; ds_x2 <<= 1; dest = ylookup[ds_y] + columnofs[ds_x1]; do { // Calculate current texture index in u,v. ytemp = (position >> 4) & 0x0fc0; xtemp = (position >> 26); spot = xtemp | ytemp; // Lowres/blocky mode does it twice, // while scale is adjusted appropriately. *dest++ = ds_colormap[ds_source[spot]]; *dest++ = ds_colormap[ds_source[spot]]; position += step; } while (count--); } // // 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; // Handle resize, // e.g. smaller view windows // with border and/or status bar. viewwindowx = (SCREENWIDTH-width) >> 1; // Column offset. For windows. for (i=0 ; i> 1; // Preclaculate all row offsets. for (i=0 ; i #include #include "doomdef.h" #include "d_loop.h" #include "m_bbox.h" #include "m_menu.h" #include "r_local.h" #include "r_sky.h" // Fineangles in the SCREENWIDTH wide window. #define FIELDOFVIEW 2048 int viewangleoffset; // increment every time a check is made int validcount = 1; lighttable_t* fixedcolormap; extern lighttable_t** walllights; int centerx; int centery; fixed_t centerxfrac; fixed_t centeryfrac; fixed_t projection; // just for profiling purposes int framecount; int sscount; int linecount; int loopcount; fixed_t viewx; fixed_t viewy; fixed_t viewz; angle_t viewangle; fixed_t viewcos; fixed_t viewsin; player_t* viewplayer; // 0 = high, 1 = low int detailshift; // // 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[SCREENWIDTH+1]; lighttable_t* scalelight[LIGHTLEVELS][MAXLIGHTSCALE]; lighttable_t* scalelightfixed[MAXLIGHTSCALE]; lighttable_t* zlight[LIGHTLEVELS][MAXLIGHTZ]; // bumped light from gun blasts int extralight; void (*colfunc) (void); void (*basecolfunc) (void); void (*fuzzcolfunc) (void); void (*transcolfunc) (void); void (*spanfunc) (void); // // R_AddPointToBox // Expand a given bbox // so that it encloses a given point. // void R_AddPointToBox ( int x, int y, fixed_t* box ) { if (x< box[BOXLEFT]) box[BOXLEFT] = x; if (x> box[BOXRIGHT]) box[BOXRIGHT] = x; if (y< box[BOXBOTTOM]) box[BOXBOTTOM] = y; if (y> box[BOXTOP]) box[BOXTOP] = y; } // // R_PointOnSide // Traverse BSP (sub) tree, // check point against partition plane. // Returns side 0 (front) or 1 (back). // int R_PointOnSide ( fixed_t x, fixed_t y, node_t* node ) { fixed_t dx; fixed_t dy; fixed_t left; fixed_t right; if (!node->dx) { if (x <= node->x) return node->dy > 0; return node->dy < 0; } if (!node->dy) { if (y <= node->y) return node->dx < 0; return node->dx > 0; } dx = (x - node->x); dy = (y - node->y); // Try to quickly decide by looking at sign bits. if ( (node->dy ^ node->dx ^ dx ^ dy)&0x80000000 ) { if ( (node->dy ^ dx) & 0x80000000 ) { // (left is negative) return 1; } return 0; } left = FixedMul ( node->dy>>FRACBITS , dx ); right = FixedMul ( dy , node->dx>>FRACBITS ); if (right < left) { // front side return 0; } // back side return 1; } int R_PointOnSegSide ( fixed_t x, fixed_t y, seg_t* line ) { fixed_t lx; fixed_t ly; fixed_t ldx; fixed_t ldy; fixed_t dx; fixed_t dy; fixed_t left; fixed_t right; lx = line->v1->x; ly = line->v1->y; ldx = line->v2->x - lx; ldy = line->v2->y - ly; if (!ldx) { if (x <= lx) return ldy > 0; return ldy < 0; } if (!ldy) { if (y <= ly) return ldx < 0; return ldx > 0; } dx = (x - lx); dy = (y - ly); // Try to quickly decide by looking at sign bits. if ( (ldy ^ ldx ^ dx ^ dy)&0x80000000 ) { if ( (ldy ^ dx) & 0x80000000 ) { // (left is negative) return 1; } return 0; } left = FixedMul ( ldy>>FRACBITS , dx ); right = FixedMul ( dy , ldx>>FRACBITS ); if (right < left) { // front side return 0; } // back side return 1; } // // 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. // angle_t R_PointToAngle ( fixed_t x, fixed_t y ) { x -= viewx; y -= viewy; if ( (!x) && (!y) ) return 0; if (x>= 0) { // x >=0 if (y>= 0) { // y>= 0 if (x>y) { // octant 0 return tantoangle[ SlopeDiv(y,x)]; } else { // octant 1 return ANG90-1-tantoangle[ SlopeDiv(x,y)]; } } else { // y<0 y = -y; if (x>y) { // octant 8 return -tantoangle[SlopeDiv(y,x)]; } else { // octant 7 return ANG270+tantoangle[ SlopeDiv(x,y)]; } } } else { // x<0 x = -x; if (y>= 0) { // y>= 0 if (x>y) { // octant 3 return ANG180-1-tantoangle[ SlopeDiv(y,x)]; } else { // octant 2 return ANG90+ tantoangle[ SlopeDiv(x,y)]; } } else { // y<0 y = -y; if (x>y) { // octant 4 return ANG180+tantoangle[ SlopeDiv(y,x)]; } else { // octant 5 return ANG270-1-tantoangle[ SlopeDiv(x,y)]; } } } return 0; } angle_t R_PointToAngle2 ( fixed_t x1, fixed_t y1, fixed_t x2, fixed_t y2 ) { viewx = x1; viewy = y1; return R_PointToAngle (x2, y2); } fixed_t R_PointToDist ( fixed_t x, fixed_t y ) { int angle; fixed_t dx; fixed_t dy; fixed_t temp; fixed_t dist; fixed_t frac; dx = abs(x - viewx); dy = abs(y - viewy); if (dy>dx) { temp = dx; dx = dy; dy = temp; } // Fix crashes in udm1.wad if (dx != 0) { frac = FixedDiv(dy, dx); } else { frac = 0; } angle = (tantoangle[frac>>DBITS]+ANG90) >> ANGLETOFINESHIFT; // use as cosine dist = FixedDiv (dx, finesine[angle] ); return dist; } // // R_InitPointToAngle // void R_InitPointToAngle (void) { // UNUSED - now getting from tables.c #if 0 int i; long t; float f; // // slope (tangent) to angle lookup // for (i=0 ; i<=SLOPERANGE ; i++) { f = atan( (float)i/SLOPERANGE )/(3.141592657*2); t = 0xffffffff*f; tantoangle[i] = t; } #endif } // // R_ScaleFromGlobalAngle // Returns the texture mapping scale // for the current line (horizontal span) // at the given angle. // rw_distance must be calculated first. // fixed_t R_ScaleFromGlobalAngle (angle_t visangle) { fixed_t scale; angle_t anglea; angle_t angleb; int sinea; int sineb; fixed_t num; int den; // UNUSED #if 0 { fixed_t dist; fixed_t z; fixed_t sinv; fixed_t cosv; sinv = finesine[(visangle-rw_normalangle)>>ANGLETOFINESHIFT]; dist = FixedDiv (rw_distance, sinv); cosv = finecosine[(viewangle-visangle)>>ANGLETOFINESHIFT]; z = abs(FixedMul (dist, cosv)); scale = FixedDiv(projection, z); return scale; } #endif anglea = ANG90 + (visangle-viewangle); angleb = ANG90 + (visangle-rw_normalangle); // both sines are allways positive sinea = finesine[anglea>>ANGLETOFINESHIFT]; sineb = finesine[angleb>>ANGLETOFINESHIFT]; num = FixedMul(projection,sineb)< num>>16) { scale = FixedDiv (num, den); if (scale > 64*FRACUNIT) scale = 64*FRACUNIT; else if (scale < 256) scale = 256; } else scale = 64*FRACUNIT; return scale; } // // R_InitTables // void R_InitTables (void) { // UNUSED: now getting from tables.c #if 0 int i; float a; float fv; int t; // viewangle tangent table 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++) { i = 0; while (viewangletox[i]>x) i++; xtoviewangle[x] = (i<>= LIGHTSCALESHIFT; level = startmap - scale/DISTMAP; if (level < 0) level = 0; if (level >= NUMCOLORMAPS) level = NUMCOLORMAPS-1; zlight[i][j] = colormaps + level*256; } } } // // R_SetViewSize // Do not really change anything here, // because it might be in the middle of a refresh. // The change will take effect next refresh. // boolean setsizeneeded; int setblocks; int setdetail; void R_SetViewSize ( int blocks, int detail ) { setsizeneeded = true; setblocks = blocks; setdetail = detail; } // // R_ExecuteSetViewSize // void R_ExecuteSetViewSize (void) { fixed_t cosadj; fixed_t dy; int i; int j; int level; int startmap; setsizeneeded = false; if (setblocks == 11) { scaledviewwidth = SCREENWIDTH; viewheight = SCREENHEIGHT; } else { scaledviewwidth = setblocks*32; viewheight = (setblocks*168/10)&~7; } detailshift = setdetail; viewwidth = scaledviewwidth>>detailshift; centery = viewheight/2; centerx = viewwidth/2; centerxfrac = centerx<>ANGLETOFINESHIFT]); distscale[i] = FixedDiv (FRACUNIT,cosadj); } // Calculate the light levels to use // for each level / scale combination. for (i=0 ; i< LIGHTLEVELS ; i++) { startmap = ((LIGHTLEVELS-1-i)*2)*NUMCOLORMAPS/LIGHTLEVELS; for (j=0 ; j= NUMCOLORMAPS) level = NUMCOLORMAPS-1; scalelight[i][j] = colormaps + level*256; } } } // // R_Init // void R_Init (void) { R_InitData (); printf ("."); R_InitPointToAngle (); printf ("."); R_InitTables (); // viewwidth / viewheight / detailLevel are set by the defaults printf ("."); R_SetViewSize (screenblocks, detailLevel); R_InitPlanes (); printf ("."); R_InitLightTables (); printf ("."); R_InitSkyMap (); R_InitTranslationTables (); printf ("."); framecount = 0; } // // R_PointInSubsector // subsector_t* R_PointInSubsector ( fixed_t x, fixed_t y ) { node_t* node; int side; int nodenum; // single subsector is a special case if (!numnodes) return subsectors; nodenum = numnodes-1; while (! (nodenum & NF_SUBSECTOR) ) { node = &nodes[nodenum]; side = R_PointOnSide (x, y, node); nodenum = node->children[side]; } return &subsectors[nodenum & ~NF_SUBSECTOR]; } // // R_SetupFrame // void R_SetupFrame (player_t* player) { int i; viewplayer = player; viewx = player->mo->x; viewy = player->mo->y; viewangle = player->mo->angle + viewangleoffset; extralight = player->extralight; viewz = player->viewz; viewsin = finesine[viewangle>>ANGLETOFINESHIFT]; viewcos = finecosine[viewangle>>ANGLETOFINESHIFT]; sscount = 0; if (player->fixedcolormap) { fixedcolormap = colormaps + player->fixedcolormap*256*sizeof(lighttable_t); walllights = scalelightfixed; for (i=0 ; i #include #include "i_system.h" #include "z_zone.h" #include "w_wad.h" #include "doomdef.h" #include "doomstat.h" #include "r_local.h" #include "r_sky.h" planefunction_t floorfunc; planefunction_t ceilingfunc; // // opening // // Here comes the obnoxious "visplane". #define MAXVISPLANES 128 visplane_t visplanes[MAXVISPLANES]; visplane_t* lastvisplane; visplane_t* floorplane; visplane_t* ceilingplane; // ? #define MAXOPENINGS SCREENWIDTH*64 short openings[MAXOPENINGS]; short* lastopening; // // Clip values are the solid pixel bounding the range. // floorclip starts out SCREENHEIGHT // ceilingclip starts out -1 // short floorclip[SCREENWIDTH]; short ceilingclip[SCREENWIDTH]; // // spanstart holds the start of a plane span // initialized to 0 at start // int spanstart[SCREENHEIGHT]; int spanstop[SCREENHEIGHT]; // // texture mapping // lighttable_t** planezlight; fixed_t planeheight; fixed_t yslope[SCREENHEIGHT]; fixed_t distscale[SCREENWIDTH]; fixed_t basexscale; fixed_t baseyscale; fixed_t cachedheight[SCREENHEIGHT]; fixed_t cacheddistance[SCREENHEIGHT]; fixed_t cachedxstep[SCREENHEIGHT]; fixed_t cachedystep[SCREENHEIGHT]; // // R_InitPlanes // Only at game startup. // void R_InitPlanes (void) { // Doh! } // // R_MapPlane // // Uses global vars: // planeheight // ds_source // basexscale // baseyscale // viewx // viewy // // BASIC PRIMITIVE // void R_MapPlane ( int y, int x1, int x2 ) { angle_t angle; fixed_t distance; fixed_t length; unsigned index; #ifdef RANGECHECK if (x2 < x1 || x1 < 0 || x2 >= viewwidth || y > 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]); ds_xstep = cachedxstep[y] = FixedMul (distance,basexscale); ds_ystep = cachedystep[y] = FixedMul (distance,baseyscale); } else { distance = cacheddistance[y]; ds_xstep = cachedxstep[y]; ds_ystep = cachedystep[y]; } length = FixedMul (distance,distscale[x1]); angle = (viewangle + xtoviewangle[x1])>>ANGLETOFINESHIFT; ds_xfrac = viewx + FixedMul(finecosine[angle], length); ds_yfrac = -viewy - FixedMul(finesine[angle], length); if (fixedcolormap) ds_colormap = fixedcolormap; else { index = distance >> LIGHTZSHIFT; if (index >= MAXLIGHTZ ) index = MAXLIGHTZ-1; ds_colormap = planezlight[index]; } ds_y = y; ds_x1 = x1; ds_x2 = x2; // high or low detail spanfunc (); } // // R_ClearPlanes // At begining of frame. // void R_ClearPlanes (void) { int i; angle_t angle; // opening / clipping determination for (i=0 ; i>ANGLETOFINESHIFT; // scale will be unit scale at SCREENWIDTH/2 distance basexscale = FixedDiv (finecosine[angle],centerxfrac); baseyscale = -FixedDiv (finesine[angle],centerxfrac); } // // R_FindPlane // visplane_t* R_FindPlane ( fixed_t height, int picnum, int lightlevel ) { visplane_t* check; if (picnum == skyflatnum) { height = 0; // all skys map together lightlevel = 0; } for (check=visplanes; checkheight && picnum == check->picnum && lightlevel == check->lightlevel) { break; } } if (check < lastvisplane) return check; if (lastvisplane - visplanes == MAXVISPLANES) I_Error ("R_FindPlane: no more visplanes"); lastvisplane++; check->height = height; check->picnum = picnum; check->lightlevel = lightlevel; check->minx = SCREENWIDTH; check->maxx = -1; memset (check->top,0xff,sizeof(check->top)); return check; } // // R_CheckPlane // visplane_t* R_CheckPlane ( visplane_t* pl, int start, int stop ) { int intrl; int intrh; int unionl; int unionh; int 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 ; x++) if (pl->top[x] != 0xff) break; if (x > intrh) { pl->minx = unionl; pl->maxx = unionh; // use the same one return pl; } // make a new visplane lastvisplane->height = pl->height; lastvisplane->picnum = pl->picnum; lastvisplane->lightlevel = pl->lightlevel; pl = lastvisplane++; pl->minx = start; pl->maxx = stop; memset (pl->top,0xff,sizeof(pl->top)); return pl; } // // R_MakeSpans // void R_MakeSpans ( int x, int t1, int b1, int t2, int b2 ) { while (t1 < t2 && t1<=b1) { R_MapPlane (t1,spanstart[t1],x-1); t1++; } while (b1 > b2 && b1>=t1) { R_MapPlane (b1,spanstart[b1],x-1); b1--; } while (t2 < t1 && t2<=b2) { spanstart[t2] = x; t2++; } while (b2 > b1 && b2>=t2) { spanstart[b2] = x; b2--; } } // // R_DrawPlanes // At the end of each frame. // void R_DrawPlanes (void) { visplane_t* pl; int light; int x; int stop; int angle; int lumpnum; #ifdef RANGECHECK if (ds_p - drawsegs > MAXDRAWSEGS) I_Error ("R_DrawPlanes: drawsegs overflow (%i)", ds_p - drawsegs); if (lastvisplane - visplanes > MAXVISPLANES) I_Error ("R_DrawPlanes: visplane overflow (%i)", lastvisplane - visplanes); if (lastopening - openings > MAXOPENINGS) I_Error ("R_DrawPlanes: opening overflow (%i)", lastopening - openings); #endif for (pl = visplanes ; pl < lastvisplane ; pl++) { if (pl->minx > pl->maxx) continue; // sky flat if (pl->picnum == skyflatnum) { dc_iscale = pspriteiscale>>detailshift; // Sky is allways drawn full bright, // i.e. colormaps[0] is used. // Because of this hack, sky is not affected // by INVUL inverse mapping. dc_colormap = colormaps; dc_texturemid = skytexturemid; for (x=pl->minx ; x <= pl->maxx ; x++) { dc_yl = pl->top[x]; dc_yh = pl->bottom[x]; if (dc_yl <= dc_yh) { angle = (viewangle + xtoviewangle[x])>>ANGLETOSKYSHIFT; dc_x = x; dc_source = R_GetColumn(skytexture, angle); colfunc (); } } continue; } // regular flat lumpnum = firstflat + flattranslation[pl->picnum]; ds_source = W_CacheLumpNum(lumpnum, PU_STATIC); planeheight = abs(pl->height-viewz); light = (pl->lightlevel >> LIGHTSEGSHIFT)+extralight; if (light >= LIGHTLEVELS) light = LIGHTLEVELS-1; if (light < 0) light = 0; planezlight = zlight[light]; pl->top[pl->maxx+1] = 0xff; pl->top[pl->minx-1] = 0xff; stop = pl->maxx + 1; for (x=pl->minx ; x<= stop ; x++) { R_MakeSpans(x,pl->top[x-1], pl->bottom[x-1], pl->top[x], pl->bottom[x]); } W_ReleaseLumpNum(lumpnum); } } ================================================ FILE: fbdoom/r_plane.h ================================================ // // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // 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. // // DESCRIPTION: // Refresh, visplane stuff (floor, ceilings). // #ifndef __R_PLANE__ #define __R_PLANE__ #include "r_data.h" // Visplane related. extern short* lastopening; typedef void (*planefunction_t) (int top, int bottom); extern planefunction_t floorfunc; extern planefunction_t ceilingfunc_t; extern short floorclip[SCREENWIDTH]; extern short ceilingclip[SCREENWIDTH]; extern fixed_t yslope[SCREENHEIGHT]; extern fixed_t distscale[SCREENWIDTH]; void R_InitPlanes (void); void R_ClearPlanes (void); void R_MapPlane ( int y, int x1, int x2 ); void R_MakeSpans ( int x, int t1, int b1, int t2, int b2 ); void R_DrawPlanes (void); visplane_t* R_FindPlane ( fixed_t height, int picnum, int lightlevel ); visplane_t* R_CheckPlane ( visplane_t* pl, int start, int stop ); #endif ================================================ FILE: fbdoom/r_segs.c ================================================ // // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // 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. // // DESCRIPTION: // All the clipping: columns, horizontal spans, sky columns. // #include #include #include "i_system.h" #include "doomdef.h" #include "doomstat.h" #include "r_local.h" #include "r_sky.h" // OPTIMIZE: closed two sided lines as single sided // True if any of the segs textures might be visible. boolean segtextured; // False if the back side is the same plane. boolean markfloor; boolean markceiling; boolean maskedtexture; int toptexture; int bottomtexture; int midtexture; angle_t rw_normalangle; // angle to line origin int rw_angle1; // // regular wall // int rw_x; int rw_stopx; angle_t rw_centerangle; fixed_t rw_offset; fixed_t rw_distance; fixed_t rw_scale; fixed_t rw_scalestep; fixed_t rw_midtexturemid; fixed_t rw_toptexturemid; fixed_t rw_bottomtexturemid; int worldtop; int worldbottom; int worldhigh; int worldlow; fixed_t pixhigh; fixed_t pixlow; fixed_t pixhighstep; fixed_t pixlowstep; fixed_t topfrac; fixed_t topstep; fixed_t bottomfrac; fixed_t bottomstep; lighttable_t** walllights; short* maskedtexturecol; // // R_RenderMaskedSegRange // void R_RenderMaskedSegRange ( drawseg_t* ds, int x1, int x2 ) { unsigned index; column_t* col; int lightnum; int texnum; // Calculate light table. // Use different light tables // for horizontal / vertical / diagonal. Diagonal? // OPTIMIZE: get rid of LIGHTSEGSHIFT globally curline = ds->curline; frontsector = curline->frontsector; backsector = curline->backsector; texnum = texturetranslation[curline->sidedef->midtexture]; lightnum = (frontsector->lightlevel >> LIGHTSEGSHIFT)+extralight; if (curline->v1->y == curline->v2->y) lightnum--; else if (curline->v1->x == curline->v2->x) lightnum++; if (lightnum < 0) walllights = scalelight[0]; else if (lightnum >= LIGHTLEVELS) walllights = scalelight[LIGHTLEVELS-1]; else walllights = scalelight[lightnum]; 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) { dc_texturemid = frontsector->floorheight > backsector->floorheight ? frontsector->floorheight : backsector->floorheight; dc_texturemid = dc_texturemid + textureheight[texnum] - viewz; } else { dc_texturemid =frontsector->ceilingheightceilingheight ? frontsector->ceilingheight : backsector->ceilingheight; dc_texturemid = dc_texturemid - viewz; } dc_texturemid += curline->sidedef->rowoffset; if (fixedcolormap) dc_colormap = fixedcolormap; // draw the columns for (dc_x = x1 ; dc_x <= x2 ; dc_x++) { // calculate lighting if (maskedtexturecol[dc_x] != SHRT_MAX) { if (!fixedcolormap) { index = spryscale>>LIGHTSCALESHIFT; if (index >= MAXLIGHTSCALE ) index = MAXLIGHTSCALE-1; dc_colormap = walllights[index]; } sprtopscreen = centeryfrac - FixedMul(dc_texturemid, spryscale); dc_iscale = 0xffffffffu / (unsigned)spryscale; // draw the texture col = (column_t *)( (byte *)R_GetColumn(texnum,maskedtexturecol[dc_x]) -3); R_DrawMaskedColumn (col); maskedtexturecol[dc_x] = SHRT_MAX; } spryscale += rw_scalestep; } } // // 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; // no space above wall? if (yl < ceilingclip[rw_x]+1) yl = ceilingclip[rw_x]+1; if (markceiling) { top = ceilingclip[rw_x]+1; 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; } } yh = bottomfrac>>HEIGHTBITS; if (yh >= floorclip[rw_x]) yh = floorclip[rw_x]-1; if (markfloor) { top = yh+1; bottom = floorclip[rw_x]-1; if (top <= ceilingclip[rw_x]) top = ceilingclip[rw_x]+1; if (top <= bottom) { floorplane->top[rw_x] = top; floorplane->bottom[rw_x] = bottom; } } // texturecolumn and lighting are independent of wall tiers if (segtextured) { // calculate texture offset angle = (rw_centerangle + xtoviewangle[rw_x])>>ANGLETOFINESHIFT; texturecolumn = rw_offset-FixedMul(finetangent[angle],rw_distance); texturecolumn >>= FRACBITS; // calculate lighting index = rw_scale>>LIGHTSCALESHIFT; if (index >= MAXLIGHTSCALE ) index = MAXLIGHTSCALE-1; dc_colormap = walllights[index]; dc_x = rw_x; dc_iscale = 0xffffffffu / (unsigned)rw_scale; } else { // purely to shut up the compiler texturecolumn = 0; } // draw the wall tiers if (midtexture) { // single sided line dc_yl = yl; dc_yh = yh; dc_texturemid = rw_midtexturemid; dc_source = R_GetColumn(midtexture,texturecolumn); colfunc (); ceilingclip[rw_x] = viewheight; floorclip[rw_x] = -1; } else { // two sided line if (toptexture) { // top wall mid = pixhigh>>HEIGHTBITS; pixhigh += pixhighstep; if (mid >= floorclip[rw_x]) mid = floorclip[rw_x]-1; if (mid >= yl) { dc_yl = yl; dc_yh = mid; dc_texturemid = rw_toptexturemid; dc_source = R_GetColumn(toptexture,texturecolumn); colfunc (); 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 mid = (pixlow+HEIGHTUNIT-1)>>HEIGHTBITS; pixlow += pixlowstep; // no space above wall? if (mid <= ceilingclip[rw_x]) mid = ceilingclip[rw_x]+1; if (mid <= yh) { dc_yl = mid; dc_yh = yh; dc_texturemid = rw_bottomtexturemid; dc_source = R_GetColumn(bottomtexture, texturecolumn); colfunc (); floorclip[rw_x] = mid; } else floorclip[rw_x] = yh+1; } else { // no bottom wall if (markfloor) floorclip[rw_x] = yh+1; } if (maskedtexture) { // save texturecol // for backdrawing of masked mid texture maskedtexturecol[rw_x] = texturecolumn; } } rw_scale += rw_scalestep; topfrac += topstep; bottomfrac += bottomstep; } } // // R_StoreWallRange // A wall segment will be drawn // between start and stop pixels (inclusive). // void R_StoreWallRange ( int start, int stop ) { fixed_t hyp; fixed_t sineval; angle_t distangle, offsetangle; fixed_t vtop; int lightnum; // don't overflow and crash if (ds_p == &drawsegs[MAXDRAWSEGS]) return; #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 = abs(rw_normalangle-rw_angle1); if (offsetangle > ANG90) offsetangle = ANG90; distangle = ANG90 - offsetangle; hyp = R_PointToDist (curline->v1->x, curline->v1->y); sineval = finesine[distangle>>ANGLETOFINESHIFT]; rw_distance = FixedMul (hyp, sineval); ds_p->x1 = rw_x = start; ds_p->x2 = stop; ds_p->curline = curline; rw_stopx = stop+1; // 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 { // UNUSED: try to fix the stretched line bug #if 0 if (rw_distance < FRACUNIT/2) { fixed_t trx,try; fixed_t gxt,gyt; trx = curline->v1->x - viewx; try = curline->v1->y - viewy; gxt = FixedMul(trx,viewcos); gyt = -FixedMul(try,viewsin); ds_p->scale1 = FixedDiv(projection, gxt-gyt)<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]; // a single sided line is terminal, so it must mark ends markfloor = markceiling = true; if (linedef->flags & ML_DONTPEGBOTTOM) { vtop = frontsector->floorheight + textureheight[sidedef->midtexture]; // bottom of texture at bottom rw_midtexturemid = vtop - viewz; } else { // top of texture at top rw_midtexturemid = worldtop; } rw_midtexturemid += sidedef->rowoffset; 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 (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; // ds_p->sprbottomclip = negonearray; } 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; // ds_p->sprtopclip = screenheightarray; } if (backsector->ceilingheight <= frontsector->floorheight) { ds_p->sprbottomclip = negonearray; ds_p->bsilheight = INT_MAX; ds_p->silhouette |= SIL_BOTTOM; } if (backsector->floorheight >= frontsector->ceilingheight) { ds_p->sprtopclip = screenheightarray; ds_p->tsilheight = INT_MIN; ds_p->silhouette |= SIL_TOP; } 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; } if (worldlow != worldbottom || backsector->floorpic != frontsector->floorpic || backsector->lightlevel != frontsector->lightlevel) { markfloor = true; } else { // same plane on both sides markfloor = false; } if (worldhigh != worldtop || backsector->ceilingpic != frontsector->ceilingpic || backsector->lightlevel != frontsector->lightlevel) { markceiling = true; } else { // same plane on both sides markceiling = false; } if (backsector->ceilingheight <= frontsector->floorheight || backsector->floorheight >= frontsector->ceilingheight) { // closed door markceiling = markfloor = true; } if (worldhigh < worldtop) { // top texture toptexture = texturetranslation[sidedef->toptexture]; if (linedef->flags & ML_DONTPEGTOP) { // top of texture at top rw_toptexturemid = worldtop; } else { vtop = backsector->ceilingheight + textureheight[sidedef->toptexture]; // bottom of texture rw_toptexturemid = vtop - viewz; } } if (worldlow > worldbottom) { // bottom texture bottomtexture = texturetranslation[sidedef->bottomtexture]; if (linedef->flags & ML_DONTPEGBOTTOM ) { // bottom of texture at bottom // top of texture at top rw_bottomtexturemid = worldtop; } else // top of texture at top rw_bottomtexturemid = worldlow; } rw_toptexturemid += sidedef->rowoffset; rw_bottomtexturemid += sidedef->rowoffset; // 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) { offsetangle = rw_normalangle-rw_angle1; if (offsetangle > ANG180) offsetangle = -offsetangle; if (offsetangle > ANG90) offsetangle = ANG90; sineval = finesine[offsetangle >>ANGLETOFINESHIFT]; rw_offset = FixedMul (hyp, sineval); if (rw_normalangle-rw_angle1 < ANG180) rw_offset = -rw_offset; rw_offset += sidedef->textureoffset + curline->offset; rw_centerangle = ANG90 + viewangle - rw_normalangle; // calculate light table // use different light tables // for horizontal / vertical / diagonal // OPTIMIZE: get rid of LIGHTSEGSHIFT globally if (!fixedcolormap) { lightnum = (frontsector->lightlevel >> LIGHTSEGSHIFT)+extralight; if (curline->v1->y == curline->v2->y) lightnum--; else if (curline->v1->x == curline->v2->x) lightnum++; if (lightnum < 0) walllights = scalelight[0]; else if (lightnum >= LIGHTLEVELS) walllights = scalelight[LIGHTLEVELS-1]; else walllights = scalelight[lightnum]; } } // 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. 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) ceilingplane = R_CheckPlane (ceilingplane, rw_x, rw_stopx-1); if (markfloor) floorplane = R_CheckPlane (floorplane, rw_x, rw_stopx-1); R_RenderSegLoop (); // save sprite clipping info if ( ((ds_p->silhouette & SIL_TOP) || maskedtexture) && !ds_p->sprtopclip) { memcpy (lastopening, ceilingclip+start, 2*(rw_stopx-start)); ds_p->sprtopclip = lastopening - start; lastopening += rw_stopx - start; } if ( ((ds_p->silhouette & SIL_BOTTOM) || maskedtexture) && !ds_p->sprbottomclip) { memcpy (lastopening, floorclip+start, 2*(rw_stopx-start)); 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++; } ================================================ FILE: fbdoom/r_segs.h ================================================ // // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // 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. // // DESCRIPTION: // Refresh module, drawing LineSegs from BSP. // #ifndef __R_SEGS__ #define __R_SEGS__ void R_RenderMaskedSegRange ( drawseg_t* ds, int x1, int x2 ); #endif ================================================ FILE: fbdoom/r_sky.c ================================================ // // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // 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. // // 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? // // // Needed for FRACUNIT. #include "m_fixed.h" // Needed for Flat retrieval. #include "r_data.h" #include "r_sky.h" // // sky mapping // int skyflatnum; int skytexture; int skytexturemid; // // R_InitSkyMap // Called whenever the view size changes. // void R_InitSkyMap (void) { // skyflatnum = R_FlatNumForName ( SKYFLATNAME ); skytexturemid = 100*FRACUNIT; } ================================================ FILE: fbdoom/r_sky.h ================================================ // // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // 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. // // DESCRIPTION: // Sky rendering. // #ifndef __R_SKY__ #define __R_SKY__ // 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 ================================================ FILE: fbdoom/r_state.h ================================================ // // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // 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. // // 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" // // Refresh internal data structures, // for rendering. // // needed for texture pegging extern fixed_t* textureheight; // needed for pre rendering (fracs) extern fixed_t* spritewidth; extern fixed_t* spriteoffset; extern fixed_t* spritetopoffset; extern lighttable_t* colormaps; extern int viewwidth; extern int scaledviewwidth; extern int viewheight; extern int firstflat; // 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[SCREENWIDTH+1]; //extern fixed_t finetangent[FINEANGLES/2]; extern fixed_t rw_distance; extern angle_t rw_normalangle; // angle to line origin extern int rw_angle1; // Segs count? extern int sscount; extern visplane_t* floorplane; extern visplane_t* ceilingplane; #endif ================================================ FILE: fbdoom/r_things.c ================================================ // // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // 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. // // DESCRIPTION: // Refresh of things, i.e. objects represented by sprites. // #include #include #include "deh_main.h" #include "doomdef.h" #include "i_swap.h" #include "i_system.h" #include "z_zone.h" #include "w_wad.h" #include "r_local.h" #include "doomstat.h" #define MINZ (FRACUNIT*4) #define BASEYCENTER 100 //void R_DrawColumn (void); //void R_DrawFuzzColumn (void); 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; lighttable_t** spritelights; // constant arrays // used for psprite clipping and initializing clipping short negonearray[SCREENWIDTH]; short screenheightarray[SCREENWIDTH]; // // INITIALIZATION FUNCTIONS // // variables used to look up // and range check thing_t sprites patches spritedef_t* sprites; int numsprites; spriteframe_t sprtemp[29]; int maxframe; char* spritename; // // R_InstallSpriteLump // Local function for R_InitSprites. // void R_InstallSpriteLump ( int lump, unsigned frame, unsigned rotation, boolean flipped ) { int r; if (frame >= 29 || 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 if (sprtemp[frame].rotate == false) I_Error ("R_InitSprites: Sprite %s frame %c has " "multip rot=0 lump", spritename, 'A'+frame); if (sprtemp[frame].rotate == true) I_Error ("R_InitSprites: Sprite %s frame %c has rotations " "and a rot=0 lump", spritename, 'A'+frame); sprtemp[frame].rotate = false; for (r=0 ; r<8 ; r++) { sprtemp[frame].lump[r] = lump - firstspritelump; sprtemp[frame].flip[r] = (byte)flipped; } return; } // the lump is only used for one rotation if (sprtemp[frame].rotate == false) I_Error ("R_InitSprites: Sprite %s frame %c has rotations " "and a rot=0 lump", spritename, 'A'+frame); sprtemp[frame].rotate = true; // make 0 based rotation--; if (sprtemp[frame].lump[rotation] != -1) I_Error ("R_InitSprites: Sprite %s : %c : %c " "has two lumps mapped to it", spritename, 'A'+frame, '1'+rotation); sprtemp[frame].lump[rotation] = lump - firstspritelump; sprtemp[frame].flip[rotation] = (byte)flipped; } // // 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 inconsistant. // 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. // void R_InitSpriteDefs (char** namelist) { char** check; int i; int l; int frame; int rotation; int start; int end; int patched; // count the number of sprite names check = namelist; while (*check != NULL) check++; numsprites = check-namelist; if (!numsprites) return; sprites = Z_Malloc(numsprites *sizeof(*sprites), PU_STATIC, NULL); start = firstspritelump-1; end = lastspritelump+1; // scan all the lump names for each of the names, // noting the highest frame letter. // Just compare 4 characters as ints for (i=0 ; itopdelta != 0xff ; ) { // calculate unclipped screen coordinates // for post topscreen = sprtopscreen + spryscale*column->topdelta; bottomscreen = topscreen + spryscale*column->length; dc_yl = (topscreen+FRACUNIT-1)>>FRACBITS; dc_yh = (bottomscreen-1)>>FRACBITS; if (dc_yh >= mfloorclip[dc_x]) dc_yh = mfloorclip[dc_x]-1; if (dc_yl <= mceilingclip[dc_x]) dc_yl = mceilingclip[dc_x]+1; if (dc_yl <= dc_yh) { dc_source = (byte *)column + 3; dc_texturemid = basetexturemid - (column->topdelta<topdelta; // Drawn by either R_DrawColumn // or (SHADOW) R_DrawFuzzColumn. colfunc (); } column = (column_t *)( (byte *)column + column->length + 4); } dc_texturemid = basetexturemid; } // // R_DrawVisSprite // mfloorclip and mceilingclip should also be set. // void R_DrawVisSprite ( vissprite_t* vis, int x1, int x2 ) { column_t* column; int texturecolumn; fixed_t frac; patch_t* patch; patch = W_CacheLumpNum (vis->patch+firstspritelump, PU_CACHE); dc_colormap = vis->colormap; if (!dc_colormap) { // NULL colormap = shadow draw colfunc = fuzzcolfunc; } else if (vis->mobjflags & MF_TRANSLATION) { colfunc = transcolfunc; dc_translation = translationtables - 256 + ( (vis->mobjflags & MF_TRANSLATION) >> (MF_TRANSSHIFT-8) ); } dc_iscale = abs(vis->xiscale)>>detailshift; dc_texturemid = vis->texturemid; frac = vis->startfrac; spryscale = vis->scale; sprtopscreen = centeryfrac - FixedMul(dc_texturemid,spryscale); for (dc_x=vis->x1 ; dc_x<=vis->x2 ; dc_x++, frac += vis->xiscale) { texturecolumn = frac>>FRACBITS; #ifdef RANGECHECK if (texturecolumn < 0 || texturecolumn >= SHORT(patch->width)) I_Error ("R_DrawSpriteRange: bad texturecolumn"); #endif column = (column_t *) ((byte *)patch + LONG(patch->columnofs[texturecolumn])); R_DrawMaskedColumn (column); } colfunc = basecolfunc; } // // R_ProjectSprite // Generates a vissprite for a thing // if it might be visible. // void R_ProjectSprite (mobj_t* thing) { fixed_t tr_x; fixed_t tr_y; fixed_t gxt; fixed_t gyt; fixed_t tx; fixed_t tz; fixed_t xscale; int x1; int x2; spritedef_t* sprdef; spriteframe_t* sprframe; int lump; unsigned rot; boolean flip; int index; vissprite_t* vis; angle_t ang; fixed_t iscale; // transform the origin point tr_x = thing->x - viewx; tr_y = thing->y - 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 (abs(tx)>(tz<<2)) return; // decide which patch to use for sprite relative to player #ifdef RANGECHECK if ((unsigned int) thing->sprite >= (unsigned int) 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 sprframe = &sprdef->spriteframes[ thing->frame & FF_FRAMEMASK]; if (sprframe->rotate) { // choose a different rotation based on player view ang = R_PointToAngle (thing->x, thing->y); 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]; } // calculate edges of the shape tx -= spriteoffset[lump]; x1 = (centerxfrac + FixedMul (tx,xscale) ) >>FRACBITS; // off the right side? if (x1 > viewwidth) return; tx += spritewidth[lump]; x2 = ((centerxfrac + FixedMul (tx,xscale) ) >>FRACBITS) - 1; // off the left side if (x2 < 0) return; // store information in a vissprite vis = R_NewVisSprite (); vis->mobjflags = thing->flags; vis->scale = xscale<gx = thing->x; vis->gy = thing->y; vis->gz = thing->z; vis->gzt = thing->z + spritetopoffset[lump]; 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 = spritewidth[lump]-1; vis->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) { // shadow draw vis->colormap = NULL; } else if (fixedcolormap) { // fixed map vis->colormap = fixedcolormap; } else if (thing->frame & FF_FULLBRIGHT) { // full bright vis->colormap = colormaps; } else { // diminished light index = xscale>>(LIGHTSCALESHIFT-detailshift); if (index >= MAXLIGHTSCALE) index = MAXLIGHTSCALE-1; vis->colormap = spritelights[index]; } } // // R_AddSprites // During BSP traversal, this adds sprites by sector. // void R_AddSprites (sector_t* sec) { mobj_t* thing; int lightnum; // 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; lightnum = (sec->lightlevel >> LIGHTSEGSHIFT)+extralight; if (lightnum < 0) spritelights = scalelight[0]; else if (lightnum >= LIGHTLEVELS) spritelights = scalelight[LIGHTLEVELS-1]; else spritelights = scalelight[lightnum]; // Handle all things in sector. for (thing = sec->thinglist ; thing ; thing = thing->snext) R_ProjectSprite (thing); } // // R_DrawPSprite // void R_DrawPSprite (pspdef_t* psp) { fixed_t tx; int x1; int x2; spritedef_t* sprdef; spriteframe_t* sprframe; int lump; boolean flip; vissprite_t* vis; vissprite_t avis; // decide which patch to use #ifdef RANGECHECK if ( (unsigned)psp->state->sprite >= (unsigned int) 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 : %i ", psp->state->sprite, psp->state->frame); #endif sprframe = &sprdef->spriteframes[ psp->state->frame & FF_FRAMEMASK ]; lump = sprframe->lump[0]; flip = (boolean)sprframe->flip[0]; // calculate edges of the shape tx = psp->sx-160*FRACUNIT; tx -= spriteoffset[lump]; x1 = (centerxfrac + FixedMul (tx,pspritescale) ) >>FRACBITS; // off the right side if (x1 > viewwidth) return; tx += spritewidth[lump]; x2 = ((centerxfrac + FixedMul (tx, pspritescale) ) >>FRACBITS) - 1; // off the left side if (x2 < 0) return; // store information in a vissprite vis = &avis; vis->mobjflags = 0; vis->texturemid = (BASEYCENTER<sy-spritetopoffset[lump]); vis->x1 = x1 < 0 ? 0 : x1; vis->x2 = x2 >= viewwidth ? viewwidth-1 : x2; vis->scale = pspritescale<xiscale = -pspriteiscale; vis->startfrac = spritewidth[lump]-1; } else { vis->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) { // shadow draw vis->colormap = NULL; } else if (fixedcolormap) { // fixed color vis->colormap = fixedcolormap; } else if (psp->state->frame & FF_FULLBRIGHT) { // full bright vis->colormap = colormaps; } else { // local light vis->colormap = spritelights[MAXLIGHTSCALE-1]; } R_DrawVisSprite (vis, vis->x1, vis->x2); } // // R_DrawPlayerSprites // void R_DrawPlayerSprites (void) { int i; int lightnum; pspdef_t* psp; // get light level lightnum = (viewplayer->mo->subsector->sector->lightlevel >> LIGHTSEGSHIFT) +extralight; if (lightnum < 0) spritelights = scalelight[0]; else if (lightnum >= LIGHTLEVELS) spritelights = scalelight[LIGHTLEVELS-1]; else spritelights = scalelight[lightnum]; // clip to screen bounds mfloorclip = screenheightarray; mceilingclip = negonearray; // add all active psprites for (i=0, psp=viewplayer->psprites; istate) R_DrawPSprite (psp); } } // // R_SortVisSprites // vissprite_t vsprsortedhead; void R_SortVisSprites (void) { int i; int count; vissprite_t* ds; vissprite_t* best; vissprite_t unsorted; fixed_t bestscale; count = vissprite_p - vissprites; unsorted.next = unsorted.prev = &unsorted; if (!count) return; for (ds=vissprites ; dsnext = ds+1; ds->prev = ds-1; } vissprites[0].prev = &unsorted; unsorted.next = &vissprites[0]; (vissprite_p-1)->next = &unsorted; unsorted.prev = vissprite_p-1; // pull the vissprites out by scale vsprsortedhead.next = vsprsortedhead.prev = &vsprsortedhead; for (i=0 ; inext) { if (ds->scale < bestscale) { bestscale = ds->scale; best = ds; } } best->next->prev = best->prev; best->prev->next = best->next; best->next = &vsprsortedhead; best->prev = vsprsortedhead.prev; vsprsortedhead.prev->next = best; vsprsortedhead.prev = best; } } // // R_DrawSprite // static short clipbot[SCREENWIDTH]; static short cliptop[SCREENWIDTH]; void R_DrawSprite (vissprite_t* spr) { drawseg_t* ds; int x; int r1; int r2; fixed_t scale; fixed_t lowscale; int silhouette; 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. for (ds=ds_p-1 ; ds >= drawsegs ; ds--) { // determine if the drawseg obscures the sprite if (ds->x1 > spr->x2 || ds->x2 < spr->x1 || (!ds->silhouette && !ds->maskedtexturecol) ) { // does not cover sprite continue; } 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) ) ) { // masked mid texture? if (ds->maskedtexturecol) R_RenderMaskedSegRange (ds, r1, r2); // seg is behind sprite continue; } // clip this piece of the sprite silhouette = ds->silhouette; if (spr->gz >= ds->bsilheight) silhouette &= ~SIL_BOTTOM; if (spr->gzt <= ds->tsilheight) silhouette &= ~SIL_TOP; if (silhouette == 1) { // bottom sil for (x=r1 ; x<=r2 ; x++) if (clipbot[x] == -2) clipbot[x] = ds->sprbottomclip[x]; } else if (silhouette == 2) { // top sil for (x=r1 ; x<=r2 ; x++) if (cliptop[x] == -2) cliptop[x] = ds->sprtopclip[x]; } else if (silhouette == 3) { // both for (x=r1 ; x<=r2 ; x++) { if (clipbot[x] == -2) clipbot[x] = ds->sprbottomclip[x]; if (cliptop[x] == -2) cliptop[x] = ds->sprtopclip[x]; } } } // 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) { vissprite_t* spr; drawseg_t* ds; R_SortVisSprites (); if (vissprite_p > vissprites) { // draw all vissprites back to front for (spr = vsprsortedhead.next ; spr != &vsprsortedhead ; spr=spr->next) { R_DrawSprite (spr); } } // render any remaining masked mid textures for (ds=ds_p-1 ; ds >= drawsegs ; ds--) 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 (); } ================================================ FILE: fbdoom/r_things.h ================================================ // // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // 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. // // DESCRIPTION: // Rendering of moving objects, sprites. // #ifndef __R_THINGS__ #define __R_THINGS__ #define MAXVISSPRITES 128 extern vissprite_t vissprites[MAXVISSPRITES]; extern vissprite_t* vissprite_p; extern vissprite_t vsprsortedhead; // Constant arrays used for psprite clipping // and initializing clipping. extern short negonearray[SCREENWIDTH]; extern short screenheightarray[SCREENWIDTH]; // vars for R_DrawMaskedColumn extern short* mfloorclip; extern short* mceilingclip; extern fixed_t spryscale; extern fixed_t sprtopscreen; extern fixed_t pspritescale; extern fixed_t pspriteiscale; void R_DrawMaskedColumn (column_t* column); void R_SortVisSprites (void); void R_AddSprites (sector_t* sec); void R_AddPSprites (void); void R_DrawSprites (void); void R_InitSprites (char** namelist); void R_ClearSprites (void); void R_DrawMasked (void); void R_ClipVisSprite ( vissprite_t* vis, int xl, int xh ); #endif ================================================ FILE: fbdoom/s_sound.c ================================================ // // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // 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. // // DESCRIPTION: none // #include #include #include "i_sound.h" #include "i_system.h" #include "doomfeatures.h" #include "deh_str.h" #include "doomstat.h" #include "doomtype.h" #include "sounds.h" #include "s_sound.h" #include "m_misc.h" #include "m_random.h" #include "m_argv.h" #include "p_local.h" #include "w_wad.h" #include "z_zone.h" // when to clip out sounds // Does not fit the large outdoor areas. #define S_CLIPPING_DIST (1200 * FRACUNIT) // Distance tp origin when sounds should be maxed out. // This should relate to movement clipping resolution // (see BLOCKMAP handling). // In the source code release: (160*FRACUNIT). Changed back to the // Vanilla value of 200 (why was this changed?) #define S_CLOSE_DIST (200 * FRACUNIT) // The range over which sound attenuates #define S_ATTENUATOR ((S_CLIPPING_DIST - S_CLOSE_DIST) >> FRACBITS) // Stereo separation #define S_STEREO_SWING (96 * FRACUNIT) #define NORM_PITCH 128 #define NORM_PRIORITY 64 #define NORM_SEP 128 typedef struct { // sound information (if null, channel avail.) sfxinfo_t *sfxinfo; // origin of sound mobj_t *origin; // handle of the sound being played int handle; } channel_t; // The set of channels available static channel_t *channels; // Maximum volume of a sound effect. // Internal default is max out of 0-15. int sfxVolume = 8; // Maximum volume of music. int musicVolume = 8; // Internal volume level, ranging from 0-127 static int snd_SfxVolume; // Whether songs are mus_paused static boolean mus_paused; // Music currently being played static musicinfo_t *mus_playing = NULL; // Number of channels to use int snd_channels = 8; // // 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) { int i; I_PrecacheSounds(S_sfx, NUMSFX); S_SetSfxVolume(sfxVolume); S_SetMusicVolume(musicVolume); // Allocating the internal channels for mixing // (the maximum numer of sounds rendered // simultaneously) within zone memory. channels = Z_Malloc(snd_channels*sizeof(channel_t), PU_STATIC, 0); // Free all channels for use for (i=0 ; isfxinfo) { // 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 = NULL; } } // // Per level startup code. // Kills playing sounds at start of level, // determines music if any, changes music. // void S_Start(void) { int cnum; int mnum; // kill all playing sounds at start of level // (trust me - a good idea) for (cnum=0 ; cnumpriority >= sfxinfo->priority) { break; } } if (cnum == snd_channels) { // FUCK! No lower priority. Sorry, Charlie. return -1; } else { // Otherwise, kick out lower priority. S_StopChannel(cnum); } } c = &channels[cnum]; // channel is decided to be cnum. c->sfxinfo = sfxinfo; c->origin = origin; return cnum; } // // Changes volume and stereo-separation 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. // static int S_AdjustSoundParams(mobj_t *listener, mobj_t *source, int *vol, int *sep) { fixed_t approx_dist; fixed_t adx; fixed_t ady; angle_t angle; // calculate the distance to sound origin // and clip it if necessary adx = abs(listener->x - source->x); ady = abs(listener->y - source->y); // From _GG1_ p.428. Appox. eucledian distance fast. approx_dist = adx + ady - ((adx < ady ? adx : ady)>>1); if (gamemap != 8 && 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 = angle - listener->angle; } else { angle = angle + (0xffffffff - 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; } else if (gamemap == 8) { if (approx_dist > S_CLIPPING_DIST) { approx_dist = S_CLIPPING_DIST; } *vol = 15+ ((snd_SfxVolume-15) *((S_CLIPPING_DIST - approx_dist)>>FRACBITS)) / S_ATTENUATOR; } else { // distance effect *vol = (snd_SfxVolume * ((S_CLIPPING_DIST - approx_dist)>>FRACBITS)) / S_ATTENUATOR; } return (*vol > 0); } void S_StartSound(void *origin_p, int sfx_id) { sfxinfo_t *sfx; mobj_t *origin; int rc; int sep; int cnum; int volume; origin = (mobj_t *) origin_p; volume = snd_SfxVolume; // check for bogus sound # if (sfx_id < 1 || sfx_id > NUMSFX) { I_Error("Bad sfx #: %d", sfx_id); } sfx = &S_sfx[sfx_id]; // Initialize sound parameters if (sfx->link) { volume += sfx->volume; if (volume < 1) { return; } if (volume > snd_SfxVolume) { volume = snd_SfxVolume; } } // Check to see if it is audible, // and if not, modify the params if (origin && origin != players[consoleplayer].mo) { rc = S_AdjustSoundParams(players[consoleplayer].mo, origin, &volume, &sep); if (origin->x == players[consoleplayer].mo->x && origin->y == players[consoleplayer].mo->y) { sep = NORM_SEP; } if (!rc) { return; } } else { sep = NORM_SEP; } // kill old sound S_StopSound(origin); // try to find a channel cnum = S_GetChannel(origin, sfx); if (cnum < 0) { return; } // increase the usefulness if (sfx->usefulness++ < 0) { sfx->usefulness = 1; } if (sfx->lumpnum < 0) { sfx->lumpnum = I_GetSfxLumpNum(sfx); } channels[cnum].handle = I_StartSound(sfx, cnum, volume, sep); } // // Stop and resume music, during game PAUSE. // void S_PauseSound(void) { if (mus_playing && !mus_paused) { I_PauseSong(); mus_paused = true; } } void S_ResumeSound(void) { if (mus_playing && mus_paused) { I_ResumeSong(); mus_paused = false; } } // // Updates music & sounds // void S_UpdateSounds(mobj_t *listener) { int audible; int cnum; int volume; int sep; sfxinfo_t* sfx; channel_t* c; I_UpdateSound(); for (cnum=0; cnumsfxinfo; if (c->sfxinfo) { if (I_SoundIsPlaying(c->handle)) { // initialize parameters volume = snd_SfxVolume; sep = NORM_SEP; if (sfx->link) { 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 != c->origin) { audible = S_AdjustSoundParams(listener, c->origin, &volume, &sep); if (!audible) { S_StopChannel(cnum); } else { I_UpdateSoundParams(c->handle, volume, sep); } } } else { // if channel is allocated but sound has stopped, // free it S_StopChannel(cnum); } } } } void S_SetMusicVolume(int volume) { if (volume < 0 || volume > 127) { I_Error("Attempt to set music volume at %d", volume); } I_SetMusicVolume(volume); } void S_SetSfxVolume(int volume) { if (volume < 0 || volume > 127) { I_Error("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) { S_ChangeMusic(m_id, false); } void S_ChangeMusic(int musicnum, int looping) { musicinfo_t *music = NULL; char namebuf[9]; void *handle; // The Doom IWAD file has two versions of the intro music: d_intro // and d_introa. The latter is used for OPL playback. if (musicnum == mus_intro && (snd_musicdevice == SNDDEVICE_ADLIB || snd_musicdevice == SNDDEVICE_SB)) { musicnum = mus_introa; } if (musicnum <= mus_None || musicnum >= NUMMUSIC) { I_Error("Bad music number %d", musicnum); } else { music = &S_music[musicnum]; } if (mus_playing == music) { return; } // shutdown old music S_StopMusic(); // get lumpnum if neccessary if (!music->lumpnum) { M_snprintf(namebuf, sizeof(namebuf), "d_%s", DEH_String(music->name)); music->lumpnum = W_GetNumForName(namebuf); } music->data = W_CacheLumpNum(music->lumpnum, PU_STATIC); handle = I_RegisterSong(music->data, W_LumpLength(music->lumpnum)); music->handle = handle; I_PlaySong(handle, looping); mus_playing = music; } boolean S_MusicPlaying(void) { return I_MusicIsPlaying(); } void S_StopMusic(void) { if (mus_playing) { if (mus_paused) { I_ResumeSong(); } I_StopSong(); I_UnRegisterSong(mus_playing->handle); W_ReleaseLumpNum(mus_playing->lumpnum); mus_playing->data = NULL; mus_playing = NULL; } } ================================================ FILE: fbdoom/s_sound.h ================================================ // // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // 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. // // DESCRIPTION: // The not so system specific sound interface. // #ifndef __S_SOUND__ #define __S_SOUND__ #include "p_mobj.h" #include "sounds.h" // // 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); // Shut down sound void S_Shutdown(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); // Stop sound for thing at void S_StopSound(mobj_t *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); // query if music is playing boolean S_MusicPlaying(void); // 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(mobj_t *listener); void S_SetMusicVolume(int volume); void S_SetSfxVolume(int volume); extern int snd_channels; #endif ================================================ FILE: fbdoom/sha1.c ================================================ /* sha1.c - SHA1 hash function * Copyright (C) 1998, 1999, 2000, 2001 Free Software Foundation, Inc. * * Please see below for more legal information! * * This file is part of GnuPG. * * GnuPG 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 3 of the License, or * (at your option) any later version. * * GnuPG 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, see . */ /* Test vectors: * * "abc" * A999 3E36 4706 816A BA3E 2571 7850 C26C 9CD0 D89D * * "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq" * 8498 3E44 1C3B D26E BAAE 4AA1 F951 29E5 E546 70F1 */ #include #include #include #include #include "i_swap.h" #include "sha1.h" void SHA1_Init(sha1_context_t *hd) { hd->h0 = 0x67452301; hd->h1 = 0xefcdab89; hd->h2 = 0x98badcfe; hd->h3 = 0x10325476; hd->h4 = 0xc3d2e1f0; hd->nblocks = 0; hd->count = 0; } /**************** * Transform the message X which consists of 16 32-bit-words */ static void Transform(sha1_context_t *hd, byte *data) { uint32_t a,b,c,d,e,tm; uint32_t x[16]; /* get values from the chaining vars */ a = hd->h0; b = hd->h1; c = hd->h2; d = hd->h3; e = hd->h4; #ifdef SYS_BIG_ENDIAN memcpy(x, data, 64); #else { int i; byte *p2; for(i=0, p2=(byte*)x; i < 16; i++, p2 += 4 ) { p2[3] = *data++; p2[2] = *data++; p2[1] = *data++; p2[0] = *data++; } } #endif #define K1 0x5A827999L #define K2 0x6ED9EBA1L #define K3 0x8F1BBCDCL #define K4 0xCA62C1D6L #define F1(x,y,z) ( z ^ ( x & ( y ^ z ) ) ) #define F2(x,y,z) ( x ^ y ^ z ) #define F3(x,y,z) ( ( x & y ) | ( z & ( x | y ) ) ) #define F4(x,y,z) ( x ^ y ^ z ) #define rol(x,n) ( ((x) << (n)) | ((x) >> (32-(n))) ) #define M(i) ( tm = x[i&0x0f] ^ x[(i-14)&0x0f] \ ^ x[(i-8)&0x0f] ^ x[(i-3)&0x0f] \ , (x[i&0x0f] = rol(tm,1)) ) #define R(a,b,c,d,e,f,k,m) do { e += rol( a, 5 ) \ + f( b, c, d ) \ + k \ + m; \ b = rol( b, 30 ); \ } while(0) R( a, b, c, d, e, F1, K1, x[ 0] ); R( e, a, b, c, d, F1, K1, x[ 1] ); R( d, e, a, b, c, F1, K1, x[ 2] ); R( c, d, e, a, b, F1, K1, x[ 3] ); R( b, c, d, e, a, F1, K1, x[ 4] ); R( a, b, c, d, e, F1, K1, x[ 5] ); R( e, a, b, c, d, F1, K1, x[ 6] ); R( d, e, a, b, c, F1, K1, x[ 7] ); R( c, d, e, a, b, F1, K1, x[ 8] ); R( b, c, d, e, a, F1, K1, x[ 9] ); R( a, b, c, d, e, F1, K1, x[10] ); R( e, a, b, c, d, F1, K1, x[11] ); R( d, e, a, b, c, F1, K1, x[12] ); R( c, d, e, a, b, F1, K1, x[13] ); R( b, c, d, e, a, F1, K1, x[14] ); R( a, b, c, d, e, F1, K1, x[15] ); R( e, a, b, c, d, F1, K1, M(16) ); R( d, e, a, b, c, F1, K1, M(17) ); R( c, d, e, a, b, F1, K1, M(18) ); R( b, c, d, e, a, F1, K1, M(19) ); R( a, b, c, d, e, F2, K2, M(20) ); R( e, a, b, c, d, F2, K2, M(21) ); R( d, e, a, b, c, F2, K2, M(22) ); R( c, d, e, a, b, F2, K2, M(23) ); R( b, c, d, e, a, F2, K2, M(24) ); R( a, b, c, d, e, F2, K2, M(25) ); R( e, a, b, c, d, F2, K2, M(26) ); R( d, e, a, b, c, F2, K2, M(27) ); R( c, d, e, a, b, F2, K2, M(28) ); R( b, c, d, e, a, F2, K2, M(29) ); R( a, b, c, d, e, F2, K2, M(30) ); R( e, a, b, c, d, F2, K2, M(31) ); R( d, e, a, b, c, F2, K2, M(32) ); R( c, d, e, a, b, F2, K2, M(33) ); R( b, c, d, e, a, F2, K2, M(34) ); R( a, b, c, d, e, F2, K2, M(35) ); R( e, a, b, c, d, F2, K2, M(36) ); R( d, e, a, b, c, F2, K2, M(37) ); R( c, d, e, a, b, F2, K2, M(38) ); R( b, c, d, e, a, F2, K2, M(39) ); R( a, b, c, d, e, F3, K3, M(40) ); R( e, a, b, c, d, F3, K3, M(41) ); R( d, e, a, b, c, F3, K3, M(42) ); R( c, d, e, a, b, F3, K3, M(43) ); R( b, c, d, e, a, F3, K3, M(44) ); R( a, b, c, d, e, F3, K3, M(45) ); R( e, a, b, c, d, F3, K3, M(46) ); R( d, e, a, b, c, F3, K3, M(47) ); R( c, d, e, a, b, F3, K3, M(48) ); R( b, c, d, e, a, F3, K3, M(49) ); R( a, b, c, d, e, F3, K3, M(50) ); R( e, a, b, c, d, F3, K3, M(51) ); R( d, e, a, b, c, F3, K3, M(52) ); R( c, d, e, a, b, F3, K3, M(53) ); R( b, c, d, e, a, F3, K3, M(54) ); R( a, b, c, d, e, F3, K3, M(55) ); R( e, a, b, c, d, F3, K3, M(56) ); R( d, e, a, b, c, F3, K3, M(57) ); R( c, d, e, a, b, F3, K3, M(58) ); R( b, c, d, e, a, F3, K3, M(59) ); R( a, b, c, d, e, F4, K4, M(60) ); R( e, a, b, c, d, F4, K4, M(61) ); R( d, e, a, b, c, F4, K4, M(62) ); R( c, d, e, a, b, F4, K4, M(63) ); R( b, c, d, e, a, F4, K4, M(64) ); R( a, b, c, d, e, F4, K4, M(65) ); R( e, a, b, c, d, F4, K4, M(66) ); R( d, e, a, b, c, F4, K4, M(67) ); R( c, d, e, a, b, F4, K4, M(68) ); R( b, c, d, e, a, F4, K4, M(69) ); R( a, b, c, d, e, F4, K4, M(70) ); R( e, a, b, c, d, F4, K4, M(71) ); R( d, e, a, b, c, F4, K4, M(72) ); R( c, d, e, a, b, F4, K4, M(73) ); R( b, c, d, e, a, F4, K4, M(74) ); R( a, b, c, d, e, F4, K4, M(75) ); R( e, a, b, c, d, F4, K4, M(76) ); R( d, e, a, b, c, F4, K4, M(77) ); R( c, d, e, a, b, F4, K4, M(78) ); R( b, c, d, e, a, F4, K4, M(79) ); /* update chainig vars */ hd->h0 += a; hd->h1 += b; hd->h2 += c; hd->h3 += d; hd->h4 += e; } /* Update the message digest with the contents * of INBUF with length INLEN. */ void SHA1_Update(sha1_context_t *hd, byte *inbuf, size_t inlen) { if (hd->count == 64) { /* flush the buffer */ Transform(hd, hd->buf); hd->count = 0; hd->nblocks++; } if (!inbuf) return; if (hd->count) { for (; inlen && hd->count < 64; inlen--) hd->buf[hd->count++] = *inbuf++; SHA1_Update(hd, NULL, 0); if (!inlen) return; } while (inlen >= 64) { Transform(hd, inbuf); hd->count = 0; hd->nblocks++; inlen -= 64; inbuf += 64; } for (; inlen && hd->count < 64; inlen--) hd->buf[hd->count++] = *inbuf++; } /* The routine final terminates the computation and * returns the digest. * The handle is prepared for a new cycle, but adding bytes to the * handle will the destroy the returned buffer. * Returns: 20 bytes representing the digest. */ void SHA1_Final(sha1_digest_t digest, sha1_context_t *hd) { uint32_t t, msb, lsb; byte *p; SHA1_Update(hd, NULL, 0); /* flush */; t = hd->nblocks; /* multiply by 64 to make a byte count */ lsb = t << 6; msb = t >> 26; /* add the count */ t = lsb; if ((lsb += hd->count) < t) msb++; /* multiply by 8 to make a bit count */ t = lsb; lsb <<= 3; msb <<= 3; msb |= t >> 29; if (hd->count < 56) { /* enough room */ hd->buf[hd->count++] = 0x80; /* pad */ while (hd->count < 56) hd->buf[hd->count++] = 0; /* pad */ } else { /* need one extra block */ hd->buf[hd->count++] = 0x80; /* pad character */ while (hd->count < 64) hd->buf[hd->count++] = 0; SHA1_Update(hd, NULL, 0); /* flush */; memset(hd->buf, 0, 56 ); /* fill next block with zeroes */ } /* append the 64 bit count */ hd->buf[56] = msb >> 24; hd->buf[57] = msb >> 16; hd->buf[58] = msb >> 8; hd->buf[59] = msb ; hd->buf[60] = lsb >> 24; hd->buf[61] = lsb >> 16; hd->buf[62] = lsb >> 8; hd->buf[63] = lsb ; Transform(hd, hd->buf); p = hd->buf; #ifdef SYS_BIG_ENDIAN #define X(a) do { *(uint32_t*)p = hd->h##a ; p += 4; } while(0) #else /* little endian */ #define X(a) do { *p++ = hd->h##a >> 24; *p++ = hd->h##a >> 16; \ *p++ = hd->h##a >> 8; *p++ = hd->h##a; } while(0) #endif X(0); X(1); X(2); X(3); X(4); #undef X memcpy(digest, hd->buf, sizeof(sha1_digest_t)); } void SHA1_UpdateInt32(sha1_context_t *context, unsigned int val) { byte buf[4]; buf[0] = (val >> 24) & 0xff; buf[1] = (val >> 16) & 0xff; buf[2] = (val >> 8) & 0xff; buf[3] = val & 0xff; SHA1_Update(context, buf, 4); } void SHA1_UpdateString(sha1_context_t *context, char *str) { SHA1_Update(context, (byte *) str, strlen(str) + 1); } ================================================ FILE: fbdoom/sha1.h ================================================ // // Copyright(C) 2005-2014 Simon Howard // // 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. // // DESCRIPTION: // SHA-1 digest. // #ifndef __SHA1_H__ #define __SHA1_H__ #include "doomtype.h" typedef struct sha1_context_s sha1_context_t; typedef byte sha1_digest_t[20]; struct sha1_context_s { uint32_t h0,h1,h2,h3,h4; uint32_t nblocks; byte buf[64]; int count; }; void SHA1_Init(sha1_context_t *context); void SHA1_Update(sha1_context_t *context, byte *buf, size_t len); void SHA1_Final(sha1_digest_t digest, sha1_context_t *context); void SHA1_UpdateInt32(sha1_context_t *context, unsigned int val); void SHA1_UpdateString(sha1_context_t *context, char *str); #endif /* #ifndef __SHA1_H__ */ ================================================ FILE: fbdoom/sounds.c ================================================ // // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // 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. // // DESCRIPTION: // Created by a sound utility. // Kept as a sample, DOOM2 sounds. // #include #include "doomtype.h" #include "sounds.h" // // Information about all the music // #define MUSIC(name) \ { name, 0, NULL, NULL } musicinfo_t S_music[] = { MUSIC(NULL), MUSIC("e1m1"), MUSIC("e1m2"), MUSIC("e1m3"), MUSIC("e1m4"), MUSIC("e1m5"), MUSIC("e1m6"), MUSIC("e1m7"), MUSIC("e1m8"), MUSIC("e1m9"), MUSIC("e2m1"), MUSIC("e2m2"), MUSIC("e2m3"), MUSIC("e2m4"), MUSIC("e2m5"), MUSIC("e2m6"), MUSIC("e2m7"), MUSIC("e2m8"), MUSIC("e2m9"), MUSIC("e3m1"), MUSIC("e3m2"), MUSIC("e3m3"), MUSIC("e3m4"), MUSIC("e3m5"), MUSIC("e3m6"), MUSIC("e3m7"), MUSIC("e3m8"), MUSIC("e3m9"), MUSIC("inter"), MUSIC("intro"), MUSIC("bunny"), MUSIC("victor"), MUSIC("introa"), MUSIC("runnin"), MUSIC("stalks"), MUSIC("countd"), MUSIC("betwee"), MUSIC("doom"), MUSIC("the_da"), MUSIC("shawn"), MUSIC("ddtblu"), MUSIC("in_cit"), MUSIC("dead"), MUSIC("stlks2"), MUSIC("theda2"), MUSIC("doom2"), MUSIC("ddtbl2"), MUSIC("runni2"), MUSIC("dead2"), MUSIC("stlks3"), MUSIC("romero"), MUSIC("shawn2"), MUSIC("messag"), MUSIC("count2"), MUSIC("ddtbl3"), MUSIC("ampie"), MUSIC("theda3"), MUSIC("adrian"), MUSIC("messg2"), MUSIC("romer2"), MUSIC("tense"), MUSIC("shawn3"), MUSIC("openin"), MUSIC("evil"), MUSIC("ultima"), MUSIC("read_m"), MUSIC("dm2ttl"), MUSIC("dm2int") }; // // Information about all the sfx // #define SOUND(name, priority) \ { NULL, name, priority, NULL, -1, -1, 0, 0, -1, NULL } #define SOUND_LINK(name, priority, link_id, pitch, volume) \ { NULL, name, priority, &S_sfx[link_id], pitch, volume, 0, 0, -1, NULL } sfxinfo_t S_sfx[] = { // S_sfx[0] needs to be a dummy for odd reasons. SOUND("none", 0), SOUND("pistol", 64), SOUND("shotgn", 64), SOUND("sgcock", 64), SOUND("dshtgn", 64), SOUND("dbopn", 64), SOUND("dbcls", 64), SOUND("dbload", 64), SOUND("plasma", 64), SOUND("bfg", 64), SOUND("sawup", 64), SOUND("sawidl", 118), SOUND("sawful", 64), SOUND("sawhit", 64), SOUND("rlaunc", 64), SOUND("rxplod", 70), SOUND("firsht", 70), SOUND("firxpl", 70), SOUND("pstart", 100), SOUND("pstop", 100), SOUND("doropn", 100), SOUND("dorcls", 100), SOUND("stnmov", 119), SOUND("swtchn", 78), SOUND("swtchx", 78), SOUND("plpain", 96), SOUND("dmpain", 96), SOUND("popain", 96), SOUND("vipain", 96), SOUND("mnpain", 96), SOUND("pepain", 96), SOUND("slop", 78), SOUND("itemup", 78), SOUND("wpnup", 78), SOUND("oof", 96), SOUND("telept", 32), SOUND("posit1", 98), SOUND("posit2", 98), SOUND("posit3", 98), SOUND("bgsit1", 98), SOUND("bgsit2", 98), SOUND("sgtsit", 98), SOUND("cacsit", 98), SOUND("brssit", 94), SOUND("cybsit", 92), SOUND("spisit", 90), SOUND("bspsit", 90), SOUND("kntsit", 90), SOUND("vilsit", 90), SOUND("mansit", 90), SOUND("pesit", 90), SOUND("sklatk", 70), SOUND("sgtatk", 70), SOUND("skepch", 70), SOUND("vilatk", 70), SOUND("claw", 70), SOUND("skeswg", 70), SOUND("pldeth", 32), SOUND("pdiehi", 32), SOUND("podth1", 70), SOUND("podth2", 70), SOUND("podth3", 70), SOUND("bgdth1", 70), SOUND("bgdth2", 70), SOUND("sgtdth", 70), SOUND("cacdth", 70), SOUND("skldth", 70), SOUND("brsdth", 32), SOUND("cybdth", 32), SOUND("spidth", 32), SOUND("bspdth", 32), SOUND("vildth", 32), SOUND("kntdth", 32), SOUND("pedth", 32), SOUND("skedth", 32), SOUND("posact", 120), SOUND("bgact", 120), SOUND("dmact", 120), SOUND("bspact", 100), SOUND("bspwlk", 100), SOUND("vilact", 100), SOUND("noway", 78), SOUND("barexp", 60), SOUND("punch", 64), SOUND("hoof", 70), SOUND("metal", 70), SOUND_LINK("chgun", 64, sfx_pistol, 150, 0), SOUND("tink", 60), SOUND("bdopn", 100), SOUND("bdcls", 100), SOUND("itmbk", 100), SOUND("flame", 32), SOUND("flamst", 32), SOUND("getpow", 60), SOUND("bospit", 70), SOUND("boscub", 70), SOUND("bossit", 70), SOUND("bospn", 70), SOUND("bosdth", 70), SOUND("manatk", 70), SOUND("mandth", 70), SOUND("sssit", 70), SOUND("ssdth", 70), SOUND("keenpn", 70), SOUND("keendt", 70), SOUND("skeact", 70), SOUND("skesit", 70), SOUND("skeatk", 70), SOUND("radio", 60), }; ================================================ FILE: fbdoom/sounds.h ================================================ // // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // 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. // // DESCRIPTION: // Created by the sound utility written by Dave Taylor. // Kept as a sample, DOOM2 sounds. Frozen. // #ifndef __SOUNDS__ #define __SOUNDS__ #include "i_sound.h" // 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, NUMSFX } sfxenum_t; #endif ================================================ FILE: fbdoom/st_lib.c ================================================ // // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // 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. // // DESCRIPTION: // The status bar widget code. // #include #include #include "deh_main.h" #include "doomdef.h" #include "z_zone.h" #include "v_video.h" #include "i_swap.h" #include "i_system.h" #include "w_wad.h" #include "st_stuff.h" #include "st_lib.h" #include "r_local.h" // in AM_map.c extern boolean automapactive; // // Hack display negative frags. // Loads and store the stminus lump. // patch_t* sttminus; void STlib_init(void) { sttminus = (patch_t *) W_CacheLumpName(DEH_String("STTMINUS"), PU_STATIC); } // ? void STlib_initNum ( st_number_t* n, int x, int y, patch_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; } // // A fairly efficient way to draw a number // based on differences from the old number. // Note: worth the trouble? // void STlib_drawNum ( st_number_t* n, boolean refresh ) { int numdigits = n->width; int num = *n->num; int w = SHORT(n->p[0]->width); int h = SHORT(n->p[0]->height); int x = n->x; int neg; n->oldnum = *n->num; neg = num < 0; if (neg) { 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; if (n->y - ST_Y < 0) I_Error("drawNum: n->y - ST_Y < 0"); V_CopyRect(x, n->y - ST_Y, st_backing_screen, w*numdigits, h, x, n->y); // if non-number, do not draw it if (num == 1994) return; x = n->x; // in the special case of 0, you draw 0 if (!num) V_DrawPatch(x - w, n->y, n->p[ 0 ]); // draw the new number while (num && numdigits--) { x -= w; V_DrawPatch(x, n->y, n->p[ num % 10 ]); num /= 10; } // draw a minus sign if necessary if (neg) V_DrawPatch(x - 8, n->y, sttminus); } // void STlib_updateNum ( st_number_t* n, boolean refresh ) { if (*n->on) STlib_drawNum(n, refresh); } // void STlib_initPercent ( st_percent_t* p, int x, int y, patch_t** pl, int* num, boolean* on, patch_t* percent ) { STlib_initNum(&p->n, x, y, pl, num, on, 3); p->p = percent; } void STlib_updatePercent ( st_percent_t* per, int refresh ) { if (refresh && *per->n.on) V_DrawPatch(per->n.x, per->n.y, per->p); STlib_updateNum(&per->n, refresh); } void STlib_initMultIcon ( st_multicon_t* i, int x, int y, patch_t** il, int* inum, boolean* on ) { i->x = x; i->y = y; i->oldinum = -1; i->inum = inum; i->on = on; i->p = il; } 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) && (*mi->inum != -1)) { if (mi->oldinum != -1) { x = mi->x - SHORT(mi->p[mi->oldinum]->leftoffset); y = mi->y - SHORT(mi->p[mi->oldinum]->topoffset); w = SHORT(mi->p[mi->oldinum]->width); h = SHORT(mi->p[mi->oldinum]->height); if (y - ST_Y < 0) I_Error("updateMultIcon: y - ST_Y < 0"); V_CopyRect(x, y-ST_Y, st_backing_screen, w, h, x, y); } V_DrawPatch(mi->x, mi->y, mi->p[*mi->inum]); mi->oldinum = *mi->inum; } } void STlib_initBinIcon ( st_binicon_t* b, int x, int y, patch_t* i, boolean* val, boolean* on ) { b->x = x; b->y = y; b->oldval = false; b->val = val; b->on = on; b->p = i; } 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 - SHORT(bi->p->leftoffset); y = bi->y - SHORT(bi->p->topoffset); w = SHORT(bi->p->width); h = SHORT(bi->p->height); if (y - ST_Y < 0) I_Error("updateBinIcon: y - ST_Y < 0"); if (*bi->val) V_DrawPatch(bi->x, bi->y, bi->p); else V_CopyRect(x, y-ST_Y, st_backing_screen, w, h, x, y); bi->oldval = *bi->val; } } ================================================ FILE: fbdoom/st_lib.h ================================================ // // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // 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. // // DESCRIPTION: // The status bar widget code. // #ifndef __STLIB__ #define __STLIB__ // We are referring to patches. #include "r_defs.h" // // 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 patch_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 patch_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 patch_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; patch_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, patch_t** pl, int* num, boolean* on, int width ); void STlib_updateNum ( st_number_t* n, boolean refresh ); // Percent widget routines void STlib_initPercent ( st_percent_t* p, int x, int y, patch_t** pl, int* num, boolean* on, patch_t* percent ); void STlib_updatePercent ( st_percent_t* per, int refresh ); // Multiple Icon widget routines void STlib_initMultIcon ( st_multicon_t* mi, int x, int y, patch_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, patch_t* i, boolean* val, boolean* on ); void STlib_updateBinIcon ( st_binicon_t* bi, boolean refresh ); #endif ================================================ FILE: fbdoom/st_stuff.c ================================================ // // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // 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. // // DESCRIPTION: // Status bar code. // Does the face/direction indicator animatin. // Does palette indicators as well (red pain/berserk, bright pickup) // #include #include "i_system.h" #include "i_video.h" #include "z_zone.h" #include "m_misc.h" #include "m_random.h" #include "w_wad.h" #include "deh_main.h" #include "deh_misc.h" #include "doomdef.h" #include "doomkeys.h" #include "g_game.h" #include "st_stuff.h" #include "st_lib.h" #include "r_local.h" #include "p_local.h" #include "p_inter.h" #include "am_map.h" #include "m_cheat.h" #include "s_sound.h" // Needs access to LFB. #include "v_video.h" // State. #include "doomstat.h" // Data. #include "dstrings.h" #include "sounds.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 // N/256*100% probability // that the normal face state will change #define ST_FACEPROBABILITY 96 // For Responder #define ST_TOGGLECHAT KEY_ENTER // Location of status bar #define ST_X 0 #define ST_X2 104 #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) #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? // AMMO number pos. #define ST_AMMOWIDTH 3 #define ST_AMMOX 44 #define ST_AMMOY 171 // HEALTH number pos. #define ST_HEALTHWIDTH 3 #define ST_HEALTHX 90 #define ST_HEALTHY 171 // Weapon pos. #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. #define ST_FRAGSX 138 #define ST_FRAGSY 171 #define ST_FRAGSWIDTH 2 // ARMOR number pos. #define ST_ARMORWIDTH 3 #define ST_ARMORX 221 #define ST_ARMORY 171 // Key icon positions. #define ST_KEY0WIDTH 8 #define ST_KEY0HEIGHT 5 #define ST_KEY0X 239 #define ST_KEY0Y 171 #define ST_KEY1WIDTH ST_KEY0WIDTH #define ST_KEY1X 239 #define ST_KEY1Y 181 #define ST_KEY2WIDTH ST_KEY0WIDTH #define ST_KEY2X 239 #define ST_KEY2Y 191 // Ammunition counter. #define ST_AMMO0WIDTH 3 #define ST_AMMO0HEIGHT 6 #define ST_AMMO0X 288 #define ST_AMMO0Y 173 #define ST_AMMO1WIDTH ST_AMMO0WIDTH #define ST_AMMO1X 288 #define ST_AMMO1Y 179 #define ST_AMMO2WIDTH ST_AMMO0WIDTH #define ST_AMMO2X 288 #define ST_AMMO2Y 191 #define ST_AMMO3WIDTH ST_AMMO0WIDTH #define ST_AMMO3X 288 #define ST_AMMO3Y 185 // Indicate maximum ammunition. // Only needed because backpack exists. #define ST_MAXAMMO0WIDTH 3 #define ST_MAXAMMO0HEIGHT 5 #define ST_MAXAMMO0X 314 #define ST_MAXAMMO0Y 173 #define ST_MAXAMMO1WIDTH ST_MAXAMMO0WIDTH #define ST_MAXAMMO1X 314 #define ST_MAXAMMO1Y 179 #define ST_MAXAMMO2WIDTH ST_MAXAMMO0WIDTH #define ST_MAXAMMO2X 314 #define ST_MAXAMMO2Y 191 #define ST_MAXAMMO3WIDTH ST_MAXAMMO0WIDTH #define ST_MAXAMMO3X 314 #define ST_MAXAMMO3Y 185 // pistol #define ST_WEAPON0X 110 #define ST_WEAPON0Y 172 // shotgun #define ST_WEAPON1X 122 #define ST_WEAPON1Y 172 // chain gun #define ST_WEAPON2X 134 #define ST_WEAPON2Y 172 // missile launcher #define ST_WEAPON3X 110 #define ST_WEAPON3Y 181 // plasma gun #define ST_WEAPON4X 122 #define ST_WEAPON4Y 181 // bfg #define ST_WEAPON5X 134 #define ST_WEAPON5Y 181 // WPNS title #define ST_WPNSX 109 #define ST_WPNSY 191 // DETH title #define ST_DETHX 109 #define ST_DETHY 191 //Incoming messages window location //UNUSED // #define ST_MSGTEXTX (viewwindowx) // #define ST_MSGTEXTY (viewwindowy+viewheight-18) #define ST_MSGTEXTX 0 #define ST_MSGTEXTY 0 // Dimensions given in characters. #define ST_MSGWIDTH 52 // Or shall I say, in lines? #define ST_MSGHEIGHT 1 #define ST_OUTTEXTX 0 #define ST_OUTTEXTY 6 // Width, in characters again. #define ST_OUTWIDTH 52 // Height, in lines. #define ST_OUTHEIGHT 1 #define ST_MAPTITLEX \ (SCREENWIDTH - ST_MAPWIDTH * ST_CHATFONTWIDTH) #define ST_MAPTITLEY 0 #define ST_MAPHEIGHT 1 // graphics are drawn to a backing screen and blitted to the real screen byte *st_backing_screen; // main player in game static player_t* plyr; // ST_Start() has just been called static boolean st_firsttime; // lump number for PLAYPAL static int lu_palette; // 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; // main bar left static patch_t* sbar; // 0-9, tall numbers static patch_t* tallnum[10]; // tall % sign static patch_t* tallpercent; // 0-9, short, yellow (,different!) numbers static patch_t* shortnum[10]; // 3 key-cards, 3 skulls static patch_t* keys[NUMCARDS]; // face status patches static patch_t* faces[ST_NUMFACES]; // face background static patch_t* faceback; // main bar right static patch_t* armsbg; // weapon ownership patches static patch_t* arms[6][2]; // ready-weapon widget static st_number_t w_ready; // 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; cheatseq_t cheat_mus = CHEAT("idmus", 2); cheatseq_t cheat_god = CHEAT("iddqd", 0); cheatseq_t cheat_ammo = CHEAT("idkfa", 0); cheatseq_t cheat_ammonokey = CHEAT("idfa", 0); cheatseq_t cheat_noclip = CHEAT("idspispopd", 0); cheatseq_t cheat_commercial_noclip = CHEAT("idclip", 0); cheatseq_t cheat_powerup[7] = { CHEAT("idbeholdv", 0), CHEAT("idbeholds", 0), CHEAT("idbeholdi", 0), CHEAT("idbeholdr", 0), CHEAT("idbeholda", 0), CHEAT("idbeholdl", 0), CHEAT("idbehold", 0), }; cheatseq_t cheat_choppers = CHEAT("idchoppers", 0); cheatseq_t cheat_clev = CHEAT("idclev", 2); cheatseq_t cheat_mypos = CHEAT("idmypos", 0); // // STATUS BAR CODE // void ST_Stop(void); void ST_refreshBackground(void) { if (st_statusbaron) { V_UseBuffer(st_backing_screen); V_DrawPatch(ST_X, 0, sbar); if (netgame) V_DrawPatch(ST_FX, 0, faceback); V_RestoreBuffer(); V_CopyRect(ST_X, 0, st_backing_screen, ST_WIDTH, ST_HEIGHT, ST_X, ST_Y); } } // Respond to keyboard input events, // intercept cheats. boolean ST_Responder (event_t* ev) { int i; // 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: // fprintf(stderr, "AM exited\n"); st_gamestate = FirstPersonState; break; } } // if a user keypress... else if (ev->type == ev_keydown) { if (!netgame && gameskill != sk_nightmare) { // 'dqd' cheat for toggleable god mode if (cht_CheckCheat(&cheat_god, ev->data2)) { plyr->cheats ^= CF_GODMODE; if (plyr->cheats & CF_GODMODE) { if (plyr->mo) plyr->mo->health = 100; plyr->health = deh_god_mode_health; plyr->message = DEH_String(STSTR_DQDON); } else plyr->message = DEH_String(STSTR_DQDOFF); } // 'fa' cheat for killer fucking arsenal else if (cht_CheckCheat(&cheat_ammonokey, ev->data2)) { plyr->armorpoints = deh_idfa_armor; plyr->armortype = deh_idfa_armor_class; for (i=0;iweaponowned[i] = true; for (i=0;iammo[i] = plyr->maxammo[i]; plyr->message = DEH_String(STSTR_FAADDED); } // 'kfa' cheat for key full ammo else if (cht_CheckCheat(&cheat_ammo, ev->data2)) { plyr->armorpoints = deh_idkfa_armor; plyr->armortype = deh_idkfa_armor_class; for (i=0;iweaponowned[i] = true; for (i=0;iammo[i] = plyr->maxammo[i]; for (i=0;icards[i] = true; plyr->message = DEH_String(STSTR_KFAADDED); } // 'mus' cheat for changing music else if (cht_CheckCheat(&cheat_mus, ev->data2)) { char buf[3]; int musnum; plyr->message = DEH_String(STSTR_MUS); cht_GetParam(&cheat_mus, buf); // Note: The original v1.9 had a bug that tried to play back // the Doom II music regardless of gamemode. This was fixed // in the Ultimate Doom executable so that it would work for // the Doom 1 music as well. if (gamemode == commercial || gameversion < exe_ultimate) { musnum = mus_runnin + (buf[0]-'0')*10 + buf[1]-'0' - 1; if (((buf[0]-'0')*10 + buf[1]-'0') > 35) plyr->message = DEH_String(STSTR_NOMUS); else S_ChangeMusic(musnum, 1); } else { musnum = mus_e1m1 + (buf[0]-'1')*9 + (buf[1]-'1'); if (((buf[0]-'1')*9 + buf[1]-'1') > 31) plyr->message = DEH_String(STSTR_NOMUS); else S_ChangeMusic(musnum, 1); } } else if ( (logical_gamemission == doom && cht_CheckCheat(&cheat_noclip, ev->data2)) || (logical_gamemission != doom && cht_CheckCheat(&cheat_commercial_noclip,ev->data2))) { // Noclip cheat. // For Doom 1, use the idspipsopd cheat; for all others, use // idclip plyr->cheats ^= CF_NOCLIP; if (plyr->cheats & CF_NOCLIP) plyr->message = DEH_String(STSTR_NCON); else plyr->message = DEH_String(STSTR_NCOFF); } // 'behold?' power-up cheats for (i=0;i<6;i++) { if (cht_CheckCheat(&cheat_powerup[i], ev->data2)) { if (!plyr->powers[i]) P_GivePower( plyr, i); else if (i!=pw_strength) plyr->powers[i] = 1; else plyr->powers[i] = 0; plyr->message = DEH_String(STSTR_BEHOLDX); } } // 'behold' power-up menu if (cht_CheckCheat(&cheat_powerup[6], ev->data2)) { plyr->message = DEH_String(STSTR_BEHOLD); } // 'choppers' invulnerability & chainsaw else if (cht_CheckCheat(&cheat_choppers, ev->data2)) { plyr->weaponowned[wp_chainsaw] = true; plyr->powers[pw_invulnerability] = true; plyr->message = DEH_String(STSTR_CHOPPERS); } // 'mypos' for player position else if (cht_CheckCheat(&cheat_mypos, ev->data2)) { static char buf[ST_MSGWIDTH]; M_snprintf(buf, sizeof(buf), "ang=0x%x;x,y=(0x%x,0x%x)", players[consoleplayer].mo->angle, players[consoleplayer].mo->x, players[consoleplayer].mo->y); plyr->message = buf; } } // 'clev' change-level cheat if (!netgame && cht_CheckCheat(&cheat_clev, ev->data2)) { char buf[3]; int epsd; int map; cht_GetParam(&cheat_clev, buf); if (gamemode == commercial) { epsd = 1; map = (buf[0] - '0')*10 + buf[1] - '0'; } else { epsd = buf[0] - '0'; map = buf[1] - '0'; } // Chex.exe always warps to episode 1. if (gameversion == exe_chex) { epsd = 1; } // Catch invalid maps. if (epsd < 1) return false; if (map < 1) return false; // Ohmygod - this is not going to work. if ((gamemode == retail) && ((epsd > 4) || (map > 9))) return false; if ((gamemode == registered) && ((epsd > 3) || (map > 9))) return false; if ((gamemode == shareware) && ((epsd > 1) || (map > 9))) return false; // The source release has this check as map > 34. However, Vanilla // Doom allows IDCLEV up to MAP40 even though it normally crashes. if ((gamemode == commercial) && (( epsd > 1) || (map > 40))) return false; // So be it. plyr->message = DEH_String(STSTR_CLEV); G_DeferedInitNew(gameskill, epsd, map); } } return false; } int ST_calcPainOffset(void) { int health; static int lastcalc; static int oldhealth = -1; 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 // 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; if (plyr->health - st_oldhealth > 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) { if (plyr->health - st_oldhealth > 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--; } 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; if (plyr->cards[i+3]) keyboxes[i] = i+3; } // 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; } static int st_palette = 0; void ST_doPaletteStuff(void) { int palette; byte* pal; int cnt; int bzc; cnt = plyr->damagecount; if (plyr->powers[pw_strength]) { // slowly fade the berzerk out bzc = 12 - (plyr->powers[pw_strength]>>6); if (bzc > cnt) cnt = bzc; } if (cnt) { palette = (cnt+7)>>3; if (palette >= NUMREDPALS) palette = NUMREDPALS-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; // In Chex Quest, the player never sees red. Instead, the // radiation suit palette is used to tint the screen green, // as though the player is being covered in goo by an // attacking flemoid. if (gameversion == exe_chex && palette >= STARTREDPALS && palette < STARTREDPALS + NUMREDPALS) { palette = RADIATIONPAL; } if (palette != st_palette) { st_palette = palette; pal = (byte *) W_CacheLumpNum (lu_palette, PU_CACHE)+palette*768; I_SetPalette (pal); } } 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; STlib_updateNum(&w_ready, refresh); for (i=0;i<4;i++) { STlib_updateNum(&w_ammo[i], refresh); STlib_updateNum(&w_maxammo[i], refresh); } STlib_updatePercent(&w_health, refresh); STlib_updatePercent(&w_armor, refresh); STlib_updateBinIcon(&w_armsbg, refresh); for (i=0;i<6;i++) STlib_updateMultIcon(&w_arms[i], refresh); STlib_updateMultIcon(&w_faces, refresh); for (i=0;i<3;i++) STlib_updateMultIcon(&w_keyboxes[i], refresh); STlib_updateNum(&w_frags, refresh); } void ST_doRefresh(void) { st_firsttime = false; // draw status bar background to off-screen buff ST_refreshBackground(); // and refresh all widgets ST_drawWidgets(true); } void ST_diffDraw(void) { // update all widgets ST_drawWidgets(false); } void ST_Drawer (boolean fullscreen, boolean refresh) { st_statusbaron = (!fullscreen) || automapactive; st_firsttime = st_firsttime || refresh; // Do red-/gold-shifts from damage/items ST_doPaletteStuff(); // If just after ST_Start(), refresh all if (st_firsttime) ST_doRefresh(); // Otherwise, update as little as possible else ST_diffDraw(); } typedef void (*load_callback_t)(char *lumpname, patch_t **variable); // Iterates through all graphics to be loaded or unloaded, along with // the variable they use, invoking the specified callback function. static void ST_loadUnloadGraphics(load_callback_t callback) { int i; int j; int facenum; char namebuf[9]; // Load the numbers, tall and short for (i=0;i<10;i++) { DEH_snprintf(namebuf, 9, "STTNUM%d", i); callback(namebuf, &tallnum[i]); DEH_snprintf(namebuf, 9, "STYSNUM%d", i); callback(namebuf, &shortnum[i]); } // Load percent key. //Note: why not load STMINUS here, too? callback(DEH_String("STTPRCNT"), &tallpercent); // key cards for (i=0;iweaponowned[i]; for (i=0;i<3;i++) keyboxes[i] = -1; STlib_init(); } 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; } void ST_Stop (void) { if (st_stopped) return; I_SetPalette (W_CacheLumpNum (lu_palette, PU_CACHE)); st_stopped = true; } void ST_Init (void) { ST_loadData(); st_backing_screen = (byte *) Z_Malloc(ST_WIDTH * ST_HEIGHT, PU_STATIC, 0); } ================================================ FILE: fbdoom/st_stuff.h ================================================ // // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // 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. // // 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" #include "m_cheat.h" // Size of statusbar. // Now sensitive for scaling. #define ST_HEIGHT 32 #define ST_WIDTH SCREENWIDTH #define ST_Y (SCREENHEIGHT - ST_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 fullscreen, 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; extern byte *st_backing_screen; extern cheatseq_t cheat_mus; extern cheatseq_t cheat_god; extern cheatseq_t cheat_ammo; extern cheatseq_t cheat_ammonokey; extern cheatseq_t cheat_noclip; extern cheatseq_t cheat_commercial_noclip; extern cheatseq_t cheat_powerup[7]; extern cheatseq_t cheat_choppers; extern cheatseq_t cheat_clev; extern cheatseq_t cheat_mypos; #endif ================================================ FILE: fbdoom/statdump.c ================================================ /* Copyright(C) 2005-2014 Simon Howard 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. -- Functions for presenting the information captured from the statistics buffer to a file. */ #include #include #include #include "d_player.h" #include "d_mode.h" #include "m_argv.h" #include "statdump.h" /* Par times for E1M1-E1M9. */ static const int doom1_par_times[] = { 30, 75, 120, 90, 165, 180, 180, 30, 165, }; /* Par times for MAP01-MAP09. */ static const int doom2_par_times[] = { 30, 90, 120, 120, 90, 150, 120, 120, 270, }; #if ORIGCODE /* Player colors. */ static const char *player_colors[] = { "Green", "Indigo", "Brown", "Red" }; #endif // Array of end-of-level statistics that have been captured. #define MAX_CAPTURES 32 static wbstartstruct_t captured_stats[MAX_CAPTURES]; static int num_captured_stats = 0; #if ORIGCODE static GameMission_t discovered_gamemission = none; #endif #if ORIGCODE /* Try to work out whether this is a Doom 1 or Doom 2 game, by looking * at the episode and map, and the par times. This is used to decide * how to format the level name. Unfortunately, in some cases it is * impossible to determine whether this is Doom 1 or Doom 2. */ static void DiscoverGamemode(wbstartstruct_t *stats, int num_stats) { int partime; int level; int i; if (discovered_gamemission != none) { return; } for (i=0; i 0) { discovered_gamemission = doom; return; } /* This is episode 1. If this is level 10 or higher, it must be Doom 2. */ if (level >= 9) { discovered_gamemission = doom2; return; } /* Try to work out if this is Doom 1 or Doom 2 by looking at the par time. */ partime = stats[i].partime; if (partime == doom1_par_times[level] * TICRATE && partime != doom2_par_times[level] * TICRATE) { discovered_gamemission = doom; return; } if (partime != doom1_par_times[level] * TICRATE && partime == doom2_par_times[level] * TICRATE) { discovered_gamemission = doom2; return; } } } #endif #if ORIGCODE /* Returns the number of players active in the given stats buffer. */ static int GetNumPlayers(wbstartstruct_t *stats) { int i; int num_players = 0; for (i=0; iplyr[i].in) { ++num_players; } } return num_players; } #endif #if ORIGCODE static void PrintBanner(FILE *stream) { fprintf(stream, "===========================================\n"); } static void PrintPercentage(FILE *stream, int amount, int total) { if (total == 0) { fprintf(stream, "0"); } else { fprintf(stream, "%i / %i", amount, total); // statdump.exe is a 16-bit program, so very occasionally an // integer overflow can occur when doing this calculation with // a large value. Therefore, cast to short to give the same // output. fprintf(stream, " (%i%%)", (short) (amount * 100) / total); } } #endif #if ORIGCODE /* Display statistics for a single player. */ static void PrintPlayerStats(FILE *stream, wbstartstruct_t *stats, int player_num) { wbplayerstruct_t *player = &stats->plyr[player_num]; fprintf(stream, "Player %i (%s):\n", player_num + 1, player_colors[player_num]); /* Kills percentage */ fprintf(stream, "\tKills: "); PrintPercentage(stream, player->skills, stats->maxkills); fprintf(stream, "\n"); /* Items percentage */ fprintf(stream, "\tItems: "); PrintPercentage(stream, player->sitems, stats->maxitems); fprintf(stream, "\n"); /* Secrets percentage */ fprintf(stream, "\tSecrets: "); PrintPercentage(stream, player->ssecret, stats->maxsecret); fprintf(stream, "\n"); } #endif #if ORIGCODE /* Frags table for multiplayer games. */ static void PrintFragsTable(FILE *stream, wbstartstruct_t *stats) { int x, y; fprintf(stream, "Frags:\n"); /* Print header */ fprintf(stream, "\t\t"); for (x=0; xplyr[x].in) { continue; } fprintf(stream, "%s\t", player_colors[x]); } fprintf(stream, "\n"); fprintf(stream, "\t\t-------------------------------- VICTIMS\n"); /* Print table */ for (y=0; yplyr[y].in) { continue; } fprintf(stream, "\t%s\t|", player_colors[y]); for (x=0; xplyr[x].in) { continue; } fprintf(stream, "%i\t", stats->plyr[y].frags[x]); } fprintf(stream, "\n"); } fprintf(stream, "\t\t|\n"); fprintf(stream, "\t KILLERS\n"); } #endif #if ORIGCODE /* Displays the level name: MAPxy or ExMy, depending on game mode. */ static void PrintLevelName(FILE *stream, int episode, int level) { PrintBanner(stream); switch (discovered_gamemission) { case doom: fprintf(stream, "E%iM%i\n", episode + 1, level + 1); break; case doom2: fprintf(stream, "MAP%02i\n", level + 1); break; default: case none: fprintf(stream, "E%iM%i / MAP%02i\n", episode + 1, level + 1, level + 1); break; } PrintBanner(stream); } #endif #if ORIGCODE /* Print details of a statistics buffer to the given file. */ static void PrintStats(FILE *stream, wbstartstruct_t *stats) { int leveltime, partime; int i; PrintLevelName(stream, stats->epsd, stats->last); fprintf(stream, "\n"); leveltime = stats->plyr[0].stime / TICRATE; partime = stats->partime / TICRATE; fprintf(stream, "Time: %i:%02i", leveltime / 60, leveltime % 60); fprintf(stream, " (par: %i:%02i)\n", partime / 60, partime % 60); fprintf(stream, "\n"); for (i=0; iplyr[i].in) { PrintPlayerStats(stream, stats, i); } } if (GetNumPlayers(stats) >= 2) { PrintFragsTable(stream, stats); } fprintf(stream, "\n"); } #endif void StatCopy(wbstartstruct_t *stats) { if (M_ParmExists("-statdump") && num_captured_stats < MAX_CAPTURES) { memcpy(&captured_stats[num_captured_stats], stats, sizeof(wbstartstruct_t)); ++num_captured_stats; } } void StatDump(void) { #if ORIGCODE FILE *dumpfile; int i; //! // @category compat // @arg // // Dump statistics information to the specified file on the levels // that were played. The output from this option matches the output // from statdump.exe (see ctrlapi.zip in the /idgames archive). // i = M_CheckParmWithArgs("-statdump", 1); if (i > 0) { printf("Statistics captured for %i level(s)\n", num_captured_stats); // We actually know what the real gamemission is, but this has // to match the output from statdump.exe. DiscoverGamemode(captured_stats, num_captured_stats); // Allow "-" as output file, for stdout. if (strcmp(myargv[i + 1], "-") != 0) { dumpfile = fopen(myargv[i + 1], "w"); } else { dumpfile = NULL; } for (i = 0; i < num_captured_stats; ++i) { PrintStats(dumpfile, &captured_stats[i]); } if (dumpfile != NULL) { fclose(dumpfile); } } #endif } ================================================ FILE: fbdoom/statdump.h ================================================ /* Copyright(C) 2005-2014 Simon Howard 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. */ #ifndef DOOM_STATDUMP_H #define DOOM_STATDUMP_H void StatCopy(wbstartstruct_t *stats); void StatDump(void); #endif /* #ifndef DOOM_STATDUMP_H */ ================================================ FILE: fbdoom/stubs.c ================================================ #include #include #include #include //XXX FIXME in_addr_t inet_addr(const char *cp) { return (in_addr_t)NULL; } ================================================ FILE: fbdoom/tables.c ================================================ // // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // 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. // // 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. // // #include "tables.h" // 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 is to handle // the case when x==y without additional checking. int SlopeDiv(unsigned int num, unsigned int den) { unsigned ans; if (den < 512) { return SLOPERANGE; } else { ans = (num << 3) / (den >> 8); if (ans <= SLOPERANGE) { return ans; } else { return SLOPERANGE; } } } const int finetangent[4096] = { -170910304,-56965752,-34178904,-24413316,-18988036,-15535599,-13145455,-11392683, -10052327,-8994149,-8137527,-7429880,-6835455,-6329090,-5892567,-5512368, -5178251,-4882318,-4618375,-4381502,-4167737,-3973855,-3797206,-3635590, -3487165,-3350381,-3223918,-3106651,-2997613,-2895966,-2800983,-2712030, -2628549,-2550052,-2476104,-2406322,-2340362,-2277919,-2218719,-2162516, -2109087,-2058233,-2009771,-1963536,-1919378,-1877161,-1836758,-1798063, -1760956,-1725348,-1691149,-1658278,-1626658,-1596220,-1566898,-1538632, -1511367,-1485049,-1459630,-1435065,-1411312,-1388330,-1366084,-1344537, -1323658,-1303416,-1283783,-1264730,-1246234,-1228269,-1210813,-1193846, -1177345,-1161294,-1145673,-1130465,-1115654,-1101225,-1087164,-1073455, -1060087,-1047046,-1034322,-1021901,-1009774,-997931,-986361,-975054, -964003,-953199,-942633,-932298,-922186,-912289,-902602,-893117, -883829,-874730,-865817,-857081,-848520,-840127,-831898,-823827, -815910,-808143,-800521,-793041,-785699,-778490,-771411,-764460, -757631,-750922,-744331,-737853,-731486,-725227,-719074,-713023, -707072,-701219,-695462,-689797,-684223,-678737,-673338,-668024, -662792,-657640,-652568,-647572,-642651,-637803,-633028,-628323, -623686,-619117,-614613,-610174,-605798,-601483,-597229,-593033, -588896,-584815,-580789,-576818,-572901,-569035,-565221,-561456, -557741,-554074,-550455,-546881,-543354,-539870,-536431,-533034, -529680,-526366,-523094,-519861,-516667,-513512,-510394,-507313, -504269,-501261,-498287,-495348,-492443,-489571,-486732,-483925, -481150,-478406,-475692,-473009,-470355,-467730,-465133,-462565, -460024,-457511,-455024,-452564,-450129,-447720,-445337,-442978, -440643,-438332,-436045,-433781,-431540,-429321,-427125,-424951, -422798,-420666,-418555,-416465,-414395,-412344,-410314,-408303, -406311,-404338,-402384,-400448,-398530,-396630,-394747,-392882, -391034,-389202,-387387,-385589,-383807,-382040,-380290,-378555, -376835,-375130,-373440,-371765,-370105,-368459,-366826,-365208, -363604,-362013,-360436,-358872,-357321,-355783,-354257,-352744, -351244,-349756,-348280,-346816,-345364,-343924,-342495,-341078, -339671,-338276,-336892,-335519,-334157,-332805,-331464,-330133, -328812,-327502,-326201,-324910,-323629,-322358,-321097,-319844, -318601,-317368,-316143,-314928,-313721,-312524,-311335,-310154, -308983,-307819,-306664,-305517,-304379,-303248,-302126,-301011, -299904,-298805,-297714,-296630,-295554,-294485,-293423,-292369, -291322,-290282,-289249,-288223,-287204,-286192,-285186,-284188, -283195,-282210,-281231,-280258,-279292,-278332,-277378,-276430, -275489,-274553,-273624,-272700,-271782,-270871,-269965,-269064, -268169,-267280,-266397,-265519,-264646,-263779,-262917,-262060, -261209,-260363,-259522,-258686,-257855,-257029,-256208,-255392, -254581,-253774,-252973,-252176,-251384,-250596,-249813,-249035, -248261,-247492,-246727,-245966,-245210,-244458,-243711,-242967, -242228,-241493,-240763,-240036,-239314,-238595,-237881,-237170, -236463,-235761,-235062,-234367,-233676,-232988,-232304,-231624, -230948,-230275,-229606,-228941,-228279,-227621,-226966,-226314, -225666,-225022,-224381,-223743,-223108,-222477,-221849,-221225, -220603,-219985,-219370,-218758,-218149,-217544,-216941,-216341, -215745,-215151,-214561,-213973,-213389,-212807,-212228,-211652, -211079,-210509,-209941,-209376,-208815,-208255,-207699,-207145, -206594,-206045,-205500,-204956,-204416,-203878,-203342,-202809, -202279,-201751,-201226,-200703,-200182,-199664,-199149,-198636, -198125,-197616,-197110,-196606,-196105,-195606,-195109,-194614, -194122,-193631,-193143,-192658,-192174,-191693,-191213,-190736, -190261,-189789,-189318,-188849,-188382,-187918,-187455,-186995, -186536,-186080,-185625,-185173,-184722,-184274,-183827,-183382, -182939,-182498,-182059,-181622,-181186,-180753,-180321,-179891, -179463,-179037,-178612,-178190,-177769,-177349,-176932,-176516, -176102,-175690,-175279,-174870,-174463,-174057,-173653,-173251, -172850,-172451,-172053,-171657,-171263,-170870,-170479,-170089, -169701,-169315,-168930,-168546,-168164,-167784,-167405,-167027, -166651,-166277,-165904,-165532,-165162,-164793,-164426,-164060, -163695,-163332,-162970,-162610,-162251,-161893,-161537,-161182, -160828,-160476,-160125,-159775,-159427,-159079,-158734,-158389, -158046,-157704,-157363,-157024,-156686,-156349,-156013,-155678, -155345,-155013,-154682,-154352,-154024,-153697,-153370,-153045, -152722,-152399,-152077,-151757,-151438,-151120,-150803,-150487, -150172,-149859,-149546,-149235,-148924,-148615,-148307,-148000, -147693,-147388,-147084,-146782,-146480,-146179,-145879,-145580, -145282,-144986,-144690,-144395,-144101,-143808,-143517,-143226, -142936,-142647,-142359,-142072,-141786,-141501,-141217,-140934, -140651,-140370,-140090,-139810,-139532,-139254,-138977,-138701, -138426,-138152,-137879,-137607,-137335,-137065,-136795,-136526, -136258,-135991,-135725,-135459,-135195,-134931,-134668,-134406, -134145,-133884,-133625,-133366,-133108,-132851,-132594,-132339, -132084,-131830,-131576,-131324,-131072,-130821,-130571,-130322, -130073,-129825,-129578,-129332,-129086,-128841,-128597,-128353, -128111,-127869,-127627,-127387,-127147,-126908,-126669,-126432, -126195,-125959,-125723,-125488,-125254,-125020,-124787,-124555, -124324,-124093,-123863,-123633,-123404,-123176,-122949,-122722, -122496,-122270,-122045,-121821,-121597,-121374,-121152,-120930, -120709,-120489,-120269,-120050,-119831,-119613,-119396,-119179, -118963,-118747,-118532,-118318,-118104,-117891,-117678,-117466, -117254,-117044,-116833,-116623,-116414,-116206,-115998,-115790, -115583,-115377,-115171,-114966,-114761,-114557,-114354,-114151, -113948,-113746,-113545,-113344,-113143,-112944,-112744,-112546, -112347,-112150,-111952,-111756,-111560,-111364,-111169,-110974, -110780,-110586,-110393,-110200,-110008,-109817,-109626,-109435, -109245,-109055,-108866,-108677,-108489,-108301,-108114,-107927, -107741,-107555,-107369,-107184,-107000,-106816,-106632,-106449, -106266,-106084,-105902,-105721,-105540,-105360,-105180,-105000, -104821,-104643,-104465,-104287,-104109,-103933,-103756,-103580, -103404,-103229,-103054,-102880,-102706,-102533,-102360,-102187, -102015,-101843,-101671,-101500,-101330,-101159,-100990,-100820, -100651,-100482,-100314,-100146,-99979,-99812,-99645,-99479, -99313,-99148,-98982,-98818,-98653,-98489,-98326,-98163, -98000,-97837,-97675,-97513,-97352,-97191,-97030,-96870, -96710,-96551,-96391,-96233,-96074,-95916,-95758,-95601, -95444,-95287,-95131,-94975,-94819,-94664,-94509,-94354, -94200,-94046,-93892,-93739,-93586,-93434,-93281,-93129, -92978,-92826,-92675,-92525,-92375,-92225,-92075,-91926, -91777,-91628,-91480,-91332,-91184,-91036,-90889,-90742, -90596,-90450,-90304,-90158,-90013,-89868,-89724,-89579, -89435,-89292,-89148,-89005,-88862,-88720,-88577,-88435, -88294,-88152,-88011,-87871,-87730,-87590,-87450,-87310, -87171,-87032,-86893,-86755,-86616,-86479,-86341,-86204, -86066,-85930,-85793,-85657,-85521,-85385,-85250,-85114, -84980,-84845,-84710,-84576,-84443,-84309,-84176,-84043, -83910,-83777,-83645,-83513,-83381,-83250,-83118,-82987, -82857,-82726,-82596,-82466,-82336,-82207,-82078,-81949, -81820,-81691,-81563,-81435,-81307,-81180,-81053,-80925, -80799,-80672,-80546,-80420,-80294,-80168,-80043,-79918, -79793,-79668,-79544,-79420,-79296,-79172,-79048,-78925, -78802,-78679,-78557,-78434,-78312,-78190,-78068,-77947, -77826,-77705,-77584,-77463,-77343,-77223,-77103,-76983, -76864,-76744,-76625,-76506,-76388,-76269,-76151,-76033, -75915,-75797,-75680,-75563,-75446,-75329,-75213,-75096, -74980,-74864,-74748,-74633,-74517,-74402,-74287,-74172, -74058,-73944,-73829,-73715,-73602,-73488,-73375,-73262, -73149,-73036,-72923,-72811,-72699,-72587,-72475,-72363, -72252,-72140,-72029,-71918,-71808,-71697,-71587,-71477, -71367,-71257,-71147,-71038,-70929,-70820,-70711,-70602, -70494,-70385,-70277,-70169,-70061,-69954,-69846,-69739, -69632,-69525,-69418,-69312,-69205,-69099,-68993,-68887, -68781,-68676,-68570,-68465,-68360,-68255,-68151,-68046, -67942,-67837,-67733,-67629,-67526,-67422,-67319,-67216, -67113,-67010,-66907,-66804,-66702,-66600,-66498,-66396, -66294,-66192,-66091,-65989,-65888,-65787,-65686,-65586, -65485,-65385,-65285,-65185,-65085,-64985,-64885,-64786, -64687,-64587,-64488,-64389,-64291,-64192,-64094,-63996, -63897,-63799,-63702,-63604,-63506,-63409,-63312,-63215, -63118,-63021,-62924,-62828,-62731,-62635,-62539,-62443, -62347,-62251,-62156,-62060,-61965,-61870,-61775,-61680, -61585,-61491,-61396,-61302,-61208,-61114,-61020,-60926, -60833,-60739,-60646,-60552,-60459,-60366,-60273,-60181, -60088,-59996,-59903,-59811,-59719,-59627,-59535,-59444, -59352,-59261,-59169,-59078,-58987,-58896,-58805,-58715, -58624,-58534,-58443,-58353,-58263,-58173,-58083,-57994, -57904,-57815,-57725,-57636,-57547,-57458,-57369,-57281, -57192,-57104,-57015,-56927,-56839,-56751,-56663,-56575, -56487,-56400,-56312,-56225,-56138,-56051,-55964,-55877, -55790,-55704,-55617,-55531,-55444,-55358,-55272,-55186, -55100,-55015,-54929,-54843,-54758,-54673,-54587,-54502, -54417,-54333,-54248,-54163,-54079,-53994,-53910,-53826, -53741,-53657,-53574,-53490,-53406,-53322,-53239,-53156, -53072,-52989,-52906,-52823,-52740,-52657,-52575,-52492, -52410,-52327,-52245,-52163,-52081,-51999,-51917,-51835, -51754,-51672,-51591,-51509,-51428,-51347,-51266,-51185, -51104,-51023,-50942,-50862,-50781,-50701,-50621,-50540, -50460,-50380,-50300,-50221,-50141,-50061,-49982,-49902, -49823,-49744,-49664,-49585,-49506,-49427,-49349,-49270, -49191,-49113,-49034,-48956,-48878,-48799,-48721,-48643, -48565,-48488,-48410,-48332,-48255,-48177,-48100,-48022, -47945,-47868,-47791,-47714,-47637,-47560,-47484,-47407, -47331,-47254,-47178,-47102,-47025,-46949,-46873,-46797, -46721,-46646,-46570,-46494,-46419,-46343,-46268,-46193, -46118,-46042,-45967,-45892,-45818,-45743,-45668,-45593, -45519,-45444,-45370,-45296,-45221,-45147,-45073,-44999, -44925,-44851,-44778,-44704,-44630,-44557,-44483,-44410, -44337,-44263,-44190,-44117,-44044,-43971,-43898,-43826, -43753,-43680,-43608,-43535,-43463,-43390,-43318,-43246, -43174,-43102,-43030,-42958,-42886,-42814,-42743,-42671, -42600,-42528,-42457,-42385,-42314,-42243,-42172,-42101, -42030,-41959,-41888,-41817,-41747,-41676,-41605,-41535, -41465,-41394,-41324,-41254,-41184,-41113,-41043,-40973, -40904,-40834,-40764,-40694,-40625,-40555,-40486,-40416, -40347,-40278,-40208,-40139,-40070,-40001,-39932,-39863, -39794,-39726,-39657,-39588,-39520,-39451,-39383,-39314, -39246,-39178,-39110,-39042,-38973,-38905,-38837,-38770, -38702,-38634,-38566,-38499,-38431,-38364,-38296,-38229, -38161,-38094,-38027,-37960,-37893,-37826,-37759,-37692, -37625,-37558,-37491,-37425,-37358,-37291,-37225,-37158, -37092,-37026,-36959,-36893,-36827,-36761,-36695,-36629, -36563,-36497,-36431,-36365,-36300,-36234,-36168,-36103, -36037,-35972,-35907,-35841,-35776,-35711,-35646,-35580, -35515,-35450,-35385,-35321,-35256,-35191,-35126,-35062, -34997,-34932,-34868,-34803,-34739,-34675,-34610,-34546, -34482,-34418,-34354,-34289,-34225,-34162,-34098,-34034, -33970,-33906,-33843,-33779,-33715,-33652,-33588,-33525, -33461,-33398,-33335,-33272,-33208,-33145,-33082,-33019, -32956,-32893,-32830,-32767,-32705,-32642,-32579,-32516, -32454,-32391,-32329,-32266,-32204,-32141,-32079,-32017, -31955,-31892,-31830,-31768,-31706,-31644,-31582,-31520, -31458,-31396,-31335,-31273,-31211,-31150,-31088,-31026, -30965,-30904,-30842,-30781,-30719,-30658,-30597,-30536, -30474,-30413,-30352,-30291,-30230,-30169,-30108,-30048, -29987,-29926,-29865,-29805,-29744,-29683,-29623,-29562, -29502,-29441,-29381,-29321,-29260,-29200,-29140,-29080, -29020,-28959,-28899,-28839,-28779,-28719,-28660,-28600, -28540,-28480,-28420,-28361,-28301,-28241,-28182,-28122, -28063,-28003,-27944,-27884,-27825,-27766,-27707,-27647, -27588,-27529,-27470,-27411,-27352,-27293,-27234,-27175, -27116,-27057,-26998,-26940,-26881,-26822,-26763,-26705, -26646,-26588,-26529,-26471,-26412,-26354,-26295,-26237, -26179,-26120,-26062,-26004,-25946,-25888,-25830,-25772, -25714,-25656,-25598,-25540,-25482,-25424,-25366,-25308, -25251,-25193,-25135,-25078,-25020,-24962,-24905,-24847, -24790,-24732,-24675,-24618,-24560,-24503,-24446,-24389, -24331,-24274,-24217,-24160,-24103,-24046,-23989,-23932, -23875,-23818,-23761,-23704,-23647,-23591,-23534,-23477, -23420,-23364,-23307,-23250,-23194,-23137,-23081,-23024, -22968,-22911,-22855,-22799,-22742,-22686,-22630,-22573, -22517,-22461,-22405,-22349,-22293,-22237,-22181,-22125, -22069,-22013,-21957,-21901,-21845,-21789,-21733,-21678, -21622,-21566,-21510,-21455,-21399,-21343,-21288,-21232, -21177,-21121,-21066,-21010,-20955,-20900,-20844,-20789, -20734,-20678,-20623,-20568,-20513,-20457,-20402,-20347, -20292,-20237,-20182,-20127,-20072,-20017,-19962,-19907, -19852,-19797,-19742,-19688,-19633,-19578,-19523,-19469, -19414,-19359,-19305,-19250,-19195,-19141,-19086,-19032, -18977,-18923,-18868,-18814,-18760,-18705,-18651,-18597, -18542,-18488,-18434,-18380,-18325,-18271,-18217,-18163, -18109,-18055,-18001,-17946,-17892,-17838,-17784,-17731, -17677,-17623,-17569,-17515,-17461,-17407,-17353,-17300, -17246,-17192,-17138,-17085,-17031,-16977,-16924,-16870, -16817,-16763,-16710,-16656,-16603,-16549,-16496,-16442, -16389,-16335,-16282,-16229,-16175,-16122,-16069,-16015, -15962,-15909,-15856,-15802,-15749,-15696,-15643,-15590, -15537,-15484,-15431,-15378,-15325,-15272,-15219,-15166, -15113,-15060,-15007,-14954,-14901,-14848,-14795,-14743, -14690,-14637,-14584,-14531,-14479,-14426,-14373,-14321, -14268,-14215,-14163,-14110,-14057,-14005,-13952,-13900, -13847,-13795,-13742,-13690,-13637,-13585,-13533,-13480, -13428,-13375,-13323,-13271,-13218,-13166,-13114,-13062, -13009,-12957,-12905,-12853,-12800,-12748,-12696,-12644, -12592,-12540,-12488,-12436,-12383,-12331,-12279,-12227, -12175,-12123,-12071,-12019,-11967,-11916,-11864,-11812, -11760,-11708,-11656,-11604,-11552,-11501,-11449,-11397, -11345,-11293,-11242,-11190,-11138,-11086,-11035,-10983, -10931,-10880,-10828,-10777,-10725,-10673,-10622,-10570, -10519,-10467,-10415,-10364,-10312,-10261,-10209,-10158, -10106,-10055,-10004,-9952,-9901,-9849,-9798,-9747, -9695,-9644,-9592,-9541,-9490,-9438,-9387,-9336, -9285,-9233,-9182,-9131,-9080,-9028,-8977,-8926, -8875,-8824,-8772,-8721,-8670,-8619,-8568,-8517, -8466,-8414,-8363,-8312,-8261,-8210,-8159,-8108, -8057,-8006,-7955,-7904,-7853,-7802,-7751,-7700, -7649,-7598,-7547,-7496,-7445,-7395,-7344,-7293, -7242,-7191,-7140,-7089,-7038,-6988,-6937,-6886, -6835,-6784,-6733,-6683,-6632,-6581,-6530,-6480, -6429,-6378,-6327,-6277,-6226,-6175,-6124,-6074, -6023,-5972,-5922,-5871,-5820,-5770,-5719,-5668, -5618,-5567,-5517,-5466,-5415,-5365,-5314,-5264, -5213,-5162,-5112,-5061,-5011,-4960,-4910,-4859, -4808,-4758,-4707,-4657,-4606,-4556,-4505,-4455, -4404,-4354,-4303,-4253,-4202,-4152,-4101,-4051, -4001,-3950,-3900,-3849,-3799,-3748,-3698,-3648, -3597,-3547,-3496,-3446,-3395,-3345,-3295,-3244, -3194,-3144,-3093,-3043,-2992,-2942,-2892,-2841, -2791,-2741,-2690,-2640,-2590,-2539,-2489,-2439, -2388,-2338,-2288,-2237,-2187,-2137,-2086,-2036, -1986,-1935,-1885,-1835,-1784,-1734,-1684,-1633, -1583,-1533,-1483,-1432,-1382,-1332,-1281,-1231, -1181,-1131,-1080,-1030,-980,-929,-879,-829, -779,-728,-678,-628,-578,-527,-477,-427, -376,-326,-276,-226,-175,-125,-75,-25, 25,75,125,175,226,276,326,376, 427,477,527,578,628,678,728,779, 829,879,929,980,1030,1080,1131,1181, 1231,1281,1332,1382,1432,1483,1533,1583, 1633,1684,1734,1784,1835,1885,1935,1986, 2036,2086,2137,2187,2237,2288,2338,2388, 2439,2489,2539,2590,2640,2690,2741,2791, 2841,2892,2942,2992,3043,3093,3144,3194, 3244,3295,3345,3395,3446,3496,3547,3597, 3648,3698,3748,3799,3849,3900,3950,4001, 4051,4101,4152,4202,4253,4303,4354,4404, 4455,4505,4556,4606,4657,4707,4758,4808, 4859,4910,4960,5011,5061,5112,5162,5213, 5264,5314,5365,5415,5466,5517,5567,5618, 5668,5719,5770,5820,5871,5922,5972,6023, 6074,6124,6175,6226,6277,6327,6378,6429, 6480,6530,6581,6632,6683,6733,6784,6835, 6886,6937,6988,7038,7089,7140,7191,7242, 7293,7344,7395,7445,7496,7547,7598,7649, 7700,7751,7802,7853,7904,7955,8006,8057, 8108,8159,8210,8261,8312,8363,8414,8466, 8517,8568,8619,8670,8721,8772,8824,8875, 8926,8977,9028,9080,9131,9182,9233,9285, 9336,9387,9438,9490,9541,9592,9644,9695, 9747,9798,9849,9901,9952,10004,10055,10106, 10158,10209,10261,10312,10364,10415,10467,10519, 10570,10622,10673,10725,10777,10828,10880,10931, 10983,11035,11086,11138,11190,11242,11293,11345, 11397,11449,11501,11552,11604,11656,11708,11760, 11812,11864,11916,11967,12019,12071,12123,12175, 12227,12279,12331,12383,12436,12488,12540,12592, 12644,12696,12748,12800,12853,12905,12957,13009, 13062,13114,13166,13218,13271,13323,13375,13428, 13480,13533,13585,13637,13690,13742,13795,13847, 13900,13952,14005,14057,14110,14163,14215,14268, 14321,14373,14426,14479,14531,14584,14637,14690, 14743,14795,14848,14901,14954,15007,15060,15113, 15166,15219,15272,15325,15378,15431,15484,15537, 15590,15643,15696,15749,15802,15856,15909,15962, 16015,16069,16122,16175,16229,16282,16335,16389, 16442,16496,16549,16603,16656,16710,16763,16817, 16870,16924,16977,17031,17085,17138,17192,17246, 17300,17353,17407,17461,17515,17569,17623,17677, 17731,17784,17838,17892,17946,18001,18055,18109, 18163,18217,18271,18325,18380,18434,18488,18542, 18597,18651,18705,18760,18814,18868,18923,18977, 19032,19086,19141,19195,19250,19305,19359,19414, 19469,19523,19578,19633,19688,19742,19797,19852, 19907,19962,20017,20072,20127,20182,20237,20292, 20347,20402,20457,20513,20568,20623,20678,20734, 20789,20844,20900,20955,21010,21066,21121,21177, 21232,21288,21343,21399,21455,21510,21566,21622, 21678,21733,21789,21845,21901,21957,22013,22069, 22125,22181,22237,22293,22349,22405,22461,22517, 22573,22630,22686,22742,22799,22855,22911,22968, 23024,23081,23137,23194,23250,23307,23364,23420, 23477,23534,23591,23647,23704,23761,23818,23875, 23932,23989,24046,24103,24160,24217,24274,24331, 24389,24446,24503,24560,24618,24675,24732,24790, 24847,24905,24962,25020,25078,25135,25193,25251, 25308,25366,25424,25482,25540,25598,25656,25714, 25772,25830,25888,25946,26004,26062,26120,26179, 26237,26295,26354,26412,26471,26529,26588,26646, 26705,26763,26822,26881,26940,26998,27057,27116, 27175,27234,27293,27352,27411,27470,27529,27588, 27647,27707,27766,27825,27884,27944,28003,28063, 28122,28182,28241,28301,28361,28420,28480,28540, 28600,28660,28719,28779,28839,28899,28959,29020, 29080,29140,29200,29260,29321,29381,29441,29502, 29562,29623,29683,29744,29805,29865,29926,29987, 30048,30108,30169,30230,30291,30352,30413,30474, 30536,30597,30658,30719,30781,30842,30904,30965, 31026,31088,31150,31211,31273,31335,31396,31458, 31520,31582,31644,31706,31768,31830,31892,31955, 32017,32079,32141,32204,32266,32329,32391,32454, 32516,32579,32642,32705,32767,32830,32893,32956, 33019,33082,33145,33208,33272,33335,33398,33461, 33525,33588,33652,33715,33779,33843,33906,33970, 34034,34098,34162,34225,34289,34354,34418,34482, 34546,34610,34675,34739,34803,34868,34932,34997, 35062,35126,35191,35256,35321,35385,35450,35515, 35580,35646,35711,35776,35841,35907,35972,36037, 36103,36168,36234,36300,36365,36431,36497,36563, 36629,36695,36761,36827,36893,36959,37026,37092, 37158,37225,37291,37358,37425,37491,37558,37625, 37692,37759,37826,37893,37960,38027,38094,38161, 38229,38296,38364,38431,38499,38566,38634,38702, 38770,38837,38905,38973,39042,39110,39178,39246, 39314,39383,39451,39520,39588,39657,39726,39794, 39863,39932,40001,40070,40139,40208,40278,40347, 40416,40486,40555,40625,40694,40764,40834,40904, 40973,41043,41113,41184,41254,41324,41394,41465, 41535,41605,41676,41747,41817,41888,41959,42030, 42101,42172,42243,42314,42385,42457,42528,42600, 42671,42743,42814,42886,42958,43030,43102,43174, 43246,43318,43390,43463,43535,43608,43680,43753, 43826,43898,43971,44044,44117,44190,44263,44337, 44410,44483,44557,44630,44704,44778,44851,44925, 44999,45073,45147,45221,45296,45370,45444,45519, 45593,45668,45743,45818,45892,45967,46042,46118, 46193,46268,46343,46419,46494,46570,46646,46721, 46797,46873,46949,47025,47102,47178,47254,47331, 47407,47484,47560,47637,47714,47791,47868,47945, 48022,48100,48177,48255,48332,48410,48488,48565, 48643,48721,48799,48878,48956,49034,49113,49191, 49270,49349,49427,49506,49585,49664,49744,49823, 49902,49982,50061,50141,50221,50300,50380,50460, 50540,50621,50701,50781,50862,50942,51023,51104, 51185,51266,51347,51428,51509,51591,51672,51754, 51835,51917,51999,52081,52163,52245,52327,52410, 52492,52575,52657,52740,52823,52906,52989,53072, 53156,53239,53322,53406,53490,53574,53657,53741, 53826,53910,53994,54079,54163,54248,54333,54417, 54502,54587,54673,54758,54843,54929,55015,55100, 55186,55272,55358,55444,55531,55617,55704,55790, 55877,55964,56051,56138,56225,56312,56400,56487, 56575,56663,56751,56839,56927,57015,57104,57192, 57281,57369,57458,57547,57636,57725,57815,57904, 57994,58083,58173,58263,58353,58443,58534,58624, 58715,58805,58896,58987,59078,59169,59261,59352, 59444,59535,59627,59719,59811,59903,59996,60088, 60181,60273,60366,60459,60552,60646,60739,60833, 60926,61020,61114,61208,61302,61396,61491,61585, 61680,61775,61870,61965,62060,62156,62251,62347, 62443,62539,62635,62731,62828,62924,63021,63118, 63215,63312,63409,63506,63604,63702,63799,63897, 63996,64094,64192,64291,64389,64488,64587,64687, 64786,64885,64985,65085,65185,65285,65385,65485, 65586,65686,65787,65888,65989,66091,66192,66294, 66396,66498,66600,66702,66804,66907,67010,67113, 67216,67319,67422,67526,67629,67733,67837,67942, 68046,68151,68255,68360,68465,68570,68676,68781, 68887,68993,69099,69205,69312,69418,69525,69632, 69739,69846,69954,70061,70169,70277,70385,70494, 70602,70711,70820,70929,71038,71147,71257,71367, 71477,71587,71697,71808,71918,72029,72140,72252, 72363,72475,72587,72699,72811,72923,73036,73149, 73262,73375,73488,73602,73715,73829,73944,74058, 74172,74287,74402,74517,74633,74748,74864,74980, 75096,75213,75329,75446,75563,75680,75797,75915, 76033,76151,76269,76388,76506,76625,76744,76864, 76983,77103,77223,77343,77463,77584,77705,77826, 77947,78068,78190,78312,78434,78557,78679,78802, 78925,79048,79172,79296,79420,79544,79668,79793, 79918,80043,80168,80294,80420,80546,80672,80799, 80925,81053,81180,81307,81435,81563,81691,81820, 81949,82078,82207,82336,82466,82596,82726,82857, 82987,83118,83250,83381,83513,83645,83777,83910, 84043,84176,84309,84443,84576,84710,84845,84980, 85114,85250,85385,85521,85657,85793,85930,86066, 86204,86341,86479,86616,86755,86893,87032,87171, 87310,87450,87590,87730,87871,88011,88152,88294, 88435,88577,88720,88862,89005,89148,89292,89435, 89579,89724,89868,90013,90158,90304,90450,90596, 90742,90889,91036,91184,91332,91480,91628,91777, 91926,92075,92225,92375,92525,92675,92826,92978, 93129,93281,93434,93586,93739,93892,94046,94200, 94354,94509,94664,94819,94975,95131,95287,95444, 95601,95758,95916,96074,96233,96391,96551,96710, 96870,97030,97191,97352,97513,97675,97837,98000, 98163,98326,98489,98653,98818,98982,99148,99313, 99479,99645,99812,99979,100146,100314,100482,100651, 100820,100990,101159,101330,101500,101671,101843,102015, 102187,102360,102533,102706,102880,103054,103229,103404, 103580,103756,103933,104109,104287,104465,104643,104821, 105000,105180,105360,105540,105721,105902,106084,106266, 106449,106632,106816,107000,107184,107369,107555,107741, 107927,108114,108301,108489,108677,108866,109055,109245, 109435,109626,109817,110008,110200,110393,110586,110780, 110974,111169,111364,111560,111756,111952,112150,112347, 112546,112744,112944,113143,113344,113545,113746,113948, 114151,114354,114557,114761,114966,115171,115377,115583, 115790,115998,116206,116414,116623,116833,117044,117254, 117466,117678,117891,118104,118318,118532,118747,118963, 119179,119396,119613,119831,120050,120269,120489,120709, 120930,121152,121374,121597,121821,122045,122270,122496, 122722,122949,123176,123404,123633,123863,124093,124324, 124555,124787,125020,125254,125488,125723,125959,126195, 126432,126669,126908,127147,127387,127627,127869,128111, 128353,128597,128841,129086,129332,129578,129825,130073, 130322,130571,130821,131072,131324,131576,131830,132084, 132339,132594,132851,133108,133366,133625,133884,134145, 134406,134668,134931,135195,135459,135725,135991,136258, 136526,136795,137065,137335,137607,137879,138152,138426, 138701,138977,139254,139532,139810,140090,140370,140651, 140934,141217,141501,141786,142072,142359,142647,142936, 143226,143517,143808,144101,144395,144690,144986,145282, 145580,145879,146179,146480,146782,147084,147388,147693, 148000,148307,148615,148924,149235,149546,149859,150172, 150487,150803,151120,151438,151757,152077,152399,152722, 153045,153370,153697,154024,154352,154682,155013,155345, 155678,156013,156349,156686,157024,157363,157704,158046, 158389,158734,159079,159427,159775,160125,160476,160828, 161182,161537,161893,162251,162610,162970,163332,163695, 164060,164426,164793,165162,165532,165904,166277,166651, 167027,167405,167784,168164,168546,168930,169315,169701, 170089,170479,170870,171263,171657,172053,172451,172850, 173251,173653,174057,174463,174870,175279,175690,176102, 176516,176932,177349,177769,178190,178612,179037,179463, 179891,180321,180753,181186,181622,182059,182498,182939, 183382,183827,184274,184722,185173,185625,186080,186536, 186995,187455,187918,188382,188849,189318,189789,190261, 190736,191213,191693,192174,192658,193143,193631,194122, 194614,195109,195606,196105,196606,197110,197616,198125, 198636,199149,199664,200182,200703,201226,201751,202279, 202809,203342,203878,204416,204956,205500,206045,206594, 207145,207699,208255,208815,209376,209941,210509,211079, 211652,212228,212807,213389,213973,214561,215151,215745, 216341,216941,217544,218149,218758,219370,219985,220603, 221225,221849,222477,223108,223743,224381,225022,225666, 226314,226966,227621,228279,228941,229606,230275,230948, 231624,232304,232988,233676,234367,235062,235761,236463, 237170,237881,238595,239314,240036,240763,241493,242228, 242967,243711,244458,245210,245966,246727,247492,248261, 249035,249813,250596,251384,252176,252973,253774,254581, 255392,256208,257029,257855,258686,259522,260363,261209, 262060,262917,263779,264646,265519,266397,267280,268169, 269064,269965,270871,271782,272700,273624,274553,275489, 276430,277378,278332,279292,280258,281231,282210,283195, 284188,285186,286192,287204,288223,289249,290282,291322, 292369,293423,294485,295554,296630,297714,298805,299904, 301011,302126,303248,304379,305517,306664,307819,308983, 310154,311335,312524,313721,314928,316143,317368,318601, 319844,321097,322358,323629,324910,326201,327502,328812, 330133,331464,332805,334157,335519,336892,338276,339671, 341078,342495,343924,345364,346816,348280,349756,351244, 352744,354257,355783,357321,358872,360436,362013,363604, 365208,366826,368459,370105,371765,373440,375130,376835, 378555,380290,382040,383807,385589,387387,389202,391034, 392882,394747,396630,398530,400448,402384,404338,406311, 408303,410314,412344,414395,416465,418555,420666,422798, 424951,427125,429321,431540,433781,436045,438332,440643, 442978,445337,447720,450129,452564,455024,457511,460024, 462565,465133,467730,470355,473009,475692,478406,481150, 483925,486732,489571,492443,495348,498287,501261,504269, 507313,510394,513512,516667,519861,523094,526366,529680, 533034,536431,539870,543354,546881,550455,554074,557741, 561456,565221,569035,572901,576818,580789,584815,588896, 593033,597229,601483,605798,610174,614613,619117,623686, 628323,633028,637803,642651,647572,652568,657640,662792, 668024,673338,678737,684223,689797,695462,701219,707072, 713023,719074,725227,731486,737853,744331,750922,757631, 764460,771411,778490,785699,793041,800521,808143,815910, 823827,831898,840127,848520,857081,865817,874730,883829, 893117,902602,912289,922186,932298,942633,953199,964003, 975054,986361,997931,1009774,1021901,1034322,1047046,1060087, 1073455,1087164,1101225,1115654,1130465,1145673,1161294,1177345, 1193846,1210813,1228269,1246234,1264730,1283783,1303416,1323658, 1344537,1366084,1388330,1411312,1435065,1459630,1485049,1511367, 1538632,1566898,1596220,1626658,1658278,1691149,1725348,1760956, 1798063,1836758,1877161,1919378,1963536,2009771,2058233,2109087, 2162516,2218719,2277919,2340362,2406322,2476104,2550052,2628549, 2712030,2800983,2895966,2997613,3106651,3223918,3350381,3487165, 3635590,3797206,3973855,4167737,4381502,4618375,4882318,5178251, 5512368,5892567,6329090,6835455,7429880,8137527,8994149,10052327, 11392683,13145455,15535599,18988036,24413316,34178904,56965752,170910304 }; const int finesine[10240] = { 25,75,125,175,226,276,326,376, 427,477,527,578,628,678,728,779, 829,879,929,980,1030,1080,1130,1181, 1231,1281,1331,1382,1432,1482,1532,1583, 1633,1683,1733,1784,1834,1884,1934,1985, 2035,2085,2135,2186,2236,2286,2336,2387, 2437,2487,2537,2587,2638,2688,2738,2788, 2839,2889,2939,2989,3039,3090,3140,3190, 3240,3291,3341,3391,3441,3491,3541,3592, 3642,3692,3742,3792,3843,3893,3943,3993, 4043,4093,4144,4194,4244,4294,4344,4394, 4445,4495,4545,4595,4645,4695,4745,4796, 4846,4896,4946,4996,5046,5096,5146,5197, 5247,5297,5347,5397,5447,5497,5547,5597, 5647,5697,5748,5798,5848,5898,5948,5998, 6048,6098,6148,6198,6248,6298,6348,6398, 6448,6498,6548,6598,6648,6698,6748,6798, 6848,6898,6948,6998,7048,7098,7148,7198, 7248,7298,7348,7398,7448,7498,7548,7598, 7648,7697,7747,7797,7847,7897,7947,7997, 8047,8097,8147,8196,8246,8296,8346,8396, 8446,8496,8545,8595,8645,8695,8745,8794, 8844,8894,8944,8994,9043,9093,9143,9193, 9243,9292,9342,9392,9442,9491,9541,9591, 9640,9690,9740,9790,9839,9889,9939,9988, 10038,10088,10137,10187,10237,10286,10336,10386, 10435,10485,10534,10584,10634,10683,10733,10782, 10832,10882,10931,10981,11030,11080,11129,11179, 11228,11278,11327,11377,11426,11476,11525,11575, 11624,11674,11723,11773,11822,11872,11921,11970, 12020,12069,12119,12168,12218,12267,12316,12366, 12415,12464,12514,12563,12612,12662,12711,12760, 12810,12859,12908,12957,13007,13056,13105,13154, 13204,13253,13302,13351,13401,13450,13499,13548, 13597,13647,13696,13745,13794,13843,13892,13941, 13990,14040,14089,14138,14187,14236,14285,14334, 14383,14432,14481,14530,14579,14628,14677,14726, 14775,14824,14873,14922,14971,15020,15069,15118, 15167,15215,15264,15313,15362,15411,15460,15509, 15557,15606,15655,15704,15753,15802,15850,15899, 15948,15997,16045,16094,16143,16191,16240,16289, 16338,16386,16435,16484,16532,16581,16629,16678, 16727,16775,16824,16872,16921,16970,17018,17067, 17115,17164,17212,17261,17309,17358,17406,17455, 17503,17551,17600,17648,17697,17745,17793,17842, 17890,17939,17987,18035,18084,18132,18180,18228, 18277,18325,18373,18421,18470,18518,18566,18614, 18663,18711,18759,18807,18855,18903,18951,19000, 19048,19096,19144,19192,19240,19288,19336,19384, 19432,19480,19528,19576,19624,19672,19720,19768, 19816,19864,19912,19959,20007,20055,20103,20151, 20199,20246,20294,20342,20390,20438,20485,20533, 20581,20629,20676,20724,20772,20819,20867,20915, 20962,21010,21057,21105,21153,21200,21248,21295, 21343,21390,21438,21485,21533,21580,21628,21675, 21723,21770,21817,21865,21912,21960,22007,22054, 22102,22149,22196,22243,22291,22338,22385,22433, 22480,22527,22574,22621,22668,22716,22763,22810, 22857,22904,22951,22998,23045,23092,23139,23186, 23233,23280,23327,23374,23421,23468,23515,23562, 23609,23656,23703,23750,23796,23843,23890,23937, 23984,24030,24077,24124,24171,24217,24264,24311, 24357,24404,24451,24497,24544,24591,24637,24684, 24730,24777,24823,24870,24916,24963,25009,25056, 25102,25149,25195,25241,25288,25334,25381,25427, 25473,25520,25566,25612,25658,25705,25751,25797, 25843,25889,25936,25982,26028,26074,26120,26166, 26212,26258,26304,26350,26396,26442,26488,26534, 26580,26626,26672,26718,26764,26810,26856,26902, 26947,26993,27039,27085,27131,27176,27222,27268, 27313,27359,27405,27450,27496,27542,27587,27633, 27678,27724,27770,27815,27861,27906,27952,27997, 28042,28088,28133,28179,28224,28269,28315,28360, 28405,28451,28496,28541,28586,28632,28677,28722, 28767,28812,28858,28903,28948,28993,29038,29083, 29128,29173,29218,29263,29308,29353,29398,29443, 29488,29533,29577,29622,29667,29712,29757,29801, 29846,29891,29936,29980,30025,30070,30114,30159, 30204,30248,30293,30337,30382,30426,30471,30515, 30560,30604,30649,30693,30738,30782,30826,30871, 30915,30959,31004,31048,31092,31136,31181,31225, 31269,31313,31357,31402,31446,31490,31534,31578, 31622,31666,31710,31754,31798,31842,31886,31930, 31974,32017,32061,32105,32149,32193,32236,32280, 32324,32368,32411,32455,32499,32542,32586,32630, 32673,32717,32760,32804,32847,32891,32934,32978, 33021,33065,33108,33151,33195,33238,33281,33325, 33368,33411,33454,33498,33541,33584,33627,33670, 33713,33756,33799,33843,33886,33929,33972,34015, 34057,34100,34143,34186,34229,34272,34315,34358, 34400,34443,34486,34529,34571,34614,34657,34699, 34742,34785,34827,34870,34912,34955,34997,35040, 35082,35125,35167,35210,35252,35294,35337,35379, 35421,35464,35506,35548,35590,35633,35675,35717, 35759,35801,35843,35885,35927,35969,36011,36053, 36095,36137,36179,36221,36263,36305,36347,36388, 36430,36472,36514,36555,36597,36639,36681,36722, 36764,36805,36847,36889,36930,36972,37013,37055, 37096,37137,37179,37220,37262,37303,37344,37386, 37427,37468,37509,37551,37592,37633,37674,37715, 37756,37797,37838,37879,37920,37961,38002,38043, 38084,38125,38166,38207,38248,38288,38329,38370, 38411,38451,38492,38533,38573,38614,38655,38695, 38736,38776,38817,38857,38898,38938,38979,39019, 39059,39100,39140,39180,39221,39261,39301,39341, 39382,39422,39462,39502,39542,39582,39622,39662, 39702,39742,39782,39822,39862,39902,39942,39982, 40021,40061,40101,40141,40180,40220,40260,40300, 40339,40379,40418,40458,40497,40537,40576,40616, 40655,40695,40734,40773,40813,40852,40891,40931, 40970,41009,41048,41087,41127,41166,41205,41244, 41283,41322,41361,41400,41439,41478,41517,41556, 41595,41633,41672,41711,41750,41788,41827,41866, 41904,41943,41982,42020,42059,42097,42136,42174, 42213,42251,42290,42328,42366,42405,42443,42481, 42520,42558,42596,42634,42672,42711,42749,42787, 42825,42863,42901,42939,42977,43015,43053,43091, 43128,43166,43204,43242,43280,43317,43355,43393, 43430,43468,43506,43543,43581,43618,43656,43693, 43731,43768,43806,43843,43880,43918,43955,43992, 44029,44067,44104,44141,44178,44215,44252,44289, 44326,44363,44400,44437,44474,44511,44548,44585, 44622,44659,44695,44732,44769,44806,44842,44879, 44915,44952,44989,45025,45062,45098,45135,45171, 45207,45244,45280,45316,45353,45389,45425,45462, 45498,45534,45570,45606,45642,45678,45714,45750, 45786,45822,45858,45894,45930,45966,46002,46037, 46073,46109,46145,46180,46216,46252,46287,46323, 46358,46394,46429,46465,46500,46536,46571,46606, 46642,46677,46712,46747,46783,46818,46853,46888, 46923,46958,46993,47028,47063,47098,47133,47168, 47203,47238,47273,47308,47342,47377,47412,47446, 47481,47516,47550,47585,47619,47654,47688,47723, 47757,47792,47826,47860,47895,47929,47963,47998, 48032,48066,48100,48134,48168,48202,48237,48271, 48305,48338,48372,48406,48440,48474,48508,48542, 48575,48609,48643,48676,48710,48744,48777,48811, 48844,48878,48911,48945,48978,49012,49045,49078, 49112,49145,49178,49211,49244,49278,49311,49344, 49377,49410,49443,49476,49509,49542,49575,49608, 49640,49673,49706,49739,49771,49804,49837,49869, 49902,49935,49967,50000,50032,50065,50097,50129, 50162,50194,50226,50259,50291,50323,50355,50387, 50420,50452,50484,50516,50548,50580,50612,50644, 50675,50707,50739,50771,50803,50834,50866,50898, 50929,50961,50993,51024,51056,51087,51119,51150, 51182,51213,51244,51276,51307,51338,51369,51401, 51432,51463,51494,51525,51556,51587,51618,51649, 51680,51711,51742,51773,51803,51834,51865,51896, 51926,51957,51988,52018,52049,52079,52110,52140, 52171,52201,52231,52262,52292,52322,52353,52383, 52413,52443,52473,52503,52534,52564,52594,52624, 52653,52683,52713,52743,52773,52803,52832,52862, 52892,52922,52951,52981,53010,53040,53069,53099, 53128,53158,53187,53216,53246,53275,53304,53334, 53363,53392,53421,53450,53479,53508,53537,53566, 53595,53624,53653,53682,53711,53739,53768,53797, 53826,53854,53883,53911,53940,53969,53997,54026, 54054,54082,54111,54139,54167,54196,54224,54252, 54280,54308,54337,54365,54393,54421,54449,54477, 54505,54533,54560,54588,54616,54644,54672,54699, 54727,54755,54782,54810,54837,54865,54892,54920, 54947,54974,55002,55029,55056,55084,55111,55138, 55165,55192,55219,55246,55274,55300,55327,55354, 55381,55408,55435,55462,55489,55515,55542,55569, 55595,55622,55648,55675,55701,55728,55754,55781, 55807,55833,55860,55886,55912,55938,55965,55991, 56017,56043,56069,56095,56121,56147,56173,56199, 56225,56250,56276,56302,56328,56353,56379,56404, 56430,56456,56481,56507,56532,56557,56583,56608, 56633,56659,56684,56709,56734,56760,56785,56810, 56835,56860,56885,56910,56935,56959,56984,57009, 57034,57059,57083,57108,57133,57157,57182,57206, 57231,57255,57280,57304,57329,57353,57377,57402, 57426,57450,57474,57498,57522,57546,57570,57594, 57618,57642,57666,57690,57714,57738,57762,57785, 57809,57833,57856,57880,57903,57927,57950,57974, 57997,58021,58044,58067,58091,58114,58137,58160, 58183,58207,58230,58253,58276,58299,58322,58345, 58367,58390,58413,58436,58459,58481,58504,58527, 58549,58572,58594,58617,58639,58662,58684,58706, 58729,58751,58773,58795,58818,58840,58862,58884, 58906,58928,58950,58972,58994,59016,59038,59059, 59081,59103,59125,59146,59168,59190,59211,59233, 59254,59276,59297,59318,59340,59361,59382,59404, 59425,59446,59467,59488,59509,59530,59551,59572, 59593,59614,59635,59656,59677,59697,59718,59739, 59759,59780,59801,59821,59842,59862,59883,59903, 59923,59944,59964,59984,60004,60025,60045,60065, 60085,60105,60125,60145,60165,60185,60205,60225, 60244,60264,60284,60304,60323,60343,60363,60382, 60402,60421,60441,60460,60479,60499,60518,60537, 60556,60576,60595,60614,60633,60652,60671,60690, 60709,60728,60747,60766,60785,60803,60822,60841, 60859,60878,60897,60915,60934,60952,60971,60989, 61007,61026,61044,61062,61081,61099,61117,61135, 61153,61171,61189,61207,61225,61243,61261,61279, 61297,61314,61332,61350,61367,61385,61403,61420, 61438,61455,61473,61490,61507,61525,61542,61559, 61577,61594,61611,61628,61645,61662,61679,61696, 61713,61730,61747,61764,61780,61797,61814,61831, 61847,61864,61880,61897,61913,61930,61946,61963, 61979,61995,62012,62028,62044,62060,62076,62092, 62108,62125,62141,62156,62172,62188,62204,62220, 62236,62251,62267,62283,62298,62314,62329,62345, 62360,62376,62391,62407,62422,62437,62453,62468, 62483,62498,62513,62528,62543,62558,62573,62588, 62603,62618,62633,62648,62662,62677,62692,62706, 62721,62735,62750,62764,62779,62793,62808,62822, 62836,62850,62865,62879,62893,62907,62921,62935, 62949,62963,62977,62991,63005,63019,63032,63046, 63060,63074,63087,63101,63114,63128,63141,63155, 63168,63182,63195,63208,63221,63235,63248,63261, 63274,63287,63300,63313,63326,63339,63352,63365, 63378,63390,63403,63416,63429,63441,63454,63466, 63479,63491,63504,63516,63528,63541,63553,63565, 63578,63590,63602,63614,63626,63638,63650,63662, 63674,63686,63698,63709,63721,63733,63745,63756, 63768,63779,63791,63803,63814,63825,63837,63848, 63859,63871,63882,63893,63904,63915,63927,63938, 63949,63960,63971,63981,63992,64003,64014,64025, 64035,64046,64057,64067,64078,64088,64099,64109, 64120,64130,64140,64151,64161,64171,64181,64192, 64202,64212,64222,64232,64242,64252,64261,64271, 64281,64291,64301,64310,64320,64330,64339,64349, 64358,64368,64377,64387,64396,64405,64414,64424, 64433,64442,64451,64460,64469,64478,64487,64496, 64505,64514,64523,64532,64540,64549,64558,64566, 64575,64584,64592,64601,64609,64617,64626,64634, 64642,64651,64659,64667,64675,64683,64691,64699, 64707,64715,64723,64731,64739,64747,64754,64762, 64770,64777,64785,64793,64800,64808,64815,64822, 64830,64837,64844,64852,64859,64866,64873,64880, 64887,64895,64902,64908,64915,64922,64929,64936, 64943,64949,64956,64963,64969,64976,64982,64989, 64995,65002,65008,65015,65021,65027,65033,65040, 65046,65052,65058,65064,65070,65076,65082,65088, 65094,65099,65105,65111,65117,65122,65128,65133, 65139,65144,65150,65155,65161,65166,65171,65177, 65182,65187,65192,65197,65202,65207,65212,65217, 65222,65227,65232,65237,65242,65246,65251,65256, 65260,65265,65270,65274,65279,65283,65287,65292, 65296,65300,65305,65309,65313,65317,65321,65325, 65329,65333,65337,65341,65345,65349,65352,65356, 65360,65363,65367,65371,65374,65378,65381,65385, 65388,65391,65395,65398,65401,65404,65408,65411, 65414,65417,65420,65423,65426,65429,65431,65434, 65437,65440,65442,65445,65448,65450,65453,65455, 65458,65460,65463,65465,65467,65470,65472,65474, 65476,65478,65480,65482,65484,65486,65488,65490, 65492,65494,65496,65497,65499,65501,65502,65504, 65505,65507,65508,65510,65511,65513,65514,65515, 65516,65518,65519,65520,65521,65522,65523,65524, 65525,65526,65527,65527,65528,65529,65530,65530, 65531,65531,65532,65532,65533,65533,65534,65534, 65534,65535,65535,65535,65535,65535,65535,65535, 65535,65535,65535,65535,65535,65535,65535,65534, 65534,65534,65533,65533,65532,65532,65531,65531, 65530,65530,65529,65528,65527,65527,65526,65525, 65524,65523,65522,65521,65520,65519,65518,65516, 65515,65514,65513,65511,65510,65508,65507,65505, 65504,65502,65501,65499,65497,65496,65494,65492, 65490,65488,65486,65484,65482,65480,65478,65476, 65474,65472,65470,65467,65465,65463,65460,65458, 65455,65453,65450,65448,65445,65442,65440,65437, 65434,65431,65429,65426,65423,65420,65417,65414, 65411,65408,65404,65401,65398,65395,65391,65388, 65385,65381,65378,65374,65371,65367,65363,65360, 65356,65352,65349,65345,65341,65337,65333,65329, 65325,65321,65317,65313,65309,65305,65300,65296, 65292,65287,65283,65279,65274,65270,65265,65260, 65256,65251,65246,65242,65237,65232,65227,65222, 65217,65212,65207,65202,65197,65192,65187,65182, 65177,65171,65166,65161,65155,65150,65144,65139, 65133,65128,65122,65117,65111,65105,65099,65094, 65088,65082,65076,65070,65064,65058,65052,65046, 65040,65033,65027,65021,65015,65008,65002,64995, 64989,64982,64976,64969,64963,64956,64949,64943, 64936,64929,64922,64915,64908,64902,64895,64887, 64880,64873,64866,64859,64852,64844,64837,64830, 64822,64815,64808,64800,64793,64785,64777,64770, 64762,64754,64747,64739,64731,64723,64715,64707, 64699,64691,64683,64675,64667,64659,64651,64642, 64634,64626,64617,64609,64600,64592,64584,64575, 64566,64558,64549,64540,64532,64523,64514,64505, 64496,64487,64478,64469,64460,64451,64442,64433, 64424,64414,64405,64396,64387,64377,64368,64358, 64349,64339,64330,64320,64310,64301,64291,64281, 64271,64261,64252,64242,64232,64222,64212,64202, 64192,64181,64171,64161,64151,64140,64130,64120, 64109,64099,64088,64078,64067,64057,64046,64035, 64025,64014,64003,63992,63981,63971,63960,63949, 63938,63927,63915,63904,63893,63882,63871,63859, 63848,63837,63825,63814,63803,63791,63779,63768, 63756,63745,63733,63721,63709,63698,63686,63674, 63662,63650,63638,63626,63614,63602,63590,63578, 63565,63553,63541,63528,63516,63504,63491,63479, 63466,63454,63441,63429,63416,63403,63390,63378, 63365,63352,63339,63326,63313,63300,63287,63274, 63261,63248,63235,63221,63208,63195,63182,63168, 63155,63141,63128,63114,63101,63087,63074,63060, 63046,63032,63019,63005,62991,62977,62963,62949, 62935,62921,62907,62893,62879,62865,62850,62836, 62822,62808,62793,62779,62764,62750,62735,62721, 62706,62692,62677,62662,62648,62633,62618,62603, 62588,62573,62558,62543,62528,62513,62498,62483, 62468,62453,62437,62422,62407,62391,62376,62360, 62345,62329,62314,62298,62283,62267,62251,62236, 62220,62204,62188,62172,62156,62141,62125,62108, 62092,62076,62060,62044,62028,62012,61995,61979, 61963,61946,61930,61913,61897,61880,61864,61847, 61831,61814,61797,61780,61764,61747,61730,61713, 61696,61679,61662,61645,61628,61611,61594,61577, 61559,61542,61525,61507,61490,61473,61455,61438, 61420,61403,61385,61367,61350,61332,61314,61297, 61279,61261,61243,61225,61207,61189,61171,61153, 61135,61117,61099,61081,61062,61044,61026,61007, 60989,60971,60952,60934,60915,60897,60878,60859, 60841,60822,60803,60785,60766,60747,60728,60709, 60690,60671,60652,60633,60614,60595,60576,60556, 60537,60518,60499,60479,60460,60441,60421,60402, 60382,60363,60343,60323,60304,60284,60264,60244, 60225,60205,60185,60165,60145,60125,60105,60085, 60065,60045,60025,60004,59984,59964,59944,59923, 59903,59883,59862,59842,59821,59801,59780,59759, 59739,59718,59697,59677,59656,59635,59614,59593, 59572,59551,59530,59509,59488,59467,59446,59425, 59404,59382,59361,59340,59318,59297,59276,59254, 59233,59211,59190,59168,59146,59125,59103,59081, 59059,59038,59016,58994,58972,58950,58928,58906, 58884,58862,58840,58818,58795,58773,58751,58729, 58706,58684,58662,58639,58617,58594,58572,58549, 58527,58504,58481,58459,58436,58413,58390,58367, 58345,58322,58299,58276,58253,58230,58207,58183, 58160,58137,58114,58091,58067,58044,58021,57997, 57974,57950,57927,57903,57880,57856,57833,57809, 57785,57762,57738,57714,57690,57666,57642,57618, 57594,57570,57546,57522,57498,57474,57450,57426, 57402,57377,57353,57329,57304,57280,57255,57231, 57206,57182,57157,57133,57108,57083,57059,57034, 57009,56984,56959,56935,56910,56885,56860,56835, 56810,56785,56760,56734,56709,56684,56659,56633, 56608,56583,56557,56532,56507,56481,56456,56430, 56404,56379,56353,56328,56302,56276,56250,56225, 56199,56173,56147,56121,56095,56069,56043,56017, 55991,55965,55938,55912,55886,55860,55833,55807, 55781,55754,55728,55701,55675,55648,55622,55595, 55569,55542,55515,55489,55462,55435,55408,55381, 55354,55327,55300,55274,55246,55219,55192,55165, 55138,55111,55084,55056,55029,55002,54974,54947, 54920,54892,54865,54837,54810,54782,54755,54727, 54699,54672,54644,54616,54588,54560,54533,54505, 54477,54449,54421,54393,54365,54337,54308,54280, 54252,54224,54196,54167,54139,54111,54082,54054, 54026,53997,53969,53940,53911,53883,53854,53826, 53797,53768,53739,53711,53682,53653,53624,53595, 53566,53537,53508,53479,53450,53421,53392,53363, 53334,53304,53275,53246,53216,53187,53158,53128, 53099,53069,53040,53010,52981,52951,52922,52892, 52862,52832,52803,52773,52743,52713,52683,52653, 52624,52594,52564,52534,52503,52473,52443,52413, 52383,52353,52322,52292,52262,52231,52201,52171, 52140,52110,52079,52049,52018,51988,51957,51926, 51896,51865,51834,51803,51773,51742,51711,51680, 51649,51618,51587,51556,51525,51494,51463,51432, 51401,51369,51338,51307,51276,51244,51213,51182, 51150,51119,51087,51056,51024,50993,50961,50929, 50898,50866,50834,50803,50771,50739,50707,50675, 50644,50612,50580,50548,50516,50484,50452,50420, 50387,50355,50323,50291,50259,50226,50194,50162, 50129,50097,50065,50032,50000,49967,49935,49902, 49869,49837,49804,49771,49739,49706,49673,49640, 49608,49575,49542,49509,49476,49443,49410,49377, 49344,49311,49278,49244,49211,49178,49145,49112, 49078,49045,49012,48978,48945,48911,48878,48844, 48811,48777,48744,48710,48676,48643,48609,48575, 48542,48508,48474,48440,48406,48372,48338,48304, 48271,48237,48202,48168,48134,48100,48066,48032, 47998,47963,47929,47895,47860,47826,47792,47757, 47723,47688,47654,47619,47585,47550,47516,47481, 47446,47412,47377,47342,47308,47273,47238,47203, 47168,47133,47098,47063,47028,46993,46958,46923, 46888,46853,46818,46783,46747,46712,46677,46642, 46606,46571,46536,46500,46465,46429,46394,46358, 46323,46287,46252,46216,46180,46145,46109,46073, 46037,46002,45966,45930,45894,45858,45822,45786, 45750,45714,45678,45642,45606,45570,45534,45498, 45462,45425,45389,45353,45316,45280,45244,45207, 45171,45135,45098,45062,45025,44989,44952,44915, 44879,44842,44806,44769,44732,44695,44659,44622, 44585,44548,44511,44474,44437,44400,44363,44326, 44289,44252,44215,44178,44141,44104,44067,44029, 43992,43955,43918,43880,43843,43806,43768,43731, 43693,43656,43618,43581,43543,43506,43468,43430, 43393,43355,43317,43280,43242,43204,43166,43128, 43091,43053,43015,42977,42939,42901,42863,42825, 42787,42749,42711,42672,42634,42596,42558,42520, 42481,42443,42405,42366,42328,42290,42251,42213, 42174,42136,42097,42059,42020,41982,41943,41904, 41866,41827,41788,41750,41711,41672,41633,41595, 41556,41517,41478,41439,41400,41361,41322,41283, 41244,41205,41166,41127,41088,41048,41009,40970, 40931,40891,40852,40813,40773,40734,40695,40655, 40616,40576,40537,40497,40458,40418,40379,40339, 40300,40260,40220,40180,40141,40101,40061,40021, 39982,39942,39902,39862,39822,39782,39742,39702, 39662,39622,39582,39542,39502,39462,39422,39382, 39341,39301,39261,39221,39180,39140,39100,39059, 39019,38979,38938,38898,38857,38817,38776,38736, 38695,38655,38614,38573,38533,38492,38451,38411, 38370,38329,38288,38248,38207,38166,38125,38084, 38043,38002,37961,37920,37879,37838,37797,37756, 37715,37674,37633,37592,37551,37509,37468,37427, 37386,37344,37303,37262,37220,37179,37137,37096, 37055,37013,36972,36930,36889,36847,36805,36764, 36722,36681,36639,36597,36556,36514,36472,36430, 36388,36347,36305,36263,36221,36179,36137,36095, 36053,36011,35969,35927,35885,35843,35801,35759, 35717,35675,35633,35590,35548,35506,35464,35421, 35379,35337,35294,35252,35210,35167,35125,35082, 35040,34997,34955,34912,34870,34827,34785,34742, 34699,34657,34614,34571,34529,34486,34443,34400, 34358,34315,34272,34229,34186,34143,34100,34057, 34015,33972,33929,33886,33843,33799,33756,33713, 33670,33627,33584,33541,33498,33454,33411,33368, 33325,33281,33238,33195,33151,33108,33065,33021, 32978,32934,32891,32847,32804,32760,32717,32673, 32630,32586,32542,32499,32455,32411,32368,32324, 32280,32236,32193,32149,32105,32061,32017,31974, 31930,31886,31842,31798,31754,31710,31666,31622, 31578,31534,31490,31446,31402,31357,31313,31269, 31225,31181,31136,31092,31048,31004,30959,30915, 30871,30826,30782,30738,30693,30649,30604,30560, 30515,30471,30426,30382,30337,30293,30248,30204, 30159,30114,30070,30025,29980,29936,29891,29846, 29801,29757,29712,29667,29622,29577,29533,29488, 29443,29398,29353,29308,29263,29218,29173,29128, 29083,29038,28993,28948,28903,28858,28812,28767, 28722,28677,28632,28586,28541,28496,28451,28405, 28360,28315,28269,28224,28179,28133,28088,28042, 27997,27952,27906,27861,27815,27770,27724,27678, 27633,27587,27542,27496,27450,27405,27359,27313, 27268,27222,27176,27131,27085,27039,26993,26947, 26902,26856,26810,26764,26718,26672,26626,26580, 26534,26488,26442,26396,26350,26304,26258,26212, 26166,26120,26074,26028,25982,25936,25889,25843, 25797,25751,25705,25658,25612,25566,25520,25473, 25427,25381,25334,25288,25241,25195,25149,25102, 25056,25009,24963,24916,24870,24823,24777,24730, 24684,24637,24591,24544,24497,24451,24404,24357, 24311,24264,24217,24171,24124,24077,24030,23984, 23937,23890,23843,23796,23750,23703,23656,23609, 23562,23515,23468,23421,23374,23327,23280,23233, 23186,23139,23092,23045,22998,22951,22904,22857, 22810,22763,22716,22668,22621,22574,22527,22480, 22433,22385,22338,22291,22243,22196,22149,22102, 22054,22007,21960,21912,21865,21817,21770,21723, 21675,21628,21580,21533,21485,21438,21390,21343, 21295,21248,21200,21153,21105,21057,21010,20962, 20915,20867,20819,20772,20724,20676,20629,20581, 20533,20485,20438,20390,20342,20294,20246,20199, 20151,20103,20055,20007,19959,19912,19864,19816, 19768,19720,19672,19624,19576,19528,19480,19432, 19384,19336,19288,19240,19192,19144,19096,19048, 19000,18951,18903,18855,18807,18759,18711,18663, 18614,18566,18518,18470,18421,18373,18325,18277, 18228,18180,18132,18084,18035,17987,17939,17890, 17842,17793,17745,17697,17648,17600,17551,17503, 17455,17406,17358,17309,17261,17212,17164,17115, 17067,17018,16970,16921,16872,16824,16775,16727, 16678,16629,16581,16532,16484,16435,16386,16338, 16289,16240,16191,16143,16094,16045,15997,15948, 15899,15850,15802,15753,15704,15655,15606,15557, 15509,15460,15411,15362,15313,15264,15215,15167, 15118,15069,15020,14971,14922,14873,14824,14775, 14726,14677,14628,14579,14530,14481,14432,14383, 14334,14285,14236,14187,14138,14089,14040,13990, 13941,13892,13843,13794,13745,13696,13646,13597, 13548,13499,13450,13401,13351,13302,13253,13204, 13154,13105,13056,13007,12957,12908,12859,12810, 12760,12711,12662,12612,12563,12514,12464,12415, 12366,12316,12267,12218,12168,12119,12069,12020, 11970,11921,11872,11822,11773,11723,11674,11624, 11575,11525,11476,11426,11377,11327,11278,11228, 11179,11129,11080,11030,10981,10931,10882,10832, 10782,10733,10683,10634,10584,10534,10485,10435, 10386,10336,10286,10237,10187,10137,10088,10038, 9988,9939,9889,9839,9790,9740,9690,9640, 9591,9541,9491,9442,9392,9342,9292,9243, 9193,9143,9093,9043,8994,8944,8894,8844, 8794,8745,8695,8645,8595,8545,8496,8446, 8396,8346,8296,8246,8196,8147,8097,8047, 7997,7947,7897,7847,7797,7747,7697,7648, 7598,7548,7498,7448,7398,7348,7298,7248, 7198,7148,7098,7048,6998,6948,6898,6848, 6798,6748,6698,6648,6598,6548,6498,6448, 6398,6348,6298,6248,6198,6148,6098,6048, 5998,5948,5898,5848,5798,5748,5697,5647, 5597,5547,5497,5447,5397,5347,5297,5247, 5197,5146,5096,5046,4996,4946,4896,4846, 4796,4745,4695,4645,4595,4545,4495,4445, 4394,4344,4294,4244,4194,4144,4093,4043, 3993,3943,3893,3843,3792,3742,3692,3642, 3592,3541,3491,3441,3391,3341,3291,3240, 3190,3140,3090,3039,2989,2939,2889,2839, 2788,2738,2688,2638,2587,2537,2487,2437, 2387,2336,2286,2236,2186,2135,2085,2035, 1985,1934,1884,1834,1784,1733,1683,1633, 1583,1532,1482,1432,1382,1331,1281,1231, 1181,1130,1080,1030,980,929,879,829, 779,728,678,628,578,527,477,427, 376,326,276,226,175,125,75,25, -25,-75,-125,-175,-226,-276,-326,-376, -427,-477,-527,-578,-628,-678,-728,-779, -829,-879,-929,-980,-1030,-1080,-1130,-1181, -1231,-1281,-1331,-1382,-1432,-1482,-1532,-1583, -1633,-1683,-1733,-1784,-1834,-1884,-1934,-1985, -2035,-2085,-2135,-2186,-2236,-2286,-2336,-2387, -2437,-2487,-2537,-2588,-2638,-2688,-2738,-2788, -2839,-2889,-2939,-2989,-3039,-3090,-3140,-3190, -3240,-3291,-3341,-3391,-3441,-3491,-3541,-3592, -3642,-3692,-3742,-3792,-3843,-3893,-3943,-3993, -4043,-4093,-4144,-4194,-4244,-4294,-4344,-4394, -4445,-4495,-4545,-4595,-4645,-4695,-4745,-4796, -4846,-4896,-4946,-4996,-5046,-5096,-5146,-5197, -5247,-5297,-5347,-5397,-5447,-5497,-5547,-5597, -5647,-5697,-5748,-5798,-5848,-5898,-5948,-5998, -6048,-6098,-6148,-6198,-6248,-6298,-6348,-6398, -6448,-6498,-6548,-6598,-6648,-6698,-6748,-6798, -6848,-6898,-6948,-6998,-7048,-7098,-7148,-7198, -7248,-7298,-7348,-7398,-7448,-7498,-7548,-7598, -7648,-7697,-7747,-7797,-7847,-7897,-7947,-7997, -8047,-8097,-8147,-8196,-8246,-8296,-8346,-8396, -8446,-8496,-8545,-8595,-8645,-8695,-8745,-8794, -8844,-8894,-8944,-8994,-9043,-9093,-9143,-9193, -9243,-9292,-9342,-9392,-9442,-9491,-9541,-9591, -9640,-9690,-9740,-9790,-9839,-9889,-9939,-9988, -10038,-10088,-10137,-10187,-10237,-10286,-10336,-10386, -10435,-10485,-10534,-10584,-10634,-10683,-10733,-10782, -10832,-10882,-10931,-10981,-11030,-11080,-11129,-11179, -11228,-11278,-11327,-11377,-11426,-11476,-11525,-11575, -11624,-11674,-11723,-11773,-11822,-11872,-11921,-11970, -12020,-12069,-12119,-12168,-12218,-12267,-12316,-12366, -12415,-12464,-12514,-12563,-12612,-12662,-12711,-12760, -12810,-12859,-12908,-12957,-13007,-13056,-13105,-13154, -13204,-13253,-13302,-13351,-13401,-13450,-13499,-13548, -13597,-13647,-13696,-13745,-13794,-13843,-13892,-13941, -13990,-14040,-14089,-14138,-14187,-14236,-14285,-14334, -14383,-14432,-14481,-14530,-14579,-14628,-14677,-14726, -14775,-14824,-14873,-14922,-14971,-15020,-15069,-15118, -15167,-15215,-15264,-15313,-15362,-15411,-15460,-15509, -15557,-15606,-15655,-15704,-15753,-15802,-15850,-15899, -15948,-15997,-16045,-16094,-16143,-16191,-16240,-16289, -16338,-16386,-16435,-16484,-16532,-16581,-16629,-16678, -16727,-16775,-16824,-16872,-16921,-16970,-17018,-17067, -17115,-17164,-17212,-17261,-17309,-17358,-17406,-17455, -17503,-17551,-17600,-17648,-17697,-17745,-17793,-17842, -17890,-17939,-17987,-18035,-18084,-18132,-18180,-18228, -18277,-18325,-18373,-18421,-18470,-18518,-18566,-18614, -18663,-18711,-18759,-18807,-18855,-18903,-18951,-19000, -19048,-19096,-19144,-19192,-19240,-19288,-19336,-19384, -19432,-19480,-19528,-19576,-19624,-19672,-19720,-19768, -19816,-19864,-19912,-19959,-20007,-20055,-20103,-20151, -20199,-20246,-20294,-20342,-20390,-20438,-20485,-20533, -20581,-20629,-20676,-20724,-20772,-20819,-20867,-20915, -20962,-21010,-21057,-21105,-21153,-21200,-21248,-21295, -21343,-21390,-21438,-21485,-21533,-21580,-21628,-21675, -21723,-21770,-21817,-21865,-21912,-21960,-22007,-22054, -22102,-22149,-22196,-22243,-22291,-22338,-22385,-22433, -22480,-22527,-22574,-22621,-22668,-22716,-22763,-22810, -22857,-22904,-22951,-22998,-23045,-23092,-23139,-23186, -23233,-23280,-23327,-23374,-23421,-23468,-23515,-23562, -23609,-23656,-23703,-23750,-23796,-23843,-23890,-23937, -23984,-24030,-24077,-24124,-24171,-24217,-24264,-24311, -24357,-24404,-24451,-24497,-24544,-24591,-24637,-24684, -24730,-24777,-24823,-24870,-24916,-24963,-25009,-25056, -25102,-25149,-25195,-25241,-25288,-25334,-25381,-25427, -25473,-25520,-25566,-25612,-25658,-25705,-25751,-25797, -25843,-25889,-25936,-25982,-26028,-26074,-26120,-26166, -26212,-26258,-26304,-26350,-26396,-26442,-26488,-26534, -26580,-26626,-26672,-26718,-26764,-26810,-26856,-26902, -26947,-26993,-27039,-27085,-27131,-27176,-27222,-27268, -27313,-27359,-27405,-27450,-27496,-27542,-27587,-27633, -27678,-27724,-27770,-27815,-27861,-27906,-27952,-27997, -28042,-28088,-28133,-28179,-28224,-28269,-28315,-28360, -28405,-28451,-28496,-28541,-28586,-28632,-28677,-28722, -28767,-28812,-28858,-28903,-28948,-28993,-29038,-29083, -29128,-29173,-29218,-29263,-29308,-29353,-29398,-29443, -29488,-29533,-29577,-29622,-29667,-29712,-29757,-29801, -29846,-29891,-29936,-29980,-30025,-30070,-30114,-30159, -30204,-30248,-30293,-30337,-30382,-30426,-30471,-30515, -30560,-30604,-30649,-30693,-30738,-30782,-30826,-30871, -30915,-30959,-31004,-31048,-31092,-31136,-31181,-31225, -31269,-31313,-31357,-31402,-31446,-31490,-31534,-31578, -31622,-31666,-31710,-31754,-31798,-31842,-31886,-31930, -31974,-32017,-32061,-32105,-32149,-32193,-32236,-32280, -32324,-32368,-32411,-32455,-32499,-32542,-32586,-32630, -32673,-32717,-32760,-32804,-32847,-32891,-32934,-32978, -33021,-33065,-33108,-33151,-33195,-33238,-33281,-33325, -33368,-33411,-33454,-33498,-33541,-33584,-33627,-33670, -33713,-33756,-33799,-33843,-33886,-33929,-33972,-34015, -34057,-34100,-34143,-34186,-34229,-34272,-34315,-34358, -34400,-34443,-34486,-34529,-34571,-34614,-34657,-34699, -34742,-34785,-34827,-34870,-34912,-34955,-34997,-35040, -35082,-35125,-35167,-35210,-35252,-35294,-35337,-35379, -35421,-35464,-35506,-35548,-35590,-35633,-35675,-35717, -35759,-35801,-35843,-35885,-35927,-35969,-36011,-36053, -36095,-36137,-36179,-36221,-36263,-36305,-36347,-36388, -36430,-36472,-36514,-36555,-36597,-36639,-36681,-36722, -36764,-36805,-36847,-36889,-36930,-36972,-37013,-37055, -37096,-37137,-37179,-37220,-37262,-37303,-37344,-37386, -37427,-37468,-37509,-37551,-37592,-37633,-37674,-37715, -37756,-37797,-37838,-37879,-37920,-37961,-38002,-38043, -38084,-38125,-38166,-38207,-38248,-38288,-38329,-38370, -38411,-38451,-38492,-38533,-38573,-38614,-38655,-38695, -38736,-38776,-38817,-38857,-38898,-38938,-38979,-39019, -39059,-39100,-39140,-39180,-39221,-39261,-39301,-39341, -39382,-39422,-39462,-39502,-39542,-39582,-39622,-39662, -39702,-39742,-39782,-39822,-39862,-39902,-39942,-39982, -40021,-40061,-40101,-40141,-40180,-40220,-40260,-40299, -40339,-40379,-40418,-40458,-40497,-40537,-40576,-40616, -40655,-40695,-40734,-40773,-40813,-40852,-40891,-40931, -40970,-41009,-41048,-41087,-41127,-41166,-41205,-41244, -41283,-41322,-41361,-41400,-41439,-41478,-41517,-41556, -41595,-41633,-41672,-41711,-41750,-41788,-41827,-41866, -41904,-41943,-41982,-42020,-42059,-42097,-42136,-42174, -42213,-42251,-42290,-42328,-42366,-42405,-42443,-42481, -42520,-42558,-42596,-42634,-42672,-42711,-42749,-42787, -42825,-42863,-42901,-42939,-42977,-43015,-43053,-43091, -43128,-43166,-43204,-43242,-43280,-43317,-43355,-43393, -43430,-43468,-43506,-43543,-43581,-43618,-43656,-43693, -43731,-43768,-43806,-43843,-43880,-43918,-43955,-43992, -44029,-44067,-44104,-44141,-44178,-44215,-44252,-44289, -44326,-44363,-44400,-44437,-44474,-44511,-44548,-44585, -44622,-44659,-44695,-44732,-44769,-44806,-44842,-44879, -44915,-44952,-44989,-45025,-45062,-45098,-45135,-45171, -45207,-45244,-45280,-45316,-45353,-45389,-45425,-45462, -45498,-45534,-45570,-45606,-45642,-45678,-45714,-45750, -45786,-45822,-45858,-45894,-45930,-45966,-46002,-46037, -46073,-46109,-46145,-46180,-46216,-46252,-46287,-46323, -46358,-46394,-46429,-46465,-46500,-46536,-46571,-46606, -46642,-46677,-46712,-46747,-46783,-46818,-46853,-46888, -46923,-46958,-46993,-47028,-47063,-47098,-47133,-47168, -47203,-47238,-47273,-47308,-47342,-47377,-47412,-47446, -47481,-47516,-47550,-47585,-47619,-47654,-47688,-47723, -47757,-47792,-47826,-47860,-47895,-47929,-47963,-47998, -48032,-48066,-48100,-48134,-48168,-48202,-48236,-48271, -48304,-48338,-48372,-48406,-48440,-48474,-48508,-48542, -48575,-48609,-48643,-48676,-48710,-48744,-48777,-48811, -48844,-48878,-48911,-48945,-48978,-49012,-49045,-49078, -49112,-49145,-49178,-49211,-49244,-49278,-49311,-49344, -49377,-49410,-49443,-49476,-49509,-49542,-49575,-49608, -49640,-49673,-49706,-49739,-49771,-49804,-49837,-49869, -49902,-49935,-49967,-50000,-50032,-50065,-50097,-50129, -50162,-50194,-50226,-50259,-50291,-50323,-50355,-50387, -50420,-50452,-50484,-50516,-50548,-50580,-50612,-50644, -50675,-50707,-50739,-50771,-50803,-50834,-50866,-50898, -50929,-50961,-50993,-51024,-51056,-51087,-51119,-51150, -51182,-51213,-51244,-51276,-51307,-51338,-51369,-51401, -51432,-51463,-51494,-51525,-51556,-51587,-51618,-51649, -51680,-51711,-51742,-51773,-51803,-51834,-51865,-51896, -51926,-51957,-51988,-52018,-52049,-52079,-52110,-52140, -52171,-52201,-52231,-52262,-52292,-52322,-52353,-52383, -52413,-52443,-52473,-52503,-52534,-52564,-52594,-52624, -52653,-52683,-52713,-52743,-52773,-52803,-52832,-52862, -52892,-52922,-52951,-52981,-53010,-53040,-53069,-53099, -53128,-53158,-53187,-53216,-53246,-53275,-53304,-53334, -53363,-53392,-53421,-53450,-53479,-53508,-53537,-53566, -53595,-53624,-53653,-53682,-53711,-53739,-53768,-53797, -53826,-53854,-53883,-53911,-53940,-53969,-53997,-54026, -54054,-54082,-54111,-54139,-54167,-54196,-54224,-54252, -54280,-54308,-54337,-54365,-54393,-54421,-54449,-54477, -54505,-54533,-54560,-54588,-54616,-54644,-54672,-54699, -54727,-54755,-54782,-54810,-54837,-54865,-54892,-54920, -54947,-54974,-55002,-55029,-55056,-55084,-55111,-55138, -55165,-55192,-55219,-55246,-55274,-55300,-55327,-55354, -55381,-55408,-55435,-55462,-55489,-55515,-55542,-55569, -55595,-55622,-55648,-55675,-55701,-55728,-55754,-55781, -55807,-55833,-55860,-55886,-55912,-55938,-55965,-55991, -56017,-56043,-56069,-56095,-56121,-56147,-56173,-56199, -56225,-56250,-56276,-56302,-56328,-56353,-56379,-56404, -56430,-56456,-56481,-56507,-56532,-56557,-56583,-56608, -56633,-56659,-56684,-56709,-56734,-56760,-56785,-56810, -56835,-56860,-56885,-56910,-56935,-56959,-56984,-57009, -57034,-57059,-57083,-57108,-57133,-57157,-57182,-57206, -57231,-57255,-57280,-57304,-57329,-57353,-57377,-57402, -57426,-57450,-57474,-57498,-57522,-57546,-57570,-57594, -57618,-57642,-57666,-57690,-57714,-57738,-57762,-57785, -57809,-57833,-57856,-57880,-57903,-57927,-57950,-57974, -57997,-58021,-58044,-58067,-58091,-58114,-58137,-58160, -58183,-58207,-58230,-58253,-58276,-58299,-58322,-58345, -58367,-58390,-58413,-58436,-58459,-58481,-58504,-58527, -58549,-58572,-58594,-58617,-58639,-58662,-58684,-58706, -58729,-58751,-58773,-58795,-58818,-58840,-58862,-58884, -58906,-58928,-58950,-58972,-58994,-59016,-59038,-59059, -59081,-59103,-59125,-59146,-59168,-59190,-59211,-59233, -59254,-59276,-59297,-59318,-59340,-59361,-59382,-59404, -59425,-59446,-59467,-59488,-59509,-59530,-59551,-59572, -59593,-59614,-59635,-59656,-59677,-59697,-59718,-59739, -59759,-59780,-59801,-59821,-59842,-59862,-59883,-59903, -59923,-59944,-59964,-59984,-60004,-60025,-60045,-60065, -60085,-60105,-60125,-60145,-60165,-60185,-60205,-60225, -60244,-60264,-60284,-60304,-60323,-60343,-60363,-60382, -60402,-60421,-60441,-60460,-60479,-60499,-60518,-60537, -60556,-60576,-60595,-60614,-60633,-60652,-60671,-60690, -60709,-60728,-60747,-60766,-60785,-60803,-60822,-60841, -60859,-60878,-60897,-60915,-60934,-60952,-60971,-60989, -61007,-61026,-61044,-61062,-61081,-61099,-61117,-61135, -61153,-61171,-61189,-61207,-61225,-61243,-61261,-61279, -61297,-61314,-61332,-61350,-61367,-61385,-61403,-61420, -61438,-61455,-61473,-61490,-61507,-61525,-61542,-61559, -61577,-61594,-61611,-61628,-61645,-61662,-61679,-61696, -61713,-61730,-61747,-61764,-61780,-61797,-61814,-61831, -61847,-61864,-61880,-61897,-61913,-61930,-61946,-61963, -61979,-61995,-62012,-62028,-62044,-62060,-62076,-62092, -62108,-62125,-62141,-62156,-62172,-62188,-62204,-62220, -62236,-62251,-62267,-62283,-62298,-62314,-62329,-62345, -62360,-62376,-62391,-62407,-62422,-62437,-62453,-62468, -62483,-62498,-62513,-62528,-62543,-62558,-62573,-62588, -62603,-62618,-62633,-62648,-62662,-62677,-62692,-62706, -62721,-62735,-62750,-62764,-62779,-62793,-62808,-62822, -62836,-62850,-62865,-62879,-62893,-62907,-62921,-62935, -62949,-62963,-62977,-62991,-63005,-63019,-63032,-63046, -63060,-63074,-63087,-63101,-63114,-63128,-63141,-63155, -63168,-63182,-63195,-63208,-63221,-63235,-63248,-63261, -63274,-63287,-63300,-63313,-63326,-63339,-63352,-63365, -63378,-63390,-63403,-63416,-63429,-63441,-63454,-63466, -63479,-63491,-63504,-63516,-63528,-63541,-63553,-63565, -63578,-63590,-63602,-63614,-63626,-63638,-63650,-63662, -63674,-63686,-63698,-63709,-63721,-63733,-63745,-63756, -63768,-63779,-63791,-63803,-63814,-63825,-63837,-63848, -63859,-63871,-63882,-63893,-63904,-63915,-63927,-63938, -63949,-63960,-63971,-63981,-63992,-64003,-64014,-64025, -64035,-64046,-64057,-64067,-64078,-64088,-64099,-64109, -64120,-64130,-64140,-64151,-64161,-64171,-64181,-64192, -64202,-64212,-64222,-64232,-64242,-64252,-64261,-64271, -64281,-64291,-64301,-64310,-64320,-64330,-64339,-64349, -64358,-64368,-64377,-64387,-64396,-64405,-64414,-64424, -64433,-64442,-64451,-64460,-64469,-64478,-64487,-64496, -64505,-64514,-64523,-64532,-64540,-64549,-64558,-64566, -64575,-64584,-64592,-64601,-64609,-64617,-64626,-64634, -64642,-64651,-64659,-64667,-64675,-64683,-64691,-64699, -64707,-64715,-64723,-64731,-64739,-64747,-64754,-64762, -64770,-64777,-64785,-64793,-64800,-64808,-64815,-64822, -64830,-64837,-64844,-64852,-64859,-64866,-64873,-64880, -64887,-64895,-64902,-64908,-64915,-64922,-64929,-64936, -64943,-64949,-64956,-64963,-64969,-64976,-64982,-64989, -64995,-65002,-65008,-65015,-65021,-65027,-65033,-65040, -65046,-65052,-65058,-65064,-65070,-65076,-65082,-65088, -65094,-65099,-65105,-65111,-65117,-65122,-65128,-65133, -65139,-65144,-65150,-65155,-65161,-65166,-65171,-65177, -65182,-65187,-65192,-65197,-65202,-65207,-65212,-65217, -65222,-65227,-65232,-65237,-65242,-65246,-65251,-65256, -65260,-65265,-65270,-65274,-65279,-65283,-65287,-65292, -65296,-65300,-65305,-65309,-65313,-65317,-65321,-65325, -65329,-65333,-65337,-65341,-65345,-65349,-65352,-65356, -65360,-65363,-65367,-65371,-65374,-65378,-65381,-65385, -65388,-65391,-65395,-65398,-65401,-65404,-65408,-65411, -65414,-65417,-65420,-65423,-65426,-65429,-65431,-65434, -65437,-65440,-65442,-65445,-65448,-65450,-65453,-65455, -65458,-65460,-65463,-65465,-65467,-65470,-65472,-65474, -65476,-65478,-65480,-65482,-65484,-65486,-65488,-65490, -65492,-65494,-65496,-65497,-65499,-65501,-65502,-65504, -65505,-65507,-65508,-65510,-65511,-65513,-65514,-65515, -65516,-65518,-65519,-65520,-65521,-65522,-65523,-65524, -65525,-65526,-65527,-65527,-65528,-65529,-65530,-65530, -65531,-65531,-65532,-65532,-65533,-65533,-65534,-65534, -65534,-65535,-65535,-65535,-65535,-65535,-65535,-65535, -65535,-65535,-65535,-65535,-65535,-65535,-65535,-65534, -65534,-65534,-65533,-65533,-65532,-65532,-65531,-65531, -65530,-65530,-65529,-65528,-65527,-65527,-65526,-65525, -65524,-65523,-65522,-65521,-65520,-65519,-65518,-65516, -65515,-65514,-65513,-65511,-65510,-65508,-65507,-65505, -65504,-65502,-65501,-65499,-65497,-65496,-65494,-65492, -65490,-65488,-65486,-65484,-65482,-65480,-65478,-65476, -65474,-65472,-65470,-65467,-65465,-65463,-65460,-65458, -65455,-65453,-65450,-65448,-65445,-65442,-65440,-65437, -65434,-65431,-65429,-65426,-65423,-65420,-65417,-65414, -65411,-65408,-65404,-65401,-65398,-65395,-65391,-65388, -65385,-65381,-65378,-65374,-65371,-65367,-65363,-65360, -65356,-65352,-65349,-65345,-65341,-65337,-65333,-65329, -65325,-65321,-65317,-65313,-65309,-65305,-65300,-65296, -65292,-65287,-65283,-65279,-65274,-65270,-65265,-65260, -65256,-65251,-65246,-65242,-65237,-65232,-65227,-65222, -65217,-65212,-65207,-65202,-65197,-65192,-65187,-65182, -65177,-65171,-65166,-65161,-65155,-65150,-65144,-65139, -65133,-65128,-65122,-65117,-65111,-65105,-65099,-65094, -65088,-65082,-65076,-65070,-65064,-65058,-65052,-65046, -65040,-65033,-65027,-65021,-65015,-65008,-65002,-64995, -64989,-64982,-64976,-64969,-64963,-64956,-64949,-64943, -64936,-64929,-64922,-64915,-64908,-64902,-64895,-64887, -64880,-64873,-64866,-64859,-64852,-64844,-64837,-64830, -64822,-64815,-64808,-64800,-64793,-64785,-64777,-64770, -64762,-64754,-64747,-64739,-64731,-64723,-64715,-64707, -64699,-64691,-64683,-64675,-64667,-64659,-64651,-64642, -64634,-64626,-64617,-64609,-64601,-64592,-64584,-64575, -64566,-64558,-64549,-64540,-64532,-64523,-64514,-64505, -64496,-64487,-64478,-64469,-64460,-64451,-64442,-64433, -64424,-64414,-64405,-64396,-64387,-64377,-64368,-64358, -64349,-64339,-64330,-64320,-64310,-64301,-64291,-64281, -64271,-64261,-64252,-64242,-64232,-64222,-64212,-64202, -64192,-64181,-64171,-64161,-64151,-64140,-64130,-64120, -64109,-64099,-64088,-64078,-64067,-64057,-64046,-64035, -64025,-64014,-64003,-63992,-63981,-63971,-63960,-63949, -63938,-63927,-63915,-63904,-63893,-63882,-63871,-63859, -63848,-63837,-63825,-63814,-63803,-63791,-63779,-63768, -63756,-63745,-63733,-63721,-63709,-63698,-63686,-63674, -63662,-63650,-63638,-63626,-63614,-63602,-63590,-63578, -63565,-63553,-63541,-63528,-63516,-63504,-63491,-63479, -63466,-63454,-63441,-63429,-63416,-63403,-63390,-63378, -63365,-63352,-63339,-63326,-63313,-63300,-63287,-63274, -63261,-63248,-63235,-63221,-63208,-63195,-63182,-63168, -63155,-63141,-63128,-63114,-63101,-63087,-63074,-63060, -63046,-63032,-63019,-63005,-62991,-62977,-62963,-62949, -62935,-62921,-62907,-62893,-62879,-62865,-62850,-62836, -62822,-62808,-62793,-62779,-62764,-62750,-62735,-62721, -62706,-62692,-62677,-62662,-62648,-62633,-62618,-62603, -62588,-62573,-62558,-62543,-62528,-62513,-62498,-62483, -62468,-62453,-62437,-62422,-62407,-62391,-62376,-62360, -62345,-62329,-62314,-62298,-62283,-62267,-62251,-62236, -62220,-62204,-62188,-62172,-62156,-62141,-62125,-62108, -62092,-62076,-62060,-62044,-62028,-62012,-61995,-61979, -61963,-61946,-61930,-61913,-61897,-61880,-61864,-61847, -61831,-61814,-61797,-61780,-61764,-61747,-61730,-61713, -61696,-61679,-61662,-61645,-61628,-61611,-61594,-61577, -61559,-61542,-61525,-61507,-61490,-61473,-61455,-61438, -61420,-61403,-61385,-61367,-61350,-61332,-61314,-61297, -61279,-61261,-61243,-61225,-61207,-61189,-61171,-61153, -61135,-61117,-61099,-61081,-61062,-61044,-61026,-61007, -60989,-60971,-60952,-60934,-60915,-60897,-60878,-60859, -60841,-60822,-60803,-60785,-60766,-60747,-60728,-60709, -60690,-60671,-60652,-60633,-60614,-60595,-60576,-60556, -60537,-60518,-60499,-60479,-60460,-60441,-60421,-60402, -60382,-60363,-60343,-60323,-60304,-60284,-60264,-60244, -60225,-60205,-60185,-60165,-60145,-60125,-60105,-60085, -60065,-60045,-60025,-60004,-59984,-59964,-59944,-59923, -59903,-59883,-59862,-59842,-59821,-59801,-59780,-59759, -59739,-59718,-59697,-59677,-59656,-59635,-59614,-59593, -59572,-59551,-59530,-59509,-59488,-59467,-59446,-59425, -59404,-59382,-59361,-59340,-59318,-59297,-59276,-59254, -59233,-59211,-59189,-59168,-59146,-59125,-59103,-59081, -59059,-59038,-59016,-58994,-58972,-58950,-58928,-58906, -58884,-58862,-58840,-58818,-58795,-58773,-58751,-58729, -58706,-58684,-58662,-58639,-58617,-58594,-58572,-58549, -58527,-58504,-58481,-58459,-58436,-58413,-58390,-58367, -58345,-58322,-58299,-58276,-58253,-58230,-58207,-58183, -58160,-58137,-58114,-58091,-58067,-58044,-58021,-57997, -57974,-57950,-57927,-57903,-57880,-57856,-57833,-57809, -57785,-57762,-57738,-57714,-57690,-57666,-57642,-57618, -57594,-57570,-57546,-57522,-57498,-57474,-57450,-57426, -57402,-57377,-57353,-57329,-57304,-57280,-57255,-57231, -57206,-57182,-57157,-57133,-57108,-57083,-57059,-57034, -57009,-56984,-56959,-56935,-56910,-56885,-56860,-56835, -56810,-56785,-56760,-56734,-56709,-56684,-56659,-56633, -56608,-56583,-56557,-56532,-56507,-56481,-56456,-56430, -56404,-56379,-56353,-56328,-56302,-56276,-56250,-56225, -56199,-56173,-56147,-56121,-56095,-56069,-56043,-56017, -55991,-55965,-55938,-55912,-55886,-55860,-55833,-55807, -55781,-55754,-55728,-55701,-55675,-55648,-55622,-55595, -55569,-55542,-55515,-55489,-55462,-55435,-55408,-55381, -55354,-55327,-55300,-55274,-55246,-55219,-55192,-55165, -55138,-55111,-55084,-55056,-55029,-55002,-54974,-54947, -54920,-54892,-54865,-54837,-54810,-54782,-54755,-54727, -54699,-54672,-54644,-54616,-54588,-54560,-54533,-54505, -54477,-54449,-54421,-54393,-54365,-54337,-54308,-54280, -54252,-54224,-54196,-54167,-54139,-54111,-54082,-54054, -54026,-53997,-53969,-53940,-53911,-53883,-53854,-53826, -53797,-53768,-53739,-53711,-53682,-53653,-53624,-53595, -53566,-53537,-53508,-53479,-53450,-53421,-53392,-53363, -53334,-53304,-53275,-53246,-53216,-53187,-53158,-53128, -53099,-53069,-53040,-53010,-52981,-52951,-52922,-52892, -52862,-52832,-52803,-52773,-52743,-52713,-52683,-52653, -52624,-52594,-52564,-52534,-52503,-52473,-52443,-52413, -52383,-52353,-52322,-52292,-52262,-52231,-52201,-52171, -52140,-52110,-52079,-52049,-52018,-51988,-51957,-51926, -51896,-51865,-51834,-51803,-51773,-51742,-51711,-51680, -51649,-51618,-51587,-51556,-51525,-51494,-51463,-51432, -51401,-51369,-51338,-51307,-51276,-51244,-51213,-51182, -51150,-51119,-51087,-51056,-51024,-50993,-50961,-50929, -50898,-50866,-50834,-50803,-50771,-50739,-50707,-50675, -50644,-50612,-50580,-50548,-50516,-50484,-50452,-50420, -50387,-50355,-50323,-50291,-50259,-50226,-50194,-50162, -50129,-50097,-50065,-50032,-50000,-49967,-49935,-49902, -49869,-49837,-49804,-49771,-49739,-49706,-49673,-49640, -49608,-49575,-49542,-49509,-49476,-49443,-49410,-49377, -49344,-49311,-49278,-49244,-49211,-49178,-49145,-49112, -49078,-49045,-49012,-48978,-48945,-48911,-48878,-48844, -48811,-48777,-48744,-48710,-48676,-48643,-48609,-48575, -48542,-48508,-48474,-48440,-48406,-48372,-48338,-48305, -48271,-48237,-48202,-48168,-48134,-48100,-48066,-48032, -47998,-47963,-47929,-47895,-47860,-47826,-47792,-47757, -47723,-47688,-47654,-47619,-47585,-47550,-47516,-47481, -47446,-47412,-47377,-47342,-47307,-47273,-47238,-47203, -47168,-47133,-47098,-47063,-47028,-46993,-46958,-46923, -46888,-46853,-46818,-46783,-46747,-46712,-46677,-46642, -46606,-46571,-46536,-46500,-46465,-46429,-46394,-46358, -46323,-46287,-46251,-46216,-46180,-46145,-46109,-46073, -46037,-46002,-45966,-45930,-45894,-45858,-45822,-45786, -45750,-45714,-45678,-45642,-45606,-45570,-45534,-45498, -45462,-45425,-45389,-45353,-45316,-45280,-45244,-45207, -45171,-45135,-45098,-45062,-45025,-44989,-44952,-44915, -44879,-44842,-44806,-44769,-44732,-44695,-44659,-44622, -44585,-44548,-44511,-44474,-44437,-44400,-44363,-44326, -44289,-44252,-44215,-44178,-44141,-44104,-44067,-44029, -43992,-43955,-43918,-43880,-43843,-43806,-43768,-43731, -43693,-43656,-43618,-43581,-43543,-43506,-43468,-43430, -43393,-43355,-43317,-43280,-43242,-43204,-43166,-43128, -43091,-43053,-43015,-42977,-42939,-42901,-42863,-42825, -42787,-42749,-42711,-42672,-42634,-42596,-42558,-42520, -42481,-42443,-42405,-42366,-42328,-42290,-42251,-42213, -42174,-42136,-42097,-42059,-42020,-41982,-41943,-41904, -41866,-41827,-41788,-41750,-41711,-41672,-41633,-41595, -41556,-41517,-41478,-41439,-41400,-41361,-41322,-41283, -41244,-41205,-41166,-41127,-41087,-41048,-41009,-40970, -40931,-40891,-40852,-40813,-40773,-40734,-40695,-40655, -40616,-40576,-40537,-40497,-40458,-40418,-40379,-40339, -40299,-40260,-40220,-40180,-40141,-40101,-40061,-40021, -39982,-39942,-39902,-39862,-39822,-39782,-39742,-39702, -39662,-39622,-39582,-39542,-39502,-39462,-39422,-39382, -39341,-39301,-39261,-39221,-39180,-39140,-39100,-39059, -39019,-38979,-38938,-38898,-38857,-38817,-38776,-38736, -38695,-38655,-38614,-38573,-38533,-38492,-38451,-38411, -38370,-38329,-38288,-38248,-38207,-38166,-38125,-38084, -38043,-38002,-37961,-37920,-37879,-37838,-37797,-37756, -37715,-37674,-37633,-37592,-37550,-37509,-37468,-37427, -37386,-37344,-37303,-37262,-37220,-37179,-37137,-37096, -37055,-37013,-36972,-36930,-36889,-36847,-36805,-36764, -36722,-36681,-36639,-36597,-36556,-36514,-36472,-36430, -36388,-36347,-36305,-36263,-36221,-36179,-36137,-36095, -36053,-36011,-35969,-35927,-35885,-35843,-35801,-35759, -35717,-35675,-35633,-35590,-35548,-35506,-35464,-35421, -35379,-35337,-35294,-35252,-35210,-35167,-35125,-35082, -35040,-34997,-34955,-34912,-34870,-34827,-34785,-34742, -34699,-34657,-34614,-34571,-34529,-34486,-34443,-34400, -34358,-34315,-34272,-34229,-34186,-34143,-34100,-34057, -34015,-33972,-33929,-33886,-33843,-33799,-33756,-33713, -33670,-33627,-33584,-33541,-33498,-33454,-33411,-33368, -33325,-33281,-33238,-33195,-33151,-33108,-33065,-33021, -32978,-32934,-32891,-32847,-32804,-32760,-32717,-32673, -32630,-32586,-32542,-32499,-32455,-32411,-32368,-32324, -32280,-32236,-32193,-32149,-32105,-32061,-32017,-31974, -31930,-31886,-31842,-31798,-31754,-31710,-31666,-31622, -31578,-31534,-31490,-31446,-31402,-31357,-31313,-31269, -31225,-31181,-31136,-31092,-31048,-31004,-30959,-30915, -30871,-30826,-30782,-30738,-30693,-30649,-30604,-30560, -30515,-30471,-30426,-30382,-30337,-30293,-30248,-30204, -30159,-30114,-30070,-30025,-29980,-29936,-29891,-29846, -29801,-29757,-29712,-29667,-29622,-29577,-29533,-29488, -29443,-29398,-29353,-29308,-29263,-29218,-29173,-29128, -29083,-29038,-28993,-28948,-28903,-28858,-28812,-28767, -28722,-28677,-28632,-28586,-28541,-28496,-28451,-28405, -28360,-28315,-28269,-28224,-28179,-28133,-28088,-28042, -27997,-27952,-27906,-27861,-27815,-27770,-27724,-27678, -27633,-27587,-27542,-27496,-27450,-27405,-27359,-27313, -27268,-27222,-27176,-27131,-27085,-27039,-26993,-26947, -26902,-26856,-26810,-26764,-26718,-26672,-26626,-26580, -26534,-26488,-26442,-26396,-26350,-26304,-26258,-26212, -26166,-26120,-26074,-26028,-25982,-25936,-25889,-25843, -25797,-25751,-25705,-25658,-25612,-25566,-25520,-25473, -25427,-25381,-25334,-25288,-25241,-25195,-25149,-25102, -25056,-25009,-24963,-24916,-24870,-24823,-24777,-24730, -24684,-24637,-24591,-24544,-24497,-24451,-24404,-24357, -24311,-24264,-24217,-24171,-24124,-24077,-24030,-23984, -23937,-23890,-23843,-23796,-23750,-23703,-23656,-23609, -23562,-23515,-23468,-23421,-23374,-23327,-23280,-23233, -23186,-23139,-23092,-23045,-22998,-22951,-22904,-22857, -22810,-22763,-22716,-22668,-22621,-22574,-22527,-22480, -22432,-22385,-22338,-22291,-22243,-22196,-22149,-22102, -22054,-22007,-21960,-21912,-21865,-21817,-21770,-21723, -21675,-21628,-21580,-21533,-21485,-21438,-21390,-21343, -21295,-21248,-21200,-21153,-21105,-21057,-21010,-20962, -20915,-20867,-20819,-20772,-20724,-20676,-20629,-20581, -20533,-20485,-20438,-20390,-20342,-20294,-20246,-20199, -20151,-20103,-20055,-20007,-19959,-19912,-19864,-19816, -19768,-19720,-19672,-19624,-19576,-19528,-19480,-19432, -19384,-19336,-19288,-19240,-19192,-19144,-19096,-19048, -19000,-18951,-18903,-18855,-18807,-18759,-18711,-18663, -18614,-18566,-18518,-18470,-18421,-18373,-18325,-18277, -18228,-18180,-18132,-18084,-18035,-17987,-17939,-17890, -17842,-17793,-17745,-17697,-17648,-17600,-17551,-17503, -17455,-17406,-17358,-17309,-17261,-17212,-17164,-17115, -17067,-17018,-16970,-16921,-16872,-16824,-16775,-16727, -16678,-16629,-16581,-16532,-16484,-16435,-16386,-16338, -16289,-16240,-16191,-16143,-16094,-16045,-15997,-15948, -15899,-15850,-15802,-15753,-15704,-15655,-15606,-15557, -15509,-15460,-15411,-15362,-15313,-15264,-15215,-15167, -15118,-15069,-15020,-14971,-14922,-14873,-14824,-14775, -14726,-14677,-14628,-14579,-14530,-14481,-14432,-14383, -14334,-14285,-14236,-14187,-14138,-14089,-14040,-13990, -13941,-13892,-13843,-13794,-13745,-13696,-13647,-13597, -13548,-13499,-13450,-13401,-13351,-13302,-13253,-13204, -13154,-13105,-13056,-13007,-12957,-12908,-12859,-12810, -12760,-12711,-12662,-12612,-12563,-12514,-12464,-12415, -12366,-12316,-12267,-12217,-12168,-12119,-12069,-12020, -11970,-11921,-11872,-11822,-11773,-11723,-11674,-11624, -11575,-11525,-11476,-11426,-11377,-11327,-11278,-11228, -11179,-11129,-11080,-11030,-10981,-10931,-10882,-10832, -10782,-10733,-10683,-10634,-10584,-10534,-10485,-10435, -10386,-10336,-10286,-10237,-10187,-10137,-10088,-10038, -9988,-9939,-9889,-9839,-9790,-9740,-9690,-9640, -9591,-9541,-9491,-9442,-9392,-9342,-9292,-9243, -9193,-9143,-9093,-9043,-8994,-8944,-8894,-8844, -8794,-8745,-8695,-8645,-8595,-8545,-8496,-8446, -8396,-8346,-8296,-8246,-8196,-8147,-8097,-8047, -7997,-7947,-7897,-7847,-7797,-7747,-7697,-7648, -7598,-7548,-7498,-7448,-7398,-7348,-7298,-7248, -7198,-7148,-7098,-7048,-6998,-6948,-6898,-6848, -6798,-6748,-6698,-6648,-6598,-6548,-6498,-6448, -6398,-6348,-6298,-6248,-6198,-6148,-6098,-6048, -5998,-5948,-5898,-5848,-5798,-5747,-5697,-5647, -5597,-5547,-5497,-5447,-5397,-5347,-5297,-5247, -5197,-5146,-5096,-5046,-4996,-4946,-4896,-4846, -4796,-4745,-4695,-4645,-4595,-4545,-4495,-4445, -4394,-4344,-4294,-4244,-4194,-4144,-4093,-4043, -3993,-3943,-3893,-3843,-3792,-3742,-3692,-3642, -3592,-3541,-3491,-3441,-3391,-3341,-3291,-3240, -3190,-3140,-3090,-3039,-2989,-2939,-2889,-2839, -2788,-2738,-2688,-2638,-2588,-2537,-2487,-2437, -2387,-2336,-2286,-2236,-2186,-2135,-2085,-2035, -1985,-1934,-1884,-1834,-1784,-1733,-1683,-1633, -1583,-1532,-1482,-1432,-1382,-1331,-1281,-1231, -1181,-1130,-1080,-1030,-980,-929,-879,-829, -779,-728,-678,-628,-578,-527,-477,-427, -376,-326,-276,-226,-175,-125,-75,-25, 25,75,125,175,226,276,326,376, 427,477,527,578,628,678,728,779, 829,879,929,980,1030,1080,1130,1181, 1231,1281,1331,1382,1432,1482,1532,1583, 1633,1683,1733,1784,1834,1884,1934,1985, 2035,2085,2135,2186,2236,2286,2336,2387, 2437,2487,2537,2587,2638,2688,2738,2788, 2839,2889,2939,2989,3039,3090,3140,3190, 3240,3291,3341,3391,3441,3491,3542,3592, 3642,3692,3742,3792,3843,3893,3943,3993, 4043,4093,4144,4194,4244,4294,4344,4394, 4445,4495,4545,4595,4645,4695,4745,4796, 4846,4896,4946,4996,5046,5096,5146,5197, 5247,5297,5347,5397,5447,5497,5547,5597, 5647,5697,5747,5798,5848,5898,5948,5998, 6048,6098,6148,6198,6248,6298,6348,6398, 6448,6498,6548,6598,6648,6698,6748,6798, 6848,6898,6948,6998,7048,7098,7148,7198, 7248,7298,7348,7398,7448,7498,7548,7598, 7648,7697,7747,7797,7847,7897,7947,7997, 8047,8097,8147,8196,8246,8296,8346,8396, 8446,8496,8545,8595,8645,8695,8745,8794, 8844,8894,8944,8994,9043,9093,9143,9193, 9243,9292,9342,9392,9442,9491,9541,9591, 9640,9690,9740,9790,9839,9889,9939,9988, 10038,10088,10137,10187,10237,10286,10336,10386, 10435,10485,10534,10584,10634,10683,10733,10782, 10832,10882,10931,10981,11030,11080,11129,11179, 11228,11278,11327,11377,11426,11476,11525,11575, 11624,11674,11723,11773,11822,11872,11921,11970, 12020,12069,12119,12168,12218,12267,12316,12366, 12415,12464,12514,12563,12612,12662,12711,12760, 12810,12859,12908,12957,13007,13056,13105,13154, 13204,13253,13302,13351,13401,13450,13499,13548, 13597,13647,13696,13745,13794,13843,13892,13941, 13990,14040,14089,14138,14187,14236,14285,14334, 14383,14432,14481,14530,14579,14628,14677,14726, 14775,14824,14873,14922,14971,15020,15069,15118, 15167,15215,15264,15313,15362,15411,15460,15509, 15557,15606,15655,15704,15753,15802,15850,15899, 15948,15997,16045,16094,16143,16191,16240,16289, 16338,16386,16435,16484,16532,16581,16629,16678, 16727,16775,16824,16872,16921,16970,17018,17067, 17115,17164,17212,17261,17309,17358,17406,17455, 17503,17551,17600,17648,17697,17745,17793,17842, 17890,17939,17987,18035,18084,18132,18180,18228, 18277,18325,18373,18421,18470,18518,18566,18614, 18663,18711,18759,18807,18855,18903,18951,19000, 19048,19096,19144,19192,19240,19288,19336,19384, 19432,19480,19528,19576,19624,19672,19720,19768, 19816,19864,19912,19959,20007,20055,20103,20151, 20199,20246,20294,20342,20390,20438,20485,20533, 20581,20629,20676,20724,20772,20819,20867,20915, 20962,21010,21057,21105,21153,21200,21248,21295, 21343,21390,21438,21485,21533,21580,21628,21675, 21723,21770,21817,21865,21912,21960,22007,22054, 22102,22149,22196,22243,22291,22338,22385,22432, 22480,22527,22574,22621,22668,22716,22763,22810, 22857,22904,22951,22998,23045,23092,23139,23186, 23233,23280,23327,23374,23421,23468,23515,23562, 23609,23656,23703,23750,23796,23843,23890,23937, 23984,24030,24077,24124,24171,24217,24264,24311, 24357,24404,24451,24497,24544,24591,24637,24684, 24730,24777,24823,24870,24916,24963,25009,25056, 25102,25149,25195,25241,25288,25334,25381,25427, 25473,25520,25566,25612,25658,25705,25751,25797, 25843,25889,25936,25982,26028,26074,26120,26166, 26212,26258,26304,26350,26396,26442,26488,26534, 26580,26626,26672,26718,26764,26810,26856,26902, 26947,26993,27039,27085,27131,27176,27222,27268, 27313,27359,27405,27450,27496,27542,27587,27633, 27678,27724,27770,27815,27861,27906,27952,27997, 28042,28088,28133,28179,28224,28269,28315,28360, 28405,28451,28496,28541,28586,28632,28677,28722, 28767,28812,28858,28903,28948,28993,29038,29083, 29128,29173,29218,29263,29308,29353,29398,29443, 29488,29533,29577,29622,29667,29712,29757,29801, 29846,29891,29936,29980,30025,30070,30114,30159, 30204,30248,30293,30337,30382,30427,30471,30516, 30560,30604,30649,30693,30738,30782,30826,30871, 30915,30959,31004,31048,31092,31136,31181,31225, 31269,31313,31357,31402,31446,31490,31534,31578, 31622,31666,31710,31754,31798,31842,31886,31930, 31974,32017,32061,32105,32149,32193,32236,32280, 32324,32368,32411,32455,32499,32542,32586,32630, 32673,32717,32760,32804,32847,32891,32934,32978, 33021,33065,33108,33151,33195,33238,33281,33325, 33368,33411,33454,33498,33541,33584,33627,33670, 33713,33756,33799,33843,33886,33929,33972,34015, 34057,34100,34143,34186,34229,34272,34315,34358, 34400,34443,34486,34529,34571,34614,34657,34699, 34742,34785,34827,34870,34912,34955,34997,35040, 35082,35125,35167,35210,35252,35294,35337,35379, 35421,35464,35506,35548,35590,35633,35675,35717, 35759,35801,35843,35885,35927,35969,36011,36053, 36095,36137,36179,36221,36263,36305,36347,36388, 36430,36472,36514,36556,36597,36639,36681,36722, 36764,36805,36847,36889,36930,36972,37013,37055, 37096,37137,37179,37220,37262,37303,37344,37386, 37427,37468,37509,37551,37592,37633,37674,37715, 37756,37797,37838,37879,37920,37961,38002,38043, 38084,38125,38166,38207,38248,38288,38329,38370, 38411,38451,38492,38533,38573,38614,38655,38695, 38736,38776,38817,38857,38898,38938,38979,39019, 39059,39100,39140,39180,39221,39261,39301,39341, 39382,39422,39462,39502,39542,39582,39622,39662, 39702,39742,39782,39822,39862,39902,39942,39982, 40021,40061,40101,40141,40180,40220,40260,40299, 40339,40379,40418,40458,40497,40537,40576,40616, 40655,40695,40734,40773,40813,40852,40891,40931, 40970,41009,41048,41087,41127,41166,41205,41244, 41283,41322,41361,41400,41439,41478,41517,41556, 41595,41633,41672,41711,41750,41788,41827,41866, 41904,41943,41982,42020,42059,42097,42136,42174, 42213,42251,42290,42328,42366,42405,42443,42481, 42520,42558,42596,42634,42672,42711,42749,42787, 42825,42863,42901,42939,42977,43015,43053,43091, 43128,43166,43204,43242,43280,43317,43355,43393, 43430,43468,43506,43543,43581,43618,43656,43693, 43731,43768,43806,43843,43880,43918,43955,43992, 44029,44067,44104,44141,44178,44215,44252,44289, 44326,44363,44400,44437,44474,44511,44548,44585, 44622,44659,44695,44732,44769,44806,44842,44879, 44915,44952,44989,45025,45062,45098,45135,45171, 45207,45244,45280,45316,45353,45389,45425,45462, 45498,45534,45570,45606,45642,45678,45714,45750, 45786,45822,45858,45894,45930,45966,46002,46037, 46073,46109,46145,46180,46216,46252,46287,46323, 46358,46394,46429,46465,46500,46536,46571,46606, 46642,46677,46712,46747,46783,46818,46853,46888, 46923,46958,46993,47028,47063,47098,47133,47168, 47203,47238,47273,47308,47342,47377,47412,47446, 47481,47516,47550,47585,47619,47654,47688,47723, 47757,47792,47826,47861,47895,47929,47963,47998, 48032,48066,48100,48134,48168,48202,48237,48271, 48305,48338,48372,48406,48440,48474,48508,48542, 48575,48609,48643,48676,48710,48744,48777,48811, 48844,48878,48911,48945,48978,49012,49045,49078, 49112,49145,49178,49211,49244,49278,49311,49344, 49377,49410,49443,49476,49509,49542,49575,49608, 49640,49673,49706,49739,49771,49804,49837,49869, 49902,49935,49967,50000,50032,50064,50097,50129, 50162,50194,50226,50259,50291,50323,50355,50387, 50420,50452,50484,50516,50548,50580,50612,50644, 50675,50707,50739,50771,50803,50834,50866,50898, 50929,50961,50993,51024,51056,51087,51119,51150, 51182,51213,51244,51276,51307,51338,51369,51401, 51432,51463,51494,51525,51556,51587,51618,51649, 51680,51711,51742,51773,51803,51834,51865,51896, 51926,51957,51988,52018,52049,52079,52110,52140, 52171,52201,52231,52262,52292,52322,52353,52383, 52413,52443,52473,52503,52534,52564,52594,52624, 52653,52683,52713,52743,52773,52803,52832,52862, 52892,52922,52951,52981,53010,53040,53069,53099, 53128,53158,53187,53216,53246,53275,53304,53334, 53363,53392,53421,53450,53479,53508,53537,53566, 53595,53624,53653,53682,53711,53739,53768,53797, 53826,53854,53883,53912,53940,53969,53997,54026, 54054,54082,54111,54139,54167,54196,54224,54252, 54280,54309,54337,54365,54393,54421,54449,54477, 54505,54533,54560,54588,54616,54644,54672,54699, 54727,54755,54782,54810,54837,54865,54892,54920, 54947,54974,55002,55029,55056,55084,55111,55138, 55165,55192,55219,55246,55274,55300,55327,55354, 55381,55408,55435,55462,55489,55515,55542,55569, 55595,55622,55648,55675,55701,55728,55754,55781, 55807,55833,55860,55886,55912,55938,55965,55991, 56017,56043,56069,56095,56121,56147,56173,56199, 56225,56250,56276,56302,56328,56353,56379,56404, 56430,56456,56481,56507,56532,56557,56583,56608, 56633,56659,56684,56709,56734,56760,56785,56810, 56835,56860,56885,56910,56935,56959,56984,57009, 57034,57059,57083,57108,57133,57157,57182,57206, 57231,57255,57280,57304,57329,57353,57377,57402, 57426,57450,57474,57498,57522,57546,57570,57594, 57618,57642,57666,57690,57714,57738,57762,57785, 57809,57833,57856,57880,57903,57927,57950,57974, 57997,58021,58044,58067,58091,58114,58137,58160, 58183,58207,58230,58253,58276,58299,58322,58345, 58367,58390,58413,58436,58459,58481,58504,58527, 58549,58572,58594,58617,58639,58662,58684,58706, 58729,58751,58773,58795,58818,58840,58862,58884, 58906,58928,58950,58972,58994,59016,59038,59059, 59081,59103,59125,59146,59168,59190,59211,59233, 59254,59276,59297,59318,59340,59361,59382,59404, 59425,59446,59467,59488,59509,59530,59551,59572, 59593,59614,59635,59656,59677,59697,59718,59739, 59759,59780,59801,59821,59842,59862,59883,59903, 59923,59944,59964,59984,60004,60025,60045,60065, 60085,60105,60125,60145,60165,60185,60205,60225, 60244,60264,60284,60304,60323,60343,60363,60382, 60402,60421,60441,60460,60479,60499,60518,60537, 60556,60576,60595,60614,60633,60652,60671,60690, 60709,60728,60747,60766,60785,60803,60822,60841, 60859,60878,60897,60915,60934,60952,60971,60989, 61007,61026,61044,61062,61081,61099,61117,61135, 61153,61171,61189,61207,61225,61243,61261,61279, 61297,61314,61332,61350,61367,61385,61403,61420, 61438,61455,61473,61490,61507,61525,61542,61559, 61577,61594,61611,61628,61645,61662,61679,61696, 61713,61730,61747,61764,61780,61797,61814,61831, 61847,61864,61880,61897,61913,61930,61946,61963, 61979,61995,62012,62028,62044,62060,62076,62092, 62108,62125,62141,62156,62172,62188,62204,62220, 62236,62251,62267,62283,62298,62314,62329,62345, 62360,62376,62391,62407,62422,62437,62453,62468, 62483,62498,62513,62528,62543,62558,62573,62588, 62603,62618,62633,62648,62662,62677,62692,62706, 62721,62735,62750,62764,62779,62793,62808,62822, 62836,62850,62865,62879,62893,62907,62921,62935, 62949,62963,62977,62991,63005,63019,63032,63046, 63060,63074,63087,63101,63114,63128,63141,63155, 63168,63182,63195,63208,63221,63235,63248,63261, 63274,63287,63300,63313,63326,63339,63352,63365, 63378,63390,63403,63416,63429,63441,63454,63466, 63479,63491,63504,63516,63528,63541,63553,63565, 63578,63590,63602,63614,63626,63638,63650,63662, 63674,63686,63698,63709,63721,63733,63745,63756, 63768,63779,63791,63803,63814,63825,63837,63848, 63859,63871,63882,63893,63904,63915,63927,63938, 63949,63960,63971,63981,63992,64003,64014,64025, 64035,64046,64057,64067,64078,64088,64099,64109, 64120,64130,64140,64151,64161,64171,64181,64192, 64202,64212,64222,64232,64242,64252,64261,64271, 64281,64291,64301,64310,64320,64330,64339,64349, 64358,64368,64377,64387,64396,64405,64414,64424, 64433,64442,64451,64460,64469,64478,64487,64496, 64505,64514,64523,64532,64540,64549,64558,64566, 64575,64584,64592,64600,64609,64617,64626,64634, 64642,64651,64659,64667,64675,64683,64691,64699, 64707,64715,64723,64731,64739,64747,64754,64762, 64770,64777,64785,64793,64800,64808,64815,64822, 64830,64837,64844,64852,64859,64866,64873,64880, 64887,64895,64902,64908,64915,64922,64929,64936, 64943,64949,64956,64963,64969,64976,64982,64989, 64995,65002,65008,65015,65021,65027,65033,65040, 65046,65052,65058,65064,65070,65076,65082,65088, 65094,65099,65105,65111,65117,65122,65128,65133, 65139,65144,65150,65155,65161,65166,65171,65177, 65182,65187,65192,65197,65202,65207,65212,65217, 65222,65227,65232,65237,65242,65246,65251,65256, 65260,65265,65270,65274,65279,65283,65287,65292, 65296,65300,65305,65309,65313,65317,65321,65325, 65329,65333,65337,65341,65345,65349,65352,65356, 65360,65363,65367,65371,65374,65378,65381,65385, 65388,65391,65395,65398,65401,65404,65408,65411, 65414,65417,65420,65423,65426,65429,65431,65434, 65437,65440,65442,65445,65448,65450,65453,65455, 65458,65460,65463,65465,65467,65470,65472,65474, 65476,65478,65480,65482,65484,65486,65488,65490, 65492,65494,65496,65497,65499,65501,65502,65504, 65505,65507,65508,65510,65511,65513,65514,65515, 65516,65518,65519,65520,65521,65522,65523,65524, 65525,65526,65527,65527,65528,65529,65530,65530, 65531,65531,65532,65532,65533,65533,65534,65534, 65534,65535,65535,65535,65535,65535,65535,65535 }; const fixed_t *finecosine = &finesine[FINEANGLES/4]; const angle_t tantoangle[2049] = { 0,333772,667544,1001315,1335086,1668857,2002626,2336395, 2670163,3003929,3337694,3671457,4005219,4338979,4672736,5006492, 5340245,5673995,6007743,6341488,6675230,7008968,7342704,7676435, 8010164,8343888,8677609,9011325,9345037,9678744,10012447,10346145, 10679838,11013526,11347209,11680887,12014558,12348225,12681885,13015539, 13349187,13682829,14016464,14350092,14683714,15017328,15350936,15684536, 16018129,16351714,16685291,17018860,17352422,17685974,18019518,18353054, 18686582,19020100,19353610,19687110,20020600,20354080,20687552,21021014, 21354466,21687906,22021338,22354758,22688168,23021568,23354956,23688332, 24021698,24355052,24688396,25021726,25355046,25688352,26021648,26354930, 26688200,27021456,27354702,27687932,28021150,28354356,28687548,29020724, 29353888,29687038,30020174,30353296,30686404,31019496,31352574,31685636, 32018684,32351718,32684734,33017736,33350722,33683692,34016648,34349584, 34682508,35015412,35348300,35681172,36014028,36346868,36679688,37012492, 37345276,37678044,38010792,38343524,38676240,39008936,39341612,39674272, 40006912,40339532,40672132,41004716,41337276,41669820,42002344,42334848, 42667332,42999796,43332236,43664660,43997060,44329444,44661800,44994140, 45326456,45658752,45991028,46323280,46655512,46987720,47319908,47652072, 47984212,48316332,48648428,48980500,49312548,49644576,49976580,50308556, 50640512,50972444,51304352,51636236,51968096,52299928,52631740,52963524, 53295284,53627020,53958728,54290412,54622068,54953704,55285308,55616888, 55948444,56279972,56611472,56942948,57274396,57605816,57937212,58268576, 58599916,58931228,59262512,59593768,59924992,60256192,60587364,60918508, 61249620,61580704,61911760,62242788,62573788,62904756,63235692,63566604, 63897480,64228332,64559148,64889940,65220696,65551424,65882120,66212788, 66543420,66874024,67204600,67535136,67865648,68196120,68526568,68856984, 69187360,69517712,69848024,70178304,70508560,70838776,71168960,71499112, 71829224,72159312,72489360,72819376,73149360,73479304,73809216,74139096, 74468936,74798744,75128520,75458264,75787968,76117632,76447264,76776864, 77106424,77435952,77765440,78094888,78424304,78753688,79083032,79412336, 79741608,80070840,80400032,80729192,81058312,81387392,81716432,82045440, 82374408,82703336,83032224,83361080,83689896,84018664,84347400,84676096, 85004760,85333376,85661952,85990488,86318984,86647448,86975864,87304240, 87632576,87960872,88289128,88617344,88945520,89273648,89601736,89929792, 90257792,90585760,90913688,91241568,91569408,91897200,92224960,92552672, 92880336,93207968,93535552,93863088,94190584,94518040,94845448,95172816, 95500136,95827416,96154648,96481832,96808976,97136080,97463136,97790144, 98117112,98444032,98770904,99097736,99424520,99751256,100077944,100404592, 100731192,101057744,101384248,101710712,102037128,102363488,102689808,103016080, 103342312,103668488,103994616,104320696,104646736,104972720,105298656,105624552, 105950392,106276184,106601928,106927624,107253272,107578872,107904416,108229920, 108555368,108880768,109206120,109531416,109856664,110181872,110507016,110832120, 111157168,111482168,111807112,112132008,112456856,112781648,113106392,113431080, 113755720,114080312,114404848,114729328,115053760,115378136,115702464,116026744, 116350960,116675128,116999248,117323312,117647320,117971272,118295176,118619024, 118942816,119266560,119590248,119913880,120237456,120560984,120884456,121207864, 121531224,121854528,122177784,122500976,122824112,123147200,123470224,123793200, 124116120,124438976,124761784,125084528,125407224,125729856,126052432,126374960, 126697424,127019832,127342184,127664472,127986712,128308888,128631008,128953072, 129275080,129597024,129918912,130240744,130562520,130884232,131205888,131527480, 131849016,132170496,132491912,132813272,133134576,133455816,133776992,134098120, 134419184,134740176,135061120,135382000,135702816,136023584,136344272,136664912, 136985488,137306016,137626464,137946864,138267184,138587456,138907664,139227808, 139547904,139867920,140187888,140507776,140827616,141147392,141467104,141786752, 142106336,142425856,142745312,143064720,143384048,143703312,144022512,144341664, 144660736,144979744,145298704,145617584,145936400,146255168,146573856,146892480, 147211040,147529536,147847968,148166336,148484640,148802880,149121056,149439152, 149757200,150075168,150393072,150710912,151028688,151346400,151664048,151981616, 152299136,152616576,152933952,153251264,153568496,153885680,154202784,154519824, 154836784,155153696,155470528,155787296,156104000,156420624,156737200,157053696, 157370112,157686480,158002768,158318976,158635136,158951216,159267232,159583168, 159899040,160214848,160530592,160846256,161161840,161477376,161792832,162108208, 162423520,162738768,163053952,163369040,163684080,163999040,164313936,164628752, 164943504,165258176,165572784,165887312,166201776,166516160,166830480,167144736, 167458912,167773008,168087040,168400992,168714880,169028688,169342432,169656096, 169969696,170283216,170596672,170910032,171223344,171536576,171849728,172162800, 172475808,172788736,173101600,173414384,173727104,174039728,174352288,174664784, 174977200,175289536,175601792,175913984,176226096,176538144,176850096,177161984, 177473792,177785536,178097200,178408784,178720288,179031728,179343088,179654368, 179965568,180276704,180587744,180898720,181209616,181520448,181831184,182141856, 182452448,182762960,183073408,183383760,183694048,184004240,184314368,184624416, 184934400,185244288,185554096,185863840,186173504,186483072,186792576,187102000, 187411344,187720608,188029808,188338912,188647936,188956896,189265760,189574560, 189883264,190191904,190500448,190808928,191117312,191425632,191733872,192042016, 192350096,192658096,192966000,193273840,193581584,193889264,194196848,194504352, 194811792,195119136,195426400,195733584,196040688,196347712,196654656,196961520, 197268304,197574992,197881616,198188144,198494592,198800960,199107248,199413456, 199719584,200025616,200331584,200637456,200943248,201248960,201554576,201860128, 202165584,202470960,202776256,203081456,203386592,203691632,203996592,204301472, 204606256,204910976,205215600,205520144,205824592,206128960,206433248,206737456, 207041584,207345616,207649568,207953424,208257216,208560912,208864512,209168048, 209471488,209774832,210078112,210381296,210684384,210987408,211290336,211593184, 211895936,212198608,212501184,212803680,213106096,213408432,213710672,214012816, 214314880,214616864,214918768,215220576,215522288,215823920,216125472,216426928, 216728304,217029584,217330784,217631904,217932928,218233856,218534704,218835472, 219136144,219436720,219737216,220037632,220337952,220638192,220938336,221238384, 221538352,221838240,222138032,222437728,222737344,223036880,223336304,223635664, 223934912,224234096,224533168,224832160,225131072,225429872,225728608,226027232, 226325776,226624240,226922608,227220880,227519056,227817152,228115168,228413088, 228710912,229008640,229306288,229603840,229901312,230198688,230495968,230793152, 231090256,231387280,231684192,231981024,232277760,232574416,232870960,233167440, 233463808,233760096,234056288,234352384,234648384,234944304,235240128,235535872, 235831504,236127056,236422512,236717888,237013152,237308336,237603424,237898416, 238193328,238488144,238782864,239077488,239372016,239666464,239960816,240255072, 240549232,240843312,241137280,241431168,241724960,242018656,242312256,242605776, 242899200,243192512,243485744,243778896,244071936,244364880,244657744,244950496, 245243168,245535744,245828224,246120608,246412912,246705104,246997216,247289216, 247581136,247872960,248164688,248456320,248747856,249039296,249330640,249621904, 249913056,250204128,250495088,250785968,251076736,251367424,251658016,251948512, 252238912,252529200,252819408,253109520,253399536,253689456,253979280,254269008, 254558640,254848176,255137632,255426976,255716224,256005376,256294432,256583392, 256872256,257161024,257449696,257738272,258026752,258315136,258603424,258891600, 259179696,259467696,259755600,260043392,260331104,260618704,260906224,261193632, 261480960,261768176,262055296,262342320,262629248,262916080,263202816,263489456, 263776000,264062432,264348784,264635024,264921168,265207216,265493168,265779024, 266064784,266350448,266636000,266921472,267206832,267492096,267777264,268062336, 268347312,268632192,268916960,269201632,269486208,269770688,270055072,270339360, 270623552,270907616,271191616,271475488,271759296,272042976,272326560,272610048, 272893440,273176736,273459936,273743040,274026048,274308928,274591744,274874432, 275157024,275439520,275721920,276004224,276286432,276568512,276850528,277132416, 277414240,277695936,277977536,278259040,278540448,278821728,279102944,279384032, 279665056,279945952,280226752,280507456,280788064,281068544,281348960,281629248, 281909472,282189568,282469568,282749440,283029248,283308960,283588544,283868032, 284147424,284426720,284705920,284985024,285264000,285542912,285821696,286100384, 286378976,286657440,286935840,287214112,287492320,287770400,288048384,288326240, 288604032,288881696,289159264,289436768,289714112,289991392,290268576,290545632, 290822592,291099456,291376224,291652896,291929440,292205888,292482272,292758528, 293034656,293310720,293586656,293862496,294138240,294413888,294689440,294964864, 295240192,295515424,295790560,296065600,296340512,296615360,296890080,297164704, 297439200,297713632,297987936,298262144,298536256,298810240,299084160,299357952, 299631648,299905248,300178720,300452128,300725408,300998592,301271680,301544640, 301817536,302090304,302362976,302635520,302908000,303180352,303452608,303724768, 303996800,304268768,304540608,304812320,305083968,305355520,305626944,305898272, 306169472,306440608,306711616,306982528,307253344,307524064,307794656,308065152, 308335552,308605856,308876032,309146112,309416096,309685984,309955744,310225408, 310494976,310764448,311033824,311303072,311572224,311841280,312110208,312379040, 312647776,312916416,313184960,313453376,313721696,313989920,314258016,314526016, 314793920,315061728,315329408,315597024,315864512,316131872,316399168,316666336, 316933408,317200384,317467232,317733984,318000640,318267200,318533632,318799968, 319066208,319332352,319598368,319864288,320130112,320395808,320661408,320926912, 321192320,321457632,321722816,321987904,322252864,322517760,322782528,323047200, 323311744,323576192,323840544,324104800,324368928,324632992,324896928,325160736, 325424448,325688096,325951584,326215008,326478304,326741504,327004608,327267584, 327530464,327793248,328055904,328318496,328580960,328843296,329105568,329367712, 329629760,329891680,330153536,330415264,330676864,330938400,331199808,331461120, 331722304,331983392,332244384,332505280,332766048,333026752,333287296,333547776, 333808128,334068384,334328544,334588576,334848512,335108352,335368064,335627712, 335887200,336146624,336405920,336665120,336924224,337183200,337442112,337700864, 337959552,338218112,338476576,338734944,338993184,339251328,339509376,339767296, 340025120,340282848,340540480,340797984,341055392,341312704,341569888,341826976, 342083968,342340832,342597600,342854272,343110848,343367296,343623648,343879904, 344136032,344392064,344648000,344903808,345159520,345415136,345670656,345926048, 346181344,346436512,346691616,346946592,347201440,347456224,347710880,347965440, 348219872,348474208,348728448,348982592,349236608,349490528,349744320,349998048, 350251648,350505152,350758528,351011808,351264992,351518048,351771040,352023872, 352276640,352529280,352781824,353034272,353286592,353538816,353790944,354042944, 354294880,354546656,354798368,355049952,355301440,355552800,355804096,356055264, 356306304,356557280,356808128,357058848,357309504,357560032,357810464,358060768, 358311008,358561088,358811104,359060992,359310784,359560480,359810048,360059520, 360308896,360558144,360807296,361056352,361305312,361554144,361802880,362051488, 362300032,362548448,362796736,363044960,363293056,363541024,363788928,364036704, 364284384,364531936,364779392,365026752,365274016,365521152,365768192,366015136, 366261952,366508672,366755296,367001792,367248192,367494496,367740704,367986784, 368232768,368478656,368724416,368970080,369215648,369461088,369706432,369951680, 370196800,370441824,370686752,370931584,371176288,371420896,371665408,371909792, 372154080,372398272,372642336,372886304,373130176,373373952,373617600,373861152, 374104608,374347936,374591168,374834304,375077312,375320224,375563040,375805760, 376048352,376290848,376533248,376775520,377017696,377259776,377501728,377743584, 377985344,378227008,378468544,378709984,378951328,379192544,379433664,379674688, 379915584,380156416,380397088,380637696,380878176,381118560,381358848,381599040, 381839104,382079072,382318912,382558656,382798304,383037856,383277280,383516640, 383755840,383994976,384233984,384472896,384711712,384950400,385188992,385427488, 385665888,385904160,386142336,386380384,386618368,386856224,387093984,387331616, 387569152,387806592,388043936,388281152,388518272,388755296,388992224,389229024, 389465728,389702336,389938816,390175200,390411488,390647680,390883744,391119712, 391355584,391591328,391826976,392062528,392297984,392533312,392768544,393003680, 393238720,393473632,393708448,393943168,394177760,394412256,394646656,394880960, 395115136,395349216,395583200,395817088,396050848,396284512,396518080,396751520, 396984864,397218112,397451264,397684288,397917248,398150080,398382784,398615424, 398847936,399080320,399312640,399544832,399776928,400008928,400240832,400472608, 400704288,400935872,401167328,401398720,401629984,401861120,402092192,402323136, 402553984,402784736,403015360,403245888,403476320,403706656,403936896,404167008, 404397024,404626944,404856736,405086432,405316032,405545536,405774912,406004224, 406233408,406462464,406691456,406920320,407149088,407377760,407606336,407834784, 408063136,408291392,408519520,408747584,408975520,409203360,409431072,409658720, 409886240,410113664,410340992,410568192,410795296,411022304,411249216,411476032, 411702720,411929312,412155808,412382176,412608480,412834656,413060736,413286720, 413512576,413738336,413964000,414189568,414415040,414640384,414865632,415090784, 415315840,415540800,415765632,415990368,416215008,416439552,416663968,416888288, 417112512,417336640,417560672,417784576,418008384,418232096,418455712,418679200, 418902624,419125920,419349120,419572192,419795200,420018080,420240864,420463552, 420686144,420908608,421130976,421353280,421575424,421797504,422019488,422241344, 422463104,422684768,422906336,423127776,423349120,423570400,423791520,424012576, 424233536,424454368,424675104,424895744,425116288,425336736,425557056,425777280, 425997408,426217440,426437376,426657184,426876928,427096544,427316064,427535488, 427754784,427974016,428193120,428412128,428631040,428849856,429068544,429287168, 429505664,429724064,429942368,430160576,430378656,430596672,430814560,431032352, 431250048,431467616,431685120,431902496,432119808,432336992,432554080,432771040, 432987936,433204736,433421408,433637984,433854464,434070848,434287104,434503296, 434719360,434935360,435151232,435367008,435582656,435798240,436013696,436229088, 436444352,436659520,436874592,437089568,437304416,437519200,437733856,437948416, 438162880,438377248,438591520,438805696,439019744,439233728,439447584,439661344, 439875008,440088576,440302048,440515392,440728672,440941824,441154880,441367872, 441580736,441793472,442006144,442218720,442431168,442643552,442855808,443067968, 443280032,443492000,443703872,443915648,444127296,444338880,444550336,444761696, 444972992,445184160,445395232,445606176,445817056,446027840,446238496,446449088, 446659552,446869920,447080192,447290400,447500448,447710432,447920320,448130112, 448339776,448549376,448758848,448968224,449177536,449386720,449595808,449804800, 450013664,450222464,450431168,450639776,450848256,451056640,451264960,451473152, 451681248,451889248,452097152,452304960,452512672,452720288,452927808,453135232, 453342528,453549760,453756864,453963904,454170816,454377632,454584384,454791008, 454997536,455203968,455410304,455616544,455822688,456028704,456234656,456440512, 456646240,456851904,457057472,457262912,457468256,457673536,457878688,458083744, 458288736,458493600,458698368,458903040,459107616,459312096,459516480,459720768, 459924960,460129056,460333056,460536960,460740736,460944448,461148064,461351584, 461554976,461758304,461961536,462164640,462367680,462570592,462773440,462976160, 463178816,463381344,463583776,463786144,463988384,464190560,464392608,464594560, 464796448,464998208,465199872,465401472,465602944,465804320,466005600,466206816, 466407904,466608896,466809824,467010624,467211328,467411936,467612480,467812896, 468013216,468213440,468413600,468613632,468813568,469013440,469213184,469412832, 469612416,469811872,470011232,470210528,470409696,470608800,470807776,471006688, 471205472,471404192,471602784,471801312,471999712,472198048,472396288,472594400, 472792448,472990400,473188256,473385984,473583648,473781216,473978688,474176064, 474373344,474570528,474767616,474964608,475161504,475358336,475555040,475751648, 475948192,476144608,476340928,476537184,476733312,476929376,477125344,477321184, 477516960,477712640,477908224,478103712,478299104,478494400,478689600,478884704, 479079744,479274656,479469504,479664224,479858880,480053408,480247872,480442240, 480636512,480830656,481024736,481218752,481412640,481606432,481800128,481993760, 482187264,482380704,482574016,482767264,482960416,483153472,483346432,483539296, 483732064,483924768,484117344,484309856,484502240,484694560,484886784,485078912, 485270944,485462880,485654720,485846464,486038144,486229696,486421184,486612576, 486803840,486995040,487186176,487377184,487568096,487758912,487949664,488140320, 488330880,488521312,488711712,488901984,489092160,489282240,489472256,489662176, 489851968,490041696,490231328,490420896,490610336,490799712,490988960,491178144, 491367232,491556224,491745120,491933920,492122656,492311264,492499808,492688256, 492876608,493064864,493253056,493441120,493629120,493817024,494004832,494192544, 494380160,494567712,494755136,494942496,495129760,495316928,495504000,495691008, 495877888,496064704,496251424,496438048,496624608,496811040,496997408,497183680, 497369856,497555936,497741920,497927840,498113632,498299360,498484992,498670560, 498856000,499041376,499226656,499411840,499596928,499781920,499966848,500151680, 500336416,500521056,500705600,500890080,501074464,501258752,501442944,501627040, 501811072,501995008,502178848,502362592,502546240,502729824,502913312,503096704, 503280000,503463232,503646368,503829408,504012352,504195200,504377984,504560672, 504743264,504925760,505108192,505290496,505472736,505654912,505836960,506018944, 506200832,506382624,506564320,506745952,506927488,507108928,507290272,507471552, 507652736,507833824,508014816,508195744,508376576,508557312,508737952,508918528, 509099008,509279392,509459680,509639904,509820032,510000064,510180000,510359872, 510539648,510719328,510898944,511078432,511257856,511437216,511616448,511795616, 511974688,512153664,512332576,512511392,512690112,512868768,513047296,513225792, 513404160,513582432,513760640,513938784,514116800,514294752,514472608,514650368, 514828064,515005664,515183168,515360608,515537952,515715200,515892352,516069440, 516246432,516423328,516600160,516776896,516953536,517130112,517306592,517482976, 517659264,517835488,518011616,518187680,518363648,518539520,518715296,518891008, 519066624,519242144,519417600,519592960,519768256,519943424,520118528,520293568, 520468480,520643328,520818112,520992800,521167392,521341888,521516320,521690656, 521864896,522039072,522213152,522387168,522561056,522734912,522908640,523082304, 523255872,523429376,523602784,523776096,523949312,524122464,524295552,524468512, 524641440,524814240,524986976,525159616,525332192,525504640,525677056,525849344, 526021568,526193728,526365792,526537760,526709632,526881440,527053152,527224800, 527396352,527567840,527739200,527910528,528081728,528252864,528423936,528594880, 528765760,528936576,529107296,529277920,529448480,529618944,529789344,529959648, 530129856,530300000,530470048,530640000,530809888,530979712,531149440,531319072, 531488608,531658080,531827488,531996800,532166016,532335168,532504224,532673184, 532842080,533010912,533179616,533348288,533516832,533685312,533853728,534022048, 534190272,534358432,534526496,534694496,534862400,535030240,535197984,535365632, 535533216,535700704,535868128,536035456,536202720,536369888,536536992,536704000, 536870912 }; // Now where did these came from? const byte gammatable[5][256] = { { 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16, 17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32, 33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48, 49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64, 65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80, 81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96, 97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112, 113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128, 128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143, 144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159, 160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175, 176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191, 192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207, 208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223, 224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239, 240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255 }, { 2,4,5,7,8,10,11,12,14,15,16,18,19,20,21,23, 24,25,26,27,29,30,31,32,33,34,36,37,38,39,40,41, 42,44,45,46,47,48,49,50,51,52,54,55,56,57,58,59, 60,61,62,63,64,65,66,67,69,70,71,72,73,74,75,76, 77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92, 93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108, 109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124, 125,126,127,128,129,129,130,131,132,133,134,135,136,137,138,139, 140,141,142,143,144,145,146,147,148,148,149,150,151,152,153,154, 155,156,157,158,159,160,161,162,163,163,164,165,166,167,168,169, 170,171,172,173,174,175,175,176,177,178,179,180,181,182,183,184, 185,186,186,187,188,189,190,191,192,193,194,195,196,196,197,198, 199,200,201,202,203,204,205,205,206,207,208,209,210,211,212,213, 214,214,215,216,217,218,219,220,221,222,222,223,224,225,226,227, 228,229,230,230,231,232,233,234,235,236,237,237,238,239,240,241, 242,243,244,245,245,246,247,248,249,250,251,252,252,253,254,255 }, { 4,7,9,11,13,15,17,19,21,22,24,26,27,29,30,32, 33,35,36,38,39,40,42,43,45,46,47,48,50,51,52,54, 55,56,57,59,60,61,62,63,65,66,67,68,69,70,72,73, 74,75,76,77,78,79,80,82,83,84,85,86,87,88,89,90, 91,92,93,94,95,96,97,98,100,101,102,103,104,105,106,107, 108,109,110,111,112,113,114,114,115,116,117,118,119,120,121,122, 123,124,125,126,127,128,129,130,131,132,133,133,134,135,136,137, 138,139,140,141,142,143,144,144,145,146,147,148,149,150,151,152, 153,153,154,155,156,157,158,159,160,160,161,162,163,164,165,166, 166,167,168,169,170,171,172,172,173,174,175,176,177,178,178,179, 180,181,182,183,183,184,185,186,187,188,188,189,190,191,192,193, 193,194,195,196,197,197,198,199,200,201,201,202,203,204,205,206, 206,207,208,209,210,210,211,212,213,213,214,215,216,217,217,218, 219,220,221,221,222,223,224,224,225,226,227,228,228,229,230,231, 231,232,233,234,235,235,236,237,238,238,239,240,241,241,242,243, 244,244,245,246,247,247,248,249,250,251,251,252,253,254,254,255 }, { 8,12,16,19,22,24,27,29,31,34,36,38,40,41,43,45, 47,49,50,52,53,55,57,58,60,61,63,64,65,67,68,70, 71,72,74,75,76,77,79,80,81,82,84,85,86,87,88,90, 91,92,93,94,95,96,98,99,100,101,102,103,104,105,106,107, 108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123, 124,125,126,127,128,129,130,131,132,133,134,135,135,136,137,138, 139,140,141,142,143,143,144,145,146,147,148,149,150,150,151,152, 153,154,155,155,156,157,158,159,160,160,161,162,163,164,165,165, 166,167,168,169,169,170,171,172,173,173,174,175,176,176,177,178, 179,180,180,181,182,183,183,184,185,186,186,187,188,189,189,190, 191,192,192,193,194,195,195,196,197,197,198,199,200,200,201,202, 202,203,204,205,205,206,207,207,208,209,210,210,211,212,212,213, 214,214,215,216,216,217,218,219,219,220,221,221,222,223,223,224, 225,225,226,227,227,228,229,229,230,231,231,232,233,233,234,235, 235,236,237,237,238,238,239,240,240,241,242,242,243,244,244,245, 246,246,247,247,248,249,249,250,251,251,252,253,253,254,254,255 }, { 16,23,28,32,36,39,42,45,48,50,53,55,57,60,62,64, 66,68,69,71,73,75,76,78,80,81,83,84,86,87,89,90, 92,93,94,96,97,98,100,101,102,103,105,106,107,108,109,110, 112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,128, 128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143, 143,144,145,146,147,148,149,150,150,151,152,153,154,155,155,156, 157,158,159,159,160,161,162,163,163,164,165,166,166,167,168,169, 169,170,171,172,172,173,174,175,175,176,177,177,178,179,180,180, 181,182,182,183,184,184,185,186,187,187,188,189,189,190,191,191, 192,193,193,194,195,195,196,196,197,198,198,199,200,200,201,202, 202,203,203,204,205,205,206,207,207,208,208,209,210,210,211,211, 212,213,213,214,214,215,216,216,217,217,218,219,219,220,220,221, 221,222,223,223,224,224,225,225,226,227,227,228,228,229,229,230, 230,231,232,232,233,233,234,234,235,235,236,236,237,237,238,239, 239,240,240,241,241,242,242,243,243,244,244,245,245,246,246,247, 247,248,248,249,249,250,250,251,251,252,252,253,254,254,255,255 } }; ================================================ FILE: fbdoom/tables.h ================================================ // // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 1993-2008 Raven Software // Copyright(C) 2005-2014 Simon Howard // // 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. // // 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. // #ifndef __TABLES__ #define __TABLES__ #include "doomtype.h" #include "m_fixed.h" #define FINEANGLES 8192 #define FINEMASK (FINEANGLES-1) // 0x100000000 to 0x2000 #define ANGLETOFINESHIFT 19 // Effective size is 10240. extern const fixed_t finesine[5*FINEANGLES/4]; // Re-use data, is just PI/2 pahse shift. extern const fixed_t *finecosine; // Effective size is 4096. extern const fixed_t finetangent[FINEANGLES/2]; // Gamma correction tables. extern const byte gammatable[5][256]; // Binary Angle Measument, BAM. #define ANG45 0x20000000 #define ANG90 0x40000000 #define ANG180 0x80000000 #define ANG270 0xc0000000 #define ANG_MAX 0xffffffff #define ANG1 (ANG45 / 45) #define ANG60 (ANG180 / 3) // Heretic code uses this definition as though it represents one // degree, but it is not! This is actually ~1.40 degrees. #define ANG1_X 0x01000000 #define SLOPERANGE 2048 #define SLOPEBITS 11 #define DBITS (FRACBITS-SLOPEBITS) typedef unsigned angle_t; // Effective size is 2049; // The +1 size is to handle the case when x==y // without additional checking. extern const angle_t tantoangle[SLOPERANGE+1]; // Utility function, // called by R_PointToAngle. int SlopeDiv(unsigned int num, unsigned int den); #endif ================================================ FILE: fbdoom/v_patch.h ================================================ // // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // 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. // // DESCRIPTION: // Refresh/rendering module, shared data struct definitions. // #ifndef V_PATCH_H #define V_PATCH_H // 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; // bounding box size short height; short leftoffset; // pixels to the left of origin short topoffset; // pixels below the origin int columnofs[8]; // only [width] used // the [0] is &columnofs[width] } PACKEDATTR patch_t; // 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 } PACKEDATTR post_t; // column_t is a list of 0 or more post_t, (byte)-1 terminated typedef post_t column_t; #endif ================================================ FILE: fbdoom/v_video.c ================================================ // // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 1993-2008 Raven Software // Copyright(C) 2005-2014 Simon Howard // // 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. // // DESCRIPTION: // Gamma correction LUT stuff. // Functions to draw patches (by post) directly to screen. // Functions to blit a block to the screen. // #include #include #include #include "i_system.h" #include "doomtype.h" #include "deh_str.h" #include "i_swap.h" #include "i_video.h" #include "m_bbox.h" #include "m_misc.h" #include "v_video.h" #include "w_wad.h" #include "z_zone.h" #include "config.h" #ifdef HAVE_LIBPNG #include #endif // TODO: There are separate RANGECHECK defines for different games, but this // is common code. Fix this. #define RANGECHECK // Blending table used for fuzzpatch, etc. // Only used in Heretic/Hexen byte *tinttable = NULL; // villsa [STRIFE] Blending table used for Strife byte *xlatab = NULL; // The screen buffer that the v_video.c code draws to. static byte *dest_screen = NULL; int dirtybox[4]; // haleyjd 08/28/10: clipping callback function for patches. // This is needed for Chocolate Strife, which clips patches to the screen. static vpatchclipfunc_t patchclip_callback = NULL; // // V_MarkRect // void V_MarkRect(int x, int y, int width, int height) { // If we are temporarily using an alternate screen, do not // affect the update box. if (dest_screen == I_VideoBuffer) { M_AddToBox (dirtybox, x, y); M_AddToBox (dirtybox, x + width-1, y + height-1); } } // // V_CopyRect // void V_CopyRect(int srcx, int srcy, byte *source, int width, int height, int destx, int desty) { byte *src; byte *dest; #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 ("Bad V_CopyRect"); } #endif V_MarkRect(destx, desty, width, height); src = source + SCREENWIDTH * srcy + srcx; dest = dest_screen + SCREENWIDTH * desty + destx; for ( ; height>0 ; height--) { memcpy(dest, src, width); src += SCREENWIDTH; dest += SCREENWIDTH; } } // // V_SetPatchClipCallback // // haleyjd 08/28/10: Added for Strife support. // By calling this function, you can setup runtime error checking for patch // clipping. Strife never caused errors by drawing patches partway off-screen. // Some versions of vanilla DOOM also behaved differently than the default // implementation, so this could possibly be extended to those as well for // accurate emulation. // void V_SetPatchClipCallback(vpatchclipfunc_t func) { patchclip_callback = func; } // // V_DrawPatch // Masks a column based masked pic to the screen. // void V_DrawPatch(int x, int y, patch_t *patch) { int count; int col; column_t *column; byte *desttop; byte *dest; byte *source; int w; y -= SHORT(patch->topoffset); x -= SHORT(patch->leftoffset); // haleyjd 08/28/10: Strife needs silent error checking here. if(patchclip_callback) { if(!patchclip_callback(patch, x, y)) return; } #ifdef RANGECHECK if (x < 0 || x + SHORT(patch->width) > SCREENWIDTH || y < 0 || y + SHORT(patch->height) > SCREENHEIGHT) { I_Error("Bad V_DrawPatch x=%i y=%i patch.width=%i patch.height=%i topoffset=%i leftoffset=%i", x, y, patch->width, patch->height, patch->topoffset, patch->leftoffset); } #endif V_MarkRect(x, y, SHORT(patch->width), SHORT(patch->height)); col = 0; desttop = dest_screen + y * SCREENWIDTH + x; w = SHORT(patch->width); for ( ; colcolumnofs[col])); // step through the posts in a column while (column->topdelta != 0xff) { source = (byte *)column + 3; dest = desttop + column->topdelta*SCREENWIDTH; count = column->length; while (count--) { *dest = *source++; dest += SCREENWIDTH; } column = (column_t *)((byte *)column + column->length + 4); } } } // // V_DrawPatchFlipped // Masks a column based masked pic to the screen. // Flips horizontally, e.g. to mirror face. // void V_DrawPatchFlipped(int x, int y, patch_t *patch) { int count; int col; column_t *column; byte *desttop; byte *dest; byte *source; int w; y -= SHORT(patch->topoffset); x -= SHORT(patch->leftoffset); // haleyjd 08/28/10: Strife needs silent error checking here. if(patchclip_callback) { if(!patchclip_callback(patch, x, y)) return; } #ifdef RANGECHECK if (x < 0 || x + SHORT(patch->width) > SCREENWIDTH || y < 0 || y + SHORT(patch->height) > SCREENHEIGHT) { I_Error("Bad V_DrawPatchFlipped"); } #endif V_MarkRect (x, y, SHORT(patch->width), SHORT(patch->height)); col = 0; desttop = dest_screen + y * SCREENWIDTH + x; w = SHORT(patch->width); for ( ; colcolumnofs[w-1-col])); // step through the posts in a column while (column->topdelta != 0xff ) { source = (byte *)column + 3; dest = desttop + column->topdelta*SCREENWIDTH; count = column->length; while (count--) { *dest = *source++; dest += SCREENWIDTH; } column = (column_t *)((byte *)column + column->length + 4); } } } // // V_DrawPatchDirect // Draws directly to the screen on the pc. // void V_DrawPatchDirect(int x, int y, patch_t *patch) { V_DrawPatch(x, y, patch); } // // V_DrawTLPatch // // Masks a column based translucent masked pic to the screen. // void V_DrawTLPatch(int x, int y, patch_t * patch) { int count, col; column_t *column; byte *desttop, *dest, *source; int w; y -= SHORT(patch->topoffset); x -= SHORT(patch->leftoffset); if (x < 0 || x + SHORT(patch->width) > SCREENWIDTH || y < 0 || y + SHORT(patch->height) > SCREENHEIGHT) { I_Error("Bad V_DrawTLPatch"); } col = 0; desttop = dest_screen + y * SCREENWIDTH + x; w = SHORT(patch->width); for (; col < w; x++, col++, desttop++) { column = (column_t *) ((byte *) patch + LONG(patch->columnofs[col])); // step through the posts in a column while (column->topdelta != 0xff) { source = (byte *) column + 3; dest = desttop + column->topdelta * SCREENWIDTH; count = column->length; while (count--) { *dest = tinttable[((*dest) << 8) + *source++]; dest += SCREENWIDTH; } column = (column_t *) ((byte *) column + column->length + 4); } } } // // V_DrawXlaPatch // // villsa [STRIFE] Masks a column based translucent masked pic to the screen. // void V_DrawXlaPatch(int x, int y, patch_t * patch) { int count, col; column_t *column; byte *desttop, *dest, *source; int w; y -= SHORT(patch->topoffset); x -= SHORT(patch->leftoffset); if(patchclip_callback) { if(!patchclip_callback(patch, x, y)) return; } col = 0; desttop = dest_screen + y * SCREENWIDTH + x; w = SHORT(patch->width); for(; col < w; x++, col++, desttop++) { column = (column_t *) ((byte *) patch + LONG(patch->columnofs[col])); // step through the posts in a column while(column->topdelta != 0xff) { source = (byte *) column + 3; dest = desttop + column->topdelta * SCREENWIDTH; count = column->length; while(count--) { *dest = xlatab[*dest + ((*source) << 8)]; source++; dest += SCREENWIDTH; } column = (column_t *) ((byte *) column + column->length + 4); } } } // // V_DrawAltTLPatch // // Masks a column based translucent masked pic to the screen. // void V_DrawAltTLPatch(int x, int y, patch_t * patch) { int count, col; column_t *column; byte *desttop, *dest, *source; int w; y -= SHORT(patch->topoffset); x -= SHORT(patch->leftoffset); if (x < 0 || x + SHORT(patch->width) > SCREENWIDTH || y < 0 || y + SHORT(patch->height) > SCREENHEIGHT) { I_Error("Bad V_DrawAltTLPatch"); } col = 0; desttop = dest_screen + y * SCREENWIDTH + x; w = SHORT(patch->width); for (; col < w; x++, col++, desttop++) { column = (column_t *) ((byte *) patch + LONG(patch->columnofs[col])); // step through the posts in a column while (column->topdelta != 0xff) { source = (byte *) column + 3; dest = desttop + column->topdelta * SCREENWIDTH; count = column->length; while (count--) { *dest = tinttable[((*dest) << 8) + *source++]; dest += SCREENWIDTH; } column = (column_t *) ((byte *) column + column->length + 4); } } } // // V_DrawShadowedPatch // // Masks a column based masked pic to the screen. // void V_DrawShadowedPatch(int x, int y, patch_t *patch) { int count, col; column_t *column; byte *desttop, *dest, *source; byte *desttop2, *dest2; int w; y -= SHORT(patch->topoffset); x -= SHORT(patch->leftoffset); if (x < 0 || x + SHORT(patch->width) > SCREENWIDTH || y < 0 || y + SHORT(patch->height) > SCREENHEIGHT) { I_Error("Bad V_DrawShadowedPatch"); } col = 0; desttop = dest_screen + y * SCREENWIDTH + x; desttop2 = dest_screen + (y + 2) * SCREENWIDTH + x + 2; w = SHORT(patch->width); for (; col < w; x++, col++, desttop++, desttop2++) { column = (column_t *) ((byte *) patch + LONG(patch->columnofs[col])); // step through the posts in a column while (column->topdelta != 0xff) { source = (byte *) column + 3; dest = desttop + column->topdelta * SCREENWIDTH; dest2 = desttop2 + column->topdelta * SCREENWIDTH; count = column->length; while (count--) { *dest2 = tinttable[((*dest2) << 8)]; dest2 += SCREENWIDTH; *dest = *source++; dest += SCREENWIDTH; } column = (column_t *) ((byte *) column + column->length + 4); } } } // // Load tint table from TINTTAB lump. // void V_LoadTintTable(void) { tinttable = W_CacheLumpName("TINTTAB", PU_STATIC); } // // V_LoadXlaTable // // villsa [STRIFE] Load xla table from XLATAB lump. // void V_LoadXlaTable(void) { xlatab = W_CacheLumpName("XLATAB", PU_STATIC); } // // V_DrawBlock // Draw a linear block of pixels into the view buffer. // void V_DrawBlock(int x, int y, int width, int height, byte *src) { byte *dest; #ifdef RANGECHECK if (x < 0 || x + width >SCREENWIDTH || y < 0 || y + height > SCREENHEIGHT) { I_Error ("Bad V_DrawBlock"); } #endif V_MarkRect (x, y, width, height); dest = dest_screen + y * SCREENWIDTH + x; while (height--) { memcpy (dest, src, width); src += width; dest += SCREENWIDTH; } } void V_DrawFilledBox(int x, int y, int w, int h, int c) { uint8_t *buf, *buf1; int x1, y1; buf = I_VideoBuffer + SCREENWIDTH * y + x; for (y1 = 0; y1 < h; ++y1) { buf1 = buf; for (x1 = 0; x1 < w; ++x1) { *buf1++ = c; } buf += SCREENWIDTH; } } void V_DrawHorizLine(int x, int y, int w, int c) { uint8_t *buf; int x1; buf = I_VideoBuffer + SCREENWIDTH * y + x; for (x1 = 0; x1 < w; ++x1) { *buf++ = c; } } void V_DrawVertLine(int x, int y, int h, int c) { uint8_t *buf; int y1; buf = I_VideoBuffer + SCREENWIDTH * y + x; for (y1 = 0; y1 < h; ++y1) { *buf = c; buf += SCREENWIDTH; } } void V_DrawBox(int x, int y, int w, int h, int c) { V_DrawHorizLine(x, y, w, c); V_DrawHorizLine(x, y+h-1, w, c); V_DrawVertLine(x, y, h, c); V_DrawVertLine(x+w-1, y, h, c); } // // Draw a "raw" screen (lump containing raw data to blit directly // to the screen) // void V_DrawRawScreen(byte *raw) { memcpy(dest_screen, raw, SCREENWIDTH * SCREENHEIGHT); } // // V_Init // void V_Init (void) { // no-op! // There used to be separate screens that could be drawn to; these are // now handled in the upper layers. } // Set the buffer that the code draws to. void V_UseBuffer(byte *buffer) { dest_screen = buffer; } // Restore screen buffer to the i_video screen buffer. void V_RestoreBuffer(void) { dest_screen = I_VideoBuffer; } // // SCREEN SHOTS // typedef struct { char manufacturer; char version; char encoding; char bits_per_pixel; unsigned short xmin; unsigned short ymin; unsigned short xmax; unsigned short ymax; unsigned short hres; unsigned short vres; unsigned char palette[48]; char reserved; char color_planes; unsigned short bytes_per_line; unsigned short palette_type; char filler[58]; unsigned char data; // unbounded } PACKEDATTR pcx_t; // // WritePCXfile // void WritePCXfile(char *filename, byte *data, int width, int height, byte *palette) { int i; int length; pcx_t* pcx; byte* pack; pcx = Z_Malloc (width*height*2+1000, PU_STATIC, NULL); pcx->manufacturer = 0x0a; // PCX id pcx->version = 5; // 256 color pcx->encoding = 1; // uncompressed pcx->bits_per_pixel = 8; // 256 color pcx->xmin = 0; pcx->ymin = 0; pcx->xmax = SHORT(width-1); pcx->ymax = SHORT(height-1); pcx->hres = SHORT(width); pcx->vres = SHORT(height); memset (pcx->palette,0,sizeof(pcx->palette)); pcx->color_planes = 1; // chunky image pcx->bytes_per_line = SHORT(width); pcx->palette_type = SHORT(2); // not a grey scale memset (pcx->filler,0,sizeof(pcx->filler)); // pack the image pack = &pcx->data; for (i=0 ; i MOUSE_SPEED_BOX_WIDTH - 1) { linelen = MOUSE_SPEED_BOX_WIDTH - 1; } V_DrawHorizLine(box_x + 1, box_y + 4, MOUSE_SPEED_BOX_WIDTH - 2, black); if (linelen < redline_x) { V_DrawHorizLine(box_x + 1, box_y + MOUSE_SPEED_BOX_HEIGHT / 2, linelen, white); } else { V_DrawHorizLine(box_x + 1, box_y + MOUSE_SPEED_BOX_HEIGHT / 2, redline_x, white); V_DrawHorizLine(box_x + redline_x, box_y + MOUSE_SPEED_BOX_HEIGHT / 2, linelen - redline_x, yellow); } // Draw red line V_DrawVertLine(box_x + redline_x, box_y + 1, MOUSE_SPEED_BOX_HEIGHT - 2, red); } ================================================ FILE: fbdoom/v_video.h ================================================ // // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // 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. // // DESCRIPTION: // Gamma correction LUT. // 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" // Needed because we are refering to patches. #include "v_patch.h" // // VIDEO // #define CENTERY (SCREENHEIGHT/2) extern int dirtybox[4]; extern byte *tinttable; // haleyjd 08/28/10: implemented for Strife support // haleyjd 08/28/10: Patch clipping callback, implemented to support Choco // Strife. typedef boolean (*vpatchclipfunc_t)(patch_t *, int, int); void V_SetPatchClipCallback(vpatchclipfunc_t func); // Allocates buffer screens, call before R_Init. void V_Init (void); // Draw a block from the specified source screen to the screen. void V_CopyRect(int srcx, int srcy, byte *source, int width, int height, int destx, int desty); void V_DrawPatch(int x, int y, patch_t *patch); void V_DrawPatchFlipped(int x, int y, patch_t *patch); void V_DrawTLPatch(int x, int y, patch_t *patch); void V_DrawAltTLPatch(int x, int y, patch_t * patch); void V_DrawShadowedPatch(int x, int y, patch_t *patch); void V_DrawXlaPatch(int x, int y, patch_t * patch); // villsa [STRIFE] void V_DrawPatchDirect(int x, int y, patch_t *patch); // Draw a linear block of pixels into the view buffer. void V_DrawBlock(int x, int y, int width, int height, byte *src); void V_MarkRect(int x, int y, int width, int height); void V_DrawFilledBox(int x, int y, int w, int h, int c); void V_DrawHorizLine(int x, int y, int w, int c); void V_DrawVertLine(int x, int y, int h, int c); void V_DrawBox(int x, int y, int w, int h, int c); // Draw a raw screen lump void V_DrawRawScreen(byte *raw); // Temporarily switch to using a different buffer to draw graphics, etc. void V_UseBuffer(byte *buffer); // Return to using the normal screen buffer to draw graphics. void V_RestoreBuffer(void); // Save a screenshot of the current screen to a file, named in the // format described in the string passed to the function, eg. // "DOOM%02i.pcx" void V_ScreenShot(char *format); // Load the lookup table for translucency calculations from the TINTTAB // lump. void V_LoadTintTable(void); // villsa [STRIFE] // Load the lookup table for translucency calculations from the XLATAB // lump. void V_LoadXlaTable(void); void V_DrawMouseSpeedBox(int speed); #endif ================================================ FILE: fbdoom/w_checksum.c ================================================ // // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // 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. // // DESCRIPTION: // Generate a checksum of the WAD directory. // #include #include #include #include "m_misc.h" #include "sha1.h" #include "w_checksum.h" #include "w_wad.h" static wad_file_t **open_wadfiles = NULL; static int num_open_wadfiles = 0; static int GetFileNumber(wad_file_t *handle) { int i; int result; for (i=0; iname, sizeof(buf)); SHA1_UpdateString(sha1_context, buf); SHA1_UpdateInt32(sha1_context, GetFileNumber(lump->wad_file)); SHA1_UpdateInt32(sha1_context, lump->position); SHA1_UpdateInt32(sha1_context, lump->size); } void W_Checksum(sha1_digest_t digest) { sha1_context_t sha1_context; unsigned int i; SHA1_Init(&sha1_context); num_open_wadfiles = 0; // Go through each entry in the WAD directory, adding information // about each entry to the SHA1 hash. for (i=0; i #include "config.h" #include "doomtype.h" #include "m_argv.h" #include "w_file.h" extern wad_file_class_t stdc_wad_file; #ifdef _WIN32 extern wad_file_class_t win32_wad_file; #endif #ifdef HAVE_MMAP extern wad_file_class_t posix_wad_file; #endif static wad_file_class_t *wad_file_classes[] = { #ifdef _WIN32 &win32_wad_file, #endif #ifdef HAVE_MMAP &posix_wad_file, #endif &stdc_wad_file, }; wad_file_t *W_OpenFile(char *path) { wad_file_t *result; int i; //! // Use the OS's virtual memory subsystem to map WAD files // directly into memory. // if (!M_CheckParm("-mmap")) { return stdc_wad_file.OpenFile(path); } // Try all classes in order until we find one that works result = NULL; for (i = 0; i < arrlen(wad_file_classes); ++i) { result = wad_file_classes[i]->OpenFile(path); if (result != NULL) { break; } } return result; } void W_CloseFile(wad_file_t *wad) { wad->file_class->CloseFile(wad); } size_t W_Read(wad_file_t *wad, unsigned int offset, void *buffer, size_t buffer_len) { return wad->file_class->Read(wad, offset, buffer, buffer_len); } ================================================ FILE: fbdoom/w_file.h ================================================ // // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // 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. // // DESCRIPTION: // WAD I/O functions. // #ifndef __W_FILE__ #define __W_FILE__ #include #include "doomtype.h" typedef struct _wad_file_s wad_file_t; typedef struct { // Open a file for reading. wad_file_t *(*OpenFile)(char *path); // Close the specified file. void (*CloseFile)(wad_file_t *file); // Read data from the specified position in the file into the // provided buffer. Returns the number of bytes read. size_t (*Read)(wad_file_t *file, unsigned int offset, void *buffer, size_t buffer_len); } wad_file_class_t; struct _wad_file_s { // Class of this file. wad_file_class_t *file_class; // If this is NULL, the file cannot be mapped into memory. If this // is non-NULL, it is a pointer to the mapped file. byte *mapped; // Length of the file, in bytes. unsigned int length; }; // Open the specified file. Returns a pointer to a new wad_file_t // handle for the WAD file, or NULL if it could not be opened. wad_file_t *W_OpenFile(char *path); // Close the specified WAD file. void W_CloseFile(wad_file_t *wad); // Read data from the specified file into the provided buffer. The // data is read from the specified offset from the start of the file. // Returns the number of bytes read. size_t W_Read(wad_file_t *wad, unsigned int offset, void *buffer, size_t buffer_len); #endif /* #ifndef __W_FILE__ */ ================================================ FILE: fbdoom/w_file_stdc.c ================================================ // // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // 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. // // DESCRIPTION: // WAD I/O functions. // #include #include "m_misc.h" #include "w_file.h" #include "z_zone.h" typedef struct { wad_file_t wad; FILE *fstream; } stdc_wad_file_t; extern wad_file_class_t stdc_wad_file; static wad_file_t *W_StdC_OpenFile(char *path) { stdc_wad_file_t *result; FILE *fstream; fstream = fopen(path, "rb"); if (fstream == NULL) { return NULL; } // Create a new stdc_wad_file_t to hold the file handle. result = Z_Malloc(sizeof(stdc_wad_file_t), PU_STATIC, 0); result->wad.file_class = &stdc_wad_file; result->wad.mapped = NULL; result->wad.length = M_FileLength(fstream); result->fstream = fstream; return &result->wad; } static void W_StdC_CloseFile(wad_file_t *wad) { stdc_wad_file_t *stdc_wad; stdc_wad = (stdc_wad_file_t *) wad; fclose(stdc_wad->fstream); Z_Free(stdc_wad); } // Read data from the specified position in the file into the // provided buffer. Returns the number of bytes read. size_t W_StdC_Read(wad_file_t *wad, unsigned int offset, void *buffer, size_t buffer_len) { stdc_wad_file_t *stdc_wad; size_t result; stdc_wad = (stdc_wad_file_t *) wad; // Jump to the specified position in the file. fseek(stdc_wad->fstream, offset, SEEK_SET); // Read into the buffer. result = fread(buffer, 1, buffer_len, stdc_wad->fstream); return result; } wad_file_class_t stdc_wad_file = { W_StdC_OpenFile, W_StdC_CloseFile, W_StdC_Read, }; ================================================ FILE: fbdoom/w_file_stdc_unbuffered.c ================================================ // // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // 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. // // DESCRIPTION: // WAD I/O functions. // #include #include #include #include #include #include #include #include "m_misc.h" #include "w_file.h" #include "z_zone.h" typedef struct { wad_file_t wad; int fd; } stdc_wad_file_t; extern wad_file_class_t stdc_wad_file; static unsigned int W_StdC_FileLength(int fd) { long savedpos; long length; // save the current position in the file savedpos = lseek(fd, 0, SEEK_CUR); // jump to the end and find the length length = lseek(fd, 0, SEEK_END); // go back to the old location lseek(fd, savedpos, SEEK_SET); return length; } static wad_file_t *W_StdC_OpenFile(char *path) { stdc_wad_file_t *result; int fd; fd = open(path, O_RDONLY); if (fd <= 0) { return NULL; } // Create a new stdc_wad_file_t to hold the file handle. result = Z_Malloc(sizeof(stdc_wad_file_t), PU_STATIC, 0); result->wad.file_class = &stdc_wad_file; result->wad.mapped = NULL; result->wad.length = W_StdC_FileLength(fd); result->fd = fd; return &result->wad; } static void W_StdC_CloseFile(wad_file_t *wad) { stdc_wad_file_t *stdc_wad; stdc_wad = (stdc_wad_file_t *) wad; close(stdc_wad->fd); Z_Free(stdc_wad); } // Read data from the specified position in the file into the // provided buffer. Returns the number of bytes read. size_t W_StdC_Read(wad_file_t *wad, unsigned int offset, void *buffer, size_t buffer_len) { stdc_wad_file_t *stdc_wad; size_t result = 0; int rc; stdc_wad = (stdc_wad_file_t *) wad; // Jump to the specified position in the file. lseek(stdc_wad->fd, offset, SEEK_SET); // Read into the buffer. do { rc = read(stdc_wad->fd, (char *)buffer + result, buffer_len - result); if (rc <= 0) { if (rc < 0 && errno == EINTR) continue; break; } result += rc; } while (result < buffer_len); return result; } wad_file_class_t stdc_wad_file = { W_StdC_OpenFile, W_StdC_CloseFile, W_StdC_Read, }; ================================================ FILE: fbdoom/w_main.c ================================================ // // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // 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. // // DESCRIPTION: // Common code to parse command line, identifying WAD files to load. // #include "doomfeatures.h" #include "d_iwad.h" #include "m_argv.h" #include "w_main.h" #include "w_merge.h" #include "w_wad.h" #include "z_zone.h" // Parse the command line, merging WAD files that are sppecified. // Returns true if at least one file was added. boolean W_ParseCommandLine(void) { boolean modifiedgame = false; int p; #ifdef FEATURE_WAD_MERGE // Merged PWADs are loaded first, because they are supposed to be // modified IWADs. //! // @arg // @category mod // // Simulates the behavior of deutex's -merge option, merging a PWAD // into the main IWAD. Multiple files may be specified. // p = M_CheckParmWithArgs("-merge", 1); if (p > 0) { for (p = p + 1; p // @category mod // // Simulates the behavior of NWT's -merge option. Multiple files // may be specified. p = M_CheckParmWithArgs("-nwtmerge", 1); if (p > 0) { for (p = p + 1; p // @category mod // // Simulates the behavior of NWT's -af option, merging flats into // the main IWAD directory. Multiple files may be specified. // p = M_CheckParmWithArgs("-af", 1); if (p > 0) { for (p = p + 1; p // @category mod // // Simulates the behavior of NWT's -as option, merging sprites // into the main IWAD directory. Multiple files may be specified. // p = M_CheckParmWithArgs("-as", 1); if (p > 0) { for (p = p + 1; p // @category mod // // Equivalent to "-af -as ". // p = M_CheckParmWithArgs("-aa", 1); if (p > 0) { for (p = p + 1; p // @vanilla // // Load the specified PWAD files. // p = M_CheckParmWithArgs ("-file", 1); if (p) { // 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][0] != '-') { char *filename; filename = D_TryFindWADByName(myargv[p]); printf(" adding %s\n", filename); W_AddFile(filename); } } // W_PrintDirectory(); return modifiedgame; } ================================================ FILE: fbdoom/w_main.h ================================================ // // Copyright(C) 2005-2014 Simon Howard // // 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. // // DESCRIPTION: // Common code to parse command line, identifying WAD files to load. // #ifndef W_MAIN_H #define W_MAIN_H boolean W_ParseCommandLine(void); #endif /* #ifndef W_MAIN_H */ ================================================ FILE: fbdoom/w_merge.h ================================================ // // Copyright(C) 2005-2014 Simon Howard // // 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. // // DESCRIPTION: // Handles merging of PWADs, similar to deutex's -merge option // // Ideally this should work exactly the same as in deutex, but trying to // read the deutex source code made my brain hurt. // #ifndef W_MERGE_H #define W_MERGE_H #define W_NWT_MERGE_SPRITES 0x1 #define W_NWT_MERGE_FLATS 0x2 // Add a new WAD and merge it into the main directory void W_MergeFile(char *filename); // NWT-style merging void W_NWTMergeFile(char *filename, int flags); // Acts the same as NWT's "-merge" option. void W_NWTDashMerge(char *filename); // Debug function that prints the WAD directory. void W_PrintDirectory(void); #endif /* #ifndef W_MERGE_H */ ================================================ FILE: fbdoom/w_wad.c ================================================ // // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // 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. // // DESCRIPTION: // Handles WAD file header, directory, lump I/O. // #include #include #include #include #include "doomtype.h" #include "config.h" #include "d_iwad.h" #include "i_swap.h" #include "i_system.h" #include "i_video.h" #include "m_misc.h" #include "z_zone.h" #include "w_wad.h" typedef struct { // Should be "IWAD" or "PWAD". char identification[4]; int numlumps; int infotableofs; } PACKEDATTR wadinfo_t; typedef struct { int filepos; int size; char name[8]; } PACKEDATTR filelump_t; // // GLOBALS // // Location of each lump on disk. lumpinfo_t *lumpinfo; unsigned int numlumps = 0; // Hash table for fast lookups static lumpinfo_t **lumphash; // Hash function used for lump names. unsigned int W_LumpNameHash(const char *s) { // This is the djb2 string hash function, modded to work on strings // that have a maximum length of 8. unsigned int result = 5381; unsigned int i; for (i=0; i < 8 && s[i] != '\0'; ++i) { result = ((result << 5) ^ result ) ^ toupper((int)s[i]); } return result; } // Increase the size of the lumpinfo[] array to the specified size. static void ExtendLumpInfo(int newnumlumps) { lumpinfo_t *newlumpinfo; unsigned int i; newlumpinfo = calloc(newnumlumps, sizeof(lumpinfo_t)); if (newlumpinfo == NULL) { I_Error ("Couldn't realloc lumpinfo"); } // Copy over lumpinfo_t structures from the old array. If any of // these lumps have been cached, we need to update the user // pointers to the new location. for (i = 0; i < numlumps && i < newnumlumps; ++i) { memcpy(&newlumpinfo[i], &lumpinfo[i], sizeof(lumpinfo_t)); if (newlumpinfo[i].cache != NULL) { Z_ChangeUser(newlumpinfo[i].cache, &newlumpinfo[i].cache); } // We shouldn't be generating a hash table until after all WADs have // been loaded, but just in case... if (lumpinfo[i].next != NULL) { int nextlumpnum = lumpinfo[i].next - lumpinfo; newlumpinfo[i].next = &newlumpinfo[nextlumpnum]; } } // All done. free(lumpinfo); lumpinfo = newlumpinfo; numlumps = newnumlumps; } // // 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. wad_file_t *W_AddFile (char *filename) { wadinfo_t header; lumpinfo_t *lump_p; unsigned int i; wad_file_t *wad_file; int length; int startlump; filelump_t *fileinfo; filelump_t *filerover; int newnumlumps; // open the file and add to directory wad_file = W_OpenFile(filename); if (wad_file == NULL) { printf (" couldn't open %s\n", filename); return NULL; } newnumlumps = numlumps; if (strcasecmp(filename+strlen(filename)-3 , "wad" ) ) { // single lump file // fraggle: Swap the filepos and size here. The WAD directory // parsing code expects a little-endian directory, so will swap // them back. Effectively we're constructing a "fake WAD directory" // here, as it would appear on disk. fileinfo = Z_Malloc(sizeof(filelump_t), PU_STATIC, 0); fileinfo->filepos = LONG(0); fileinfo->size = LONG(wad_file->length); // Name the lump after the base of the filename (without the // extension). M_ExtractFileBase (filename, fileinfo->name); newnumlumps++; } else { // WAD file W_Read(wad_file, 0, &header, sizeof(header)); if (strncmp(header.identification,"IWAD",4)) { // Homebrew levels? if (strncmp(header.identification,"PWAD",4)) { I_Error ("Wad file %s doesn't have IWAD " "or PWAD id\n", filename); } // ???modifiedgame = true; } header.numlumps = LONG(header.numlumps); header.infotableofs = LONG(header.infotableofs); length = header.numlumps*sizeof(filelump_t); fileinfo = Z_Malloc(length, PU_STATIC, 0); W_Read(wad_file, header.infotableofs, fileinfo, length); newnumlumps += header.numlumps; } // Increase size of numlumps array to accomodate the new file. startlump = numlumps; ExtendLumpInfo(newnumlumps); lump_p = &lumpinfo[startlump]; filerover = fileinfo; for (i=startlump; iwad_file = wad_file; lump_p->position = LONG(filerover->filepos); lump_p->size = LONG(filerover->size); lump_p->cache = NULL; strncpy(lump_p->name, filerover->name, 8); ++lump_p; ++filerover; } Z_Free(fileinfo); if (lumphash != NULL) { Z_Free(lumphash); lumphash = NULL; } return wad_file; } // // W_NumLumps // int W_NumLumps (void) { return numlumps; } // // W_CheckNumForName // Returns -1 if name not found. // int W_CheckNumForName (char* name) { lumpinfo_t *lump_p; int i; // Do we have a hash table yet? if (lumphash != NULL) { int hash; // We do! Excellent. hash = W_LumpNameHash(name) % numlumps; for (lump_p = lumphash[hash]; lump_p != NULL; lump_p = lump_p->next) { if (!strncasecmp(lump_p->name, name, 8)) { return lump_p - lumpinfo; } } } else { // We don't have a hash table generate yet. Linear search :-( // // scan backwards so patch lump files take precedence for (i=numlumps-1; i >= 0; --i) { if (!strncasecmp(lumpinfo[i].name, name, 8)) { return i; } } } // TFB. Not found. return -1; } // // W_GetNumForName // Calls W_CheckNumForName, but bombs out if not found. // int W_GetNumForName (char* name) { int i; i = W_CheckNumForName (name); if (i < 0) { I_Error ("W_GetNumForName: %s not found!", name); } return i; } // // W_LumpLength // Returns the buffer size needed to load the given lump. // int W_LumpLength (unsigned int lump) { if (lump >= 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(unsigned int lump, void *dest) { int c; lumpinfo_t *l; if (lump >= numlumps) { I_Error ("W_ReadLump: %i >= numlumps", lump); } l = lumpinfo+lump; I_BeginRead (); c = W_Read(l->wad_file, l->position, dest, l->size); if (c < l->size) { I_Error ("W_ReadLump: only read %i of %i on lump %i", c, l->size, lump); } I_EndRead (); } // // W_CacheLumpNum // // Load a lump into memory and return a pointer to a buffer containing // the lump data. // // 'tag' is the type of zone memory buffer to allocate for the lump // (usually PU_STATIC or PU_CACHE). If the lump is loaded as // PU_STATIC, it should be released back using W_ReleaseLumpNum // when no longer needed (do not use Z_ChangeTag). // void *W_CacheLumpNum(int lumpnum, int tag) { byte *result; lumpinfo_t *lump; if ((unsigned)lumpnum >= numlumps) { I_Error ("W_CacheLumpNum: %i >= numlumps", lumpnum); } lump = &lumpinfo[lumpnum]; // Get the pointer to return. If the lump is in a memory-mapped // file, we can just return a pointer to within the memory-mapped // region. If the lump is in an ordinary file, we may already // have it cached; otherwise, load it into memory. if (lump->wad_file->mapped != NULL) { // Memory mapped file, return from the mmapped region. result = lump->wad_file->mapped + lump->position; } else if (lump->cache != NULL) { // Already cached, so just switch the zone tag. result = lump->cache; Z_ChangeTag(lump->cache, tag); } else { // Not yet loaded, so load it now lump->cache = Z_Malloc(W_LumpLength(lumpnum), tag, &lump->cache); W_ReadLump (lumpnum, lump->cache); result = lump->cache; } return result; } // // W_CacheLumpName // void *W_CacheLumpName(char *name, int tag) { return W_CacheLumpNum(W_GetNumForName(name), tag); } // // Release a lump back to the cache, so that it can be reused later // without having to read from disk again, or alternatively, discarded // if we run out of memory. // // Back in Vanilla Doom, this was just done using Z_ChangeTag // directly, but now that we have WAD mmap, things are a bit more // complicated ... // void W_ReleaseLumpNum(int lumpnum) { lumpinfo_t *lump; if ((unsigned)lumpnum >= numlumps) { I_Error ("W_ReleaseLumpNum: %i >= numlumps", lumpnum); } lump = &lumpinfo[lumpnum]; if (lump->wad_file->mapped != NULL) { // Memory-mapped file, so nothing needs to be done here. } else { Z_ChangeTag(lump->cache, PU_CACHE); } } void W_ReleaseLumpName(char *name) { W_ReleaseLumpNum(W_GetNumForName(name)); } #if 0 // // W_Profile // int info[2500][10]; int profilecount; void W_Profile (void) { int i; memblock_t* block; void* ptr; char ch; FILE* f; int j; char name[9]; for (i=0 ; itag < PU_PURGELEVEL) ch = 'S'; else ch = 'P'; } info[i][profilecount] = ch; } profilecount++; #if ORIGCODE f = fopen ("waddump.txt","w"); name[8] = 0; for (i=0 ; i 0) { lumphash = Z_Malloc(sizeof(lumpinfo_t *) * numlumps, PU_STATIC, NULL); memset(lumphash, 0, sizeof(lumpinfo_t *) * numlumps); for (i=0; i= 0) { I_Error("\nYou are trying to use a %s IWAD file with " "the %s%s binary.\nThis isn't going to work.\n" "You probably want to use the %s%s binary.", D_SuggestGameName(unique_lumps[i].mission, indetermined), PROGRAM_PREFIX, D_GameMissionString(mission), PROGRAM_PREFIX, D_GameMissionString(unique_lumps[i].mission)); } } } } ================================================ FILE: fbdoom/w_wad.h ================================================ // // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // 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. // // DESCRIPTION: // WAD I/O functions. // #ifndef __W_WAD__ #define __W_WAD__ #include #include "doomtype.h" #include "d_mode.h" #include "w_file.h" // // TYPES // // // WADFILE I/O related stuff. // typedef struct lumpinfo_s lumpinfo_t; struct lumpinfo_s { char name[8]; wad_file_t *wad_file; int position; int size; void *cache; // Used for hash table lookups lumpinfo_t *next; }; extern lumpinfo_t *lumpinfo; extern unsigned int numlumps; wad_file_t *W_AddFile (char *filename); int W_CheckNumForName (char* name); int W_GetNumForName (char* name); int W_LumpLength (unsigned int lump); void W_ReadLump (unsigned int lump, void *dest); void* W_CacheLumpNum (int lump, int tag); void* W_CacheLumpName (char* name, int tag); void W_GenerateHashTable(void); extern unsigned int W_LumpNameHash(const char *s); void W_ReleaseLumpNum(int lump); void W_ReleaseLumpName(char *name); void W_CheckCorrectIWAD(GameMission_t mission); #endif ================================================ FILE: fbdoom/wi_stuff.c ================================================ // // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // 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. // // DESCRIPTION: // Intermission screens. // #include #include "z_zone.h" #include "m_misc.h" #include "m_random.h" #include "deh_main.h" #include "i_swap.h" #include "i_system.h" #include "w_wad.h" #include "g_game.h" #include "r_local.h" #include "s_sound.h" #include "doomstat.h" // Data. #include "sounds.h" // Needs access to LFB. #include "v_video.h" #include "wi_stuff.h" // // 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 // 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 // SINGPLE-PLAYER STUFF #define SP_STATSX 50 #define SP_STATSY 50 #define SP_TIMEX 16 #define SP_TIMEY (SCREENHEIGHT-32) // NET GAME STUFF #define NG_STATSY 50 #define NG_STATSX (32 + SHORT(star->width)/2 + 32*!dofrags) #define NG_SPACINGX 64 // 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 typedef enum { ANIM_ALWAYS, ANIM_RANDOM, ANIM_LEVEL } animenum_t; typedef struct { int x; 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 patch_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. // #define ANIM(type, period, nanims, x, y, nexttic) \ { (type), (period), (nanims), { (x), (y) }, (nexttic), \ 0, { NULL, NULL, NULL }, 0, 0, 0, 0 } static anim_t epsd0animinfo[] = { ANIM(ANIM_ALWAYS, TICRATE/3, 3, 224, 104, 0), ANIM(ANIM_ALWAYS, TICRATE/3, 3, 184, 160, 0), ANIM(ANIM_ALWAYS, TICRATE/3, 3, 112, 136, 0), ANIM(ANIM_ALWAYS, TICRATE/3, 3, 72, 112, 0), ANIM(ANIM_ALWAYS, TICRATE/3, 3, 88, 96, 0), ANIM(ANIM_ALWAYS, TICRATE/3, 3, 64, 48, 0), ANIM(ANIM_ALWAYS, TICRATE/3, 3, 192, 40, 0), ANIM(ANIM_ALWAYS, TICRATE/3, 3, 136, 16, 0), ANIM(ANIM_ALWAYS, TICRATE/3, 3, 80, 16, 0), ANIM(ANIM_ALWAYS, TICRATE/3, 3, 64, 24, 0), }; static anim_t epsd1animinfo[] = { ANIM(ANIM_LEVEL, TICRATE/3, 1, 128, 136, 1), ANIM(ANIM_LEVEL, TICRATE/3, 1, 128, 136, 2), ANIM(ANIM_LEVEL, TICRATE/3, 1, 128, 136, 3), ANIM(ANIM_LEVEL, TICRATE/3, 1, 128, 136, 4), ANIM(ANIM_LEVEL, TICRATE/3, 1, 128, 136, 5), ANIM(ANIM_LEVEL, TICRATE/3, 1, 128, 136, 6), ANIM(ANIM_LEVEL, TICRATE/3, 1, 128, 136, 7), ANIM(ANIM_LEVEL, TICRATE/3, 3, 192, 144, 8), ANIM(ANIM_LEVEL, TICRATE/3, 1, 128, 136, 8), }; static anim_t epsd2animinfo[] = { ANIM(ANIM_ALWAYS, TICRATE/3, 3, 104, 168, 0), ANIM(ANIM_ALWAYS, TICRATE/3, 3, 40, 136, 0), ANIM(ANIM_ALWAYS, TICRATE/3, 3, 160, 96, 0), ANIM(ANIM_ALWAYS, TICRATE/3, 3, 104, 80, 0), ANIM(ANIM_ALWAYS, TICRATE/3, 3, 120, 32, 0), ANIM(ANIM_ALWAYS, TICRATE/4, 3, 40, 0, 0), }; static int NUMANIMS[NUMEPISODES] = { arrlen(epsd0animinfo), arrlen(epsd1animinfo), arrlen(epsd2animinfo), }; static anim_t *anims[NUMEPISODES] = { epsd0animinfo, epsd1animinfo, epsd2animinfo }; // // GENERAL DATA // // // Locally used stuff. // // 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 static int acceleratestage; // 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_kills[MAXPLAYERS]; static int cnt_items[MAXPLAYERS]; static int cnt_secret[MAXPLAYERS]; static int cnt_time; static int cnt_par; static int cnt_pause; // # of commercial levels static int NUMCMAPS; // // GRAPHICS // // You Are Here graphic static patch_t* yah[3] = { NULL, NULL, NULL }; // splat static patch_t* splat[2] = { NULL, NULL }; // %, : graphics static patch_t* percent; static patch_t* colon; // 0-9 graphic static patch_t* num[10]; // minus sign static patch_t* wiminus; // "Finished!" graphics static patch_t* finished; // "Entering" graphic static patch_t* entering; // "secret" static patch_t* sp_secret; // "Kills", "Scrt", "Items", "Frags" static patch_t* kills; static patch_t* secret; static patch_t* items; static patch_t* frags; // Time sucks. static patch_t* timepatch; static patch_t* par; static patch_t* sucks; // "killers", "victims" static patch_t* killers; static patch_t* victims; // "Total", your face, your dead face static patch_t* total; static patch_t* star; static patch_t* bstar; // "red P[1..MAXPLAYERS]" static patch_t* p[MAXPLAYERS]; // "gray P[1..MAXPLAYERS]" static patch_t* bp[MAXPLAYERS]; // Name graphics of each level (centered) static patch_t** lnames; // Buffer storing the backdrop static patch_t *background; // // CODE // // slam background void WI_slamBackground(void) { V_DrawPatch(0, 0, background); } // The ticker is used to detect keys // because of timing issues in netgames. boolean WI_Responder(event_t* ev) { return false; } // Draws " Finished!" void WI_drawLF(void) { int y = WI_TITLEY; if (gamemode != commercial || wbs->last < NUMCMAPS) { // draw V_DrawPatch((SCREENWIDTH - SHORT(lnames[wbs->last]->width))/2, y, lnames[wbs->last]); // draw "Finished!" y += (5*SHORT(lnames[wbs->last]->height))/4; V_DrawPatch((SCREENWIDTH - SHORT(finished->width)) / 2, y, finished); } else if (wbs->last == NUMCMAPS) { // MAP33 - nothing is displayed! } else if (wbs->last > NUMCMAPS) { // > MAP33. Doom bombs out here with a Bad V_DrawPatch error. // I'm pretty sure that doom2.exe is just reading into random // bits of memory at this point, but let's try to be accurate // anyway. This deliberately triggers a V_DrawPatch error. patch_t tmp = { SCREENWIDTH, SCREENHEIGHT, 1, 1, { 0, 0, 0, 0, 0, 0, 0, 0 } }; V_DrawPatch(0, y, &tmp); } } // Draws "Entering " void WI_drawEL(void) { int y = WI_TITLEY; // draw "Entering" V_DrawPatch((SCREENWIDTH - SHORT(entering->width))/2, y, entering); // draw level y += (5*SHORT(lnames[wbs->next]->height))/4; V_DrawPatch((SCREENWIDTH - SHORT(lnames[wbs->next]->width))/2, y, lnames[wbs->next]); } void WI_drawOnLnode ( int n, patch_t* c[] ) { int i; int left; int top; int right; int bottom; boolean fits = false; i = 0; do { left = lnodes[wbs->epsd][n].x - SHORT(c[i]->leftoffset); top = lnodes[wbs->epsd][n].y - SHORT(c[i]->topoffset); right = left + SHORT(c[i]->width); bottom = top + SHORT(c[i]->height); if (left >= 0 && right < SCREENWIDTH && top >= 0 && bottom < SCREENHEIGHT) { fits = true; } else { i++; } } while (!fits && i!=2 && c[i] != NULL); if (fits && i<2) { V_DrawPatch(lnodes[wbs->epsd][n].x, lnodes[wbs->epsd][n].y, c[i]); } else { // DEBUG printf("Could not place patch on level %d", n+1); } } void WI_initAnimatedBack(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]; // 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; } } 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; } } } } void WI_drawAnimatedBack(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 (a->ctr >= 0) V_DrawPatch(a->loc.x, a->loc.y, a->p[a->ctr]); } } // // Draws a number. // If digits > 0, then use that many digits minimum, // otherwise only use as many as necessary. // Returns new x position. // int WI_drawNum ( int x, int y, int n, int digits ) { int fontwidth = SHORT(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; V_DrawPatch(x, y, num[ n % 10 ]); n /= 10; } // draw a minus sign if necessary if (neg) V_DrawPatch(x-=8, y, wiminus); return x; } void WI_drawPercent ( int x, int y, int p ) { if (p < 0) return; V_DrawPatch(x, y, percent); WI_drawNum(x, y, p, -1); } // // Display level completion time and par, // or "sucks" message if overflow. // void WI_drawTime ( int x, int y, int t ) { int div; int n; if (t<0) return; if (t <= 61*59) { div = 1; do { n = (t / div) % 60; x = WI_drawNum(x, y, n, 2) - SHORT(colon->width); div *= 60; // draw if (div==60 || t / div) V_DrawPatch(x, y, colon); } while (t / div); } else { // "sucks" V_DrawPatch(x - SHORT(sucks->width), y, sucks); } } void WI_End(void) { void WI_unloadData(void); WI_unloadData(); } void WI_initNoState(void) { state = NoState; acceleratestage = 0; cnt = 10; } void WI_updateNoState(void) { WI_updateAnimatedBack(); if (!--cnt) { // Don't call WI_End yet. G_WorldDone doesnt immediately // change gamestate, so WI_Drawer is still going to get // run until that happens. If we do that after WI_End // (which unloads all the graphics), we're in trouble. //WI_End(); G_WorldDone(); } } static boolean snl_pointeron = false; void WI_initShowNextLoc(void) { state = ShowNextLoc; acceleratestage = 0; cnt = SHOWNEXTLOCDELAY * TICRATE; WI_initAnimatedBack(); } void WI_updateShowNextLoc(void) { WI_updateAnimatedBack(); if (!--cnt || acceleratestage) WI_initNoState(); else snl_pointeron = (cnt & 31) < 20; } void WI_drawShowNextLoc(void) { int i; int last; WI_slamBackground(); // draw animated background WI_drawAnimatedBack(); if ( gamemode != commercial) { if (wbs->epsd > 2) { WI_drawEL(); 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) WI_drawEL(); } void WI_drawNoState(void) { snl_pointeron = true; WI_drawShowNextLoc(); } int WI_fragSum(int playernum) { int i; int frags = 0; for (i=0 ; i 99) dm_frags[i][j] = 99; if (dm_frags[i][j] < -99) dm_frags[i][j] = -99; stillticking = true; } } dm_totals[i] = WI_fragSum(i); if (dm_totals[i] > 99) dm_totals[i] = 99; if (dm_totals[i] < -99) dm_totals[i] = -99; } } 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; } } } void WI_drawDeathmatchStats(void) { int i; int j; int x; int y; int w; WI_slamBackground(); // draw animated background WI_drawAnimatedBack(); WI_drawLF(); // draw stat titles (top line) V_DrawPatch(DM_TOTALSX-SHORT(total->width)/2, DM_MATRIXY-WI_SPACINGY+10, total); V_DrawPatch(DM_KILLERSX, DM_KILLERSY, killers); V_DrawPatch(DM_VICTIMSX, DM_VICTIMSY, victims); // draw P? x = DM_MATRIXX + DM_SPACINGX; y = DM_MATRIXY; for (i=0 ; iwidth)/2, DM_MATRIXY - WI_SPACINGY, p[i]); V_DrawPatch(DM_MATRIXX-SHORT(p[i]->width)/2, y, p[i]); if (i == me) { V_DrawPatch(x-SHORT(p[i]->width)/2, DM_MATRIXY - WI_SPACINGY, bstar); V_DrawPatch(DM_MATRIXX-SHORT(p[i]->width)/2, y, star); } } else { // V_DrawPatch(x-SHORT(bp[i]->width)/2, // DM_MATRIXY - WI_SPACINGY, bp[i]); // V_DrawPatch(DM_MATRIXX-SHORT(bp[i]->width)/2, // y, bp[i]); } x += DM_SPACINGX; y += WI_SPACINGY; } // draw stats y = DM_MATRIXY+10; w = SHORT(num[0]->width); for (i=0 ; imaxkills; cnt_items[i] = (plrs[i].sitems * 100) / wbs->maxitems; cnt_secret[i] = (plrs[i].ssecret * 100) / wbs->maxsecret; if (dofrags) cnt_frags[i] = WI_fragSum(i); } S_StartSound(0, sfx_barexp); ng_state = 10; } if (ng_state == 2) { if (!(bcnt&3)) S_StartSound(0, sfx_pistol); stillticking = false; for (i=0 ; i= (plrs[i].skills * 100) / wbs->maxkills) cnt_kills[i] = (plrs[i].skills * 100) / wbs->maxkills; else stillticking = true; } 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= (plrs[i].ssecret * 100) / wbs->maxsecret) cnt_secret[i] = (plrs[i].ssecret * 100) / wbs->maxsecret; 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; } } } void WI_drawNetgameStats(void) { int i; int x; int y; int pwidth = SHORT(percent->width); WI_slamBackground(); // draw animated background WI_drawAnimatedBack(); WI_drawLF(); // draw stat titles (top line) V_DrawPatch(NG_STATSX+NG_SPACINGX-SHORT(kills->width), NG_STATSY, kills); V_DrawPatch(NG_STATSX+2*NG_SPACINGX-SHORT(items->width), NG_STATSY, items); V_DrawPatch(NG_STATSX+3*NG_SPACINGX-SHORT(secret->width), NG_STATSY, secret); if (dofrags) V_DrawPatch(NG_STATSX+4*NG_SPACINGX-SHORT(frags->width), NG_STATSY, frags); // draw stats y = NG_STATSY + SHORT(kills->height); for (i=0 ; iwidth), y, p[i]); if (i == me) V_DrawPatch(x-SHORT(p[i]->width), y, star); x += NG_SPACINGX; WI_drawPercent(x-pwidth, y+10, cnt_kills[i]); x += NG_SPACINGX; WI_drawPercent(x-pwidth, y+10, cnt_items[i]); x += NG_SPACINGX; WI_drawPercent(x-pwidth, y+10, cnt_secret[i]); x += NG_SPACINGX; if (dofrags) WI_drawNum(x, y+10, cnt_frags[i], -1); y += WI_SPACINGY; } } static int sp_state; void WI_initStats(void) { state = StatCount; acceleratestage = 0; sp_state = 1; cnt_kills[0] = cnt_items[0] = cnt_secret[0] = -1; cnt_time = cnt_par = -1; cnt_pause = TICRATE; WI_initAnimatedBack(); } 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; cnt_secret[0] = (plrs[me].ssecret * 100) / wbs->maxsecret; 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); if (cnt_secret[0] >= (plrs[me].ssecret * 100) / wbs->maxsecret) { cnt_secret[0] = (plrs[me].ssecret * 100) / wbs->maxsecret; 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_par += 3; if (cnt_par >= wbs->partime / TICRATE) { cnt_par = wbs->partime / TICRATE; if (cnt_time >= plrs[me].stime / 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; } } } void WI_drawStats(void) { // line height int lh; lh = (3*SHORT(num[0]->height))/2; WI_slamBackground(); // draw animated background WI_drawAnimatedBack(); WI_drawLF(); V_DrawPatch(SP_STATSX, SP_STATSY, kills); WI_drawPercent(SCREENWIDTH - SP_STATSX, SP_STATSY, cnt_kills[0]); V_DrawPatch(SP_STATSX, SP_STATSY+lh, items); WI_drawPercent(SCREENWIDTH - SP_STATSX, SP_STATSY+lh, cnt_items[0]); V_DrawPatch(SP_STATSX, SP_STATSY+2*lh, sp_secret); WI_drawPercent(SCREENWIDTH - SP_STATSX, SP_STATSY+2*lh, cnt_secret[0]); V_DrawPatch(SP_TIMEX, SP_TIMEY, timepatch); WI_drawTime(SCREENWIDTH/2 - SP_TIMEX, SP_TIMEY, cnt_time); if (wbs->epsd < 3) { V_DrawPatch(SCREENWIDTH/2 + SP_TIMEX, SP_TIMEY, par); WI_drawTime(SCREENWIDTH - SP_TIMEX, SP_TIMEY, cnt_par); } } 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; } } } // Updates stuff each tick 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; } } typedef void (*load_callback_t)(char *lumpname, patch_t **variable); // Common load/unload function. Iterates over all the graphics // lumps to be loaded/unloaded into memory. static void WI_loadUnloadData(load_callback_t callback) { int i, j; char name[9]; anim_t *a; if (gamemode == commercial) { for (i=0 ; iepsd, i); callback(name, &lnames[i]); } // you are here callback(DEH_String("WIURH0"), &yah[0]); // you are here (alt.) callback(DEH_String("WIURH1"), &yah[1]); // splat callback(DEH_String("WISPLAT"), &splat[0]); 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 DEH_snprintf(name, 9, "WIA%d%.2d%.2d", wbs->epsd, j, i); callback(name, &a->p[i]); } else { // HACK ALERT! a->p[i] = anims[1][4].p[i]; } } } } } // More hacks on minus sign. callback(DEH_String("WIMINUS"), &wiminus); for (i=0;i<10;i++) { // numbers 0-9 DEH_snprintf(name, 9, "WINUM%d", i); callback(name, &num[i]); } // percent sign callback(DEH_String("WIPCNT"), &percent); // "finished" callback(DEH_String("WIF"), &finished); // "entering" callback(DEH_String("WIENTER"), &entering); // "kills" callback(DEH_String("WIOSTK"), &kills); // "scrt" callback(DEH_String("WIOSTS"), &secret); // "secret" callback(DEH_String("WISCRT2"), &sp_secret); // french wad uses WIOBJ (?) if (W_CheckNumForName(DEH_String("WIOBJ")) >= 0) { // "items" if (netgame && !deathmatch) callback(DEH_String("WIOBJ"), &items); else callback(DEH_String("WIOSTI"), &items); } else { callback(DEH_String("WIOSTI"), &items); } // "frgs" callback(DEH_String("WIFRGS"), &frags); // ":" callback(DEH_String("WICOLON"), &colon); // "time" callback(DEH_String("WITIME"), &timepatch); // "sucks" callback(DEH_String("WISUCKS"), &sucks); // "par" callback(DEH_String("WIPAR"), &par); // "killers" (vertical) callback(DEH_String("WIKILRS"), &killers); // "victims" (horiz) callback(DEH_String("WIVCTMS"), &victims); // "total" callback(DEH_String("WIMSTT"), &total); for (i=0 ; iepsd == 3) { M_StringCopy(name, DEH_String("INTERPIC"), sizeof(name)); } else { DEH_snprintf(name, sizeof(name), "WIMAP%d", wbs->epsd); } // Draw backdrop and save to a temporary buffer callback(name, &background); } static void WI_loadCallback(char *name, patch_t **variable) { *variable = W_CacheLumpName(name, PU_STATIC); } void WI_loadData(void) { if (gamemode == commercial) { NUMCMAPS = 32; lnames = (patch_t **) Z_Malloc(sizeof(patch_t*) * NUMCMAPS, PU_STATIC, NULL); } else { lnames = (patch_t **) Z_Malloc(sizeof(patch_t*) * NUMMAPS, PU_STATIC, NULL); } WI_loadUnloadData(WI_loadCallback); // These two graphics are special cased because we're sharing // them with the status bar code // your face star = W_CacheLumpName(DEH_String("STFST01"), PU_STATIC); // dead face bstar = W_CacheLumpName(DEH_String("STFDEAD0"), PU_STATIC); } static void WI_unloadCallback(char *name, patch_t **variable) { W_ReleaseLumpName(name); *variable = NULL; } void WI_unloadData(void) { WI_loadUnloadData(WI_unloadCallback); // We do not free these lumps as they are shared with the status // bar code. // W_ReleaseLumpName("STFST01"); // W_ReleaseLumpName("STFDEAD0"); } 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; } } 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; if (!wbs->maxitems) wbs->maxitems = 1; if (!wbs->maxsecret) wbs->maxsecret = 1; if ( gamemode != retail ) if (wbs->epsd > 2) wbs->epsd -= 3; } void WI_Start(wbstartstruct_t* wbstartstruct) { WI_initVariables(wbstartstruct); WI_loadData(); if (deathmatch) WI_initDeathmatchStats(); else if (netgame) WI_initNetgameStats(); else WI_initStats(); } ================================================ FILE: fbdoom/wi_stuff.h ================================================ // // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // 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. // // DESCRIPTION: // Intermission. // #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); // Shut down the intermission screen void WI_End(void); #endif ================================================ FILE: fbdoom/z_zone.c ================================================ // // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // 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. // // DESCRIPTION: // Zone Memory Allocation. Neat. // #include "z_zone.h" #include "i_system.h" #include "doomtype.h" // // ZONE MEMORY ALLOCATION // // There is never any space between memblocks, // and there will never be two contiguous free memblocks. // The rover can be left pointing at a non-empty block. // // It is of no value to free a cachable block, // because it will get overwritten automatically if needed. // #define MEM_ALIGN sizeof(void *) #define ZONEID 0x1d4a11 typedef struct memblock_s { int size; // including the header and possibly tiny fragments void** user; int tag; // PU_FREE if this is free int id; // should be ZONEID struct memblock_s* next; struct memblock_s* prev; } memblock_t; typedef struct { // total bytes malloced, including header int size; // start / end cap for linked list memblock_t blocklist; memblock_t* rover; } memzone_t; memzone_t* mainzone; // // Z_ClearZone // void Z_ClearZone (memzone_t* zone) { memblock_t* block; // set the entire zone to one free block zone->blocklist.next = zone->blocklist.prev = block = (memblock_t *)( (byte *)zone + sizeof(memzone_t) ); zone->blocklist.user = (void *)zone; zone->blocklist.tag = PU_STATIC; zone->rover = block; block->prev = block->next = &zone->blocklist; // a free block. block->tag = PU_FREE; block->size = zone->size - sizeof(memzone_t); } // // Z_Init // void Z_Init (void) { memblock_t* block; int size; mainzone = (memzone_t *)I_ZoneBase (&size); mainzone->size = size; // set the entire zone to one free block mainzone->blocklist.next = mainzone->blocklist.prev = block = (memblock_t *)( (byte *)mainzone + sizeof(memzone_t) ); mainzone->blocklist.user = (void *)mainzone; mainzone->blocklist.tag = PU_STATIC; mainzone->rover = block; block->prev = block->next = &mainzone->blocklist; // free block block->tag = PU_FREE; block->size = mainzone->size - sizeof(memzone_t); } // // Z_Free // void Z_Free (void* ptr) { memblock_t* block; memblock_t* other; block = (memblock_t *) ( (byte *)ptr - sizeof(memblock_t)); if (block->id != ZONEID) I_Error ("Z_Free: freed a pointer without ZONEID"); if (block->tag != PU_FREE && block->user != NULL) { // clear the user's mark *block->user = 0; } // mark as free block->tag = PU_FREE; block->user = NULL; block->id = 0; other = block->prev; if (other->tag == PU_FREE) { // merge with previous free block other->size += block->size; other->next = block->next; other->next->prev = other; if (block == mainzone->rover) mainzone->rover = other; block = other; } other = block->next; if (other->tag == PU_FREE) { // merge the next free block onto the end block->size += other->size; block->next = other->next; block->next->prev = block; if (other == mainzone->rover) mainzone->rover = block; } } // // Z_Malloc // You can pass a NULL user if the tag is < PU_PURGELEVEL. // #define MINFRAGMENT 64 void* Z_Malloc ( int size, int tag, void* user ) { int extra; memblock_t* start; memblock_t* rover; memblock_t* newblock; memblock_t* base; void *result; size = (size + MEM_ALIGN - 1) & ~(MEM_ALIGN - 1); // scan through the block list, // looking for the first free block // of sufficient size, // throwing out any purgable blocks along the way. // account for size of block header size += sizeof(memblock_t); // if there is a free block behind the rover, // back up over them base = mainzone->rover; if (base->prev->tag == PU_FREE) base = base->prev; rover = base; start = base->prev; do { if (rover == start) { // scanned all the way around the list I_Error ("Z_Malloc: failed on allocation of %i bytes", size); } if (rover->tag != PU_FREE) { if (rover->tag < PU_PURGELEVEL) { // hit a block that can't be purged, // so move base past it base = rover = rover->next; } else { // free the rover block (adding the size to base) // the rover can be the base block base = base->prev; Z_Free ((byte *)rover+sizeof(memblock_t)); base = base->next; rover = base->next; } } else { rover = rover->next; } } while (base->tag != PU_FREE || base->size < size); // found a block big enough extra = base->size - size; if (extra > MINFRAGMENT) { // there will be a free fragment after the allocated block newblock = (memblock_t *) ((byte *)base + size ); newblock->size = extra; newblock->tag = PU_FREE; newblock->user = NULL; newblock->prev = base; newblock->next = base->next; newblock->next->prev = newblock; base->next = newblock; base->size = size; } if (user == NULL && tag >= PU_PURGELEVEL) I_Error ("Z_Malloc: an owner is required for purgable blocks"); base->user = user; base->tag = tag; result = (void *) ((byte *)base + sizeof(memblock_t)); if (base->user) { *base->user = result; } // next allocation will start looking here mainzone->rover = base->next; base->id = ZONEID; return result; } // // Z_FreeTags // void Z_FreeTags ( int lowtag, int hightag ) { memblock_t* block; memblock_t* next; for (block = mainzone->blocklist.next ; block != &mainzone->blocklist ; block = next) { // get link before freeing next = block->next; // free block? if (block->tag == PU_FREE) continue; if (block->tag >= lowtag && block->tag <= hightag) Z_Free ( (byte *)block+sizeof(memblock_t)); } } // // Z_DumpHeap // Note: TFileDumpHeap( stdout ) ? // void Z_DumpHeap ( int lowtag, int hightag ) { memblock_t* block; printf ("zone size: %i location: %p\n", mainzone->size,mainzone); printf ("tag range: %i to %i\n", lowtag, hightag); for (block = mainzone->blocklist.next ; ; block = block->next) { if (block->tag >= lowtag && block->tag <= hightag) printf ("block:%p size:%7i user:%p tag:%3i\n", block, block->size, block->user, block->tag); if (block->next == &mainzone->blocklist) { // all blocks have been hit break; } if ( (byte *)block + block->size != (byte *)block->next) printf ("ERROR: block size does not touch the next block\n"); if ( block->next->prev != block) printf ("ERROR: next block doesn't have proper back link\n"); if (block->tag == PU_FREE && block->next->tag == PU_FREE) printf ("ERROR: two consecutive free blocks\n"); } } // // Z_FileDumpHeap // void Z_FileDumpHeap (FILE* f) { memblock_t* block; fprintf (f,"zone size: %i location: %p\n",mainzone->size,mainzone); for (block = mainzone->blocklist.next ; ; block = block->next) { fprintf (f,"block:%p size:%7i user:%p tag:%3i\n", block, block->size, block->user, block->tag); if (block->next == &mainzone->blocklist) { // all blocks have been hit break; } if ( (byte *)block + block->size != (byte *)block->next) fprintf (f,"ERROR: block size does not touch the next block\n"); if ( block->next->prev != block) fprintf (f,"ERROR: next block doesn't have proper back link\n"); if (block->tag == PU_FREE && block->next->tag == PU_FREE) fprintf (f,"ERROR: two consecutive free blocks\n"); } } // // Z_CheckHeap // void Z_CheckHeap (void) { memblock_t* block; for (block = mainzone->blocklist.next ; ; block = block->next) { if (block->next == &mainzone->blocklist) { // all blocks have been hit break; } if ( (byte *)block + block->size != (byte *)block->next) I_Error ("Z_CheckHeap: block size does not touch the next block\n"); if ( block->next->prev != block) I_Error ("Z_CheckHeap: next block doesn't have proper back link\n"); if (block->tag == PU_FREE && block->next->tag == PU_FREE) I_Error ("Z_CheckHeap: two consecutive free blocks\n"); } } // // Z_ChangeTag // void Z_ChangeTag2(void *ptr, int tag, char *file, int line) { memblock_t* block; block = (memblock_t *) ((byte *)ptr - sizeof(memblock_t)); if (block->id != ZONEID) I_Error("%s:%i: Z_ChangeTag: block without a ZONEID!", file, line); if (tag >= PU_PURGELEVEL && block->user == NULL) I_Error("%s:%i: Z_ChangeTag: an owner is required " "for purgable blocks", file, line); block->tag = tag; } void Z_ChangeUser(void *ptr, void **user) { memblock_t* block; block = (memblock_t *) ((byte *)ptr - sizeof(memblock_t)); if (block->id != ZONEID) { I_Error("Z_ChangeUser: Tried to change user for invalid block!"); } block->user = user; *user = ptr; } // // Z_FreeMemory // int Z_FreeMemory (void) { memblock_t* block; int free; free = 0; for (block = mainzone->blocklist.next ; block != &mainzone->blocklist; block = block->next) { if (block->tag == PU_FREE || block->tag >= PU_PURGELEVEL) free += block->size; } return free; } unsigned int Z_ZoneSize(void) { return mainzone->size; } ================================================ FILE: fbdoom/z_zone.h ================================================ // // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // 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. // // 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. // #ifndef __Z_ZONE__ #define __Z_ZONE__ #include // // ZONE MEMORY // PU - purge tags. enum { PU_STATIC = 1, // static entire execution time PU_SOUND, // static while playing PU_MUSIC, // static while playing PU_FREE, // a free block PU_LEVEL, // static until level exited PU_LEVSPEC, // a special thinker in a level // Tags >= PU_PURGELEVEL are purgable whenever needed. PU_PURGELEVEL, PU_CACHE, // Total number of different tag types PU_NUM_TAGS }; void Z_Init (void); void* Z_Malloc (int size, int tag, void *ptr); void Z_Free (void *ptr); void Z_FreeTags (int lowtag, int hightag); void Z_DumpHeap (int lowtag, int hightag); void Z_FileDumpHeap (FILE *f); void Z_CheckHeap (void); void Z_ChangeTag2 (void *ptr, int tag, char *file, int line); void Z_ChangeUser(void *ptr, void **user); int Z_FreeMemory (void); unsigned int Z_ZoneSize(void); // // This is used to get the local FILE:LINE info from CPP // prior to really call the function in question. // #define Z_ChangeTag(p,t) \ Z_ChangeTag2((p), (t), __FILE__, __LINE__) #endif ================================================ FILE: ipx/DOOMNET.C ================================================ //#define DOOM2 #include #include #include #include #include #include #include "doomnet.h" //#include "ipxstr.h" #include "ipx_frch.h" // FRENCH VERSION doomcom_t doomcom; int vectorishooked; void interrupt (*olddoomvect) (void); /* ============= = = LaunchDOOM = These fields in doomcom should be filled in before calling: short numnodes; // console is allways node 0 short ticdup; // 1 = no duplication, 2-5 = dup for slow nets short extratics; // 1 = send a backup tic in every packet short consoleplayer; // 0-3 = player number short numplayers; // 1-4 short angleoffset; // 1 = left, 0 = center, -1 = right short drone; // 1 = drone ============= */ void LaunchDOOM (void) { char *newargs[99]; char adrstring[10]; long flatadr; // prepare for DOOM doomcom.id = DOOMCOM_ID; // hook the interrupt vector olddoomvect = getvect (doomcom.intnum); setvect (doomcom.intnum,(void interrupt (*)(void))MK_FP(_CS, (int)NetISR)); vectorishooked = 1; // build the argument list for DOOM, adding a -net &doomcom memcpy (newargs, _argv, (_argc+1)*2); newargs[_argc] = "-net"; flatadr = (long)_DS*16 + (unsigned)&doomcom; sprintf (adrstring,"%lu",flatadr); newargs[_argc+1] = adrstring; newargs[_argc+2] = NULL; if (!access("doom2.exe",0)) spawnv (P_WAIT, "doom2", newargs); else spawnv (P_WAIT, "doom", newargs); #ifdef DOOM2 printf (STR_RETURNED"\n"); #else printf ("Returned from DOOM\n"); #endif } ================================================ FILE: ipx/DOOMNET.H ================================================ // doomnet.h #define PEL_WRITE_ADR 0x3c8 #define PEL_DATA 0x3c9 #define I_ColorBlack(r,g,b) {outp(PEL_WRITE_ADR,0);outp(PEL_DATA,r);outp(PEL_DATA,g);outp(PEL_DATA,b);}; #define MAXNETNODES 8 // max computers in a game #define MAXPLAYERS 4 // 4 players max + drones #define CMD_SEND 1 #define CMD_GET 2 #define DOOMCOM_ID 0x12345678l typedef struct { long id; short intnum; // DOOM executes an int to send commands // communication between DOOM and the driver short command; // CMD_SEND or CMD_GET short remotenode; // dest for send, set by get (-1 = no packet) short datalength; // bytes in doomdata to be sent / bytes read // info common to all nodes short numnodes; // console is allways node 0 short ticdup; // 1 = no duplication, 2-5 = dup for slow nets short extratics; // 1 = send a backup tic in every packet short deathmatch; // 1 = deathmatch short savegame; // -1 = new game, 0-5 = load savegame short episode; // 1-3 short map; // 1-9 short skill; // 1-5 // info specific to this node short consoleplayer; // 0-3 = player number short numplayers; // 1-4 short angleoffset; // 1 = left, 0 = center, -1 = right short drone; // 1 = drone // packet data to be sent char data[512]; } doomcom_t; extern doomcom_t doomcom; extern void interrupt (*olddoomvect) (void); extern int vectorishooked; int CheckParm (char *check); void LaunchDOOM (void); void interrupt NetISR (void); ================================================ FILE: ipx/IPXNET.C ================================================ // ipxnet.c #include #include #include #include #include #include #include "ipxnet.h" /* ========================================================================== === IPX PACKET DRIVER ========================================================================== === */ packet_t packets[NUMPACKETS]; nodeadr_t nodeadr[MAXNETNODES+1]; // first is local, last is broadcast nodeadr_t remoteadr; // set by each GetPacket localadr_t localadr; // set at startup extern int socketid; void far (*IPX)(void); long localtime; // for time stamp in packets long remotetime; //=========================================================================== int OpenSocket(short socketNumber) { _DX = socketNumber; _BX = 0; _AL = 0; IPX(); if(_AL) Error ("OpenSocket: 0x%x", _AL); return _DX; } void CloseSocket(short socketNumber) { _DX = socketNumber; _BX = 1; IPX(); } void ListenForPacket(ECB *ecb) { _SI = FP_OFF(ecb); _ES = FP_SEG(ecb); _BX = 4; IPX(); if(_AL) Error ("ListenForPacket: 0x%x", _AL); } void GetLocalAddress (void) { _SI = FP_OFF(&localadr); _ES = FP_SEG(&localadr); _BX = 9; IPX(); } /* ==================== = = InitNetwork = ==================== */ void InitNetwork (void) { int i,j; // // get IPX function address // _AX = 0x7a00; geninterrupt(0x2f); if(_AL != 0xff) Error ("IPX not detected\n"); IPX = MK_FP(_ES, _DI); // // allocate a socket for sending and receiving // socketid = OpenSocket ( (socketid>>8) + ((socketid&255)<<8) ); GetLocalAddress(); // // set up several receiving ECBs // memset (packets,0,NUMPACKETS*sizeof(packet_t)); for (i=1 ; i>8; packets[0].ecb.f2Address[0] = FP_OFF(&doomcom.data); packets[0].ecb.f2Address[1] = FP_SEG(&doomcom.data); // known local node at 0 for (i=0 ; i<6 ; i++) nodeadr[0].node[i] = localadr.node[i]; // broadcast node at MAXNETNODES for (j=0 ; j<6 ; j++) nodeadr[MAXNETNODES].node[j] = 0xff; } /* ==================== = = ShutdownNetwork = ==================== */ void ShutdownNetwork (void) { if (IPX) CloseSocket (socketid); } /* ============== = = SendPacket = = A destination of MAXNETNODES is a broadcast ============== */ void SendPacket (int destination) { int j; // set the time packets[0].time = localtime; // set the address for (j=0 ; j<6 ; j++) packets[0].ipx.dNode[j] = packets[0].ecb.ImmediateAddress[j] = nodeadr[destination].node[j]; // set the length (ipx + time + datalength) packets[0].ecb.fSize = sizeof(IPXPacket) + 4; packets[0].ecb.f2Size = doomcom.datalength + 4; // send the packet _SI = FP_OFF(&packets[0]); _ES = FP_SEG(&packets[0]); _BX = 3; IPX(); if(_AL) Error("SendPacket: 0x%x", _AL); while(packets[0].ecb.InUseFlag != 0) { // IPX Relinquish Control - polled drivers MUST have this here! _BX = 10; IPX(); } } unsigned short ShortSwap (unsigned short i) { return ((i&255)<<8) + ((i>>8)&255); } /* ============== = = GetPacket = = Returns false if no packet is waiting = ============== */ int GetPacket (void) { int packetnum; int i, j; long besttic; packet_t *packet; // if multiple packets are waiting, return them in order by time besttic = MAXLONG; packetnum = -1; doomcom.remotenode = -1; for ( i = 1 ; i < NUMPACKETS ; i++) { if (packets[i].ecb.InUseFlag) { continue; } if (packets[i].time < besttic) { besttic = packets[i].time; packetnum = i; } } if (besttic == MAXLONG) return 0; // no packets packet = &packets[packetnum]; if (besttic == -1 && localtime != -1) { ListenForPacket (&packet->ecb); return 0; // setup broadcast from other game } remotetime = besttic; // // got a good packet // if (packet->ecb.CompletionCode) Error ("GetPacket: ecb.ComletionCode = 0x%x",packet->ecb.CompletionCode); // set remoteadr to the sender of the packet memcpy (&remoteadr, packet->ipx.sNode, sizeof(remoteadr)); for (i=0 ; iecb); return 0; } } // copy out the data doomcom.datalength = ShortSwap(packet->ipx.PacketLength) - 38; memcpy (&doomcom.data, &packet->data, doomcom.datalength); // repost the ECB ListenForPacket (&packet->ecb); return 1; } ================================================ FILE: ipx/IPXNET.H ================================================ // ipxnet.h typedef struct { char private[512]; } doomdata_t; #include "DoomNet.h" //=========================================================================== #define NUMPACKETS 10 // max outstanding packets before loss // setupdata_t is used as doomdata_t during setup typedef struct { short gameid; // so multiple games can setup at once short drone; short nodesfound; short nodeswanted; } setupdata_t; typedef unsigned char BYTE; typedef unsigned short WORD; typedef unsigned long LONG; typedef struct IPXPacketStructure { WORD PacketCheckSum; /* high-low */ WORD PacketLength; /* high-low */ BYTE PacketTransportControl; BYTE PacketType; BYTE dNetwork[4]; /* high-low */ BYTE dNode[6]; /* high-low */ BYTE dSocket[2]; /* high-low */ BYTE sNetwork[4]; /* high-low */ BYTE sNode[6]; /* high-low */ BYTE sSocket[2]; /* high-low */ } IPXPacket; typedef struct { BYTE network[4]; /* high-low */ BYTE node[6]; /* high-low */ } localadr_t; typedef struct { BYTE node[6]; /* high-low */ } nodeadr_t; typedef struct ECBStructure { WORD Link[2]; /* offset-segment */ WORD ESRAddress[2]; /* offset-segment */ BYTE InUseFlag; BYTE CompletionCode; WORD ECBSocket; /* high-low */ BYTE IPXWorkspace[4]; /* N/A */ BYTE DriverWorkspace[12]; /* N/A */ BYTE ImmediateAddress[6]; /* high-low */ WORD FragmentCount; /* low-high */ WORD fAddress[2]; /* offset-segment */ WORD fSize; /* low-high */ WORD f2Address[2]; /* offset-segment */ WORD f2Size; /* low-high */ } ECB; // time is used by the communication driver to sequence packets returned // to DOOM when more than one is waiting typedef struct { ECB ecb; IPXPacket ipx; long time; doomdata_t data; } packet_t; extern doomcom_t doomcom; extern int gameid; extern nodeadr_t nodeadr[MAXNETNODES+1]; extern int localnodenum; extern long localtime; // for time stamp in packets extern long remotetime; // timestamp of last packet gotten extern nodeadr_t remoteadr; extern int myargc; extern char **myargv; void Error (char *error, ...); void InitNetwork (void); void ShutdownNetwork (void); void SendPacket (int destination); int GetPacket (void); int CheckParm (char *check); void PrintAddress (nodeadr_t *adr, char *str); ================================================ FILE: ipx/IPXSETUP.C ================================================ // ipxsetup.c #define DOOM2 #include #include #include #include #include #include #include #include #include "ipxnet.h" //#include "ipxstr.h" #include "ipx_frch.h" // FRENCH VERSION int gameid; int numnetnodes; int socketid = 0x869c; // 0x869c is the official DOOM socket int myargc; char **myargv; setupdata_t nodesetup[MAXNETNODES]; /* ================= = = Error = = For abnormal program terminations = ================= */ void Error (char *error, ...) { va_list argptr; if (vectorishooked) setvect (doomcom.intnum,olddoomvect); va_start (argptr,error); vprintf (error,argptr); va_end (argptr); printf ("\n"); ShutdownNetwork (); exit (1); } /* ================= = = 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 CheckParm(char *parm) { int i; for(i = 1; i < myargc; i++) if(stricmp(parm, myargv[i]) == 0) return i; return 0; } /* ============= = = NetISR = ============= */ void interrupt NetISR (void) { if (doomcom.command == CMD_SEND) { localtime++; SendPacket (doomcom.remotenode); } else if (doomcom.command == CMD_GET) { GetPacket (); } } /* =================== = = LookForNodes = = Finds all the nodes for the game and works out player numbers among them = = Exits with nodesetup[0..numnodes] and nodeadr[0..numnodes] filled in =================== */ void LookForNodes (void) { int i,j,k; int netids[MAXNETNODES]; int netplayer[MAXNETNODES]; struct time time; int oldsec; setupdata_t *setup, *dest; char str[80]; int total, console; // // wait until we get [numnetnodes] packets, then start playing // the playernumbers are assigned by netid // printf(STR_ATTEMPT, numnetnodes); printf (STR_LOOKING); oldsec = -1; setup = (setupdata_t *)&doomcom.data; localtime = -1; // in setup time, not game time // // build local setup info // nodesetup[0].nodesfound = 1; nodesetup[0].nodeswanted = numnetnodes; doomcom.numnodes = 1; do { // // check for aborting // while ( bioskey(1) ) { if ( (bioskey (0) & 0xff) == 27) Error ("\n\n"STR_NETABORT); } // // listen to the network // while (GetPacket ()) { if (doomcom.remotenode == -1) dest = &nodesetup[doomcom.numnodes]; else dest = &nodesetup[doomcom.remotenode]; if (remotetime != -1) { // an early game packet, not a setup packet if (doomcom.remotenode == -1) Error (STR_UNKNOWN); // if it allready started, it must have found all nodes dest->nodesfound = dest->nodeswanted; continue; } // update setup ingo memcpy (dest, setup, sizeof(*dest) ); if (doomcom.remotenode != -1) continue; // allready know that node address // // this is a new node // memcpy (&nodeadr[doomcom.numnodes], &remoteadr , sizeof(nodeadr[doomcom.numnodes]) ); // // if this node has a lower address, take all startup info // if ( memcmp (&remoteadr, &nodeadr[0], sizeof(&remoteadr) ) < 0 ) { } doomcom.numnodes++; printf ("\n"STR_FOUND"\n"); if (doomcom.numnodes < numnetnodes) printf (STR_LOOKING); } // // we are done if all nodes have found all other nodes // for (i=0 ; i MAXPLAYERS) Error (STR_MORETHAN,MAXPLAYERS); if (memcmp (&nodeadr[i], &nodeadr[0], sizeof(nodeadr[0])) < 0) console++; } if (!total) Error (STR_NONESPEC); doomcom.consoleplayer = console; doomcom.numplayers = total; printf (STR_CONSOLEIS"\n", console+1, total); } //======================================================== // // Find a Response File // //======================================================== void FindResponseFile (void) { int i; #define MAXARGVS 100 for (i = 1;i < myargc;i++) if (myargv[i][0] == '@') { FILE * handle; int size; int k; int index; int indexinfile; char *infile; char *file; char *moreargs[20]; char *firstargv; // READ THE RESPONSE FILE INTO MEMORY handle = fopen (&myargv[i][1],"rb"); if (!handle) Error (STR_NORESP); printf(STR_FOUNDRESP" \"%s\"!\n",strupr(&myargv[i][1])); fseek (handle,0,SEEK_END); size = ftell(handle); fseek (handle,0,SEEK_SET); file = malloc (size); fread (file,size,1,handle); fclose (handle); // KEEP ALL CMDLINE ARGS FOLLOWING @RESPONSEFILE ARG for (index = 0,k = i+1; k < myargc; k++) moreargs[index++] = myargv[k]; firstargv = myargv[0]; myargv = malloc(sizeof(char *)*MAXARGVS); memset(myargv,0,sizeof(char *)*MAXARGVS); myargv[0] = firstargv; infile = file; indexinfile = k = 0; indexinfile++; // SKIP PAST ARGV[0] (KEEP IT) do { myargv[indexinfile++] = infile+k; while(k < size && ((*(infile+k)>= ' '+1) && (*(infile+k)<='z'))) k++; *(infile+k) = 0; while(k < size && ((*(infile+k)<= ' ') || (*(infile+k)>'z'))) k++; } while(k < size); for (k = 0;k < index;k++) myargv[indexinfile++] = moreargs[k]; myargc = indexinfile; // DISPLAY ARGS // printf("%d command-line args:\n",myargc); // for (k=1;k parameter." #define STR_COMMVECT "Communicating with interrupt vector 0x%x" #define STR_USEALT "Using alternate port %i for network" #define STR_RETURNED "Returned from DOOM II" #define STR_ATTEMPT "Attempting to find all players for %i player net play. "\ "Press ESC to exit.\n" ================================================ FILE: ipx/IPX_FRCH.H ================================================ #define STR_NETABORT "Synchronisation du jeu sur rseau annule." #define STR_UNKNOWN "Paquet de jeu inconnu durant la configuration" #define STR_FOUND "Noeud dtect!" #define STR_LOOKING "Recherche d'un noeud" #define STR_MORETHAN "Plus de %i joueurs spcifis!" #define STR_NONESPEC "Pas de joueurs spcifis pour le jeu!" #define STR_CONSOLEIS "Console: joueur %i sur %i" #define STR_NORESP "Ce fichier de rponse n'existe pas!" #define STR_FOUNDRESP "Fichier de rponse trouv" #define STR_DOOMNETDRV "GESTIONNAIRE DE RESEAU DOOM II" #define STR_VECTSPEC "Le vecteur spcifi (0x%02x) tait dj connect." #define STR_NONULL \ "Attention: pas de vecteurs d'interruption NULL ou iret trouvs entre 0x60 et 0x66.\n"\ "Vous pouvez spcifier un vecteur avec le paramtre -vector 0x." #define STR_COMMVECT "Communication avec le vecteur d'interruption 0x%x" #define STR_USEALT "Utilisation du port alternatif %i pour le rseau" #define STR_RETURNED "Retour de DOOM II" #define STR_ATTEMPT \ "Tentatative de recherche de tous les joueurs pour le jeu en riseau `%i jouers\n" \ "Appuyez sur ECHAP pour quitter.\n" ================================================ FILE: ipx/README ================================================ This is the source for the DOOM ipx network driver. ================================================ FILE: sersrc/DOOMNET.C ================================================ #include #include #include #include #include #include #include "doomnet.h" //#include "serstr.h" #include "ser_frch.h" // FRENCH VERSION #define DOOM2 extern int myargc; extern char **myargv; doomcom_t doomcom; int vectorishooked; void interrupt (*olddoomvect) (void); /* ================= = = 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 CheckParm (char *check) { int i; for (i = 1;i= inque.head) return -1; c = inque.data[inque.tail&(QUESIZE-1)]; inque.tail++; return c; } void write_byte( int c ) { outque.data[outque.head&(QUESIZE-1)] = c; outque.head++; } //========================================================================== /* ============== = = isr_8250 = ============== */ void interrupt isr_8250(void) { int c; while (1) { switch( INPUT( uart + INTERRUPT_ID_REGISTER ) & 7 ) { // not enabled case IIR_MODEM_STATUS_INTERRUPT : modem_status = INPUT( uart + MODEM_STATUS_REGISTER ); break; // not enabled case IIR_LINE_STATUS_INTERRUPT : line_status = INPUT( uart + LINE_STATUS_REGISTER ); break; // // transmit // case IIR_TX_HOLDING_REGISTER_INTERRUPT : //I_ColorBlack (63,0,0); if (outque.tail < outque.head) { c = outque.data[outque.tail&(QUESIZE-1)]; outque.tail++; OUTPUT( uart + TRANSMIT_HOLDING_REGISTER, c ); } break; // // receive // case IIR_RX_DATA_READY_INTERRUPT : //I_ColorBlack (0,63,0); c = INPUT( uart + RECEIVE_BUFFER_REGISTER ); inque.data[inque.head&(QUESIZE-1)] = c; inque.head++; break; // // done // default : //I_ColorBlack (0,0,0); OUTPUT( 0x20, 0x20 ); return; } } } /* ============== = = isr_16550 = ============== */ void interrupt isr_16550(void) { int c; int count; while (1) { switch( INPUT( uart + INTERRUPT_ID_REGISTER ) & 7 ) { // not enabled case IIR_MODEM_STATUS_INTERRUPT : modem_status = INPUT( uart + MODEM_STATUS_REGISTER ); break; // not enabled case IIR_LINE_STATUS_INTERRUPT : line_status = INPUT( uart + LINE_STATUS_REGISTER ); break; // // transmit // case IIR_TX_HOLDING_REGISTER_INTERRUPT : //I_ColorBlack (63,0,0); count = 16; while (outque.tail < outque.head && count--) { c = outque.data[outque.tail&(QUESIZE-1)]; outque.tail++; OUTPUT( uart + TRANSMIT_HOLDING_REGISTER, c ); } break; // // receive // case IIR_RX_DATA_READY_INTERRUPT : //I_ColorBlack (0,63,0); do { c = INPUT( uart + RECEIVE_BUFFER_REGISTER ); inque.data[inque.head&(QUESIZE-1)] = c; inque.head++; } while (INPUT( uart + LINE_STATUS_REGISTER ) & LSR_DATA_READY ); break; // // done // default : //I_ColorBlack (0,0,0); OUTPUT( 0x20, 0x20 ); return; } } } /* =============== = = jump_start = = Start up the transmition interrupts by sending the first char =============== */ void jump_start( void ) { int c; if (outque.tail < outque.head) { c = outque.data [outque.tail&(QUESIZE-1)]; outque.tail++; OUTPUT( uart, c ); } } ================================================ FILE: sersrc/README.TXT ================================================ This is the source for the DOOM serial / modem driver. ================================================ FILE: sersrc/SERSETUP.C ================================================ // sersetup.c #define DOOM2 #include "sersetup.h" //#include "serstr.h" #include "ser_frch.h" // FRENCH VERSION #include "DoomNet.h" extern que_t inque, outque; void jump_start( void ); extern int uart; int usemodem; char startup[256], shutdown[256], baudrate[256]; extern int baudbits; void ModemCommand (char *str); int myargc; char **myargv; //====================================== // // I_Error // //====================================== void I_Error(char *string) { printf("%s\n",string); exit(1); } /* ================ = = write_buffer = ================ */ void write_buffer( char *buffer, unsigned int count ) { int i; // if this would overrun the buffer, throw everything else out if (outque.head-outque.tail+count > QUESIZE) outque.tail = outque.head; while (count--) write_byte (*buffer++); if ( INPUT( uart + LINE_STATUS_REGISTER ) & 0x40) jump_start(); } /* ================= = = Error = = For abnormal program terminations = ================= */ void Error (char *error, ...) { va_list argptr; if (usemodem) { printf ("\n"); printf ("\n"STR_DROPDTR"\n"); OUTPUT(uart+MODEM_CONTROL_REGISTER, INPUT(uart+MODEM_CONTROL_REGISTER)&~MCR_DTR); delay (1250); OUTPUT( uart + MODEM_CONTROL_REGISTER, INPUT( uart + MODEM_CONTROL_REGISTER ) | MCR_DTR ); ModemCommand("+++"); delay (1250); ModemCommand(shutdown); delay (1250); } ShutdownPort (); if (vectorishooked) setvect (doomcom.intnum,olddoomvect); if (error) { va_start (argptr,error); vprintf (error,argptr); va_end (argptr); printf ("\n"); exit (1); } printf (STR_CLEANEXIT"\n"); exit (0); } /* ================ = = ReadPacket = ================ */ #define MAXPACKET 512 #define FRAMECHAR 0x70 char packet[MAXPACKET]; int packetlen; int inescape; int newpacket; boolean ReadPacket (void) { int c; // if the buffer has overflowed, throw everything out if (inque.head-inque.tail > QUESIZE - 4) // check for buffer overflow { inque.tail = inque.head; newpacket = true; return false; } if (newpacket) { packetlen = 0; newpacket = 0; } do { c = read_byte (); if (c < 0) return false; // haven't read a complete packet //printf ("%c",c); if (inescape) { inescape = false; if (c!=FRAMECHAR) { newpacket = 1; return true; // got a good packet } } else if (c==FRAMECHAR) { inescape = true; continue; // don't know yet if it is a terminator } // or a literal FRAMECHAR if (packetlen >= MAXPACKET) continue; // oversize packet packet[packetlen] = c; packetlen++; } while (1); } /* ============= = = WritePacket = ============= */ void WritePacket (char *buffer, int len) { int b; char static localbuffer[MAXPACKET*2+2]; b = 0; if (len > MAXPACKET) return; while (len--) { if (*buffer == FRAMECHAR) localbuffer[b++] = FRAMECHAR; // escape it for literal localbuffer[b++] = *buffer++; } localbuffer[b++] = FRAMECHAR; localbuffer[b++] = 0; write_buffer (localbuffer, b); } /* ============= = = NetISR = ============= */ void interrupt NetISR (void) { if (doomcom.command == CMD_SEND) { //I_ColorBlack (0,0,63); WritePacket ((char *)&doomcom.data, doomcom.datalength); } else if (doomcom.command == CMD_GET) { //I_ColorBlack (63,63,0); if (ReadPacket () && packetlen <= sizeof(doomcom.data) ) { doomcom.remotenode = 1; doomcom.datalength = packetlen; memcpy (&doomcom.data, &packet, packetlen); } else doomcom.remotenode = -1; } //I_ColorBlack (0,0,0); } /* ================= = = Connect = = Figures out who is player 0 and 1 ================= */ void Connect (void) { struct time time; int oldsec; int localstage, remotestage; char str[20]; char idstr[7]; char remoteidstr[7]; unsigned long idnum; int i; // // wait for a good packet // printf (STR_ATTEMPT"\n"); // // build a (hopefully) unique id string by hashing up the current milliseconds // and the interrupt table // if (CheckParm ("-player1")) idnum = 0; else if (CheckParm ("-player2")) idnum = 999999; else { gettime (&time); idnum = time.ti_sec*100+time.ti_hund; for (i=0 ; i<512 ; i++) idnum += ((unsigned far *)0)[i]; idnum %= 1000000; } idstr[0] = '0' + idnum/ 100000l; idnum -= (idstr[0]-'0')*100000l; idstr[1] = '0' + idnum/ 10000l; idnum -= (idstr[1]-'0')*10000l; idstr[2] = '0' + idnum/ 1000l; idnum -= (idstr[2]-'0')*1000l; idstr[3] = '0' + idnum/ 100l; idnum -= (idstr[3]-'0')*100l; idstr[4] = '0' + idnum/ 10l; idnum -= (idstr[4]-'0')*10l; idstr[5] = '0' + idnum; idstr[6] = 0; // // sit in a loop until things are worked out // // the packet is: ID000000_0 // the first field is the idnum, the second is the acknowledge stage // ack stage starts out 0, is bumped to 1 after the other computer's id // is known, and is bumped to 2 after the other computer has raised to 1 // oldsec = -1; localstage = remotestage = 0; do { while ( bioskey(1) ) { if ( (bioskey (0) & 0xff) == 27) Error ("\n\n"STR_NETABORT); } if (ReadPacket ()) { packet[packetlen] = 0; printf ("read : %s\n",packet); if (packetlen != 10) continue; if (strncmp(packet,"ID",2) ) continue; if (!strncmp (packet+2,idstr,6)) Error ("\n\n"STR_DUPLICATE); strncpy (remoteidstr,packet+2,6); remotestage = packet[9] - '0'; localstage = remotestage+1; oldsec = -1; } gettime (&time); if (time.ti_sec != oldsec) { oldsec = time.ti_sec; sprintf (str,"ID%s_%i",idstr,localstage); WritePacket (str,strlen(str)); printf ("wrote: %s\n",str); } } while (localstage < 2); // // decide who is who // if (strcmp(remoteidstr,idstr) > 0) doomcom.consoleplayer = 0; else doomcom.consoleplayer = 1; // // flush out any extras // while (ReadPacket ()) ; } /* ============== = = ModemCommand = ============== */ void ModemCommand (char *str) { int i,l; printf (STR_MODEMCMD,str); l = strlen(str); for (i=0 ; i=' ') { response[respptr] = c; respptr++; } } while (1); } while (strncmp(response,resp,strlen(resp))); } /* ============= = = ReadLine = ============= */ void ReadLine (FILE *f, char *dest) { int c; do { c = fgetc (f); if (c == EOF || c == '\r' || c == '\n') break; *dest++ = c; } while (1); *dest = 0; } /* ============= = = ReadModemCfg = ============= */ void ReadModemCfg (void) { int mcr; FILE *f; unsigned baud; f = fopen ("modem.cfg","r"); if (!f) Error (STR_CANTREAD); ReadLine (f, startup); ReadLine (f, shutdown); ReadLine (f, baudrate); fclose (f); baud = atol(baudrate); if (baud) baudbits = 115200l/baud; usemodem = true; } /* ============= = = Dial = ============= */ void Dial (void) { char cmd[80]; int p; ModemCommand(startup); ModemResponse ("OK"); printf ("\n"STR_DIALING"\n\n"); p = CheckParm ("-dial"); sprintf (cmd,"ATDT%s",myargv[p+1]); ModemCommand(cmd); ModemResponse (STR_CONNECT); doomcom.consoleplayer = 1; } /* ============= = = Answer = ============= */ void Answer (void) { ModemCommand(startup); ModemResponse ("OK"); printf ("\n"STR_WAITRING"\n\n"); ModemResponse (STR_RING); ModemCommand ("ATA"); ModemResponse (STR_CONNECT); doomcom.consoleplayer = 0; } //======================================================== // // Find a Response File // //======================================================== void FindResponseFile (void) { int i; #define MAXARGVS 100 for (i = 1;i < myargc;i++) if (myargv[i][0] == '@') { FILE * handle; int size; int k; int index; int indexinfile; char *infile; char *file; char *moreargs[20]; char *firstargv; // READ THE RESPONSE FILE INTO MEMORY handle = fopen (&myargv[i][1],"rb"); if (!handle) I_Error (STR_NORESP); printf("Found response file \"%s\"!\n",strupr(&myargv[i][1])); fseek (handle,0,SEEK_END); size = ftell(handle); fseek (handle,0,SEEK_SET); file = malloc (size); fread (file,size,1,handle); fclose (handle); // KEEP ALL CMDLINE ARGS FOLLOWING @RESPONSEFILE ARG for (index = 0,k = i+1; k < myargc; k++) moreargs[index++] = myargv[k]; firstargv = myargv[0]; myargv = malloc(sizeof(char *)*MAXARGVS); memset(myargv,0,sizeof(char *)*MAXARGVS); myargv[0] = firstargv; infile = file; indexinfile = k = 0; indexinfile++; // SKIP PAST ARGV[0] (KEEP IT) do { myargv[indexinfile++] = infile+k; while(k < size && ((*(infile+k)>= ' '+1) && (*(infile+k)<='z'))) k++; *(infile+k) = 0; while(k < size && ((*(infile+k)<= ' ') || (*(infile+k)>'z'))) k++; } while(k < size); for (k = 0;k < index;k++) myargv[indexinfile++] = moreargs[k]; myargc = indexinfile; // DISPLAY ARGS // printf("%d command-line args:\n",myargc); // for (k=1;k #include #include #include #include #include #include #include #include #include #include #define INPUT(port) inportb(port) #define OUTPUT(port,data) outportb(port,data) #define CLI() disable() #define STI() enable() typedef enum {false, true} boolean; typedef unsigned char byte; #define TRANSMIT_HOLDING_REGISTER 0x00 #define RECEIVE_BUFFER_REGISTER 0x00 #define INTERRUPT_ENABLE_REGISTER 0x01 #define IER_RX_DATA_READY 0x01 #define IER_TX_HOLDING_REGISTER_EMPTY 0x02 #define IER_LINE_STATUS 0x04 #define IER_MODEM_STATUS 0x08 #define INTERRUPT_ID_REGISTER 0x02 #define IIR_MODEM_STATUS_INTERRUPT 0x00 #define IIR_TX_HOLDING_REGISTER_INTERRUPT 0x02 #define IIR_RX_DATA_READY_INTERRUPT 0x04 #define IIR_LINE_STATUS_INTERRUPT 0x06 #define FIFO_CONTROL_REGISTER 0x02 #define FCR_FIFO_ENABLE 0x01 #define FCR_RCVR_FIFO_RESET 0x02 #define FCR_XMIT_FIFO_RESET 0x04 #define FCR_RCVR_TRIGGER_LSB 0x40 #define FCR_RCVR_TRIGGER_MSB 0x80 #define FCR_TRIGGER_01 0x00 #define FCR_TRIGGER_04 0x40 #define FCR_TRIGGER_08 0x80 #define FCR_TRIGGER_14 0xc0 #define LINE_CONTROL_REGISTER 0x03 #define LCR_WORD_LENGTH_MASK 0x03 #define LCR_WORD_LENGTH_SELECT_0 0x01 #define LCR_WORD_LENGTH_SELECT_1 0x02 #define LCR_STOP_BITS 0x04 #define LCR_PARITY_MASK 0x38 #define LCR_PARITY_ENABLE 0x08 #define LCR_EVEN_PARITY_SELECT 0x10 #define LCR_STICK_PARITY 0x20 #define LCR_SET_BREAK 0x40 #define LCR_DLAB 0x80 #define MODEM_CONTROL_REGISTER 0x04 #define MCR_DTR 0x01 #define MCR_RTS 0x02 #define MCR_OUT1 0x04 #define MCR_OUT2 0x08 #define MCR_LOOPBACK 0x10 #define LINE_STATUS_REGISTER 0x05 #define LSR_DATA_READY 0x01 #define LSR_OVERRUN_ERROR 0x02 #define LSR_PARITY_ERROR 0x04 #define LSR_FRAMING_ERROR 0x08 #define LSR_BREAK_DETECT 0x10 #define LSR_THRE 0x20 #define MODEM_STATUS_REGISTER 0x06 #define MSR_DELTA_CTS 0x01 #define MSR_DELTA_DSR 0x02 #define MSR_TERI 0x04 #define MSR_DELTA_CD 0x08 #define MSR_CTS 0x10 #define MSR_DSR 0x20 #define MSR_RI 0x40 #define MSR_CD 0x80 #define DIVISOR_LATCH_LOW 0x00 #define DIVISOR_LATCH_HIGH 0x01 #define QUESIZE 2048 typedef struct { long head, tail; // bytes are put on head and pulled from tail unsigned char data[QUESIZE]; } que_t; void InitPort (void); void ShutdownPort (void); int read_byte( void ); void write_byte( int c ); void Error (char *error, ...); extern int argc; extern char **argv; ================================================ FILE: sersrc/SERSTR.H ================================================ #define STR_DROPDTR "Dropping DTR" #define STR_CLEANEXIT "Clean exit from SERSETUP" #define STR_ATTEMPT "Attempting to connect across serial link, press escape to abort." #define STR_NETABORT "Network game synchronization aborted." #define STR_DUPLICATE "Duplicate id string, try again or check modem init string." #define STR_MODEMCMD "Modem command : " #define STR_MODEMRESP "Modem response: " #define STR_RESPABORT "Modem response aborted." #define STR_CANTREAD "Couldn't read MODEM.CFG" #define STR_DIALING "Dialing..." #define STR_CONNECT "CONNECT" #define STR_WAITRING "Waiting for ring..." #define STR_RING "RING" #define STR_NORESP "No such response file!" #define STR_DOOMSERIAL "DOOM II SERIAL DEVICE DRIVER v1.4" #define STR_WARNING \ "Warning: no NULL or iret interrupt vectors were found in the 0x60 to 0x66\n"\ "range. You can specify a vector with the -vector 0x parameter.\n" #define STR_COMM "Communicating with interrupt vector 0x%x" #define STR_RETURNED "Returned from DOOM II" #define STR_PORTSET "Setting port to %lu baud" ================================================ FILE: sersrc/SER_FRCH.H ================================================ #define STR_DROPDTR "Abandon de DTR" #define STR_CLEANEXIT "Sortie normale de SERSETUP" #define STR_ATTEMPT "Tentative de connexion en srie, appuyez sur ESC pour annuler." #define STR_NETABORT "Synchronisation de jeu sur rseau annule." #define STR_DUPLICATE "Chane id en double. Ressayez ou vrifiez la chane d'initialistion du modem." #define STR_MODEMCMD "Commande du modem: " #define STR_MODEMRESP "Rponse du modem: " #define STR_RESPABORT "Rponse du modem annule." #define STR_CANTREAD "Lecture de MODEM.CFG impossible" #define STR_DIALING "Composition du numro..." #define STR_CONNECT "CONNECTION" #define STR_WAITRING "Attente d'appel..." #define STR_RING "APPEL" #define STR_NORESP "Ce fichier de rponse n'existe pas!" #define STR_DOOMSERIAL "GESTIONNAIRE DE LIAISON SERIE DOOM II v1.4" #define STR_WARNING \ "Attention: pas de vecteurs d'interruption NULL ou iret trouvs entre 0x60 et 0x66.\n"\ "Vous pouvez spcifier un vecteur avec le paramtre -vector 0x." #define STR_COMM "Communication avec le vecteur d'interruption 0x%x" #define STR_RETURNED "Retour de DOOM II" #define STR_PORTLOOK "Recherche de l'UART sur le port" #define STR_UART8250 "UART = 8250" #define STR_UART16550 "UART = 16550" #define STR_CLEARPEND "Riinitilisation des interruptions en attente.\n" #define STR_PORTSET "Rglage du port %lu baud" ================================================ FILE: sndserv/Makefile ================================================ ########################################################## # # $Id:$ # # $Log:$ # # CC=gcc CFLAGS=-O -DNORMALUNIX -DLINUX LDFLAGS= LIBS=-lm O=linux all: $(O)/sndserver clean: rm -f *.o *~ *.flc rm -f linux/* # Target $(O)/sndserver: \ $(O)/soundsrv.o \ $(O)/sounds.o \ $(O)/wadread.o \ $(O)/linux.o $(CC) $(CFLAGS) $(LDFLAGS) \ $(O)/soundsrv.o \ $(O)/sounds.o \ $(O)/wadread.o \ $(O)/linux.o -o $(O)/sndserver $(LIBS) echo make complete. # Rule $(O)/%.o: %.c $(CC) $(CFLAGS) -c $< -o $@ ================================================ FILE: sndserv/README.sndserv ================================================ This is the soundserver as used by the original Linuxdoom release. I separated the source from the actual Linuxduum source. For various reasons the separate sound process seems to give the best results - both synchronous and timer driven output demonstrate glitches. These might either be timing issues, or introduced by the changes I made to the linux sound code while merging it back into the main tree. Note that neither John Carmack nor Dave Taylor are responsible for the current sound handling. ================================================ FILE: sndserv/linux.c ================================================ // Emacs style mode select -*- C++ -*- //----------------------------------------------------------------------------- // // $Id: linux.c,v 1.3 1997/01/26 07:45:01 b1 Exp $ // // Copyright (C) 1993-1996 by id Software, Inc. // // This source is available for distribution and/or modification // only under the terms of the DOOM Source Code License as // published by id Software. All rights reserved. // // The source is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License // for more details. // // // $Log: linux.c,v $ // Revision 1.3 1997/01/26 07:45:01 b1 // 2nd formatting run, fixed a few warnings as well. // // Revision 1.2 1997/01/21 19:00:01 b1 // First formatting run: // using Emacs cc-mode.el indentation for C++ now. // // Revision 1.1 1997/01/19 17:22:45 b1 // Initial check in DOOM sources as of Jan. 10th, 1997 // // // DESCRIPTION: // UNIX, soundserver for Linux i386. // //----------------------------------------------------------------------------- static const char rcsid[] = "$Id: linux.c,v 1.3 1997/01/26 07:45:01 b1 Exp $"; #include #include #include #include #include #include "soundsrv.h" int audio_fd; void myioctl ( int fd, int command, int* arg ) { int rc; extern int errno; rc = ioctl(fd, command, arg); if (rc < 0) { fprintf(stderr, "ioctl(dsp,%d,arg) failed\n", command); fprintf(stderr, "errno=%d\n", errno); exit(-1); } } void I_InitMusic(void) { } void I_InitSound ( int samplerate, int samplesize ) { int i; audio_fd = open("/dev/dsp", O_WRONLY); if (audio_fd<0) fprintf(stderr, "Could not open /dev/dsp\n"); i = 11 | (2<<16); myioctl(audio_fd, SNDCTL_DSP_SETFRAGMENT, &i); myioctl(audio_fd, SNDCTL_DSP_RESET, 0); i=11025; myioctl(audio_fd, SNDCTL_DSP_SPEED, &i); i=1; myioctl(audio_fd, SNDCTL_DSP_STEREO, &i); myioctl(audio_fd, SNDCTL_DSP_GETFMTS, &i); if (i&=AFMT_S16_LE) myioctl(audio_fd, SNDCTL_DSP_SETFMT, &i); else fprintf(stderr, "Could not play signed 16 data\n"); } void I_SubmitOutputBuffer ( void* samples, int samplecount ) { write(audio_fd, samples, samplecount*4); } void I_ShutdownSound(void) { close(audio_fd); } void I_ShutdownMusic(void) { } ================================================ FILE: sndserv/sounds.c ================================================ // Emacs style mode select -*- C++ -*- //----------------------------------------------------------------------------- // // $Id: sounds.c,v 1.3 1997/01/29 22:40:44 b1 Exp $ // // Copyright (C) 1993-1996 by id Software, Inc. // // This source is available for distribution and/or modification // only under the terms of the DOOM Source Code License as // published by id Software. All rights reserved. // // The source is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License // for more details. // // // $Log: sounds.c,v $ // Revision 1.3 1997/01/29 22:40:44 b1 // Reformatting, S (sound) module files. // // Revision 1.2 1997/01/21 19:00:07 b1 // First formatting run: // using Emacs cc-mode.el indentation for C++ now. // // Revision 1.1 1997/01/19 17:22:50 b1 // Initial check in DOOM sources as of Jan. 10th, 1997 // // // DESCRIPTION: // Created by Dave Taylor's sound utility. // Kept as a sample, DOOM sounds. // //----------------------------------------------------------------------------- static const char rcsid[] = "$Id: sounds.c,v 1.3 1997/01/29 22:40:44 b1 Exp $"; // Not exactly a good idea. enum { false, true }; #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[] = { { 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 } }; ================================================ FILE: sndserv/sounds.h ================================================ // Emacs style mode select -*- C++ -*- //----------------------------------------------------------------------------- // // $Id: sounds.h,v 1.3 1997/01/29 22:40:44 b1 Exp $ // // Copyright (C) 1993-1996 by id Software, Inc. // // This source is available for distribution and/or modification // only under the terms of the DOOM Source Code License as // published by id Software. All rights reserved. // // The source is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License // for more details. // // // $Log: sounds.h,v $ // Revision 1.3 1997/01/29 22:40:44 b1 // Reformatting, S (sound) module files. // // Revision 1.2 1997/01/21 19:00:07 b1 // First formatting run: // using Emacs cc-mode.el indentation for C++ now. // // Revision 1.1 1997/01/19 17:22:50 b1 // Initial check in DOOM sources as of Jan. 10th, 1997 // // // DESCRIPTION: // Created by Dave Taylor's sound utility. // Kept as a sample, DOOM sounds. // //----------------------------------------------------------------------------- #ifndef __SOUNDSH__ #define __SOUNDSH__ #include "soundst.h" // // 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, NUMSFX } sfxenum_t; extern musicinfo_t S_music[]; extern sfxinfo_t S_sfx[]; #endif ================================================ FILE: sndserv/soundsrv.c ================================================ // Emacs style mode select -*- C++ -*- //----------------------------------------------------------------------------- // // $Id: soundsrv.c,v 1.3 1997/01/29 22:40:44 b1 Exp $ // // Copyright (C) 1993-1996 by id Software, Inc. // // This source is available for distribution and/or modification // only under the terms of the DOOM Source Code License as // published by id Software. All rights reserved. // // The source is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License // for more details. // // // $Log: soundsrv.c,v $ // Revision 1.3 1997/01/29 22:40:44 b1 // Reformatting, S (sound) module files. // // Revision 1.2 1997/01/21 19:00:07 b1 // First formatting run: // using Emacs cc-mode.el indentation for C++ now. // // Revision 1.1 1997/01/19 17:22:50 b1 // Initial check in DOOM sources as of Jan. 10th, 1997 // // // DESCRIPTION: // UNIX soundserver, run as a separate process, // started by DOOM program. // Originally conceived fopr SGI Irix, // mostly used with Linux voxware. // //----------------------------------------------------------------------------- static const char rcsid[] = "$Id: soundsrv.c,v 1.3 1997/01/29 22:40:44 b1 Exp $"; #include #include #include #include #include #include #include #include #include #include #include "sounds.h" #include "soundsrv.h" #include "wadread.h" // // Department of Redundancy Department. // typedef struct wadinfo_struct { // should be IWAD char identification[4]; int numlumps; int infotableofs; } wadinfo_t; typedef struct filelump_struct { int filepos; int size; char name[8]; } filelump_t; // an internal time keeper static int mytime = 0; // number of sound effects int numsounds; // longest sound effect int longsound; // lengths of all sound effects int lengths[NUMSFX]; // mixing buffer signed short mixbuffer[MIXBUFFERSIZE]; // file descriptor of sfx device int sfxdevice; // file descriptor of music device int musdevice; // the channel data pointers unsigned char* channels[8]; // the channel step amount unsigned int channelstep[8]; // 0.16 bit remainder of last step unsigned int channelstepremainder[8]; // the channel data end pointers unsigned char* channelsend[8]; // time that the channel started playing int channelstart[8]; // the channel handles int channelhandles[8]; // the channel left volume lookup int* channelleftvol_lookup[8]; // the channel right volume lookup int* channelrightvol_lookup[8]; // sfx id of the playing sound effect int channelids[8]; int snd_verbose=1; int steptable[256]; int vol_lookup[128*256]; static void derror(char* msg) { fprintf(stderr, "error: %s\n", msg); exit(-1); } int mix(void) { register int dl; register int dr; register unsigned int sample; signed short* leftout; signed short* rightout; signed short* leftend; int step; leftout = mixbuffer; rightout = mixbuffer+1; step = 2; leftend = mixbuffer + SAMPLECOUNT*step; // mix into the mixing buffer while (leftout != leftend) { dl = 0; dr = 0; if (channels[0]) { sample = *channels[0]; dl += channelleftvol_lookup[0][sample]; dr += channelrightvol_lookup[0][sample]; channelstepremainder[0] += channelstep[0]; channels[0] += channelstepremainder[0] >> 16; channelstepremainder[0] &= 65536-1; if (channels[0] >= channelsend[0]) channels[0] = 0; } if (channels[1]) { sample = *channels[1]; dl += channelleftvol_lookup[1][sample]; dr += channelrightvol_lookup[1][sample]; channelstepremainder[1] += channelstep[1]; channels[1] += channelstepremainder[1] >> 16; channelstepremainder[1] &= 65536-1; if (channels[1] >= channelsend[1]) channels[1] = 0; } if (channels[2]) { sample = *channels[2]; dl += channelleftvol_lookup[2][sample]; dr += channelrightvol_lookup[2][sample]; channelstepremainder[2] += channelstep[2]; channels[2] += channelstepremainder[2] >> 16; channelstepremainder[2] &= 65536-1; if (channels[2] >= channelsend[2]) channels[2] = 0; } if (channels[3]) { sample = *channels[3]; dl += channelleftvol_lookup[3][sample]; dr += channelrightvol_lookup[3][sample]; channelstepremainder[3] += channelstep[3]; channels[3] += channelstepremainder[3] >> 16; channelstepremainder[3] &= 65536-1; if (channels[3] >= channelsend[3]) channels[3] = 0; } if (channels[4]) { sample = *channels[4]; dl += channelleftvol_lookup[4][sample]; dr += channelrightvol_lookup[4][sample]; channelstepremainder[4] += channelstep[4]; channels[4] += channelstepremainder[4] >> 16; channelstepremainder[4] &= 65536-1; if (channels[4] >= channelsend[4]) channels[4] = 0; } if (channels[5]) { sample = *channels[5]; dl += channelleftvol_lookup[5][sample]; dr += channelrightvol_lookup[5][sample]; channelstepremainder[5] += channelstep[5]; channels[5] += channelstepremainder[5] >> 16; channelstepremainder[5] &= 65536-1; if (channels[5] >= channelsend[5]) channels[5] = 0; } if (channels[6]) { sample = *channels[6]; dl += channelleftvol_lookup[6][sample]; dr += channelrightvol_lookup[6][sample]; channelstepremainder[6] += channelstep[6]; channels[6] += channelstepremainder[6] >> 16; channelstepremainder[6] &= 65536-1; if (channels[6] >= channelsend[6]) channels[6] = 0; } if (channels[7]) { sample = *channels[7]; dl += channelleftvol_lookup[7][sample]; dr += channelrightvol_lookup[7][sample]; channelstepremainder[7] += channelstep[7]; channels[7] += channelstepremainder[7] >> 16; channelstepremainder[7] &= 65536-1; if (channels[7] >= channelsend[7]) channels[7] = 0; } // Has been char instead of short. // if (dl > 127) *leftout = 127; // else if (dl < -128) *leftout = -128; // else *leftout = dl; // if (dr > 127) *rightout = 127; // else if (dr < -128) *rightout = -128; // else *rightout = dr; if (dl > 0x7fff) *leftout = 0x7fff; else if (dl < -0x8000) *leftout = -0x8000; else *leftout = dl; if (dr > 0x7fff) *rightout = 0x7fff; else if (dr < -0x8000) *rightout = -0x8000; else *rightout = dr; leftout += step; rightout += step; } return 1; } void grabdata ( int c, char** v ) { int i; char* name; char* doom1wad; char* doomwad; char* doomuwad; char* doom2wad; char* doom2fwad; // Now where are TNT and Plutonia. Yuck. // char *home; char* doomwaddir; doomwaddir = getenv("DOOMWADDIR"); if (!doomwaddir) doomwaddir = "."; doom1wad = malloc(strlen(doomwaddir)+1+9+1); sprintf(doom1wad, "%s/doom1.wad", doomwaddir); doom2wad = malloc(strlen(doomwaddir)+1+9+1); sprintf(doom2wad, "%s/doom2.wad", doomwaddir); doom2fwad = malloc(strlen(doomwaddir)+1+10+1); sprintf(doom2fwad, "%s/doom2f.wad", doomwaddir); doomuwad = malloc(strlen(doomwaddir)+1+8+1); sprintf(doomuwad, "%s/doomu.wad", doomwaddir); doomwad = malloc(strlen(doomwaddir)+1+8+1); sprintf(doomwad, "%s/doom.wad", doomwaddir); // home = getenv("HOME"); // if (!home) // derror("Please set $HOME to your home directory"); // sprintf(basedefault, "%s/.doomrc", home); for (i=1 ; idata; lengths[i] = lengths[(S_sfx[i].link - S_sfx)/sizeof(sfxinfo_t)]; } // test only // { // int fd; // char name[10]; // sprintf(name, "sfx%d", i); // fd = open(name, O_WRONLY|O_CREAT, 0644); // write(fd, S_sfx[i].data, lengths[i]); // close(fd); // } } } static struct timeval last={0,0}; //static struct timeval now; static struct timezone whocares; void updatesounds(void) { mix(); I_SubmitOutputBuffer(mixbuffer, SAMPLECOUNT); } int addsfx ( int sfxid, int volume, int step, int seperation ) { static unsigned short handlenums = 0; int i; int rc = -1; int oldest = mytime; int oldestnum = 0; int slot; int rightvol; int leftvol; // play these sound effects // only one at a time if ( sfxid == sfx_sawup || sfxid == sfx_sawidl || sfxid == sfx_sawful || sfxid == sfx_sawhit || sfxid == sfx_stnmov || sfxid == sfx_pistol ) { for (i=0 ; i<8 ; i++) { if (channels[i] && channelids[i] == sfxid) { channels[i] = 0; break; } } } for (i=0 ; i<8 && channels[i] ; i++) { if (channelstart[i] < oldest) { oldestnum = i; oldest = channelstart[i]; } } if (i == 8) slot = oldestnum; else slot = i; channels[slot] = (unsigned char *) S_sfx[sfxid].data; channelsend[slot] = channels[slot] + lengths[sfxid]; if (!handlenums) handlenums = 100; channelhandles[slot] = rc = handlenums++; channelstep[slot] = step; channelstepremainder[slot] = 0; channelstart[slot] = mytime; // (range: 1 - 256) seperation += 1; // (x^2 seperation) leftvol = volume - (volume*seperation*seperation)/(256*256); seperation = seperation - 257; // (x^2 seperation) rightvol = volume - (volume*seperation*seperation)/(256*256); // sanity check if (rightvol < 0 || rightvol > 127) derror("rightvol out of bounds"); if (leftvol < 0 || leftvol > 127) derror("leftvol out of bounds"); // get the proper lookup table piece // for this volume level channelleftvol_lookup[slot] = &vol_lookup[leftvol*256]; channelrightvol_lookup[slot] = &vol_lookup[rightvol*256]; channelids[slot] = sfxid; return rc; } void outputushort(int num) { static unsigned char buff[5] = { 0, 0, 0, 0, '\n' }; static char* badbuff = "xxxx\n"; // outputs a 16-bit # in hex or "xxxx" if -1. if (num < 0) { write(1, badbuff, 5); } else { buff[0] = num>>12; buff[0] += buff[0] > 9 ? 'a'-10 : '0'; buff[1] = (num>>8) & 0xf; buff[1] += buff[1] > 9 ? 'a'-10 : '0'; buff[2] = (num>>4) & 0xf; buff[2] += buff[2] > 9 ? 'a'-10 : '0'; buff[3] = num & 0xf; buff[3] += buff[3] > 9 ? 'a'-10 : '0'; write(1, buff, 5); } } void initdata(void) { int i; int j; int* steptablemid = steptable + 128; for (i=0 ; i 0) { // fprintf(stderr, "select is true\n"); // got a command nrc = read(0, commandbuf, 1); if (!nrc) { done = 1; rc = 0; } else { if (snd_verbose) fprintf(stderr, "cmd: %c", commandbuf[0]); switch (commandbuf[0]) { case 'p': // play a new sound effect read(0, commandbuf, 9); if (snd_verbose) { commandbuf[9]=0; fprintf(stderr, "%s\n", commandbuf); } commandbuf[0] -= commandbuf[0]>='a' ? 'a'-10 : '0'; commandbuf[1] -= commandbuf[1]>='a' ? 'a'-10 : '0'; commandbuf[2] -= commandbuf[2]>='a' ? 'a'-10 : '0'; commandbuf[3] -= commandbuf[3]>='a' ? 'a'-10 : '0'; commandbuf[4] -= commandbuf[4]>='a' ? 'a'-10 : '0'; commandbuf[5] -= commandbuf[5]>='a' ? 'a'-10 : '0'; commandbuf[6] -= commandbuf[6]>='a' ? 'a'-10 : '0'; commandbuf[7] -= commandbuf[7]>='a' ? 'a'-10 : '0'; // p sndnum = (commandbuf[0]<<4) + commandbuf[1]; step = (commandbuf[2]<<4) + commandbuf[3]; step = steptable[step]; vol = (commandbuf[4]<<4) + commandbuf[5]; sep = (commandbuf[6]<<4) + commandbuf[7]; handle = addsfx(sndnum, vol, step, sep); // returns the handle // outputushort(handle); break; case 'q': read(0, commandbuf, 1); waitingtofinish = 1; rc = 0; break; case 's': { int fd; read(0, commandbuf, 3); commandbuf[2] = 0; fd = open((char*)commandbuf, O_CREAT|O_WRONLY, 0644); commandbuf[0] -= commandbuf[0]>='a' ? 'a'-10 : '0'; commandbuf[1] -= commandbuf[1]>='a' ? 'a'-10 : '0'; sndnum = (commandbuf[0]<<4) + commandbuf[1]; write(fd, S_sfx[sndnum].data, lengths[sndnum]); close(fd); } break; default: fprintf(stderr, "Did not recognize command\n"); break; } } } else if (rc < 0) { quit(); } } while (rc > 0); } updatesounds(); if (waitingtofinish) { for(i=0 ; i<8 && !channels[i] ; i++); if (i==8) done=1; } } quit(); return 0; } ================================================ FILE: sndserv/soundsrv.h ================================================ // Emacs style mode select -*- C++ -*- //----------------------------------------------------------------------------- // // $Id: soundsrv.h,v 1.3 1997/01/29 22:40:44 b1 Exp $ // // Copyright (C) 1993-1996 by id Software, Inc. // // This source is available for distribution and/or modification // only under the terms of the DOOM Source Code License as // published by id Software. All rights reserved. // // The source is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License // for more details. // // // $Log: soundsrv.h,v $ // Revision 1.3 1997/01/29 22:40:44 b1 // Reformatting, S (sound) module files. // // Revision 1.2 1997/01/21 19:00:07 b1 // First formatting run: // using Emacs cc-mode.el indentation for C++ now. // // Revision 1.1 1997/01/19 17:22:50 b1 // Initial check in DOOM sources as of Jan. 10th, 1997 // // // DESCRIPTION: // UNIX soundserver, separate process. // //----------------------------------------------------------------------------- #ifndef __SNDSERVER_H__ #define __SNDSERVER_H__ #define SAMPLECOUNT 512 #define MIXBUFFERSIZE (SAMPLECOUNT*2*2) #define SPEED 11025 void I_InitMusic(void); void I_InitSound ( int samplerate, int samplesound ); void I_SubmitOutputBuffer ( void* samples, int samplecount ); void I_ShutdownSound(void); void I_ShutdownMusic(void); #endif ================================================ FILE: sndserv/soundst.h ================================================ // Emacs style mode select -*- C++ -*- //----------------------------------------------------------------------------- // // $Id: soundst.h,v 1.3 1997/01/29 22:40:45 b1 Exp $ // // Copyright (C) 1993-1996 by id Software, Inc. // // This source is available for distribution and/or modification // only under the terms of the DOOM Source Code License as // published by id Software. All rights reserved. // // The source is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License // for more details. // // // $Log: soundst.h,v $ // Revision 1.3 1997/01/29 22:40:45 b1 // Reformatting, S (sound) module files. // // Revision 1.2 1997/01/21 19:00:07 b1 // First formatting run: // using Emacs cc-mode.el indentation for C++ now. // // Revision 1.1 1997/01/19 17:22:50 b1 // Initial check in DOOM sources as of Jan. 10th, 1997 // // // DESCRIPTION: // Sound (utility) related. Hang on. // See gensounds.h and gensounds.c for what soundst.h is made of. // //----------------------------------------------------------------------------- #ifndef __SOUNDSTH__ #define __SOUNDSTH__ #define S_MAX_VOLUME 127 // when to clip out sounds // Doesn't fit the large outdoor areas. #define S_CLIPPING_DIST (1200*0x10000) // when sounds should be max'd out #define S_CLOSE_DIST (200*0x10000) #define S_ATTENUATOR ((S_CLIPPING_DIST-S_CLOSE_DIST)>>FRACBITS) #define NORM_PITCH 128 #define NORM_PRIORITY 64 #define NORM_VOLUME snd_MaxVolume #define S_PITCH_PERTURB 1 #define NORM_SEP 128 #define S_STEREO_SWING (96*0x10000) // % attenuation from front to back #define S_IFRACVOL 30 #define NA 0 #define S_NUMCHANNELS 2 // // MusicInfo struct. // typedef struct { // up to 6-character name char* name; // lump number of music int lumpnum; // music data void* data; // music handle once registered int handle; } musicinfo_t; // // SoundFX struct. // typedef struct sfxinfo_struct sfxinfo_t; struct sfxinfo_struct { // up to 6-character name char* name; // 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's in use) int usefulness; // lump number of sfx int lumpnum; }; typedef struct { // sound information (if null, channel avail.) sfxinfo_t* sfxinfo; // origin of sound void* origin; // handle of the sound being played int handle; } channel_t; enum { Music, Sfx, SfxLink }; enum { PC=1, Adlib=2, SB=4, Midi=8 }; // cards available enum { sfxThrowOut=-1, sfxNotUsed=0 }; // // Initialize the sound code at start of level // void S_Start(void); // // Start sound for thing at // using from sounds.h // extern void S_StartSound ( void* origin, int sound_id ); // Will start a sound at a given volume. extern void S_StartSoundAtVolume ( void* origin, int sound_id, int volume ); // Stop sound for thing at extern void S_StopSound(void* origin); // Start music using from sounds.h extern void S_StartMusic(int music_id); // Start music using from sounds.h, // and set whether looping extern void S_ChangeMusic ( int music_id, int looping ); // Stops the music extern void S_StopMusic(void); void S_PauseSound(void); void S_ResumeSound(void); // // Updates music & sounds // extern void S_UpdateSounds(void* listener); void S_SetMusicVolume(int volume); void S_SetSfxVolume(int volume); // // Initializes sound stuff, including volume // void S_Init ( int , int ); // // SOUND IO // #define FREQ_LOW 0x40 #define FREQ_NORM 0x80 #define FREQ_HIGH 0xff void I_SetMusicVolume(int volume); void I_SetSfxVolume(int volume); // // MUSIC I/O // void I_PauseSong(int handle); void I_ResumeSong(int handle); // // 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); // registers a song handle to song data int I_RegisterSong(void *data); // see above then think backwards void I_UnRegisterSong(int handle); // is the song playing? int I_QrySongPlaying(int handle); // // SFX I/O // void I_SetChannels(int channels); int I_GetSfxLumpNum (sfxinfo_t*); // Starts a sound in a particular sound channel. int I_StartSound ( int id, void* data, int vol, int sep, int pitch, int priority ); // Updates the volume, separation, // and pitch of a sound channel. void I_UpdateSoundParams ( int handle, int vol, int sep, int pitch ); // Stops a sound channel. void I_StopSound(int handle); // Called by S_*()'s to see if a channel is still playing. // Returns 0 if no longer playing, 1 if playing. int I_SoundIsPlaying(int handle); // the complete set of sound effects extern sfxinfo_t S_sfx[]; // the complete set of music extern musicinfo_t S_music[]; #endif ================================================ FILE: sndserv/wadread.c ================================================ // Emacs style mode select -*- C++ -*- //----------------------------------------------------------------------------- // // $Id: wadread.c,v 1.3 1997/01/30 19:54:23 b1 Exp $ // // Copyright (C) 1993-1996 by id Software, Inc. // // This source is available for distribution and/or modification // only under the terms of the DOOM Source Code License as // published by id Software. All rights reserved. // // The source is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License // for more details. // // // $Log: wadread.c,v $ // Revision 1.3 1997/01/30 19:54:23 b1 // Final reformatting run. All the remains (ST, W, WI, Z). // // Revision 1.2 1997/01/21 19:00:10 b1 // First formatting run: // using Emacs cc-mode.el indentation for C++ now. // // Revision 1.1 1997/01/19 17:22:51 b1 // Initial check in DOOM sources as of Jan. 10th, 1997 // // // DESCRIPTION: // WAD and Lump I/O, the second. // This time for soundserver only. // Welcome to Department of Redundancy Department. Again :-). // //----------------------------------------------------------------------------- static const char rcsid[] = "$Id: wadread.c,v 1.3 1997/01/30 19:54:23 b1 Exp $"; #include #include #include #include #include #include #include #include #include "soundsrv.h" #include "wadread.h" int* sfxlengths; typedef struct wadinfo_struct { char identification[4]; int numlumps; int infotableofs; } wadinfo_t; typedef struct filelump_struct { int filepos; int size; char name[8]; } filelump_t; typedef struct lumpinfo_struct { int handle; int filepos; int size; char name[8]; } lumpinfo_t; lumpinfo_t* lumpinfo; int numlumps; void** lumpcache; #define strcmpi strcasecmp // // Something new. // This version of w_wad.c does handle endianess. // #ifndef __BIG_ENDIAN__ #define LONG(x) (x) #define SHORT(x) (x) #else #define LONG(x) ((long)SwapLONG((unsigned long) (x))) #define SHORT(x) ((short)SwapSHORT((unsigned short) (x))) unsigned long SwapLONG(unsigned long x) { return (x>>24) | ((x>>8) & 0xff00) | ((x<<8) & 0xff0000) | (x<<24); } unsigned short SwapSHORT(unsigned short x) { return (x>>8) | (x<<8); } #endif // Way too many of those... static void derror(char* msg) { fprintf(stderr, "\nwadread error: %s\n", msg); exit(-1); } void strupr (char *s) { while (*s) *s++ = toupper(*s); } int filelength (int handle) { struct stat fileinfo; if (fstat (handle,&fileinfo) == -1) fprintf (stderr, "Error fstating\n"); return fileinfo.st_size; } void openwad(char* wadname) { int wadfile; int tableoffset; int tablelength; int tablefilelength; int i; wadinfo_t header; filelump_t* filetable; // open and read the wadfile header wadfile = open(wadname, O_RDONLY); if (wadfile < 0) derror("Could not open wadfile"); read(wadfile, &header, sizeof header); if (strncmp(header.identification, "IWAD", 4)) derror("wadfile has weirdo header"); numlumps = LONG(header.numlumps); tableoffset = LONG(header.infotableofs); tablelength = numlumps * sizeof(lumpinfo_t); tablefilelength = numlumps * sizeof(filelump_t); lumpinfo = (lumpinfo_t *) malloc(tablelength); filetable = (filelump_t *) ((char*)lumpinfo + tablelength - tablefilelength); // get the lumpinfo table lseek(wadfile, tableoffset, SEEK_SET); read(wadfile, filetable, tablefilelength); // process the table to make the endianness right and shift it down for (i=0 ; i