Merge branch 'next' of https://git.magicalgirl.moe/STJr/SRB2 into pictureformats

This commit is contained in:
Jaime Passos 2020-07-06 19:29:14 -03:00
commit 7911deebf8
140 changed files with 10016 additions and 6975 deletions

View file

@ -750,12 +750,6 @@ linedeftypes
prefix = "(20)";
}
21
{
title = "Explicitly Include Line <disabled>";
prefix = "(21)";
}
22
{
title = "Parameters";
@ -766,6 +760,7 @@ linedeftypes
flags128text = "[7] Intangible";
flags256text = "[8] Stopped by pushables";
flags512text = "[9] Render flats";
flags8192text = "[13] Cut cyan flat pixels";
}
30
@ -920,6 +915,7 @@ linedeftypes
flags32text = "[5] Only block player";
flags64text = "[6] Render insides";
flags128text = "[7] Only block non-players";
flags8192text = "[13] Cut cyan flat pixels";
3dfloor = true;
3dfloorflags = "195F";
flags643dfloorflagsadd = "7C80";
@ -979,6 +975,7 @@ linedeftypes
flags32text = "[5] Only block player";
flags64text = "[6] Don't cast shadow";
flags128text = "[7] Render insides/block non-plr";
flags8192text = "[13] Cut cyan flat pixels";
3dfloor = true;
3dfloorflags = "200191F";
flags1283dfloorflagsadd = "7C80";
@ -992,6 +989,7 @@ linedeftypes
flags32text = "[5] Only block player";
flags64text = "[6] Don't cast shadow";
flags128text = "[7] Render insides/block non-plr";
flags8192text = "[13] Cut cyan flat pixels";
3dfloor = true;
3dfloorflags = "2001917";
flags1283dfloorflagsadd = "7C80";
@ -1019,6 +1017,7 @@ linedeftypes
flags32text = "[5] Only block player";
flags64text = "[6] Don't cast shadow";
flags128text = "[7] Render insides/block non-plr";
flags8192text = "[13] Cut cyan flat pixels";
3dfloor = true;
3dfloorflags = "400191F";
flags1283dfloorflagsadd = "7C80";
@ -1032,6 +1031,7 @@ linedeftypes
flags32text = "[5] Only block player";
flags64text = "[6] Don't cast shadow";
flags128text = "[7] Render insides/block non-plr";
flags8192text = "[13] Cut cyan flat pixels";
3dfloor = true;
3dfloorflags = "4001917";
flags1283dfloorflagsadd = "7C80";
@ -1077,6 +1077,7 @@ linedeftypes
flags64text = "[6] Use two light levels";
flags512text = "[9] Use target light level";
flags1024text = "[10] Ripple effect";
flags8192text = "[13] Cut cyan flat pixels";
3dfloor = true;
3dfloorflags = "9F39";
flags643dfloorflagsadd = "20000";
@ -1105,6 +1106,7 @@ linedeftypes
flags64text = "[6] Use two light levels";
flags512text = "[9] Use target light level";
flags1024text = "[10] Ripple effect";
flags8192text = "[13] Cut cyan flat pixels";
3dfloor = true;
3dfloorflags = "1F31";
flags643dfloorflagsadd = "20000";
@ -1120,6 +1122,7 @@ linedeftypes
flags64text = "[6] Use two light levels";
flags512text = "[9] Use target light level";
flags1024text = "[10] Ripple effect";
flags8192text = "[13] Cut cyan flat pixels";
3dfloor = true;
3dfloorflags = "209F39";
flags643dfloorflagsadd = "20000";
@ -1134,6 +1137,7 @@ linedeftypes
flags64text = "[6] Use two light levels";
flags512text = "[9] Use target light level";
flags1024text = "[10] Ripple effect";
flags8192text = "[13] Cut cyan flat pixels";
3dfloor = true;
3dfloorflags = "201F31";
flags643dfloorflagsadd = "20000";
@ -1156,6 +1160,7 @@ linedeftypes
prefix = "(221)";
flags8text = "[3] Slope skew sides";
flags64text = "[6] Cast shadow";
flags8192text = "[13] Cut cyan flat pixels";
3dfloor = true;
3dfloorflags = "1B59";
flags643dfloorflagsremove = "40";
@ -1279,6 +1284,7 @@ linedeftypes
flags32text = "[5] Only block player";
flags64text = "[6] Spindash to move";
flags128text = "[7] Only block non-players";
flags8192text = "[13] Cut cyan flat pixels";
3dfloor = true;
3dfloorflags = "195F";
}
@ -1318,6 +1324,7 @@ linedeftypes
flags32text = "[5] Only block player";
flags64text = "[6] Spindash, no shadow";
flags128text = "[7] Only block non-players";
flags8192text = "[13] Cut cyan flat pixels";
3dfloor = true;
3dfloorflags = "2009D1F";
flags643dfloorflagsadd = "40";
@ -1384,6 +1391,7 @@ linedeftypes
flags32text = "[5] Only block player";
flags64text = "[6] Don't cast shadow";
flags128text = "[7] Only block non-players";
flags8192text = "[13] Cut cyan flat pixels";
3dfloor = true;
3dfloorflags = "210959F";
flags643dfloorflagsadd = "40";
@ -1397,6 +1405,7 @@ linedeftypes
flags32text = "[5] Only block player";
flags64text = "[6] Don't cast shadow";
flags128text = "[7] Only block non-players";
flags8192text = "[13] Cut cyan flat pixels";
3dfloor = true;
3dfloorflags = "218959F";
flags643dfloorflagsadd = "40";
@ -1535,6 +1544,7 @@ linedeftypes
flags8text = "[3] Slope skew sides";
flags512text = "[9] Shattered by pushables";
flags1024text = "[10] Trigger linedef executor";
flags8192text = "[13] Cut cyan flat pixels";
3dfloor = true;
3dfloorflags = "880101D";
}
@ -1576,6 +1586,7 @@ linedeftypes
flags128text = "[7] Only block non-players";
flags512text = "[9] Shattered by pushables";
flags1024text = "[10] Trigger linedef executor";
flags8192text = "[13] Cut cyan flat pixels";
3dfloor = true;
3dfloorflags = "1080101F";
}
@ -1597,6 +1608,7 @@ linedeftypes
prefix = "(258)";
flags8text = "[3] Slope skew sides";
flags32text = "[5] Don't damage bosses";
flags8192text = "[13] Cut cyan flat pixels";
3dfloor = true;
3dfloorflags = "959";
}
@ -1610,6 +1622,7 @@ linedeftypes
flags128text = "[7] Only block non-players";
flags512text = "[9] Shattered by pushables";
flags1024text = "[10] Trigger linedef executor";
flags8192text = "[13] Cut cyan flat pixels";
3dfloor = true;
3dfloorcustom = true;
}
@ -2603,45 +2616,63 @@ linedeftypes
500
{
title = "Scroll Wall Front Side Left";
title = "Scroll Front Wall Left";
prefix = "(500)";
}
501
{
title = "Scroll Wall Front Side Right";
title = "Scroll Front Wall Right";
prefix = "(501)";
}
502
{
title = "Scroll Wall According to Linedef";
title = "Scroll Tagged Wall";
prefix = "(502)";
flags128text = "[7] Use texture offsets";
flags256text = "[8] Scroll back side";
}
503
{
title = "Scroll Wall According to Linedef (Accelerative)";
title = "Scroll Tagged Wall (Accelerative)";
prefix = "(503)";
flags128text = "[7] Use texture offsets";
flags256text = "[8] Scroll back side";
}
504
{
title = "Scroll Wall According to Linedef (Displacement)";
title = "Scroll Tagged Wall (Displacement)";
prefix = "(504)";
flags128text = "[7] Use texture offsets";
flags256text = "[8] Scroll back side";
}
505
{
title = "Scroll Texture by Front Side Offsets";
title = "Scroll Front Wall by Front Side Offsets";
prefix = "(505)";
}
506
{
title = "Scroll Texture by Back Side Offsets";
title = "Scroll Front Wall by Back Side Offsets";
prefix = "(506)";
}
507
{
title = "Scroll Back Wall by Front Side Offsets";
prefix = "(507)";
}
508
{
title = "Scroll Back Wall by Back Side Offsets";
prefix = "(508)";
}
}
planescroll

View file

@ -420,6 +420,7 @@ endif()
if(${SRB2_CONFIG_HWRENDER})
add_definitions(-DHWRENDER)
set(SRB2_HWRENDER_SOURCES
${CMAKE_CURRENT_SOURCE_DIR}/hardware/hw_batching.c
${CMAKE_CURRENT_SOURCE_DIR}/hardware/hw_bsp.c
${CMAKE_CURRENT_SOURCE_DIR}/hardware/hw_cache.c
${CMAKE_CURRENT_SOURCE_DIR}/hardware/hw_clip.c
@ -435,6 +436,7 @@ if(${SRB2_CONFIG_HWRENDER})
)
set (SRB2_HWRENDER_HEADERS
${CMAKE_CURRENT_SOURCE_DIR}/hardware/hw_batching.h
${CMAKE_CURRENT_SOURCE_DIR}/hardware/hw_clip.h
${CMAKE_CURRENT_SOURCE_DIR}/hardware/hw_data.h
${CMAKE_CURRENT_SOURCE_DIR}/hardware/hw_defs.h

View file

@ -222,12 +222,10 @@ endif
ifdef NOHW
OPTS+=-DNOHW
else
#Hurdler: not really supported and not tested recently
#OPTS+=-DUSE_PALETTED_TEXTURE
OPTS+=-DHWRENDER
OBJS+=$(OBJDIR)/hw_bsp.o $(OBJDIR)/hw_draw.o $(OBJDIR)/hw_light.o \
$(OBJDIR)/hw_main.o $(OBJDIR)/hw_clip.o $(OBJDIR)/hw_md2.o $(OBJDIR)/hw_cache.o $(OBJDIR)/hw_trick.o \
$(OBJDIR)/hw_md2load.o $(OBJDIR)/hw_md3load.o $(OBJDIR)/hw_model.o $(OBJDIR)/u_list.o
$(OBJDIR)/hw_md2load.o $(OBJDIR)/hw_md3load.o $(OBJDIR)/hw_model.o $(OBJDIR)/u_list.o $(OBJDIR)/hw_batching.o
endif
ifdef NOHS

View file

@ -931,11 +931,8 @@ static inline void AM_drawWalls(void)
l.b.y = lines[i].v2->y >> FRACTOMAPBITS;
#define SLOPEPARAMS(slope, end1, end2, normalheight) \
if (slope) { \
end1 = P_GetZAt(slope, lines[i].v1->x, lines[i].v1->y); \
end2 = P_GetZAt(slope, lines[i].v2->x, lines[i].v2->y); \
} else \
end1 = end2 = normalheight;
end1 = P_GetZAt(slope, lines[i].v1->x, lines[i].v1->y, normalheight); \
end2 = P_GetZAt(slope, lines[i].v2->x, lines[i].v2->y, normalheight);
SLOPEPARAMS(lines[i].frontsector->f_slope, frontf1, frontf2, lines[i].frontsector->floorheight)
SLOPEPARAMS(lines[i].frontsector->c_slope, frontc1, frontc2, lines[i].frontsector->ceilingheight)

View file

@ -11,6 +11,10 @@
#include <stdlib.h>
#include <string.h>
#include "../doomdef.h"
#include "../lua_script.h"
#include "../w_wad.h"
#define lbaselib_c
#define LUA_LIB
@ -263,6 +267,27 @@ static int luaB_ipairs (lua_State *L) {
}
// Edited to load PK3 entries instead
static int luaB_dofile (lua_State *L) {
const char *filename = luaL_checkstring(L, 1);
char fullfilename[256];
UINT16 lumpnum;
int n = lua_gettop(L);
if (wadfiles[numwadfiles - 1]->type != RET_PK3)
luaL_error(L, "dofile() only works with PK3 files");
snprintf(fullfilename, sizeof(fullfilename), "Lua/%s", filename);
lumpnum = W_CheckNumForFullNamePK3(fullfilename, numwadfiles - 1, 0);
if (lumpnum == INT16_MAX)
luaL_error(L, "can't find script " LUA_QS, fullfilename);
LUA_LoadLump(numwadfiles - 1, lumpnum, false);
return lua_gettop(L) - n;
}
static int luaB_assert (lua_State *L) {
luaL_checkany(L, 1);
if (!lua_toboolean(L, 1))
@ -380,6 +405,7 @@ static const luaL_Reg base_funcs[] = {
{"assert", luaB_assert},
{"collectgarbage", luaB_collectgarbage},
{"error", luaB_error},
{"dofile", luaB_dofile},
{"gcinfo", luaB_gcinfo},
{"getfenv", luaB_getfenv},
{"getmetatable", luaB_getmetatable},

View file

@ -284,8 +284,16 @@ void Got_LuaFile(UINT8 **cp, INT32 playernum)
// Push the first argument (file handle or nil) on the stack
if (success)
{
char mode[4];
// Ensure we are opening in binary mode
// (if it's a text file, newlines have been converted already)
strcpy(mode, luafiletransfers->mode);
if (!strchr(mode, 'b'))
strcat(mode, "b");
pf = newfile(gL); // Create and push the file handle
*pf = fopen(luafiletransfers->realfilename, luafiletransfers->mode); // Open the file
*pf = fopen(luafiletransfers->realfilename, mode); // Open the file
if (!*pf)
I_Error("Can't open file \"%s\"\n", luafiletransfers->realfilename); // The file SHOULD exist
}
@ -313,17 +321,14 @@ void Got_LuaFile(UINT8 **cp, INT32 playernum)
RemoveLuaFileTransfer();
if (server && luafiletransfers)
if (waitingforluafilecommand)
{
if (FIL_FileOK(luafiletransfers->realfilename))
SV_PrepareSendLuaFileToNextNode();
else
{
// Send a net command with 0 as its first byte to indicate the file couldn't be opened
success = 0;
SendNetXCmd(XD_LUAFILE, &success, 1);
}
waitingforluafilecommand = false;
CL_PrepareDownloadLuaFile();
}
if (server && luafiletransfers)
SV_PrepareSendLuaFile();
}

View file

@ -770,7 +770,7 @@ boolean CON_Responder(event_t *ev)
// check for console toggle key
if (ev->type != ev_console)
{
if (modeattacking || metalrecording)
if (modeattacking || metalrecording || marathonmode)
return false;
if (key == gamecontrol[gc_console][0] || key == gamecontrol[gc_console][1])

File diff suppressed because it is too large Load diff

View file

@ -33,7 +33,8 @@ applications may follow different packet versions.
// be transmitted.
// Networking and tick handling related.
#define BACKUPTICS 96
#define BACKUPTICS 1024
#define CLIENTBACKUPTICS 32
#define MAXTEXTCMD 256
//
// Packet structure
@ -75,6 +76,8 @@ typedef enum
// In addition, this packet can't occupy all the available slots.
PT_FILEFRAGMENT = PT_CANFAIL, // A part of a file.
PT_FILEACK,
PT_FILERECEIVED,
PT_TEXTCMD, // Extra text commands from the client.
PT_TEXTCMD2, // Splitscreen text commands.
@ -128,7 +131,7 @@ typedef struct
// this packet is too large
typedef struct
{
UINT8 starttic;
tic_t starttic;
UINT8 numtics;
UINT8 numslots; // "Slots filled": Highest player number in use plus one.
ticcmd_t cmds[45]; // Normally [BACKUPTIC][MAXPLAYERS] but too large
@ -171,6 +174,9 @@ typedef struct
UINT32 pflags; // pflags_t
UINT8 panim; // panim_t
INT16 angleturn;
INT16 oldrelangleturn;
angle_t aiming;
INT32 currentweapon;
INT32 ringweapons;
@ -188,7 +194,7 @@ typedef struct
SINT8 xtralife;
SINT8 pity;
UINT8 skincolor;
UINT16 skincolor;
INT32 skin;
UINT32 availabilities;
// Just in case Lua does something like
@ -308,7 +314,7 @@ typedef struct
// 0xFF == not in game; else player skin num
UINT8 playerskins[MAXPLAYERS];
UINT8 playercolor[MAXPLAYERS];
UINT16 playercolor[MAXPLAYERS];
UINT32 playeravailabilities[MAXPLAYERS];
UINT8 gametype;
@ -320,13 +326,30 @@ typedef struct
UINT8 varlengthinputs[0]; // Playernames and netvars
} ATTRPACK serverconfig_pak;
typedef struct {
typedef struct
{
UINT8 fileid;
UINT32 filesize;
UINT8 iteration;
UINT32 position;
UINT16 size;
UINT8 data[0]; // Size is variable using hardware_MAXPACKETLENGTH
} ATTRPACK filetx_pak;
typedef struct
{
UINT32 start;
UINT32 acks;
} ATTRPACK fileacksegment_t;
typedef struct
{
UINT8 fileid;
UINT8 iteration;
UINT8 numsegments;
fileacksegment_t segments[0];
} ATTRPACK fileack_pak;
#ifdef _MSC_VER
#pragma warning(default : 4200)
#endif
@ -414,7 +437,7 @@ typedef struct
{
char name[MAXPLAYERNAME+1];
UINT8 skin;
UINT8 color;
UINT16 color;
UINT32 pflags;
UINT32 score;
UINT8 ctfteam;
@ -442,6 +465,8 @@ typedef struct
UINT8 resynchgot; //
UINT8 textcmd[MAXTEXTCMD+1]; // 66049 bytes (wut??? 64k??? More like 257 bytes...)
filetx_pak filetxpak; // 139 bytes
fileack_pak fileack;
UINT8 filereceived;
clientconfig_pak clientcfg; // 136 bytes
UINT8 md5sum[16];
serverinfo_pak serverinfo; // 1024 bytes
@ -520,7 +545,7 @@ extern consvar_t cv_resynchattempts, cv_blamecfail;
extern consvar_t cv_maxsend, cv_noticedownload, cv_downloadspeed;
// Used in d_net, the only dependence
tic_t ExpandTics(INT32 low);
tic_t ExpandTics(INT32 low, INT32 node);
void D_ClientServerInit(void);
// Initialise the other field
@ -534,7 +559,6 @@ void NetUpdate(void);
void SV_StartSinglePlayerServer(void);
boolean SV_SpawnServer(void);
void SV_SpawnPlayer(INT32 playernum, INT32 x, INT32 y, angle_t angle);
void SV_StopServer(void);
void SV_ResetServer(void);
void CL_AddSplitscreenPlayer(void);

View file

@ -125,7 +125,11 @@ boolean advancedemo;
INT32 debugload = 0;
#endif
UINT16 numskincolors;
menucolor_t *menucolorhead, *menucolortail;
char savegamename[256];
char liveeventbackup[256];
char srb2home[256] = ".";
char srb2path[256] = ".";
@ -413,6 +417,7 @@ static void D_Display(void)
if (!automapactive && !dedicated && cv_renderview.value)
{
rs_rendercalltime = I_GetTimeMicros();
if (players[displayplayer].mo || players[displayplayer].playerstate == PST_DEAD)
{
topleft = screens[0] + viewwindowy*vid.width + viewwindowx;
@ -459,6 +464,7 @@ static void D_Display(void)
if (postimgtype2)
V_DoPostProcessor(1, postimgtype2, postimgparam2);
}
rs_rendercalltime = I_GetTimeMicros() - rs_rendercalltime;
}
if (lastdraw)
@ -594,17 +600,99 @@ static void D_Display(void)
V_DrawRightAlignedString(BASEVIDWIDTH, BASEVIDHEIGHT-ST_HEIGHT-10, V_YELLOWMAP, s);
}
if (cv_renderstats.value)
{
char s[50];
int frametime = I_GetTimeMicros() - rs_prevframetime;
int divisor = 1;
rs_prevframetime = I_GetTimeMicros();
if (rs_rendercalltime > 10000) divisor = 1000;
snprintf(s, sizeof s - 1, "ft %d", frametime / divisor);
V_DrawThinString(30, 10, V_MONOSPACE | V_YELLOWMAP, s);
snprintf(s, sizeof s - 1, "rtot %d", rs_rendercalltime / divisor);
V_DrawThinString(30, 20, V_MONOSPACE | V_YELLOWMAP, s);
snprintf(s, sizeof s - 1, "bsp %d", rs_bsptime / divisor);
V_DrawThinString(30, 30, V_MONOSPACE | V_YELLOWMAP, s);
snprintf(s, sizeof s - 1, "nbsp %d", rs_numbspcalls);
V_DrawThinString(80, 10, V_MONOSPACE | V_BLUEMAP, s);
snprintf(s, sizeof s - 1, "nspr %d", rs_numsprites);
V_DrawThinString(80, 20, V_MONOSPACE | V_BLUEMAP, s);
snprintf(s, sizeof s - 1, "nnod %d", rs_numdrawnodes);
V_DrawThinString(80, 30, V_MONOSPACE | V_BLUEMAP, s);
snprintf(s, sizeof s - 1, "npob %d", rs_numpolyobjects);
V_DrawThinString(80, 40, V_MONOSPACE | V_BLUEMAP, s);
if (rendermode == render_opengl) // OpenGL specific stats
{
snprintf(s, sizeof s - 1, "nsrt %d", rs_hw_nodesorttime / divisor);
V_DrawThinString(30, 40, V_MONOSPACE | V_YELLOWMAP, s);
snprintf(s, sizeof s - 1, "ndrw %d", rs_hw_nodedrawtime / divisor);
V_DrawThinString(30, 50, V_MONOSPACE | V_YELLOWMAP, s);
snprintf(s, sizeof s - 1, "ssrt %d", rs_hw_spritesorttime / divisor);
V_DrawThinString(30, 60, V_MONOSPACE | V_YELLOWMAP, s);
snprintf(s, sizeof s - 1, "sdrw %d", rs_hw_spritedrawtime / divisor);
V_DrawThinString(30, 70, V_MONOSPACE | V_YELLOWMAP, s);
snprintf(s, sizeof s - 1, "fin %d", rs_swaptime / divisor);
V_DrawThinString(30, 80, V_MONOSPACE | V_YELLOWMAP, s);
if (cv_grbatching.value)
{
snprintf(s, sizeof s - 1, "bsrt %d", rs_hw_batchsorttime / divisor);
V_DrawThinString(80, 55, V_MONOSPACE | V_REDMAP, s);
snprintf(s, sizeof s - 1, "bdrw %d", rs_hw_batchdrawtime / divisor);
V_DrawThinString(80, 65, V_MONOSPACE | V_REDMAP, s);
snprintf(s, sizeof s - 1, "npol %d", rs_hw_numpolys);
V_DrawThinString(130, 10, V_MONOSPACE | V_PURPLEMAP, s);
snprintf(s, sizeof s - 1, "ndc %d", rs_hw_numcalls);
V_DrawThinString(130, 20, V_MONOSPACE | V_PURPLEMAP, s);
snprintf(s, sizeof s - 1, "nshd %d", rs_hw_numshaders);
V_DrawThinString(130, 30, V_MONOSPACE | V_PURPLEMAP, s);
snprintf(s, sizeof s - 1, "nvrt %d", rs_hw_numverts);
V_DrawThinString(130, 40, V_MONOSPACE | V_PURPLEMAP, s);
snprintf(s, sizeof s - 1, "ntex %d", rs_hw_numtextures);
V_DrawThinString(185, 10, V_MONOSPACE | V_PURPLEMAP, s);
snprintf(s, sizeof s - 1, "npf %d", rs_hw_numpolyflags);
V_DrawThinString(185, 20, V_MONOSPACE | V_PURPLEMAP, s);
snprintf(s, sizeof s - 1, "ncol %d", rs_hw_numcolors);
V_DrawThinString(185, 30, V_MONOSPACE | V_PURPLEMAP, s);
}
}
else // software specific stats
{
snprintf(s, sizeof s - 1, "prtl %d", rs_sw_portaltime / divisor);
V_DrawThinString(30, 40, V_MONOSPACE | V_YELLOWMAP, s);
snprintf(s, sizeof s - 1, "plns %d", rs_sw_planetime / divisor);
V_DrawThinString(30, 50, V_MONOSPACE | V_YELLOWMAP, s);
snprintf(s, sizeof s - 1, "mskd %d", rs_sw_maskedtime / divisor);
V_DrawThinString(30, 60, V_MONOSPACE | V_YELLOWMAP, s);
snprintf(s, sizeof s - 1, "fin %d", rs_swaptime / divisor);
V_DrawThinString(30, 70, V_MONOSPACE | V_YELLOWMAP, s);
}
}
rs_swaptime = I_GetTimeMicros();
I_FinishUpdate(); // page flip or blit buffer
rs_swaptime = I_GetTimeMicros() - rs_swaptime;
}
needpatchflush = false;
needpatchrecache = false;
}
// Check the renderer's state
// after a possible renderer switch.
void D_CheckRendererState(void)
{
// flush all patches from memory
if (needpatchflush)
{
Z_FlushCachedPatches();
needpatchflush = false;
}
// some patches have been freed,
// so cache them again
if (needpatchrecache)
R_ReloadHUDGraphics();
}
@ -657,7 +745,7 @@ void D_SRB2Loop(void)
*/
/* Smells like a hack... Don't fade Sonic's ass into the title screen. */
if (gamestate != GS_TITLESCREEN)
V_DrawScaledPatch(0, 0, 0, W_CachePatchNum(W_GetNumForName("CONSBACK"), PU_CACHE));
V_DrawScaledPatch(0, 0, 0, W_CachePatchNum(W_GetNumForName("CONSBACK"), PU_PATCH));
for (;;)
{
@ -809,6 +897,7 @@ void D_StartTitle(void)
// In case someone exits out at the same time they start a time attack run,
// reset modeattacking
modeattacking = ATTACKING_NONE;
marathonmode = 0;
// empty maptol so mario/etc sounds don't play in sound test when they shouldn't
maptol = 0;
@ -1125,6 +1214,7 @@ void D_SRB2Main(void)
// default savegame
strcpy(savegamename, SAVEGAMENAME"%u.ssg");
strcpy(liveeventbackup, "live"SAVEGAMENAME".bkp"); // intentionally not ending with .ssg
{
const char *userhome = D_Home(); //Alam: path to home
@ -1153,6 +1243,7 @@ void D_SRB2Main(void)
// can't use sprintf since there is %u in savegamename
strcatbf(savegamename, srb2home, PATHSEP);
strcatbf(liveeventbackup, srb2home, PATHSEP);
snprintf(luafiledir, sizeof luafiledir, "%s" PATHSEP "luafiles", srb2home);
#else // DEFAULTDIR
@ -1165,6 +1256,7 @@ void D_SRB2Main(void)
// can't use sprintf since there is %u in savegamename
strcatbf(savegamename, userhome, PATHSEP);
strcatbf(liveeventbackup, userhome, PATHSEP);
snprintf(luafiledir, sizeof luafiledir, "%s" PATHSEP "luafiles", userhome);
#endif // DEFAULTDIR
@ -1179,10 +1271,17 @@ void D_SRB2Main(void)
// rand() needs seeded regardless of password
srand((unsigned int)time(NULL));
rand();
rand();
rand();
if (M_CheckParm("-password") && M_IsNextParm())
D_SetPassword(M_GetNextParm());
// player setup menu colors must be initialized before
// any wad file is added, as they may contain colors themselves
M_InitPlayerSetupColors();
CONS_Printf("Z_Init(): Init zone memory allocation daemon. \n");
Z_Init();

View file

@ -806,11 +806,15 @@ static const char *packettypename[NUMPACKETTYPE] =
"HASLUAFILE",
"FILEFRAGMENT",
"FILEACK",
"FILERECEIVED",
"TEXTCMD",
"TEXTCMD2",
"CLIENTJOIN",
"NODETIMEOUT",
"RESYNCHING",
"LOGIN",
"PING"
};
@ -837,7 +841,7 @@ static void DebugPrintpacket(const char *header)
size_t ntxtcmd = &((UINT8 *)netbuffer)[doomcom->datalength] - cmd;
fprintf(debugfile, " firsttic %u ply %d tics %d ntxtcmd %s\n ",
(UINT32)ExpandTics(serverpak->starttic), serverpak->numslots, serverpak->numtics, sizeu1(ntxtcmd));
(UINT32)serverpak->starttic, serverpak->numslots, serverpak->numtics, sizeu1(ntxtcmd));
/// \todo Display more readable information about net commands
fprintfstringnewline((char *)cmd, ntxtcmd);
/*fprintfstring((char *)cmd, 3);
@ -856,8 +860,8 @@ static void DebugPrintpacket(const char *header)
case PT_NODEKEEPALIVE:
case PT_NODEKEEPALIVEMIS:
fprintf(debugfile, " tic %4u resendfrom %u\n",
(UINT32)ExpandTics(netbuffer->u.clientpak.client_tic),
(UINT32)ExpandTics (netbuffer->u.clientpak.resendfrom));
(UINT32)ExpandTics(netbuffer->u.clientpak.client_tic, doomcom->remotenode),
(UINT32)ExpandTics (netbuffer->u.clientpak.resendfrom, doomcom->remotenode));
break;
case PT_TEXTCMD:
case PT_TEXTCMD2:

View file

@ -225,6 +225,7 @@ consvar_t cv_allowseenames = {"allowseenames", "Yes", CV_NETVAR, CV_YesNo, NULL,
consvar_t cv_playername = {"name", "Sonic", CV_SAVE|CV_CALL|CV_NOINIT, NULL, Name_OnChange, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_playername2 = {"name2", "Tails", CV_SAVE|CV_CALL|CV_NOINIT, NULL, Name2_OnChange, 0, NULL, NULL, 0, 0, NULL};
// player colors
UINT16 lastgoodcolor = SKINCOLOR_BLUE, lastgoodcolor2 = SKINCOLOR_BLUE;
consvar_t cv_playercolor = {"color", "Blue", CV_CALL|CV_NOINIT, Color_cons_t, Color_OnChange, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_playercolor2 = {"color2", "Orange", CV_CALL|CV_NOINIT, Color_cons_t, Color2_OnChange, 0, NULL, NULL, 0, 0, NULL};
// player's skin, saved for commodity, when using a favorite skins wad..
@ -501,6 +502,8 @@ void D_RegisterServerCommands(void)
COM_AddCommand("archivetest", Command_Archivetest_f);
#endif
COM_AddCommand("downloads", Command_Downloads_f);
// for master server connection
AddMServCommands();
@ -619,7 +622,7 @@ void D_RegisterClientCommands(void)
for (i = 0; i < MAXSKINCOLORS; i++)
{
Color_cons_t[i].value = i;
Color_cons_t[i].strvalue = Color_Names[i];
Color_cons_t[i].strvalue = skincolors[i].name;
}
Color_cons_t[MAXSKINCOLORS].value = 0;
Color_cons_t[MAXSKINCOLORS].strvalue = NULL;
@ -672,6 +675,7 @@ void D_RegisterClientCommands(void)
// GIF variables
CV_RegisterVar(&cv_gif_optimize);
CV_RegisterVar(&cv_gif_downscale);
CV_RegisterVar(&cv_gif_dynamicdelay);
CV_RegisterVar(&cv_gif_localcolortable);
#ifdef WALLSPLATS
@ -1153,7 +1157,7 @@ UINT8 CanChangeSkin(INT32 playernum)
// Server has skin change restrictions.
if (cv_restrictskinchange.value)
{
if (gametype == GT_COOP)
if (gametyperules & GTR_FRIENDLY)
return true;
// Can change skin during initial countdown.
@ -1221,15 +1225,20 @@ static void SendNameAndColor(void)
CV_StealthSetValue(&cv_playercolor, skincolor_blueteam);
}
// never allow the color "none"
if (!cv_playercolor.value)
// don't allow inaccessible colors
if (!skincolors[cv_playercolor.value].accessible)
{
if (players[consoleplayer].skincolor)
if (players[consoleplayer].skincolor && skincolors[players[consoleplayer].skincolor].accessible)
CV_StealthSetValue(&cv_playercolor, players[consoleplayer].skincolor);
else if (skins[players[consoleplayer].skin].prefcolor)
CV_StealthSetValue(&cv_playercolor, skins[players[consoleplayer].skin].prefcolor);
else
else if (skincolors[atoi(cv_playercolor.defaultvalue)].accessible)
CV_StealthSet(&cv_playercolor, cv_playercolor.defaultvalue);
else if (skins[players[consoleplayer].skin].prefcolor && skincolors[skins[players[consoleplayer].skin].prefcolor].accessible)
CV_StealthSetValue(&cv_playercolor, skins[players[consoleplayer].skin].prefcolor);
else {
UINT16 i = 0;
while (i<numskincolors && !skincolors[i].accessible) i++;
CV_StealthSetValue(&cv_playercolor, (i != numskincolors) ? i : SKINCOLOR_BLUE);
}
}
if (!strcmp(cv_playername.string, player_names[consoleplayer])
@ -1276,10 +1285,10 @@ static void SendNameAndColor(void)
{
CV_StealthSetValue(&cv_playercolor, skins[cv_skin.value].prefcolor);
players[consoleplayer].skincolor = cv_playercolor.value % MAXSKINCOLORS;
players[consoleplayer].skincolor = cv_playercolor.value % numskincolors;
if (players[consoleplayer].mo)
players[consoleplayer].mo->color = (UINT8)players[consoleplayer].skincolor;
players[consoleplayer].mo->color = (UINT16)players[consoleplayer].skincolor;
}*/
}
else
@ -1317,7 +1326,7 @@ static void SendNameAndColor(void)
// Finally write out the complete packet and send it off.
WRITESTRINGN(p, cv_playername.zstring, MAXPLAYERNAME);
WRITEUINT32(p, (UINT32)players[consoleplayer].availabilities);
WRITEUINT8(p, (UINT8)cv_playercolor.value);
WRITEUINT16(p, (UINT16)cv_playercolor.value);
WRITEUINT8(p, (UINT8)cv_skin.value);
SendNetXCmd(XD_NAMEANDCOLOR, buf, p - buf);
}
@ -1344,15 +1353,20 @@ static void SendNameAndColor2(void)
CV_StealthSetValue(&cv_playercolor2, skincolor_blueteam);
}
// never allow the color "none"
if (!cv_playercolor2.value)
// don't allow inaccessible colors
if (!skincolors[cv_playercolor2.value].accessible)
{
if (players[secondplaya].skincolor)
if (players[secondplaya].skincolor && skincolors[players[secondplaya].skincolor].accessible)
CV_StealthSetValue(&cv_playercolor2, players[secondplaya].skincolor);
else if (skins[players[secondplaya].skin].prefcolor)
else if (skincolors[atoi(cv_playercolor2.defaultvalue)].accessible)
CV_StealthSet(&cv_playercolor, cv_playercolor2.defaultvalue);
else if (skins[players[secondplaya].skin].prefcolor && skincolors[skins[players[secondplaya].skin].prefcolor].accessible)
CV_StealthSetValue(&cv_playercolor2, skins[players[secondplaya].skin].prefcolor);
else
CV_StealthSet(&cv_playercolor2, cv_playercolor2.defaultvalue);
else {
UINT16 i = 0;
while (i<numskincolors && !skincolors[i].accessible) i++;
CV_StealthSetValue(&cv_playercolor2, (i != numskincolors) ? i : SKINCOLOR_BLUE);
}
}
players[secondplaya].availabilities = R_GetSkinAvailabilities();
@ -1405,7 +1419,7 @@ static void SendNameAndColor2(void)
{
CV_StealthSetValue(&cv_playercolor2, skins[players[secondplaya].skin].prefcolor);
players[secondplaya].skincolor = cv_playercolor2.value % MAXSKINCOLORS;
players[secondplaya].skincolor = cv_playercolor2.value % numskincolors;
if (players[secondplaya].mo)
players[secondplaya].mo->color = players[secondplaya].skincolor;
@ -1428,7 +1442,8 @@ static void Got_NameAndColor(UINT8 **cp, INT32 playernum)
{
player_t *p = &players[playernum];
char name[MAXPLAYERNAME+1];
UINT8 color, skin;
UINT16 color;
UINT8 skin;
#ifdef PARANOIA
if (playernum < 0 || playernum > MAXPLAYERS)
@ -1447,7 +1462,7 @@ static void Got_NameAndColor(UINT8 **cp, INT32 playernum)
READSTRINGN(*cp, name, MAXPLAYERNAME);
p->availabilities = READUINT32(*cp);
color = READUINT8(*cp);
color = READUINT16(*cp);
skin = READUINT8(*cp);
// set name
@ -1455,9 +1470,9 @@ static void Got_NameAndColor(UINT8 **cp, INT32 playernum)
SetPlayerName(playernum, name);
// set color
p->skincolor = color % MAXSKINCOLORS;
p->skincolor = color % numskincolors;
if (p->mo)
p->mo->color = (UINT8)p->skincolor;
p->mo->color = (UINT16)p->skincolor;
// normal player colors
if (server && (p != &players[consoleplayer] && p != &players[secondarydisplayplayer]))
@ -1474,8 +1489,8 @@ static void Got_NameAndColor(UINT8 **cp, INT32 playernum)
kick = true;
}
// don't allow color "none"
if (!p->skincolor)
// don't allow inaccessible colors
if (skincolors[p->skincolor].accessible == false)
kick = true;
// availabilities
@ -1733,7 +1748,7 @@ void D_MapChange(INT32 mapnum, INT32 newgametype, boolean pultmode, boolean rese
}
CONS_Debug(DBG_GAMELOGIC, "Map change: mapnum=%d gametype=%d ultmode=%d resetplayers=%d delay=%d skipprecutscene=%d\n",
mapnum, newgametype, pultmode, resetplayers, delay, skipprecutscene);
if ((netgame || multiplayer) && !((gametype == newgametype) && (newgametype == GT_COOP)))
if ((netgame || multiplayer) && !((gametype == newgametype) && (gametypedefaultrules[newgametype] & GTR_CAMPAIGN)))
FLS = false;
if (delay != 2)
@ -1975,7 +1990,7 @@ static void Command_Map_f(void)
fromlevelselect =
( netgame || multiplayer ) &&
newgametype == gametype &&
newgametype == GT_COOP;
gametypedefaultrules[newgametype] & GTR_CAMPAIGN;
}
}
@ -2765,7 +2780,7 @@ static void Got_Teamchange(UINT8 **cp, INT32 playernum)
players[playernum].spectator = false;
//If joining after hidetime in normal tag, default to being IT.
if (gametype == GT_TAG && (leveltime > (hidetime * TICRATE)))
if (((gametyperules & (GTR_TAG|GTR_HIDEFROZEN)) == GTR_TAG) && (leveltime > (hidetime * TICRATE)))
{
NetPacket.packet.newteam = 1; //minor hack, causes the "is it" message to be printed later.
players[playernum].pflags |= PF_TAGIT; //make the player IT.
@ -3556,6 +3571,8 @@ static void Command_Playintro_f(void)
*/
FUNCNORETURN static ATTRNORETURN void Command_Quit_f(void)
{
if (Playing())
LUAh_GameQuit();
I_Quit();
}
@ -3613,7 +3630,7 @@ static void PointLimit_OnChange(void)
static void NumLaps_OnChange(void)
{
// Just don't be verbose
if (gametype == GT_RACE)
if ((gametyperules & (GTR_RACE|GTR_LIVES)) == GTR_RACE)
CONS_Printf(M_GetText("Number of laps set to %d\n"), cv_numlaps.value);
}
@ -3727,7 +3744,7 @@ static void ExitMove_OnChange(void)
{
UINT8 i;
if (!(netgame || multiplayer) || gametype != GT_COOP)
if (!(netgame || multiplayer) || !(gametyperules & GTR_FRIENDLY))
return;
if (cv_exitmove.value)
@ -4217,6 +4234,9 @@ void Command_ExitGame_f(void)
{
INT32 i;
if (Playing())
LUAh_GameQuit();
D_QuitNetGame();
CL_Reset();
CV_ClearChangedFlags();
@ -4473,25 +4493,30 @@ static void Skin2_OnChange(void)
*/
static void Color_OnChange(void)
{
if (!Playing())
return; // do whatever you want
if (!(cv_debug || devparm) && !(multiplayer || netgame)) // In single player.
{
CV_StealthSet(&cv_skin, skins[players[consoleplayer].skin].name);
return;
}
if (!P_PlayerMoving(consoleplayer))
{
// Color change menu scrolling fix is no longer necessary
SendNameAndColor();
if (!Playing()) {
if (!cv_playercolor.value || !skincolors[cv_playercolor.value].accessible)
CV_StealthSetValue(&cv_playercolor, lastgoodcolor);
}
else
{
CV_StealthSetValue(&cv_playercolor,
players[consoleplayer].skincolor);
if (!(cv_debug || devparm) && !(multiplayer || netgame)) // In single player.
{
CV_StealthSet(&cv_skin, skins[players[consoleplayer].skin].name);
return;
}
if (!P_PlayerMoving(consoleplayer) && skincolors[players[consoleplayer].skincolor].accessible == true)
{
// Color change menu scrolling fix is no longer necessary
SendNameAndColor();
}
else
{
CV_StealthSetValue(&cv_playercolor,
players[consoleplayer].skincolor);
}
}
lastgoodcolor = cv_playercolor.value;
}
/** Sends a color change for the secondary splitscreen player, unless that
@ -4502,18 +4527,24 @@ static void Color_OnChange(void)
static void Color2_OnChange(void)
{
if (!Playing() || !splitscreen)
return; // do whatever you want
if (!P_PlayerMoving(secondarydisplayplayer))
{
// Color change menu scrolling fix is no longer necessary
SendNameAndColor2();
if (!cv_playercolor2.value || !skincolors[cv_playercolor2.value].accessible)
CV_StealthSetValue(&cv_playercolor2, lastgoodcolor2);
}
else
{
CV_StealthSetValue(&cv_playercolor2,
players[secondarydisplayplayer].skincolor);
if (!P_PlayerMoving(secondarydisplayplayer) && skincolors[players[secondarydisplayplayer].skincolor].accessible == true)
{
// Color change menu scrolling fix is no longer necessary
SendNameAndColor2();
}
else
{
CV_StealthSetValue(&cv_playercolor2,
players[secondarydisplayplayer].skincolor);
}
}
lastgoodcolor2 = cv_playercolor2.value;
}
/** Displays the result of the chat being muted or unmuted.
@ -4590,7 +4621,7 @@ static void Command_ShowTime_f(void)
static void BaseNumLaps_OnChange(void)
{
if (gametype == GT_RACE)
if ((gametyperules & (GTR_RACE|GTR_LIVES)) == GTR_RACE)
{
if (cv_basenumlaps.value)
CONS_Printf(M_GetText("Number of laps will be changed to map defaults next round.\n"));

View file

@ -56,7 +56,7 @@
#include <errno.h>
// Prototypes
static boolean SV_SendFile(INT32 node, const char *filename, UINT8 fileid);
static boolean AddFileToSendQueue(INT32 node, const char *filename, UINT8 fileid);
// Sender structure
typedef struct filetx_s
@ -69,7 +69,6 @@ typedef struct filetx_s
UINT32 size; // Size of the file
UINT8 fileid;
INT32 node; // Destination
boolean textmode; // For files requested by Lua without the "b" option
struct filetx_s *next; // Next file in the list
} filetx_t;
@ -77,8 +76,13 @@ typedef struct filetx_s
typedef struct filetran_s
{
filetx_t *txlist; // Linked list of all files for the node
UINT8 iteration;
UINT8 ackediteration;
UINT32 position; // The current position in the file
boolean *ackedfragments;
UINT32 ackedsize;
FILE *currentfile; // The file currently being sent/received
tic_t dontsenduntil;
} filetran_t;
static filetran_t transfer[MAXNETNODES];
@ -88,15 +92,28 @@ static filetran_t transfer[MAXNETNODES];
// Receiver structure
INT32 fileneedednum; // Number of files needed to join the server
fileneeded_t fileneeded[MAX_WADFILES]; // List of needed files
static tic_t lasttimeackpacketsent = 0;
char downloaddir[512] = "DOWNLOAD";
#ifdef CLIENT_LOADINGSCREEN
// For resuming failed downloads
typedef struct
{
char filename[MAX_WADPATH];
UINT8 md5sum[16];
boolean *receivedfragments;
UINT32 fragmentsize;
UINT32 currentsize;
} pauseddownload_t;
static pauseddownload_t *pauseddownload = NULL;
#ifndef NONET
// for cl loading screen
INT32 lastfilenum = -1;
#endif
luafiletransfer_t *luafiletransfers = NULL;
boolean waitingforluafiletransfer = false;
boolean waitingforluafilecommand = false;
char luafiledir[256 + 16] = "luafiles";
@ -159,25 +176,29 @@ void D_ParseFileneeded(INT32 fileneedednum_parm, UINT8 *fileneededstr)
for (i = 0; i < fileneedednum; i++)
{
fileneeded[i].status = FS_NOTFOUND; // We haven't even started looking for the file yet
fileneeded[i].justdownloaded = false;
filestatus = READUINT8(p); // The first byte is the file status
fileneeded[i].willsend = (UINT8)(filestatus >> 4);
fileneeded[i].totalsize = READUINT32(p); // The four next bytes are the file size
fileneeded[i].file = NULL; // The file isn't open yet
READSTRINGN(p, fileneeded[i].filename, MAX_WADPATH); // The next bytes are the file name
READMEM(p, fileneeded[i].md5sum, 16); // The last 16 bytes are the file checksum
fileneeded[i].textmode = false;
}
}
void CL_PrepareDownloadSaveGame(const char *tmpsave)
{
#ifndef NONET
lastfilenum = -1;
#endif
fileneedednum = 1;
fileneeded[0].status = FS_REQUESTED;
fileneeded[0].justdownloaded = false;
fileneeded[0].totalsize = UINT32_MAX;
fileneeded[0].file = NULL;
memset(fileneeded[0].md5sum, 0, 16);
strcpy(fileneeded[0].filename, tmpsave);
fileneeded[0].textmode = false;
}
/** Checks the server to see if we CAN download all the files,
@ -246,6 +267,31 @@ boolean CL_CheckDownloadable(void)
return false;
}
/** Returns true if a needed file transfer can be resumed
*
* \param file The needed file to resume the transfer for
* \return True if the transfer can be resumed
*
*/
static boolean CL_CanResumeDownload(fileneeded_t *file)
{
return pauseddownload
&& !strcmp(pauseddownload->filename, file->filename) // Same name
&& !memcmp(pauseddownload->md5sum, file->md5sum, 16) // Same checksum
&& pauseddownload->fragmentsize == file->fragmentsize; // Same fragment size
}
void CL_AbortDownloadResume(void)
{
if (!pauseddownload)
return;
free(pauseddownload->receivedfragments);
remove(pauseddownload->filename);
free(pauseddownload);
pauseddownload = NULL;
}
/** Sends requests for files in the ::fileneeded table with a status of
* ::FS_NOTFOUND.
*
@ -253,7 +299,7 @@ boolean CL_CheckDownloadable(void)
* \note Sends a PT_REQUESTFILE packet
*
*/
boolean CL_SendRequestFile(void)
boolean CL_SendFileRequest(void)
{
char *p;
INT32 i;
@ -298,7 +344,7 @@ boolean CL_SendRequestFile(void)
// get request filepak and put it on the send queue
// returns false if a requested file was not found or cannot be sent
boolean Got_RequestFilePak(INT32 node)
boolean PT_RequestFile(INT32 node)
{
char wad[MAX_WADPATH+1];
UINT8 *p = netbuffer->u.textcmd;
@ -309,7 +355,7 @@ boolean Got_RequestFilePak(INT32 node)
if (id == 0xFF)
break;
READSTRINGN(p, wad, MAX_WADPATH);
if (!SV_SendFile(node, wad, id))
if (!AddFileToSendQueue(node, wad, id))
{
SV_AbortSendFiles(node);
return false; // don't read the rest of the files
@ -462,8 +508,6 @@ void AddLuaFileTransfer(const char *filename, const char *mode)
luafiletransfer_t *filetransfer;
static INT32 id;
//CONS_Printf("AddLuaFileTransfer \"%s\"\n", filename);
// Find the last transfer in the list and set a pointer to its "next" field
prevnext = &luafiletransfers;
while (*prevnext)
@ -493,26 +537,11 @@ void AddLuaFileTransfer(const char *filename, const char *mode)
strlcpy(filetransfer->mode, mode, sizeof(filetransfer->mode));
if (server)
{
INT32 i;
// Set status to "waiting" for everyone
for (i = 0; i < MAXNETNODES; i++)
filetransfer->nodestatus[i] = LFTNS_WAITING;
if (!luafiletransfers->next) // Only if there is no transfer already going on
{
if (FIL_ReadFileOK(filetransfer->realfilename))
SV_PrepareSendLuaFileToNextNode();
else
{
// Send a net command with 0 as its first byte to indicate the file couldn't be opened
UINT8 success = 0;
SendNetXCmd(XD_LUAFILE, &success, 1);
}
}
}
// Only if there is no transfer already going on
if (server && filetransfer == luafiletransfers)
SV_PrepareSendLuaFile();
else
filetransfer->ongoing = false;
// Store the callback so it can be called once everyone has the file
filetransfer->id = id;
@ -526,7 +555,7 @@ void AddLuaFileTransfer(const char *filename, const char *mode)
}
}
void SV_PrepareSendLuaFileToNextNode(void)
static void SV_PrepareSendLuaFileToNextNode(void)
{
INT32 i;
UINT8 success = 1;
@ -550,6 +579,45 @@ void SV_PrepareSendLuaFileToNextNode(void)
SendNetXCmd(XD_LUAFILE, &success, 1);
}
void SV_PrepareSendLuaFile(void)
{
char *binfilename;
INT32 i;
luafiletransfers->ongoing = true;
// Set status to "waiting" for everyone
for (i = 0; i < MAXNETNODES; i++)
luafiletransfers->nodestatus[i] = LFTNS_WAITING;
if (FIL_ReadFileOK(luafiletransfers->realfilename))
{
// If opening in text mode, convert all newlines to LF
if (!strchr(luafiletransfers->mode, 'b'))
{
binfilename = strdup(va("%s" PATHSEP "$$$%d%d.tmp",
luafiledir, rand(), rand()));
if (!binfilename)
I_Error("SV_PrepareSendLuaFile: Out of memory\n");
if (!FIL_ConvertTextFileToBinary(luafiletransfers->realfilename, binfilename))
I_Error("SV_PrepareSendLuaFile: Failed to convert file newlines\n");
// Use the temporary file instead
free(luafiletransfers->realfilename);
luafiletransfers->realfilename = binfilename;
}
SV_PrepareSendLuaFileToNextNode();
}
else
{
// Send a net command with 0 as its first byte to indicate the file couldn't be opened
UINT8 success = 0;
SendNetXCmd(XD_LUAFILE, &success, 1);
}
}
void SV_HandleLuaFileSent(UINT8 node)
{
luafiletransfers->nodestatus[node] = LFTNS_SENT;
@ -560,6 +628,10 @@ void RemoveLuaFileTransfer(void)
{
luafiletransfer_t *filetransfer = luafiletransfers;
// If it was a temporary file, delete it
if (server && !strchr(filetransfer->mode, 'b'))
remove(filetransfer->realfilename);
RemoveLuaFileCallback(filetransfer->id);
luafiletransfers = filetransfer->next;
@ -596,20 +668,28 @@ void CL_PrepareDownloadLuaFile(void)
return;
}
if (luafiletransfers->ongoing)
{
waitingforluafilecommand = true;
return;
}
// Tell the server we are ready to receive the file
netbuffer->packettype = PT_ASKLUAFILE;
HSendPacket(servernode, true, 0, 0);
fileneedednum = 1;
fileneeded[0].status = FS_REQUESTED;
fileneeded[0].justdownloaded = false;
fileneeded[0].totalsize = UINT32_MAX;
fileneeded[0].file = NULL;
memset(fileneeded[0].md5sum, 0, 16);
strcpy(fileneeded[0].filename, luafiletransfers->realfilename);
fileneeded[0].textmode = !strchr(luafiletransfers->mode, 'b');
// Make sure all directories in the file path exist
MakePathDirs(fileneeded[0].filename);
luafiletransfers->ongoing = true;
}
// Number of files to send
@ -620,12 +700,12 @@ static INT32 filestosend = 0;
*
* \param node The node to send the file to
* \param filename The file to send
* \param fileid ???
* \sa SV_SendRam
* \sa SV_SendLuaFile
* \param fileid The index of the file in the list of added files
* \sa AddRamToSendQueue
* \sa AddLuaFileToSendQueue
*
*/
static boolean SV_SendFile(INT32 node, const char *filename, UINT8 fileid)
static boolean AddFileToSendQueue(INT32 node, const char *filename, UINT8 fileid)
{
filetx_t **q; // A pointer to the "next" field of the last file in the list
filetx_t *p; // The new file request
@ -643,7 +723,7 @@ static boolean SV_SendFile(INT32 node, const char *filename, UINT8 fileid)
// Allocate a file request and append it to the file list
p = *q = (filetx_t *)malloc(sizeof (filetx_t));
if (!p)
I_Error("SV_SendFile: No more memory\n");
I_Error("AddFileToSendQueue: No more memory\n");
// Initialise with zeros
memset(p, 0, sizeof (filetx_t));
@ -651,7 +731,7 @@ static boolean SV_SendFile(INT32 node, const char *filename, UINT8 fileid)
// Allocate the file name
p->id.filename = (char *)malloc(MAX_WADPATH);
if (!p->id.filename)
I_Error("SV_SendFile: No more memory\n");
I_Error("AddFileToSendQueue: No more memory\n");
// Set the file name and get rid of the path
strlcpy(p->id.filename, filename, MAX_WADPATH);
@ -711,12 +791,12 @@ static boolean SV_SendFile(INT32 node, const char *filename, UINT8 fileid)
* \param data The memory block to send
* \param size The size of the block in bytes
* \param freemethod How to free the block after it has been sent
* \param fileid ???
* \sa SV_SendFile
* \sa SV_SendLuaFile
* \param fileid The index of the file in the list of added files
* \sa AddFileToSendQueue
* \sa AddLuaFileToSendQueue
*
*/
void SV_SendRam(INT32 node, void *data, size_t size, freemethod_t freemethod, UINT8 fileid)
void AddRamToSendQueue(INT32 node, void *data, size_t size, freemethod_t freemethod, UINT8 fileid)
{
filetx_t **q; // A pointer to the "next" field of the last file in the list
filetx_t *p; // The new file request
@ -729,7 +809,7 @@ void SV_SendRam(INT32 node, void *data, size_t size, freemethod_t freemethod, UI
// Allocate a file request and append it to the file list
p = *q = (filetx_t *)malloc(sizeof (filetx_t));
if (!p)
I_Error("SV_SendRam: No more memory\n");
I_Error("AddRamToSendQueue: No more memory\n");
// Initialise with zeros
memset(p, 0, sizeof (filetx_t));
@ -749,11 +829,11 @@ void SV_SendRam(INT32 node, void *data, size_t size, freemethod_t freemethod, UI
*
* \param node The node to send the file to
* \param filename The file to send
* \sa SV_SendFile
* \sa SV_SendRam
* \sa AddFileToSendQueue
* \sa AddRamToSendQueue
*
*/
boolean SV_SendLuaFile(INT32 node, const char *filename, boolean textmode)
boolean AddLuaFileToSendQueue(INT32 node, const char *filename)
{
filetx_t **q; // A pointer to the "next" field of the last file in the list
filetx_t *p; // The new file request
@ -770,7 +850,7 @@ boolean SV_SendLuaFile(INT32 node, const char *filename, boolean textmode)
// Allocate a file request and append it to the file list
p = *q = (filetx_t *)malloc(sizeof (filetx_t));
if (!p)
I_Error("SV_SendLuaFile: No more memory\n");
I_Error("AddLuaFileToSendQueue: No more memory\n");
// Initialise with zeros
memset(p, 0, sizeof (filetx_t));
@ -778,15 +858,12 @@ boolean SV_SendLuaFile(INT32 node, const char *filename, boolean textmode)
// Allocate the file name
p->id.filename = (char *)malloc(MAX_WADPATH); // !!!
if (!p->id.filename)
I_Error("SV_SendLuaFile: No more memory\n");
I_Error("AddLuaFileToSendQueue: No more memory\n");
// Set the file name and get rid of the path
strlcpy(p->id.filename, filename, MAX_WADPATH); // !!!
//nameonly(p->id.filename);
// Open in text mode if required by the Lua script
p->textmode = textmode;
DEBFILE(va("Sending Lua file %s to %d\n", filename, node));
p->ram = SF_FILE; // It's a file, we need to close it and free its name once we're done sending it
p->next = NULL; // End of list
@ -804,7 +881,8 @@ static void SV_EndFileSend(INT32 node)
{
filetx_t *p = transfer[node].txlist;
// Free the file request according to the freemethod parameter used with SV_SendFile/Ram
// Free the file request according to the freemethod
// parameter used with AddFileToSendQueue/AddRamToSendQueue
switch (p->ram)
{
case SF_FILE: // It's a file, close it and free its filename
@ -829,43 +907,32 @@ static void SV_EndFileSend(INT32 node)
// Indicate that the transmission is over
transfer[node].currentfile = NULL;
if (transfer[node].ackedfragments)
free(transfer[node].ackedfragments);
transfer[node].ackedfragments = NULL;
filestosend--;
}
#define PACKETPERTIC net_bandwidth/(TICRATE*software_MAXPACKETLENGTH)
#define FILEFRAGMENTSIZE (software_MAXPACKETLENGTH - (FILETXHEADER + BASEPACKETSIZE))
/** Handles file transmission
*
* \todo Use an acknowledging method more adapted to file transmission
* The current download speed suffers from lack of ack packets,
* especially when the one downloading has high latency
*
*/
void SV_FileSendTicker(void)
void FileSendTicker(void)
{
static INT32 currentnode = 0;
filetx_pak *p;
size_t size;
size_t fragmentsize;
filetx_t *f;
INT32 packetsent, ram, i, j;
INT32 maxpacketsent;
if (!filestosend) // No file to send
return;
if (cv_downloadspeed.value) // New (and experimental) behavior
{
if (cv_downloadspeed.value) // New behavior
packetsent = cv_downloadspeed.value;
// Don't send more packets than we have free acks
#ifndef NONET
maxpacketsent = Net_GetFreeAcks(false) - 5; // Let 5 extra acks just in case
#else
maxpacketsent = 1;
#endif
if (packetsent > maxpacketsent && maxpacketsent > 0) // Send at least one packet
packetsent = maxpacketsent;
}
else // Old behavior
{
packetsent = PACKETPERTIC;
@ -882,11 +949,12 @@ void SV_FileSendTicker(void)
i = (i+1) % MAXNETNODES, j++)
{
if (transfer[i].txlist)
goto found;
break;
}
// no transfer to do
I_Error("filestosend=%d but no file to send found\n", filestosend);
found:
if (j >= MAXNETNODES)
I_Error("filestosend=%d but no file to send found\n", filestosend);
currentnode = (i+1) % MAXNETNODES;
f = transfer[i].txlist;
ram = f->ram;
@ -899,7 +967,7 @@ void SV_FileSendTicker(void)
long filesize;
transfer[i].currentfile =
fopen(f->id.filename, f->textmode ? "r" : "rb");
fopen(f->id.filename, "rb");
if (!transfer[i].currentfile)
I_Error("File %s does not exist",
@ -920,57 +988,232 @@ void SV_FileSendTicker(void)
}
else // Sending RAM
transfer[i].currentfile = (FILE *)1; // Set currentfile to a non-null value to indicate that it is open
transfer[i].iteration = 1;
transfer[i].ackediteration = 0;
transfer[i].position = 0;
transfer[i].ackedsize = 0;
transfer[i].ackedfragments = calloc(f->size / FILEFRAGMENTSIZE + 1, sizeof(*transfer[i].ackedfragments));
if (!transfer[i].ackedfragments)
I_Error("FileSendTicker: No more memory\n");
transfer[i].dontsenduntil = 0;
}
// If the client hasn't acknowledged any fragment from the previous iteration,
// it is most likely because their acks haven't had enough time to reach the server
// yet, due to latency. In that case, we wait a little to avoid useless resend.
if (I_GetTime() < transfer[i].dontsenduntil)
continue;
// Find the first non-acknowledged fragment
while (transfer[i].ackedfragments[transfer[i].position / FILEFRAGMENTSIZE])
{
transfer[i].position += FILEFRAGMENTSIZE;
if (transfer[i].position >= f->size)
{
if (transfer[i].ackediteration < transfer[i].iteration)
transfer[i].dontsenduntil = I_GetTime() + TICRATE / 2;
transfer[i].position = 0;
transfer[i].iteration++;
}
}
// Build a packet containing a file fragment
p = &netbuffer->u.filetxpak;
size = software_MAXPACKETLENGTH - (FILETXHEADER + BASEPACKETSIZE);
if (f->size-transfer[i].position < size)
size = f->size-transfer[i].position;
fragmentsize = FILEFRAGMENTSIZE;
if (f->size-transfer[i].position < fragmentsize)
fragmentsize = f->size-transfer[i].position;
if (ram)
M_Memcpy(p->data, &f->id.ram[transfer[i].position], size);
M_Memcpy(p->data, &f->id.ram[transfer[i].position], fragmentsize);
else
{
size_t n = fread(p->data, 1, size, transfer[i].currentfile);
if (n != size) // Either an error or Windows turning CR-LF into LF
{
if (f->textmode && feof(transfer[i].currentfile))
size = n;
else if (fread(p->data, 1, size, transfer[i].currentfile) != size)
I_Error("SV_FileSendTicker: can't read %s byte on %s at %d because %s", sizeu1(size), f->id.filename, transfer[i].position, M_FileError(transfer[i].currentfile));
}
fseek(transfer[i].currentfile, transfer[i].position, SEEK_SET);
if (fread(p->data, 1, fragmentsize, transfer[i].currentfile) != fragmentsize)
I_Error("FileSendTicker: can't read %s byte on %s at %d because %s", sizeu1(fragmentsize), f->id.filename, transfer[i].position, M_FileError(transfer[i].currentfile));
}
p->iteration = transfer[i].iteration;
p->position = LONG(transfer[i].position);
// Put flag so receiver knows the total size
if (transfer[i].position + size == f->size || (f->textmode && feof(transfer[i].currentfile)))
p->position |= LONG(0x80000000);
p->fileid = f->fileid;
p->size = SHORT((UINT16)size);
p->filesize = LONG(f->size);
p->size = SHORT((UINT16)FILEFRAGMENTSIZE);
// Send the packet
if (HSendPacket(i, true, 0, FILETXHEADER + size)) // Reliable SEND
if (HSendPacket(i, false, 0, FILETXHEADER + fragmentsize)) // Don't use the default acknowledgement system
{ // Success
transfer[i].position = (UINT32)(transfer[i].position + size);
if (transfer[i].position == f->size || (f->textmode && feof(transfer[i].currentfile))) // Finish?
SV_EndFileSend(i);
transfer[i].position = (UINT32)(transfer[i].position + fragmentsize);
if (transfer[i].position >= f->size)
{
if (transfer[i].ackediteration < transfer[i].iteration)
transfer[i].dontsenduntil = I_GetTime() + TICRATE / 2;
transfer[i].position = 0;
transfer[i].iteration++;
}
}
else
{ // Not sent for some odd reason, retry at next call
if (!ram)
fseek(transfer[i].currentfile,transfer[i].position, SEEK_SET);
// Exit the while (can't send this one so why should i send the next?)
break;
}
}
}
void Got_Filetxpak(void)
void PT_FileAck(void)
{
fileack_pak *packet = &netbuffer->u.fileack;
INT32 node = doomcom->remotenode;
filetran_t *trans = &transfer[node];
INT32 i, j;
// Wrong file id? Ignore it, it's probably a late packet
if (!(trans->txlist && packet->fileid == trans->txlist->fileid))
return;
if (packet->numsegments * sizeof(*packet->segments) != doomcom->datalength - BASEPACKETSIZE - sizeof(*packet))
{
Net_CloseConnection(node);
return;
}
if (packet->iteration > trans->ackediteration)
{
trans->ackediteration = packet->iteration;
if (trans->ackediteration >= trans->iteration - 1)
trans->dontsenduntil = 0;
}
for (i = 0; i < packet->numsegments; i++)
{
fileacksegment_t *segment = &packet->segments[i];
for (j = 0; j < 32; j++)
if (LONG(segment->acks) & (1 << j))
{
if (LONG(segment->start) * FILEFRAGMENTSIZE >= trans->txlist->size)
{
Net_CloseConnection(node);
return;
}
if (!trans->ackedfragments[LONG(segment->start) + j])
{
trans->ackedfragments[LONG(segment->start) + j] = true;
trans->ackedsize += FILEFRAGMENTSIZE;
// If the last missing fragment was acked, finish!
if (trans->ackedsize == trans->txlist->size)
{
SV_EndFileSend(node);
return;
}
}
}
}
}
void PT_FileReceived(void)
{
filetx_t *trans = transfer[doomcom->remotenode].txlist;
if (trans && netbuffer->u.filereceived == trans->fileid)
SV_EndFileSend(doomcom->remotenode);
}
static void SendAckPacket(fileack_pak *packet, UINT8 fileid)
{
size_t packetsize;
INT32 i;
packetsize = sizeof(*packet) + packet->numsegments * sizeof(*packet->segments);
// Finalise the packet
packet->fileid = fileid;
for (i = 0; i < packet->numsegments; i++)
{
packet->segments[i].start = LONG(packet->segments[i].start);
packet->segments[i].acks = LONG(packet->segments[i].acks);
}
// Send the packet
netbuffer->packettype = PT_FILEACK;
M_Memcpy(&netbuffer->u.fileack, packet, packetsize);
HSendPacket(servernode, false, 0, packetsize);
// Clear the packet
memset(packet, 0, sizeof(*packet) + 512);
}
static void AddFragmentToAckPacket(fileack_pak *packet, UINT8 iteration, UINT32 fragmentpos, UINT8 fileid)
{
fileacksegment_t *segment = &packet->segments[packet->numsegments - 1];
packet->iteration = max(packet->iteration, iteration);
if (packet->numsegments == 0
|| fragmentpos < segment->start
|| fragmentpos - segment->start >= 32)
{
// If the packet becomes too big, send it
if ((packet->numsegments + 1) * sizeof(*segment) > 512)
SendAckPacket(packet, fileid);
packet->numsegments++;
segment = &packet->segments[packet->numsegments - 1];
segment->start = fragmentpos;
}
// Set the bit that represents the fragment
segment->acks |= 1 << (fragmentpos - segment->start);
}
void FileReceiveTicker(void)
{
INT32 i;
for (i = 0; i < fileneedednum; i++)
{
fileneeded_t *file = &fileneeded[i];
if (file->status == FS_DOWNLOADING)
{
if (lasttimeackpacketsent - I_GetTime() > TICRATE / 2)
SendAckPacket(file->ackpacket, i);
// When resuming a tranfer, start with telling
// the server what parts we already received
if (file->ackresendposition != UINT32_MAX && file->status == FS_DOWNLOADING)
{
// Acknowledge ~70 MB/s, whichs means the client sends ~18 KB/s
INT32 j;
for (j = 0; j < 2048; j++)
{
if (file->receivedfragments[file->ackresendposition])
AddFragmentToAckPacket(file->ackpacket, file->iteration, file->ackresendposition, i);
file->ackresendposition++;
if (file->ackresendposition * file->fragmentsize >= file->totalsize)
{
file->ackresendposition = UINT32_MAX;
break;
}
}
}
}
}
}
void PT_FileFragment(void)
{
INT32 filenum = netbuffer->u.filetxpak.fileid;
fileneeded_t *file = &fileneeded[filenum];
UINT32 fragmentpos = LONG(netbuffer->u.filetxpak.position);
UINT16 fragmentsize = SHORT(netbuffer->u.filetxpak.size);
UINT16 boundedfragmentsize = doomcom->datalength - BASEPACKETSIZE - sizeof(netbuffer->u.filetxpak);
char *filename;
static INT32 filetime = 0;
filename = va("%s", file->filename);
nameonly(filename);
@ -995,49 +1238,105 @@ void Got_Filetxpak(void)
if (file->status == FS_REQUESTED)
{
if (file->file)
I_Error("Got_Filetxpak: already open file\n");
file->file = fopen(filename, file->textmode ? "w" : "wb");
if (!file->file)
I_Error("Can't create file %s: %s", filename, strerror(errno));
CONS_Printf("\r%s...\n",filename);
file->currentsize = 0;
I_Error("PT_FileFragment: already open file\n");
file->status = FS_DOWNLOADING;
file->fragmentsize = fragmentsize;
file->iteration = 0;
file->ackpacket = calloc(1, sizeof(*file->ackpacket) + 512);
if (!file->ackpacket)
I_Error("FileSendTicker: No more memory\n");
if (CL_CanResumeDownload(file))
{
file->file = fopen(filename, "r+b");
if (!file->file)
I_Error("Can't reopen file %s: %s", filename, strerror(errno));
CONS_Printf("\r%s...\n", filename);
CONS_Printf("Resuming download...\n");
file->currentsize = pauseddownload->currentsize;
file->receivedfragments = pauseddownload->receivedfragments;
file->ackresendposition = 0;
free(pauseddownload);
pauseddownload = NULL;
}
else
{
CL_AbortDownloadResume();
file->file = fopen(filename, "wb");
if (!file->file)
I_Error("Can't create file %s: %s", filename, strerror(errno));
CONS_Printf("\r%s...\n",filename);
file->currentsize = 0;
file->totalsize = LONG(netbuffer->u.filetxpak.filesize);
file->ackresendposition = UINT32_MAX; // Only used for resumed downloads
file->receivedfragments = calloc(file->totalsize / fragmentsize + 1, sizeof(*file->receivedfragments));
if (!file->receivedfragments)
I_Error("FileSendTicker: No more memory\n");
}
lasttimeackpacketsent = I_GetTime();
}
if (file->status == FS_DOWNLOADING)
{
UINT32 pos = LONG(netbuffer->u.filetxpak.position);
UINT16 size = SHORT(netbuffer->u.filetxpak.size);
// Use a special trick to know when the file is complete (not always used)
// WARNING: file fragments can arrive out of order so don't stop yet!
if (pos & 0x80000000)
{
pos &= ~0x80000000;
file->totalsize = pos + size;
}
// We can receive packet in the wrong order, anyway all os support gaped file
fseek(file->file, pos, SEEK_SET);
if (size && fwrite(netbuffer->u.filetxpak.data,size,1,file->file) != 1)
I_Error("Can't write to %s: %s\n",filename, M_FileError(file->file));
file->currentsize += size;
if (fragmentpos >= file->totalsize)
I_Error("Invalid file fragment\n");
// Finished?
if (file->currentsize == file->totalsize)
file->iteration = max(file->iteration, netbuffer->u.filetxpak.iteration);
if (!file->receivedfragments[fragmentpos / fragmentsize]) // Not received yet
{
fclose(file->file);
file->file = NULL;
file->status = FS_FOUND;
CONS_Printf(M_GetText("Downloading %s...(done)\n"),
filename);
if (luafiletransfers)
file->receivedfragments[fragmentpos / fragmentsize] = true;
// We can receive packets in the wrong order, anyway all OSes support gaped files
fseek(file->file, fragmentpos, SEEK_SET);
if (fragmentsize && fwrite(netbuffer->u.filetxpak.data, boundedfragmentsize, 1, file->file) != 1)
I_Error("Can't write to %s: %s\n",filename, M_FileError(file->file));
file->currentsize += boundedfragmentsize;
AddFragmentToAckPacket(file->ackpacket, file->iteration, fragmentpos / fragmentsize, filenum);
// Finished?
if (file->currentsize == file->totalsize)
{
fclose(file->file);
file->file = NULL;
free(file->receivedfragments);
free(file->ackpacket);
file->status = FS_FOUND;
file->justdownloaded = true;
CONS_Printf(M_GetText("Downloading %s...(done)\n"),
filename);
// Tell the server we have received the file
netbuffer->packettype = PT_HASLUAFILE;
HSendPacket(servernode, true, 0, 0);
netbuffer->packettype = PT_FILERECEIVED;
netbuffer->u.filereceived = filenum;
HSendPacket(servernode, true, 0, 1);
if (luafiletransfers)
{
// Tell the server we have received the file
netbuffer->packettype = PT_HASLUAFILE;
HSendPacket(servernode, true, 0, 0);
}
}
}
else // Already received
{
// If they are sending us the fragment again, it's probably because
// they missed our previous ack, so we must re-acknowledge it
AddFragmentToAckPacket(file->ackpacket, file->iteration, fragmentpos / fragmentsize, filenum);
}
}
else
else if (!file->justdownloaded)
{
const char *s;
switch(file->status)
@ -1060,14 +1359,8 @@ void Got_Filetxpak(void)
}
I_Error("Received a file not requested (file id: %d, file status: %s)\n", filenum, s);
}
// Send ack back quickly
if (++filetime == 3)
{
Net_SendAcks(servernode);
filetime = 0;
}
#ifdef CLIENT_LOADINGSCREEN
#ifndef NONET
lastfilenum = filenum;
#endif
}
@ -1078,7 +1371,7 @@ void Got_Filetxpak(void)
* \return True if the node is downloading a file
*
*/
boolean SV_SendingFile(INT32 node)
boolean SendingFile(INT32 node)
{
return transfer[node].txlist != NULL;
}
@ -1107,12 +1400,62 @@ void CloseNetFile(void)
if (fileneeded[i].status == FS_DOWNLOADING && fileneeded[i].file)
{
fclose(fileneeded[i].file);
// File is not complete delete it
remove(fileneeded[i].filename);
}
free(fileneeded[i].ackpacket);
// Remove PT_FILEFRAGMENT from acknowledge list
Net_AbortPacketType(PT_FILEFRAGMENT);
if (!pauseddownload && i != 0) // 0 is either srb2.srb or the gamestate...
{
// Don't remove the file, save it for later in case we resume the download
pauseddownload = malloc(sizeof(*pauseddownload));
if (!pauseddownload)
I_Error("CloseNetFile: No more memory\n");
strcpy(pauseddownload->filename, fileneeded[i].filename);
memcpy(pauseddownload->md5sum, fileneeded[i].md5sum, 16);
pauseddownload->currentsize = fileneeded[i].currentsize;
pauseddownload->receivedfragments = fileneeded[i].receivedfragments;
pauseddownload->fragmentsize = fileneeded[i].fragmentsize;
}
else
{
free(fileneeded[i].receivedfragments);
// File is not complete delete it
remove(fileneeded[i].filename);
}
}
}
void Command_Downloads_f(void)
{
INT32 node;
for (node = 0; node < MAXNETNODES; node++)
if (transfer[node].txlist
&& transfer[node].txlist->ram == SF_FILE) // Node is downloading a file?
{
const char *name = transfer[node].txlist->id.filename;
UINT32 position = transfer[node].ackedsize;
UINT32 size = transfer[node].txlist->size;
char ratecolor;
// Avoid division by zero errors
if (!size)
size = 1;
name = &name[strlen(name) - nameonlylength(name)];
switch (4 * (position - 1) / size)
{
case 0: ratecolor = '\x85'; break;
case 1: ratecolor = '\x87'; break;
case 2: ratecolor = '\x82'; break;
case 3: ratecolor = '\x83'; break;
default: ratecolor = '\x80';
}
CONS_Printf("%2d %c%s ", node, ratecolor, name); // Node and file name
CONS_Printf("\x80%uK\x84/\x80%uK ", position / 1024, size / 1024); // Progress in kB
CONS_Printf("\x80(%c%u%%\x80) ", ratecolor, (UINT32)(100.0 * position / size)); // Progress in %
CONS_Printf("%s\n", I_GetNodeAddress(node)); // Address and newline
}
}
// Functions cut and pasted from Doomatic :)

View file

@ -14,6 +14,7 @@
#define __D_NETFIL__
#include "d_net.h"
#include "d_clisrv.h"
#include "w_wad.h"
typedef enum
@ -39,19 +40,25 @@ typedef struct
UINT8 willsend; // Is the server willing to send it?
char filename[MAX_WADPATH];
UINT8 md5sum[16];
filestatus_t status; // The value returned by recsearch
boolean justdownloaded; // To prevent late fragments from causing an I_Error
// Used only for download
FILE *file;
boolean *receivedfragments;
UINT32 fragmentsize;
UINT8 iteration;
fileack_pak *ackpacket;
UINT32 currentsize;
UINT32 totalsize;
filestatus_t status; // The value returned by recsearch
boolean textmode; // For files requested by Lua without the "b" option
UINT32 ackresendposition; // Used when resuming downloads
} fileneeded_t;
extern INT32 fileneedednum;
extern fileneeded_t fileneeded[MAX_WADFILES];
extern char downloaddir[512];
#ifdef CLIENT_LOADINGSCREEN
#ifndef NONET
extern INT32 lastfilenum;
#endif
@ -61,16 +68,20 @@ void CL_PrepareDownloadSaveGame(const char *tmpsave);
INT32 CL_CheckFiles(void);
void CL_LoadServerFiles(void);
void SV_SendRam(INT32 node, void *data, size_t size, freemethod_t freemethod,
void AddRamToSendQueue(INT32 node, void *data, size_t size, freemethod_t freemethod,
UINT8 fileid);
void SV_FileSendTicker(void);
void Got_Filetxpak(void);
boolean SV_SendingFile(INT32 node);
void FileSendTicker(void);
void PT_FileAck(void);
void PT_FileReceived(void);
boolean SendingFile(INT32 node);
void FileReceiveTicker(void);
void PT_FileFragment(void);
boolean CL_CheckDownloadable(void);
boolean CL_SendRequestFile(void);
boolean Got_RequestFilePak(INT32 node);
boolean CL_SendFileRequest(void);
boolean PT_RequestFile(INT32 node);
typedef enum
{
@ -86,18 +97,19 @@ typedef struct luafiletransfer_s
char *realfilename;
char mode[4]; // rb+/wb+/ab+ + null character
INT32 id; // Callback ID
boolean ongoing;
luafiletransfernodestatus_t nodestatus[MAXNETNODES];
struct luafiletransfer_s *next;
} luafiletransfer_t;
extern luafiletransfer_t *luafiletransfers;
extern boolean waitingforluafiletransfer;
extern boolean waitingforluafilecommand;
extern char luafiledir[256 + 16];
void AddLuaFileTransfer(const char *filename, const char *mode);
void SV_PrepareSendLuaFileToNextNode(void);
boolean SV_SendLuaFile(INT32 node, const char *filename, boolean textmode);
void SV_PrepareSendLuaFile(const char *filename);
void SV_PrepareSendLuaFile(void);
boolean AddLuaFileToSendQueue(INT32 node, const char *filename);
void SV_HandleLuaFileSent(UINT8 node);
void RemoveLuaFileTransfer(void);
void RemoveAllLuaFileTransfers(void);
@ -110,6 +122,9 @@ void MakePathDirs(char *path);
void SV_AbortSendFiles(INT32 node);
void CloseNetFile(void);
void CL_AbortDownloadResume(void);
void Command_Downloads_f(void);
boolean fileexist(char *filename, time_t ptime);

View file

@ -49,6 +49,8 @@ typedef enum
SF_MULTIABILITY = 1<<13, // Revenge of Final Demo.
SF_NONIGHTSROTATION = 1<<14, // Disable sprite rotation for NiGHTS
SF_NONIGHTSSUPER = 1<<15, // Disable super colors for NiGHTS (if you have SF_SUPER)
SF_NOSUPERSPRITES = 1<<16, // Don't use super sprites while super
SF_NOSUPERJUMPBOOST = 1<<17, // Disable the jump boost given while super (i.e. Knuckles)
// free up to and including 1<<31
} skinflags_t;
@ -239,7 +241,8 @@ typedef enum
CR_MACESPIN,
CR_MINECART,
CR_ROLLOUT,
CR_PTERABYTE
CR_PTERABYTE,
CR_DUSTDEVIL
} carrytype_t; // pw_carry
// Player powers. (don't edit this comment)
@ -284,6 +287,8 @@ typedef enum
pw_justlaunched, // Launched off a slope this tic (0=none, 1=standard launch, 2=half-pipe launch)
pw_ignorelatch, // Don't grab onto CR_GENERIC, add 32768 (powers[pw_ignorelatch] & 1<<15) to avoid ALL not-NiGHTS CR_ types
NUMPOWERS
} powertype_t;
@ -331,6 +336,9 @@ typedef struct player_s
angle_t viewrollangle;
INT16 angleturn;
INT16 oldrelangleturn;
// Mouse aiming, where the guy is looking at!
// It is updated with cmd->aiming.
angle_t aiming;
@ -365,7 +373,7 @@ typedef struct player_s
UINT16 flashpal;
// Player skin colorshift, 0-15 for which color to draw player.
UINT8 skincolor;
UINT16 skincolor;
INT32 skin;
UINT32 availabilities;

View file

@ -57,10 +57,12 @@ int vsnprintf(char *str, size_t n, const char *fmt, va_list ap);
// The crazy word-reading stuff uses these.
static char *FREE_STATES[NUMSTATEFREESLOTS];
static char *FREE_MOBJS[NUMMOBJFREESLOTS];
static char *FREE_SKINCOLORS[NUMCOLORFREESLOTS];
static UINT8 used_spr[(NUMSPRITEFREESLOTS / 8) + 1]; // Bitwise flag for sprite freeslot in use! I would use ceil() here if I could, but it only saves 1 byte of memory anyway.
#define initfreeslots() {\
memset(FREE_STATES,0,sizeof(char *) * NUMSTATEFREESLOTS);\
memset(FREE_MOBJS,0,sizeof(char *) * NUMMOBJFREESLOTS);\
memset(FREE_SKINCOLORS,0,sizeof(char *) * NUMCOLORFREESLOTS);\
memset(used_spr,0,sizeof(UINT8) * ((NUMSPRITEFREESLOTS / 8) + 1));\
}
@ -78,6 +80,7 @@ static hudnum_t get_huditem(const char *word);
static menutype_t get_menutype(const char *word);
//static INT16 get_gametype(const char *word);
//static powertype_t get_power(const char *word);
skincolornum_t get_skincolor(const char *word);
boolean deh_loaded = false;
static int dbg_line;
@ -393,7 +396,7 @@ static void readPlayer(MYFILE *f, INT32 num)
// It works down here, though.
{
INT32 numline = 0;
for (i = 0; i < MAXLINELEN-1; i++)
for (i = 0; (size_t)i < sizeof(description[num].notes)-1; i++)
{
if (numline < 20 && description[num].notes[i] == '\n')
numline++;
@ -450,7 +453,7 @@ static void readPlayer(MYFILE *f, INT32 num)
else if (fastcmp(word, "OPPOSITECOLOR") || fastcmp(word, "OPPOSITECOLOUR"))
{
SLOTFOUND
description[num].oppositecolor = (UINT8)get_number(word2);
description[num].oppositecolor = (UINT16)get_number(word2);
}
else if (fastcmp(word, "NAMETAG") || fastcmp(word, "TAGNAME"))
{
@ -460,12 +463,12 @@ static void readPlayer(MYFILE *f, INT32 num)
else if (fastcmp(word, "TAGTEXTCOLOR") || fastcmp(word, "TAGTEXTCOLOUR"))
{
SLOTFOUND
description[num].tagtextcolor = (UINT8)get_number(word2);
description[num].tagtextcolor = (UINT16)get_number(word2);
}
else if (fastcmp(word, "TAGOUTLINECOLOR") || fastcmp(word, "TAGOUTLINECOLOUR"))
{
SLOTFOUND
description[num].tagoutlinecolor = (UINT8)get_number(word2);
description[num].tagoutlinecolor = (UINT16)get_number(word2);
}
else if (fastcmp(word, "STATUS"))
{
@ -572,6 +575,16 @@ static void readfreeslots(MYFILE *f)
break;
}
}
else if (fastcmp(type, "SKINCOLOR"))
{
for (i = 0; i < NUMCOLORFREESLOTS; i++)
if (!FREE_SKINCOLORS[i]) {
FREE_SKINCOLORS[i] = Z_Malloc(strlen(word)+1, PU_STATIC, NULL);
strcpy(FREE_SKINCOLORS[i],word);
M_AddMenuColor(numskincolors++);
break;
}
}
else if (fastcmp(type, "SPR2"))
{
// Search if we already have an SPR2 by that name...
@ -754,6 +767,84 @@ static void readthing(MYFILE *f, INT32 num)
Z_Free(s);
}
static void readskincolor(MYFILE *f, INT32 num)
{
char *s = Z_Malloc(MAXLINELEN, PU_STATIC, NULL);
char *word = s;
char *word2;
char *tmp;
Color_cons_t[num].value = num;
do
{
if (myfgets(s, MAXLINELEN, f))
{
if (s[0] == '\n')
break;
// First remove trailing newline, if there is one
tmp = strchr(s, '\n');
if (tmp)
*tmp = '\0';
tmp = strchr(s, '#');
if (tmp)
*tmp = '\0';
if (s == tmp)
continue; // Skip comment lines, but don't break.
// Get the part before the " = "
tmp = strchr(s, '=');
if (tmp)
*(tmp-1) = '\0';
else
break;
strupr(word);
// Now get the part after
word2 = tmp += 2;
if (fastcmp(word, "NAME"))
{
deh_strlcpy(skincolors[num].name, word2,
sizeof (skincolors[num].name), va("Skincolor %d: name", num));
}
else if (fastcmp(word, "RAMP"))
{
UINT8 i;
tmp = strtok(word2,",");
for (i = 0; i < COLORRAMPSIZE; i++) {
skincolors[num].ramp[i] = (UINT8)get_number(tmp);
if ((tmp = strtok(NULL,",")) == NULL)
break;
}
}
else if (fastcmp(word, "INVCOLOR"))
{
skincolors[num].invcolor = (UINT16)get_number(word2);
}
else if (fastcmp(word, "INVSHADE"))
{
skincolors[num].invshade = get_number(word2)%COLORRAMPSIZE;
}
else if (fastcmp(word, "CHATCOLOR"))
{
skincolors[num].chatcolor = get_number(word2);
}
else if (fastcmp(word, "ACCESSIBLE"))
{
if (num > FIRSTSUPERCOLOR)
skincolors[num].accessible = (boolean)(atoi(word2) || word2[0] == 'T' || word2[0] == 'Y');
}
else
deh_warning("Skincolor %d: unknown word '%s'", num, word);
}
} while (!myfeof(f)); // finish when the line is empty
Z_Free(s);
}
#ifdef HWRENDER
static void readlight(MYFILE *f, INT32 num)
{
@ -1175,7 +1266,7 @@ static void readgametype(MYFILE *f, char *gtname)
// It works down here, though.
{
INT32 numline = 0;
for (i = 0; i < MAXLINELEN-1; i++)
for (i = 0; (size_t)i < sizeof(gtdescription)-1; i++)
{
if (numline < 20 && gtdescription[i] == '\n')
numline++;
@ -1579,6 +1670,22 @@ static void readlevelheader(MYFILE *f, INT32 num)
mapheaderinfo[num-1]->nextlevel = (INT16)i;
}
else if (fastcmp(word, "MARATHONNEXT"))
{
if (fastcmp(word2, "TITLE")) i = 1100;
else if (fastcmp(word2, "EVALUATION")) i = 1101;
else if (fastcmp(word2, "CREDITS")) i = 1102;
else if (fastcmp(word2, "ENDING")) i = 1103;
else
// Support using the actual map name,
// i.e., MarathonNext = AB, MarathonNext = FZ, etc.
// Convert to map number
if (word2[0] >= 'A' && word2[0] <= 'Z' && word2[2] == '\0')
i = M_MapNumber(word2[0], word2[1]);
mapheaderinfo[num-1]->marathonnext = (INT16)i;
}
else if (fastcmp(word, "TYPEOFLEVEL"))
{
if (i) // it's just a number
@ -2814,7 +2921,7 @@ static actionpointer_t actionpointers[] =
{{A_ThrownRing}, "A_THROWNRING"},
{{A_SetSolidSteam}, "A_SETSOLIDSTEAM"},
{{A_UnsetSolidSteam}, "A_UNSETSOLIDSTEAM"},
{{A_SignSpin}, "S_SIGNSPIN"},
{{A_SignSpin}, "A_SIGNSPIN"},
{{A_SignPlayer}, "A_SIGNPLAYER"},
{{A_OverlayThink}, "A_OVERLAYTHINK"},
{{A_JetChase}, "A_JETCHASE"},
@ -3921,7 +4028,20 @@ static void readmaincfg(MYFILE *f)
else
value = get_number(word2);
spstage_start = (INT16)value;
spstage_start = spmarathon_start = (INT16)value;
}
else if (fastcmp(word, "SPMARATHON_START"))
{
// Support using the actual map name,
// i.e., Level AB, Level FZ, etc.
// Convert to map number
if (word2[0] >= 'A' && word2[0] <= 'Z')
value = M_MapNumber(word2[0], word2[1]);
else
value = get_number(word2);
spmarathon_start = (INT16)value;
}
else if (fastcmp(word, "SSTAGE_START"))
{
@ -3953,19 +4073,19 @@ static void readmaincfg(MYFILE *f)
}
else if (fastcmp(word, "REDTEAM"))
{
skincolor_redteam = (UINT8)get_number(word2);
skincolor_redteam = (UINT16)get_number(word2);
}
else if (fastcmp(word, "BLUETEAM"))
{
skincolor_blueteam = (UINT8)get_number(word2);
skincolor_blueteam = (UINT16)get_number(word2);
}
else if (fastcmp(word, "REDRING"))
{
skincolor_redring = (UINT8)get_number(word2);
skincolor_redring = (UINT16)get_number(word2);
}
else if (fastcmp(word, "BLUERING"))
{
skincolor_bluering = (UINT8)get_number(word2);
skincolor_bluering = (UINT16)get_number(word2);
}
else if (fastcmp(word, "INVULNTICS"))
{
@ -4015,6 +4135,17 @@ static void readmaincfg(MYFILE *f)
introtoplay = 128;
introchanged = true;
}
else if (fastcmp(word, "CREDITSCUTSCENE"))
{
creditscutscene = (UINT8)get_number(word2);
// range check, you morons.
if (creditscutscene > 128)
creditscutscene = 128;
}
else if (fastcmp(word, "USEBLACKROCK"))
{
useBlackRock = (UINT8)(value || word2[0] == 'T' || word2[0] == 'Y');
}
else if (fastcmp(word, "LOOPTITLE"))
{
looptitle = (value || word2[0] == 'T' || word2[0] == 'Y');
@ -4116,13 +4247,6 @@ static void readmaincfg(MYFILE *f)
titlescrollyspeed = get_number(word2);
titlechanged = true;
}
else if (fastcmp(word, "CREDITSCUTSCENE"))
{
creditscutscene = (UINT8)get_number(word2);
// range check, you morons.
if (creditscutscene > 128)
creditscutscene = 128;
}
else if (fastcmp(word, "DISABLESPEEDADJUST"))
{
disableSpeedAdjust = (value || word2[0] == 'T' || word2[0] == 'Y');
@ -4179,6 +4303,9 @@ static void readmaincfg(MYFILE *f)
// can't use sprintf since there is %u in savegamename
strcatbf(savegamename, srb2home, PATHSEP);
strcpy(liveeventbackup, va("live%s.bkp", timeattackfolder));
strcatbf(liveeventbackup, srb2home, PATHSEP);
gamedataadded = true;
titlechanged = true;
}
@ -4557,6 +4684,18 @@ static void DEH_LoadDehackedFile(MYFILE *f, boolean mainfile)
ignorelines(f);
}
}
else if (fastcmp(word, "SKINCOLOR") || fastcmp(word, "COLOR"))
{
if (i == 0 && word2[0] != '0') // If word2 isn't a number
i = get_skincolor(word2); // find a skincolor by name
if (i < numskincolors && i >= (INT32)SKINCOLOR_FIRSTFREESLOT)
readskincolor(f, i);
else
{
deh_warning("Skincolor %d out of range (%d - %d)", i, SKINCOLOR_FIRSTFREESLOT, numskincolors-1);
ignorelines(f);
}
}
else if (fastcmp(word, "SPRITE2"))
{
if (i == 0 && word2[0] != '0') // If word2 isn't a number
@ -5196,6 +5335,7 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit
"S_JETJAW_CHOMP14",
"S_JETJAW_CHOMP15",
"S_JETJAW_CHOMP16",
"S_JETJAW_SOUND",
// Snailer
"S_SNAILER1",
@ -5618,10 +5758,12 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit
"S_FANG_PINCHPATHINGSTART1",
"S_FANG_PINCHPATHINGSTART2",
"S_FANG_PINCHPATHING",
"S_FANG_PINCHBOUNCE0",
"S_FANG_PINCHBOUNCE1",
"S_FANG_PINCHBOUNCE2",
"S_FANG_PINCHBOUNCE3",
"S_FANG_PINCHBOUNCE4",
"S_FANG_PINCHFALL0",
"S_FANG_PINCHFALL1",
"S_FANG_PINCHFALL2",
"S_FANG_PINCHSKID1",
@ -8008,6 +8150,9 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit
"S_GFZDEBRIS",
"S_BRICKDEBRIS",
"S_WOODDEBRIS",
"S_REDBRICKDEBRIS",
"S_BLUEBRICKDEBRIS",
"S_YELLOWBRICKDEBRIS",
#ifdef SEENAMES
"S_NAMECHECK",
@ -8788,6 +8933,9 @@ static const char *const MOBJTYPE_LIST[] = { // array length left dynamic for s
"MT_GFZDEBRIS",
"MT_BRICKDEBRIS",
"MT_WOODDEBRIS",
"MT_REDBRICKDEBRIS",
"MT_BLUEBRICKDEBRIS",
"MT_YELLOWBRICKDEBRIS",
#ifdef SEENAMES
"MT_NAMECHECK",
@ -9001,8 +9149,6 @@ static const char *const ML_LIST[16] = {
"TFERLINE"
};
// This DOES differ from r_draw's Color_Names, unfortunately.
// Also includes Super colors
static const char *COLOR_ENUMS[] = {
"NONE", // SKINCOLOR_NONE,
@ -9176,7 +9322,9 @@ static const char *const POWERS_LIST[] = {
//for dyes
"DYE",
"JUSTLAUNCHED"
"JUSTLAUNCHED",
"IGNORELATCH"
};
static const char *const HUDITEMS_LIST[] = {
@ -9264,8 +9412,6 @@ static const char *const MENUTYPES_LIST[] = {
"OP_COLOR",
"OP_OPENGL",
"OP_OPENGL_LIGHTING",
"OP_OPENGL_FOG",
"OP_OPENGL_COLOR",
"OP_SOUND",
@ -9370,6 +9516,7 @@ struct {
{"FF_GLOBALANIM",FF_GLOBALANIM},
{"FF_FULLBRIGHT",FF_FULLBRIGHT},
{"FF_VERTICALFLIP",FF_VERTICALFLIP},
{"FF_HORIZONTALFLIP",FF_HORIZONTALFLIP},
{"FF_PAPERSPRITE",FF_PAPERSPRITE},
{"FF_TRANSMASK",FF_TRANSMASK},
{"FF_TRANSSHIFT",FF_TRANSSHIFT},
@ -9438,7 +9585,8 @@ struct {
// SKINCOLOR_ doesn't include these..!
{"MAXSKINCOLORS",MAXSKINCOLORS},
{"MAXTRANSLATIONS",MAXTRANSLATIONS},
{"FIRSTSUPERCOLOR",FIRSTSUPERCOLOR},
{"NUMSUPERCOLORS",NUMSUPERCOLORS},
// Precipitation
{"PRECIP_NONE",PRECIP_NONE},
@ -9489,6 +9637,7 @@ struct {
{"CR_MINECART",CR_MINECART},
{"CR_ROLLOUT",CR_ROLLOUT},
{"CR_PTERABYTE",CR_PTERABYTE},
{"CR_DUSTDEVIL",CR_DUSTDEVIL},
// Ring weapons (ringweapons_t)
// Useful for A_GiveWeapon
@ -9517,6 +9666,8 @@ struct {
{"SF_MULTIABILITY",SF_MULTIABILITY},
{"SF_NONIGHTSROTATION",SF_NONIGHTSROTATION},
{"SF_NONIGHTSSUPER",SF_NONIGHTSSUPER},
{"SF_NOSUPERSPRITES",SF_NOSUPERSPRITES},
{"SF_NOSUPERJUMPBOOST",SF_NOSUPERJUMPBOOST},
// Dashmode constants
{"DASHMODE_THRESHOLD",DASHMODE_THRESHOLD},
@ -9896,6 +10047,12 @@ struct {
{"TC_BLINK",TC_BLINK},
{"TC_DASHMODE",TC_DASHMODE},
// marathonmode flags
{"MA_INIT",MA_INIT},
{"MA_RUNNING",MA_RUNNING},
{"MA_NOCUTSCENES",MA_NOCUTSCENES},
{"MA_INGAME",MA_INGAME},
{NULL,0}
};
@ -9939,6 +10096,26 @@ static statenum_t get_state(const char *word)
return S_NULL;
}
skincolornum_t get_skincolor(const char *word)
{ // Returns the value of SKINCOLOR_ enumerations
skincolornum_t i;
if (*word >= '0' && *word <= '9')
return atoi(word);
if (fastncmp("SKINCOLOR_",word,10))
word += 10; // take off the SKINCOLOR_
for (i = 0; i < NUMCOLORFREESLOTS; i++) {
if (!FREE_SKINCOLORS[i])
break;
if (fastcmp(word, FREE_SKINCOLORS[i]))
return SKINCOLOR_FIRSTFREESLOT+i;
}
for (i = 0; i < SKINCOLOR_FIRSTFREESLOT; i++)
if (fastcmp(word, COLOR_ENUMS[i]))
return i;
deh_warning("Couldn't find skincolor named 'SKINCOLOR_%s'",word);
return SKINCOLOR_GREEN;
}
static spritenum_t get_sprite(const char *word)
{ // Returns the value of SPR_ enumerations
spritenum_t i;
@ -10233,6 +10410,11 @@ static fixed_t find_const(const char **rword)
free(word);
return r;
}
else if (fastncmp("SKINCOLOR_",word,10)) {
r = get_skincolor(word);
free(word);
return r;
}
else if (fastncmp("MT_",word,3)) {
r = get_mobjtype(word);
free(word);
@ -10301,17 +10483,6 @@ static fixed_t find_const(const char **rword)
free(word);
return r;
}
else if (fastncmp("SKINCOLOR_",word,10)) {
char *p = word+10;
for (i = 0; i < MAXTRANSLATIONS; i++)
if (fastcmp(p, COLOR_ENUMS[i])) {
free(word);
return i;
}
const_warning("color",word);
free(word);
return 0;
}
else if (fastncmp("GRADE_",word,6))
{
char *p = word+6;
@ -10372,8 +10543,8 @@ void DEH_Check(void)
if (dehpowers != NUMPOWERS)
I_Error("You forgot to update the Dehacked powers list, you dolt!\n(%d powers defined, versus %s in the Dehacked list)\n", NUMPOWERS, sizeu1(dehpowers));
if (dehcolors != MAXTRANSLATIONS)
I_Error("You forgot to update the Dehacked colors list, you dolt!\n(%d colors defined, versus %s in the Dehacked list)\n", MAXTRANSLATIONS, sizeu1(dehcolors));
if (dehcolors != SKINCOLOR_FIRSTFREESLOT)
I_Error("You forgot to update the Dehacked colors list, you dolt!\n(%d colors defined, versus %s in the Dehacked list)\n", SKINCOLOR_FIRSTFREESLOT, sizeu1(dehcolors));
#endif
}
@ -10481,6 +10652,22 @@ static inline int lib_freeslot(lua_State *L)
if (i == NUMMOBJFREESLOTS)
CONS_Alert(CONS_WARNING, "Ran out of free MobjType slots!\n");
}
else if (fastcmp(type, "SKINCOLOR"))
{
skincolornum_t i;
for (i = 0; i < NUMCOLORFREESLOTS; i++)
if (!FREE_SKINCOLORS[i]) {
CONS_Printf("Skincolor SKINCOLOR_%s allocated.\n",word);
FREE_SKINCOLORS[i] = Z_Malloc(strlen(word)+1, PU_STATIC, NULL);
strcpy(FREE_SKINCOLORS[i],word);
M_AddMenuColor(numskincolors++);
lua_pushinteger(L, i);
r++;
break;
}
if (i == NUMCOLORFREESLOTS)
CONS_Alert(CONS_WARNING, "Ran out of free skincolor slots!\n");
}
else if (fastcmp(type, "SPR2"))
{
// Search if we already have an SPR2 by that name...
@ -10812,13 +10999,20 @@ static inline int lib_getenum(lua_State *L)
}
else if (fastncmp("SKINCOLOR_",word,10)) {
p = word+10;
for (i = 0; i < MAXTRANSLATIONS; i++)
for (i = 0; i < NUMCOLORFREESLOTS; i++) {
if (!FREE_SKINCOLORS[i])
break;
if (fastcmp(p, FREE_SKINCOLORS[i])) {
lua_pushinteger(L, SKINCOLOR_FIRSTFREESLOT+i);
return 1;
}
}
for (i = 0; i < SKINCOLOR_FIRSTFREESLOT; i++)
if (fastcmp(p, COLOR_ENUMS[i])) {
lua_pushinteger(L, i);
return 1;
}
if (mathlib) return luaL_error(L, "skincolor '%s' could not be found.\n", word);
return 0;
return luaL_error(L, "skincolor '%s' could not be found.\n", word);
}
else if (fastncmp("GRADE_",word,6))
{

View file

@ -61,6 +61,8 @@
#include "../console.h"
#include "../m_menu.h"
#ifdef __GNUG__
#pragma implementation "../i_system.h"
#endif
@ -555,6 +557,7 @@ void I_Error (const char *error, ...)
if (demorecording)
G_CheckDemoStatus();
D_QuitNetGame ();
M_FreePlayerSetupColors();
if (shutdowning)
{
@ -622,6 +625,7 @@ void I_Quit (void)
if (demorecording)
G_CheckDemoStatus();
D_QuitNetGame ();
M_FreePlayerSetupColors();
I_ShutdownMusic();
I_ShutdownSound();
I_ShutdownCD();

View file

@ -90,6 +90,9 @@ static unsigned long nombre = NEWTICRATE*10;
static void I_BlitScreenVesa1(void); //see later
void I_FinishUpdate (void)
{
if (marathonmode)
SCR_DisplayMarathonInfo();
// draw captions if enabled
if (cv_closedcaptioning.value)
SCR_ClosedCaptions();

View file

@ -208,10 +208,6 @@ typedef struct
#define ZSHIFT 4
extern const UINT8 Color_Index[MAXTRANSLATIONS-1][16];
extern const char *Color_Names[MAXSKINCOLORS + NUMSUPERCOLORS];
extern const UINT8 Color_Opposite[MAXSKINCOLORS - 1][2];
#define NUMMAPS 1035
#endif // __DOOMDATA__

View file

@ -239,6 +239,20 @@ extern char logfilename[1024];
#define PLAYERSMASK (MAXPLAYERS-1)
#define MAXPLAYERNAME 21
#define COLORRAMPSIZE 16
#define MAXCOLORNAME 32
#define NUMCOLORFREESLOTS 1024
typedef struct skincolor_s
{
char name[MAXCOLORNAME+1]; // Skincolor name
UINT8 ramp[COLORRAMPSIZE]; // Colormap ramp
UINT16 invcolor; // Signpost color
UINT8 invshade; // Signpost color shade
UINT16 chatcolor; // Chat color
boolean accessible; // Accessible by the color command + setup menu
} skincolor_t;
typedef enum
{
SKINCOLOR_NONE = 0,
@ -317,12 +331,10 @@ typedef enum
SKINCOLOR_RASPBERRY,
SKINCOLOR_ROSY,
// SKINCOLOR_? - one left before we bump up against 0x39, which isn't a HARD limit anymore but would be excessive
MAXSKINCOLORS,
FIRSTSUPERCOLOR,
// Super special awesome Super flashing colors!
SKINCOLOR_SUPERSILVER1 = MAXSKINCOLORS,
SKINCOLOR_SUPERSILVER1 = FIRSTSUPERCOLOR,
SKINCOLOR_SUPERSILVER2,
SKINCOLOR_SUPERSILVER3,
SKINCOLOR_SUPERSILVER4,
@ -376,9 +388,17 @@ typedef enum
SKINCOLOR_SUPERTAN4,
SKINCOLOR_SUPERTAN5,
MAXTRANSLATIONS,
NUMSUPERCOLORS = ((MAXTRANSLATIONS - MAXSKINCOLORS)/5)
} skincolors_t;
SKINCOLOR_FIRSTFREESLOT,
SKINCOLOR_LASTFREESLOT = SKINCOLOR_FIRSTFREESLOT + NUMCOLORFREESLOTS - 1,
MAXSKINCOLORS,
NUMSUPERCOLORS = ((SKINCOLOR_FIRSTFREESLOT - FIRSTSUPERCOLOR)/5)
} skincolornum_t;
extern UINT16 numskincolors;
extern skincolor_t skincolors[MAXSKINCOLORS];
// State updates, number of tics / second.
// NOTE: used to setup the timer rate, see I_StartupTimer().
@ -459,6 +479,7 @@ void CONS_Debug(INT32 debugflags, const char *fmt, ...) FUNCDEBUG;
// Things that used to be in dstrings.h
#define SAVEGAMENAME "srb2sav"
extern char savegamename[256];
extern char liveeventbackup[256];
// m_misc.h
#ifdef GETTEXT
@ -565,10 +586,6 @@ extern const char *compdate, *comptime, *comprevision, *compbranch;
// None of these that are disabled in the normal build are guaranteed to work perfectly
// Compile them at your own risk!
/// Backwards compatibility with SRB2CB's slope linedef types.
/// \note A simple shim that prints a warning.
#define ESLOPE_TYPESHIM
/// Allows the use of devmode in multiplayer. AKA "fishcake"
//#define NETGAME_DEVMODE
@ -600,11 +617,6 @@ extern const char *compdate, *comptime, *comprevision, *compbranch;
/// memory that never gets touched.
#define ALLOW_RESETDATA
#ifndef NONET
/// Display a connection screen on join attempts.
#define CLIENT_LOADINGSCREEN
#endif
/// Experimental tweaks to analog mode. (Needs a lot of work before it's ready for primetime.)
//#define REDSANALOG
@ -622,17 +634,13 @@ extern const char *compdate, *comptime, *comprevision, *compbranch;
/// SRB2CB itself ported this from PrBoom+
#define NEWCLIP
/// OpenGL shaders
#define GL_SHADERS
/// Handle touching sector specials in P_PlayerAfterThink instead of P_PlayerThink.
/// \note Required for proper collision with moving sloped surfaces that have sector specials on them.
#define SECTORSPECIALSAFTERTHINK
/// FINALLY some real clipping that doesn't make walls dissappear AND speeds the game up
/// (that was the original comment from SRB2CB, sadly it is a lie and actually slows game down)
/// on the bright side it fixes some weird issues with translucent walls
/// \note SRB2CB port.
/// SRB2CB itself ported this from PrBoom+
#define NEWCLIP
/// Cache patches in Lua in a way that renderer switching will work flawlessly.
//#define LUA_PATCH_SAFETY

View file

@ -45,7 +45,19 @@ extern INT32 curWeather;
extern INT32 cursaveslot;
//extern INT16 lastmapsaved;
extern INT16 lastmaploaded;
extern boolean gamecomplete;
extern UINT8 gamecomplete;
// Extra abilities/settings for skins (combinable stuff)
typedef enum
{
MA_RUNNING = 1, // In action
MA_INIT = 1<<1, // Initialisation
MA_NOCUTSCENES = 1<<2, // No cutscenes
MA_INGAME = 1<<3 // Timer ignores loads
} marathonmode_t;
extern marathonmode_t marathonmode;
extern tic_t marathontime;
#define maxgameovers 13
extern UINT8 numgameovers;
@ -127,7 +139,7 @@ extern INT32 displayplayer;
extern INT32 secondarydisplayplayer; // for splitscreen
// Maps of special importance
extern INT16 spstage_start;
extern INT16 spstage_start, spmarathon_start;
extern INT16 sstage_start, sstage_end, smpstage_start, smpstage_end;
extern INT16 titlemap;
@ -145,7 +157,7 @@ extern INT32 tutorialanalog; // store cv_analog[0] user value
extern boolean looptitle;
// CTF colors.
extern UINT8 skincolor_redteam, skincolor_blueteam, skincolor_redring, skincolor_bluering;
extern UINT16 skincolor_redteam, skincolor_blueteam, skincolor_redring, skincolor_bluering;
extern tic_t countdowntimer;
extern boolean countdowntimeup;
@ -289,6 +301,7 @@ typedef struct
UINT8 actnum; ///< Act number or 0 for none.
UINT32 typeoflevel; ///< Combination of typeoflevel flags.
INT16 nextlevel; ///< Map number of next level, or 1100-1102 to end.
INT16 marathonnext; ///< See nextlevel, but for Marathon mode. Necessary to support hub worlds ala SUGOI.
char keywords[33]; ///< Keywords separated by space to search for. 32 characters.
char musname[7]; ///< Music track to play. "" for no music.
UINT16 mustrack; ///< Subsong to play. Only really relevant for music modules and specific formats supported by GME. 0 to ignore.
@ -575,11 +588,12 @@ extern UINT16 nightslinktics;
extern UINT8 introtoplay;
extern UINT8 creditscutscene;
extern UINT8 useBlackRock;
extern UINT8 use1upSound;
extern UINT8 maxXtraLife; // Max extra lives from rings
extern UINT8 useContinues;
#define continuesInSession (!multiplayer && (useContinues || ultimatemode || !(cursaveslot > 0)))
#define continuesInSession (!multiplayer && (ultimatemode || (useContinues && !marathonmode) || (!modeattacking && !(cursaveslot > 0))))
extern mobj_t *hunt1, *hunt2, *hunt3; // Emerald hunt locations
@ -620,6 +634,19 @@ extern mapthing_t *playerstarts[MAXPLAYERS]; // Cooperative
extern mapthing_t *bluectfstarts[MAXPLAYERS]; // CTF
extern mapthing_t *redctfstarts[MAXPLAYERS]; // CTF
#define WAYPOINTSEQUENCESIZE 256
#define NUMWAYPOINTSEQUENCES 256
extern mobj_t *waypoints[NUMWAYPOINTSEQUENCES][WAYPOINTSEQUENCESIZE];
extern UINT16 numwaypoints[NUMWAYPOINTSEQUENCES];
void P_AddWaypoint(UINT8 sequence, UINT8 id, mobj_t *waypoint);
mobj_t *P_GetFirstWaypoint(UINT8 sequence);
mobj_t *P_GetLastWaypoint(UINT8 sequence);
mobj_t *P_GetPreviousWaypoint(mobj_t *current, boolean wrap);
mobj_t *P_GetNextWaypoint(mobj_t *current, boolean wrap);
mobj_t *P_GetClosestWaypoint(UINT8 sequence, mobj_t *mo);
boolean P_IsDegeneratedWaypointSequence(UINT8 sequence);
// =====================================
// Internal parameters, used for engine.
// =====================================

View file

@ -16,6 +16,11 @@ tic_t I_GetTime(void)
return 0;
}
int I_GetTimeMicros(void)
{
return 0;
}
void I_Sleep(void){}
void I_GetEvent(void){}

View file

@ -1118,9 +1118,6 @@ static const char *credits[] = {
"\1Sonic Robo Blast II",
"\1Credits",
"",
"\1Producer",
"Rob Tisdell",
"",
"\1Game Design",
"Ben \"Mystic\" Geyer",
"\"SSNTails\"",
@ -1151,11 +1148,13 @@ static const char *credits[] = {
"",
"\1Programming",
"\1Assistance",
"Colette \"fickleheart\" Bordelon",
"\"chi.miru\"", // helped port slope drawing code from ZDoom
"Andrew \"orospakr\" Clunis",
"Sally \"TehRealSalt\" Cochenour",
"Gregor \"Oogaland\" Dick",
"Julio \"Chaos Zero 64\" Guir",
"\"Hannu_Hanhi\"", // For many OpenGL performance improvements!
"\"Kalaron\"", // Coded some of Sryder13's collection of OpenGL fixes, especially fog
"\"Lat'\"", // SRB2-CHAT, the chat window from Kart
"Matthew \"Shuffle\" Marsalko",
@ -1166,6 +1165,7 @@ static const char *credits[] = {
"Tasos \"tatokis\" Sahanidis", // Corrected C FixedMul, making 64-bit builds netplay compatible
"Wessel \"sphere\" Smit",
"Ben \"Cue\" Woodford",
"\"VelocitOni\"", // Wrote the original dashmode script
"Ikaro \"Tatsuru\" Vinhas",
// Git contributors with 5+ approved merges of substantive quality,
// or contributors with at least one groundbreaking merge, may be named.
@ -1174,6 +1174,7 @@ static const char *credits[] = {
"\1Art",
"Victor \"VAdaPEGA\" Ara\x1Fjo", // Araújo -- sorry for our limited font! D:
"Ryan \"Blaze Hedgehog\" Bloom",
"\"ChrispyPixels\"",
"Paul \"Boinciel\" Clempson",
"Sally \"TehRealSalt\" Cochenour",
"\"Dave Lite\"",
@ -1220,6 +1221,7 @@ static const char *credits[] = {
"\"SSNTails\"",
"",
"\1Level Design",
"Colette \"fickleheart\" Bordelon",
"Hank \"FuriousFox\" Brannock",
"Matthew \"Fawfulfan\" Chapman",
"Paul \"Boinciel\" Clempson",
@ -1255,6 +1257,7 @@ static const char *credits[] = {
"Johnny \"Sonikku\" Wallbank",
"",
"\1Testing",
"Discord Community Testers",
"Hank \"FuriousFox\" Brannock",
"Cody \"SRB2 Playah\" Koester",
"Skye \"OmegaVelocity\" Meredith",
@ -1330,10 +1333,6 @@ void F_StartCredits(void)
// Just in case they're open ... somehow
M_ClearMenus(true);
// Save the second we enter the credits
if ((!modifiedgame || savemoddata) && !(netgame || multiplayer) && cursaveslot > 0)
G_SaveGame((UINT32)cursaveslot);
if (creditscutscene)
{
F_StartCustomCutscene(creditscutscene - 1, false, false);
@ -1529,12 +1528,6 @@ void F_StartGameEvaluation(void)
// Just in case they're open ... somehow
M_ClearMenus(true);
// Save the second we enter the evaluation
// We need to do this again! Remember, it's possible a mod designed skipped
// the credits sequence!
if ((!modifiedgame || savemoddata) && !(netgame || multiplayer) && cursaveslot > 0)
G_SaveGame((UINT32)cursaveslot);
goodending = (ALL7EMERALDS(emeralds));
gameaction = ga_nothing;
@ -1551,13 +1544,20 @@ void F_GameEvaluationDrawer(void)
angle_t fa;
INT32 eemeralds_cur;
char patchname[7] = "CEMGx0";
const char* endingtext = (goodending ? "CONGRATULATIONS!" : "TRY AGAIN...");
const char* endingtext;
if (marathonmode)
endingtext = "THANKS FOR THE RUN!";
else if (goodending)
endingtext = "CONGRATULATIONS!";
else
endingtext = "TRY AGAIN...";
V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, 31);
// Draw all the good crap here.
if (finalecount > 0)
if (finalecount > 0 && useBlackRock)
{
INT32 scale = FRACUNIT;
patch_t *rockpat;
@ -1684,7 +1684,9 @@ void F_GameEvaluationTicker(void)
return;
}
if (!goodending)
if (!useBlackRock)
;
else if (!goodending)
{
if (sparklloop)
sparklloop--;
@ -1841,10 +1843,6 @@ void F_StartEnding(void)
// Just in case they're open ... somehow
M_ClearMenus(true);
// Save before the credits sequence.
if ((!modifiedgame || savemoddata) && !(netgame || multiplayer) && cursaveslot > 0)
G_SaveGame((UINT32)cursaveslot);
gameaction = ga_nothing;
paused = false;
CON_ToggleOff();
@ -2185,7 +2183,7 @@ void F_EndingDrawer(void)
for (i = 0; i < 7; ++i)
{
UINT8* colormap;
skincolors_t col = SKINCOLOR_GREEN;
skincolornum_t col = SKINCOLOR_GREEN;
switch (i)
{
case 1:
@ -3959,6 +3957,7 @@ static void F_AdvanceToNextScene(void)
animtimer = pictime = cutscenes[cutnum]->scene[scenenum].picduration[picnum];
}
// See also G_AfterIntermission, the only other place which handles intra-map/ending transitions
void F_EndCutScene(void)
{
cutsceneover = true; // do this first, just in case G_EndGame or something wants to turn it back false later

View file

@ -162,7 +162,9 @@ extern wipestyleflags_t wipestyleflags;
// Even my function names are borderline
boolean F_ShouldColormapFade(void);
boolean F_TryColormapFade(UINT8 wipecolor);
#ifndef NOWIPE
void F_DecideWipeStyle(void);
#endif
#define FADECOLORMAPDIV 8
#define FADECOLORMAPROWS (256/FADECOLORMAPDIV)

View file

@ -464,6 +464,7 @@ void F_WipeEndScreen(void)
*/
boolean F_ShouldColormapFade(void)
{
#ifndef NOWIPE
if ((wipestyleflags & (WSF_FADEIN|WSF_FADEOUT)) // only if one of those wipestyleflags are actually set
&& !(wipestyleflags & WSF_CROSSFADE)) // and if not crossfading
{
@ -479,11 +480,13 @@ boolean F_ShouldColormapFade(void)
// Menus
|| gamestate == GS_TIMEATTACK);
}
#endif
return false;
}
/** Decides what wipe style to use.
*/
#ifndef NOWIPE
void F_DecideWipeStyle(void)
{
// Set default wipe style
@ -493,6 +496,7 @@ void F_DecideWipeStyle(void)
if (F_ShouldColormapFade())
wipestyle = WIPESTYLE_COLORMAP;
}
#endif
/** Attempt to run a colormap fade,
provided all the conditionals were properly met.
@ -501,6 +505,7 @@ void F_DecideWipeStyle(void)
*/
boolean F_TryColormapFade(UINT8 wipecolor)
{
#ifndef NOWIPE
if (F_ShouldColormapFade())
{
#ifdef HWRENDER
@ -510,6 +515,7 @@ boolean F_TryColormapFade(UINT8 wipecolor)
return true;
}
else
#endif
{
F_WipeColorFill(wipecolor);
return false;
@ -608,6 +614,7 @@ void F_RunWipe(UINT8 wipetype, boolean drawMenu)
tic_t F_GetWipeLength(UINT8 wipetype)
{
#ifdef NOWIPE
(void)wipetype;
return 0;
#else
static char lumpname[10] = "FADEmmss";
@ -634,6 +641,7 @@ tic_t F_GetWipeLength(UINT8 wipetype)
boolean F_WipeExists(UINT8 wipetype)
{
#ifdef NOWIPE
(void)wipetype;
return false;
#else
static char lumpname[10] = "FADEmm00";

View file

@ -68,7 +68,7 @@ static struct {
UINT8 flags; // EZT flags
// EZT_COLOR
UINT8 color, lastcolor;
UINT16 color, lastcolor;
// EZT_SCALE
fixed_t scale, lastscale;
@ -82,7 +82,8 @@ static struct {
// There is no conflict here.
typedef struct demoghost {
UINT8 checksum[16];
UINT8 *buffer, *p, color, fadein;
UINT8 *buffer, *p, fadein;
UINT16 color;
UINT16 version;
mobj_t oldmo, *mo;
struct demoghost *next;
@ -93,7 +94,7 @@ demoghost *ghosts = NULL;
// DEMO RECORDING
//
#define DEMOVERSION 0x000c
#define DEMOVERSION 0x000d
#define DEMOHEADER "\xF0" "SRB2Replay" "\x0F"
#define DF_GHOST 0x01 // This demo contains ghost data too!
@ -165,7 +166,6 @@ void G_LoadMetal(UINT8 **buffer)
void G_ReadDemoTiccmd(ticcmd_t *cmd, INT32 playernum)
{
UINT8 ziptic;
(void)playernum;
if (!demo_p || !demo_start)
return;
@ -183,6 +183,7 @@ void G_ReadDemoTiccmd(ticcmd_t *cmd, INT32 playernum)
oldcmd.aiming = READINT16(demo_p);
G_CopyTiccmd(cmd, &oldcmd, 1);
players[playernum].angleturn = cmd->angleturn;
if (!(demoflags & DF_GHOST) && *demo_p == DEMOMARKER)
{
@ -280,13 +281,13 @@ void G_GhostAddColor(ghostcolor_t color)
{
if (!demorecording || !(demoflags & DF_GHOST))
return;
if (ghostext.lastcolor == (UINT8)color)
if (ghostext.lastcolor == (UINT16)color)
{
ghostext.flags &= ~EZT_COLOR;
return;
}
ghostext.flags |= EZT_COLOR;
ghostext.color = (UINT8)color;
ghostext.color = (UINT16)color;
}
void G_GhostAddScale(fixed_t scale)
@ -425,7 +426,7 @@ void G_WriteGhostTic(mobj_t *ghost)
WRITEUINT8(demo_p,ghostext.flags);
if (ghostext.flags & EZT_COLOR)
{
WRITEUINT8(demo_p,ghostext.color);
WRITEUINT16(demo_p,ghostext.color);
ghostext.lastcolor = ghostext.color;
}
if (ghostext.flags & EZT_SCALE)
@ -501,7 +502,7 @@ void G_WriteGhostTic(mobj_t *ghost)
WRITEUINT8(demo_p,ghost->player->followmobj->sprite2);
WRITEUINT16(demo_p,ghost->player->followmobj->sprite);
WRITEUINT8(demo_p,(ghost->player->followmobj->frame & FF_FRAMEMASK));
WRITEUINT8(demo_p,ghost->player->followmobj->color);
WRITEUINT16(demo_p,ghost->player->followmobj->color);
*followtic_p = followtic;
}
@ -566,7 +567,7 @@ void G_ConsGhostTic(void)
{ // But wait, there's more!
UINT8 xziptic = READUINT8(demo_p);
if (xziptic & EZT_COLOR)
demo_p++;
demo_p += (demoversion==0x000c) ? 1 : sizeof(UINT16);
if (xziptic & EZT_SCALE)
demo_p += sizeof(fixed_t);
if (xziptic & EZT_HIT)
@ -633,7 +634,7 @@ void G_ConsGhostTic(void)
demo_p++;
demo_p += sizeof(UINT16);
demo_p++;
demo_p++;
demo_p += (demoversion==0x000c) ? 1 : sizeof(UINT16);
}
// Re-synchronise
@ -731,7 +732,7 @@ void G_GhostTicker(void)
xziptic = READUINT8(g->p);
if (xziptic & EZT_COLOR)
{
g->color = READUINT8(g->p);
g->color = (g->version==0x000c) ? READUINT8(g->p) : READUINT16(g->p);
switch(g->color)
{
default:
@ -764,7 +765,7 @@ void G_GhostTicker(void)
if (xziptic & EZT_THOKMASK)
{ // Let's only spawn ONE of these per frame, thanks.
mobj_t *mobj;
INT32 type = -1;
UINT32 type = MT_NULL;
if (g->mo->skin)
{
skin_t *skin = (skin_t *)g->mo->skin;
@ -864,7 +865,7 @@ void G_GhostTicker(void)
g->mo->color += abs( ( (signed)( (unsigned)leveltime >> 1 ) % 9) - 4);
break;
case GHC_INVINCIBLE: // Mario invincibility (P_CheckInvincibilityTimer)
g->mo->color = (UINT8)(SKINCOLOR_RUBY + (leveltime % (MAXSKINCOLORS - SKINCOLOR_RUBY))); // Passes through all saturated colours
g->mo->color = (UINT16)(SKINCOLOR_RUBY + (leveltime % (FIRSTSUPERCOLOR - SKINCOLOR_RUBY))); // Passes through all saturated colours
break;
default:
break;
@ -918,7 +919,7 @@ void G_GhostTicker(void)
follow->sprite = READUINT16(g->p);
follow->frame = (READUINT8(g->p)) | (g->mo->frame & FF_TRANSMASK);
follow->angle = g->mo->angle;
follow->color = READUINT8(g->p);
follow->color = (g->version==0x000c) ? READUINT8(g->p) : READUINT16(g->p);
if (!(followtic & FZT_SPAWNED))
{
@ -996,7 +997,11 @@ void G_ReadMetalTic(mobj_t *metal)
// Read changes from the tic
if (ziptic & GZT_XYZ)
{
P_TeleportMove(metal, READFIXED(metal_p), READFIXED(metal_p), READFIXED(metal_p));
// make sure the values are read in the right order
oldmetal.x = READFIXED(metal_p);
oldmetal.y = READFIXED(metal_p);
oldmetal.z = READFIXED(metal_p);
P_TeleportMove(metal, oldmetal.x, oldmetal.y, oldmetal.z);
oldmetal.x = metal->x;
oldmetal.y = metal->y;
oldmetal.z = metal->z;
@ -1051,7 +1056,7 @@ void G_ReadMetalTic(mobj_t *metal)
if (xziptic & EZT_THOKMASK)
{ // Let's only spawn ONE of these per frame, thanks.
mobj_t *mobj;
INT32 type = -1;
UINT32 type = MT_NULL;
if (metal->skin)
{
skin_t *skin = (skin_t *)metal->skin;
@ -1158,7 +1163,7 @@ void G_ReadMetalTic(mobj_t *metal)
follow->sprite = READUINT16(metal_p);
follow->frame = READUINT32(metal_p); // NOT & FF_FRAMEMASK here, so 32 bits
follow->angle = metal->angle;
follow->color = READUINT8(metal_p);
follow->color = (metalversion==0x000c) ? READUINT8(metal_p) : READUINT16(metal_p);
if (!(followtic & FZT_SPAWNED))
{
@ -1340,7 +1345,7 @@ void G_WriteMetalTic(mobj_t *metal)
WRITEUINT8(demo_p,metal->player->followmobj->sprite2);
WRITEUINT16(demo_p,metal->player->followmobj->sprite);
WRITEUINT32(demo_p,metal->player->followmobj->frame); // NOT & FF_FRAMEMASK here, so 32 bits
WRITEUINT8(demo_p,metal->player->followmobj->color);
WRITEUINT16(demo_p,metal->player->followmobj->color);
*followtic_p = followtic;
}
@ -1394,7 +1399,7 @@ void G_RecordMetal(void)
void G_BeginRecording(void)
{
UINT8 i;
char name[16];
char name[MAXCOLORNAME+1];
player_t *player = &players[consoleplayer];
if (demo_p)
@ -1457,12 +1462,12 @@ void G_BeginRecording(void)
demo_p += 16;
// Color
for (i = 0; i < 16 && cv_playercolor.string[i]; i++)
for (i = 0; i < MAXCOLORNAME && cv_playercolor.string[i]; i++)
name[i] = cv_playercolor.string[i];
for (; i < 16; i++)
for (; i < MAXCOLORNAME; i++)
name[i] = '\0';
M_Memcpy(demo_p,name,16);
demo_p += 16;
M_Memcpy(demo_p,name,MAXCOLORNAME);
demo_p += MAXCOLORNAME;
// Stats
WRITEUINT8(demo_p,player->charability);
@ -1622,7 +1627,7 @@ UINT8 G_CmpDemoTime(char *oldname, char *newname)
c = READUINT8(p); // SUBVERSION
I_Assert(c == SUBVERSION);
s = READUINT16(p);
I_Assert(s == DEMOVERSION);
I_Assert(s >= 0x000c);
p += 16; // demo checksum
I_Assert(!memcmp(p, "PLAY", 4));
p += 4; // PLAY
@ -1671,6 +1676,7 @@ UINT8 G_CmpDemoTime(char *oldname, char *newname)
switch(oldversion) // demoversion
{
case DEMOVERSION: // latest always supported
case 0x000c: // all that changed between then and now was longer color name
break;
// too old, cannot support.
default:
@ -1744,15 +1750,15 @@ void G_DoPlayDemo(char *defdemoname)
{
UINT8 i;
lumpnum_t l;
char skin[17],color[17],*n,*pdemoname;
UINT8 version,subversion,charability,charability2,thrustfactor,accelstart,acceleration;
char skin[17],color[MAXCOLORNAME+1],*n,*pdemoname;
UINT8 version,subversion,charability,charability2,thrustfactor,accelstart,acceleration,cnamelen;
pflags_t pflags;
UINT32 randseed, followitem;
fixed_t camerascale,shieldscale,actionspd,mindash,maxdash,normalspeed,runspeed,jumpfactor,height,spinheight;
char msg[1024];
skin[16] = '\0';
color[16] = '\0';
color[MAXCOLORNAME] = '\0';
n = defdemoname+strlen(defdemoname);
while (*n != '/' && *n != '\\' && n != defdemoname)
@ -1810,6 +1816,11 @@ void G_DoPlayDemo(char *defdemoname)
switch(demoversion)
{
case DEMOVERSION: // latest always supported
cnamelen = MAXCOLORNAME;
break;
// all that changed between then and now was longer color name
case 0x000c:
cnamelen = 16;
break;
// too old, cannot support.
default:
@ -1876,8 +1887,8 @@ void G_DoPlayDemo(char *defdemoname)
demo_p += 16;
// Color
M_Memcpy(color,demo_p,16);
demo_p += 16;
M_Memcpy(color,demo_p,cnamelen);
demo_p += cnamelen;
charability = READUINT8(demo_p);
charability2 = READUINT8(demo_p);
@ -1941,7 +1952,9 @@ void G_DoPlayDemo(char *defdemoname)
// Set skin
SetPlayerSkin(0, skin);
#ifdef HAVE_BLUA
LUAh_MapChange(gamemap);
#endif
displayplayer = consoleplayer = 0;
memset(playeringame,0,sizeof(playeringame));
playeringame[0] = true;
@ -1949,8 +1962,9 @@ void G_DoPlayDemo(char *defdemoname)
G_InitNew(false, G_BuildMapName(gamemap), true, true, false);
// Set color
for (i = 0; i < MAXSKINCOLORS; i++)
if (!stricmp(Color_Names[i],color))
players[0].skincolor = skins[players[0].skin].prefcolor;
for (i = 0; i < numskincolors; i++)
if (!stricmp(skincolors[i].name,color))
{
players[0].skincolor = i;
break;
@ -1992,7 +2006,8 @@ void G_AddGhost(char *defdemoname)
{
INT32 i;
lumpnum_t l;
char name[17],skin[17],color[17],*n,*pdemoname,md5[16];
char name[17],skin[17],color[MAXCOLORNAME+1],*n,*pdemoname,md5[16];
UINT8 cnamelen;
demoghost *gh;
UINT8 flags;
UINT8 *buffer,*p;
@ -2047,6 +2062,11 @@ void G_AddGhost(char *defdemoname)
switch(ghostversion)
{
case DEMOVERSION: // latest always supported
cnamelen = MAXCOLORNAME;
break;
// all that changed between then and now was longer color name
case 0x000c:
cnamelen = 16;
break;
// too old, cannot support.
default:
@ -2109,8 +2129,8 @@ void G_AddGhost(char *defdemoname)
p += 16;
// Color
M_Memcpy(color, p,16);
p += 16;
M_Memcpy(color, p,cnamelen);
p += cnamelen;
// Ghosts do not have a player structure to put this in.
p++; // charability
@ -2198,10 +2218,10 @@ void G_AddGhost(char *defdemoname)
// Set color
gh->mo->color = ((skin_t*)gh->mo->skin)->prefcolor;
for (i = 0; i < MAXSKINCOLORS; i++)
if (!stricmp(Color_Names[i],color))
for (i = 0; i < numskincolors; i++)
if (!stricmp(skincolors[i].name,color))
{
gh->mo->color = (UINT8)i;
gh->mo->color = (UINT16)i;
break;
}
gh->oldmo.color = gh->mo->color;
@ -2292,6 +2312,7 @@ void G_DoPlayMetal(void)
switch(metalversion)
{
case DEMOVERSION: // latest always supported
case 0x000c: // all that changed between then and now was longer color name
break;
// too old, cannot support.
default:
@ -2332,6 +2353,38 @@ void G_DoneLevelLoad(void)
===================
*/
// Writes the demo's checksum, or just random garbage if you can't do that for some reason.
static void WriteDemoChecksum(void)
{
UINT8 *p = demobuffer+16; // checksum position
#ifdef NOMD5
UINT8 i;
for (i = 0; i < 16; i++, p++)
*p = P_RandomByte(); // This MD5 was chosen by fair dice roll and most likely < 50% correct.
#else
md5_buffer((char *)p+16, demo_p - (p+16), p); // make a checksum of everything after the checksum in the file.
#endif
}
// Stops recording a demo.
static void G_StopDemoRecording(void)
{
boolean saved = false;
WRITEUINT8(demo_p, DEMOMARKER); // add the demo end marker
WriteDemoChecksum();
saved = FIL_WriteFile(va(pandf, srb2home, demoname), demobuffer, demo_p - demobuffer); // finally output the file.
free(demobuffer);
demorecording = false;
if (modeattacking != ATTACKING_RECORD)
{
if (saved)
CONS_Printf(M_GetText("Demo %s recorded\n"), demoname);
else
CONS_Alert(CONS_WARNING, M_GetText("Demo %s not saved\n"), demoname);
}
}
// Stops metal sonic's demo. Separate from other functions because metal + replays can coexist
void G_StopMetalDemo(void)
{
@ -2349,20 +2402,8 @@ ATTRNORETURN void FUNCNORETURN G_StopMetalRecording(boolean kill)
boolean saved = false;
if (demo_p)
{
UINT8 *p = demobuffer+16; // checksum position
if (kill)
WRITEUINT8(demo_p, METALDEATH); // add the metal death marker
else
WRITEUINT8(demo_p, DEMOMARKER); // add the demo end marker
#ifdef NOMD5
{
UINT8 i;
for (i = 0; i < 16; i++, p++)
*p = P_RandomByte(); // This MD5 was chosen by fair dice roll and most likely < 50% correct.
}
#else
md5_buffer((char *)p+16, demo_p - (p+16), (void *)p); // make a checksum of everything after the checksum in the file.
#endif
WRITEUINT8(demo_p, (kill) ? METALDEATH : DEMOMARKER); // add the demo end (or metal death) marker
WriteDemoChecksum();
saved = FIL_WriteFile(va("%sMS.LMP", G_BuildMapName(gamemap)), demobuffer, demo_p - demobuffer); // finally output the file.
}
free(demobuffer);
@ -2372,6 +2413,63 @@ ATTRNORETURN void FUNCNORETURN G_StopMetalRecording(boolean kill)
I_Error("Failed to save demo!");
}
// Stops timing a demo.
static void G_StopTimingDemo(void)
{
INT32 demotime;
double f1, f2;
demotime = I_GetTime() - demostarttime;
if (!demotime)
return;
G_StopDemo();
timingdemo = false;
f1 = (double)demotime;
f2 = (double)framecount*TICRATE;
CONS_Printf(M_GetText("timed %u gametics in %d realtics - %u frames\n%f seconds, %f avg fps\n"),
leveltime,demotime,(UINT32)framecount,f1/TICRATE,f2/f1);
// CSV-readable timedemo results, for external parsing
if (timedemo_csv)
{
FILE *f;
const char *csvpath = va("%s"PATHSEP"%s", srb2home, "timedemo.csv");
const char *header = "id,demoname,seconds,avgfps,leveltime,demotime,framecount,ticrate,rendermode,vidmode,vidwidth,vidheight,procbits\n";
const char *rowformat = "\"%s\",\"%s\",%f,%f,%u,%d,%u,%u,%u,%u,%u,%u,%u\n";
boolean headerrow = !FIL_FileExists(csvpath);
UINT8 procbits = 0;
// Bitness
if (sizeof(void*) == 4)
procbits = 32;
else if (sizeof(void*) == 8)
procbits = 64;
f = fopen(csvpath, "a+");
if (f)
{
if (headerrow)
fputs(header, f);
fprintf(f, rowformat,
timedemo_csv_id,timedemo_name,f1/TICRATE,f2/f1,leveltime,demotime,(UINT32)framecount,TICRATE,rendermode,vid.modenum,vid.width,vid.height,procbits);
fclose(f);
CONS_Printf("Timedemo results saved to '%s'\n", csvpath);
}
else
{
// Just print the CSV output to console
CON_LogMessage(header);
CONS_Printf(rowformat,
timedemo_csv_id,timedemo_name,f1/TICRATE,f2/f1,leveltime,demotime,(UINT32)framecount,TICRATE,rendermode,vid.modenum,vid.width,vid.height,procbits);
}
}
if (restorecv_vidwait != cv_vidwait.value)
CV_SetValue(&cv_vidwait, restorecv_vidwait);
D_AdvanceDemo();
}
// reset engine variable set for the demos
// called from stopdemo command, map command, and g_checkdemoStatus.
void G_StopDemo(void)
@ -2394,66 +2492,13 @@ void G_StopDemo(void)
boolean G_CheckDemoStatus(void)
{
boolean saved;
G_FreeGhosts();
// DO NOT end metal sonic demos here
if (timingdemo)
{
INT32 demotime;
double f1, f2;
demotime = I_GetTime() - demostarttime;
if (!demotime)
return true;
G_StopDemo();
timingdemo = false;
f1 = (double)demotime;
f2 = (double)framecount*TICRATE;
CONS_Printf(M_GetText("timed %u gametics in %d realtics - %u frames\n%f seconds, %f avg fps\n"),
leveltime,demotime,(UINT32)framecount,f1/TICRATE,f2/f1);
// CSV-readable timedemo results, for external parsing
if (timedemo_csv)
{
FILE *f;
const char *csvpath = va("%s"PATHSEP"%s", srb2home, "timedemo.csv");
const char *header = "id,demoname,seconds,avgfps,leveltime,demotime,framecount,ticrate,rendermode,vidmode,vidwidth,vidheight,procbits\n";
const char *rowformat = "\"%s\",\"%s\",%f,%f,%u,%d,%u,%u,%u,%u,%u,%u,%u\n";
boolean headerrow = !FIL_FileExists(csvpath);
UINT8 procbits = 0;
// Bitness
if (sizeof(void*) == 4)
procbits = 32;
else if (sizeof(void*) == 8)
procbits = 64;
f = fopen(csvpath, "a+");
if (f)
{
if (headerrow)
fputs(header, f);
fprintf(f, rowformat,
timedemo_csv_id,timedemo_name,f1/TICRATE,f2/f1,leveltime,demotime,(UINT32)framecount,TICRATE,rendermode,vid.modenum,vid.width,vid.height,procbits);
fclose(f);
CONS_Printf("Timedemo results saved to '%s'\n", csvpath);
}
else
{
// Just print the CSV output to console
CON_LogMessage(header);
CONS_Printf(rowformat,
timedemo_csv_id,timedemo_name,f1/TICRATE,f2/f1,leveltime,demotime,(UINT32)framecount,TICRATE,rendermode,vid.modenum,vid.width,vid.height,procbits);
}
}
if (restorecv_vidwait != cv_vidwait.value)
CV_SetValue(&cv_vidwait, restorecv_vidwait);
D_AdvanceDemo();
G_StopTimingDemo();
return true;
}
@ -2473,27 +2518,7 @@ boolean G_CheckDemoStatus(void)
if (demorecording)
{
UINT8 *p = demobuffer+16; // checksum position
#ifdef NOMD5
UINT8 i;
WRITEUINT8(demo_p, DEMOMARKER); // add the demo end marker
for (i = 0; i < 16; i++, p++)
*p = P_RandomByte(); // This MD5 was chosen by fair dice roll and most likely < 50% correct.
#else
WRITEUINT8(demo_p, DEMOMARKER); // add the demo end marker
md5_buffer((char *)p+16, demo_p - (p+16), p); // make a checksum of everything after the checksum in the file.
#endif
saved = FIL_WriteFile(va(pandf, srb2home, demoname), demobuffer, demo_p - demobuffer); // finally output the file.
free(demobuffer);
demorecording = false;
if (modeattacking != ATTACKING_RECORD)
{
if (saved)
CONS_Printf(M_GetText("Demo %s recorded\n"), demoname);
else
CONS_Alert(CONS_WARNING, M_GetText("Demo %s not saved\n"), demoname);
}
G_StopDemoRecording();
return true;
}

View file

@ -54,7 +54,7 @@ UINT8 ultimatemode = false;
boolean botingame;
UINT8 botskin;
UINT8 botcolor;
UINT16 botcolor;
JoyType_t Joystick;
JoyType_t Joystick2;
@ -82,7 +82,10 @@ INT32 curWeather = PRECIP_NONE;
INT32 cursaveslot = 0; // Auto-save 1p savegame slot
//INT16 lastmapsaved = 0; // Last map we auto-saved at
INT16 lastmaploaded = 0; // Last map the game loaded
boolean gamecomplete = false;
UINT8 gamecomplete = 0;
marathonmode_t marathonmode = 0;
tic_t marathontime = 0;
UINT8 numgameovers = 0; // for startinglives balance
SINT8 startinglivesbalance[maxgameovers+1] = {3, 5, 7, 9, 12, 15, 20, 25, 30, 40, 50, 75, 99, 0x7F};
@ -118,7 +121,7 @@ UINT32 ssspheres; // old special stage
INT16 lastmap; // last level you were at (returning from special stages)
tic_t timeinmap; // Ticker for time spent in level (used for levelcard display)
INT16 spstage_start;
INT16 spstage_start, spmarathon_start;
INT16 sstage_start, sstage_end, smpstage_start, smpstage_end;
INT16 titlemap = 0;
@ -135,10 +138,10 @@ INT32 tutorialanalog = 0; // store cv_analog[0] user value
boolean looptitle = false;
UINT8 skincolor_redteam = SKINCOLOR_RED;
UINT8 skincolor_blueteam = SKINCOLOR_BLUE;
UINT8 skincolor_redring = SKINCOLOR_SALMON;
UINT8 skincolor_bluering = SKINCOLOR_CORNFLOWER;
UINT16 skincolor_redteam = SKINCOLOR_RED;
UINT16 skincolor_blueteam = SKINCOLOR_BLUE;
UINT16 skincolor_redring = SKINCOLOR_SALMON;
UINT16 skincolor_bluering = SKINCOLOR_CORNFLOWER;
tic_t countdowntimer = 0;
boolean countdowntimeup = false;
@ -223,6 +226,7 @@ UINT8 useContinues = 0; // Set to 1 to enable continues outside of no-save scena
UINT8 introtoplay;
UINT8 creditscutscene;
UINT8 useBlackRock = 1;
// Emerald locations
mobj_t *hunt1;
@ -716,14 +720,14 @@ void G_SetNightsRecords(void)
I_Error("Out of memory for replay filepath\n");
sprintf(gpath,"%s"PATHSEP"replay"PATHSEP"%s"PATHSEP"%s", srb2home, timeattackfolder, G_BuildMapName(gamemap));
snprintf(lastdemo, 255, "%s-last.lmp", gpath);
snprintf(lastdemo, 255, "%s-%s-last.lmp", gpath, skins[cv_chooseskin.value-1].name);
if (FIL_FileExists(lastdemo))
{
UINT8 *buf;
size_t len = FIL_ReadFile(lastdemo, &buf);
snprintf(bestdemo, 255, "%s-time-best.lmp", gpath);
snprintf(bestdemo, 255, "%s-%s-time-best.lmp", gpath, skins[cv_chooseskin.value-1].name);;
if (!FIL_FileExists(bestdemo) || G_CmpDemoTime(bestdemo, lastdemo) & 1)
{ // Better time, save this demo.
if (FIL_FileExists(bestdemo))
@ -732,7 +736,7 @@ void G_SetNightsRecords(void)
CONS_Printf("\x83%s\x80 %s '%s'\n", M_GetText("NEW RECORD TIME!"), M_GetText("Saved replay as"), bestdemo);
}
snprintf(bestdemo, 255, "%s-score-best.lmp", gpath);
snprintf(bestdemo, 255, "%s-%s-score-best.lmp", gpath, skins[cv_chooseskin.value-1].name);
if (!FIL_FileExists(bestdemo) || (G_CmpDemoTime(bestdemo, lastdemo) & (1<<1)))
{ // Better score, save this demo.
if (FIL_FileExists(bestdemo))
@ -769,6 +773,8 @@ void G_SetGameModified(boolean silent)
// If in record attack recording, cancel it.
if (modeattacking)
M_EndModeAttackRun();
else if (marathonmode)
Command_ExitGame_f();
}
/** Builds an original game map name from a map number.
@ -1065,6 +1071,7 @@ static fixed_t forwardmove[2] = {25<<FRACBITS>>16, 50<<FRACBITS>>16};
static fixed_t sidemove[2] = {25<<FRACBITS>>16, 50<<FRACBITS>>16}; // faster!
static fixed_t angleturn[3] = {640, 1280, 320}; // + slow turn
INT16 ticcmd_oldangleturn[2];
boolean ticcmd_centerviewdown[2]; // For simple controls, lock the camera behind the player
mobj_t *ticcmd_ztargetfocus[2]; // Locking onto an object?
void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer)
@ -1140,7 +1147,7 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer)
if (paused || P_AutoPause() || (gamestate == GS_LEVEL && (player->playerstate == PST_REBORN || ((gametyperules & GTR_TAG)
&& (leveltime < hidetime * TICRATE) && (player->pflags & PF_TAGIT)))))
{//@TODO splitscreen player
cmd->angleturn = (INT16)(*myangle >> 16);
cmd->angleturn = ticcmd_oldangleturn[forplayer];
cmd->aiming = G_ClipAimingPitch(myaiming);
return;
}
@ -1361,7 +1368,7 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer)
if (controlstyle == CS_SIMPLE && !ticcmd_centerviewdown[forplayer] && !G_RingSlingerGametype())
{
CV_SetValue(&cv_directionchar[forplayer], 2);
*myangle = player->mo->angle;
cmd->angleturn = (INT16)((player->mo->angle - *myangle) >> 16);
*myaiming = 0;
if (cv_cam_lockonboss[forplayer].value)
@ -1430,7 +1437,7 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer)
else if (anglediff < -maxturn)
anglediff = -maxturn;
*myangle += anglediff;
cmd->angleturn = (INT16)(cmd->angleturn + (anglediff >> 16));
}
}
}
@ -1567,19 +1574,23 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer)
B_HandleFlightIndicator(player);
}
else if (player->bot == 2)
*myangle = localangle; // Fix offset angle for P2-controlled Tailsbot when P2's controls are set to non-Legacy
// Fix offset angle for P2-controlled Tailsbot when P2's controls are set to non-Legacy
cmd->angleturn = (INT16)((localangle - *myangle) >> 16);
*myangle += (cmd->angleturn<<16);
if (controlstyle == CS_LMAOGALOG) {
angle_t angle;
if (player->awayviewtics)
cmd->angleturn = (INT16)(player->awayviewmobj->angle >> 16);
angle = player->awayviewmobj->angle;
else
cmd->angleturn = (INT16)(thiscam->angle >> 16);
angle = thiscam->angle;
cmd->angleturn = (INT16)((angle - (ticcmd_oldangleturn[forplayer] << 16)) >> 16);
}
else
{
*myangle += (cmd->angleturn<<16);
cmd->angleturn = (INT16)(*myangle >> 16);
// Adjust camera angle by player input
if (controlstyle == CS_SIMPLE && !forcestrafe && thiscam->chase && !turnheld[forplayer] && !ticcmd_centerviewdown[forplayer] && !player->climbing && player->powers[pw_carry] != CR_MINECART)
{
@ -1589,13 +1600,17 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer)
{
fixed_t sine = FINESINE((R_PointToAngle2(0, 0, player->rmomx, player->rmomy) - localangle)>>ANGLETOFINESHIFT);
fixed_t factor;
INT16 camadjust;
if ((sine > 0) == (cmd->sidemove > 0))
sine = 0; // Prevent jerking right when braking from going left, or vice versa
factor = min(40, FixedMul(player->speed, abs(sine))*2 / FRACUNIT);
*myangle -= cmd->sidemove * factor * camadjustfactor;
camadjust = (cmd->sidemove * factor * camadjustfactor) >> 16;
*myangle -= camadjust << 16;
cmd->angleturn = (INT16)(cmd->angleturn - camadjust);
}
if (ticcmd_centerviewdown[forplayer] && (cv_cam_lockedinput[forplayer].value || (player->pflags & PF_STARTDASH)))
@ -1632,9 +1647,10 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer)
{
angle_t controlangle;
INT32 anglediff;
INT16 camadjust;
if ((cmd->forwardmove || cmd->sidemove) && !(player->pflags & PF_SPINNING))
controlangle = (cmd->angleturn<<16) + R_PointToAngle2(0, 0, cmd->forwardmove << FRACBITS, -cmd->sidemove << FRACBITS);
controlangle = *myangle + R_PointToAngle2(0, 0, cmd->forwardmove << FRACBITS, -cmd->sidemove << FRACBITS);
else
controlangle = player->drawangle + drawangleoffset;
@ -1651,7 +1667,10 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer)
anglediff = FixedMul(anglediff, sine);
}
*myangle += FixedMul(anglediff, camadjustfactor);
camadjust = FixedMul(anglediff, camadjustfactor) >> 16;
*myangle += camadjust << 16;
cmd->angleturn = (INT16)(cmd->angleturn + camadjust);
}
}
}
@ -1665,6 +1684,9 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer)
LUAh_ViewpointSwitch(player, &players[consoleplayer], true);
displayplayer = consoleplayer;
}
cmd->angleturn = (INT16)(cmd->angleturn + ticcmd_oldangleturn[forplayer]);
ticcmd_oldangleturn[forplayer] = cmd->angleturn;
}
ticcmd_t *G_CopyTiccmd(ticcmd_t* dest, const ticcmd_t* src, const size_t n)
@ -1866,6 +1888,7 @@ void G_StartTitleCard(void)
//
void G_PreLevelTitleCard(void)
{
#ifndef NOWIPE
tic_t starttime = I_GetTime();
tic_t endtime = starttime + (PRELEVELTIME*NEWTICRATERATIO);
tic_t nowtime = starttime;
@ -1888,6 +1911,7 @@ void G_PreLevelTitleCard(void)
}
if (!cv_showhud.value)
wipestyleflags = WSF_CROSSFADE;
#endif
}
static boolean titlecardforreload = false;
@ -2043,7 +2067,7 @@ boolean G_Responder(event_t *ev)
&& players[displayplayer].ctfteam != players[consoleplayer].ctfteam)
continue;
}
else if (gametype == GT_HIDEANDSEEK)
else if (gametyperules & GTR_HIDEFROZEN)
{
if (players[consoleplayer].pflags & PF_TAGIT)
continue;
@ -2161,6 +2185,10 @@ void G_Ticker(boolean run)
UINT32 i;
INT32 buf;
// see also SCR_DisplayMarathonInfo
if ((marathonmode & (MA_INIT|MA_INGAME)) == MA_INGAME && gamestate == GS_LEVEL)
marathontime++;
P_MapStart();
// do player reborns if needed
if (gamestate == GS_LEVEL)
@ -2177,8 +2205,13 @@ void G_Ticker(boolean run)
}
else
{
// Costs a life to retry ... unless the player in question is dead already.
if (G_GametypeUsesLives() && players[consoleplayer].playerstate == PST_LIVE && players[consoleplayer].lives != INFLIVES)
// Costs a life to retry ... unless the player in question is dead already, or you haven't even touched the first starpost in marathon run.
if (marathonmode && gamemap == spmarathon_start && !players[consoleplayer].starposttime)
{
marathonmode |= MA_INIT;
marathontime = 0;
}
else if (G_GametypeUsesLives() && players[consoleplayer].playerstate == PST_LIVE && players[consoleplayer].lives != INFLIVES)
players[consoleplayer].lives -= 1;
G_DoReborn(consoleplayer);
@ -2205,11 +2238,16 @@ void G_Ticker(boolean run)
buf = gametic % BACKUPTICS;
// read/write demo and check turbo cheat
for (i = 0; i < MAXPLAYERS; i++)
{
if (playeringame[i])
{
G_CopyTiccmd(&players[i].cmd, &netcmds[buf][i], 1);
players[i].angleturn += players[i].cmd.angleturn - players[i].oldrelangleturn;
players[i].oldrelangleturn = players[i].cmd.angleturn;
players[i].cmd.angleturn = players[i].angleturn;
}
}
// do main actions
@ -2377,11 +2415,12 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps)
fixed_t height;
fixed_t spinheight;
INT32 exiting;
tic_t dashmode;
INT16 numboxes;
INT16 totalring;
UINT8 laps;
UINT8 mare;
UINT8 skincolor;
UINT16 skincolor;
INT32 skin;
UINT32 availabilities;
tic_t jointime;
@ -2392,6 +2431,8 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps)
SINT8 pity;
INT16 rings;
INT16 spheres;
INT16 playerangleturn;
INT16 oldrelangleturn;
score = players[player].score;
lives = players[player].lives;
@ -2403,6 +2444,8 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps)
spectator = players[player].spectator;
outofcoop = players[player].outofcoop;
pflags = (players[player].pflags & (PF_FLIPCAM|PF_ANALOGMODE|PF_DIRECTIONCHAR|PF_AUTOBRAKE|PF_TAGIT|PF_GAMETYPEOVER));
playerangleturn = players[player].angleturn;
oldrelangleturn = players[player].oldrelangleturn;
if (!betweenmaps)
pflags |= (players[player].pflags & PF_FINISHED);
@ -2411,6 +2454,8 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps)
if (!(netgame || multiplayer))
pflags |= (players[player].pflags & (PF_GODMODE|PF_NOCLIP|PF_INVIS));
dashmode = players[player].dashmode;
numboxes = players[player].numboxes;
laps = players[player].laps;
totalring = players[player].totalring;
@ -2474,6 +2519,8 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps)
p->quittime = quittime;
p->spectator = spectator;
p->outofcoop = outofcoop;
p->angleturn = playerangleturn;
p->oldrelangleturn = oldrelangleturn;
// save player config truth reborn
p->skincolor = skincolor;
@ -2509,6 +2556,8 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps)
p->spinheight = spinheight;
p->exiting = exiting;
p->dashmode = dashmode;
p->numboxes = numboxes;
p->laps = laps;
p->totalring = totalring;
@ -2569,7 +2618,7 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps)
S_ChangeMusicEx(mapmusname, mapmusflags, true, mapmusposition, 0, 0);
}
if (gametype == GT_COOP)
if (gametyperules & GTR_EMERALDHUNT)
P_FindEmerald(); // scan for emeralds to hunt for
// If NiGHTS, find lowest mare to start with.
@ -2737,6 +2786,26 @@ mapthing_t *G_FindCoopStart(INT32 playernum)
return NULL;
}
// Find a Co-op start, or fallback into other types of starts.
static inline mapthing_t *G_FindCoopStartOrFallback(INT32 playernum)
{
mapthing_t *spawnpoint = NULL;
if (!(spawnpoint = G_FindCoopStart(playernum)) // find a Co-op start
&& !(spawnpoint = G_FindMatchStart(playernum))) // find a DM start
spawnpoint = G_FindCTFStart(playernum); // fallback
return spawnpoint;
}
// Find a Match start, or fallback into other types of starts.
static inline mapthing_t *G_FindMatchStartOrFallback(INT32 playernum)
{
mapthing_t *spawnpoint = NULL;
if (!(spawnpoint = G_FindMatchStart(playernum)) // find a DM start
&& !(spawnpoint = G_FindCTFStart(playernum))) // find a CTF start
spawnpoint = G_FindCoopStart(playernum); // fallback
return spawnpoint;
}
mapthing_t *G_FindMapStart(INT32 playernum)
{
mapthing_t *spawnpoint;
@ -2744,9 +2813,22 @@ mapthing_t *G_FindMapStart(INT32 playernum)
if (!playeringame[playernum])
return NULL;
// -- Spectators --
// Order in platform gametypes: Coop->DM->CTF
// And, with deathmatch starts: DM->CTF->Coop
if (players[playernum].spectator)
{
// In platform gametypes, spawn in Co-op starts first
// Overriden by GTR_DEATHMATCHSTARTS.
if (G_PlatformGametype() && !(gametyperules & GTR_DEATHMATCHSTARTS))
spawnpoint = G_FindCoopStartOrFallback(playernum);
else
spawnpoint = G_FindMatchStartOrFallback(playernum);
}
// -- CTF --
// Order: CTF->DM->Coop
if ((gametyperules & (GTR_TEAMFLAGS|GTR_TEAMS)) && players[playernum].ctfteam)
else if ((gametyperules & (GTR_TEAMFLAGS|GTR_TEAMS)) && players[playernum].ctfteam)
{
if (!(spawnpoint = G_FindCTFStart(playernum)) // find a CTF start
&& !(spawnpoint = G_FindMatchStart(playernum))) // find a DM start
@ -2755,21 +2837,13 @@ mapthing_t *G_FindMapStart(INT32 playernum)
// -- DM/Tag/CTF-spectator/etc --
// Order: DM->CTF->Coop
else if ((gametyperules & GTR_DEATHMATCHSTARTS) && !(players[playernum].pflags & PF_TAGIT))
{
if (!(spawnpoint = G_FindMatchStart(playernum)) // find a DM start
&& !(spawnpoint = G_FindCTFStart(playernum))) // find a CTF start
spawnpoint = G_FindCoopStart(playernum); // fallback
}
else if (G_TagGametype() ? (!(players[playernum].pflags & PF_TAGIT)) : (gametyperules & GTR_DEATHMATCHSTARTS))
spawnpoint = G_FindMatchStartOrFallback(playernum);
// -- Other game modes --
// Order: Coop->DM->CTF
else
{
if (!(spawnpoint = G_FindCoopStart(playernum)) // find a Co-op start
&& !(spawnpoint = G_FindMatchStart(playernum))) // find a DM start
spawnpoint = G_FindCTFStart(playernum); // fallback
}
spawnpoint = G_FindCoopStartOrFallback(playernum);
//No spawns found. ANYWHERE.
if (!spawnpoint)
@ -2855,7 +2929,7 @@ void G_DoReborn(INT32 playernum)
return;
}
if (countdowntimeup || (!(netgame || multiplayer) && gametype == GT_COOP))
if (countdowntimeup || (!(netgame || multiplayer) && (gametyperules & GTR_CAMPAIGN)))
resetlevel = true;
else if ((G_GametypeUsesCoopLives() || G_GametypeUsesCoopStarposts()) && (netgame || multiplayer) && !G_IsSpecialStage(gamemap))
{
@ -2929,7 +3003,7 @@ void G_DoReborn(INT32 playernum)
players[i].starpostnum = 0;
}
}
if (!countdowntimeup && (mapheaderinfo[gamemap-1]->levelflags & LF_NORELOAD))
if (!countdowntimeup && (mapheaderinfo[gamemap-1]->levelflags & LF_NORELOAD) && !(marathonmode & MA_INIT))
{
P_RespawnThings();
@ -3060,7 +3134,7 @@ void G_AddPlayer(INT32 playernum)
p->height = mobjinfo[MT_PLAYER].height;
if (G_GametypeUsesLives() || ((netgame || multiplayer) && gametype == GT_COOP))
if (G_GametypeUsesLives() || ((netgame || multiplayer) && (gametyperules & GTR_FRIENDLY)))
p->lives = cv_startinglives.value;
if ((countplayers && !notexiting) || G_IsSpecialStage(gamemap))
@ -3109,7 +3183,7 @@ void G_ExitLevel(void)
CV_SetValue(&cv_teamscramble, cv_scrambleonchange.value);
}
if (!(gametyperules & GTR_CAMPAIGN))
if (!(gametyperules & (GTR_FRIENDLY|GTR_CAMPAIGN)))
CONS_Printf(M_GetText("The round has ended.\n"));
// Remove CEcho text on round end.
@ -3175,7 +3249,7 @@ UINT32 gametypedefaultrules[NUMGAMETYPES] =
// Tag
GTR_RINGSLINGER|GTR_FIRSTPERSON|GTR_TAG|GTR_SPECTATORS|GTR_POINTLIMIT|GTR_TIMELIMIT|GTR_OVERTIME|GTR_STARTCOUNTDOWN|GTR_BLINDFOLDED|GTR_DEATHMATCHSTARTS|GTR_SPAWNINVUL|GTR_RESPAWNDELAY,
// Hide and Seek
GTR_RINGSLINGER|GTR_FIRSTPERSON|GTR_TAG|GTR_SPECTATORS|GTR_POINTLIMIT|GTR_TIMELIMIT|GTR_OVERTIME|GTR_STARTCOUNTDOWN|GTR_BLINDFOLDED|GTR_DEATHMATCHSTARTS|GTR_SPAWNINVUL|GTR_RESPAWNDELAY,
GTR_RINGSLINGER|GTR_FIRSTPERSON|GTR_TAG|GTR_SPECTATORS|GTR_POINTLIMIT|GTR_TIMELIMIT|GTR_OVERTIME|GTR_STARTCOUNTDOWN|GTR_HIDEFROZEN|GTR_BLINDFOLDED|GTR_DEATHMATCHSTARTS|GTR_SPAWNINVUL|GTR_RESPAWNDELAY,
// CTF
GTR_RINGSLINGER|GTR_FIRSTPERSON|GTR_SPECTATORS|GTR_TEAMS|GTR_TEAMFLAGS|GTR_POINTLIMIT|GTR_TIMELIMIT|GTR_OVERTIME|GTR_POWERSTONES|GTR_DEATHMATCHSTARTS|GTR_SPAWNINVUL|GTR_RESPAWNDELAY|GTR_PITYSHIELD,
@ -3520,6 +3594,16 @@ boolean G_PlatformGametype(void)
return (!(gametyperules & GTR_RINGSLINGER));
}
//
// G_CoopGametype
//
// Returns true if a gametype is a Co-op gametype.
//
boolean G_CoopGametype(void)
{
return ((gametyperules & (GTR_FRIENDLY|GTR_CAMPAIGN)) == (GTR_FRIENDLY|GTR_CAMPAIGN));
}
//
// G_TagGametype
//
@ -3627,6 +3711,24 @@ static void G_UpdateVisited(void)
}
}
static boolean CanSaveLevel(INT32 mapnum)
{
// You can never save in a special stage.
if (G_IsSpecialStage(mapnum))
return false;
// If the game is complete for this save slot, then any level can save!
if (gamecomplete)
return true;
// Be kind with Marathon Mode live event backups.
if (marathonmode)
return true;
// Any levels that have the savegame flag can save normally.
return (mapheaderinfo[mapnum-1] && (mapheaderinfo[mapnum-1]->levelflags & LF_SAVEGAME));
}
//
// G_DoCompleted
//
@ -3662,66 +3764,74 @@ static void G_DoCompleted(void)
// nextmap is 0-based, unlike gamemap
if (nextmapoverride != 0)
nextmap = (INT16)(nextmapoverride-1);
else if (marathonmode && mapheaderinfo[gamemap-1]->marathonnext)
nextmap = (INT16)(mapheaderinfo[gamemap-1]->marathonnext-1);
else
{
nextmap = (INT16)(mapheaderinfo[gamemap-1]->nextlevel-1);
// Remember last map for when you come out of the special stage.
if (!spec)
lastmap = nextmap;
if (marathonmode && nextmap == spmarathon_start-1)
nextmap = 1100-1; // No infinite loop for you
}
// If nextmap is actually going to get used, make sure it points to
// a map of the proper gametype -- skip levels that don't support
// the current gametype. (Helps avoid playing boss levels in Race,
// for instance).
if (!token && !spec
&& (nextmap >= 0 && nextmap < NUMMAPS))
if (!spec)
{
register INT16 cm = nextmap;
UINT32 tolflag = G_TOLFlag(gametype);
UINT8 visitedmap[(NUMMAPS+7)/8];
memset(visitedmap, 0, sizeof (visitedmap));
while (!mapheaderinfo[cm] || !(mapheaderinfo[cm]->typeoflevel & tolflag))
if (nextmap >= 0 && nextmap < NUMMAPS)
{
visitedmap[cm/8] |= (1<<(cm&7));
if (!mapheaderinfo[cm])
cm = -1; // guarantee error execution
else
cm = (INT16)(mapheaderinfo[cm]->nextlevel-1);
register INT16 cm = nextmap;
UINT32 tolflag = G_TOLFlag(gametype);
UINT8 visitedmap[(NUMMAPS+7)/8];
if (cm >= NUMMAPS || cm < 0) // out of range (either 1100-1102 or error)
memset(visitedmap, 0, sizeof (visitedmap));
while (!mapheaderinfo[cm] || !(mapheaderinfo[cm]->typeoflevel & tolflag))
{
cm = nextmap; //Start the loop again so that the error checking below is executed.
visitedmap[cm/8] |= (1<<(cm&7));
if (!mapheaderinfo[cm])
cm = -1; // guarantee error execution
else if (marathonmode && mapheaderinfo[cm]->marathonnext)
cm = (INT16)(mapheaderinfo[cm]->marathonnext-1);
else
cm = (INT16)(mapheaderinfo[cm]->nextlevel-1);
//Make sure the map actually exists before you try to go to it!
if ((W_CheckNumForName(G_BuildMapName(cm + 1)) == LUMPERROR))
if (cm >= NUMMAPS || cm < 0) // out of range (either 1100ish or error)
{
CONS_Alert(CONS_ERROR, M_GetText("Next map given (MAP %d) doesn't exist! Reverting to MAP01.\n"), cm+1);
cm = 0;
cm = nextmap; //Start the loop again so that the error checking below is executed.
//Make sure the map actually exists before you try to go to it!
if ((W_CheckNumForName(G_BuildMapName(cm + 1)) == LUMPERROR))
{
CONS_Alert(CONS_ERROR, M_GetText("Next map given (MAP %d) doesn't exist! Reverting to MAP01.\n"), cm+1);
cm = 0;
break;
}
}
if (visitedmap[cm/8] & (1<<(cm&7))) // smells familiar
{
// We got stuck in a loop, came back to the map we started on
// without finding one supporting the current gametype.
// Thus, print a warning, and just use this map anyways.
CONS_Alert(CONS_WARNING, M_GetText("Can't find a compatible map after map %d; using map %d anyway\n"), prevmap+1, cm+1);
break;
}
}
if (visitedmap[cm/8] & (1<<(cm&7))) // smells familiar
{
// We got stuck in a loop, came back to the map we started on
// without finding one supporting the current gametype.
// Thus, print a warning, and just use this map anyways.
CONS_Alert(CONS_WARNING, M_GetText("Can't find a compatible map after map %d; using map %d anyway\n"), prevmap+1, cm+1);
break;
}
nextmap = cm;
}
nextmap = cm;
// wrap around in race
if (nextmap >= 1100-1 && nextmap <= 1102-1 && !(gametyperules & GTR_CAMPAIGN))
nextmap = (INT16)(spstage_start-1);
if (nextmap < 0 || (nextmap >= NUMMAPS && nextmap < 1100-1) || nextmap > 1103-1)
I_Error("Followed map %d to invalid map %d\n", prevmap + 1, nextmap + 1);
lastmap = nextmap; // Remember last map for when you come out of the special stage.
}
if (nextmap < 0 || (nextmap >= NUMMAPS && nextmap < 1100-1) || nextmap > 1103-1)
I_Error("Followed map %d to invalid map %d\n", prevmap + 1, nextmap + 1);
// wrap around in race
if (nextmap >= 1100-1 && nextmap <= 1102-1 && !(gametyperules & GTR_CAMPAIGN))
nextmap = (INT16)(spstage_start-1);
if ((gottoken = ((gametyperules & GTR_SPECIALSTAGES) && token)))
{
token--;
@ -3759,7 +3869,10 @@ static void G_DoCompleted(void)
if (nextmap < NUMMAPS && !mapheaderinfo[nextmap])
P_AllocMapHeader(nextmap);
if ((skipstats && !modeattacking) || (spec && modeattacking && stagefailed))
// If the current gametype has no intermission screen set, then don't start it.
Y_DetermineIntermissionType();
if ((skipstats && !modeattacking) || (spec && modeattacking && stagefailed) || (intertype == int_none))
{
G_UpdateVisited();
G_AfterIntermission();
@ -3770,8 +3883,32 @@ static void G_DoCompleted(void)
Y_StartIntermission();
G_UpdateVisited();
}
// do this before running the intermission or custom cutscene, mostly for the sake of marathon mode but it also massively reduces redundant file save events in f_finale.c
if (nextmap >= 1100-1)
{
if (!gamecomplete)
gamecomplete = 2; // special temporary mode to prevent using SP level select in pause menu until the intermission is over without restricting it in every intermission
if (cursaveslot > 0)
{
if (marathonmode)
{
// don't keep a backup around when the run is done!
if (FIL_FileExists(liveeventbackup))
remove(liveeventbackup);
cursaveslot = 0;
}
else if ((!modifiedgame || savemoddata) && !(netgame || multiplayer || ultimatemode || demorecording || metalrecording || modeattacking))
G_SaveGame((UINT32)cursaveslot, spstage_start);
}
}
// and doing THIS here means you don't lose your progress if you close the game mid-intermission
else if (!(ultimatemode || netgame || multiplayer || demoplayback || demorecording || metalrecording || modeattacking)
&& (!modifiedgame || savemoddata) && cursaveslot > 0 && CanSaveLevel(lastmap+1))
G_SaveGame((UINT32)cursaveslot, lastmap+1); // not nextmap+1 to route around special stages
}
// See also F_EndCutscene, the only other place which handles intra-map/ending transitions
void G_AfterIntermission(void)
{
Y_CleanupScreenBuffer();
@ -3782,9 +3919,12 @@ void G_AfterIntermission(void)
return;
}
if (gamecomplete == 2) // special temporary mode to prevent using SP level select in pause menu until the intermission is over without restricting it in every intermission
gamecomplete = 1;
HU_ClearCEcho();
if ((gametyperules & GTR_CUTSCENES) && mapheaderinfo[gamemap-1]->cutscenenum && !modeattacking && skipstats <= 1) // Start a custom cutscene.
if ((gametyperules & GTR_CUTSCENES) && mapheaderinfo[gamemap-1]->cutscenenum && !modeattacking && skipstats <= 1 && (gamecomplete || !(marathonmode & MA_NOCUTSCENES))) // Start a custom cutscene.
F_StartCustomCutscene(mapheaderinfo[gamemap-1]->cutscenenum-1, false, false);
else
{
@ -3810,7 +3950,7 @@ static void G_DoWorldDone(void)
{
if (server)
{
if (gametype == GT_COOP)
if (gametyperules & GTR_CAMPAIGN)
// don't reset player between maps
D_MapChange(nextmap+1, gametype, ultimatemode, false, 0, false, false);
else
@ -3925,7 +4065,7 @@ void G_EndGame(void)
void G_LoadGameSettings(void)
{
// defaults
spstage_start = 1;
spstage_start = spmarathon_start = 1;
sstage_start = 50;
sstage_end = 56; // 7 special stages in vanilla SRB2
sstage_end++; // plus one weirdo
@ -4245,7 +4385,10 @@ void G_LoadGame(UINT32 slot, INT16 mapoverride)
startonmapnum = mapoverride;
#endif
sprintf(savename, savegamename, slot);
if (marathonmode)
strcpy(savename, liveeventbackup);
else
sprintf(savename, savegamename, slot);
length = FIL_ReadFile(savename, &savebuffer);
if (!length)
@ -4257,7 +4400,7 @@ void G_LoadGame(UINT32 slot, INT16 mapoverride)
save_p = savebuffer;
memset(vcheck, 0, sizeof (vcheck));
sprintf(vcheck, "version %d", VERSION);
sprintf(vcheck, (marathonmode ? "back-up %d" : "version %d"), VERSION);
if (strcmp((const char *)save_p, (const char *)vcheck))
{
#ifdef SAVEGAME_OTHERVERSIONS
@ -4297,6 +4440,11 @@ void G_LoadGame(UINT32 slot, INT16 mapoverride)
memset(&savedata, 0, sizeof(savedata));
return;
}
if (marathonmode)
{
marathontime = READUINT32(save_p);
marathonmode |= READUINT8(save_p);
}
// done
Z_Free(savebuffer);
@ -4319,19 +4467,18 @@ void G_LoadGame(UINT32 slot, INT16 mapoverride)
// G_SaveGame
// Saves your game.
//
void G_SaveGame(UINT32 slot)
void G_SaveGame(UINT32 slot, INT16 mapnum)
{
boolean saved;
char savename[256] = "";
const char *backup;
sprintf(savename, savegamename, slot);
if (marathonmode)
strcpy(savename, liveeventbackup);
else
sprintf(savename, savegamename, slot);
backup = va("%s",savename);
// save during evaluation or credits? game's over, folks!
if (gamestate == GS_ENDING || gamestate == GS_CREDITS || gamestate == GS_EVALUATION)
gamecomplete = true;
gameaction = ga_nothing;
{
char name[VERSIONSIZE];
@ -4345,10 +4492,15 @@ void G_SaveGame(UINT32 slot)
}
memset(name, 0, sizeof (name));
sprintf(name, "version %d", VERSION);
sprintf(name, (marathonmode ? "back-up %d" : "version %d"), VERSION);
WRITEMEM(save_p, name, VERSIONSIZE);
P_SaveGame();
P_SaveGame(mapnum);
if (marathonmode)
{
WRITEUINT32(save_p, marathontime);
WRITEUINT8(save_p, (marathonmode & ~MA_INIT));
}
length = save_p - savebuffer;
saved = FIL_WriteFile(backup, savebuffer, length);
@ -4361,7 +4513,7 @@ void G_SaveGame(UINT32 slot)
if (cv_debug && saved)
CONS_Printf(M_GetText("Game saved.\n"));
else if (!saved)
CONS_Alert(CONS_ERROR, M_GetText("Error while writing to %s for save slot %u, base: %s\n"), backup, slot, savegamename);
CONS_Alert(CONS_ERROR, M_GetText("Error while writing to %s for save slot %u, base: %s\n"), backup, slot, (marathonmode ? liveeventbackup : savegamename));
}
#define BADSAVE goto cleanup;
@ -4374,7 +4526,10 @@ void G_SaveGameOver(UINT32 slot, boolean modifylives)
char savename[255];
const char *backup;
sprintf(savename, savegamename, slot);
if (marathonmode)
strcpy(savename, liveeventbackup);
else
sprintf(savename, savegamename, slot);
backup = va("%s",savename);
length = FIL_ReadFile(savename, &savebuffer);
@ -4393,7 +4548,7 @@ void G_SaveGameOver(UINT32 slot, boolean modifylives)
save_p = savebuffer;
// Version check
memset(vcheck, 0, sizeof (vcheck));
sprintf(vcheck, "version %d", VERSION);
sprintf(vcheck, (marathonmode ? "back-up %d" : "version %d"), VERSION);
if (strcmp((const char *)save_p, (const char *)vcheck)) BADSAVE
save_p += VERSIONSIZE;
@ -4460,7 +4615,7 @@ cleanup:
if (cv_debug && saved)
CONS_Printf(M_GetText("Game saved.\n"));
else if (!saved)
CONS_Alert(CONS_ERROR, M_GetText("Error while writing to %s for save slot %u, base: %s\n"), backup, slot, savegamename);
CONS_Alert(CONS_ERROR, M_GetText("Error while writing to %s for save slot %u, base: %s\n"), backup, slot, (marathonmode ? liveeventbackup : savegamename));
Z_Free(savebuffer);
save_p = savebuffer = NULL;
@ -4475,7 +4630,7 @@ cleanup:
//
void G_DeferedInitNew(boolean pultmode, const char *mapname, INT32 pickedchar, boolean SSSG, boolean FLS)
{
UINT8 color = skins[pickedchar].prefcolor;
UINT16 color = skins[pickedchar].prefcolor;
paused = false;
if (demoplayback)
@ -4598,7 +4753,7 @@ void G_InitNew(UINT8 pultmode, const char *mapname, boolean resetplayer, boolean
automapactive = false;
imcontinuing = false;
if ((gametyperules & GTR_CUTSCENES) && !skipprecutscene && mapheaderinfo[gamemap-1]->precutscenenum && !modeattacking) // Start a custom cutscene.
if ((gametyperules & GTR_CUTSCENES) && !skipprecutscene && mapheaderinfo[gamemap-1]->precutscenenum && !modeattacking && !(marathonmode & MA_NOCUTSCENES)) // Start a custom cutscene.
F_StartCustomCutscene(mapheaderinfo[gamemap-1]->precutscenenum-1, true, resetplayer);
else
G_DoLoadLevel(resetplayer);

View file

@ -93,6 +93,7 @@ typedef enum
// build an internal map name MAPxx from map number
const char *G_BuildMapName(INT32 map);
extern INT16 ticcmd_oldangleturn[2];
extern boolean ticcmd_centerviewdown[2]; // For simple controls, lock the camera behind the player
extern mobj_t *ticcmd_ztargetfocus[2]; // Locking onto an object?
void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer);
@ -165,7 +166,7 @@ void G_LoadGame(UINT32 slot, INT16 mapoverride);
void G_SaveGameData(void);
void G_SaveGame(UINT32 slot);
void G_SaveGame(UINT32 slot, INT16 mapnum);
void G_SaveGameOver(UINT32 slot, boolean modifylives);
@ -190,6 +191,7 @@ boolean G_GametypeHasTeams(void);
boolean G_GametypeHasSpectators(void);
boolean G_RingSlingerGametype(void);
boolean G_PlatformGametype(void);
boolean G_CoopGametype(void);
boolean G_TagGametype(void);
boolean G_CompetitionGametype(void);
boolean G_EnoughPlayersFinished(void);

View file

@ -57,6 +57,7 @@ extern UINT8 ultimatemode; // was sk_insane
extern gameaction_t gameaction;
extern boolean botingame;
extern UINT8 botskin, botcolor;
extern UINT8 botskin;
extern UINT16 botcolor;
#endif //__G_STATE__

View file

@ -1,20 +1,12 @@
// Emacs style mode select -*- C++ -*-
// SONIC ROBO BLAST 2
//-----------------------------------------------------------------------------
//
// Copyright (C) 2001 by DooM Legacy Team.
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
// See the 'LICENSE' file for more details.
//-----------------------------------------------------------------------------
/// \file
/// \file hw3dsdrv.h
/// \brief 3D sound import/export prototypes for low-level hardware interface
#ifndef __HW_3DS_DRV_H__

View file

@ -1,19 +1,12 @@
// Emacs style mode select -*- C++ -*-
// SONIC ROBO BLAST 2
//-----------------------------------------------------------------------------
//
// Copyright (C) 2001 by DooM Legacy Team.
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
// See the 'LICENSE' file for more details.
//-----------------------------------------------------------------------------
/// \file
/// \file hw3sound.c
/// \brief Hardware 3D sound general code
#include "../doomdef.h"

View file

@ -1,20 +1,12 @@
// Emacs style mode select -*- C++ -*-
// SONIC ROBO BLAST 2
//-----------------------------------------------------------------------------
//
// Copyright (C) 2001 by DooM Legacy Team.
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
// See the 'LICENSE' file for more details.
//-----------------------------------------------------------------------------
/// \file
/// \file hw3sound.h
/// \brief High-level functions of hardware 3D sound
#ifndef __HW3_SOUND_H__

454
src/hardware/hw_batching.c Normal file
View file

@ -0,0 +1,454 @@
// SONIC ROBO BLAST 2
//-----------------------------------------------------------------------------
// Copyright (C) 1998-2000 by DooM Legacy Team.
// Copyright (C) 1999-2020 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
// See the 'LICENSE' file for more details.
//-----------------------------------------------------------------------------
/// \file hw_batching.c
/// \brief Draw call batching and related things.
#ifdef HWRENDER
#include "hw_glob.h"
#include "hw_batching.h"
#include "../i_system.h"
// The texture for the next polygon given to HWR_ProcessPolygon.
// Set with HWR_SetCurrentTexture.
GLMipmap_t *current_texture = NULL;
boolean currently_batching = false;
FOutVector* finalVertexArray = NULL;// contains subset of sorted vertices and texture coordinates to be sent to gpu
UINT32* finalVertexIndexArray = NULL;// contains indexes for glDrawElements, taking into account fan->triangles conversion
// NOTE have this alloced as 3x finalVertexArray size
int finalVertexArrayAllocSize = 65536;
//GLubyte* colorArray = NULL;// contains color data to be sent to gpu, if needed
//int colorArrayAllocSize = 65536;
// not gonna use this for now, just sort by color and change state when it changes
// later maybe when using vertex attributes if it's needed
PolygonArrayEntry* polygonArray = NULL;// contains the polygon data from DrawPolygon, waiting to be processed
int polygonArraySize = 0;
UINT32* polygonIndexArray = NULL;// contains sorting pointers for polygonArray
int polygonArrayAllocSize = 65536;
FOutVector* unsortedVertexArray = NULL;// contains unsorted vertices and texture coordinates from DrawPolygon
int unsortedVertexArraySize = 0;
int unsortedVertexArrayAllocSize = 65536;
// Enables batching mode. HWR_ProcessPolygon will collect polygons instead of passing them directly to the rendering backend.
// Call HWR_RenderBatches to render all the collected geometry.
void HWR_StartBatching(void)
{
if (currently_batching)
I_Error("Repeat call to HWR_StartBatching without HWR_RenderBatches");
// init arrays if that has not been done yet
if (!finalVertexArray)
{
finalVertexArray = malloc(finalVertexArrayAllocSize * sizeof(FOutVector));
finalVertexIndexArray = malloc(finalVertexArrayAllocSize * 3 * sizeof(UINT32));
polygonArray = malloc(polygonArrayAllocSize * sizeof(PolygonArrayEntry));
polygonIndexArray = malloc(polygonArrayAllocSize * sizeof(UINT32));
unsortedVertexArray = malloc(unsortedVertexArrayAllocSize * sizeof(FOutVector));
}
currently_batching = true;
}
// This replaces the direct calls to pfnSetTexture in cases where batching is available.
// The texture selection is saved for the next HWR_ProcessPolygon call.
// Doing this was easier than getting a texture pointer to HWR_ProcessPolygon.
void HWR_SetCurrentTexture(GLMipmap_t *texture)
{
if (currently_batching)
{
current_texture = texture;
}
else
{
HWD.pfnSetTexture(texture);
}
}
// If batching is enabled, this function collects the polygon data and the chosen texture
// for later use in HWR_RenderBatches. Otherwise the rendering backend is used to
// render the polygon immediately.
void HWR_ProcessPolygon(FSurfaceInfo *pSurf, FOutVector *pOutVerts, FUINT iNumPts, FBITFIELD PolyFlags, int shader, boolean horizonSpecial)
{
if (currently_batching)
{
if (!pSurf)
I_Error("Got a null FSurfaceInfo in batching");// nulls should not come in the stuff that batching currently applies to
if (polygonArraySize == polygonArrayAllocSize)
{
PolygonArrayEntry* new_array;
// ran out of space, make new array double the size
polygonArrayAllocSize *= 2;
new_array = malloc(polygonArrayAllocSize * sizeof(PolygonArrayEntry));
memcpy(new_array, polygonArray, polygonArraySize * sizeof(PolygonArrayEntry));
free(polygonArray);
polygonArray = new_array;
// also need to redo the index array, dont need to copy it though
free(polygonIndexArray);
polygonIndexArray = malloc(polygonArrayAllocSize * sizeof(UINT32));
}
while (unsortedVertexArraySize + (int)iNumPts > unsortedVertexArrayAllocSize)
{
FOutVector* new_array;
// need more space for vertices in unsortedVertexArray
unsortedVertexArrayAllocSize *= 2;
new_array = malloc(unsortedVertexArrayAllocSize * sizeof(FOutVector));
memcpy(new_array, unsortedVertexArray, unsortedVertexArraySize * sizeof(FOutVector));
free(unsortedVertexArray);
unsortedVertexArray = new_array;
}
// add the polygon data to the arrays
polygonArray[polygonArraySize].surf = *pSurf;
polygonArray[polygonArraySize].vertsIndex = unsortedVertexArraySize;
polygonArray[polygonArraySize].numVerts = iNumPts;
polygonArray[polygonArraySize].polyFlags = PolyFlags;
polygonArray[polygonArraySize].texture = current_texture;
polygonArray[polygonArraySize].shader = shader;
polygonArray[polygonArraySize].horizonSpecial = horizonSpecial;
polygonArraySize++;
memcpy(&unsortedVertexArray[unsortedVertexArraySize], pOutVerts, iNumPts * sizeof(FOutVector));
unsortedVertexArraySize += iNumPts;
}
else
{
if (shader)
HWD.pfnSetShader(shader);
HWD.pfnDrawPolygon(pSurf, pOutVerts, iNumPts, PolyFlags);
}
}
static int comparePolygons(const void *p1, const void *p2)
{
unsigned int index1 = *(const unsigned int*)p1;
unsigned int index2 = *(const unsigned int*)p2;
PolygonArrayEntry* poly1 = &polygonArray[index1];
PolygonArrayEntry* poly2 = &polygonArray[index2];
int diff;
INT64 diff64;
int shader1 = poly1->shader;
int shader2 = poly2->shader;
// make skywalls and horizon lines first in order
if (poly1->polyFlags & PF_NoTexture || poly1->horizonSpecial)
shader1 = -1;
if (poly2->polyFlags & PF_NoTexture || poly2->horizonSpecial)
shader2 = -1;
diff = shader1 - shader2;
if (diff != 0) return diff;
// skywalls and horizon lines must retain their order for horizon lines to work
if (shader1 == -1 && shader2 == -1)
return index1 - index2;
diff64 = poly1->texture - poly2->texture;
if (diff64 != 0) return diff64;
diff = poly1->polyFlags - poly2->polyFlags;
if (diff != 0) return diff;
diff64 = poly1->surf.PolyColor.rgba - poly2->surf.PolyColor.rgba;
if (diff64 < 0) return -1; else if (diff64 > 0) return 1;
diff64 = poly1->surf.TintColor.rgba - poly2->surf.TintColor.rgba;
if (diff64 < 0) return -1; else if (diff64 > 0) return 1;
diff64 = poly1->surf.FadeColor.rgba - poly2->surf.FadeColor.rgba;
if (diff64 < 0) return -1; else if (diff64 > 0) return 1;
diff = poly1->surf.LightInfo.light_level - poly2->surf.LightInfo.light_level;
if (diff != 0) return diff;
diff = poly1->surf.LightInfo.fade_start - poly2->surf.LightInfo.fade_start;
if (diff != 0) return diff;
diff = poly1->surf.LightInfo.fade_end - poly2->surf.LightInfo.fade_end;
return diff;
}
static int comparePolygonsNoShaders(const void *p1, const void *p2)
{
unsigned int index1 = *(const unsigned int*)p1;
unsigned int index2 = *(const unsigned int*)p2;
PolygonArrayEntry* poly1 = &polygonArray[index1];
PolygonArrayEntry* poly2 = &polygonArray[index2];
int diff;
INT64 diff64;
GLMipmap_t *texture1 = poly1->texture;
GLMipmap_t *texture2 = poly2->texture;
if (poly1->polyFlags & PF_NoTexture || poly1->horizonSpecial)
texture1 = NULL;
if (poly2->polyFlags & PF_NoTexture || poly2->horizonSpecial)
texture2 = NULL;
diff64 = texture1 - texture2;
if (diff64 != 0) return diff64;
// skywalls and horizon lines must retain their order for horizon lines to work
if (texture1 == NULL && texture2 == NULL)
return index1 - index2;
diff = poly1->polyFlags - poly2->polyFlags;
if (diff != 0) return diff;
diff64 = poly1->surf.PolyColor.rgba - poly2->surf.PolyColor.rgba;
if (diff64 < 0) return -1; else if (diff64 > 0) return 1;
return 0;
}
// This function organizes the geometry collected by HWR_ProcessPolygon calls into batches and uses
// the rendering backend to draw them.
void HWR_RenderBatches(void)
{
int finalVertexWritePos = 0;// position in finalVertexArray
int finalIndexWritePos = 0;// position in finalVertexIndexArray
int polygonReadPos = 0;// position in polygonIndexArray
int currentShader;
int nextShader = 0;
GLMipmap_t *currentTexture;
GLMipmap_t *nextTexture = NULL;
FBITFIELD currentPolyFlags = 0;
FBITFIELD nextPolyFlags = 0;
FSurfaceInfo currentSurfaceInfo;
FSurfaceInfo nextSurfaceInfo;
int i;
if (!currently_batching)
I_Error("HWR_RenderBatches called without starting batching");
nextSurfaceInfo.LightInfo.fade_end = 0;
nextSurfaceInfo.LightInfo.fade_start = 0;
nextSurfaceInfo.LightInfo.light_level = 0;
currently_batching = false;// no longer collecting batches
if (!polygonArraySize)
{
rs_hw_numpolys = rs_hw_numcalls = rs_hw_numshaders = rs_hw_numtextures = rs_hw_numpolyflags = rs_hw_numcolors = 0;
return;// nothing to draw
}
// init stats vars
rs_hw_numpolys = polygonArraySize;
rs_hw_numcalls = rs_hw_numverts = 0;
rs_hw_numshaders = rs_hw_numtextures = rs_hw_numpolyflags = rs_hw_numcolors = 1;
// init polygonIndexArray
for (i = 0; i < polygonArraySize; i++)
{
polygonIndexArray[i] = i;
}
// sort polygons
rs_hw_batchsorttime = I_GetTimeMicros();
if (cv_grshaders.value && gr_shadersavailable)
qsort(polygonIndexArray, polygonArraySize, sizeof(unsigned int), comparePolygons);
else
qsort(polygonIndexArray, polygonArraySize, sizeof(unsigned int), comparePolygonsNoShaders);
rs_hw_batchsorttime = I_GetTimeMicros() - rs_hw_batchsorttime;
// sort order
// 1. shader
// 2. texture
// 3. polyflags
// 4. colors + light level
// not sure about what order of the last 2 should be, or if it even matters
rs_hw_batchdrawtime = I_GetTimeMicros();
currentShader = polygonArray[polygonIndexArray[0]].shader;
currentTexture = polygonArray[polygonIndexArray[0]].texture;
currentPolyFlags = polygonArray[polygonIndexArray[0]].polyFlags;
currentSurfaceInfo = polygonArray[polygonIndexArray[0]].surf;
// For now, will sort and track the colors. Vertex attributes could be used instead of uniforms
// and a color array could replace the color calls.
// set state for first batch
if (cv_grshaders.value && gr_shadersavailable)
{
HWD.pfnSetShader(currentShader);
}
if (currentPolyFlags & PF_NoTexture)
currentTexture = NULL;
else
HWD.pfnSetTexture(currentTexture);
while (1)// note: remember handling notexture polyflag as having texture number 0 (also in comparePolygons)
{
int firstIndex;
int lastIndex;
boolean stopFlag = false;
boolean changeState = false;
boolean changeShader = false;
boolean changeTexture = false;
boolean changePolyFlags = false;
boolean changeSurfaceInfo = false;
// steps:
// write vertices
// check for changes or end, otherwise go back to writing
// changes will affect the next vars and the change bools
// end could set flag for stopping
// execute draw call
// could check ending flag here
// change states according to next vars and change bools, updating the current vars and reseting the bools
// reset write pos
// repeat loop
int index = polygonIndexArray[polygonReadPos++];
int numVerts = polygonArray[index].numVerts;
// before writing, check if there is enough room
// using 'while' instead of 'if' here makes sure that there will *always* be enough room.
// probably never will this loop run more than once though
while (finalVertexWritePos + numVerts > finalVertexArrayAllocSize)
{
FOutVector* new_array;
unsigned int* new_index_array;
finalVertexArrayAllocSize *= 2;
new_array = malloc(finalVertexArrayAllocSize * sizeof(FOutVector));
memcpy(new_array, finalVertexArray, finalVertexWritePos * sizeof(FOutVector));
free(finalVertexArray);
finalVertexArray = new_array;
// also increase size of index array, 3x of vertex array since
// going from fans to triangles increases vertex count to 3x
new_index_array = malloc(finalVertexArrayAllocSize * 3 * sizeof(UINT32));
memcpy(new_index_array, finalVertexIndexArray, finalIndexWritePos * sizeof(UINT32));
free(finalVertexIndexArray);
finalVertexIndexArray = new_index_array;
}
// write the vertices of the polygon
memcpy(&finalVertexArray[finalVertexWritePos], &unsortedVertexArray[polygonArray[index].vertsIndex],
numVerts * sizeof(FOutVector));
// write the indexes, pointing to the fan vertexes but in triangles format
firstIndex = finalVertexWritePos;
lastIndex = finalVertexWritePos + numVerts;
finalVertexWritePos += 2;
while (finalVertexWritePos < lastIndex)
{
finalVertexIndexArray[finalIndexWritePos++] = firstIndex;
finalVertexIndexArray[finalIndexWritePos++] = finalVertexWritePos - 1;
finalVertexIndexArray[finalIndexWritePos++] = finalVertexWritePos++;
}
if (polygonReadPos >= polygonArraySize)
{
stopFlag = true;
}
else
{
// check if a state change is required, set the change bools and next vars
int nextIndex = polygonIndexArray[polygonReadPos];
nextShader = polygonArray[nextIndex].shader;
nextTexture = polygonArray[nextIndex].texture;
nextPolyFlags = polygonArray[nextIndex].polyFlags;
nextSurfaceInfo = polygonArray[nextIndex].surf;
if (nextPolyFlags & PF_NoTexture)
nextTexture = 0;
if (currentShader != nextShader && cv_grshaders.value && gr_shadersavailable)
{
changeState = true;
changeShader = true;
}
if (currentTexture != nextTexture)
{
changeState = true;
changeTexture = true;
}
if (currentPolyFlags != nextPolyFlags)
{
changeState = true;
changePolyFlags = true;
}
if (cv_grshaders.value && gr_shadersavailable)
{
if (currentSurfaceInfo.PolyColor.rgba != nextSurfaceInfo.PolyColor.rgba ||
currentSurfaceInfo.TintColor.rgba != nextSurfaceInfo.TintColor.rgba ||
currentSurfaceInfo.FadeColor.rgba != nextSurfaceInfo.FadeColor.rgba ||
currentSurfaceInfo.LightInfo.light_level != nextSurfaceInfo.LightInfo.light_level ||
currentSurfaceInfo.LightInfo.fade_start != nextSurfaceInfo.LightInfo.fade_start ||
currentSurfaceInfo.LightInfo.fade_end != nextSurfaceInfo.LightInfo.fade_end)
{
changeState = true;
changeSurfaceInfo = true;
}
}
else
{
if (currentSurfaceInfo.PolyColor.rgba != nextSurfaceInfo.PolyColor.rgba)
{
changeState = true;
changeSurfaceInfo = true;
}
}
}
if (changeState || stopFlag)
{
// execute draw call
HWD.pfnDrawIndexedTriangles(&currentSurfaceInfo, finalVertexArray, finalIndexWritePos, currentPolyFlags, finalVertexIndexArray);
// update stats
rs_hw_numcalls++;
rs_hw_numverts += finalIndexWritePos;
// reset write positions
finalVertexWritePos = 0;
finalIndexWritePos = 0;
}
else continue;
// if we're here then either its time to stop or time to change state
if (stopFlag) break;
// change state according to change bools and next vars, update current vars and reset bools
if (changeShader)
{
HWD.pfnSetShader(nextShader);
currentShader = nextShader;
changeShader = false;
rs_hw_numshaders++;
}
if (changeTexture)
{
// texture should be already ready for use from calls to SetTexture during batch collection
HWD.pfnSetTexture(nextTexture);
currentTexture = nextTexture;
changeTexture = false;
rs_hw_numtextures++;
}
if (changePolyFlags)
{
currentPolyFlags = nextPolyFlags;
changePolyFlags = false;
rs_hw_numpolyflags++;
}
if (changeSurfaceInfo)
{
currentSurfaceInfo = nextSurfaceInfo;
changeSurfaceInfo = false;
rs_hw_numcolors++;
}
// and that should be it?
}
// reset the arrays (set sizes to 0)
polygonArraySize = 0;
unsortedVertexArraySize = 0;
rs_hw_batchdrawtime = I_GetTimeMicros() - rs_hw_batchdrawtime;
}
#endif // HWRENDER

View file

@ -0,0 +1,37 @@
// SONIC ROBO BLAST 2
//-----------------------------------------------------------------------------
// Copyright (C) 1998-2000 by DooM Legacy Team.
// Copyright (C) 1999-2020 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
// See the 'LICENSE' file for more details.
//-----------------------------------------------------------------------------
/// \file hw_batching.h
/// \brief Draw call batching and related things.
#ifndef __HWR_BATCHING_H__
#define __HWR_BATCHING_H__
#include "hw_defs.h"
#include "hw_data.h"
#include "hw_drv.h"
typedef struct
{
FSurfaceInfo surf;// surf also has its own polyflags for some reason, but it seems unused
unsigned int vertsIndex;// location of verts in unsortedVertexArray
FUINT numVerts;
FBITFIELD polyFlags;
GLMipmap_t *texture;
int shader;
// this tells batching that the plane belongs to a horizon line and must be drawn in correct order with the skywalls
boolean horizonSpecial;
} PolygonArrayEntry;
void HWR_StartBatching(void);
void HWR_SetCurrentTexture(GLMipmap_t *texture);
void HWR_ProcessPolygon(FSurfaceInfo *pSurf, FOutVector *pOutVerts, FUINT iNumPts, FBITFIELD PolyFlags, int shader, boolean horizonSpecial);
void HWR_RenderBatches(void);
#endif

View file

@ -1,19 +1,12 @@
// Emacs style mode select -*- C++ -*-
// SONIC ROBO BLAST 2
//-----------------------------------------------------------------------------
//
// Copyright (C) 1998-2000 by DooM Legacy Team.
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
// See the 'LICENSE' file for more details.
//-----------------------------------------------------------------------------
/// \file
/// \file hw_bsp.c
/// \brief convert SRB2 map
#include "../doomdef.h"

View file

@ -1,20 +1,13 @@
// Emacs style mode select -*- C++ -*-
// SONIC ROBO BLAST 2
//-----------------------------------------------------------------------------
//
// Copyright (C) 1998-2000 by DooM Legacy Team.
// Copyright (C) 1999-2020 by Sonic Team Junior.
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
// See the 'LICENSE' file for more details.
//-----------------------------------------------------------------------------
/// \file
/// \file hw_cache.c
/// \brief load and convert graphics to the hardware format
#include "../doomdef.h"
@ -22,6 +15,7 @@
#ifdef HWRENDER
#include "hw_glob.h"
#include "hw_drv.h"
#include "hw_batching.h"
#include "../doomstat.h" //gamemode
#include "../i_video.h" //rendermode
@ -34,9 +28,6 @@
#include "../r_picformats.h"
#include "../p_setup.h"
// Values set after a call to HWR_ResizeBlock()
static INT32 blocksize, blockwidth, blockheight;
INT32 patchformat = GR_TEXFMT_AP_88; // use alpha for holes
INT32 textureformat = GR_TEXFMT_P_8; // use chromakey for hole
@ -311,13 +302,13 @@ static void HWR_DrawPatchInCache(GLMipmap_t *mipmap,
if (pwidth <= 0 || pheight <= 0)
return;
ncols = (pwidth * pblockwidth) / pwidth;
ncols = pwidth;
// source advance
xfrac = 0;
xfracstep = (pwidth << FRACBITS) / pblockwidth;
yfracstep = (pheight << FRACBITS) / pblockheight;
scale_y = (pblockheight << FRACBITS) / pheight;
xfracstep = FRACUNIT;
yfracstep = FRACUNIT;
scale_y = FRACUNIT;
bpp = format2bpp[mipmap->grInfo.format];
@ -325,7 +316,7 @@ static void HWR_DrawPatchInCache(GLMipmap_t *mipmap,
I_Error("HWR_DrawPatchInCache: no drawer defined for this bpp (%d)\n",bpp);
// NOTE: should this actually be pblockwidth*bpp?
blockmodulo = blockwidth*bpp;
blockmodulo = pblockwidth*bpp;
// Draw each column to the block cache
for (; ncols--; block += bpp, xfrac += xfracstep)
@ -418,7 +409,7 @@ static void HWR_DrawTexturePatchInCache(GLMipmap_t *mipmap,
I_Error("HWR_DrawPatchInCache: no drawer defined for this bpp (%d)\n",bpp);
// NOTE: should this actually be pblockwidth*bpp?
blockmodulo = blockwidth*bpp;
blockmodulo = pblockwidth*bpp;
// Draw each column to the block cache
for (block += col*bpp; ncols--; block += bpp, xfrac += xfracstep)
@ -436,142 +427,12 @@ static void HWR_DrawTexturePatchInCache(GLMipmap_t *mipmap,
}
}
// resize the patch to be 3dfx compliant
// set : blocksize = blockwidth * blockheight (no bpp used)
// blockwidth
// blockheight
//note : 8bit (1 byte per pixel) palettized format
static void HWR_ResizeBlock(INT32 originalwidth, INT32 originalheight,
GrTexInfo *grInfo)
{
#ifdef GLIDE_API_COMPATIBILITY
// Build the full textures from patches.
static const GrLOD_t gr_lods[9] =
{
GR_LOD_LOG2_256,
GR_LOD_LOG2_128,
GR_LOD_LOG2_64,
GR_LOD_LOG2_32,
GR_LOD_LOG2_16,
GR_LOD_LOG2_8,
GR_LOD_LOG2_4,
GR_LOD_LOG2_2,
GR_LOD_LOG2_1
};
typedef struct
{
GrAspectRatio_t aspect;
float max_s;
float max_t;
} booring_aspect_t;
static const booring_aspect_t gr_aspects[8] =
{
{GR_ASPECT_LOG2_1x1, 255, 255},
{GR_ASPECT_LOG2_2x1, 255, 127},
{GR_ASPECT_LOG2_4x1, 255, 63},
{GR_ASPECT_LOG2_8x1, 255, 31},
{GR_ASPECT_LOG2_1x1, 255, 255},
{GR_ASPECT_LOG2_1x2, 127, 255},
{GR_ASPECT_LOG2_1x4, 63, 255},
{GR_ASPECT_LOG2_1x8, 31, 255}
};
INT32 j,k;
INT32 max,min;
#else
(void)grInfo;
#endif
// find a power of 2 width/height
if (cv_grrounddown.value)
{
blockwidth = 256;
while (originalwidth < blockwidth)
blockwidth >>= 1;
if (blockwidth < 1)
I_Error("3D GenerateTexture : too small");
blockheight = 256;
while (originalheight < blockheight)
blockheight >>= 1;
if (blockheight < 1)
I_Error("3D GenerateTexture : too small");
}
else
{
#ifdef GLIDE_API_COMPATIBILITY
//size up to nearest power of 2
blockwidth = 1;
while (blockwidth < originalwidth)
blockwidth <<= 1;
// scale down the original graphics to fit in 256
if (blockwidth > 2048)
blockwidth = 2048;
//I_Error("3D GenerateTexture : too big");
//size up to nearest power of 2
blockheight = 1;
while (blockheight < originalheight)
blockheight <<= 1;
// scale down the original graphics to fit in 256
if (blockheight > 2048)
blockheight = 2048;
//I_Error("3D GenerateTexture : too big");
#else
blockwidth = originalwidth;
blockheight = originalheight;
#endif
}
// do the boring LOD stuff.. blech!
#ifdef GLIDE_API_COMPATIBILITY
if (blockwidth >= blockheight)
{
max = blockwidth;
min = blockheight;
}
else
{
max = blockheight;
min = blockwidth;
}
for (k = 2048, j = 0; k > max; j++)
k>>=1;
grInfo->smallLodLog2 = gr_lods[j];
grInfo->largeLodLog2 = gr_lods[j];
for (k = max, j = 0; k > min && j < 4; j++)
k>>=1;
// aspect ratio too small for 3Dfx (eg: 8x128 is 1x16 : use 1x8)
if (j == 4)
{
j = 3;
//CONS_Debug(DBG_RENDER, "HWR_ResizeBlock : bad aspect ratio %dx%d\n", blockwidth,blockheight);
if (blockwidth < blockheight)
blockwidth = max>>3;
else
blockheight = max>>3;
}
if (blockwidth < blockheight)
j += 4;
grInfo->aspectRatioLog2 = gr_aspects[j].aspect;
#endif
blocksize = blockwidth * blockheight;
//CONS_Debug(DBG_RENDER, "Width is %d, Height is %d\n", blockwidth, blockheight);
}
static UINT8 *MakeBlock(GLMipmap_t *grMipmap)
{
UINT8 *block;
INT32 bpp, i;
UINT16 bu16 = ((0x00 <<8) | HWR_PATCHES_CHROMAKEY_COLORINDEX);
INT32 blocksize = (grMipmap->width * grMipmap->height);
bpp = format2bpp[grMipmap->grInfo.format];
block = Z_Malloc(blocksize*bpp, PU_HWRCACHE, &(grMipmap->grInfo.data));
@ -602,6 +463,7 @@ static void HWR_GenerateTexture(INT32 texnum, GLTexture_t *grtex)
texpatch_t *patch;
patch_t *realpatch;
UINT8 *pdata;
INT32 blockwidth, blockheight, blocksize;
INT32 i;
boolean skyspecial = false; //poor hack for Legacy large skies..
@ -622,11 +484,13 @@ static void HWR_GenerateTexture(INT32 texnum, GLTexture_t *grtex)
else
grtex->mipmap.flags = TF_CHROMAKEYED | TF_WRAPXY;
HWR_ResizeBlock (texture->width, texture->height, &grtex->mipmap.grInfo);
grtex->mipmap.width = (UINT16)blockwidth;
grtex->mipmap.height = (UINT16)blockheight;
grtex->mipmap.width = (UINT16)texture->width;
grtex->mipmap.height = (UINT16)texture->height;
grtex->mipmap.grInfo.format = textureformat;
blockwidth = texture->width;
blockheight = texture->height;
blocksize = (blockwidth * blockheight);
block = MakeBlock(&grtex->mipmap);
if (skyspecial) //Hurdler: not efficient, but better than holes in the sky (and it's done only at level loading)
@ -699,8 +563,6 @@ static void HWR_GenerateTexture(INT32 texnum, GLTexture_t *grtex)
// patch may be NULL if grMipmap has been initialised already and makebitmap is false
void HWR_MakePatch (const patch_t *patch, GLPatch_t *grPatch, GLMipmap_t *grMipmap, boolean makebitmap)
{
INT32 newwidth, newheight;
#ifndef NO_PNG_LUMPS
// lump is a png so convert it
size_t len = W_LumpLengthPwad(grPatch->wadnum, grPatch->lumpnum);
@ -724,51 +586,32 @@ void HWR_MakePatch (const patch_t *patch, GLPatch_t *grPatch, GLMipmap_t *grMipm
grPatch->leftoffset = SHORT(patch->leftoffset);
grPatch->topoffset = SHORT(patch->topoffset);
// find the good 3dfx size (boring spec)
HWR_ResizeBlock (SHORT(patch->width), SHORT(patch->height), &grMipmap->grInfo);
grMipmap->width = (UINT16)blockwidth;
grMipmap->height = (UINT16)blockheight;
grMipmap->width = grMipmap->height = 1;
while (grMipmap->width < grPatch->width) grMipmap->width <<= 1;
while (grMipmap->height < grPatch->height) grMipmap->height <<= 1;
// no wrap around, no chroma key
grMipmap->flags = 0;
// setup the texture info
grMipmap->grInfo.format = patchformat;
}
else
{
blockwidth = grMipmap->width;
blockheight = grMipmap->height;
blocksize = blockwidth * blockheight;
//grPatch->max_s = grPatch->max_t = 1.0f;
grPatch->max_s = (float)grPatch->width / (float)grMipmap->width;
grPatch->max_t = (float)grPatch->height / (float)grMipmap->height;
}
Z_Free(grMipmap->grInfo.data);
grMipmap->grInfo.data = NULL;
// if rounddown, rounddown patches as well as textures
if (cv_grrounddown.value)
{
newwidth = blockwidth;
newheight = blockheight;
}
else
{
// no rounddown, do not size up patches, so they don't look 'scaled'
newwidth = min(grPatch->width, blockwidth);
newheight = min(grPatch->height, blockheight);
}
if (makebitmap)
{
MakeBlock(grMipmap);
HWR_DrawPatchInCache(grMipmap,
newwidth, newheight,
grMipmap->width, grMipmap->height,
grPatch->width, grPatch->height,
patch);
}
grPatch->max_s = (float)newwidth / (float)blockwidth;
grPatch->max_t = (float)newheight / (float)blockheight;
}
@ -908,8 +751,11 @@ GLTexture_t *HWR_GetTexture(INT32 tex)
if (!grtex->mipmap.grInfo.data && !grtex->mipmap.downloaded)
HWR_GenerateTexture(tex, grtex);
// Tell the hardware driver to bind the current texture to the flat's mipmap
HWD.pfnSetTexture(&grtex->mipmap);
// If hardware does not have the texture, then call pfnSetTexture to upload it
if (!grtex->mipmap.downloaded)
HWD.pfnSetTexture(&grtex->mipmap);
HWR_SetCurrentTexture(&grtex->mipmap);
// The system-memory data can be purged now.
Z_ChangeTag(grtex->mipmap.grInfo.data, PU_HWRCACHE_UNLOCKED);
@ -922,11 +768,6 @@ static void HWR_CacheFlat(GLMipmap_t *grMipmap, lumpnum_t flatlumpnum)
size_t size, pflatsize;
// setup the texture info
#ifdef GLIDE_API_COMPATIBILITY
grMipmap->grInfo.smallLodLog2 = GR_LOD_LOG2_64;
grMipmap->grInfo.largeLodLog2 = GR_LOD_LOG2_64;
grMipmap->grInfo.aspectRatioLog2 = GR_ASPECT_LOG2_1x1;
#endif
grMipmap->grInfo.format = GR_TEXFMT_P_8;
grMipmap->flags = TF_WRAPXY|TF_CHROMAKEYED;
@ -972,11 +813,6 @@ static void HWR_CacheTextureAsFlat(GLMipmap_t *grMipmap, INT32 texturenum)
size_t size;
// setup the texture info
#ifdef GLIDE_API_COMPATIBILITY
grMipmap->grInfo.smallLodLog2 = GR_LOD_LOG2_64;
grMipmap->grInfo.largeLodLog2 = GR_LOD_LOG2_64;
grMipmap->grInfo.aspectRatioLog2 = GR_ASPECT_LOG2_1x1;
#endif
grMipmap->grInfo.format = GR_TEXFMT_P_8;
grMipmap->flags = TF_WRAPXY|TF_CHROMAKEYED;
@ -1001,7 +837,11 @@ void HWR_LiterallyGetFlat(lumpnum_t flatlumpnum)
if (!grmip->downloaded && !grmip->grInfo.data)
HWR_CacheFlat(grmip, flatlumpnum);
HWD.pfnSetTexture(grmip);
// If hardware does not have the texture, then call pfnSetTexture to upload it
if (!grmip->downloaded)
HWD.pfnSetTexture(grmip);
HWR_SetCurrentTexture(grmip);
// The system-memory data can be purged now.
Z_ChangeTag(grmip->grInfo.data, PU_HWRCACHE_UNLOCKED);
@ -1035,8 +875,11 @@ void HWR_GetLevelFlat(levelflat_t *levelflat)
if (!grtex->mipmap.grInfo.data && !grtex->mipmap.downloaded)
HWR_CacheTextureAsFlat(&grtex->mipmap, texturenum);
// Tell the hardware driver to bind the current texture to the flat's mipmap
HWD.pfnSetTexture(&grtex->mipmap);
// If hardware does not have the texture, then call pfnSetTexture to upload it
if (!grtex->mipmap.downloaded)
HWD.pfnSetTexture(&grtex->mipmap);
HWR_SetCurrentTexture(&grtex->mipmap);
// The system-memory data can be purged now.
Z_ChangeTag(grtex->mipmap.grInfo.data, PU_HWRCACHE_UNLOCKED);
@ -1094,7 +937,7 @@ void HWR_GetLevelFlat(levelflat_t *levelflat)
}
#endif
else // set no texture
HWD.pfnSetTexture(NULL);
HWR_SetCurrentTexture(NULL);
}
//
@ -1116,7 +959,11 @@ static void HWR_LoadMappedPatch(GLMipmap_t *grmip, GLPatch_t *gpatch)
Z_Free(patch);
}
HWD.pfnSetTexture(grmip);
// If hardware does not have the texture, then call pfnSetTexture to upload it
if (!grmip->downloaded)
HWD.pfnSetTexture(grmip);
HWR_SetCurrentTexture(grmip);
// The system-memory data can be purged now.
Z_ChangeTag(grmip->grInfo.data, PU_HWRCACHE_UNLOCKED);
@ -1143,7 +990,11 @@ void HWR_GetPatch(GLPatch_t *gpatch)
Z_Free(ptr);
}
HWD.pfnSetTexture(gpatch->mipmap);
// If hardware does not have the texture, then call pfnSetTexture to upload it
if (!gpatch->mipmap->downloaded)
HWD.pfnSetTexture(gpatch->mipmap);
HWR_SetCurrentTexture(gpatch->mipmap);
// The system-memory patch data can be purged now.
Z_ChangeTag(gpatch->mipmap->grInfo.data, PU_HWRCACHE_UNLOCKED);
@ -1286,7 +1137,6 @@ GLPatch_t *HWR_GetPic(lumpnum_t lumpnum)
pic_t *pic;
UINT8 *block;
size_t len;
INT32 newwidth, newheight;
pic = W_CacheLumpNum(lumpnum, PU_CACHE);
grpatch->width = SHORT(pic->width);
@ -1296,10 +1146,8 @@ GLPatch_t *HWR_GetPic(lumpnum_t lumpnum)
grpatch->leftoffset = 0;
grpatch->topoffset = 0;
// find the good 3dfx size (boring spec)
HWR_ResizeBlock (grpatch->width, grpatch->height, &grpatch->mipmap->grInfo);
grpatch->mipmap->width = (UINT16)blockwidth;
grpatch->mipmap->height = (UINT16)blockheight;
grpatch->mipmap->width = (UINT16)grpatch->width;
grpatch->mipmap->height = (UINT16)grpatch->height;
if (pic->mode == PALETTE)
grpatch->mipmap->grInfo.format = textureformat; // can be set by driver
@ -1311,30 +1159,16 @@ GLPatch_t *HWR_GetPic(lumpnum_t lumpnum)
// allocate block
block = MakeBlock(grpatch->mipmap);
// if rounddown, rounddown patches as well as textures
if (cv_grrounddown.value)
{
newwidth = blockwidth;
newheight = blockheight;
}
else
{
// no rounddown, do not size up patches, so they don't look 'scaled'
newwidth = min(SHORT(pic->width),blockwidth);
newheight = min(SHORT(pic->height),blockheight);
}
if (grpatch->width == blockwidth &&
grpatch->height == blockheight &&
if (grpatch->width == SHORT(pic->width) &&
grpatch->height == SHORT(pic->height) &&
format2bpp[grpatch->mipmap->grInfo.format] == format2bpp[picmode2GR[pic->mode]])
{
// no conversion needed
M_Memcpy(grpatch->mipmap->grInfo.data, pic->data,len);
}
else
HWR_DrawPicInCache(block, newwidth, newheight,
blockwidth*format2bpp[grpatch->mipmap->grInfo.format],
HWR_DrawPicInCache(block, SHORT(pic->width), SHORT(pic->height),
SHORT(pic->width)*format2bpp[grpatch->mipmap->grInfo.format],
pic,
format2bpp[grpatch->mipmap->grInfo.format]);
@ -1342,8 +1176,7 @@ GLPatch_t *HWR_GetPic(lumpnum_t lumpnum)
Z_ChangeTag(block, PU_HWRCACHE_UNLOCKED);
grpatch->mipmap->flags = 0;
grpatch->max_s = (float)newwidth / (float)blockwidth;
grpatch->max_t = (float)newheight / (float)blockheight;
grpatch->max_s = grpatch->max_t = 1.0f;
}
HWD.pfnSetTexture(grpatch->mipmap);
//CONS_Debug(DBG_RENDER, "picloaded at %x as texture %d\n",grpatch->mipmap.grInfo.data, grpatch->mipmap.downloaded);
@ -1379,7 +1212,7 @@ static void HWR_DrawFadeMaskInCache(GLMipmap_t *mipmap, INT32 pblockwidth, INT32
{
INT32 i,j;
fixed_t posx, posy, stepx, stepy;
UINT8 *block = mipmap->grInfo.data; // places the data directly into here, it already has the space allocated from HWR_ResizeBlock
UINT8 *block = mipmap->grInfo.data; // places the data directly into here
UINT8 *flat;
UINT8 *dest, *src, texel;
RGBA_t col;
@ -1394,7 +1227,7 @@ static void HWR_DrawFadeMaskInCache(GLMipmap_t *mipmap, INT32 pblockwidth, INT32
for (j = 0; j < pblockheight; j++)
{
posx = 0;
dest = &block[j*blockwidth]; // 1bpp
dest = &block[j*(mipmap->width)]; // 1bpp
src = &flat[(posy>>FRACBITS)*SHORT(fmwidth)];
for (i = 0; i < pblockwidth;i++)
{
@ -1448,14 +1281,12 @@ static void HWR_CacheFadeMask(GLMipmap_t *grMipmap, lumpnum_t fademasklumpnum)
}
// Thankfully, this will still work for this scenario
HWR_ResizeBlock(fmwidth, fmheight, &grMipmap->grInfo);
grMipmap->width = blockwidth;
grMipmap->height = blockheight;
grMipmap->width = fmwidth;
grMipmap->height = fmheight;
MakeBlock(grMipmap);
HWR_DrawFadeMaskInCache(grMipmap, blockwidth, blockheight, fademasklumpnum, fmwidth, fmheight);
HWR_DrawFadeMaskInCache(grMipmap, fmwidth, fmheight, fademasklumpnum, fmwidth, fmheight);
// I DO need to convert this because it isn't power of 2 and we need the alpha
}

View file

@ -78,8 +78,8 @@
#include "r_opengl/r_opengl.h"
#ifdef HAVE_SPHEREFRUSTRUM
static GLfloat viewMatrix[16];
static GLfloat projMatrix[16];
static GLdouble viewMatrix[16];
static GLdouble projMatrix[16];
float frustum[6][4];
#endif
@ -320,12 +320,12 @@ void gld_clipper_Clear(void)
#define RMUL (1.6f/1.333333f)
angle_t gld_FrustumAngle(void)
angle_t gld_FrustumAngle(angle_t tiltangle)
{
double floatangle;
angle_t a1;
float tilt = (float)fabs(((double)(int)aimingangle) / ANG1);
float tilt = (float)fabs(((double)(int)tiltangle) / ANG1);
// NEWCLIP TODO: SRB2CBTODO: make a global render_fov for this function
@ -339,7 +339,7 @@ angle_t gld_FrustumAngle(void)
}
// If the pitch is larger than this you can look all around at a FOV of 90
if (abs((signed)aimingangle) > 46 * ANG1)
if (abs((signed)tiltangle) > 46 * ANG1)
return 0xffffffff;
// ok, this is a gross hack that barely works...
@ -352,7 +352,7 @@ angle_t gld_FrustumAngle(void)
}
// SRB2CB I don't think used any of this stuff, let's disable for now since SRB2 probably doesn't want it either
// compiler complains about (p)glGetDoublev anyway, in case anyone wants this
// compiler complains about (p)glGetFloatv anyway, in case anyone wants this
// only r_opengl.c can use the base gl funcs as it turns out, that's a problem for whoever wants sphere frustum checks
// btw to renable define HAVE_SPHEREFRUSTRUM in hw_clip.h
#ifdef HAVE_SPHEREFRUSTRUM
@ -381,7 +381,7 @@ void gld_FrustrumSetup(void)
float t;
float clip[16];
pglGeFloatv(GL_PROJECTION_MATRIX, projMatrix);
pglGetFloatv(GL_PROJECTION_MATRIX, projMatrix);
pglGetFloatv(GL_MODELVIEW_MATRIX, viewMatrix);
clip[0] = CALCMATRIX(0, 0, 1, 4, 2, 8, 3, 12);

View file

@ -17,7 +17,7 @@
boolean gld_clipper_SafeCheckRange(angle_t startAngle, angle_t endAngle);
void gld_clipper_SafeAddClipRange(angle_t startangle, angle_t endangle);
void gld_clipper_Clear(void);
angle_t gld_FrustumAngle(void);
angle_t gld_FrustumAngle(angle_t tiltangle);
#ifdef HAVE_SPHEREFRUSTRUM
void gld_FrustrumSetup(void);
boolean gld_SphereInFrustum(float x, float y, float z, float radius);

View file

@ -1,21 +1,14 @@
// Emacs style mode select -*- C++ -*-
// SONIC ROBO BLAST 2
//-----------------------------------------------------------------------------
//
// Copyright (C) 1998-2000 by DooM Legacy Team.
// Copyright (C) 1999-2020 by Sonic Team Junior.
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
// See the 'LICENSE' file for more details.
//-----------------------------------------------------------------------------
/// \file
/// \brief defines structures and exports for the standard 3D driver DLL used by Doom Legacy
/// \file hw_data.h
/// \brief defines structures and exports for the hardware interface used by Sonic Robo Blast 2
#ifndef _HWR_DATA_
#define _HWR_DATA_

View file

@ -1,24 +1,19 @@
// Emacs style mode select -*- C++ -*-
// SONIC ROBO BLAST 2
//-----------------------------------------------------------------------------
//
// Copyright (C) 1998-2000 by DooM Legacy Team.
// Copyright (C) 1999-2020 by Sonic Team Junior.
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
// See the 'LICENSE' file for more details.
//-----------------------------------------------------------------------------
/// \file
/// \file hw_defs.h
/// \brief 3D hardware renderer definitions
#ifndef _HWR_DEFS_
#define _HWR_DEFS_
#include "../doomtype.h"
#include "../r_defs.h"
#define ZCLIP_PLANE 4.0f // Used for the actual game drawing
#define NZCLIP_PLANE 0.9f // Seems to be only used for the HUD and screen textures
@ -82,16 +77,18 @@ typedef struct
// Simple 3D vector
typedef struct FVector
{
FLOAT x,y,z;
FLOAT x,y,z;
} FVector;
// 3D model vector (coords + texture coords)
typedef struct
{
//FVector Point;
FLOAT x,y,z;
FLOAT s,t,w; // texture coordinates
} v3d_t, wallVert3D;
// ======================
// wallVert3D
// ----------------------
// :crab: IS GONE! :crab:
// ======================
// -----------
// structures
// -----------
//Hurdler: Transform (coords + angles)
//BP: transform order : scale(rotation_x(rotation_y(translation(v))))
@ -123,15 +120,16 @@ typedef struct
#ifdef USE_FTRANSFORM_MIRROR
boolean mirror; // SRB2Kart: Encore Mode
#endif
boolean shearing; // 14042019
float viewaiming; // 17052019
} FTransform;
// Transformed vector, as passed to HWR API
typedef struct
{
FLOAT x,y,z;
FUINT argb; // flat-shaded color
FLOAT sow; // s texture ordinate (s over w)
FLOAT tow; // t texture ordinate (t over w)
FLOAT s; // s texture ordinate (s over w)
FLOAT t; // t texture ordinate (t over w)
} FOutVector;
@ -162,10 +160,10 @@ enum EPolyFlags
PF_Invisible = 0x00000400, // Disable write to color buffer
PF_Decal = 0x00000800, // Enable polygon offset
PF_Modulated = 0x00001000, // Modulation (multiply output with constant ARGB)
// When set, pass the color constant into the FSurfaceInfo -> FlatColor
// When set, pass the color constant into the FSurfaceInfo -> PolyColor
PF_NoTexture = 0x00002000, // Use the small white texture
PF_Corona = 0x00004000, // Tell the rendrer we are drawing a corona
PF_Unused = 0x00008000, // Unused
PF_Ripple = 0x00008000, // Water shader effect
PF_RemoveYWrap = 0x00010000, // Force clamp texture on Y
PF_ForceWrapX = 0x00020000, // Force repeat texture on X
PF_ForceWrapY = 0x00040000, // Force repeat texture on Y
@ -178,7 +176,6 @@ enum EPolyFlags
enum ESurfFlags
{
SF_DYNLIGHT = 0x00000001,
};
enum ETextureFlags
@ -190,38 +187,36 @@ enum ETextureFlags
TF_TRANSPARENT = 0x00000040, // texture with some alpha == 0
};
#ifdef TODO
struct FTextureInfo
{
FUINT Width; // Pixels
FUINT Height; // Pixels
FUBYTE *TextureData; // Image data
FUINT Format; // FORMAT_RGB, ALPHA ...
FBITFIELD Flags; // Flags to tell driver about texture (see ETextureFlags)
void DriverExtra; // (OpenGL texture object nr, ...)
// chromakey enabled,...
struct FTextureInfo *Next; // Manage list of downloaded textures.
};
#else
typedef struct GLMipmap_s FTextureInfo;
#endif
// jimita 14032019
struct FLightInfo
{
FUINT light_level;
FUINT fade_start;
FUINT fade_end;
};
typedef struct FLightInfo FLightInfo;
// Description of a renderable surface
struct FSurfaceInfo
{
FUINT PolyFlags; // Surface flags -- UNUSED YET --
RGBA_t FlatColor; // Flat-shaded color used with PF_Modulated mode
FUINT PolyFlags;
RGBA_t PolyColor;
RGBA_t TintColor;
RGBA_t FadeColor;
FLightInfo LightInfo; // jimita 14032019
};
typedef struct FSurfaceInfo FSurfaceInfo;
#define GL_DEFAULTMIX 0x00000000
#define GL_DEFAULTFOG 0xFF000000
//Hurdler: added for backward compatibility
enum hwdsetspecialstate
{
HWD_SET_MODEL_LIGHTING = 1,
HWD_SET_FOG_MODE,
HWD_SET_FOG_COLOR,
HWD_SET_FOG_DENSITY,
HWD_SET_SHADERS,
HWD_SET_TEXTUREFILTERMODE,
HWD_SET_TEXTUREANISOTROPICMODE,
HWD_NUMSTATE
@ -229,6 +224,15 @@ enum hwdsetspecialstate
typedef enum hwdsetspecialstate hwdspecialstate_t;
// Lactozilla: Shader info
// Generally set at the start of the frame.
enum hwdshaderinfo
{
HWD_SHADERINFO_LEVELTIME = 1,
};
typedef enum hwdshaderinfo hwdshaderinfo_t;
enum hwdfiltermode
{
HWD_SET_TEXTUREFILTER_POINTSAMPLED,

View file

@ -1,19 +1,12 @@
// Emacs style mode select -*- C++ -*-
// SONIC ROBO BLAST 2
//-----------------------------------------------------------------------------
// Copyright (C) 2005 by Sonic Team Junior.
//
// Copyright (C) 2005 by SRB2 Jr. Team.
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
// See the 'LICENSE' file for more details.
//-----------------------------------------------------------------------------
/// \file
/// \file hw_dll.h
/// \brief Win32 DLL and Shared Objects API definitions
#ifndef __HWR_DLL_H__
@ -54,8 +47,6 @@
#endif
#endif
typedef void (*I_Error_t) (const char *error, ...) FUNCIERROR;
// ==========================================================================
// MATHS
// ==========================================================================
@ -63,7 +54,7 @@ typedef void (*I_Error_t) (const char *error, ...) FUNCIERROR;
// Constants
#define DEGREE (0.017453292519943295769236907684883l) // 2*PI/360
void DBG_Printf(const char *lpFmt, ...) /*FUNCPRINTF*/;
void GL_DBG_Printf(const char *format, ...) /*FUNCPRINTF*/;
#ifdef _WINDOWS
BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved);

View file

@ -1,19 +1,13 @@
// Emacs style mode select -*- C++ -*-
// SONIC ROBO BLAST 2
//-----------------------------------------------------------------------------
//
// Copyright (C) 1998-2000 by DooM Legacy Team.
// Copyright (C) 1999-2020 by Sonic Team Junior.
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
// See the 'LICENSE' file for more details.
//-----------------------------------------------------------------------------
/// \file
/// \file hw_draw.c
/// \brief miscellaneous drawing (mainly 2d)
#ifdef __GNUC__
@ -23,6 +17,7 @@
#include "../doomdef.h"
#ifdef HWRENDER
#include "hw_main.h"
#include "hw_glob.h"
#include "hw_drv.h"
@ -43,9 +38,6 @@
#define O_BINARY 0
#endif
float gr_patch_scalex;
float gr_patch_scaley;
#if defined(_MSC_VER)
#pragma pack(1)
#endif
@ -65,9 +57,6 @@ typedef struct
#if defined(_MSC_VER)
#pragma pack()
#endif
typedef UINT8 GLRGB[3];
#define BLENDMODE PF_Translucent
static UINT8 softwaretranstogl[11] = { 0, 25, 51, 76,102,127,153,178,204,229,255};
static UINT8 softwaretranstogl_hi[11] = { 0, 51,102,153,204,255,255,255,255,255,255};
@ -121,12 +110,12 @@ void HWR_DrawPatch(GLPatch_t *gpatch, INT32 x, INT32 y, INT32 option)
v[0].z = v[1].z = v[2].z = v[3].z = 1.0f;
v[0].sow = v[3].sow = 0.0f;
v[2].sow = v[1].sow = gpatch->max_s;
v[0].tow = v[1].tow = 0.0f;
v[2].tow = v[3].tow = gpatch->max_t;
v[0].s = v[3].s = 0.0f;
v[2].s = v[1].s = gpatch->max_s;
v[0].t = v[1].t = 0.0f;
v[2].t = v[3].t = gpatch->max_t;
flags = BLENDMODE|PF_Clip|PF_NoZClip|PF_NoDepthTest;
flags = PF_Translucent|PF_NoDepthTest;
if (option & V_WRAPX)
flags |= PF_ForceWrapX;
@ -356,19 +345,19 @@ void HWR_DrawStretchyFixedPatch(GLPatch_t *gpatch, fixed_t x, fixed_t y, fixed_t
if (option & V_FLIP)
{
v[0].sow = v[3].sow = gpatch->max_s;
v[2].sow = v[1].sow = 0.0f;
v[0].s = v[3].s = gpatch->max_s;
v[2].s = v[1].s = 0.0f;
}
else
{
v[0].sow = v[3].sow = 0.0f;
v[2].sow = v[1].sow = gpatch->max_s;
v[0].s = v[3].s = 0.0f;
v[2].s = v[1].s = gpatch->max_s;
}
v[0].tow = v[1].tow = 0.0f;
v[2].tow = v[3].tow = gpatch->max_t;
v[0].t = v[1].t = 0.0f;
v[2].t = v[3].t = gpatch->max_t;
flags = BLENDMODE|PF_Clip|PF_NoZClip|PF_NoDepthTest;
flags = PF_Translucent|PF_NoDepthTest;
if (option & V_WRAPX)
flags |= PF_ForceWrapX;
@ -379,11 +368,11 @@ void HWR_DrawStretchyFixedPatch(GLPatch_t *gpatch, fixed_t x, fixed_t y, fixed_t
if (alphalevel)
{
FSurfaceInfo Surf;
Surf.FlatColor.s.red = Surf.FlatColor.s.green = Surf.FlatColor.s.blue = 0xff;
if (alphalevel == 13) Surf.FlatColor.s.alpha = softwaretranstogl_lo[st_translucency];
else if (alphalevel == 14) Surf.FlatColor.s.alpha = softwaretranstogl[st_translucency];
else if (alphalevel == 15) Surf.FlatColor.s.alpha = softwaretranstogl_hi[st_translucency];
else Surf.FlatColor.s.alpha = softwaretranstogl[10-alphalevel];
Surf.PolyColor.s.red = Surf.PolyColor.s.green = Surf.PolyColor.s.blue = 0xff;
if (alphalevel == 13) Surf.PolyColor.s.alpha = softwaretranstogl_lo[st_translucency];
else if (alphalevel == 14) Surf.PolyColor.s.alpha = softwaretranstogl[st_translucency];
else if (alphalevel == 15) Surf.PolyColor.s.alpha = softwaretranstogl_hi[st_translucency];
else Surf.PolyColor.s.alpha = softwaretranstogl[10-alphalevel];
flags |= PF_Modulated;
HWD.pfnDrawPolygon(&Surf, v, 4, flags);
}
@ -514,19 +503,19 @@ void HWR_DrawCroppedPatch(GLPatch_t *gpatch, fixed_t x, fixed_t y, fixed_t pscal
v[0].z = v[1].z = v[2].z = v[3].z = 1.0f;
v[0].sow = v[3].sow = ((sx )/(float)SHORT(gpatch->width) )*gpatch->max_s;
v[0].s = v[3].s = ((sx )/(float)SHORT(gpatch->width) )*gpatch->max_s;
if (sx + w > SHORT(gpatch->width))
v[2].sow = v[1].sow = gpatch->max_s;
v[2].s = v[1].s = gpatch->max_s;
else
v[2].sow = v[1].sow = ((sx+w)/(float)SHORT(gpatch->width) )*gpatch->max_s;
v[2].s = v[1].s = ((sx+w)/(float)SHORT(gpatch->width) )*gpatch->max_s;
v[0].tow = v[1].tow = ((sy )/(float)SHORT(gpatch->height))*gpatch->max_t;
v[0].t = v[1].t = ((sy )/(float)SHORT(gpatch->height))*gpatch->max_t;
if (sy + h > SHORT(gpatch->height))
v[2].tow = v[3].tow = gpatch->max_t;
v[2].t = v[3].t = gpatch->max_t;
else
v[2].tow = v[3].tow = ((sy+h)/(float)SHORT(gpatch->height))*gpatch->max_t;
v[2].t = v[3].t = ((sy+h)/(float)SHORT(gpatch->height))*gpatch->max_t;
flags = BLENDMODE|PF_Clip|PF_NoZClip|PF_NoDepthTest;
flags = PF_Translucent|PF_NoDepthTest;
if (option & V_WRAPX)
flags |= PF_ForceWrapX;
@ -537,11 +526,11 @@ void HWR_DrawCroppedPatch(GLPatch_t *gpatch, fixed_t x, fixed_t y, fixed_t pscal
if (alphalevel)
{
FSurfaceInfo Surf;
Surf.FlatColor.s.red = Surf.FlatColor.s.green = Surf.FlatColor.s.blue = 0xff;
if (alphalevel == 13) Surf.FlatColor.s.alpha = softwaretranstogl_lo[st_translucency];
else if (alphalevel == 14) Surf.FlatColor.s.alpha = softwaretranstogl[st_translucency];
else if (alphalevel == 15) Surf.FlatColor.s.alpha = softwaretranstogl_hi[st_translucency];
else Surf.FlatColor.s.alpha = softwaretranstogl[10-alphalevel];
Surf.PolyColor.s.red = Surf.PolyColor.s.green = Surf.PolyColor.s.blue = 0xff;
if (alphalevel == 13) Surf.PolyColor.s.alpha = softwaretranstogl_lo[st_translucency];
else if (alphalevel == 14) Surf.PolyColor.s.alpha = softwaretranstogl[st_translucency];
else if (alphalevel == 15) Surf.PolyColor.s.alpha = softwaretranstogl_hi[st_translucency];
else Surf.PolyColor.s.alpha = softwaretranstogl[10-alphalevel];
flags |= PF_Modulated;
HWD.pfnDrawPolygon(&Surf, v, 4, flags);
}
@ -569,10 +558,10 @@ void HWR_DrawPic(INT32 x, INT32 y, lumpnum_t lumpnum)
v[0].z = v[1].z = v[2].z = v[3].z = 1.0f;
v[0].sow = v[3].sow = 0;
v[2].sow = v[1].sow = patch->max_s;
v[0].tow = v[1].tow = 0;
v[2].tow = v[3].tow = patch->max_t;
v[0].s = v[3].s = 0;
v[2].s = v[1].s = patch->max_s;
v[0].t = v[1].t = 0;
v[2].t = v[3].t = patch->max_t;
//Hurdler: Boris, the same comment as above... but maybe for pics
@ -581,7 +570,7 @@ void HWR_DrawPic(INT32 x, INT32 y, lumpnum_t lumpnum)
// But then, the question is: why not 0 instead of PF_Masked ?
// or maybe PF_Environment ??? (like what I said above)
// BP: PF_Environment don't change anything ! and 0 is undifined
HWD.pfnDrawPolygon(NULL, v, 4, BLENDMODE | PF_NoDepthTest | PF_Clip | PF_NoZClip);
HWD.pfnDrawPolygon(NULL, v, 4, PF_Translucent | PF_NoDepthTest | PF_Clip | PF_NoZClip);
}
// ==========================================================================
@ -644,10 +633,10 @@ void HWR_DrawFlatFill (INT32 x, INT32 y, INT32 w, INT32 h, lumpnum_t flatlumpnum
v[0].z = v[1].z = v[2].z = v[3].z = 1.0f;
// flat is 64x64 lod and texture offsets are [0.0, 1.0]
v[0].sow = v[3].sow = (float)((x & flatflag)/dflatsize);
v[2].sow = v[1].sow = (float)(v[0].sow + w/dflatsize);
v[0].tow = v[1].tow = (float)((y & flatflag)/dflatsize);
v[2].tow = v[3].tow = (float)(v[0].tow + h/dflatsize);
v[0].s = v[3].s = (float)((x & flatflag)/dflatsize);
v[2].s = v[1].s = (float)(v[0].s + w/dflatsize);
v[0].t = v[1].t = (float)((y & flatflag)/dflatsize);
v[2].t = v[3].t = (float)(v[0].t + h/dflatsize);
HWR_LiterallyGetFlat(flatlumpnum);
@ -679,20 +668,20 @@ void HWR_FadeScreenMenuBack(UINT16 color, UINT8 strength)
v[2].y = v[3].y = 1.0f;
v[0].z = v[1].z = v[2].z = v[3].z = 1.0f;
v[0].sow = v[3].sow = 0.0f;
v[2].sow = v[1].sow = 1.0f;
v[0].tow = v[1].tow = 1.0f;
v[2].tow = v[3].tow = 0.0f;
v[0].s = v[3].s = 0.0f;
v[2].s = v[1].s = 1.0f;
v[0].t = v[1].t = 1.0f;
v[2].t = v[3].t = 0.0f;
if (color & 0xFF00) // Do COLORMAP fade.
{
Surf.FlatColor.rgba = UINT2RGBA(0x01010160);
Surf.FlatColor.s.alpha = (strength*8);
Surf.PolyColor.rgba = UINT2RGBA(0x01010160);
Surf.PolyColor.s.alpha = (strength*8);
}
else // Do TRANSMAP** fade.
{
Surf.FlatColor.rgba = V_GetColor(color).rgba;
Surf.FlatColor.s.alpha = softwaretranstogl[strength];
Surf.PolyColor.rgba = V_GetColor(color).rgba;
Surf.PolyColor.s.alpha = softwaretranstogl[strength];
}
HWD.pfnDrawPolygon(&Surf, v, 4, PF_NoTexture|PF_Modulated|PF_Translucent|PF_NoDepthTest);
}
@ -850,24 +839,22 @@ void HWR_DrawFadeFill(INT32 x, INT32 y, INT32 w, INT32 h, INT32 color, UINT16 ac
v[0].y = v[1].y = fy;
v[2].y = v[3].y = fy - fh;
//Hurdler: do we still use this argb color? if not, we should remove it
v[0].argb = v[1].argb = v[2].argb = v[3].argb = 0xff00ff00; //;
v[0].z = v[1].z = v[2].z = v[3].z = 1.0f;
v[0].sow = v[3].sow = 0.0f;
v[2].sow = v[1].sow = 1.0f;
v[0].tow = v[1].tow = 0.0f;
v[2].tow = v[3].tow = 1.0f;
v[0].s = v[3].s = 0.0f;
v[2].s = v[1].s = 1.0f;
v[0].t = v[1].t = 0.0f;
v[2].t = v[3].t = 1.0f;
if (actualcolor & 0xFF00) // Do COLORMAP fade.
{
Surf.FlatColor.rgba = UINT2RGBA(0x01010160);
Surf.FlatColor.s.alpha = (strength*8);
Surf.PolyColor.rgba = UINT2RGBA(0x01010160);
Surf.PolyColor.s.alpha = (strength*8);
}
else // Do TRANSMAP** fade.
{
Surf.FlatColor.rgba = V_GetColor(actualcolor).rgba;
Surf.FlatColor.s.alpha = softwaretranstogl[strength];
Surf.PolyColor.rgba = V_GetColor(actualcolor).rgba;
Surf.PolyColor.s.alpha = softwaretranstogl[strength];
}
HWD.pfnDrawPolygon(&Surf, v, 4, PF_NoTexture|PF_Modulated|PF_Translucent|PF_NoDepthTest);
}
@ -888,13 +875,13 @@ void HWR_DrawConsoleBack(UINT32 color, INT32 height)
v[2].y = v[3].y = 1.0f;
v[0].z = v[1].z = v[2].z = v[3].z = 1.0f;
v[0].sow = v[3].sow = 0.0f;
v[2].sow = v[1].sow = 1.0f;
v[0].tow = v[1].tow = 1.0f;
v[2].tow = v[3].tow = 0.0f;
v[0].s = v[3].s = 0.0f;
v[2].s = v[1].s = 1.0f;
v[0].t = v[1].t = 1.0f;
v[2].t = v[3].t = 0.0f;
Surf.FlatColor.rgba = UINT2RGBA(color);
Surf.FlatColor.s.alpha = 0x80;
Surf.PolyColor.rgba = UINT2RGBA(color);
Surf.PolyColor.s.alpha = 0x80;
HWD.pfnDrawPolygon(&Surf, v, 4, PF_NoTexture|PF_Modulated|PF_Translucent|PF_NoDepthTest);
}
@ -904,7 +891,11 @@ void HWR_DrawTutorialBack(UINT32 color, INT32 boxheight)
{
FOutVector v[4];
FSurfaceInfo Surf;
INT32 height = (boxheight * 4) + (boxheight/2)*5; // 4 lines of space plus gaps between and some leeway
INT32 height;
if (boxheight < 0)
height = -boxheight;
else
height = (boxheight * 4) + (boxheight/2)*5; // 4 lines of space plus gaps between and some leeway
// setup some neat-o translucency effect
@ -914,13 +905,13 @@ void HWR_DrawTutorialBack(UINT32 color, INT32 boxheight)
v[2].y = v[3].y = -1.0f+((height<<1)/(float)vid.height);
v[0].z = v[1].z = v[2].z = v[3].z = 1.0f;
v[0].sow = v[3].sow = 0.0f;
v[2].sow = v[1].sow = 1.0f;
v[0].tow = v[1].tow = 1.0f;
v[2].tow = v[3].tow = 0.0f;
v[0].s = v[3].s = 0.0f;
v[2].s = v[1].s = 1.0f;
v[0].t = v[1].t = 1.0f;
v[2].t = v[3].t = 0.0f;
Surf.FlatColor.rgba = UINT2RGBA(color);
Surf.FlatColor.s.alpha = (color == 0 ? 0xC0 : 0x80); // make black darker, like software
Surf.PolyColor.rgba = UINT2RGBA(color);
Surf.PolyColor.s.alpha = (color == 0 ? 0xC0 : 0x80); // make black darker, like software
HWD.pfnDrawPolygon(&Surf, v, 4, PF_NoTexture|PF_Modulated|PF_Translucent|PF_NoDepthTest);
}
@ -1232,17 +1223,15 @@ void HWR_DrawConsoleFill(INT32 x, INT32 y, INT32 w, INT32 h, INT32 color, UINT32
v[0].y = v[1].y = fy;
v[2].y = v[3].y = fy - fh;
//Hurdler: do we still use this argb color? if not, we should remove it
v[0].argb = v[1].argb = v[2].argb = v[3].argb = 0xff00ff00; //;
v[0].z = v[1].z = v[2].z = v[3].z = 1.0f;
v[0].sow = v[3].sow = 0.0f;
v[2].sow = v[1].sow = 1.0f;
v[0].tow = v[1].tow = 0.0f;
v[2].tow = v[3].tow = 1.0f;
v[0].s = v[3].s = 0.0f;
v[2].s = v[1].s = 1.0f;
v[0].t = v[1].t = 0.0f;
v[2].t = v[3].t = 1.0f;
Surf.FlatColor.rgba = UINT2RGBA(actualcolor);
Surf.FlatColor.s.alpha = 0x80;
Surf.PolyColor.rgba = UINT2RGBA(actualcolor);
Surf.PolyColor.s.alpha = 0x80;
HWD.pfnDrawPolygon(&Surf, v, 4, PF_NoTexture|PF_Modulated|PF_Translucent|PF_NoDepthTest);
}
@ -1412,16 +1401,14 @@ void HWR_DrawFill(INT32 x, INT32 y, INT32 w, INT32 h, INT32 color)
v[0].y = v[1].y = fy;
v[2].y = v[3].y = fy - fh;
//Hurdler: do we still use this argb color? if not, we should remove it
v[0].argb = v[1].argb = v[2].argb = v[3].argb = 0xff00ff00; //;
v[0].z = v[1].z = v[2].z = v[3].z = 1.0f;
v[0].sow = v[3].sow = 0.0f;
v[2].sow = v[1].sow = 1.0f;
v[0].tow = v[1].tow = 0.0f;
v[2].tow = v[3].tow = 1.0f;
v[0].s = v[3].s = 0.0f;
v[2].s = v[1].s = 1.0f;
v[0].t = v[1].t = 0.0f;
v[2].t = v[3].t = 1.0f;
Surf.FlatColor = V_GetColor(color);
Surf.PolyColor = V_GetColor(color);
HWD.pfnDrawPolygon(&Surf, v, 4,
PF_Modulated|PF_NoTexture|PF_NoDepthTest);

View file

@ -1,20 +1,13 @@
// Emacs style mode select -*- C++ -*-
// SONIC ROBO BLAST 2
//-----------------------------------------------------------------------------
//
// Copyright (C) 1998-2000 by DooM Legacy Team.
// Copyright (C) 1999-2020 by Sonic Team Junior.
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
// See the 'LICENSE' file for more details.
//-----------------------------------------------------------------------------
/// \file
/// \file hw_drv.h
/// \brief imports/exports for the 3D hardware low-level interface API
#ifndef __HWR_DRV_H__
@ -32,7 +25,7 @@
// STANDARD DLL EXPORTS
// ==========================================================================
EXPORT boolean HWRAPI(Init) (I_Error_t ErrorFunction);
EXPORT boolean HWRAPI(Init) (void);
#ifndef HAVE_SDL
EXPORT void HWRAPI(Shutdown) (void);
#endif
@ -43,10 +36,12 @@ EXPORT void HWRAPI(SetPalette) (RGBA_t *ppal);
EXPORT void HWRAPI(FinishUpdate) (INT32 waitvbl);
EXPORT void HWRAPI(Draw2DLine) (F2DCoord *v1, F2DCoord *v2, RGBA_t Color);
EXPORT void HWRAPI(DrawPolygon) (FSurfaceInfo *pSurf, FOutVector *pOutVerts, FUINT iNumPts, FBITFIELD PolyFlags);
EXPORT void HWRAPI(DrawIndexedTriangles) (FSurfaceInfo *pSurf, FOutVector *pOutVerts, FUINT iNumPts, FBITFIELD PolyFlags, UINT32 *IndexArray);
EXPORT void HWRAPI(RenderSkyDome) (INT32 tex, INT32 texture_width, INT32 texture_height, FTransform transform);
EXPORT void HWRAPI(SetBlend) (FBITFIELD PolyFlags);
EXPORT void HWRAPI(ClearBuffer) (FBOOLEAN ColorMask, FBOOLEAN DepthMask, FRGBAFloat *ClearColor);
EXPORT void HWRAPI(SetTexture) (FTextureInfo *TexInfo);
EXPORT void HWRAPI(UpdateTexture) (FTextureInfo *TexInfo);
EXPORT void HWRAPI(ReadRect) (INT32 x, INT32 y, INT32 width, INT32 height, INT32 dst_stride, UINT16 *dst_data);
EXPORT void HWRAPI(GClipRect) (INT32 minx, INT32 miny, INT32 maxx, INT32 maxy, float nearclip);
EXPORT void HWRAPI(ClearMipMapCache) (void);
@ -55,14 +50,11 @@ EXPORT void HWRAPI(ClearMipMapCache) (void);
EXPORT void HWRAPI(SetSpecialState) (hwdspecialstate_t IdState, INT32 Value);
//Hurdler: added for new development
EXPORT void HWRAPI(DrawModel) (model_t *model, INT32 frameIndex, INT32 duration, INT32 tics, INT32 nextFrameIndex, FTransform *pos, float scale, UINT8 flipped, UINT8 *color);
EXPORT void HWRAPI(DrawModel) (model_t *model, INT32 frameIndex, INT32 duration, INT32 tics, INT32 nextFrameIndex, FTransform *pos, float scale, UINT8 flipped, UINT8 hflipped, FSurfaceInfo *Surface);
EXPORT void HWRAPI(CreateModelVBOs) (model_t *model);
EXPORT void HWRAPI(SetTransform) (FTransform *ptransform);
EXPORT INT32 HWRAPI(GetTextureUsed) (void);
EXPORT INT32 HWRAPI(GetRenderVersion) (void);
#define SCREENVERTS 10
EXPORT void HWRAPI(PostImgRedraw) (float points[SCREENVERTS][SCREENVERTS][2]);
EXPORT void HWRAPI(FlushScreenTextures) (void);
EXPORT void HWRAPI(StartScreenWipe) (void);
EXPORT void HWRAPI(EndScreenWipe) (void);
@ -71,6 +63,20 @@ EXPORT void HWRAPI(DrawIntermissionBG) (void);
EXPORT void HWRAPI(MakeScreenTexture) (void);
EXPORT void HWRAPI(MakeScreenFinalTexture) (void);
EXPORT void HWRAPI(DrawScreenFinalTexture) (int width, int height);
#define SCREENVERTS 10
EXPORT void HWRAPI(PostImgRedraw) (float points[SCREENVERTS][SCREENVERTS][2]);
// jimita
EXPORT boolean HWRAPI(LoadShaders) (void);
EXPORT void HWRAPI(KillShaders) (void);
EXPORT void HWRAPI(SetShader) (int shader);
EXPORT void HWRAPI(UnSetShader) (void);
EXPORT void HWRAPI(SetShaderInfo) (hwdshaderinfo_t info, INT32 value);
EXPORT void HWRAPI(LoadCustomShader) (int number, char *shader, size_t size, boolean fragment);
EXPORT boolean HWRAPI(InitCustomShaders) (void);
// ==========================================================================
// HWR DRIVER OBJECT, FOR CLIENT PROGRAM
// ==========================================================================
@ -84,10 +90,12 @@ struct hwdriver_s
FinishUpdate pfnFinishUpdate;
Draw2DLine pfnDraw2DLine;
DrawPolygon pfnDrawPolygon;
DrawIndexedTriangles pfnDrawIndexedTriangles;
RenderSkyDome pfnRenderSkyDome;
SetBlend pfnSetBlend;
ClearBuffer pfnClearBuffer;
SetTexture pfnSetTexture;
UpdateTexture pfnUpdateTexture;
ReadRect pfnReadRect;
GClipRect pfnGClipRect;
ClearMipMapCache pfnClearMipMapCache;
@ -96,7 +104,6 @@ struct hwdriver_s
CreateModelVBOs pfnCreateModelVBOs;
SetTransform pfnSetTransform;
GetTextureUsed pfnGetTextureUsed;
GetRenderVersion pfnGetRenderVersion;
#ifdef _WINDOWS
GetModeList pfnGetModeList;
#endif
@ -112,13 +119,19 @@ struct hwdriver_s
MakeScreenTexture pfnMakeScreenTexture;
MakeScreenFinalTexture pfnMakeScreenFinalTexture;
DrawScreenFinalTexture pfnDrawScreenFinalTexture;
LoadShaders pfnLoadShaders;
KillShaders pfnKillShaders;
SetShader pfnSetShader;
UnSetShader pfnUnSetShader;
SetShaderInfo pfnSetShaderInfo;
LoadCustomShader pfnLoadCustomShader;
InitCustomShaders pfnInitCustomShaders;
};
extern struct hwdriver_s hwdriver;
//Hurdler: 16/10/99: added for OpenGL gamma correction
//extern RGBA_t gamma_correction;
#define HWD hwdriver
#endif //not defined _CREATE_DLL_

View file

@ -1,19 +1,12 @@
// Emacs style mode select -*- C++ -*-
// SONIC ROBO BLAST 2
//-----------------------------------------------------------------------------
//
// Copyright (C) 1998-2000 by DooM Legacy Team.
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
// See the 'LICENSE' file for more details.
//-----------------------------------------------------------------------------
/// \file
/// \file hw_glide.h
/// \brief Declaration needed by Glide renderer
/// !!! To be replaced by our own def in the future !!!
@ -25,26 +18,6 @@
typedef unsigned long FxU32;
typedef long FxI32;
typedef FxI32 GrAspectRatio_t;
#define GR_ASPECT_LOG2_8x1 3 /* 8W x 1H */
#define GR_ASPECT_LOG2_4x1 2 /* 4W x 1H */
#define GR_ASPECT_LOG2_2x1 1 /* 2W x 1H */
#define GR_ASPECT_LOG2_1x1 0 /* 1W x 1H */
#define GR_ASPECT_LOG2_1x2 -1 /* 1W x 2H */
#define GR_ASPECT_LOG2_1x4 -2 /* 1W x 4H */
#define GR_ASPECT_LOG2_1x8 -3 /* 1W x 8H */
typedef FxI32 GrLOD_t;
#define GR_LOD_LOG2_256 0x8
#define GR_LOD_LOG2_128 0x7
#define GR_LOD_LOG2_64 0x6
#define GR_LOD_LOG2_32 0x5
#define GR_LOD_LOG2_16 0x4
#define GR_LOD_LOG2_8 0x3
#define GR_LOD_LOG2_4 0x2
#define GR_LOD_LOG2_2 0x1
#define GR_LOD_LOG2_1 0x0
typedef FxI32 GrTextureFormat_t;
#define GR_TEXFMT_ALPHA_8 0x2 /* (0..0xFF) alpha */
#define GR_TEXFMT_INTENSITY_8 0x3 /* (0..0xFF) intensity */
@ -59,11 +32,6 @@ typedef FxI32 GrTextureFormat_t;
typedef struct
{
#ifdef GLIDE_API_COMPATIBILITY
GrLOD_t smallLodLog2;
GrLOD_t largeLodLog2;
GrAspectRatio_t aspectRatioLog2;
#endif
GrTextureFormat_t format;
void *data;
} GrTexInfo;

View file

@ -1,20 +1,13 @@
// Emacs style mode select -*- C++ -*-
// SONIC ROBO BLAST 2
//-----------------------------------------------------------------------------
//
// Copyright (C) 1998-2000 by DooM Legacy Team.
// Copyright (C) 1999-2020 by Sonic Team Junior.
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
// See the 'LICENSE' file for more details.
//-----------------------------------------------------------------------------
/// \file
/// \file hw_glob.h
/// \brief globals (shared data & code) for hw_ modules
#ifndef _HWR_GLOB_H_
@ -68,9 +61,6 @@ typedef struct
// equivalent of the software renderer's vissprites
typedef struct gr_vissprite_s
{
// Doubly linked list
struct gr_vissprite_s *prev;
struct gr_vissprite_s *next;
float x1, x2;
float tz, ty;
//lumpnum_t patchlumpnum;
@ -118,11 +108,6 @@ void HWR_GetFadeMask(lumpnum_t fademasklumpnum);
// --------
// hw_draw.c
// --------
extern float gr_patch_scalex;
extern float gr_patch_scaley;
extern consvar_t cv_grrounddown; // on/off
extern INT32 patchformat;
extern INT32 textureformat;

View file

@ -1,19 +1,13 @@
// Emacs style mode select -*- C++ -*-
// SONIC ROBO BLAST 2
//-----------------------------------------------------------------------------
//
// Copyright (C) 1998-2000 by DooM Legacy Team.
// Copyright (C) 1999-2020 by Sonic Team Junior.
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
// See the 'LICENSE' file for more details.
//-----------------------------------------------------------------------------
/// \file
/// \file hw_light.c
/// \brief Corona/Dynamic/Static lighting add on by Hurdler
/// !!! Under construction !!!
@ -611,6 +605,9 @@ light_t *t_lspr[NUMSPRITES] =
&lspr[NOLIGHT], // SPR_GFZD
&lspr[NOLIGHT], // SPR_BRIC
&lspr[NOLIGHT], // SPR_WDDB
&lspr[NOLIGHT], // SPR_BRIR
&lspr[NOLIGHT], // SPR_BRIB
&lspr[NOLIGHT], // SPR_BRIY
// Gravity Well Objects
&lspr[NOLIGHT], // SPR_GWLG
@ -879,19 +876,19 @@ void HWR_WallLighting(FOutVector *wlVerts)
#endif
for (i = 0; i < 4; i++)
{
wlVerts[i].sow = (float)(0.5f + d[i]*s);
wlVerts[i].tow = (float)(0.5f + (wlVerts[i].y-LIGHT_POS(j).y)*s*1.2f);
wlVerts[i].s = (float)(0.5f + d[i]*s);
wlVerts[i].t = (float)(0.5f + (wlVerts[i].y-LIGHT_POS(j).y)*s*1.2f);
}
HWR_SetLight();
Surf.FlatColor.rgba = LONG(dynlights->p_lspr[j]->dynamic_color);
Surf.PolyColor.rgba = LONG(dynlights->p_lspr[j]->dynamic_color);
#ifdef DL_HIGH_QUALITY
Surf.FlatColor.s.alpha = (UINT8)((1-dist_p2d/DL_SQRRADIUS(j))*Surf.FlatColor.s.alpha);
Surf.PolyColor.s.alpha = (UINT8)((1-dist_p2d/DL_SQRRADIUS(j))*Surf.PolyColor.s.alpha);
#endif
// next state is null so fade out with alpha
if (dynlights->mo[j]->state->nextstate == S_NULL)
Surf.FlatColor.s.alpha = (UINT8)(((float)dynlights->mo[j]->tics/(float)dynlights->mo[j]->state->tics)*Surf.FlatColor.s.alpha);
Surf.PolyColor.s.alpha = (UINT8)(((float)dynlights->mo[j]->tics/(float)dynlights->mo[j]->state->tics)*Surf.PolyColor.s.alpha);
HWD.pfnDrawPolygon (&Surf, wlVerts, 4, LIGHTMAPFLAGS);
@ -948,19 +945,19 @@ void HWR_PlaneLighting(FOutVector *clVerts, int nrClipVerts)
#endif
for (i = 0; i < nrClipVerts; i++)
{
clVerts[i].sow = 0.5f + (clVerts[i].x-LIGHT_POS(j).x)*s;
clVerts[i].tow = 0.5f + (clVerts[i].z-LIGHT_POS(j).z)*s*1.2f;
clVerts[i].s = 0.5f + (clVerts[i].x-LIGHT_POS(j).x)*s;
clVerts[i].t = 0.5f + (clVerts[i].z-LIGHT_POS(j).z)*s*1.2f;
}
HWR_SetLight();
Surf.FlatColor.rgba = LONG(dynlights->p_lspr[j]->dynamic_color);
Surf.PolyColor.rgba = LONG(dynlights->p_lspr[j]->dynamic_color);
#ifdef DL_HIGH_QUALITY
Surf.FlatColor.s.alpha = (unsigned char)((1 - dist_p2d/DL_SQRRADIUS(j))*Surf.FlatColor.s.alpha);
Surf.PolyColor.s.alpha = (unsigned char)((1 - dist_p2d/DL_SQRRADIUS(j))*Surf.PolyColor.s.alpha);
#endif
// next state is null so fade out with alpha
if ((dynlights->mo[j]->state->nextstate == S_NULL))
Surf.FlatColor.s.alpha = (unsigned char)(((float)dynlights->mo[j]->tics/(float)dynlights->mo[j]->state->tics)*Surf.FlatColor.s.alpha);
Surf.PolyColor.s.alpha = (unsigned char)(((float)dynlights->mo[j]->tics/(float)dynlights->mo[j]->state->tics)*Surf.PolyColor.s.alpha);
HWD.pfnDrawPolygon (&Surf, clVerts, nrClipVerts, LIGHTMAPFLAGS);
@ -1027,11 +1024,11 @@ void HWR_DoCoronasLighting(FOutVector *outVerts, gr_vissprite_t *spr)
// more realistique corona !
if (cz >= 255*8+250)
return;
Surf.FlatColor.rgba = p_lspr->corona_color;
Surf.PolyColor.rgba = p_lspr->corona_color;
if (cz > 250.0f)
Surf.FlatColor.s.alpha = 0xff-((int)cz-250)/8;
Surf.PolyColor.s.alpha = 0xff-((int)cz-250)/8;
else
Surf.FlatColor.s.alpha = 0xff;
Surf.PolyColor.s.alpha = 0xff;
// do not be hide by sprite of the light itself !
cz = cz - 2.0f;
@ -1043,19 +1040,19 @@ void HWR_DoCoronasLighting(FOutVector *outVerts, gr_vissprite_t *spr)
// car comme l'offset est minime sa ce voit pas !
light[0].x = cx-size; light[0].z = cz;
light[0].y = cy-size*1.33f+p_lspr->light_yoffset;
light[0].sow = 0.0f; light[0].tow = 0.0f;
light[0].s = 0.0f; light[0].t = 0.0f;
light[1].x = cx+size; light[1].z = cz;
light[1].y = cy-size*1.33f+p_lspr->light_yoffset;
light[1].sow = 1.0f; light[1].tow = 0.0f;
light[1].s = 1.0f; light[1].t = 0.0f;
light[2].x = cx+size; light[2].z = cz;
light[2].y = cy+size*1.33f+p_lspr->light_yoffset;
light[2].sow = 1.0f; light[2].tow = 1.0f;
light[2].s = 1.0f; light[2].t = 1.0f;
light[3].x = cx-size; light[3].z = cz;
light[3].y = cy+size*1.33f+p_lspr->light_yoffset;
light[3].sow = 0.0f; light[3].tow = 1.0f;
light[3].s = 0.0f; light[3].t = 1.0f;
HWR_GetPic(coronalumpnum); /// \todo use different coronas
@ -1101,11 +1098,11 @@ void HWR_DrawCoronas(void)
// more realistique corona !
if (cz >= 255*8+250)
continue;
Surf.FlatColor.rgba = p_lspr->corona_color;
Surf.PolyColor.rgba = p_lspr->corona_color;
if (cz > 250.0f)
Surf.FlatColor.s.alpha = (UINT8)(0xff-(UINT8)(((int)cz-250)/8));
Surf.PolyColor.s.alpha = (UINT8)(0xff-(UINT8)(((int)cz-250)/8));
else
Surf.FlatColor.s.alpha = 0xff;
Surf.PolyColor.s.alpha = 0xff;
switch (p_lspr->type)
{
@ -1113,7 +1110,7 @@ void HWR_DrawCoronas(void)
size = p_lspr->corona_radius * ((cz+120.0f)/950.0f); // d'ou vienne ces constante ?
break;
case ROCKET_SPR:
Surf.FlatColor.s.alpha = (UINT8)((M_RandomByte()>>1)&0xff);
Surf.PolyColor.s.alpha = (UINT8)((M_RandomByte()>>1)&0xff);
// don't need a break
case CORONA_SPR:
size = p_lspr->corona_radius * ((cz+60.0f)/100.0f); // d'ou vienne ces constante ?
@ -1133,19 +1130,19 @@ void HWR_DrawCoronas(void)
light[0].x = cx-size; light[0].z = cz;
light[0].y = cy-size*1.33f;
light[0].sow = 0.0f; light[0].tow = 0.0f;
light[0].s = 0.0f; light[0].t = 0.0f;
light[1].x = cx+size; light[1].z = cz;
light[1].y = cy-size*1.33f;
light[1].sow = 1.0f; light[1].tow = 0.0f;
light[1].s = 1.0f; light[1].t = 0.0f;
light[2].x = cx+size; light[2].z = cz;
light[2].y = cy+size*1.33f;
light[2].sow = 1.0f; light[2].tow = 1.0f;
light[2].s = 1.0f; light[2].t = 1.0f;
light[3].x = cx-size; light[3].z = cz;
light[3].y = cy+size*1.33f;
light[3].sow = 0.0f; light[3].tow = 1.0f;
light[3].s = 0.0f; light[3].t = 1.0f;
HWD.pfnDrawPolygon (&Surf, light, 4, PF_Modulated | PF_Additive | PF_Clip | PF_NoDepthTest | PF_Corona);
}
@ -1254,11 +1251,6 @@ static void HWR_SetLight(void)
lightmappatch.height = 128;
lightmappatch.mipmap->width = 128;
lightmappatch.mipmap->height = 128;
#ifdef GLIDE_API_COMPATIBILITY
lightmappatch.mipmap->grInfo.smallLodLog2 = GR_LOD_LOG2_128;
lightmappatch.mipmap->grInfo.largeLodLog2 = GR_LOD_LOG2_128;
lightmappatch.mipmap->grInfo.aspectRatioLog2 = GR_ASPECT_LOG2_1x1;
#endif
lightmappatch.mipmap->flags = 0; //TF_WRAPXY; // DEBUG: view the overdraw !
}
HWD.pfnSetTexture(lightmappatch.mipmap);

View file

@ -1,19 +1,13 @@
// Emacs style mode select -*- C++ -*-
// SONIC ROBO BLAST 2
//-----------------------------------------------------------------------------
//
// Copyright (C) 1998-2000 by DooM Legacy Team.
// Copyright (C) 1999-2020 by Sonic Team Junior.
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
// See the 'LICENSE' file for more details.
//-----------------------------------------------------------------------------
/// \file
/// \file hw_light.h
/// \brief Dynamic lighting & coronas add on by Hurdler
#ifndef _HW_LIGHTS_

File diff suppressed because it is too large Load diff

View file

@ -1,20 +1,13 @@
// Emacs style mode select -*- C++ -*-
// SONIC ROBO BLAST 2
//-----------------------------------------------------------------------------
//
// Copyright (C) 1998-2000 by DooM Legacy Team.
// Copyright (C) 1999-2020 by Sonic Team Junior.
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
// See the 'LICENSE' file for more details.
//-----------------------------------------------------------------------------
/// \file
/// \file hw_main.h
/// \brief 3D render mode functions
#ifndef __HWR_MAIN_H__
@ -73,8 +66,11 @@ void HWR_MakeScreenFinalTexture(void);
void HWR_DrawScreenFinalTexture(int width, int height);
// This stuff is put here so MD2's can use them
UINT32 HWR_Lighting(INT32 light, UINT32 color, UINT32 fadecolor, boolean fogblockpoly, boolean plane);
FUNCMATH UINT8 LightLevelToLum(INT32 l);
void HWR_Lighting(FSurfaceInfo *Surface, INT32 light_level, extracolormap_t *colormap);
UINT8 HWR_FogBlockAlpha(INT32 light, extracolormap_t *colormap); // Let's see if this can work
void HWR_ReadShaders(UINT16 wadnum, boolean PK3);
boolean HWR_LoadShaders(void);
extern CV_PossibleValue_t granisotropicmode_cons_t[];
@ -84,21 +80,23 @@ extern consvar_t cv_grstaticlighting;
extern consvar_t cv_grcoronas;
extern consvar_t cv_grcoronasize;
#endif
extern consvar_t cv_grshaders;
extern consvar_t cv_grmodels;
extern consvar_t cv_grmodelinterpolation;
extern consvar_t cv_grmodellighting;
extern consvar_t cv_grfog;
extern consvar_t cv_grfogcolor;
extern consvar_t cv_grfogdensity;
extern consvar_t cv_grsoftwarefog;
extern consvar_t cv_grfiltermode;
extern consvar_t cv_granisotropicmode;
extern consvar_t cv_grcorrecttricks;
extern consvar_t cv_fovchange;
extern consvar_t cv_grsolvetjoin;
extern consvar_t cv_grshearing;
extern consvar_t cv_grspritebillboarding;
extern consvar_t cv_grskydome;
extern consvar_t cv_grfakecontrast;
extern consvar_t cv_grslopecontrast;
extern consvar_t cv_grbatching;
extern float gr_viewwidth, gr_viewheight, gr_baseviewwindowy;
@ -108,4 +106,24 @@ extern float gr_viewwindowx, gr_basewindowcentery;
extern fixed_t *hwbbox;
extern FTransform atransform;
// Render stats
extern int rs_hw_nodesorttime;
extern int rs_hw_nodedrawtime;
extern int rs_hw_spritesorttime;
extern int rs_hw_spritedrawtime;
// Render stats for batching
extern int rs_hw_numpolys;
extern int rs_hw_numverts;
extern int rs_hw_numcalls;
extern int rs_hw_numshaders;
extern int rs_hw_numtextures;
extern int rs_hw_numpolyflags;
extern int rs_hw_numcolors;
extern int rs_hw_batchsorttime;
extern int rs_hw_batchdrawtime;
extern boolean gr_shadersavailable;
#endif

View file

@ -1,23 +1,16 @@
// Emacs style mode select -*- C++ -*-
// SONIC ROBO BLAST 2
//-----------------------------------------------------------------------------
//
// Copyright (C) 1998-2000 by DooM Legacy Team.
// Copyright (C) 1999-2020 by Sonic Team Junior.
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
// See the 'LICENSE' file for more details.
//-----------------------------------------------------------------------------
/// \file
/// \brief MD2 Handling
/// \file hw_md2.c
/// \brief 3D Model Handling
/// Inspired from md2.c by Mete Ciragan (mete@swissquake.ch)
#ifdef __GNUC__
#include <unistd.h>
#endif
@ -382,7 +375,10 @@ static void md2_loadTexture(md2_t *model)
#endif
grpatch->mipmap->grInfo.format = PCX_Load(filename, &w, &h, grpatch);
if (grpatch->mipmap->grInfo.format == 0)
{
model->notexturefile = true; // mark it so its not searched for again repeatedly
return;
}
grpatch->mipmap->downloaded = 0;
grpatch->mipmap->flags = 0;
@ -400,13 +396,6 @@ static void md2_loadTexture(md2_t *model)
V_CubeApply(&image->s.red, &image->s.green, &image->s.blue);
image++;
}
#ifdef GLIDE_API_COMPATIBILITY
// not correct!
grpatch->mipmap->grInfo.smallLodLog2 = GR_LOD_LOG2_256;
grpatch->mipmap->grInfo.largeLodLog2 = GR_LOD_LOG2_256;
grpatch->mipmap->grInfo.aspectRatioLog2 = GR_ASPECT_LOG2_1x1;
#endif
}
HWD.pfnSetTexture(grpatch->mipmap);
}
@ -444,6 +433,7 @@ static void md2_loadBlendTexture(md2_t *model)
grpatch->mipmap->grInfo.format = PCX_Load(filename, &w, &h, grpatch);
if (grpatch->mipmap->grInfo.format == 0)
{
model->noblendfile = true; // mark it so its not searched for again repeatedly
Z_Free(filename);
return;
}
@ -455,13 +445,6 @@ static void md2_loadBlendTexture(md2_t *model)
grpatch->height = (INT16)h;
grpatch->mipmap->width = (UINT16)w;
grpatch->mipmap->height = (UINT16)h;
#ifdef GLIDE_API_COMPATIBILITY
// not correct!
grpatch->mipmap->grInfo.smallLodLog2 = GR_LOD_LOG2_256;
grpatch->mipmap->grInfo.largeLodLog2 = GR_LOD_LOG2_256;
grpatch->mipmap->grInfo.aspectRatioLog2 = GR_ASPECT_LOG2_1x1;
#endif
}
HWD.pfnSetTexture(grpatch->mipmap); // We do need to do this so that it can be cleared and knows to recreate it when necessary
@ -486,6 +469,8 @@ void HWR_InitModels(void)
md2_playermodels[s].scale = -1.0f;
md2_playermodels[s].model = NULL;
md2_playermodels[s].grpatch = NULL;
md2_playermodels[s].notexturefile = false;
md2_playermodels[s].noblendfile = false;
md2_playermodels[s].skin = -1;
md2_playermodels[s].notfound = true;
md2_playermodels[s].error = false;
@ -495,6 +480,8 @@ void HWR_InitModels(void)
md2_models[i].scale = -1.0f;
md2_models[i].model = NULL;
md2_models[i].grpatch = NULL;
md2_models[i].notexturefile = false;
md2_models[i].noblendfile = false;
md2_models[i].skin = -1;
md2_models[i].notfound = true;
md2_models[i].error = false;
@ -677,12 +664,12 @@ spritemodelfound:
#define SETBRIGHTNESS(brightness,r,g,b) \
brightness = (UINT8)(((1063*(UINT16)(r))/5000) + ((3576*(UINT16)(g))/5000) + ((361*(UINT16)(b))/5000))
static void HWR_CreateBlendedTexture(GLPatch_t *gpatch, GLPatch_t *blendgpatch, GLMipmap_t *grmip, INT32 skinnum, skincolors_t color)
static void HWR_CreateBlendedTexture(GLPatch_t *gpatch, GLPatch_t *blendgpatch, GLMipmap_t *grmip, INT32 skinnum, skincolornum_t color)
{
UINT16 w = gpatch->width, h = gpatch->height;
UINT32 size = w*h;
RGBA_t *image, *blendimage, *cur, blendcolor;
UINT8 translation[16]; // First the color index
UINT16 translation[16]; // First the color index
UINT8 cutoff[16]; // Brightness cutoff before using the next color
UINT8 translen = 0;
UINT8 i;
@ -718,16 +705,16 @@ static void HWR_CreateBlendedTexture(GLPatch_t *gpatch, GLPatch_t *blendgpatch,
if (skinnum == TC_METALSONIC)
color = SKINCOLOR_COBALT;
if (color != SKINCOLOR_NONE)
if (color != SKINCOLOR_NONE && color < numskincolors)
{
UINT8 numdupes = 1;
translation[translen] = Color_Index[color-1][0];
translation[translen] = skincolors[color].ramp[0];
cutoff[translen] = 255;
for (i = 1; i < 16; i++)
{
if (translation[translen] == Color_Index[color-1][i])
if (translation[translen] == skincolors[color].ramp[i])
{
numdupes++;
continue;
@ -741,7 +728,7 @@ static void HWR_CreateBlendedTexture(GLPatch_t *gpatch, GLPatch_t *blendgpatch,
numdupes = 1;
translen++;
translation[translen] = (UINT8)Color_Index[color-1][i];
translation[translen] = (UINT16)skincolors[color].ramp[i];
}
translen++;
@ -949,11 +936,19 @@ static void HWR_CreateBlendedTexture(GLPatch_t *gpatch, GLPatch_t *blendgpatch,
blendcolor = V_GetColor(translation[firsti]);
if (secondi >= translen)
mul = 0;
if (mul > 0) // If it's 0, then we only need the first color.
{
if (secondi >= translen) // blend to black
#if 0
if (secondi >= translen)
{
// blend to black
nextcolor = V_GetColor(31);
}
else
#endif
nextcolor = V_GetColor(translation[secondi]);
// Find difference between points
@ -1043,7 +1038,7 @@ skippixel:
#undef SETBRIGHTNESS
static void HWR_GetBlendedTexture(GLPatch_t *gpatch, GLPatch_t *blendgpatch, INT32 skinnum, const UINT8 *colormap, skincolors_t color)
static void HWR_GetBlendedTexture(GLPatch_t *gpatch, GLPatch_t *blendgpatch, INT32 skinnum, const UINT8 *colormap, skincolornum_t color)
{
// mostly copied from HWR_GetMappedPatch, hence the similarities and comment
GLMipmap_t *grmip, *newmip;
@ -1188,15 +1183,14 @@ static UINT8 HWR_GetModelSprite2(md2_t *md2, skin_t *skin, UINT8 spr2, player_t
boolean HWR_DrawModel(gr_vissprite_t *spr)
{
FSurfaceInfo Surf;
md2_t *md2;
char filename[64];
INT32 frame = 0;
INT32 nextFrame = -1;
UINT8 spr2 = 0;
FTransform p;
md2_t *md2;
UINT8 color[4];
FSurfaceInfo Surf;
if (!cv_grmodels.value)
return false;
@ -1235,13 +1229,10 @@ boolean HWR_DrawModel(gr_vissprite_t *spr)
colormap = sector->extra_colormap;
}
if (colormap)
Surf.FlatColor.rgba = HWR_Lighting(lightlevel, colormap->rgba, colormap->fadergba, false, false);
else
Surf.FlatColor.rgba = HWR_Lighting(lightlevel, NORMALFOG, FADEFOG, false, false);
HWR_Lighting(&Surf, lightlevel, colormap);
}
else
Surf.FlatColor.rgba = 0xFFFFFFFF;
Surf.PolyColor.rgba = 0xFFFFFFFF;
// Look at HWR_ProjectSprite for more
{
@ -1251,6 +1242,7 @@ boolean HWR_DrawModel(gr_vissprite_t *spr)
//mdlframe_t *next = NULL;
const boolean papersprite = (spr->mobj->frame & FF_PAPERSPRITE);
const UINT8 flip = (UINT8)(!(spr->mobj->eflags & MFE_VERTICALFLIP) != !(spr->mobj->frame & FF_VERTICALFLIP));
const UINT8 hflip = (UINT8)(!(spr->mobj->mirrored) != !(spr->mobj->frame & FF_HORIZONTALFLIP));
spritedef_t *sprdef;
spriteframe_t *sprframe;
spriteinfo_t *sprinfo;
@ -1263,11 +1255,11 @@ boolean HWR_DrawModel(gr_vissprite_t *spr)
//durs = tics;
if (spr->mobj->flags2 & MF2_SHADOW)
Surf.FlatColor.s.alpha = 0x40;
Surf.PolyColor.s.alpha = 0x40;
else if (spr->mobj->frame & FF_TRANSMASK)
HWR_TranstableToAlpha((spr->mobj->frame & FF_TRANSMASK)>>FF_TRANSSHIFT, &Surf);
else
Surf.FlatColor.s.alpha = 0xFF;
Surf.PolyColor.s.alpha = 0xFF;
// dont forget to enabled the depth test because we can't do this like
// before: polygons models are not sorted
@ -1315,12 +1307,14 @@ boolean HWR_DrawModel(gr_vissprite_t *spr)
finalscale = md2->scale;
//Hurdler: arf, I don't like that implementation at all... too much crappy
gpatch = md2->grpatch;
if (!gpatch || !gpatch->mipmap->grInfo.format || !gpatch->mipmap->downloaded)
if (!gpatch || ((!gpatch->mipmap->grInfo.format || !gpatch->mipmap->downloaded) && !md2->notexturefile))
md2_loadTexture(md2);
gpatch = md2->grpatch; // Load it again, because it isn't being loaded into gpatch after md2_loadtexture...
if ((gpatch && gpatch->mipmap->grInfo.format) // don't load the blend texture if the base texture isn't available
&& (!md2->blendgrpatch || !((GLPatch_t *)md2->blendgrpatch)->mipmap->grInfo.format || !((GLPatch_t *)md2->blendgrpatch)->mipmap->downloaded))
&& (!md2->blendgrpatch
|| ((!((GLPatch_t *)md2->blendgrpatch)->mipmap->grInfo.format || !((GLPatch_t *)md2->blendgrpatch)->mipmap->downloaded)
&& !md2->noblendfile)))
md2_loadBlendTexture(md2);
if (gpatch && gpatch->mipmap->grInfo.format) // else if meant that if a texture couldn't be loaded, it would just end up using something else's texture
@ -1336,7 +1330,7 @@ boolean HWR_DrawModel(gr_vissprite_t *spr)
else
skinnum = TC_BOSS;
}
else if ((skincolors_t)spr->mobj->color != SKINCOLOR_NONE)
else if ((skincolornum_t)spr->mobj->color != SKINCOLOR_NONE)
{
if (spr->mobj->colorized)
skinnum = TC_RAINBOW;
@ -1356,7 +1350,7 @@ boolean HWR_DrawModel(gr_vissprite_t *spr)
}
// Translation or skin number found
HWR_GetBlendedTexture(gpatch, (GLPatch_t *)md2->blendgrpatch, skinnum, spr->colormap, (skincolors_t)spr->mobj->color);
HWR_GetBlendedTexture(gpatch, (GLPatch_t *)md2->blendgrpatch, skinnum, spr->colormap, (skincolornum_t)spr->mobj->color);
}
else
{
@ -1512,11 +1506,6 @@ boolean HWR_DrawModel(gr_vissprite_t *spr)
}
#endif
color[0] = Surf.FlatColor.s.red;
color[1] = Surf.FlatColor.s.green;
color[2] = Surf.FlatColor.s.blue;
color[3] = Surf.FlatColor.s.alpha;
// SRB2CBTODO: MD2 scaling support
finalscale *= FIXED_TO_FLOAT(spr->mobj->scale);
@ -1525,7 +1514,8 @@ boolean HWR_DrawModel(gr_vissprite_t *spr)
p.mirror = atransform.mirror; // from Kart
#endif
HWD.pfnDrawModel(md2->model, frame, durs, tics, nextFrame, &p, finalscale, flip, color);
HWD.pfnSetShader(4); // model shader
HWD.pfnDrawModel(md2->model, frame, durs, tics, nextFrame, &p, finalscale, flip, hflip, &Surf);
}
return true;

View file

@ -1,21 +1,14 @@
// Emacs style mode select -*- C++ -*-
// SONIC ROBO BLAST 2
//-----------------------------------------------------------------------------
//
// Copyright (C) 1998-2000 by DooM Legacy Team.
// Copyright (C) 1999-2020 by Sonic Team Junior.
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
// See the 'LICENSE' file for more details.
//-----------------------------------------------------------------------------
/// \file
/// \brief MD2 Handling
/// \file hw_md2.h
/// \brief 3D Model Handling
/// Inspired from md2.h by Mete Ciragan (mete@swissquake.ch)
#ifndef _HW_MD2_H_
@ -35,7 +28,9 @@ typedef struct
float offset;
model_t *model;
void *grpatch;
boolean notexturefile; // true if texture file was not found
void *blendgrpatch;
boolean noblendfile; // true if blend texture file was not found
boolean notfound;
INT32 skin;
boolean error;

View file

@ -1,19 +1,12 @@
// Emacs style mode select -*- C++ -*-
// SONIC ROBO BLAST 2
//-----------------------------------------------------------------------------
// Copyright (C) 2005 by Sonic Team Junior.
//
// Copyright (C) 2005 by SRB2 Jr. Team.
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
// See the 'LICENSE' file for more details.
//-----------------------------------------------------------------------------
/// \file
/// \file hws_data.h
/// \brief 3D sound definitions
#ifndef __HWS_DATA_H__

View file

@ -206,7 +206,7 @@ int SetupPixelFormat(INT32 WantColorBits, INT32 WantStencilBits, INT32 WantDepth
if (iLastPFD)
{
DBG_Printf("WARNING : SetPixelFormat() called twise not supported by all drivers !\n");
GL_DBG_Printf("WARNING : SetPixelFormat() called twise not supported by all drivers !\n");
}
// set the pixel format only if different than the current
@ -215,17 +215,17 @@ int SetupPixelFormat(INT32 WantColorBits, INT32 WantStencilBits, INT32 WantDepth
else
iLastPFD = iPFD;
DBG_Printf("SetupPixelFormat() - %d ColorBits - %d StencilBits - %d DepthBits\n",
GL_DBG_Printf("SetupPixelFormat() - %d ColorBits - %d StencilBits - %d DepthBits\n",
WantColorBits, WantStencilBits, WantDepthBits);
nPixelFormat = ChoosePixelFormat(hDC, &pfd);
if (nPixelFormat == 0)
DBG_Printf("ChoosePixelFormat() FAILED\n");
GL_DBG_Printf("ChoosePixelFormat() FAILED\n");
if (SetPixelFormat(hDC, nPixelFormat, &pfd) == 0)
{
DBG_Printf("SetPixelFormat() FAILED\n");
GL_DBG_Printf("SetPixelFormat() FAILED\n");
return 0;
}
@ -243,7 +243,7 @@ static INT32 WINAPI SetRes(viddef_t *lvid, vmode_t *pcurrentmode)
BOOL WantFullScreen = !(lvid->u.windowed); //(lvid->u.windowed ? 0 : CDS_FULLSCREEN);
UNREFERENCED_PARAMETER(pcurrentmode);
DBG_Printf ("SetMode(): %dx%d %d bits (%s)\n",
GL_DBG_Printf ("SetMode(): %dx%d %d bits (%s)\n",
lvid->width, lvid->height, lvid->bpp*8,
WantFullScreen ? "fullscreen" : "windowed");
@ -301,7 +301,7 @@ static INT32 WINAPI SetRes(viddef_t *lvid, vmode_t *pcurrentmode)
hDC = GetDC(hWnd);
if (!hDC)
{
DBG_Printf("GetDC() FAILED\n");
GL_DBG_Printf("GetDC() FAILED\n");
return 0;
}
@ -321,12 +321,12 @@ static INT32 WINAPI SetRes(viddef_t *lvid, vmode_t *pcurrentmode)
hGLRC = pwglCreateContext(hDC);
if (!hGLRC)
{
DBG_Printf("pwglCreateContext() FAILED\n");
GL_DBG_Printf("pwglCreateContext() FAILED\n");
return 0;
}
if (!pwglMakeCurrent(hDC, hGLRC))
{
DBG_Printf("wglMakeCurrent() FAILED\n");
GL_DBG_Printf("wglMakeCurrent() FAILED\n");
return 0;
}
}
@ -337,15 +337,15 @@ static INT32 WINAPI SetRes(viddef_t *lvid, vmode_t *pcurrentmode)
//BP: why don't we make it earlier ?
//Hurdler: we cannot do that before intialising gl context
renderer = (LPCSTR)pglGetString(GL_RENDERER);
DBG_Printf("Vendor : %s\n", pglGetString(GL_VENDOR));
DBG_Printf("Renderer : %s\n", renderer);
DBG_Printf("Version : %s\n", pglGetString(GL_VERSION));
DBG_Printf("Extensions : %s\n", gl_extensions);
GL_DBG_Printf("Vendor : %s\n", pglGetString(GL_VENDOR));
GL_DBG_Printf("Renderer : %s\n", renderer);
GL_DBG_Printf("Version : %s\n", pglGetString(GL_VERSION));
GL_DBG_Printf("Extensions : %s\n", gl_extensions);
// BP: disable advenced feature that don't work on somes hardware
// Hurdler: Now works on G400 with bios 1.6 and certified drivers 6.04
if (strstr(renderer, "810")) oglflags |= GLF_NOZBUFREAD;
DBG_Printf("oglflags : 0x%X\n", oglflags);
GL_DBG_Printf("oglflags : 0x%X\n", oglflags);
#ifdef USE_WGL_SWAP
if (isExtAvailable("WGL_EXT_swap_control",gl_extensions))
@ -386,7 +386,7 @@ static INT32 WINAPI SetRes(viddef_t *lvid, vmode_t *pcurrentmode)
// -----------------+
static void UnSetRes(void)
{
DBG_Printf("UnSetRes()\n");
GL_DBG_Printf("UnSetRes()\n");
pwglMakeCurrent(hDC, NULL);
pwglDeleteContext(hGLRC);
@ -437,7 +437,7 @@ EXPORT void HWRAPI(GetModeList) (vmode_t** pvidmodes, INT32 *numvidmodes)
video_modes[iMode].misc = 0;
video_modes[iMode].name = malloc(12 * sizeof (CHAR));
sprintf(video_modes[iMode].name, "%dx%d", (INT32)Tmp.dmPelsWidth, (INT32)Tmp.dmPelsHeight);
DBG_Printf ("Mode: %s\n", video_modes[iMode].name);
GL_DBG_Printf ("Mode: %s\n", video_modes[iMode].name);
video_modes[iMode].width = Tmp.dmPelsWidth;
video_modes[iMode].height = Tmp.dmPelsHeight;
video_modes[iMode].bytesperpixel = Tmp.dmBitsPerPel/8;
@ -474,7 +474,7 @@ EXPORT void HWRAPI(GetModeList) (vmode_t** pvidmodes, INT32 *numvidmodes)
HDC bpphdc;
INT32 iBitsPerPel;
DBG_Printf ("HWRAPI GetModeList()\n");
GL_DBG_Printf ("HWRAPI GetModeList()\n");
bpphdc = GetDC(NULL); // on obtient le bpp actuel
iBitsPerPel = GetDeviceCaps(bpphdc, BITSPIXEL);
@ -490,7 +490,7 @@ EXPORT void HWRAPI(GetModeList) (vmode_t** pvidmodes, INT32 *numvidmodes)
video_modes[i].misc = 0;
video_modes[i].name = malloc(12 * sizeof (CHAR));
sprintf(video_modes[i].name, "%dx%d", res[i][0], res[i][1]);
DBG_Printf ("Mode: %s\n", video_modes[i].name);
GL_DBG_Printf ("Mode: %s\n", video_modes[i].name);
video_modes[i].width = res[i][0];
video_modes[i].height = res[i][1];
video_modes[i].bytesperpixel = iBitsPerPel/8;
@ -511,9 +511,9 @@ EXPORT void HWRAPI(Shutdown) (void)
#ifdef DEBUG_TO_FILE
long nb_centiemes;
DBG_Printf ("HWRAPI Shutdown()\n");
GL_DBG_Printf ("HWRAPI Shutdown()\n");
nb_centiemes = ((clock()-my_clock)*100)/CLOCKS_PER_SEC;
DBG_Printf("Nb frames: %li; Nb sec: %2.2f -> %2.1f fps\n",
GL_DBG_Printf("Nb frames: %li; Nb sec: %2.2f -> %2.1f fps\n",
nb_frames, nb_centiemes/100.0f, (100*nb_frames)/(double)nb_centiemes);
#endif
@ -530,7 +530,7 @@ EXPORT void HWRAPI(Shutdown) (void)
}
FreeLibrary(GLU32);
FreeLibrary(OGL32);
DBG_Printf ("HWRAPI Shutdown(DONE)\n");
GL_DBG_Printf ("HWRAPI Shutdown(DONE)\n");
}
// -----------------+
@ -543,7 +543,7 @@ EXPORT void HWRAPI(FinishUpdate) (INT32 waitvbl)
#else
UNREFERENCED_PARAMETER(waitvbl);
#endif
// DBG_Printf ("FinishUpdate()\n");
// GL_DBG_Printf ("FinishUpdate()\n");
#ifdef DEBUG_TO_FILE
if ((++nb_frames)==2) // on ne commence pas <20> la premi<6D>re frame
my_clock = clock();

File diff suppressed because it is too large Load diff

View file

@ -70,10 +70,6 @@
extern FILE *gllogstream;
#endif
#ifndef DRIVER_STRING
#define DRIVER_STRING "HWRAPI Init(): SRB2 OpenGL renderer" // Tails
#endif
// ==========================================================================
// PROTOS
// ==========================================================================
@ -81,13 +77,11 @@ extern FILE *gllogstream;
boolean LoadGL(void);
void *GetGLFunc(const char *proc);
boolean SetupGLfunc(void);
boolean SetupGLFunc13(void);
void SetupGLFunc4(void);
void Flush(void);
INT32 isExtAvailable(const char *extension, const GLubyte *start);
int SetupPixelFormat(INT32 WantColorBits, INT32 WantStencilBits, INT32 WantDepthBits);
void SetModelView(GLint w, GLint h);
void SetStates(void);
FUNCMATH float byteasfloat(UINT8 fbyte);
#ifndef GL_EXT_texture_filter_anisotropic
#define GL_TEXTURE_MAX_ANISOTROPY_EXT 0x84FE
@ -123,12 +117,15 @@ static PFNglEnableClientState pglEnableClientState;
// GLOBAL
// ==========================================================================
extern const GLubyte *gl_extensions;
extern RGBA_t myPaletteData[];
extern GLint screen_width;
extern GLint screen_height;
extern GLbyte screen_depth;
extern GLint maximumAnisotropy;
extern const GLubyte *gl_version;
extern const GLubyte *gl_renderer;
extern const GLubyte *gl_extensions;
extern RGBA_t myPaletteData[];
extern GLint screen_width;
extern GLint screen_height;
extern GLbyte screen_depth;
extern GLint maximumAnisotropy;
/** \brief OpenGL flags for video driver
*/

View file

@ -755,113 +755,40 @@ static void Got_Saycmd(UINT8 **p, INT32 playernum)
}
else
{
const UINT8 color = players[playernum].skincolor;
UINT16 chatcolor = skincolors[players[playernum].skincolor].chatcolor;
cstart = "\x83";
// Follow palette order at r_draw.c Color_Names
switch (color)
{
default:
case SKINCOLOR_WHITE:
case SKINCOLOR_BONE:
case SKINCOLOR_CLOUDY:
case SKINCOLOR_GREY:
case SKINCOLOR_SILVER:
case SKINCOLOR_AETHER:
case SKINCOLOR_SLATE:
cstart = "\x80"; // white
break;
case SKINCOLOR_CARBON:
case SKINCOLOR_JET:
case SKINCOLOR_BLACK:
cstart = "\x86"; // V_GRAYMAP
break;
case SKINCOLOR_PINK:
case SKINCOLOR_RUBY:
case SKINCOLOR_SALMON:
case SKINCOLOR_RED:
case SKINCOLOR_CRIMSON:
case SKINCOLOR_FLAME:
case SKINCOLOR_KETCHUP:
cstart = "\x85"; // V_REDMAP
break;
case SKINCOLOR_YOGURT:
case SKINCOLOR_BROWN:
case SKINCOLOR_BRONZE:
case SKINCOLOR_TAN:
case SKINCOLOR_BEIGE:
case SKINCOLOR_QUAIL:
cstart = "\x8d"; // V_BROWNMAP
break;
case SKINCOLOR_MOSS:
case SKINCOLOR_GREEN:
case SKINCOLOR_FOREST:
case SKINCOLOR_EMERALD:
case SKINCOLOR_MINT:
cstart = "\x83"; // V_GREENMAP
break;
case SKINCOLOR_AZURE:
cstart = "\x8c"; // V_AZUREMAP
break;
case SKINCOLOR_LAVENDER:
case SKINCOLOR_PASTEL:
case SKINCOLOR_PURPLE:
cstart = "\x89"; // V_PURPLEMAP
break;
case SKINCOLOR_PEACHY:
case SKINCOLOR_LILAC:
case SKINCOLOR_PLUM:
case SKINCOLOR_ROSY:
cstart = "\x8e"; // V_ROSYMAP
break;
case SKINCOLOR_SUNSET:
case SKINCOLOR_COPPER:
case SKINCOLOR_APRICOT:
case SKINCOLOR_ORANGE:
case SKINCOLOR_RUST:
cstart = "\x87"; // V_ORANGEMAP
break;
case SKINCOLOR_GOLD:
case SKINCOLOR_SANDY:
case SKINCOLOR_YELLOW:
case SKINCOLOR_OLIVE:
cstart = "\x82"; // V_YELLOWMAP
break;
case SKINCOLOR_LIME:
case SKINCOLOR_PERIDOT:
case SKINCOLOR_APPLE:
cstart = "\x8b"; // V_PERIDOTMAP
break;
case SKINCOLOR_SEAFOAM:
case SKINCOLOR_AQUA:
cstart = "\x8a"; // V_AQUAMAP
break;
case SKINCOLOR_TEAL:
case SKINCOLOR_WAVE:
case SKINCOLOR_CYAN:
case SKINCOLOR_SKY:
case SKINCOLOR_CERULEAN:
case SKINCOLOR_ICY:
case SKINCOLOR_SAPPHIRE:
case SKINCOLOR_VAPOR:
cstart = "\x88"; // V_SKYMAP
break;
case SKINCOLOR_CORNFLOWER:
case SKINCOLOR_BLUE:
case SKINCOLOR_COBALT:
case SKINCOLOR_DUSK:
case SKINCOLOR_BLUEBELL:
cstart = "\x84"; // V_BLUEMAP
break;
case SKINCOLOR_BUBBLEGUM:
case SKINCOLOR_MAGENTA:
case SKINCOLOR_NEON:
case SKINCOLOR_VIOLET:
case SKINCOLOR_RASPBERRY:
cstart = "\x81"; // V_MAGENTAMAP
break;
}
if (!chatcolor || chatcolor%0x1000 || chatcolor>V_INVERTMAP)
cstart = "\x80";
else if (chatcolor == V_MAGENTAMAP)
cstart = "\x81";
else if (chatcolor == V_YELLOWMAP)
cstart = "\x82";
else if (chatcolor == V_GREENMAP)
cstart = "\x83";
else if (chatcolor == V_BLUEMAP)
cstart = "\x84";
else if (chatcolor == V_REDMAP)
cstart = "\x85";
else if (chatcolor == V_GRAYMAP)
cstart = "\x86";
else if (chatcolor == V_ORANGEMAP)
cstart = "\x87";
else if (chatcolor == V_SKYMAP)
cstart = "\x88";
else if (chatcolor == V_PURPLEMAP)
cstart = "\x89";
else if (chatcolor == V_AQUAMAP)
cstart = "\x8a";
else if (chatcolor == V_PERIDOTMAP)
cstart = "\x8b";
else if (chatcolor == V_AZUREMAP)
cstart = "\x8c";
else if (chatcolor == V_BROWNMAP)
cstart = "\x8d";
else if (chatcolor == V_ROSYMAP)
cstart = "\x8e";
else if (chatcolor == V_INVERTMAP)
cstart = "\x8f";
}
prefix = cstart;
@ -2172,7 +2099,7 @@ void HU_Drawer(void)
{
if (LUA_HudEnabled(hud_rankings))
HU_DrawRankings();
if (gametype == GT_COOP)
if (gametyperules & GTR_CAMPAIGN)
HU_DrawNetplayCoopOverlay();
}
else
@ -2310,7 +2237,7 @@ void HU_Erase(void)
// IN-LEVEL MULTIPLAYER RANKINGS
//======================================================================
#define supercheckdef ((players[tab[i].num].powers[pw_super] && players[tab[i].num].mo && (players[tab[i].num].mo->state < &states[S_PLAY_SUPER_TRANS1] || players[tab[i].num].mo->state >= &states[S_PLAY_SUPER_TRANS6])) || (players[tab[i].num].powers[pw_carry] == CR_NIGHTSMODE && skins[players[tab[i].num].skin].flags & SF_SUPER))
#define supercheckdef (!(players[tab[i].num].charflags & SF_NOSUPERSPRITES) && ((players[tab[i].num].powers[pw_super] && players[tab[i].num].mo && (players[tab[i].num].mo->state < &states[S_PLAY_SUPER_TRANS1] || players[tab[i].num].mo->state >= &states[S_PLAY_SUPER_TRANS6])) || (players[tab[i].num].powers[pw_carry] == CR_NIGHTSMODE && skins[players[tab[i].num].skin].flags & SF_SUPER)))
#define greycheckdef (players[tab[i].num].spectator || players[tab[i].num].playerstate == PST_DEAD || (G_IsSpecialStage(gamemap) && players[tab[i].num].exiting))
//
@ -2868,7 +2795,7 @@ static void HU_Draw32TabRankings(INT32 x, INT32 y, playersort_t *tab, INT32 scor
if (tab[i].color == 0)
{
colormap = colormaps;
if (players[tab[i].num].powers[pw_super])
if (players[tab[i].num].powers[pw_super] && !(players[tab[i].num].charflags & SF_NOSUPERSPRITES))
V_DrawFixedPatch(x*FRACUNIT, y*FRACUNIT, FRACUNIT/4, 0, superprefix[players[tab[i].num].skin], 0);
else
{

View file

@ -46,6 +46,8 @@ UINT32 I_GetFreeMem(UINT32 *total);
*/
tic_t I_GetTime(void);
int I_GetTimeMicros(void);// provides microsecond counter for render stats
/** \brief The I_Sleep function
\return void

View file

@ -487,7 +487,7 @@ static void cleanupnodes(void)
// Why can't I start at zero?
for (j = 1; j < MAXNETNODES; j++)
if (!(nodeingame[j] || SV_SendingFile(j)))
if (!(nodeingame[j] || SendingFile(j)))
nodeconnected[j] = false;
}

View file

@ -20,6 +20,7 @@
#include "m_misc.h"
#include "z_zone.h"
#include "d_player.h"
#include "v_video.h" // V_*MAP constants
#include "lzf.h"
#ifdef HWRENDER
#include "hardware/hw_light.h"
@ -509,6 +510,9 @@ char sprnames[NUMSPRITES + 1][5] =
"GFZD", // GFZ debris
"BRIC", // Bricks
"WDDB", // Wood Debris
"BRIR", // CEZ3 colored bricks
"BRIB", // CEZ3 colored bricks
"BRIY", // CEZ3 colored bricks
// Gravity Well Objects
"GWLG",
@ -723,7 +727,7 @@ state_t states[NUMSTATES] =
// CA_GLIDEANDCLIMB
{SPR_PLAY, SPR2_GLID, 2, {NULL}, 0, 0, S_PLAY_GLIDE}, // S_PLAY_GLIDE
{SPR_PLAY, SPR2_LAND, 9, {NULL}, 0, 0, S_PLAY_STND}, // S_PLAY_GLIDE_LANDING
{SPR_PLAY, SPR2_LAND, 7, {NULL}, 0, 0, S_PLAY_STND}, // S_PLAY_GLIDE_LANDING
{SPR_PLAY, SPR2_CLNG|FF_ANIMATE, -1, {NULL}, 0, 4, S_NULL}, // S_PLAY_CLING
{SPR_PLAY, SPR2_CLMB, 5, {NULL}, 0, 0, S_PLAY_CLIMB}, // S_PLAY_CLIMB
@ -965,30 +969,31 @@ state_t states[NUMSTATES] =
{SPR_CSPR, 1, 1, {A_CrushclawAim}, 50, 20, S_CDIAG1}, // S_CDIAG8
// Jet Jaw
{SPR_JJAW, 0, 1, {A_JetJawRoam}, 0, 0, S_JETJAW_ROAM2}, // S_JETJAW_ROAM1
{SPR_JJAW, 0, 1, {A_JetJawRoam}, 0, 0, S_JETJAW_ROAM3}, // S_JETJAW_ROAM2
{SPR_JJAW, 0, 1, {A_JetJawRoam}, 0, 0, S_JETJAW_ROAM4}, // S_JETJAW_ROAM3
{SPR_JJAW, 0, 1, {A_JetJawRoam}, 0, 0, S_JETJAW_ROAM5}, // S_JETJAW_ROAM4
{SPR_JJAW, 1, 1, {A_JetJawRoam}, 0, 0, S_JETJAW_ROAM6}, // S_JETJAW_ROAM5
{SPR_JJAW, 1, 1, {A_JetJawRoam}, 0, 0, S_JETJAW_ROAM7}, // S_JETJAW_ROAM6
{SPR_JJAW, 1, 1, {A_JetJawRoam}, 0, 0, S_JETJAW_ROAM8}, // S_JETJAW_ROAM7
{SPR_JJAW, 1, 1, {A_JetJawRoam}, 0, 0, S_JETJAW_ROAM1}, // S_JETJAW_ROAM8
{SPR_JJAW, 0, 1, {A_JetJawChomp}, 0, 0, S_JETJAW_CHOMP2}, // S_JETJAW_CHOMP1
{SPR_JJAW, 0, 1, {A_JetJawChomp}, 0, 0, S_JETJAW_CHOMP3}, // S_JETJAW_CHOMP2
{SPR_JJAW, 0, 1, {A_JetJawChomp}, 0, 0, S_JETJAW_CHOMP4}, // S_JETJAW_CHOMP3
{SPR_JJAW, 0, 1, {A_JetJawChomp}, 0, 0, S_JETJAW_CHOMP5}, // S_JETJAW_CHOMP4
{SPR_JJAW, 1, 1, {A_JetJawChomp}, 0, 0, S_JETJAW_CHOMP6}, // S_JETJAW_CHOMP5
{SPR_JJAW, 1, 1, {A_JetJawChomp}, 0, 0, S_JETJAW_CHOMP7}, // S_JETJAW_CHOMP6
{SPR_JJAW, 1, 1, {A_JetJawChomp}, 0, 0, S_JETJAW_CHOMP8}, // S_JETJAW_CHOMP7
{SPR_JJAW, 1, 1, {A_JetJawChomp}, 0, 0, S_JETJAW_CHOMP9}, // S_JETJAW_CHOMP8
{SPR_JJAW, 2, 1, {A_JetJawChomp}, 0, 0, S_JETJAW_CHOMP10}, // S_JETJAW_CHOMP9
{SPR_JJAW, 2, 1, {A_JetJawChomp}, 0, 0, S_JETJAW_CHOMP11}, // S_JETJAW_CHOMP10
{SPR_JJAW, 2, 1, {A_JetJawChomp}, 0, 0, S_JETJAW_CHOMP12}, // S_JETJAW_CHOMP11
{SPR_JJAW, 2, 1, {A_JetJawChomp}, 0, 0, S_JETJAW_CHOMP13}, // S_JETJAW_CHOMP12
{SPR_JJAW, 3, 1, {A_JetJawChomp}, 0, 0, S_JETJAW_CHOMP14}, // S_JETJAW_CHOMP13
{SPR_JJAW, 3, 1, {A_JetJawChomp}, 0, 0, S_JETJAW_CHOMP15}, // S_JETJAW_CHOMP14
{SPR_JJAW, 3, 1, {A_JetJawChomp}, 0, 0, S_JETJAW_CHOMP16}, // S_JETJAW_CHOMP15
{SPR_JJAW, 3, 1, {A_JetJawChomp}, 0, 0, S_JETJAW_CHOMP1}, // S_JETJAW_CHOMP16
{SPR_JJAW, 0, 1, {A_JetJawRoam}, 0, 0, S_JETJAW_ROAM2}, // S_JETJAW_ROAM1
{SPR_JJAW, 0, 1, {A_JetJawRoam}, 0, 0, S_JETJAW_ROAM3}, // S_JETJAW_ROAM2
{SPR_JJAW, 0, 1, {A_JetJawRoam}, 0, 0, S_JETJAW_ROAM4}, // S_JETJAW_ROAM3
{SPR_JJAW, 0, 1, {A_JetJawRoam}, 0, 0, S_JETJAW_ROAM5}, // S_JETJAW_ROAM4
{SPR_JJAW, 1, 1, {A_JetJawRoam}, 0, 0, S_JETJAW_ROAM6}, // S_JETJAW_ROAM5
{SPR_JJAW, 1, 1, {A_JetJawRoam}, 0, 0, S_JETJAW_ROAM7}, // S_JETJAW_ROAM6
{SPR_JJAW, 1, 1, {A_JetJawRoam}, 0, 0, S_JETJAW_ROAM8}, // S_JETJAW_ROAM7
{SPR_JJAW, 1, 1, {A_JetJawRoam}, 0, 0, S_JETJAW_ROAM1}, // S_JETJAW_ROAM8
{SPR_JJAW, 0, 1, {A_DualAction}, S_JETJAW_CHOMP16, S_JETJAW_SOUND, S_JETJAW_CHOMP2}, // S_JETJAW_CHOMP1
{SPR_JJAW, 0, 1, {A_JetJawChomp}, 0, 0, S_JETJAW_CHOMP3}, // S_JETJAW_CHOMP2
{SPR_JJAW, 0, 1, {A_JetJawChomp}, 0, 0, S_JETJAW_CHOMP4}, // S_JETJAW_CHOMP3
{SPR_JJAW, 0, 1, {A_JetJawChomp}, 0, 0, S_JETJAW_CHOMP5}, // S_JETJAW_CHOMP4
{SPR_JJAW, 1, 1, {A_JetJawChomp}, 0, 0, S_JETJAW_CHOMP6}, // S_JETJAW_CHOMP5
{SPR_JJAW, 1, 1, {A_JetJawChomp}, 0, 0, S_JETJAW_CHOMP7}, // S_JETJAW_CHOMP6
{SPR_JJAW, 1, 1, {A_JetJawChomp}, 0, 0, S_JETJAW_CHOMP8}, // S_JETJAW_CHOMP7
{SPR_JJAW, 1, 1, {A_JetJawChomp}, 0, 0, S_JETJAW_CHOMP9}, // S_JETJAW_CHOMP8
{SPR_JJAW, 2, 1, {A_JetJawChomp}, 0, 0, S_JETJAW_CHOMP10}, // S_JETJAW_CHOMP9
{SPR_JJAW, 2, 1, {A_JetJawChomp}, 0, 0, S_JETJAW_CHOMP11}, // S_JETJAW_CHOMP10
{SPR_JJAW, 2, 1, {A_JetJawChomp}, 0, 0, S_JETJAW_CHOMP12}, // S_JETJAW_CHOMP11
{SPR_JJAW, 2, 1, {A_JetJawChomp}, 0, 0, S_JETJAW_CHOMP13}, // S_JETJAW_CHOMP12
{SPR_JJAW, 3, 1, {A_JetJawChomp}, 0, 0, S_JETJAW_CHOMP14}, // S_JETJAW_CHOMP13
{SPR_JJAW, 3, 1, {A_JetJawChomp}, 0, 0, S_JETJAW_CHOMP15}, // S_JETJAW_CHOMP14
{SPR_JJAW, 3, 1, {A_JetJawChomp}, 0, 0, S_JETJAW_CHOMP16}, // S_JETJAW_CHOMP15
{SPR_JJAW, 3, 1, {A_JetJawChomp}, 0, 0, S_JETJAW_CHOMP1}, // S_JETJAW_CHOMP16
{SPR_JJAW, 0, 1, {A_PlayAttackSound}, 0, 0, S_JETJAW_SOUND}, // S_JETJAW_SOUND
// Snailer
{SPR_SNLR, 0, 1, {A_SnailerThink}, 0, 0, S_SNAILER1}, // S_SNAILER1
@ -1429,11 +1434,13 @@ state_t states[NUMSTATES] =
{SPR_FANG, 8, 0, {A_PrepareRepeat}, 1, 0, S_FANG_PINCHPATHINGSTART2}, // S_FANG_PINCHPATHINGSTART1
{SPR_FANG, 8, 0, {A_PlayActiveSound}, 0, 0, S_FANG_PINCHPATHING}, // S_FANG_PINCHPATHINGSTART2
{SPR_FANG, 8, 0, {A_Boss5FindWaypoint}, 1, 0, S_FANG_PINCHBOUNCE1}, // S_FANG_PINCHPATHING
{SPR_FANG, 8, 0, {A_Boss5FindWaypoint}, 1, 0, S_FANG_PINCHBOUNCE0}, // S_FANG_PINCHPATHING
{SPR_FANG, 8, 0, {A_SetObjectFlags}, MF_NOCLIP|MF_NOCLIPHEIGHT, 2, S_FANG_PINCHBOUNCE1}, // S_FANG_PINCHBOUNCE0
{SPR_FANG, 8, 2, {A_Thrust}, 0, 1, S_FANG_PINCHBOUNCE2}, // S_FANG_PINCHBOUNCE1
{SPR_FANG, 9, 2, {NULL}, 0, 0, S_FANG_PINCHBOUNCE3}, // S_FANG_PINCHBOUNCE2
{SPR_FANG, 10, 2, {A_Boss5Jump}, 0, 0, S_FANG_PINCHBOUNCE4}, // S_FANG_PINCHBOUNCE3
{SPR_FANG, 10, 1, {A_Boss5CheckFalling}, S_FANG_PINCHSKID1, S_FANG_PINCHFALL1, S_FANG_PINCHBOUNCE4}, // S_FANG_PINCHBOUNCE4
{SPR_FANG, 10, 1, {A_Boss5CheckFalling}, S_FANG_PINCHSKID1, S_FANG_PINCHFALL0, S_FANG_PINCHBOUNCE4}, // S_FANG_PINCHBOUNCE4
{SPR_FANG, 12, 0, {A_SetObjectFlags}, MF_NOCLIP|MF_NOCLIPHEIGHT, 1, S_FANG_PINCHFALL1}, // S_FANG_PINCHFALL0
{SPR_FANG, 12, 1, {A_Boss5CheckOnGround}, S_FANG_PINCHSKID1, 0, S_FANG_PINCHFALL2}, // S_FANG_PINCHFALL1
{SPR_FANG, 13, 1, {A_Boss5CheckOnGround}, S_FANG_PINCHSKID1, 0, S_FANG_PINCHFALL1}, // S_FANG_PINCHFALL2
{SPR_FANG, 4, 0, {A_PlayAttackSound}, 0, 0, S_FANG_PINCHSKID2}, // S_FANG_PINCHSKID1
@ -2926,11 +2933,11 @@ state_t states[NUMSTATES] =
{SPR_IVSP, FF_ANIMATE, 32, {NULL}, 31, 1, S_NULL}, // S_IVSP
// Super Sonic Spark
{SPR_SSPK, 0, 2, {NULL}, 0, 0, S_SSPK2}, // S_SSPK1
{SPR_SSPK, 1, 2, {NULL}, 0, 0, S_SSPK3}, // S_SSPK2
{SPR_SSPK, 2, 2, {NULL}, 0, 0, S_SSPK4}, // S_SSPK3
{SPR_SSPK, 1, 2, {NULL}, 0, 0, S_SSPK5}, // S_SSPK4
{SPR_SSPK, 0, 2, {NULL}, 0, 0, S_NULL}, // S_SSPK5
{SPR_SSPK, FF_FULLBRIGHT, 2, {NULL}, 0, 0, S_SSPK2}, // S_SSPK1
{SPR_SSPK, 1|FF_FULLBRIGHT, 2, {NULL}, 0, 0, S_SSPK3}, // S_SSPK2
{SPR_SSPK, 2|FF_FULLBRIGHT, 2, {NULL}, 0, 0, S_SSPK4}, // S_SSPK3
{SPR_SSPK, 1|FF_FULLBRIGHT, 2, {NULL}, 0, 0, S_SSPK5}, // S_SSPK4
{SPR_SSPK, FF_FULLBRIGHT, 2, {NULL}, 0, 0, S_NULL}, // S_SSPK5
// Flicky-sized bubble
{SPR_FBUB, 0, -1, {NULL}, 0, 0, S_NULL}, // S_FLICKY_BUBBLE
@ -3361,7 +3368,7 @@ state_t states[NUMSTATES] =
// CTF Sign
{SPR_GFLG, FF_FULLBRIGHT, 2, {NULL}, 0, 0, S_NULL}, // S_GOTFLAG
// Finish flag
{SPR_FNSF, FF_TRANS30, -1, {NULL}, 0, 0, S_NULL}, // S_FINISHFLAG
@ -3910,6 +3917,9 @@ state_t states[NUMSTATES] =
{SPR_GFZD, FF_ANIMATE|FF_RANDOMANIM, -1, {NULL}, 31, 1, S_NULL}, // S_GFZDEBRIS
{SPR_BRIC, FF_ANIMATE, -1, {A_DebrisRandom}, 7, 2, S_NULL}, // S_BRICKDEBRIS
{SPR_WDDB, FF_ANIMATE, -1, {A_DebrisRandom}, 7, 2, S_NULL}, // S_WOODDEBRIS
{SPR_BRIR, FF_ANIMATE|FF_RANDOMANIM, -1, {NULL}, 31, 1, S_NULL}, // S_REDBRICKDEBRIS
{SPR_BRIB, FF_ANIMATE|FF_RANDOMANIM, -1, {NULL}, 31, 1, S_NULL}, // S_BLUEBRICKDEBRIS
{SPR_BRIY, FF_ANIMATE|FF_RANDOMANIM, -1, {NULL}, 31, 1, S_NULL}, // S_YELLOWBRICKDEBRIS
#ifdef SEENAMES
{SPR_NULL, 0, 1, {NULL}, 0, 0, S_NULL}, // S_NAMECHECK
@ -4573,7 +4583,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
S_JETJAW_CHOMP1,// seestate
sfx_None, // seesound
4*TICRATE, // reactiontime
sfx_None, // attacksound
sfx_s1ab, // attacksound
S_NULL, // painstate
0, // painchance
sfx_None, // painsound
@ -5291,7 +5301,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
100, // mass
0, // damage
sfx_None, // activesound
MF_SPECIAL|MF_SHOOTABLE|MF_ENEMY|MF_NOGRAVITY|MF_BOUNCE|MF_RUNSPAWNFUNC, // flags
MF_SPECIAL|MF_SHOOTABLE|MF_ENEMY|MF_NOGRAVITY|MF_SLIDEME|MF_RUNSPAWNFUNC, // flags
S_NULL // raisestate
},
@ -18009,7 +18019,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
MF_NOBLOCKMAP|MF_NOCLIP|MF_NOGRAVITY|MF_SCENERY, // flags
S_NULL // raisestate
},
{ // MT_FINISHFLAG
-1, // doomednum
S_FINISHFLAG, // spawnstate
@ -19854,7 +19864,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
MF_SLIDEME|MF_SPECIAL|MF_NOGRAVITY|MF_NOCLIPHEIGHT, // flags
S_NULL // raisestate
},
{ // MT_FLINGNIGHTSSTAR
-1, // doomednum
S_NIGHTSSTAR, // spawnstate
@ -21586,6 +21596,87 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
S_NULL // raisestate
},
{ // MT_REDBRICKDEBRIS
-1, // doomednum
S_REDBRICKDEBRIS, // spawnstate
1, // spawnhealth
S_NULL, // seestate
sfx_None, // seesound
0, // 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
64*FRACUNIT, // height
0, // display offset
100, // mass
0, // damage
sfx_crumbl, // activesound
MF_NOBLOCKMAP|MF_NOCLIPTHING|MF_RUNSPAWNFUNC|MF_NOCLIPHEIGHT|MF_SCENERY, // flags
S_NULL // raisestate
},
{ // MT_BLUEBRICKDEBRIS
-1, // doomednum
S_BLUEBRICKDEBRIS, // spawnstate
1, // spawnhealth
S_NULL, // seestate
sfx_None, // seesound
0, // 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
64*FRACUNIT, // height
0, // display offset
100, // mass
0, // damage
sfx_crumbl, // activesound
MF_NOBLOCKMAP|MF_NOCLIPTHING|MF_RUNSPAWNFUNC|MF_NOCLIPHEIGHT|MF_SCENERY, // flags
S_NULL // raisestate
},
{ // MT_YELLOWBRICKDEBRIS
-1, // doomednum
S_YELLOWBRICKDEBRIS, // spawnstate
1, // spawnhealth
S_NULL, // seestate
sfx_None, // seesound
0, // 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
64*FRACUNIT, // height
0, // display offset
100, // mass
0, // damage
sfx_crumbl, // activesound
MF_NOBLOCKMAP|MF_NOCLIPTHING|MF_RUNSPAWNFUNC|MF_NOCLIPHEIGHT|MF_SCENERY, // flags
S_NULL // raisestate
},
#ifdef SEENAMES
{ // MT_NAMECHECK
-1, // doomednum
@ -21616,8 +21707,140 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
#endif
};
skincolor_t skincolors[MAXSKINCOLORS] = {
{"None", {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, SKINCOLOR_NONE, 0, 0, false}, // SKINCOLOR_NONE
/** Patches the mobjinfo table and state table.
// Greyscale ranges
{"White", {0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x02, 0x02, 0x04, 0x06, 0x08, 0x0a, 0x0c, 0x0e, 0x10, 0x11}, SKINCOLOR_BLACK, 5, 0, true}, // SKINCOLOR_WHITE
{"Bone", {0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x05, 0x06, 0x08, 0x0a, 0x0c, 0x0e, 0x10, 0x11, 0x12}, SKINCOLOR_JET, 7, 0, true}, // SKINCOLOR_BONE
{"Cloudy", {0x02, 0x03, 0x04, 0x05, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14}, SKINCOLOR_CARBON, 7, 0, true}, // SKINCOLOR_CLOUDY
{"Grey", {0x04, 0x06, 0x08, 0x0a, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18}, SKINCOLOR_AETHER, 12, 0, true}, // SKINCOLOR_GREY
{"Silver", {0x02, 0x03, 0x05, 0x07, 0x09, 0x0b, 0x0d, 0x0f, 0x11, 0x13, 0x15, 0x17, 0x19, 0x1b, 0x1d, 0x1f}, SKINCOLOR_SLATE, 12, 0, true}, // SKINCOLOR_SILVER
{"Carbon", {0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x16, 0x17, 0x17, 0x19, 0x19, 0x1a, 0x1a, 0x1b, 0x1c, 0x1d}, SKINCOLOR_CLOUDY, 7, V_GRAYMAP, true}, // SKINCOLOR_CARBON
{"Jet", {0x00, 0x05, 0x0a, 0x0f, 0x14, 0x19, 0x1a, 0x1b, 0x1c, 0x1e, 0x1e, 0x1e, 0x1f, 0x1f, 0x1f, 0x1f}, SKINCOLOR_BONE, 7, V_GRAYMAP, true}, // SKINCOLOR_JET
{"Black", {0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1b, 0x1b, 0x1c, 0x1d, 0x1d, 0x1e, 0x1e, 0x1f, 0x1f}, SKINCOLOR_WHITE, 7, V_GRAYMAP, true}, // SKINCOLOR_BLACK
// Desaturated
{"Aether", {0x00, 0x00, 0x01, 0x02, 0x02, 0x03, 0x91, 0x91, 0x91, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, 0xaf}, SKINCOLOR_GREY, 15, 0, true}, // SKINCOLOR_AETHER
{"Slate", {0x00, 0x02, 0x04, 0x06, 0x08, 0x0a, 0xaa, 0xaa, 0xaa, 0xab, 0xac, 0xac, 0xad, 0xad, 0xae, 0xaf}, SKINCOLOR_SILVER, 12, 0, true}, // SKINCOLOR_SLATE
{"Bluebell", {0x90, 0x91, 0x92, 0x93, 0x94, 0x94, 0x95, 0xac, 0xac, 0xad, 0xad, 0xa8, 0xa8, 0xa9, 0xfd, 0xfe}, SKINCOLOR_COPPER, 4, V_BLUEMAP, true}, // SKINCOLOR_BLUEBELL
{"Pink", {0xd0, 0xd0, 0xd1, 0xd1, 0xd2, 0xd2, 0xd3, 0xd3, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0x2b, 0x2c, 0x2e}, SKINCOLOR_AZURE, 9, V_REDMAP, true}, // SKINCOLOR_PINK
{"Yogurt", {0xd0, 0x30, 0xd8, 0xd9, 0xda, 0xdb, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, 0xe0, 0xe3, 0xe6, 0xe8, 0xe9}, SKINCOLOR_RUST, 7, V_BROWNMAP, true}, // SKINCOLOR_YOGURT
{"Brown", {0xdf, 0xe0, 0xe1, 0xe2, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef}, SKINCOLOR_TAN, 2, V_BROWNMAP, true}, // SKINCOLOR_BROWN
{"Bronze", {0xde, 0xe0, 0xe1, 0xe4, 0xe7, 0xe9, 0xeb, 0xec, 0xed, 0xed, 0xed, 0x19, 0x19, 0x1b, 0x1d, 0x1e}, SKINCOLOR_KETCHUP, 0, V_BROWNMAP, true}, // SKINCOLOR_BRONZE
{"Tan", {0x51, 0x51, 0x54, 0x54, 0x55, 0x55, 0x56, 0x56, 0x56, 0x57, 0xf5, 0xf5, 0xf9, 0xf9, 0xed, 0xed}, SKINCOLOR_BROWN, 12, V_BROWNMAP, true}, // SKINCOLOR_TAN
{"Beige", {0x54, 0x55, 0x56, 0x56, 0xf2, 0xf3, 0xf3, 0xf4, 0xf5, 0xf6, 0xf8, 0xf9, 0xfa, 0xfb, 0xed, 0xed}, SKINCOLOR_MOSS, 5, V_BROWNMAP, true}, // SKINCOLOR_BEIGE
{"Moss", {0x58, 0x58, 0x59, 0x59, 0x5a, 0x5a, 0x5b, 0x5b, 0x5b, 0x5c, 0x5d, 0x5d, 0x5e, 0x5e, 0x5f, 0x5f}, SKINCOLOR_BEIGE, 13, V_GREENMAP, true}, // SKINCOLOR_MOSS
{"Azure", {0x90, 0x90, 0x91, 0x91, 0xaa, 0xaa, 0xab, 0xab, 0xab, 0xac, 0xad, 0xad, 0xae, 0xae, 0xaf, 0xaf}, SKINCOLOR_PINK, 5, V_AZUREMAP, true}, // SKINCOLOR_AZURE
{"Lavender", {0xc0, 0xc0, 0xc1, 0xc1, 0xc2, 0xc2, 0xc3, 0xc3, 0xc3, 0xc4, 0xc5, 0xc5, 0xc6, 0xc6, 0xc7, 0xc7}, SKINCOLOR_GOLD, 4, V_PURPLEMAP, true}, // SKINCOLOR_LAVENDER
// Viv's vivid colours (toast 21/07/17)
{"Ruby", {0xb0, 0xb0, 0xc9, 0xca, 0xcc, 0x26, 0x27, 0x28, 0x29, 0x2a, 0xb9, 0xb9, 0xba, 0xba, 0xbb, 0xfd}, SKINCOLOR_EMERALD, 10, V_REDMAP, true}, // SKINCOLOR_RUBY
{"Salmon", {0xd0, 0xd0, 0xd1, 0xd2, 0x20, 0x21, 0x24, 0x25, 0x26, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e}, SKINCOLOR_FOREST, 6, V_REDMAP, true}, // SKINCOLOR_SALMON
{"Red", {0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x47, 0x2e, 0x2f}, SKINCOLOR_GREEN, 10, V_REDMAP, true}, // SKINCOLOR_RED
{"Crimson", {0x27, 0x27, 0x28, 0x28, 0x29, 0x2a, 0x2b, 0x2b, 0x2c, 0x2d, 0x2e, 0x2e, 0x2e, 0x2f, 0x2f, 0x1f}, SKINCOLOR_ICY, 10, V_REDMAP, true}, // SKINCOLOR_CRIMSON
{"Flame", {0x31, 0x32, 0x33, 0x36, 0x22, 0x22, 0x25, 0x25, 0x25, 0xcd, 0xcf, 0xcf, 0xc5, 0xc5, 0xc7, 0xc7}, SKINCOLOR_PURPLE, 8, V_REDMAP, true}, // SKINCOLOR_FLAME
{"Ketchup", {0x48, 0x49, 0x40, 0x33, 0x34, 0x36, 0x22, 0x24, 0x26, 0x28, 0x2a, 0x2b, 0x2c, 0x47, 0x2e, 0x2f}, SKINCOLOR_BRONZE, 8, V_REDMAP, true}, // SKINCOLOR_KETCHUP
{"Peachy", {0xd0, 0x30, 0x31, 0x31, 0x32, 0x32, 0xdc, 0xdc, 0xdc, 0xd3, 0xd4, 0xd4, 0xcc, 0xcd, 0xce, 0xcf}, SKINCOLOR_TEAL, 7, V_ROSYMAP, true}, // SKINCOLOR_PEACHY
{"Quail", {0xd8, 0xd9, 0xdb, 0xdc, 0xde, 0xdf, 0xd5, 0xd5, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0x1d, 0x1f}, SKINCOLOR_WAVE, 5, V_BROWNMAP, true}, // SKINCOLOR_QUAIL
{"Sunset", {0x51, 0x52, 0x40, 0x40, 0x34, 0x36, 0xd5, 0xd5, 0xd6, 0xd7, 0xcf, 0xcf, 0xc6, 0xc6, 0xc7, 0xfe}, SKINCOLOR_SAPPHIRE, 5, V_ORANGEMAP, true}, // SKINCOLOR_SUNSET
{"Copper", {0x58, 0x54, 0x40, 0x34, 0x35, 0x38, 0x3a, 0x3c, 0x3d, 0x2a, 0x2b, 0x2c, 0x2c, 0xba, 0xba, 0xbb}, SKINCOLOR_BLUEBELL, 5, V_ORANGEMAP, true}, // SKINCOLOR_COPPER
{"Apricot", {0x00, 0xd8, 0xd9, 0xda, 0xdb, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e}, SKINCOLOR_CYAN, 4, V_ORANGEMAP, true}, // SKINCOLOR_APRICOT
{"Orange", {0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x2c}, SKINCOLOR_BLUE, 4, V_ORANGEMAP, true}, // SKINCOLOR_ORANGE
{"Rust", {0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3c, 0x3d, 0x3d, 0x3d, 0x3f, 0x2c, 0x2d, 0x47, 0x2e, 0x2f, 0x2f}, SKINCOLOR_YOGURT, 8, V_ORANGEMAP, true}, // SKINCOLOR_RUST
{"Gold", {0x51, 0x51, 0x54, 0x54, 0x41, 0x42, 0x43, 0x43, 0x44, 0x45, 0x46, 0x3f, 0x2d, 0x2e, 0x2f, 0x2f}, SKINCOLOR_LAVENDER, 10, V_YELLOWMAP, true}, // SKINCOLOR_GOLD
{"Sandy", {0x53, 0x40, 0x41, 0x42, 0x43, 0xe6, 0xe9, 0xe9, 0xea, 0xec, 0xec, 0xc6, 0xc6, 0xc7, 0xc7, 0xfe}, SKINCOLOR_SKY, 8, V_YELLOWMAP, true}, // SKINCOLOR_SANDY
{"Yellow", {0x52, 0x53, 0x49, 0x49, 0x4a, 0x4a, 0x4b, 0x4b, 0x4b, 0x4c, 0x4d, 0x4d, 0x4e, 0x4e, 0x4f, 0xed}, SKINCOLOR_CORNFLOWER, 8, V_YELLOWMAP, true}, // SKINCOLOR_YELLOW
{"Olive", {0x4b, 0x4b, 0x4c, 0x4c, 0x4d, 0x4e, 0xe7, 0xe7, 0xe9, 0xc5, 0xc5, 0xc6, 0xc6, 0xc7, 0xc7, 0xfd}, SKINCOLOR_DUSK, 3, V_YELLOWMAP, true}, // SKINCOLOR_OLIVE
{"Lime", {0x50, 0x51, 0x52, 0x53, 0x48, 0xbc, 0xbd, 0xbe, 0xbe, 0xbf, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f}, SKINCOLOR_MAGENTA, 9, V_PERIDOTMAP, true}, // SKINCOLOR_LIME
{"Peridot", {0x58, 0x58, 0xbc, 0xbc, 0xbd, 0xbd, 0xbe, 0xbe, 0xbe, 0xbf, 0x5e, 0x5e, 0x5f, 0x5f, 0x77, 0x77}, SKINCOLOR_COBALT, 2, V_PERIDOTMAP, true}, // SKINCOLOR_PERIDOT
{"Apple", {0x49, 0x49, 0xbc, 0xbd, 0xbe, 0xbe, 0xbe, 0x67, 0x69, 0x6a, 0x6b, 0x6b, 0x6c, 0x6d, 0x6d, 0x6d}, SKINCOLOR_RASPBERRY, 13, V_PERIDOTMAP, true}, // SKINCOLOR_APPLE
{"Green", {0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f}, SKINCOLOR_RED, 6, V_GREENMAP, true}, // SKINCOLOR_GREEN
{"Forest", {0x65, 0x66, 0x67, 0x68, 0x69, 0x69, 0x6a, 0x6b, 0x6b, 0x6c, 0x6d, 0x6d, 0x6e, 0x6e, 0x6e, 0x6f}, SKINCOLOR_SALMON, 9, V_GREENMAP, true}, // SKINCOLOR_FOREST
{"Emerald", {0x70, 0x70, 0x71, 0x71, 0x72, 0x72, 0x73, 0x73, 0x73, 0x74, 0x75, 0x75, 0x76, 0x76, 0x77, 0x77}, SKINCOLOR_RUBY, 4, V_GREENMAP, true}, // SKINCOLOR_EMERALD
{"Mint", {0x00, 0x00, 0x58, 0x58, 0x59, 0x62, 0x62, 0x62, 0x64, 0x67, 0x7e, 0x7e, 0x8f, 0x8f, 0x8a, 0x8a}, SKINCOLOR_VIOLET, 5, V_GREENMAP, true}, // SKINCOLOR_MINT
{"Seafoam", {0x01, 0x58, 0x59, 0x5a, 0x7d, 0x7d, 0x7e, 0x7e, 0x7e, 0x8f, 0x8f, 0x8a, 0x8a, 0x8a, 0xfd, 0xfd}, SKINCOLOR_PLUM, 6, V_AQUAMAP, true}, // SKINCOLOR_SEAFOAM
{"Aqua", {0x78, 0x79, 0x7a, 0x7a, 0x7b, 0x7b, 0x7c, 0x7c, 0x7c, 0x7d, 0x7e, 0x7e, 0x7f, 0x7f, 0x76, 0x77}, SKINCOLOR_ROSY, 7, V_AQUAMAP, true}, // SKINCOLOR_AQUA
{"Teal", {0x78, 0x78, 0x8c, 0x8c, 0x8d, 0x8d, 0x8d, 0x8e, 0x8e, 0x8f, 0x8f, 0x8f, 0x8a, 0x8a, 0x8a, 0x8a}, SKINCOLOR_PEACHY, 7, V_SKYMAP, true}, // SKINCOLOR_TEAL
{"Wave", {0x00, 0x78, 0x78, 0x79, 0x8d, 0x87, 0x88, 0x89, 0x89, 0xae, 0xa8, 0xa8, 0xa9, 0xa9, 0xfd, 0xfd}, SKINCOLOR_QUAIL, 5, V_SKYMAP, true}, // SKINCOLOR_WAVE
{"Cyan", {0x80, 0x81, 0xff, 0xff, 0x83, 0x83, 0x8d, 0x8d, 0x8d, 0x8e, 0x7e, 0x7f, 0x76, 0x76, 0x77, 0x6e}, SKINCOLOR_APRICOT, 6, V_SKYMAP, true}, // SKINCOLOR_CYAN
{"Sky", {0x80, 0x80, 0x81, 0x82, 0x83, 0x83, 0x84, 0x85, 0x85, 0x86, 0x87, 0x88, 0x89, 0x89, 0x8a, 0x8b}, SKINCOLOR_SANDY, 1, V_SKYMAP, true}, // SKINCOLOR_SKY
{"Cerulean", {0x85, 0x86, 0x87, 0x88, 0x88, 0x89, 0x89, 0x89, 0x8a, 0x8a, 0xfd, 0xfd, 0xfd, 0x1f, 0x1f, 0x1f}, SKINCOLOR_NEON, 4, V_SKYMAP, true}, // SKINCOLOR_CERULEAN
{"Icy", {0x00, 0x00, 0x00, 0x00, 0x80, 0x81, 0x83, 0x83, 0x86, 0x87, 0x95, 0x95, 0xad, 0xad, 0xae, 0xaf}, SKINCOLOR_CRIMSON, 0, V_SKYMAP, true}, // SKINCOLOR_ICY
{"Sapphire", {0x80, 0x83, 0x86, 0x87, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0xfd, 0xfe}, SKINCOLOR_SUNSET, 5, V_SKYMAP, true}, // SKINCOLOR_SAPPHIRE
{"Cornflower", {0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x9a, 0x9c, 0x9d, 0x9d, 0x9e, 0x9e, 0x9e}, SKINCOLOR_YELLOW, 4, V_BLUEMAP, true}, // SKINCOLOR_CORNFLOWER
{"Blue", {0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0xfd, 0xfe}, SKINCOLOR_ORANGE, 5, V_BLUEMAP, true}, // SKINCOLOR_BLUE
{"Cobalt", {0x93, 0x94, 0x95, 0x96, 0x98, 0x9a, 0x9b, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0xfd, 0xfd, 0xfe, 0xfe}, SKINCOLOR_PERIDOT, 5, V_BLUEMAP, true}, // SKINCOLOR_COBALT
{"Vapor", {0x80, 0x81, 0x83, 0x86, 0x94, 0x94, 0xa3, 0xa3, 0xa4, 0xa6, 0xa6, 0xa6, 0xa8, 0xa8, 0xa9, 0xa9}, SKINCOLOR_LILAC, 4, V_SKYMAP, true}, // SKINCOLOR_VAPOR
{"Dusk", {0x92, 0x93, 0x94, 0x94, 0xac, 0xad, 0xad, 0xad, 0xae, 0xae, 0xaf, 0xaf, 0xa9, 0xa9, 0xfd, 0xfd}, SKINCOLOR_OLIVE, 0, V_BLUEMAP, true}, // SKINCOLOR_DUSK
{"Pastel", {0x90, 0x90, 0xa0, 0xa0, 0xa1, 0xa1, 0xa2, 0xa2, 0xa2, 0xa3, 0xa4, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8}, SKINCOLOR_BUBBLEGUM, 9, V_PURPLEMAP, true}, // SKINCOLOR_PASTEL
{"Purple", {0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa4, 0xa5, 0xa5, 0xa5, 0xa6, 0xa7, 0xa7, 0xa8, 0xa8, 0xa9, 0xa9}, SKINCOLOR_FLAME, 7, V_PURPLEMAP, true}, // SKINCOLOR_PURPLE
{"Bubblegum", {0x00, 0xd0, 0xd0, 0xc8, 0xc8, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8}, SKINCOLOR_PASTEL, 8, V_MAGENTAMAP, true}, // SKINCOLOR_BUBBLEGUM
{"Magenta", {0xb3, 0xb3, 0xb4, 0xb5, 0xb6, 0xb6, 0xb7, 0xb7, 0xb7, 0xb8, 0xb9, 0xb9, 0xba, 0xba, 0xbb, 0xbb}, SKINCOLOR_LIME, 6, V_MAGENTAMAP, true}, // SKINCOLOR_MAGENTA
{"Neon", {0xb3, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xb9, 0xba, 0xba, 0xbb, 0xbb, 0xc7, 0xc7, 0x1d, 0x1d, 0x1e}, SKINCOLOR_CERULEAN, 2, V_MAGENTAMAP, true}, // SKINCOLOR_NEON
{"Violet", {0xd0, 0xd1, 0xd2, 0xca, 0xcc, 0xb8, 0xb9, 0xb9, 0xba, 0xa8, 0xa8, 0xa9, 0xa9, 0xfd, 0xfe, 0xfe}, SKINCOLOR_MINT, 6, V_MAGENTAMAP, true}, // SKINCOLOR_VIOLET
{"Lilac", {0x00, 0xd0, 0xd1, 0xd2, 0xd3, 0xc1, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc5, 0xc6, 0xc6, 0xfe, 0x1f}, SKINCOLOR_VAPOR, 4, V_ROSYMAP, true}, // SKINCOLOR_LILAC
{"Plum", {0xc8, 0xd3, 0xd5, 0xd6, 0xd7, 0xce, 0xcf, 0xb9, 0xb9, 0xba, 0xba, 0xa9, 0xa9, 0xa9, 0xfd, 0xfe}, SKINCOLOR_MINT, 7, V_ROSYMAP, true}, // SKINCOLOR_PLUM
{"Raspberry", {0xc8, 0xc9, 0xca, 0xcb, 0xcb, 0xcc, 0xcd, 0xcd, 0xce, 0xb9, 0xb9, 0xba, 0xba, 0xbb, 0xfe, 0xfe}, SKINCOLOR_APPLE, 13, V_MAGENTAMAP, true}, // SKINCOLOR_RASPBERRY
{"Rosy", {0xfc, 0xc8, 0xc8, 0xc9, 0xc9, 0xca, 0xca, 0xcb, 0xcb, 0xcc, 0xcc, 0xcd, 0xcd, 0xce, 0xce, 0xcf}, SKINCOLOR_AQUA, 1, V_ROSYMAP, true}, // SKINCOLOR_ROSY
// super
{"Super Silver 1", {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x02, 0x03}, SKINCOLOR_BLACK, 15, 0, false}, // SKINCOLOR_SUPERSILVER1
{"Super Silver 2", {0x00, 0x01, 0x02, 0x02, 0x03, 0x03, 0x03, 0x03, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x05, 0x07}, SKINCOLOR_BLACK, 6, 0, false}, // SKINCOLOR_SUPERSILVER2
{"Super Silver 3", {0x01, 0x02, 0x02, 0x03, 0x03, 0x03, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x05, 0x07, 0x09, 0x0b}, SKINCOLOR_BLACK, 5, 0, false}, // SKINCOLOR_SUPERSILVER3
{"Super Silver 4", {0x02, 0x03, 0x03, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x05, 0x07, 0x09, 0x0b, 0x0d, 0x0f, 0x11}, SKINCOLOR_BLACK, 5, V_GRAYMAP, false}, // SKINCOLOR_SUPERSILVER4
{"Super Silver 5", {0x03, 0x03, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x05, 0x07, 0x09, 0x0b, 0x0d, 0x0f, 0x11, 0x13}, SKINCOLOR_BLACK, 5, V_GRAYMAP, false}, // SKINCOLOR_SUPERSILVER5
{"Super Red 1", {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd0, 0xd0, 0xd1, 0xd1, 0xd2, 0xd2}, SKINCOLOR_CYAN, 15, 0, false}, // SKINCOLOR_SUPERRED1
{"Super Red 2", {0x00, 0x00, 0x00, 0xd0, 0xd0, 0xd0, 0xd1, 0xd1, 0xd1, 0xd2, 0xd2, 0xd2, 0x20, 0x20, 0x21, 0x21}, SKINCOLOR_CYAN, 14, V_ROSYMAP, false}, // SKINCOLOR_SUPERRED2
{"Super Red 3", {0x00, 0x00, 0xd0, 0xd0, 0xd1, 0xd1, 0xd2, 0xd2, 0x20, 0x20, 0x21, 0x21, 0x22, 0x22, 0x23, 0x23}, SKINCOLOR_CYAN, 13, V_REDMAP, false}, // SKINCOLOR_SUPERRED3
{"Super Red 4", {0x00, 0xd0, 0xd1, 0xd1, 0xd2, 0xd2, 0x20, 0x20, 0x21, 0x21, 0x22, 0x22, 0x23, 0x23, 0x24, 0x24}, SKINCOLOR_CYAN, 11, V_REDMAP, false}, // SKINCOLOR_SUPERRED4
{"Super Red 5", {0xd0, 0xd1, 0xd2, 0xd2, 0x20, 0x20, 0x21, 0x21, 0x22, 0x22, 0x23, 0x23, 0x24, 0x24, 0x25, 0x25}, SKINCOLOR_CYAN, 10, V_REDMAP, false}, // SKINCOLOR_SUPERRED5
{"Super Orange 1", {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd0, 0x30, 0x31, 0x32, 0x33, 0x34}, SKINCOLOR_SAPPHIRE, 15, 0, false}, // SKINCOLOR_SUPERORANGE1
{"Super Orange 2", {0x00, 0x00, 0x00, 0x00, 0xd0, 0xd0, 0x30, 0x30, 0x31, 0x31, 0x32, 0x32, 0x33, 0x33, 0x34, 0x34}, SKINCOLOR_SAPPHIRE, 12, V_ORANGEMAP, false}, // SKINCOLOR_SUPERORANGE2
{"Super Orange 3", {0x00, 0x00, 0xd0, 0xd0, 0x30, 0x30, 0x31, 0x31, 0x32, 0x32, 0x33, 0x33, 0x34, 0x34, 0x35, 0x35}, SKINCOLOR_SAPPHIRE, 9, V_ORANGEMAP, false}, // SKINCOLOR_SUPERORANGE3
{"Super Orange 4", {0x00, 0xd0, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x44, 0x45, 0x46}, SKINCOLOR_SAPPHIRE, 4, V_ORANGEMAP, false}, // SKINCOLOR_SUPERORANGE4
{"Super Orange 5", {0xd0, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x44, 0x45, 0x46, 0x47}, SKINCOLOR_SAPPHIRE, 3, V_ORANGEMAP, false}, // SKINCOLOR_SUPERORANGE5
{"Super Gold 1", {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x50, 0x51, 0x52, 0x53, 0x48}, SKINCOLOR_CORNFLOWER, 15, 0, false}, // SKINCOLOR_SUPERGOLD1
{"Super Gold 2", {0x00, 0x50, 0x51, 0x52, 0x53, 0x53, 0x48, 0x48, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x40, 0x41}, SKINCOLOR_CORNFLOWER, 9, V_YELLOWMAP, false}, // SKINCOLOR_SUPERGOLD2
{"Super Gold 3", {0x51, 0x52, 0x53, 0x53, 0x48, 0x48, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x40, 0x41, 0x42, 0x43}, SKINCOLOR_CORNFLOWER, 8, V_YELLOWMAP, false}, // SKINCOLOR_SUPERGOLD3
{"Super Gold 4", {0x53, 0x48, 0x48, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46}, SKINCOLOR_CORNFLOWER, 8, V_YELLOWMAP, false}, // SKINCOLOR_SUPERGOLD4
{"Super Gold 5", {0x48, 0x48, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47}, SKINCOLOR_CORNFLOWER, 8, V_YELLOWMAP, false}, // SKINCOLOR_SUPERGOLD5
{"Super Peridot 1", {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x58, 0x58, 0x58, 0xbc, 0xbc, 0xbc}, SKINCOLOR_COBALT, 15, 0, false}, // SKINCOLOR_SUPERPERIDOT1
{"Super Peridot 2", {0x00, 0x58, 0x58, 0x58, 0xbc, 0xbc, 0xbc, 0xbc, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbe, 0xbe}, SKINCOLOR_COBALT, 4, V_PERIDOTMAP, false}, // SKINCOLOR_SUPERPERIDOT2
{"Super Peridot 3", {0x58, 0x58, 0xbc, 0xbc, 0xbc, 0xbc, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbe, 0xbe, 0xbf, 0xbf}, SKINCOLOR_COBALT, 3, V_PERIDOTMAP, false}, // SKINCOLOR_SUPERPERIDOT3
{"Super Peridot 4", {0x58, 0xbc, 0xbc, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbe, 0xbe, 0xbf, 0xbf, 0x5e, 0x5e, 0x5f}, SKINCOLOR_COBALT, 3, V_PERIDOTMAP, false}, // SKINCOLOR_SUPERPERIDOT4
{"Super Peridot 5", {0xbc, 0xbc, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbe, 0xbe, 0xbf, 0xbf, 0x5e, 0x5e, 0x5f, 0x77}, SKINCOLOR_COBALT, 3, V_PERIDOTMAP, false}, // SKINCOLOR_SUPERPERIDOT5
{"Super Sky 1", {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x81, 0x82, 0x83, 0x84}, SKINCOLOR_RUST, 15, 0, false}, // SKINCOLOR_SUPERSKY1
{"Super Sky 2", {0x00, 0x80, 0x81, 0x82, 0x83, 0x83, 0x84, 0x84, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x86, 0x86}, SKINCOLOR_RUST, 4, V_SKYMAP, false}, // SKINCOLOR_SUPERSKY2
{"Super Sky 3", {0x81, 0x82, 0x83, 0x83, 0x84, 0x84, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x86, 0x86, 0x87, 0x87}, SKINCOLOR_RUST, 3, V_SKYMAP, false}, // SKINCOLOR_SUPERSKY3
{"Super Sky 4", {0x83, 0x84, 0x84, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x86, 0x86, 0x87, 0x87, 0x88, 0x89, 0x8a}, SKINCOLOR_RUST, 3, V_SKYMAP, false}, // SKINCOLOR_SUPERSKY4
{"Super Sky 5", {0x84, 0x84, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x86, 0x86, 0x87, 0x87, 0x88, 0x89, 0x8a, 0x8b}, SKINCOLOR_RUST, 3, V_SKYMAP, false}, // SKINCOLOR_SUPERSKY5
{"Super Purple 1", {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x90, 0x90, 0xa0, 0xa0, 0xa1, 0xa2}, SKINCOLOR_EMERALD, 15, 0, false}, // SKINCOLOR_SUPERPURPLE1
{"Super Purple 2", {0x00, 0x90, 0xa0, 0xa0, 0xa1, 0xa1, 0xa2, 0xa2, 0xa3, 0xa3, 0xa3, 0xa3, 0xa4, 0xa4, 0xa5, 0xa5}, SKINCOLOR_EMERALD, 4, V_PURPLEMAP, false}, // SKINCOLOR_SUPERPURPLE2
{"Super Purple 3", {0xa0, 0xa0, 0xa1, 0xa1, 0xa2, 0xa2, 0xa3, 0xa3, 0xa3, 0xa3, 0xa4, 0xa4, 0xa5, 0xa5, 0xa6, 0xa6}, SKINCOLOR_EMERALD, 0, V_PURPLEMAP, false}, // SKINCOLOR_SUPERPURPLE3
{"Super Purple 4", {0xa1, 0xa2, 0xa2, 0xa3, 0xa3, 0xa3, 0xa3, 0xa4, 0xa4, 0xa5, 0xa5, 0xa6, 0xa6, 0xa7, 0xa8, 0xa9}, SKINCOLOR_EMERALD, 0, V_PURPLEMAP, false}, // SKINCOLOR_SUPERPURPLE4
{"Super Purple 5", {0xa2, 0xa2, 0xa3, 0xa3, 0xa3, 0xa3, 0xa4, 0xa4, 0xa5, 0xa5, 0xa6, 0xa6, 0xa7, 0xa8, 0xa9, 0xfd}, SKINCOLOR_EMERALD, 0, V_PURPLEMAP, false}, // SKINCOLOR_SUPERPURPLE5
{"Super Rust 1", {0x00, 0xd0, 0xd0, 0xd0, 0x30, 0x30, 0x31, 0x32, 0x33, 0x37, 0x3a, 0x44, 0x45, 0x46, 0x47, 0x2e}, SKINCOLOR_CYAN, 14, V_ORANGEMAP, false}, // SKINCOLOR_SUPERRUST1
{"Super Rust 2", {0x30, 0x31, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x38, 0x3a, 0x44, 0x45, 0x46, 0x47, 0x47, 0x2e}, SKINCOLOR_CYAN, 10, V_ORANGEMAP, false}, // SKINCOLOR_SUPERRUST2
{"Super Rust 3", {0x31, 0x32, 0x33, 0x34, 0x36, 0x37, 0x38, 0x3a, 0x44, 0x45, 0x45, 0x46, 0x46, 0x47, 0x2e, 0x2e}, SKINCOLOR_CYAN, 9, V_ORANGEMAP, false}, // SKINCOLOR_SUPERRUST3
{"Super Rust 4", {0x48, 0x40, 0x41, 0x42, 0x43, 0x44, 0x44, 0x45, 0x45, 0x46, 0x46, 0x47, 0x47, 0x2e, 0x2e, 0x2e}, SKINCOLOR_CYAN, 8, V_ORANGEMAP, false}, // SKINCOLOR_SUPERRUST4
{"Super Rust 5", {0x41, 0x42, 0x43, 0x43, 0x44, 0x44, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xed, 0xee, 0xee, 0xef, 0xef}, SKINCOLOR_CYAN, 8, V_ORANGEMAP, false}, // SKINCOLOR_SUPERRUST5
{"Super Tan 1", {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x50, 0x51, 0x51, 0x52, 0x52}, SKINCOLOR_BROWN, 14, 0, false}, // SKINCOLOR_SUPERTAN1
{"Super Tan 2", {0x00, 0x50, 0x50, 0x51, 0x51, 0x52, 0x52, 0x52, 0x54, 0x54, 0x54, 0x54, 0x55, 0x56, 0x57, 0xf5}, SKINCOLOR_BROWN, 13, V_BROWNMAP, false}, // SKINCOLOR_SUPERTAN2
{"Super Tan 3", {0x50, 0x51, 0x51, 0x52, 0x52, 0x52, 0x54, 0x54, 0x54, 0x54, 0x55, 0x56, 0x57, 0xf5, 0xf7, 0xf9}, SKINCOLOR_BROWN, 12, V_BROWNMAP, false}, // SKINCOLOR_SUPERTAN3
{"Super Tan 4", {0x51, 0x52, 0x52, 0x52, 0x52, 0x54, 0x54, 0x54, 0x55, 0x56, 0x57, 0xf5, 0xf7, 0xf9, 0xfb, 0xed}, SKINCOLOR_BROWN, 11, V_BROWNMAP, false}, // SKINCOLOR_SUPERTAN4
{"Super Tan 5", {0x52, 0x52, 0x54, 0x54, 0x54, 0x55, 0x56, 0x57, 0xf5, 0xf7, 0xf9, 0xfb, 0xed, 0xee, 0xef, 0xef}, SKINCOLOR_BROWN, 10, V_BROWNMAP, false} // SKINCOLOR_SUPERTAN5
};
/** Patches the mobjinfo, state, and skincolor tables.
* Free slots are emptied out and set to initial values.
*/
void P_PatchInfoTables(void)
@ -21645,6 +21868,11 @@ void P_PatchInfoTables(void)
sprnames[i][0] = '\0'; // i == NUMSPRITES
memset(&states[S_FIRSTFREESLOT], 0, sizeof (state_t) * NUMSTATEFREESLOTS);
memset(&mobjinfo[MT_FIRSTFREESLOT], 0, sizeof (mobjinfo_t) * NUMMOBJFREESLOTS);
memset(&skincolors[SKINCOLOR_FIRSTFREESLOT], 0, sizeof (skincolor_t) * NUMCOLORFREESLOTS);
for (i = SKINCOLOR_FIRSTFREESLOT; i <= SKINCOLOR_LASTFREESLOT; i++) {
skincolors[i].accessible = false;
skincolors[i].name[0] = '\0';
}
for (i = MT_FIRSTFREESLOT; i <= MT_LASTFREESLOT; i++)
mobjinfo[i].doomednum = -1;
}
@ -21653,7 +21881,8 @@ void P_PatchInfoTables(void)
static char *sprnamesbackup;
static state_t *statesbackup;
static mobjinfo_t *mobjinfobackup;
static size_t sprnamesbackupsize, statesbackupsize, mobjinfobackupsize;
static skincolor_t *skincolorsbackup;
static size_t sprnamesbackupsize, statesbackupsize, mobjinfobackupsize, skincolorsbackupsize;
#endif
void P_BackupTables(void)
@ -21663,6 +21892,7 @@ void P_BackupTables(void)
sprnamesbackup = Z_Malloc(sizeof(sprnames), PU_STATIC, NULL);
statesbackup = Z_Malloc(sizeof(states), PU_STATIC, NULL);
mobjinfobackup = Z_Malloc(sizeof(mobjinfo), PU_STATIC, NULL);
skincolorsbackup = Z_Malloc(sizeof(skincolors), PU_STATIC, NULL);
// Sprite names
sprnamesbackupsize = lzf_compress(sprnames, sizeof(sprnames), sprnamesbackup, sizeof(sprnames));
@ -21684,6 +21914,13 @@ void P_BackupTables(void)
mobjinfobackup = Z_Realloc(mobjinfobackup, mobjinfobackupsize, PU_STATIC, NULL);
else
M_Memcpy(mobjinfobackup, mobjinfo, sizeof(mobjinfo));
//Skincolor info
skincolorsbackupsize = lzf_compress(skincolors, sizeof(skincolors), skincolorsbackup, sizeof(skincolors));
if (skincolorsbackupsize > 0)
skincolorsbackup = Z_Realloc(skincolorsbackup, skincolorsbackupsize, PU_STATIC, NULL);
else
M_Memcpy(skincolorsbackup, skincolors, sizeof(skincolors));
#endif
}
@ -21716,5 +21953,13 @@ void P_ResetData(INT32 flags)
else
M_Memcpy(mobjinfo, mobjinfobackup, sizeof(mobjinfobackup));
}
if (flags & 8)
{
if (skincolorsbackupsize > 0)
lzf_decompress(skincolorsbackup, skincolorsbackupsize, skincolors, sizeof(skincolors));
else
M_Memcpy(skincolors, skincolorsbackup, sizeof(skincolorsbackup));
}
#endif
}

View file

@ -774,6 +774,9 @@ typedef enum sprite
SPR_GFZD, // GFZ debris
SPR_BRIC, // Bricks
SPR_WDDB, // Wood Debris
SPR_BRIR, // CEZ3 colored bricks
SPR_BRIB,
SPR_BRIY,
// Gravity Well Objects
SPR_GWLG,
@ -1186,6 +1189,7 @@ typedef enum state
S_JETJAW_CHOMP14,
S_JETJAW_CHOMP15,
S_JETJAW_CHOMP16,
S_JETJAW_SOUND,
// Snailer
S_SNAILER1,
@ -1608,10 +1612,12 @@ typedef enum state
S_FANG_PINCHPATHINGSTART1,
S_FANG_PINCHPATHINGSTART2,
S_FANG_PINCHPATHING,
S_FANG_PINCHBOUNCE0,
S_FANG_PINCHBOUNCE1,
S_FANG_PINCHBOUNCE2,
S_FANG_PINCHBOUNCE3,
S_FANG_PINCHBOUNCE4,
S_FANG_PINCHFALL0,
S_FANG_PINCHFALL1,
S_FANG_PINCHFALL2,
S_FANG_PINCHSKID1,
@ -3498,7 +3504,7 @@ typedef enum state
// Got Flag Sign
S_GOTFLAG,
// Finish flag
S_FINISHFLAG,
@ -3998,6 +4004,9 @@ typedef enum state
S_GFZDEBRIS,
S_BRICKDEBRIS,
S_WOODDEBRIS,
S_REDBRICKDEBRIS, // for CEZ3
S_BLUEBRICKDEBRIS, // for CEZ3
S_YELLOWBRICKDEBRIS, // for CEZ3
#ifdef SEENAMES
S_NAMECHECK,
@ -4798,6 +4807,9 @@ typedef enum mobj_type
MT_GFZDEBRIS,
MT_BRICKDEBRIS,
MT_WOODDEBRIS,
MT_REDBRICKDEBRIS, // for CEZ3
MT_BLUEBRICKDEBRIS, // for CEZ3
MT_YELLOWBRICKDEBRIS, // for CEZ3
#ifdef SEENAMES
MT_NAMECHECK,

View file

@ -2068,7 +2068,7 @@ msgstr ""
#: m_cheat.c:292
#, c-format
msgid "Sissy Mode %s\n"
msgid "Cheese Mode %s\n"
msgstr ""
#: m_cheat.c:315 m_cheat.c:349 m_cheat.c:514 m_cheat.c:538 m_cheat.c:557

View file

@ -2145,7 +2145,7 @@ msgstr ""
#: m_cheat.c:294
#, c-format
msgid "Sissy Mode %s\n"
msgid "Cheese Mode %s\n"
msgstr ""
#: m_cheat.c:314

View file

@ -14,7 +14,7 @@
#include "fastcmp.h"
#include "p_local.h"
#include "p_setup.h" // So we can have P_SetupLevelSky
#include "p_slopes.h" // P_GetZAt
#include "p_slopes.h" // P_GetSlopeZAt
#include "z_zone.h"
#include "r_main.h"
#include "r_draw.h"
@ -27,6 +27,7 @@
#include "hu_stuff.h" // HU_AddChatText
#include "console.h"
#include "d_netcmd.h" // IsPlayerAdmin
#include "m_menu.h" // Player Setup menu color stuff
#include "lua_script.h"
#include "lua_libs.h"
@ -144,6 +145,8 @@ static const struct {
{META_STATE, "state_t"},
{META_MOBJINFO, "mobjinfo_t"},
{META_SFXINFO, "sfxinfo_t"},
{META_SKINCOLOR, "skincolor_t"},
{META_COLORRAMP, "skincolor_t.ramp"},
{META_SPRITEINFO, "spriteinfo_t"},
{META_PIVOTLIST, "spriteframepivot_t[]"},
{META_FRAMEPIVOT, "spriteframepivot_t"},
@ -252,6 +255,43 @@ static int lib_reserveLuabanks(lua_State *L)
return 1;
}
// M_MENU
//////////////
static int lib_pMoveColorBefore(lua_State *L)
{
UINT16 color = (UINT16)luaL_checkinteger(L, 1);
UINT16 targ = (UINT16)luaL_checkinteger(L, 2);
NOHUD
M_MoveColorBefore(color, targ);
return 0;
}
static int lib_pMoveColorAfter(lua_State *L)
{
UINT16 color = (UINT16)luaL_checkinteger(L, 1);
UINT16 targ = (UINT16)luaL_checkinteger(L, 2);
NOHUD
M_MoveColorAfter(color, targ);
return 0;
}
static int lib_pGetColorBefore(lua_State *L)
{
UINT16 color = (UINT16)luaL_checkinteger(L, 1);
lua_pushinteger(L, M_GetColorBefore(color));
return 1;
}
static int lib_pGetColorAfter(lua_State *L)
{
UINT16 color = (UINT16)luaL_checkinteger(L, 1);
lua_pushinteger(L, M_GetColorAfter(color));
return 1;
}
// M_RANDOM
//////////////
@ -1219,6 +1259,19 @@ static int lib_pElementalFire(lua_State *L)
return 0;
}
static int lib_pSpawnSkidDust(lua_State *L)
{
player_t *player = *((player_t **)luaL_checkudata(L, 1, META_PLAYER));
fixed_t radius = luaL_checkfixed(L, 2);
boolean sound = lua_optboolean(L, 3);
NOHUD
INLEVEL
if (!player)
return LUA_ErrInvalid(L, "player_t");
P_SpawnSkidDust(player, radius, sound);
return 0;
}
static int lib_pDoPlayerFinish(lua_State *L)
{
player_t *player = *((player_t **)luaL_checkudata(L, 1, META_PLAYER));
@ -1306,6 +1359,19 @@ static int lib_pNukeEnemies(lua_State *L)
return 0;
}
static int lib_pEarthquake(lua_State *L)
{
mobj_t *inflictor = *((mobj_t **)luaL_checkudata(L, 1, META_MOBJ));
mobj_t *source = *((mobj_t **)luaL_checkudata(L, 2, META_MOBJ));
fixed_t radius = luaL_checkfixed(L, 3);
NOHUD
INLEVEL
if (!inflictor || !source)
return LUA_ErrInvalid(L, "mobj_t");
P_Earthquake(inflictor, source, radius);
return 0;
}
static int lib_pHomingAttack(lua_State *L)
{
mobj_t *source = *((mobj_t **)luaL_checkudata(L, 1, META_MOBJ));
@ -1514,11 +1580,12 @@ static int lib_pRadiusAttack(lua_State *L)
mobj_t *source = *((mobj_t **)luaL_checkudata(L, 2, META_MOBJ));
fixed_t damagedist = luaL_checkfixed(L, 3);
UINT8 damagetype = luaL_optinteger(L, 4, 0);
boolean sightcheck = lua_opttrueboolean(L, 5);
NOHUD
INLEVEL
if (!spot || !source)
return LUA_ErrInvalid(L, "mobj_t");
P_RadiusAttack(spot, source, damagedist, damagetype);
P_RadiusAttack(spot, source, damagedist, damagetype, sightcheck);
return 0;
}
@ -2184,14 +2251,20 @@ static int lib_evStartCrumble(lua_State *L)
static int lib_pGetZAt(lua_State *L)
{
pslope_t *slope = *((pslope_t **)luaL_checkudata(L, 1, META_SLOPE));
fixed_t x = luaL_checkfixed(L, 2);
fixed_t y = luaL_checkfixed(L, 3);
//HUDSAFE
if (!slope)
return LUA_ErrInvalid(L, "pslope_t");
if (lua_isnil(L, 1))
{
fixed_t z = luaL_checkfixed(L, 4);
lua_pushfixed(L, P_GetZAt(NULL, x, y, z));
}
else
{
pslope_t *slope = *((pslope_t **)luaL_checkudata(L, 1, META_SLOPE));
lua_pushfixed(L, P_GetSlopeZAt(slope, x, y));
}
lua_pushfixed(L, P_GetZAt(slope, x, y));
return 1;
}
@ -2382,10 +2455,10 @@ static int lib_rGetColorByName(lua_State *L)
// SKINCOLOR_GREEN > "Green" for example
static int lib_rGetNameByColor(lua_State *L)
{
UINT8 colornum = (UINT8)luaL_checkinteger(L, 1);
if (!colornum || colornum >= MAXSKINCOLORS)
return luaL_error(L, "skincolor %d out of range (1 - %d).", colornum, MAXSKINCOLORS-1);
lua_pushstring(L, Color_Names[colornum]);
UINT16 colornum = (UINT16)luaL_checkinteger(L, 1);
if (!colornum || colornum >= numskincolors)
return luaL_error(L, "skincolor %d out of range (1 - %d).", colornum, numskincolors-1);
lua_pushstring(L, skincolors[colornum].name);
return 1;
}
@ -2458,6 +2531,20 @@ static int lib_sStopSound(lua_State *L)
return 0;
}
static int lib_sStopSoundByID(lua_State *L)
{
void *origin = *((mobj_t **)luaL_checkudata(L, 1, META_MOBJ));
sfxenum_t sound_id = luaL_checkinteger(L, 2);
//NOHUD
if (!origin)
return LUA_ErrInvalid(L, "mobj_t");
if (sound_id >= NUMSFX)
return luaL_error(L, "sfx %d out of range (0 - %d)", sound_id, NUMSFX-1);
S_StopSoundByID(origin, sound_id);
return 0;
}
static int lib_sChangeMusic(lua_State *L)
{
#ifdef MUSICSLOT_COMPATIBILITY
@ -2878,15 +2965,50 @@ static int lib_gAddGametype(lua_State *L)
return 0;
}
static int Lcheckmapnumber (lua_State *L, int idx, const char *fun)
{
if (ISINLEVEL)
return luaL_optinteger(L, idx, gamemap);
else
{
if (lua_isnoneornil(L, idx))
{
return luaL_error(L,
"%s can only be used without a parameter while in a level.",
fun
);
}
else
return luaL_checkinteger(L, idx);
}
}
static int lib_gBuildMapName(lua_State *L)
{
INT32 map = luaL_optinteger(L, 1, gamemap);
INT32 map = Lcheckmapnumber(L, 1, "G_BuildMapName");
//HUDSAFE
INLEVEL
lua_pushstring(L, G_BuildMapName(map));
return 1;
}
static int lib_gBuildMapTitle(lua_State *L)
{
INT32 map = Lcheckmapnumber(L, 1, "G_BuildMapTitle");
char *name;
if (map < 1 || map > NUMMAPS)
{
return luaL_error(L,
"map number %d out of range (1 - %d)",
map,
NUMMAPS
);
}
name = G_BuildMapTitle(map);
lua_pushstring(L, name);
Z_Free(name);
return 1;
}
static int lib_gDoReborn(lua_State *L)
{
INT32 playernum = luaL_checkinteger(L, 1);
@ -3008,6 +3130,14 @@ static int lib_gPlatformGametype(lua_State *L)
return 1;
}
static int lib_gCoopGametype(lua_State *L)
{
//HUDSAFE
INLEVEL
lua_pushboolean(L, G_CoopGametype());
return 1;
}
static int lib_gTagGametype(lua_State *L)
{
//HUDSAFE
@ -3073,6 +3203,12 @@ static luaL_Reg lib[] = {
{"IsPlayerAdmin", lib_isPlayerAdmin},
{"reserveLuabanks", lib_reserveLuabanks},
// m_menu
{"M_MoveColorAfter",lib_pMoveColorAfter},
{"M_MoveColorBefore",lib_pMoveColorBefore},
{"M_GetColorAfter",lib_pGetColorAfter},
{"M_GetColorBefore",lib_pGetColorBefore},
// m_random
{"P_RandomFixed",lib_pRandomFixed},
{"P_RandomByte",lib_pRandomByte},
@ -3153,6 +3289,7 @@ static luaL_Reg lib[] = {
{"P_DoBubbleBounce",lib_pDoBubbleBounce},
{"P_BlackOw",lib_pBlackOw},
{"P_ElementalFire",lib_pElementalFire},
{"P_SpawnSkidDust", lib_pSpawnSkidDust},
{"P_DoPlayerFinish",lib_pDoPlayerFinish},
{"P_DoPlayerExit",lib_pDoPlayerExit},
{"P_InstaThrust",lib_pInstaThrust},
@ -3160,6 +3297,7 @@ static luaL_Reg lib[] = {
{"P_ReturnThrustY",lib_pReturnThrustY},
{"P_LookForEnemies",lib_pLookForEnemies},
{"P_NukeEnemies",lib_pNukeEnemies},
{"P_Earthquake",lib_pEarthquake},
{"P_HomingAttack",lib_pHomingAttack},
{"P_SuperReady",lib_pSuperReady},
{"P_DoJump",lib_pDoJump},
@ -3253,6 +3391,7 @@ static luaL_Reg lib[] = {
{"S_StartSound",lib_sStartSound},
{"S_StartSoundAtVolume",lib_sStartSoundAtVolume},
{"S_StopSound",lib_sStopSound},
{"S_StopSoundByID",lib_sStopSoundByID},
{"S_ChangeMusic",lib_sChangeMusic},
{"S_SpeedMusic",lib_sSpeedMusic},
{"S_StopMusic",lib_sStopMusic},
@ -3271,6 +3410,7 @@ static luaL_Reg lib[] = {
// g_game
{"G_AddGametype", lib_gAddGametype},
{"G_BuildMapName",lib_gBuildMapName},
{"G_BuildMapTitle",lib_gBuildMapTitle},
{"G_DoReborn",lib_gDoReborn},
{"G_SetCustomExitVars",lib_gSetCustomExitVars},
{"G_EnoughPlayersFinished",lib_gEnoughPlayersFinished},
@ -3283,6 +3423,7 @@ static luaL_Reg lib[] = {
{"G_GametypeHasSpectators",lib_gGametypeHasSpectators},
{"G_RingSlingerGametype",lib_gRingSlingerGametype},
{"G_PlatformGametype",lib_gPlatformGametype},
{"G_CoopGametype",lib_gCoopGametype},
{"G_TagGametype",lib_gTagGametype},
{"G_CompetitionGametype",lib_gCompetitionGametype},
{"G_TicsToHours",lib_gTicsToHours},

View file

@ -58,6 +58,7 @@ enum hook {
hook_SeenPlayer,
hook_PlayerThink,
hook_ShouldJingleContinue,
hook_GameQuit,
hook_MAX // last hook
};
@ -112,3 +113,4 @@ boolean LUAh_SeenPlayer(player_t *player, player_t *seenfriend); // Hook for MT_
#endif
#define LUAh_PlayerThink(player) LUAh_PlayerHook(player, hook_PlayerThink) // Hook for P_PlayerThink
boolean LUAh_ShouldJingleContinue(player_t *player, const char *musname); // Hook for whether a jingle of the given music should continue playing
void LUAh_GameQuit(void); // Hook for game quitting

File diff suppressed because it is too large Load diff

View file

@ -878,8 +878,8 @@ static int libd_drawNameTag(lua_State *L)
INT32 y;
const char *str;
INT32 flags;
UINT8 basecolor;
UINT8 outlinecolor;
UINT16 basecolor;
UINT16 outlinecolor;
UINT8 *basecolormap = NULL;
UINT8 *outlinecolormap = NULL;
@ -908,8 +908,8 @@ static int libd_drawScaledNameTag(lua_State *L)
const char *str;
INT32 flags;
fixed_t scale;
UINT8 basecolor;
UINT8 outlinecolor;
UINT16 basecolor;
UINT16 outlinecolor;
UINT8 *basecolormap = NULL;
UINT8 *outlinecolormap = NULL;
@ -966,7 +966,7 @@ static int libd_nameTagWidth(lua_State *L)
static int libd_getColormap(lua_State *L)
{
INT32 skinnum = TC_DEFAULT;
skincolors_t color = luaL_optinteger(L, 2, 0);
skincolornum_t color = luaL_optinteger(L, 2, 0);
UINT8* colormap = NULL;
HUDONLY
if (lua_isnoneornil(L, 1))

View file

@ -25,6 +25,9 @@
#include "lua_libs.h"
#include "lua_hud.h" // hud_running errors
extern CV_PossibleValue_t Color_cons_t[MAXSKINCOLORS+1];
extern void R_FlushTranslationColormapCache(void);
boolean LUA_CallAction(const char *action, mobj_t *actor);
state_t *astate;
@ -1465,6 +1468,229 @@ static int lib_luabankslen(lua_State *L)
return 1;
}
////////////////////
// SKINCOLOR INFO //
////////////////////
// Arbitrary skincolors[] table index -> skincolor_t *
static int lib_getSkinColor(lua_State *L)
{
UINT32 i;
lua_remove(L, 1);
i = luaL_checkinteger(L, 1);
if (!i || i >= numskincolors)
return luaL_error(L, "skincolors[] index %d out of range (1 - %d)", i, numskincolors-1);
LUA_PushUserdata(L, &skincolors[i], META_SKINCOLOR);
return 1;
}
//Set the entire c->ramp array
static void setRamp(lua_State *L, skincolor_t* c) {
UINT32 i;
lua_pushnil(L);
for (i=0; i<COLORRAMPSIZE; i++) {
if (lua_objlen(L,-2)<COLORRAMPSIZE) {
luaL_error(L, LUA_QL("skincolor_t") " field 'ramp' must be %d entries long; got %d.", COLORRAMPSIZE, lua_objlen(L,-2));
break;
}
if (lua_next(L, -2) != 0) {
c->ramp[i] = lua_isnumber(L,-1) ? (UINT8)luaL_checkinteger(L,-1) : 120;
lua_pop(L, 1);
} else
c->ramp[i] = 120;
}
lua_pop(L,1);
}
// Lua table full of data -> skincolors[]
static int lib_setSkinColor(lua_State *L)
{
UINT32 j;
skincolor_t *info;
UINT16 cnum; //skincolor num
lua_remove(L, 1); // don't care about skincolors[] userdata.
{
cnum = (UINT16)luaL_checkinteger(L, 1);
if (cnum < SKINCOLOR_FIRSTFREESLOT || cnum >= numskincolors)
return luaL_error(L, "skincolors[] index %d out of range (%d - %d)", cnum, SKINCOLOR_FIRSTFREESLOT, numskincolors-1);
info = &skincolors[cnum]; // get the skincolor to assign to.
}
luaL_checktype(L, 2, LUA_TTABLE); // check that we've been passed a table.
lua_remove(L, 1); // pop skincolor num, don't need it any more.
lua_settop(L, 1); // cut the stack here. the only thing left now is the table of data we're assigning to the skincolor.
if (hud_running)
return luaL_error(L, "Do not alter skincolors in HUD rendering code!");
// clear the skincolor to start with, in case of missing table elements
memset(info,0,sizeof(skincolor_t));
Color_cons_t[cnum].value = cnum;
lua_pushnil(L);
while (lua_next(L, 1)) {
lua_Integer i = 0;
const char *str = NULL;
if (lua_isnumber(L, 2))
i = lua_tointeger(L, 2);
else
str = luaL_checkstring(L, 2);
if (i == 1 || (str && fastcmp(str,"name"))) {
const char* n = luaL_checkstring(L, 3);
strlcpy(info->name, n, MAXCOLORNAME+1);
if (strlen(n) > MAXCOLORNAME)
CONS_Alert(CONS_WARNING, "skincolor_t field 'name' ('%s') longer than %d chars; shortened to %s.\n", n, MAXCOLORNAME, info->name);
} else if (i == 2 || (str && fastcmp(str,"ramp"))) {
if (!lua_istable(L, 3) && luaL_checkudata(L, 3, META_COLORRAMP) == NULL)
return luaL_error(L, LUA_QL("skincolor_t") " field 'ramp' must be a table or array.");
else if (lua_istable(L, 3))
setRamp(L, info);
else
for (j=0; j<COLORRAMPSIZE; j++)
info->ramp[j] = (*((UINT8 **)luaL_checkudata(L, 3, META_COLORRAMP)))[j];
R_FlushTranslationColormapCache();
} else if (i == 3 || (str && fastcmp(str,"invcolor")))
info->invcolor = (UINT16)luaL_checkinteger(L, 3);
else if (i == 4 || (str && fastcmp(str,"invshade")))
info->invshade = (UINT8)luaL_checkinteger(L, 3)%COLORRAMPSIZE;
else if (i == 5 || (str && fastcmp(str,"chatcolor")))
info->chatcolor = (UINT16)luaL_checkinteger(L, 3);
else if (i == 6 || (str && fastcmp(str,"accessible"))) {
boolean v = lua_isboolean(L,3) ? lua_toboolean(L, 3) : true;
if (cnum < FIRSTSUPERCOLOR && v != skincolors[cnum].accessible)
return luaL_error(L, "skincolors[] index %d is a standard color; accessibility changes are prohibited.", i);
else
info->accessible = v;
}
lua_pop(L, 1);
}
return 0;
}
// #skincolors -> numskincolors
static int lib_skincolorslen(lua_State *L)
{
lua_pushinteger(L, numskincolors);
return 1;
}
// skincolor_t *, field -> number
static int skincolor_get(lua_State *L)
{
skincolor_t *info = *((skincolor_t **)luaL_checkudata(L, 1, META_SKINCOLOR));
const char *field = luaL_checkstring(L, 2);
I_Assert(info != NULL);
I_Assert(info >= skincolors);
if (fastcmp(field,"name"))
lua_pushstring(L, info->name);
else if (fastcmp(field,"ramp"))
LUA_PushUserdata(L, info->ramp, META_COLORRAMP);
else if (fastcmp(field,"invcolor"))
lua_pushinteger(L, info->invcolor);
else if (fastcmp(field,"invshade"))
lua_pushinteger(L, info->invshade);
else if (fastcmp(field,"chatcolor"))
lua_pushinteger(L, info->chatcolor);
else if (fastcmp(field,"accessible"))
lua_pushboolean(L, info->accessible);
else
CONS_Debug(DBG_LUA, M_GetText("'%s' has no field named '%s'; returning nil.\n"), "skincolor_t", field);
return 1;
}
// skincolor_t *, field, number -> skincolors[]
static int skincolor_set(lua_State *L)
{
UINT32 i;
skincolor_t *info = *((skincolor_t **)luaL_checkudata(L, 1, META_SKINCOLOR));
const char *field = luaL_checkstring(L, 2);
I_Assert(info != NULL);
I_Assert(info >= skincolors);
if (info-skincolors < SKINCOLOR_FIRSTFREESLOT || info-skincolors >= numskincolors)
return luaL_error(L, "skincolors[] index %d out of range (%d - %d)", info-skincolors, SKINCOLOR_FIRSTFREESLOT, numskincolors-1);
if (fastcmp(field,"name")) {
const char* n = luaL_checkstring(L, 3);
if (strchr(n, ' ') != NULL)
CONS_Alert(CONS_WARNING, "skincolor_t field 'name' ('%s') contains spaces.\n", n);
strlcpy(info->name, n, MAXCOLORNAME+1);
if (strlen(n) > MAXCOLORNAME)
CONS_Alert(CONS_WARNING, "skincolor_t field 'name' ('%s') longer than %d chars; clipped to %s.\n", n, MAXCOLORNAME, info->name);
} else if (fastcmp(field,"ramp")) {
if (!lua_istable(L, 3) && luaL_checkudata(L, 3, META_COLORRAMP) == NULL)
return luaL_error(L, LUA_QL("skincolor_t") " field 'ramp' must be a table or array.");
else if (lua_istable(L, 3))
setRamp(L, info);
else
for (i=0; i<COLORRAMPSIZE; i++)
info->ramp[i] = (*((UINT8 **)luaL_checkudata(L, 3, META_COLORRAMP)))[i];
R_FlushTranslationColormapCache();
} else if (fastcmp(field,"invcolor"))
info->invcolor = (UINT16)luaL_checkinteger(L, 3);
else if (fastcmp(field,"invshade"))
info->invshade = (UINT8)luaL_checkinteger(L, 3)%COLORRAMPSIZE;
else if (fastcmp(field,"chatcolor"))
info->chatcolor = (UINT16)luaL_checkinteger(L, 3);
else if (fastcmp(field,"accessible"))
info->accessible = lua_isboolean(L,3);
else
CONS_Debug(DBG_LUA, M_GetText("'%s' has no field named '%s'; returning nil.\n"), "skincolor_t", field);
return 1;
}
// skincolor_t * -> SKINCOLOR_*
static int skincolor_num(lua_State *L)
{
skincolor_t *info = *((skincolor_t **)luaL_checkudata(L, 1, META_SKINCOLOR));
I_Assert(info != NULL);
I_Assert(info >= skincolors);
lua_pushinteger(L, info-skincolors);
return 1;
}
// ramp, n -> ramp[n]
static int colorramp_get(lua_State *L)
{
UINT8 *colorramp = *((UINT8 **)luaL_checkudata(L, 1, META_COLORRAMP));
UINT32 n = luaL_checkinteger(L, 2);
if (n >= COLORRAMPSIZE)
return luaL_error(L, LUA_QL("skincolor_t") " field 'ramp' index %d out of range (0 - %d)", n, COLORRAMPSIZE-1);
lua_pushinteger(L, colorramp[n]);
return 1;
}
// ramp, n, value -> ramp[n] = value
static int colorramp_set(lua_State *L)
{
UINT8 *colorramp = *((UINT8 **)luaL_checkudata(L, 1, META_COLORRAMP));
UINT16 cnum = (UINT16)(((uint8_t*)colorramp - (uint8_t*)(skincolors[0].ramp))/sizeof(skincolor_t));
UINT32 n = luaL_checkinteger(L, 2);
UINT8 i = (UINT8)luaL_checkinteger(L, 3);
if (cnum < SKINCOLOR_FIRSTFREESLOT || cnum >= numskincolors)
return luaL_error(L, "skincolors[] index %d out of range (%d - %d)", cnum, SKINCOLOR_FIRSTFREESLOT, numskincolors-1);
if (n >= COLORRAMPSIZE)
return luaL_error(L, LUA_QL("skincolor_t") " field 'ramp' index %d out of range (0 - %d)", n, COLORRAMPSIZE-1);
if (hud_running)
return luaL_error(L, "Do not alter skincolor_t in HUD rendering code!");
colorramp[n] = i;
R_FlushTranslationColormapCache();
return 0;
}
// #ramp -> COLORRAMPSIZE
static int colorramp_len(lua_State *L)
{
lua_pushinteger(L, COLORRAMPSIZE);
return 1;
}
//////////////////////////////
//
// Now push all these functions into the Lua state!
@ -1502,6 +1728,28 @@ int LUA_InfoLib(lua_State *L)
lua_setfield(L, -2, "__len");
lua_pop(L, 1);
luaL_newmetatable(L, META_SKINCOLOR);
lua_pushcfunction(L, skincolor_get);
lua_setfield(L, -2, "__index");
lua_pushcfunction(L, skincolor_set);
lua_setfield(L, -2, "__newindex");
lua_pushcfunction(L, skincolor_num);
lua_setfield(L, -2, "__len");
lua_pop(L, 1);
luaL_newmetatable(L, META_COLORRAMP);
lua_pushcfunction(L, colorramp_get);
lua_setfield(L, -2, "__index");
lua_pushcfunction(L, colorramp_set);
lua_setfield(L, -2, "__newindex");
lua_pushcfunction(L, colorramp_len);
lua_setfield(L, -2, "__len");
lua_pop(L,1);
luaL_newmetatable(L, META_SFXINFO);
lua_pushcfunction(L, sfxinfo_get);
lua_setfield(L, -2, "__index");
@ -1605,6 +1853,19 @@ int LUA_InfoLib(lua_State *L)
lua_setmetatable(L, -2);
lua_setglobal(L, "mobjinfo");
lua_newuserdata(L, 0);
lua_createtable(L, 0, 2);
lua_pushcfunction(L, lib_getSkinColor);
lua_setfield(L, -2, "__index");
lua_pushcfunction(L, lib_setSkinColor);
lua_setfield(L, -2, "__newindex");
lua_pushcfunction(L, lib_skincolorslen);
lua_setfield(L, -2, "__len");
lua_setmetatable(L, -2);
lua_setglobal(L, "skincolors");
lua_newuserdata(L, 0);
lua_createtable(L, 0, 2);
lua_pushcfunction(L, lib_getSfxInfo);

View file

@ -20,6 +20,8 @@ extern lua_State *gL;
#define META_STATE "STATE_T*"
#define META_MOBJINFO "MOBJINFO_T*"
#define META_SFXINFO "SFXINFO_T*"
#define META_SKINCOLOR "SKINCOLOR_T*"
#define META_COLORRAMP "SKINCOLOR_T*RAMP"
#define META_SPRITEINFO "SPRITEINFO_T*"
#define META_PIVOTLIST "SPRITEFRAMEPIVOT_T[]"
#define META_FRAMEPIVOT "SPRITEFRAMEPIVOT_T*"

View file

@ -139,6 +139,7 @@ static const char *const side_opt[] = {
"toptexture",
"bottomtexture",
"midtexture",
"line",
"sector",
"special",
"repeatcnt",
@ -2009,6 +2010,8 @@ static int mapheaderinfo_get(lua_State *L)
lua_pushinteger(L, header->typeoflevel);
else if (fastcmp(field,"nextlevel"))
lua_pushinteger(L, header->nextlevel);
else if (fastcmp(field,"marathonnext"))
lua_pushinteger(L, header->marathonnext);
else if (fastcmp(field,"keywords"))
lua_pushstring(L, header->keywords);
else if (fastcmp(field,"musname"))

View file

@ -113,7 +113,8 @@ static int lib_fixeddiv(lua_State *L)
static int lib_fixedrem(lua_State *L)
{
lua_pushfixed(L, FixedRem(luaL_checkfixed(L, 1), luaL_checkfixed(L, 2)));
LUA_Deprecated(L, "FixedRem(a, b)", "a % b");
lua_pushfixed(L, luaL_checkfixed(L, 1) % luaL_checkfixed(L, 2));
return 1;
}
@ -172,15 +173,14 @@ static int lib_all7emeralds(lua_State *L)
return 1;
}
// Whee, special Lua-exclusive function for making use of Color_Opposite[]
// Returns both color and signpost shade numbers!
static int lib_coloropposite(lua_State *L)
{
UINT8 colornum = (UINT8)luaL_checkinteger(L, 1);
if (!colornum || colornum >= MAXSKINCOLORS)
return luaL_error(L, "skincolor %d out of range (1 - %d).", colornum, MAXSKINCOLORS-1);
lua_pushinteger(L, Color_Opposite[colornum-1][0]); // push color
lua_pushinteger(L, Color_Opposite[colornum-1][1]); // push sign shade index, 0-15
UINT16 colornum = (UINT16)luaL_checkinteger(L, 1);
if (!colornum || colornum >= numskincolors)
return luaL_error(L, "skincolor %d out of range (1 - %d).", colornum, numskincolors-1);
lua_pushinteger(L, skincolors[colornum].invcolor); // push color
lua_pushinteger(L, skincolors[colornum].invshade); // push sign shade index, 0-15
return 2;
}

View file

@ -86,6 +86,7 @@ enum mobj_e {
mobj_cvmem,
mobj_standingslope,
mobj_colorized,
mobj_mirrored,
mobj_shadowscale
};
@ -152,6 +153,7 @@ static const char *const mobj_opt[] = {
"cvmem",
"standingslope",
"colorized",
"mirrored",
"shadowscale",
NULL};
@ -385,6 +387,9 @@ static int mobj_get(lua_State *L)
case mobj_colorized:
lua_pushboolean(L, mo->colorized);
break;
case mobj_mirrored:
lua_pushboolean(L, mo->mirrored);
break;
case mobj_shadowscale:
lua_pushfixed(L, mo->shadowscale);
break;
@ -449,10 +454,8 @@ static int mobj_set(lua_State *L)
return UNIMPLEMENTED;
case mobj_angle:
mo->angle = luaL_checkangle(L, 3);
if (mo->player == &players[consoleplayer])
localangle = mo->angle;
else if (mo->player == &players[secondarydisplayplayer])
localangle2 = mo->angle;
if (mo->player)
P_SetPlayerAngle(mo->player, mo->angle);
break;
case mobj_rollangle:
mo->rollangle = luaL_checkangle(L, 3);
@ -574,9 +577,9 @@ static int mobj_set(lua_State *L)
}
case mobj_color:
{
UINT8 newcolor = (UINT8)luaL_checkinteger(L,3);
if (newcolor >= MAXTRANSLATIONS)
return luaL_error(L, "mobj.color %d out of range (0 - %d).", newcolor, MAXTRANSLATIONS-1);
UINT16 newcolor = (UINT16)luaL_checkinteger(L,3);
if (newcolor >= numskincolors)
return luaL_error(L, "mobj.color %d out of range (0 - %d).", newcolor, numskincolors-1);
mo->color = newcolor;
break;
}
@ -715,6 +718,9 @@ static int mobj_set(lua_State *L)
case mobj_colorized:
mo->colorized = luaL_checkboolean(L, 3);
break;
case mobj_mirrored:
mo->mirrored = luaL_checkboolean(L, 3);
break;
case mobj_shadowscale:
mo->shadowscale = luaL_checkfixed(L, 3);
break;
@ -829,6 +835,15 @@ static int mapthing_set(lua_State *L)
return 0;
}
static int mapthing_num(lua_State *L)
{
mapthing_t *mt = *((mapthing_t **)luaL_checkudata(L, 1, META_MAPTHING));
if (!mt)
return luaL_error(L, "accessed mapthing_t doesn't exist anymore.");
lua_pushinteger(L, mt-mapthings);
return 1;
}
static int lib_iterateMapthings(lua_State *L)
{
size_t i = 0;
@ -893,6 +908,9 @@ int LUA_MobjLib(lua_State *L)
lua_pushcfunction(L, mapthing_set);
lua_setfield(L, -2, "__newindex");
lua_pushcfunction(L, mapthing_num);
lua_setfield(L, -2, "__len");
lua_pop(L,1);
lua_newuserdata(L, 0);

View file

@ -461,9 +461,9 @@ static int player_set(lua_State *L)
plr->flashpal = (UINT16)luaL_checkinteger(L, 3);
else if (fastcmp(field,"skincolor"))
{
UINT8 newcolor = (UINT8)luaL_checkinteger(L,3);
if (newcolor >= MAXSKINCOLORS)
return luaL_error(L, "player.skincolor %d out of range (0 - %d).", newcolor, MAXSKINCOLORS-1);
UINT16 newcolor = (UINT16)luaL_checkinteger(L,3);
if (newcolor >= numskincolors)
return luaL_error(L, "player.skincolor %d out of range (0 - %d).", newcolor, numskincolors-1);
plr->skincolor = newcolor;
}
else if (fastcmp(field,"score"))

View file

@ -78,6 +78,58 @@ FUNCNORETURN static int LUA_Panic(lua_State *L)
#endif
}
#define LEVELS1 12 // size of the first part of the stack
#define LEVELS2 10 // size of the second part of the stack
// Error handler used with pcall() when loading scripts or calling hooks
// Takes a string with the original error message,
// appends the traceback to it, and return the result
int LUA_GetErrorMessage(lua_State *L)
{
int level = 1;
int firstpart = 1; // still before eventual `...'
lua_Debug ar;
lua_pushliteral(L, "\nstack traceback:");
while (lua_getstack(L, level++, &ar))
{
if (level > LEVELS1 && firstpart)
{
// no more than `LEVELS2' more levels?
if (!lua_getstack(L, level + LEVELS2, &ar))
level--; // keep going
else
{
lua_pushliteral(L, "\n ..."); // too many levels
while (lua_getstack(L, level + LEVELS2, &ar)) // find last levels
level++;
}
firstpart = 0;
continue;
}
lua_pushliteral(L, "\n ");
lua_getinfo(L, "Snl", &ar);
lua_pushfstring(L, "%s:", ar.short_src);
if (ar.currentline > 0)
lua_pushfstring(L, "%d:", ar.currentline);
if (*ar.namewhat != '\0') // is there a name?
lua_pushfstring(L, " in function " LUA_QS, ar.name);
else
{
if (*ar.what == 'm') // main?
lua_pushfstring(L, " in main chunk");
else if (*ar.what == 'C' || *ar.what == 't')
lua_pushliteral(L, " ?"); // C function or tail call
else
lua_pushfstring(L, " in function <%s:%d>",
ar.short_src, ar.linedefined);
}
lua_concat(L, lua_gettop(L));
}
lua_concat(L, lua_gettop(L));
return 1;
}
// Moved here from lib_getenum.
int LUA_PushGlobals(lua_State *L, const char *word)
{
@ -115,7 +167,10 @@ int LUA_PushGlobals(lua_State *L, const char *word)
lua_pushboolean(L, splitscreen);
return 1;
} else if (fastcmp(word,"gamecomplete")) {
lua_pushboolean(L, gamecomplete);
lua_pushboolean(L, (gamecomplete != 0));
return 1;
} else if (fastcmp(word,"marathonmode")) {
lua_pushinteger(L, marathonmode);
return 1;
} else if (fastcmp(word,"devparm")) {
lua_pushboolean(L, devparm);
@ -145,6 +200,9 @@ int LUA_PushGlobals(lua_State *L, const char *word)
} else if (fastcmp(word,"spstage_start")) {
lua_pushinteger(L, spstage_start);
return 1;
} else if (fastcmp(word,"spmarathon_start")) {
lua_pushinteger(L, spmarathon_start);
return 1;
} else if (fastcmp(word,"sstage_start")) {
lua_pushinteger(L, sstage_start);
return 1;
@ -268,6 +326,12 @@ int LUA_PushGlobals(lua_State *L, const char *word)
return 0;
LUA_PushUserdata(L, &players[secondarydisplayplayer], META_PLAYER);
return 1;
} else if (fastcmp(word,"isserver")) {
lua_pushboolean(L, server);
return 1;
} else if (fastcmp(word,"isdedicatedserver")) {
lua_pushboolean(L, dedicated);
return 1;
// end local player variables
} else if (fastcmp(word,"server")) {
if ((!multiplayer || !netgame) && !playeringame[serverplayer])
@ -395,11 +459,13 @@ void LUA_ClearExtVars(void)
// Use this variable to prevent certain functions from running
// if they were not called on lump load
// (i.e. they were called in hooks or coroutines etc)
boolean lua_lumploading = false;
INT32 lua_lumploading = 0;
// Load a script from a MYFILE
static inline void LUA_LoadFile(MYFILE *f, char *name)
static inline void LUA_LoadFile(MYFILE *f, char *name, boolean noresults)
{
int errorhandlerindex;
if (!name)
name = wadfiles[f->wad]->filename;
CONS_Printf("Loading Lua script from %s\n", name);
@ -408,19 +474,22 @@ static inline void LUA_LoadFile(MYFILE *f, char *name)
lua_pushinteger(gL, f->wad);
lua_setfield(gL, LUA_REGISTRYINDEX, "WAD");
lua_lumploading = true; // turn on loading flag
lua_lumploading++; // turn on loading flag
if (luaL_loadbuffer(gL, f->data, f->size, va("@%s",name)) || lua_pcall(gL, 0, 0, 0)) {
lua_pushcfunction(gL, LUA_GetErrorMessage);
errorhandlerindex = lua_gettop(gL);
if (luaL_loadbuffer(gL, f->data, f->size, va("@%s",name)) || lua_pcall(gL, 0, noresults ? 0 : LUA_MULTRET, lua_gettop(gL) - 1)) {
CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL,-1));
lua_pop(gL,1);
}
lua_gc(gL, LUA_GCCOLLECT, 0);
lua_remove(gL, errorhandlerindex);
lua_lumploading = false; // turn off again
lua_lumploading--; // turn off again
}
// Load a script from a lump
void LUA_LoadLump(UINT16 wad, UINT16 lump)
void LUA_LoadLump(UINT16 wad, UINT16 lump, boolean noresults)
{
MYFILE f;
char *name;
@ -447,7 +516,7 @@ void LUA_LoadLump(UINT16 wad, UINT16 lump)
name[len] = '\0';
}
LUA_LoadFile(&f, name); // actually load file!
LUA_LoadFile(&f, name, noresults); // actually load file!
free(name);
Z_Free(f.data);
@ -756,6 +825,7 @@ enum
ARCH_FFLOOR,
ARCH_SLOPE,
ARCH_MAPHEADER,
ARCH_SKINCOLOR,
ARCH_TEND=0xFF,
};
@ -781,6 +851,7 @@ static const struct {
{META_FFLOOR, ARCH_FFLOOR},
{META_SLOPE, ARCH_SLOPE},
{META_MAPHEADER, ARCH_MAPHEADER},
{META_SKINCOLOR, ARCH_SKINCOLOR},
{NULL, ARCH_NULL}
};
@ -1068,6 +1139,14 @@ static UINT8 ArchiveValue(int TABLESINDEX, int myindex)
}
break;
}
case ARCH_SKINCOLOR:
{
skincolor_t *info = *((skincolor_t **)lua_touserdata(gL, myindex));
WRITEUINT8(save_p, ARCH_SKINCOLOR);
WRITEUINT16(save_p, info - skincolors);
break;
}
default:
WRITEUINT8(save_p, ARCH_NULL);
return 2;
@ -1299,6 +1378,9 @@ static UINT8 UnArchiveValue(int TABLESINDEX)
case ARCH_MAPHEADER:
LUA_PushUserdata(gL, mapheaderinfo[READUINT16(save_p)], META_MAPHEADER);
break;
case ARCH_SKINCOLOR:
LUA_PushUserdata(gL, &skincolors[READUINT16(save_p)], META_SKINCOLOR);
break;
case ARCH_TEND:
return 1;
}

View file

@ -37,9 +37,10 @@
void LUA_ClearExtVars(void);
#endif
extern boolean lua_lumploading; // is LUA_LoadLump being called?
extern INT32 lua_lumploading; // is LUA_LoadLump being called?
void LUA_LoadLump(UINT16 wad, UINT16 lump);
int LUA_GetErrorMessage(lua_State *L);
void LUA_LoadLump(UINT16 wad, UINT16 lump, boolean noresults);
#ifdef LUA_ALLOW_BYTECODE
void LUA_DumpFile(const char *filename);
#endif
@ -99,5 +100,8 @@ void COM_Lua_f(void);
// uncomment if you want seg_t/node_t in Lua
// #define HAVE_LUA_SEGS
#define INLEVEL if (gamestate != GS_LEVEL && !titlemapinaction)\
#define ISINLEVEL \
(gamestate == GS_LEVEL || titlemapinaction)
#define INLEVEL if (! ISINLEVEL)\
return luaL_error(L, "This can only be used in a level!");

View file

@ -18,6 +18,7 @@
#include "z_zone.h"
#include "v_video.h"
#include "i_video.h"
#include "i_system.h" // I_GetTimeMicros
#include "m_misc.h"
#include "st_stuff.h" // st_palette
@ -30,11 +31,13 @@
consvar_t cv_gif_optimize = {"gif_optimize", "On", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_gif_downscale = {"gif_downscale", "On", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_gif_dynamicdelay = {"gif_dynamicdelay", "On", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_gif_localcolortable = {"gif_localcolortable", "On", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL};
#ifdef HAVE_ANIGIF
static boolean gif_optimize = false; // So nobody can do something dumb
static boolean gif_downscale = false; // like changing cvars mid output
static boolean gif_dynamicdelay = false; // and messing something up
// Palette handling
static boolean gif_localcolortable = false;
@ -44,6 +47,7 @@ static RGBA_t *gif_framepalette = NULL;
static FILE *gif_out = NULL;
static INT32 gif_frames = 0;
static UINT32 gif_prevframems = 0;
static UINT8 gif_writeover = 0;
@ -588,11 +592,25 @@ static void GIF_framewrite(void)
// screen regions are handled in GIF_lzw
{
int d1 = (int)((100.0f/NEWTICRATE)*(gif_frames+1));
int d2 = (int)((100.0f/NEWTICRATE)*(gif_frames));
UINT16 delay = d1-d2;
UINT16 delay;
INT32 startline;
if (gif_dynamicdelay) {
// golden's attempt at creating a "dynamic delay"
float delayf = ceil(100.0f/NEWTICRATE);
delay = (UINT16)((I_GetTimeMicros() - gif_prevframems)/10/1000);
if (delay < (int)(delayf))
delay = (int)(delayf);
}
else
{
// the original code
int d1 = (int)((100.0f/NEWTICRATE)*(gif_frames+1));
int d2 = (int)((100.0f/NEWTICRATE)*(gif_frames));
delay = d1-d2;
}
WRITEMEM(p, gifframe_gchead, 4);
WRITEUINT16(p, delay);
@ -670,6 +688,7 @@ static void GIF_framewrite(void)
}
fwrite(gifframe_data, 1, (p - gifframe_data), gif_out);
++gif_frames;
gif_prevframems = I_GetTimeMicros();
}
@ -690,12 +709,14 @@ INT32 GIF_open(const char *filename)
gif_optimize = (!!cv_gif_optimize.value);
gif_downscale = (!!cv_gif_downscale.value);
gif_dynamicdelay = (!!cv_gif_dynamicdelay.value);
gif_localcolortable = (!!cv_gif_localcolortable.value);
gif_colorprofile = (!!cv_screenshot_colorprofile.value);
gif_headerpalette = GIF_getpalette(0);
GIF_headwrite();
gif_frames = 0;
gif_prevframems = I_GetTimeMicros();
return 1;
}

View file

@ -27,6 +27,6 @@ void GIF_frame(void);
INT32 GIF_close(void);
#endif
extern consvar_t cv_gif_optimize, cv_gif_downscale, cv_gif_localcolortable;
extern consvar_t cv_gif_optimize, cv_gif_downscale, cv_gif_dynamicdelay, cv_gif_localcolortable;
#endif

View file

@ -288,7 +288,7 @@ void Command_CheatGod_f(void)
plyr = &players[consoleplayer];
plyr->pflags ^= PF_GODMODE;
CONS_Printf(M_GetText("Sissy Mode %s\n"), plyr->pflags & PF_GODMODE ? M_GetText("On") : M_GetText("Off"));
CONS_Printf(M_GetText("Cheese Mode %s\n"), plyr->pflags & PF_GODMODE ? M_GetText("On") : M_GetText("Off"));
G_SetGameModified(multiplayer);
}
@ -555,7 +555,8 @@ void Command_Teleport_f(void)
p->mo->flags2 &= ~MF2_OBJECTFLIP;
}
localangle = p->mo->angle = p->drawangle = FixedAngle(mt->angle<<FRACBITS);
p->mo->angle = p->drawangle = FixedAngle(mt->angle<<FRACBITS);
P_SetPlayerAngle(p, p->mo->angle);
}
else // scan the thinkers to find starposts...
{
@ -619,7 +620,8 @@ void Command_Teleport_f(void)
p->mo->flags2 &= ~MF2_OBJECTFLIP;
}
localangle = p->mo->angle = p->drawangle = mo2->angle;
p->mo->angle = p->drawangle = mo2->angle;
P_SetPlayerAngle(p, p->mo->angle);
}
CONS_Printf(M_GetText("Teleporting to checkpoint %d, %d...\n"), starpostnum, starpostpath);
@ -673,7 +675,10 @@ void Command_Teleport_f(void)
i = COM_CheckParm("-ang");
if (i)
localangle = p->drawangle = p->mo->angle = FixedAngle(atoi(COM_Argv(i + 1))<<FRACBITS);
{
p->drawangle = p->mo->angle = FixedAngle(atoi(COM_Argv(i + 1))<<FRACBITS);
P_SetPlayerAngle(p, p->mo->angle);
}
i = COM_CheckParm("-aim");
if (i)
@ -978,7 +983,7 @@ static mobjflag2_t op_oldflags2 = 0;
static UINT32 op_oldeflags = 0;
static fixed_t op_oldmomx = 0, op_oldmomy = 0, op_oldmomz = 0, op_oldheight = 0;
static statenum_t op_oldstate = 0;
static UINT8 op_oldcolor = 0;
static UINT16 op_oldcolor = 0;
//
// Static calculation / common output help
@ -1032,8 +1037,8 @@ static boolean OP_HeightOkay(player_t *player, UINT8 ceiling)
if (ceiling)
{
// Truncate position to match where mapthing would be when spawned
// (this applies to every further P_GetZAt call as well)
fixed_t cheight = sec->c_slope ? P_GetZAt(sec->c_slope, player->mo->x & 0xFFFF0000, player->mo->y & 0xFFFF0000) : sec->ceilingheight;
// (this applies to every further P_GetSlopeZAt call as well)
fixed_t cheight = P_GetSectorCeilingZAt(sec, player->mo->x & 0xFFFF0000, player->mo->y & 0xFFFF0000);
if (((cheight - player->mo->z - player->mo->height)>>FRACBITS) >= (1 << (16-ZSHIFT)))
{
@ -1044,7 +1049,7 @@ static boolean OP_HeightOkay(player_t *player, UINT8 ceiling)
}
else
{
fixed_t fheight = sec->f_slope ? P_GetZAt(sec->f_slope, player->mo->x & 0xFFFF0000, player->mo->y & 0xFFFF0000) : sec->floorheight;
fixed_t fheight = P_GetSectorFloorZAt(sec, player->mo->x & 0xFFFF0000, player->mo->y & 0xFFFF0000);
if (((player->mo->z - fheight)>>FRACBITS) >= (1 << (16-ZSHIFT)))
{
CONS_Printf(M_GetText("Sorry, you're too %s to place this object (max: %d %s).\n"), M_GetText("high"),
@ -1091,12 +1096,12 @@ static mapthing_t *OP_CreateNewMapThing(player_t *player, UINT16 type, boolean c
mt->y = (INT16)(player->mo->y>>FRACBITS);
if (ceiling)
{
fixed_t cheight = sec->c_slope ? P_GetZAt(sec->c_slope, mt->x << FRACBITS, mt->y << FRACBITS) : sec->ceilingheight;
fixed_t cheight = P_GetSectorCeilingZAt(sec, mt->x << FRACBITS, mt->y << FRACBITS);
mt->z = (UINT16)((cheight - player->mo->z - player->mo->height)>>FRACBITS);
}
else
{
fixed_t fheight = sec->f_slope ? P_GetZAt(sec->f_slope, mt->x << FRACBITS, mt->y << FRACBITS) : sec->floorheight;
fixed_t fheight = P_GetSectorFloorZAt(sec, mt->x << FRACBITS, mt->y << FRACBITS);
mt->z = (UINT16)((player->mo->z - fheight)>>FRACBITS);
}
mt->angle = (INT16)(FixedInt(AngleFixed(player->mo->angle)));
@ -1342,12 +1347,12 @@ void OP_ObjectplaceMovement(player_t *player)
if (!!(mobjinfo[op_currentthing].flags & MF_SPAWNCEILING) ^ !!(cv_opflags.value & MTF_OBJECTFLIP))
{
fixed_t cheight = sec->c_slope ? P_GetZAt(sec->c_slope, player->mo->x & 0xFFFF0000, player->mo->y & 0xFFFF0000) : sec->ceilingheight;
fixed_t cheight = P_GetSectorCeilingZAt(sec, player->mo->x & 0xFFFF0000, player->mo->y & 0xFFFF0000);
op_displayflags = (UINT16)((cheight - player->mo->z - mobjinfo[op_currentthing].height)>>FRACBITS);
}
else
{
fixed_t fheight = sec->f_slope ? P_GetZAt(sec->f_slope, player->mo->x & 0xFFFF0000, player->mo->y & 0xFFFF0000) : sec->floorheight;
fixed_t fheight = P_GetSectorFloorZAt(sec, player->mo->x & 0xFFFF0000, player->mo->y & 0xFFFF0000);
op_displayflags = (UINT16)((player->mo->z - fheight)>>FRACBITS);
}
op_displayflags <<= ZSHIFT;

View file

@ -523,9 +523,9 @@ emblem_t *M_GetLevelEmblems(INT32 mapnum)
return NULL;
}
skincolors_t M_GetEmblemColor(emblem_t *em)
skincolornum_t M_GetEmblemColor(emblem_t *em)
{
if (!em || em->color >= MAXSKINCOLORS)
if (!em || em->color >= numskincolors)
return SKINCOLOR_NONE;
return em->color;
}
@ -549,9 +549,9 @@ const char *M_GetEmblemPatch(emblem_t *em, boolean big)
return pnamebuf;
}
skincolors_t M_GetExtraEmblemColor(extraemblem_t *em)
skincolornum_t M_GetExtraEmblemColor(extraemblem_t *em)
{
if (!em || em->color >= MAXSKINCOLORS)
if (!em || em->color >= numskincolors)
return SKINCOLOR_NONE;
return em->color;
}

View file

@ -90,7 +90,7 @@ typedef struct
INT16 tag; ///< Tag of emblem mapthing
INT16 level; ///< Level on which this emblem can be found.
UINT8 sprite; ///< emblem sprite to use, 0 - 25
UINT8 color; ///< skincolor to use
UINT16 color; ///< skincolor to use
INT32 var; ///< If needed, specifies information on the target amount to achieve (or target skin)
char hint[110]; ///< Hint for emblem hints menu
UINT8 collected; ///< Do you have this emblem?
@ -102,7 +102,7 @@ typedef struct
UINT8 conditionset; ///< Condition set that awards this emblem.
UINT8 showconditionset; ///< Condition set that shows this emblem.
UINT8 sprite; ///< emblem sprite to use, 0 - 25
UINT8 color; ///< skincolor to use
UINT16 color; ///< skincolor to use
UINT8 collected; ///< Do you have this emblem?
} extraemblem_t;
@ -172,9 +172,9 @@ INT32 M_CountEmblems(void);
// Emblem shit
emblem_t *M_GetLevelEmblems(INT32 mapnum);
skincolors_t M_GetEmblemColor(emblem_t *em);
skincolornum_t M_GetEmblemColor(emblem_t *em);
const char *M_GetEmblemPatch(emblem_t *em, boolean big);
skincolors_t M_GetExtraEmblemColor(extraemblem_t *em);
skincolornum_t M_GetExtraEmblemColor(extraemblem_t *em);
const char *M_GetExtraEmblemPatch(extraemblem_t *em, boolean big);
// If you're looking to compare stats for unlocks or what not, use these

View file

@ -204,18 +204,6 @@ FUNCMATH FUNCINLINE static ATTRINLINE fixed_t FixedDiv(fixed_t a, fixed_t b)
return FixedDiv2(a, b);
}
/** \brief The FixedRem function
\param x fixed_t number
\param y fixed_t number
\return remainder of dividing x by y
*/
FUNCMATH FUNCINLINE static ATTRINLINE fixed_t FixedRem(fixed_t x, fixed_t y)
{
return x % y;
}
/** \brief The FixedSqrt function
\param x fixed_t number

File diff suppressed because it is too large Load diff

View file

@ -21,6 +21,9 @@
#include "r_skins.h" // for SKINNAMESIZE
#include "f_finale.h" // for ttmode_enum
// Compatibility with old-style named NiGHTS replay files.
#define OLDNREPLAYNAME
//
// MENUS
//
@ -61,6 +64,8 @@ typedef enum
MN_SP_NIGHTS_REPLAY,
MN_SP_NIGHTS_GHOST,
MN_SP_MARATHON,
// Multiplayer
MN_MP_MAIN,
MN_MP_SPLITSCREEN, // SplitServer
@ -92,8 +97,6 @@ typedef enum
MN_OP_COLOR,
MN_OP_OPENGL,
MN_OP_OPENGL_LIGHTING,
MN_OP_OPENGL_FOG,
MN_OP_OPENGL_COLOR,
MN_OP_SOUND,
@ -355,11 +358,11 @@ typedef struct
// new character select
char displayname[SKINNAMESIZE+1];
SINT8 skinnum[2];
UINT8 oppositecolor;
UINT16 oppositecolor;
char nametag[8];
patch_t *namepic;
UINT8 tagtextcolor;
UINT8 tagoutlinecolor;
UINT16 tagtextcolor;
UINT16 tagoutlinecolor;
} description_t;
// level select platter
@ -419,6 +422,7 @@ extern INT16 char_on, startchar;
#define MAXSAVEGAMES 31
#define NOSAVESLOT 0 //slot where Play Without Saving appears
#define MARATHONSLOT 420 // just has to be nonzero, but let's use one that'll show up as an obvious error if something goes wrong while not using our existing saves
#define BwehHehHe() S_StartSound(NULL, sfx_bewar1+M_RandomKey(4)) // Bweh heh he
@ -443,6 +447,23 @@ void Addons_option_Onchange(void);
// Moviemode menu updating
void Moviemode_option_Onchange(void);
// Player Setup menu colors linked list
typedef struct menucolor_s {
struct menucolor_s *next;
struct menucolor_s *prev;
UINT16 color;
} menucolor_t;
extern menucolor_t *menucolorhead, *menucolortail;
void M_AddMenuColor(UINT16 color);
void M_MoveColorBefore(UINT16 color, UINT16 targ);
void M_MoveColorAfter(UINT16 color, UINT16 targ);
UINT16 M_GetColorBefore(UINT16 color);
UINT16 M_GetColorAfter(UINT16 color);
void M_InitPlayerSetupColors(void);
void M_FreePlayerSetupColors(void);
// These defines make it a little easier to make menus
#define DEFAULTMENUSTYLE(id, header, source, prev, x, y)\
{\

View file

@ -297,6 +297,44 @@ size_t FIL_ReadFileTag(char const *name, UINT8 **buffer, INT32 tag)
return length;
}
/** Makes a copy of a text file with all newlines converted into LF newlines.
*
* \param textfilename The name of the source file
* \param binfilename The name of the destination file
*/
boolean FIL_ConvertTextFileToBinary(const char *textfilename, const char *binfilename)
{
FILE *textfile;
FILE *binfile;
UINT8 buffer[1024];
size_t count;
boolean success;
textfile = fopen(textfilename, "r");
if (!textfile)
return false;
binfile = fopen(binfilename, "wb");
if (!binfile)
{
fclose(textfile);
return false;
}
do
{
count = fread(buffer, 1, sizeof(buffer), textfile);
fwrite(buffer, 1, count, binfile);
} while (count);
success = !(ferror(textfile) || ferror(binfile));
fclose(textfile);
fclose(binfile);
return success;
}
/** Check if the filename exists
*
* \param name Filename to check.

View file

@ -48,6 +48,8 @@ boolean FIL_WriteFile(char const *name, const void *source, size_t length);
size_t FIL_ReadFileTag(char const *name, UINT8 **buffer, INT32 tag);
#define FIL_ReadFile(n, b) FIL_ReadFileTag(n, b, PU_STATIC)
boolean FIL_ConvertTextFileToBinary(const char *textfilename, const char *binfilename);
boolean FIL_FileExists(const char *name);
boolean FIL_WriteFileOK(char const *name);
boolean FIL_ReadFileOK(char const *name);

View file

@ -3858,7 +3858,7 @@ void A_Explode(mobj_t *actor)
if (LUA_CallAction("A_Explode", actor))
return;
P_RadiusAttack(actor, actor->target, actor->info->damage, locvar1);
P_RadiusAttack(actor, actor->target, actor->info->damage, locvar1, true);
}
// Function: A_BossDeath
@ -4950,7 +4950,7 @@ void A_ThrownRing(mobj_t *actor)
continue;
// Don't home in on teammates.
if ((gametyperules & GTR_TEAMFLAGS)
if ((gametyperules & GTR_TEAMS)
&& actor->target->player->ctfteam == player->ctfteam)
continue;
}
@ -5106,13 +5106,15 @@ void A_SignPlayer(mobj_t *actor)
INT32 locvar2 = var2;
skin_t *skin = NULL;
mobj_t *ov;
UINT8 facecolor, signcolor = (UINT8)locvar2;
UINT16 facecolor, signcolor = 0;
UINT32 signframe = states[actor->info->raisestate].frame;
facecolor = signcolor = (UINT16)locvar2;
if (LUA_CallAction("A_SignPlayer", actor))
return;
if (actor->tracer == NULL || locvar1 < -3 || locvar1 >= numskins || signcolor >= MAXTRANSLATIONS)
if (actor->tracer == NULL || locvar1 < -3 || locvar1 >= numskins || signcolor >= numskincolors)
return;
// if no face overlay, spawn one
@ -5140,12 +5142,10 @@ void A_SignPlayer(mobj_t *actor)
;
else if (!skin->sprites[SPR2_SIGN].numframes)
signcolor = facecolor;
else if ((actor->target->player->skincolor == skin->prefcolor) && (skin->prefoppositecolor)) // Set it as the skin's preferred oppositecolor?
else if ((facecolor == skin->prefcolor) && (skin->prefoppositecolor)) // Set it as the skin's preferred oppositecolor?
signcolor = skin->prefoppositecolor;
else if (actor->target->player->skincolor) // Set the sign to be an appropriate background color for this player's skincolor.
signcolor = Color_Opposite[actor->target->player->skincolor - 1][0];
else
signcolor = SKINCOLOR_NONE;
else if (facecolor) // Set the sign to be an appropriate background color for this player's skincolor.
signcolor = skincolors[facecolor].invcolor;
}
else if (locvar1 != -3) // set to a defined skin
{
@ -5181,7 +5181,7 @@ void A_SignPlayer(mobj_t *actor)
else if (skin->prefoppositecolor)
signcolor = skin->prefoppositecolor;
else if (facecolor)
signcolor = Color_Opposite[facecolor - 1][0];
signcolor = skincolors[facecolor].invcolor;
}
if (skin)
@ -5209,22 +5209,12 @@ void A_SignPlayer(mobj_t *actor)
P_SetMobjState(ov, actor->info->meleestate); // S_EGGMANSIGN
if (!signcolor)
signcolor = SKINCOLOR_CARBON;
facecolor = signcolor;
}
actor->tracer->color = signcolor;
/*
If you're here from the comment above Color_Opposite,
the following line is the one which is dependent on the
array being symmetrical. It gets the opposite of the
opposite of your desired colour just so it can get the
brightness frame for the End Sign. It's not a great
design choice, but it's constant time array access and
the idea that the colours should be OPPOSITES is kind
of in the name. If you have a better idea, feel free
to let me know. ~toast 2016/07/20
*/
if (signcolor && signcolor < MAXSKINCOLORS)
signframe += (15 - Color_Opposite[Color_Opposite[signcolor - 1][0] - 1][1]);
if (signcolor && signcolor < numskincolors)
signframe += (15 - skincolors[facecolor].invshade);
actor->tracer->frame = signframe;
}
@ -5649,7 +5639,7 @@ void A_MinusPopup(mobj_t *actor)
P_SetObjectMomZ(rock, 3*FRACUNIT, false);
P_SetScale(rock, rock->scale/3);
}
P_RadiusAttack(actor, actor, 2*actor->radius, 0);
P_RadiusAttack(actor, actor, 2*actor->radius, 0, true);
if (actor->tracer)
P_DamageMobj(actor->tracer, actor, actor, 1, 0);
@ -6533,7 +6523,7 @@ void A_OldRingExplode(mobj_t *actor) {
if (changecolor)
{
if (!(gametyperules & GTR_TEAMFLAGS))
if (!(gametyperules & GTR_TEAMS))
mo->color = actor->target->color; //copy color
else if (actor->target->player->ctfteam == 2)
mo->color = skincolor_bluering;
@ -6549,7 +6539,7 @@ void A_OldRingExplode(mobj_t *actor) {
if (changecolor)
{
if (!(gametyperules & GTR_TEAMFLAGS))
if (!(gametyperules & GTR_TEAMS))
mo->color = actor->target->color; //copy color
else if (actor->target->player->ctfteam == 2)
mo->color = skincolor_bluering;
@ -6564,7 +6554,7 @@ void A_OldRingExplode(mobj_t *actor) {
if (changecolor)
{
if (!(gametyperules & GTR_TEAMFLAGS))
if (!(gametyperules & GTR_TEAMS))
mo->color = actor->target->color; //copy color
else if (actor->target->player->ctfteam == 2)
mo->color = skincolor_bluering;
@ -6955,7 +6945,9 @@ void A_RecyclePowers(mobj_t *actor)
for (j = 0; j < NUMPOWERS; j++)
{
if (j == pw_flashing || j == pw_underwater || j == pw_spacetime || j == pw_carry
|| j == pw_tailsfly || j == pw_extralife || j == pw_nocontrol || j == pw_super)
|| j == pw_tailsfly || j == pw_extralife || j == pw_nocontrol || j == pw_super
|| j == pw_pushing || j == pw_justsprung || j == pw_noautobrake || j == pw_justlaunched
|| j == pw_ignorelatch)
continue;
players[recv_pl].powers[j] = powers[send_pl][j];
}
@ -8804,10 +8796,10 @@ void A_ChangeColorRelative(mobj_t *actor)
{
// Have you ever seen anything so hideous?
if (actor->target)
actor->color = (UINT8)(actor->color + actor->target->color);
actor->color = (UINT16)(actor->color + actor->target->color);
}
else
actor->color = (UINT8)(actor->color + locvar2);
actor->color = (UINT16)(actor->color + locvar2);
}
// Function: A_ChangeColorAbsolute
@ -8831,7 +8823,7 @@ void A_ChangeColorAbsolute(mobj_t *actor)
actor->color = actor->target->color;
}
else
actor->color = (UINT8)locvar2;
actor->color = (UINT16)locvar2;
}
// Function: A_Dye
@ -8850,21 +8842,21 @@ void A_Dye(mobj_t *actor)
UINT8 color = (UINT8)locvar2;
if (LUA_CallAction("A_Dye", actor))
return;
if (color >= MAXTRANSLATIONS)
if (color >= numskincolors)
return;
if (!color)
target->colorized = false;
else
target->colorized = true;
// What if it's a player?
if (target->player)
{
target->player->powers[pw_dye] = color;
return;
}
target->color = color;
}
@ -9707,6 +9699,9 @@ void A_SplitShot(mobj_t *actor)
if (LUA_CallAction("A_SplitShot", actor))
return;
if (!actor->target)
return;
A_FaceTarget(actor);
{
const angle_t an = (actor->angle + ANGLE_90) >> ANGLETOFINESHIFT;
@ -11039,7 +11034,7 @@ void A_VileAttack(mobj_t *actor)
actor->target->x - P_ReturnThrustX(fire, actor->angle, FixedMul(24*FRACUNIT, fire->scale)),
actor->target->y - P_ReturnThrustY(fire, actor->angle, FixedMul(24*FRACUNIT, fire->scale)),
fire->z);
P_RadiusAttack(fire, actor, 70*FRACUNIT, 0);
P_RadiusAttack(fire, actor, 70*FRACUNIT, 0, true);
}
else
{
@ -11084,7 +11079,7 @@ void A_VileAttack(mobj_t *actor)
actor->target->x - P_ReturnThrustX(fire, actor->angle, FixedMul(24*FRACUNIT, fire->scale)),
actor->target->y - P_ReturnThrustY(fire, actor->angle, FixedMul(24*FRACUNIT, fire->scale)),
fire->z);
P_RadiusAttack(fire, actor, 70*FRACUNIT, 0);
P_RadiusAttack(fire, actor, 70*FRACUNIT, 0, true);
}
}
@ -12320,7 +12315,7 @@ void A_MineExplode(mobj_t *actor)
quake.intensity = 8*FRACUNIT;
quake.time = TICRATE/3;
P_RadiusAttack(actor, actor->tracer, 192*FRACUNIT, DMG_CANHURTSELF);
P_RadiusAttack(actor, actor->tracer, 192*FRACUNIT, DMG_CANHURTSELF, true);
P_MobjCheckWater(actor);
{
@ -12700,8 +12695,8 @@ void A_Boss5FindWaypoint(mobj_t *actor)
else // locvar1 == 0
{
fixed_t hackoffset = P_MobjFlip(actor)*56*FRACUNIT;
INT32 numwaypoints = 0;
mobj_t **waypoints;
INT32 numfangwaypoints = 0;
mobj_t **fangwaypoints;
INT32 key;
actor->z += hackoffset;
@ -12726,7 +12721,7 @@ void A_Boss5FindWaypoint(mobj_t *actor)
continue;
if (!P_CheckSight(actor, mapthings[i].mobj))
continue;
numwaypoints++;
numfangwaypoints++;
}
// players also count as waypoints apparently
@ -12748,11 +12743,11 @@ void A_Boss5FindWaypoint(mobj_t *actor)
continue;
if (!P_CheckSight(actor, players[i].mo))
continue;
numwaypoints++;
numfangwaypoints++;
}
}
if (!numwaypoints)
if (!numfangwaypoints)
{
// restore z position
actor->z -= hackoffset;
@ -12760,8 +12755,8 @@ void A_Boss5FindWaypoint(mobj_t *actor)
}
// allocate the table and reset count to zero
waypoints = Z_Calloc(sizeof(*waypoints)*numwaypoints, PU_STATIC, NULL);
numwaypoints = 0;
fangwaypoints = Z_Calloc(sizeof(*waypoints)*numfangwaypoints, PU_STATIC, NULL);
numfangwaypoints = 0;
// now find them again and add them to the table!
for (i = 0; i < nummapthings; i++)
@ -12786,7 +12781,7 @@ void A_Boss5FindWaypoint(mobj_t *actor)
}
if (!P_CheckSight(actor, mapthings[i].mobj))
continue;
waypoints[numwaypoints++] = mapthings[i].mobj;
fangwaypoints[numfangwaypoints++] = mapthings[i].mobj;
}
if (actor->extravalue2 > 1)
@ -12807,25 +12802,25 @@ void A_Boss5FindWaypoint(mobj_t *actor)
continue;
if (!P_CheckSight(actor, players[i].mo))
continue;
waypoints[numwaypoints++] = players[i].mo;
fangwaypoints[numfangwaypoints++] = players[i].mo;
}
}
// restore z position
actor->z -= hackoffset;
if (!numwaypoints)
if (!numfangwaypoints)
{
Z_Free(waypoints); // free table
Z_Free(fangwaypoints); // free table
goto nowaypoints; // ???
}
key = P_RandomKey(numwaypoints);
key = P_RandomKey(numfangwaypoints);
P_SetTarget(&actor->tracer, waypoints[key]);
P_SetTarget(&actor->tracer, fangwaypoints[key]);
if (actor->tracer->type == MT_FANGWAYPOINT)
actor->tracer->reactiontime = numwaypoints/4; // Monster Iestyn: is this how it should be? I count center waypoints as waypoints unlike the original Lua script
Z_Free(waypoints); // free table
actor->tracer->reactiontime = numfangwaypoints/4; // Monster Iestyn: is this how it should be? I count center waypoints as waypoints unlike the original Lua script
Z_Free(fangwaypoints); // free table
}
// now face the tracer you just set!
@ -13321,7 +13316,7 @@ void A_Boss5BombExplode(mobj_t *actor)
actor->flags2 = MF2_EXPLOSION;
if (actor->target)
P_RadiusAttack(actor, actor->target, 7*actor->radius, 0);
P_RadiusAttack(actor, actor->target, 7*actor->radius, 0, true);
P_DustRing(locvar1, 4, actor->x, actor->y, actor->z+actor->height, 2*actor->radius, 0, FRACUNIT, actor->scale);
P_DustRing(locvar1, 6, actor->x, actor->y, actor->z+actor->height/2, 3*actor->radius, FRACUNIT, FRACUNIT, actor->scale);
@ -13346,6 +13341,9 @@ static boolean PIT_DustDevilLaunch(mobj_t *thing)
if (!player)
return true;
if (player->powers[pw_carry] != CR_DUSTDEVIL && (player->powers[pw_ignorelatch] & (1<<15)))
return true;
if (abs(thing->x - dustdevil->x) > dustdevil->radius || abs(thing->y - dustdevil->y) > dustdevil->radius)
return true;
@ -13369,8 +13367,9 @@ static boolean PIT_DustDevilLaunch(mobj_t *thing)
P_ResetPlayer(player);
A_PlayActiveSound(dustdevil);
}
player->powers[pw_carry] = CR_DUSTDEVIL;
player->powers[pw_nocontrol] = 2;
player->drawangle += ANG20;
P_SetTarget(&thing->tracer, dustdevil);
P_SetPlayerMobjState(thing, S_PLAY_PAIN);
if (dist > dragamount)
@ -13390,7 +13389,9 @@ static boolean PIT_DustDevilLaunch(mobj_t *thing)
P_ResetPlayer(player);
thing->z = dustdevil->z + dustdevil->height;
thrust = 20 * FRACUNIT;
player->powers[pw_carry] = CR_NONE;
player->powers[pw_nocontrol] = 0;
P_SetTarget(&thing->tracer, NULL);
S_StartSound(thing, sfx_wdjump);
P_SetPlayerMobjState(thing, S_PLAY_FALL);
}

View file

@ -696,10 +696,20 @@ void T_BounceCheese(bouncecheese_t *bouncer)
return;
}
T_MovePlane(bouncer->sector, bouncer->speed/2, bouncer->sector->ceilingheight -
70*FRACUNIT, false, true, -1); // move ceiling
T_MovePlane(bouncer->sector, bouncer->speed/2, bouncer->sector->floorheight - 70*FRACUNIT,
false, false, -1); // move floor
if (bouncer->speed >= 0) // move floor first to fix height desync and any bizarre bugs following that
{
T_MovePlane(bouncer->sector, bouncer->speed/2, bouncer->sector->floorheight - 70*FRACUNIT,
false, false, -1); // move floor
T_MovePlane(bouncer->sector, bouncer->speed/2, bouncer->sector->ceilingheight -
70*FRACUNIT, false, true, -1); // move ceiling
}
else
{
T_MovePlane(bouncer->sector, bouncer->speed/2, bouncer->sector->ceilingheight -
70*FRACUNIT, false, true, -1); // move ceiling
T_MovePlane(bouncer->sector, bouncer->speed/2, bouncer->sector->floorheight - 70*FRACUNIT,
false, false, -1); // move floor
}
bouncer->sector->floorspeed = -bouncer->speed/2;
bouncer->sector->ceilspeed = 42;
@ -2231,9 +2241,9 @@ void EV_CrumbleChain(sector_t *sec, ffloor_t *rover)
{
mobj_t *spawned = NULL;
if (*rover->t_slope)
topz = P_GetZAt(*rover->t_slope, a, b) - (spacing>>1);
topz = P_GetSlopeZAt(*rover->t_slope, a, b) - (spacing>>1);
if (*rover->b_slope)
bottomz = P_GetZAt(*rover->b_slope, a, b);
bottomz = P_GetSlopeZAt(*rover->b_slope, a, b);
for (c = topz; c > bottomz; c -= spacing)
{

View file

@ -1143,10 +1143,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
toucher->angle = special->angle;
if (player == &players[consoleplayer])
localangle = toucher->angle;
else if (player == &players[secondarydisplayplayer])
localangle2 = toucher->angle;
P_SetPlayerAngle(player, toucher->angle);
P_ResetPlayer(player);
@ -1471,7 +1468,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
return;
case MT_BLACKEGGMAN_GOOPFIRE:
if (!player->powers[pw_flashing])
if (!player->powers[pw_flashing] && !(player->powers[pw_ignorelatch] & (1<<15)))
{
toucher->momx = 0;
toucher->momy = 0;
@ -1564,10 +1561,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
#if 0 // camera redirection - deemed unnecessary
toucher->angle = special->angle;
if (player == &players[consoleplayer])
localangle = toucher->angle;
else if (player == &players[secondarydisplayplayer])
localangle2 = toucher->angle;
P_SetPlayerAngle(player, toucher->angle);
#endif
S_StartSound(toucher, special->info->attacksound); // home run
@ -1590,44 +1584,53 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
return;
case MT_SMALLGRABCHAIN:
case MT_BIGGRABCHAIN:
if (P_MobjFlip(toucher)*toucher->momz > 0
|| (player->powers[pw_carry]))
return;
if (toucher->z > special->z + special->height/2)
return;
if (toucher->z + toucher->height/2 < special->z)
return;
if (player->powers[pw_flashing])
return;
if (special->movefactor && special->tracer && special->tracer->angle != ANGLE_90 && special->tracer->angle != ANGLE_270)
{ // I don't expect you to understand this, Mr Bond...
angle_t ang = R_PointToAngle2(special->x, special->y, toucher->x, toucher->y) - special->tracer->angle;
if ((special->movefactor > 0) == (special->tracer->angle > ANGLE_90 && special->tracer->angle < ANGLE_270))
ang += ANGLE_180;
if (ang < ANGLE_180)
return; // I expect you to die.
}
P_ResetPlayer(player);
P_SetTarget(&toucher->tracer, special);
if (special->tracer && !(special->tracer->flags2 & MF2_STRONGBOX))
{
player->powers[pw_carry] = CR_MACESPIN;
S_StartSound(toucher, sfx_spin);
P_SetPlayerMobjState(toucher, S_PLAY_ROLL);
boolean macespin = false;
if (P_MobjFlip(toucher)*toucher->momz > 0
|| (player->powers[pw_carry]))
return;
if (toucher->z > special->z + special->height/2)
return;
if (toucher->z + toucher->height/2 < special->z)
return;
if (player->powers[pw_flashing])
return;
if (special->tracer && !(special->tracer->flags2 & MF2_STRONGBOX))
macespin = true;
if (macespin ? (player->powers[pw_ignorelatch] & (1<<15)) : (player->powers[pw_ignorelatch]))
return;
if (special->movefactor && special->tracer && special->tracer->angle != ANGLE_90 && special->tracer->angle != ANGLE_270)
{ // I don't expect you to understand this, Mr Bond...
angle_t ang = R_PointToAngle2(special->x, special->y, toucher->x, toucher->y) - special->tracer->angle;
if ((special->movefactor > 0) == (special->tracer->angle > ANGLE_90 && special->tracer->angle < ANGLE_270))
ang += ANGLE_180;
if (ang < ANGLE_180)
return; // I expect you to die.
}
P_ResetPlayer(player);
P_SetTarget(&toucher->tracer, special);
if (macespin)
{
player->powers[pw_carry] = CR_MACESPIN;
S_StartSound(toucher, sfx_spin);
P_SetPlayerMobjState(toucher, S_PLAY_ROLL);
}
else
player->powers[pw_carry] = CR_GENERIC;
// Can't jump first frame
player->pflags |= PF_JUMPSTASIS;
return;
}
else
player->powers[pw_carry] = CR_GENERIC;
// Can't jump first frame
player->pflags |= PF_JUMPSTASIS;
return;
case MT_EGGMOBILE2_POGO:
// sanity checks
if (!special->target || !special->target->health)
@ -1717,7 +1720,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
return;
case MT_MINECARTSPAWNER:
if (!player->bot && special->fuse <= TICRATE && player->powers[pw_carry] != CR_MINECART)
if (!player->bot && special->fuse <= TICRATE && player->powers[pw_carry] != CR_MINECART && !(player->powers[pw_ignorelatch] & (1<<15)))
{
mobj_t *mcart = P_SpawnMobj(special->x, special->y, special->z, MT_MINECART);
P_SetTarget(&mcart->target, toucher);
@ -2284,7 +2287,7 @@ void P_CheckSurvivors(void)
if (!taggers) //If there are no taggers, pick a survivor at random to be it.
{
// Exception for hide and seek. If a round has started and the IT player leaves, end the round.
if (gametype == GT_HIDEANDSEEK && (leveltime >= (hidetime * TICRATE)))
if ((gametyperules & GTR_HIDEFROZEN) && (leveltime >= (hidetime * TICRATE)))
{
CONS_Printf(M_GetText("The IT player has left the game.\n"));
if (server)
@ -2522,7 +2525,7 @@ void P_KillMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 damaget
}
P_RestoreMusic(target->player);
if (gametype != GT_COOP)
if (!G_CoopGametype())
{
HU_SetCEchoFlags(0);
HU_SetCEchoDuration(5);
@ -2600,7 +2603,7 @@ void P_KillMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 damaget
// allow them to try again, rather than sitting the whole thing out.
if (leveltime >= hidetime * TICRATE)
{
if (gametype == GT_TAG)//suiciding in survivor makes you IT.
if (!(gametyperules & GTR_HIDEFROZEN))//suiciding in survivor makes you IT.
{
target->player->pflags |= PF_TAGIT;
CONS_Printf(M_GetText("%s is now IT!\n"), player_names[target->player-players]); // Tell everyone who is it!
@ -3094,7 +3097,7 @@ static boolean P_TagDamage(mobj_t *target, mobj_t *inflictor, mobj_t *source, IN
P_AddPlayerScore(source->player, 100); //award points to tagger.
P_HitDeathMessages(player, inflictor, source, 0);
if (gametype == GT_TAG) //survivor
if (!(gametyperules & GTR_HIDEFROZEN)) //survivor
{
player->pflags |= PF_TAGIT; //in survivor, the player becomes IT and helps hunt down the survivors.
CONS_Printf(M_GetText("%s is now IT!\n"), player_names[player-players]); // Tell everyone who is it!
@ -3155,7 +3158,7 @@ static boolean P_PlayerHitsPlayer(mobj_t *target, mobj_t *inflictor, mobj_t *sou
// In COOP/RACE, you can't hurt other players unless cv_friendlyfire is on
if (!(cv_friendlyfire.value || (gametyperules & GTR_FRIENDLYFIRE)) && (gametyperules & GTR_FRIENDLY))
{
if (gametype == GT_COOP && inflictor->type == MT_LHRT && !(player->powers[pw_shield] & SH_NOSTACK)) // co-op only
if ((gametyperules & GTR_FRIENDLY) && inflictor->type == MT_LHRT && !(player->powers[pw_shield] & SH_NOSTACK)) // co-op only
{
if (player->revitem != MT_LHRT && player->spinitem != MT_LHRT && player->thokitem != MT_LHRT) // Healers do not get to heal other healers.
{
@ -3254,7 +3257,7 @@ static void P_KillPlayer(player_t *player, mobj_t *source, INT32 damage)
}
// If the player was super, tell them he/she ain't so super nomore.
if (gametype != GT_COOP && player->powers[pw_super])
if (!G_CoopGametype() && player->powers[pw_super])
{
S_StartSound(NULL, sfx_s3k66); //let all players hear it.
HU_SetCEchoFlags(0);
@ -3616,7 +3619,7 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da
if (source == target)
return false; // Don't hit yourself with your own paraloop, baka
if (source && source->player && !(cv_friendlyfire.value || (gametyperules & GTR_FRIENDLYFIRE))
&& (gametype == GT_COOP
&& ((gametyperules & GTR_FRIENDLY)
|| (G_GametypeHasTeams() && player->ctfteam == source->player->ctfteam)))
return false; // Don't run eachother over in special stages and team games and such
}

View file

@ -138,6 +138,10 @@ void P_DoPlayerPain(player_t *player, mobj_t *source, mobj_t *inflictor);
void P_ResetPlayer(player_t *player);
boolean P_PlayerCanDamage(player_t *player, mobj_t *thing);
boolean P_IsLocalPlayer(player_t *player);
void P_SetPlayerAngle(player_t *player, angle_t angle);
angle_t P_GetLocalAngle(player_t *player);
void P_SetLocalAngle(player_t *player, angle_t angle);
void P_ForceLocalAngle(player_t *player, angle_t angle);
boolean P_IsObjectInGoop(mobj_t *mo);
boolean P_IsObjectOnGround(mobj_t *mo);
@ -171,6 +175,7 @@ void P_DoAbilityBounce(player_t *player, boolean changemomz);
void P_TwinSpinRejuvenate(player_t *player, mobjtype_t type);
void P_BlackOw(player_t *player);
void P_ElementalFire(player_t *player, boolean cropcircle);
void P_SpawnSkidDust(player_t *player, fixed_t radius, boolean sound);
void P_DoPityCheck(player_t *player);
void P_PlayerThink(player_t *player);
@ -188,6 +193,7 @@ mobj_t *P_LookForFocusTarget(player_t *player, mobj_t *exclude, SINT8 direction,
mobj_t *P_LookForEnemies(player_t *player, boolean nonenemies, boolean bullet);
void P_NukeEnemies(mobj_t *inflictor, mobj_t *source, fixed_t radius);
void P_Earthquake(mobj_t *inflictor, mobj_t *source, fixed_t radius);
boolean P_HomingAttack(mobj_t *source, mobj_t *enemy); /// \todo doesn't belong in p_user
boolean P_SuperReady(player_t *player);
void P_DoJump(player_t *player, boolean soundandstate);
@ -415,7 +421,7 @@ void P_DelPrecipSeclist(mprecipsecnode_t *node);
void P_CreateSecNodeList(mobj_t *thing, fixed_t x, fixed_t y);
void P_Initsecnode(void);
void P_RadiusAttack(mobj_t *spot, mobj_t *source, fixed_t damagedist, UINT8 damagetype);
void P_RadiusAttack(mobj_t *spot, mobj_t *source, fixed_t damagedist, UINT8 damagetype, boolean sightcheck);
fixed_t P_FloorzAtPos(fixed_t x, fixed_t y, fixed_t z, fixed_t height);
boolean PIT_PushableMoved(mobj_t *thing);

View file

@ -372,12 +372,7 @@ boolean P_DoSpring(mobj_t *spring, mobj_t *object)
object->angle = object->player->drawangle = spring->angle;
if (!demoplayback || P_ControlStyle(object->player) == CS_LMAOGALOG)
{
if (object->player == &players[consoleplayer])
localangle = spring->angle;
else if (object->player == &players[secondarydisplayplayer])
localangle2 = spring->angle;
}
P_SetPlayerAngle(object->player, spring->angle);
}
if (object->player->pflags & PF_GLIDING)
@ -532,6 +527,8 @@ static void P_DoPterabyteCarry(player_t *player, mobj_t *ptera)
{
if (player->powers[pw_carry] && player->powers[pw_carry] != CR_ROLLOUT)
return;
if (player->powers[pw_ignorelatch] & (1<<15))
return;
if (ptera->extravalue1 != 1)
return; // Not swooping
if (ptera->target != player->mo)
@ -616,7 +613,8 @@ static void P_DoTailsCarry(player_t *sonic, player_t *tails)
if (zdist <= sonic->mo->height + sonic->mo->scale // FixedMul(FRACUNIT, sonic->mo->scale), but scale == FRACUNIT by default
&& zdist > sonic->mo->height*2/3
&& P_MobjFlip(tails->mo)*sonic->mo->momz <= 0)
&& P_MobjFlip(tails->mo)*sonic->mo->momz <= 0
&& !(sonic->powers[pw_ignorelatch] & (1<<15)))
{
if (sonic-players == consoleplayer && botingame)
CV_SetValue(&cv_analog[1], false);
@ -1007,6 +1005,7 @@ static boolean PIT_CheckThing(mobj_t *thing)
}
if ((thing->flags & MF_PUSHABLE) // not carrying a player
&& (tmthing->player->powers[pw_carry] == CR_NONE) // player is not already riding something
&& !(tmthing->player->powers[pw_ignorelatch] & (1<<15))
&& ((tmthing->eflags & MFE_VERTICALFLIP) == (thing->eflags & MFE_VERTICALFLIP))
&& (P_MobjFlip(tmthing)*tmthing->momz <= 0)
&& ((!(tmthing->eflags & MFE_VERTICALFLIP) && abs(thing->z + thing->height - tmthing->z) < (thing->height>>2))
@ -1296,6 +1295,7 @@ static boolean PIT_CheckThing(mobj_t *thing)
else if (tmthing->type == MT_BLACKEGGMAN_MISSILE && thing->player
&& (thing->player->pflags & PF_JUMPED)
&& !thing->player->powers[pw_flashing]
&& !thing->player->powers[pw_ignorelatch]
&& thing->tracer != tmthing
&& tmthing->target != thing)
{
@ -1310,12 +1310,7 @@ static boolean PIT_CheckThing(mobj_t *thing)
thing->angle = tmthing->angle;
if (!demoplayback || P_ControlStyle(thing->player) == CS_LMAOGALOG)
{
if (thing->player == &players[consoleplayer])
localangle = thing->angle;
else if (thing->player == &players[secondarydisplayplayer])
localangle2 = thing->angle;
}
P_SetPlayerAngle(thing->player, thing->angle);
return true;
}
@ -1598,7 +1593,7 @@ static boolean PIT_CheckThing(mobj_t *thing)
}
// Force solid players in hide and seek to avoid corner stacking.
if (cv_tailspickup.value && gametype != GT_HIDEANDSEEK)
if (cv_tailspickup.value && !(gametyperules & GTR_HIDEFROZEN))
{
if (tmthing->player && thing->player)
{
@ -2833,14 +2828,22 @@ boolean P_TryMove(mobj_t *thing, fixed_t x, fixed_t y, boolean allowdropoff)
P_HandleSlopeLanding(thing, tmfloorslope);
if (thing->momz <= 0)
{
thing->standingslope = tmfloorslope;
if (thing->momz == 0 && thing->player && !startingonground)
P_PlayerHitFloor(thing->player, true);
}
}
else if (thing->z+thing->height >= tmceilingz && (thing->eflags & MFE_VERTICALFLIP)) {
if (!startingonground && tmceilingslope)
P_HandleSlopeLanding(thing, tmceilingslope);
if (thing->momz >= 0)
{
thing->standingslope = tmceilingslope;
if (thing->momz == 0 && thing->player && !startingonground)
P_PlayerHitFloor(thing->player, true);
}
}
}
else // don't set standingslope if you're not going to clip against it
@ -2934,6 +2937,8 @@ static boolean P_ThingHeightClip(mobj_t *thing)
ffloor_t *oldceilingrover = thing->ceilingrover;
boolean onfloor = P_IsObjectOnGround(thing);//(thing->z <= thing->floorz);
ffloor_t *rover = NULL;
boolean bouncing;
boolean hitfloor = false;
if (thing->flags & MF_NOCLIPHEIGHT)
return true;
@ -2956,7 +2961,9 @@ static boolean P_ThingHeightClip(mobj_t *thing)
if (tmfloorz > oldfloorz+thing->height)
return true;
if (onfloor && !(thing->flags & MF_NOGRAVITY) && floormoved)
bouncing = thing->player && thing->state-states == S_PLAY_BOUNCE_LANDING && P_IsObjectOnGround(thing);
if ((onfloor || bouncing) && !(thing->flags & MF_NOGRAVITY) && floormoved)
{
rover = (thing->eflags & MFE_VERTICALFLIP) ? oldceilingrover : oldfloorrover;
@ -2964,6 +2971,7 @@ static boolean P_ThingHeightClip(mobj_t *thing)
// If ~FF_EXISTS, don't set mobj Z.
if (!rover || ((rover->flags & FF_EXISTS) && (rover->flags & FF_SOLID)))
{
hitfloor = bouncing;
if (thing->eflags & MFE_VERTICALFLIP)
thing->pmomz = thing->ceilingz - (thing->z + thing->height);
else
@ -2988,7 +2996,7 @@ static boolean P_ThingHeightClip(mobj_t *thing)
thing->z = thing->ceilingz - thing->height;
}
if (P_MobjFlip(thing)*(thing->z - oldz) > 0 && thing->player)
if ((P_MobjFlip(thing)*(thing->z - oldz) > 0 || hitfloor) && thing->player)
P_PlayerHitFloor(thing->player, !onfloor);
// debug: be sure it falls to the floor
@ -3202,102 +3210,83 @@ isblocking:
static boolean P_IsClimbingValid(player_t *player, angle_t angle)
{
fixed_t platx, platy;
subsector_t *glidesector;
sector_t *glidesector;
fixed_t floorz, ceilingz;
mobj_t *mo = player->mo;
ffloor_t *rover;
platx = P_ReturnThrustX(player->mo, angle, player->mo->radius + FixedMul(8*FRACUNIT, player->mo->scale));
platy = P_ReturnThrustY(player->mo, angle, player->mo->radius + FixedMul(8*FRACUNIT, player->mo->scale));
platx = P_ReturnThrustX(mo, angle, mo->radius + FixedMul(8*FRACUNIT, mo->scale));
platy = P_ReturnThrustY(mo, angle, mo->radius + FixedMul(8*FRACUNIT, mo->scale));
glidesector = R_PointInSubsector(player->mo->x + platx, player->mo->y + platy);
glidesector = R_PointInSubsector(mo->x + platx, mo->y + platy)->sector;
floorz = glidesector->sector->f_slope ? P_GetZAt(glidesector->sector->f_slope, player->mo->x, player->mo->y) : glidesector->sector->floorheight;
ceilingz = glidesector->sector->c_slope ? P_GetZAt(glidesector->sector->c_slope, player->mo->x, player->mo->y) : glidesector->sector->ceilingheight;
floorz = P_GetSectorFloorZAt (glidesector, mo->x, mo->y);
ceilingz = P_GetSectorCeilingZAt(glidesector, mo->x, mo->y);
if (glidesector->sector != player->mo->subsector->sector)
if (glidesector != mo->subsector->sector)
{
boolean floorclimb = false;
fixed_t topheight, bottomheight;
if (glidesector->sector->ffloors)
for (rover = glidesector->ffloors; rover; rover = rover->next)
{
ffloor_t *rover;
for (rover = glidesector->sector->ffloors; rover; rover = rover->next)
if (!(rover->flags & FF_EXISTS) || !(rover->flags & FF_BLOCKPLAYER))
continue;
topheight = P_GetFFloorTopZAt (rover, mo->x, mo->y);
bottomheight = P_GetFFloorBottomZAt(rover, mo->x, mo->y);
floorclimb = true;
if (mo->eflags & MFE_VERTICALFLIP)
{
if (!(rover->flags & FF_EXISTS) || !(rover->flags & FF_BLOCKPLAYER))
continue;
topheight = *rover->topheight;
bottomheight = *rover->bottomheight;
if (*rover->t_slope)
topheight = P_GetZAt(*rover->t_slope, player->mo->x, player->mo->y);
if (*rover->b_slope)
bottomheight = P_GetZAt(*rover->b_slope, player->mo->x, player->mo->y);
floorclimb = true;
if (player->mo->eflags & MFE_VERTICALFLIP)
{
if ((topheight < player->mo->z + player->mo->height) && ((player->mo->z + player->mo->height + player->mo->momz) < topheight))
{
floorclimb = true;
}
if (topheight < player->mo->z) // Waaaay below the ledge.
{
floorclimb = false;
}
if (bottomheight > player->mo->z + player->mo->height - FixedMul(16*FRACUNIT,player->mo->scale))
{
floorclimb = false;
}
}
else
{
if ((bottomheight > player->mo->z) && ((player->mo->z - player->mo->momz) > bottomheight))
{
floorclimb = true;
}
if (bottomheight > player->mo->z + player->mo->height) // Waaaay below the ledge.
{
floorclimb = false;
}
if (topheight < player->mo->z + FixedMul(16*FRACUNIT,player->mo->scale))
{
floorclimb = false;
}
}
if (floorclimb)
break;
if ((topheight < mo->z + mo->height) && ((mo->z + mo->height + mo->momz) < topheight))
floorclimb = true;
if (topheight < mo->z) // Waaaay below the ledge.
floorclimb = false;
if (bottomheight > mo->z + mo->height - FixedMul(16*FRACUNIT,mo->scale))
floorclimb = false;
}
else
{
if ((bottomheight > mo->z) && ((mo->z - mo->momz) > bottomheight))
floorclimb = true;
if (bottomheight > mo->z + mo->height) // Waaaay below the ledge.
floorclimb = false;
if (topheight < mo->z + FixedMul(16*FRACUNIT,mo->scale))
floorclimb = false;
}
if (floorclimb)
break;
}
if (player->mo->eflags & MFE_VERTICALFLIP)
if (mo->eflags & MFE_VERTICALFLIP)
{
if ((floorz <= player->mo->z + player->mo->height)
&& ((player->mo->z + player->mo->height - player->mo->momz) <= floorz))
if ((floorz <= mo->z + mo->height)
&& ((mo->z + mo->height - mo->momz) <= floorz))
floorclimb = true;
if ((floorz > player->mo->z)
&& glidesector->sector->floorpic == skyflatnum)
if ((floorz > mo->z)
&& glidesector->floorpic == skyflatnum)
return false;
if ((player->mo->z + player->mo->height - FixedMul(16*FRACUNIT,player->mo->scale) > ceilingz)
|| (player->mo->z + player->mo->height <= floorz))
if ((mo->z + mo->height - FixedMul(16*FRACUNIT,mo->scale) > ceilingz)
|| (mo->z + mo->height <= floorz))
floorclimb = true;
}
else
{
if ((ceilingz >= player->mo->z)
&& ((player->mo->z - player->mo->momz) >= ceilingz))
if ((ceilingz >= mo->z)
&& ((mo->z - mo->momz) >= ceilingz))
floorclimb = true;
if ((ceilingz < player->mo->z+player->mo->height)
&& glidesector->sector->ceilingpic == skyflatnum)
if ((ceilingz < mo->z+mo->height)
&& glidesector->ceilingpic == skyflatnum)
return false;
if ((player->mo->z + FixedMul(16*FRACUNIT,player->mo->scale) < floorz)
|| (player->mo->z >= ceilingz))
if ((mo->z + FixedMul(16*FRACUNIT,mo->scale) < floorz)
|| (mo->z >= ceilingz))
floorclimb = true;
}
@ -3310,9 +3299,128 @@ static boolean P_IsClimbingValid(player_t *player, angle_t angle)
return false;
}
//
// PTR_SlideTraverse
//
static boolean PTR_LineIsBlocking(line_t *li)
{
// one-sided linedefs are always solid to sliding movement.
if (!li->backsector)
return !P_PointOnLineSide(slidemo->x, slidemo->y, li);
if (!(slidemo->flags & MF_MISSILE))
{
if (li->flags & ML_IMPASSIBLE)
return true;
if ((slidemo->flags & (MF_ENEMY|MF_BOSS)) && li->flags & ML_BLOCKMONSTERS)
return true;
}
// set openrange, opentop, openbottom
P_LineOpening(li, slidemo);
if (openrange < slidemo->height)
return true; // doesn't fit
if (opentop - slidemo->z < slidemo->height)
return true; // mobj is too high
if (openbottom - slidemo->z > FixedMul(MAXSTEPMOVE, slidemo->scale))
return true; // too big a step up
return false;
}
static void PTR_GlideClimbTraverse(line_t *li)
{
line_t *checkline = li;
ffloor_t *rover;
fixed_t topheight, bottomheight;
boolean fofline = false;
sector_t *checksector = (li->backsector && !P_PointOnLineSide(slidemo->x, slidemo->y, li)) ? li->backsector : li->frontsector;
if (checksector->ffloors)
{
for (rover = checksector->ffloors; rover; rover = rover->next)
{
if (!(rover->flags & FF_EXISTS) || !(rover->flags & FF_BLOCKPLAYER) || (rover->flags & FF_BUSTUP))
continue;
topheight = P_GetFFloorTopZAt (rover, slidemo->x, slidemo->y);
bottomheight = P_GetFFloorBottomZAt(rover, slidemo->x, slidemo->y);
if (topheight < slidemo->z)
continue;
if (bottomheight > slidemo->z + slidemo->height)
continue;
// Got this far, so I guess it's climbable. // TODO: Climbing check, also, better method to do this?
if (rover->master->flags & ML_TFERLINE)
{
size_t linenum = li-checksector->lines[0];
checkline = rover->master->frontsector->lines[0] + linenum;
fofline = true;
}
break;
}
}
// see about climbing on the wall
if (!(checkline->flags & ML_NOCLIMB) && checkline->special != HORIZONSPECIAL)
{
boolean canclimb;
angle_t climbangle, climbline;
INT32 whichside = P_PointOnLineSide(slidemo->x, slidemo->y, li);
climbangle = climbline = R_PointToAngle2(li->v1->x, li->v1->y, li->v2->x, li->v2->y);
if (whichside) // on second side?
climbline += ANGLE_180;
climbangle += (ANGLE_90 * (whichside ? -1 : 1));
canclimb = (li->backsector ? P_IsClimbingValid(slidemo->player, climbangle) : true);
if (((!slidemo->player->climbing && abs((signed)(slidemo->angle - ANGLE_90 - climbline)) < ANGLE_45)
|| (slidemo->player->climbing == 1 && abs((signed)(slidemo->angle - climbline)) < ANGLE_135))
&& canclimb)
{
slidemo->angle = climbangle;
/*if (!demoplayback || P_ControlStyle(slidemo->player) == CS_LMAOGALOG)
P_SetPlayerAngle(slidemo->player, slidemo->angle);*/
if (!slidemo->player->climbing)
{
S_StartSound(slidemo, sfx_s3k4a);
slidemo->player->climbing = 5;
if (slidemo->player->powers[pw_super])
{
P_Earthquake(slidemo, slidemo, 256*FRACUNIT);
S_StartSound(slidemo, sfx_s3k49);
}
}
slidemo->player->pflags &= ~(PF_GLIDING|PF_SPINNING|PF_JUMPED|PF_NOJUMPDAMAGE|PF_THOKKED);
slidemo->player->glidetime = 0;
slidemo->player->secondjump = 0;
if (slidemo->player->climbing > 1)
slidemo->momz = slidemo->momx = slidemo->momy = 0;
if (fofline)
whichside = 0;
if (!whichside)
{
slidemo->player->lastsidehit = checkline->sidenum[whichside];
slidemo->player->lastlinehit = (INT16)(checkline - lines);
}
P_Thrust(slidemo, slidemo->angle, FixedMul(5*FRACUNIT, slidemo->scale));
}
}
}
static boolean PTR_SlideTraverse(intercept_t *in)
{
line_t *li;
@ -3321,151 +3429,20 @@ static boolean PTR_SlideTraverse(intercept_t *in)
li = in->d.line;
// one-sided linedefs are always solid to sliding movement.
// one-sided linedef
if (!li->backsector)
{
if (P_PointOnLineSide(slidemo->x, slidemo->y, li))
return true; // don't hit the back side
goto isblocking;
}
if (!PTR_LineIsBlocking(li))
return true;
if (!(slidemo->flags & MF_MISSILE))
{
if (li->flags & ML_IMPASSIBLE)
goto isblocking;
if ((slidemo->flags & (MF_ENEMY|MF_BOSS)) && li->flags & ML_BLOCKMONSTERS)
goto isblocking;
}
// set openrange, opentop, openbottom
P_LineOpening(li, slidemo);
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 > FixedMul(MAXSTEPMOVE, slidemo->scale))
goto isblocking; // too big a step up
// this line doesn't block movement
return true;
// the line does block movement,
// the line blocks movement,
// see if it is closer than best so far
isblocking:
if (li->polyobj && slidemo->player)
{
if ((li->polyobj->lines[0]->backsector->flags & SF_TRIGGERSPECIAL_TOUCH) && !(li->polyobj->flags & POF_NOSPECIALS))
P_ProcessSpecialSector(slidemo->player, slidemo->subsector->sector, li->polyobj->lines[0]->backsector);
}
if (slidemo->player && (slidemo->player->pflags & PF_GLIDING || slidemo->player->climbing)
&& slidemo->player->charability == CA_GLIDEANDCLIMB)
{
line_t *checkline = li;
sector_t *checksector;
ffloor_t *rover;
fixed_t topheight, bottomheight;
boolean fofline = false;
INT32 side = P_PointOnLineSide(slidemo->x, slidemo->y, li);
if (!side && li->backsector)
checksector = li->backsector;
else
checksector = li->frontsector;
if (checksector->ffloors)
{
for (rover = checksector->ffloors; rover; rover = rover->next)
{
if (!(rover->flags & FF_EXISTS) || !(rover->flags & FF_BLOCKPLAYER) || (rover->flags & FF_BUSTUP))
continue;
topheight = *rover->topheight;
bottomheight = *rover->bottomheight;
if (*rover->t_slope)
topheight = P_GetZAt(*rover->t_slope, slidemo->x, slidemo->y);
if (*rover->b_slope)
bottomheight = P_GetZAt(*rover->b_slope, slidemo->x, slidemo->y);
if (topheight < slidemo->z)
continue;
if (bottomheight > slidemo->z + slidemo->height)
continue;
// Got this far, so I guess it's climbable. // TODO: Climbing check, also, better method to do this?
if (rover->master->flags & ML_TFERLINE)
{
size_t linenum = li-checksector->lines[0];
checkline = rover->master->frontsector->lines[0] + linenum;
fofline = true;
}
break;
}
}
// see about climbing on the wall
if (!(checkline->flags & ML_NOCLIMB) && checkline->special != HORIZONSPECIAL)
{
boolean canclimb;
angle_t climbangle, climbline;
INT32 whichside = P_PointOnLineSide(slidemo->x, slidemo->y, li);
climbangle = climbline = R_PointToAngle2(li->v1->x, li->v1->y, li->v2->x, li->v2->y);
if (whichside) // on second side?
climbline += ANGLE_180;
climbangle += (ANGLE_90 * (whichside ? -1 : 1));
canclimb = (li->backsector ? P_IsClimbingValid(slidemo->player, climbangle) : true);
if (((!slidemo->player->climbing && abs((signed)(slidemo->angle - ANGLE_90 - climbline)) < ANGLE_45)
|| (slidemo->player->climbing == 1 && abs((signed)(slidemo->angle - climbline)) < ANGLE_135))
&& canclimb)
{
slidemo->angle = climbangle;
/*if (!demoplayback || P_ControlStyle(slidemo->player) == CS_LMAOGALOG)
{
if (slidemo->player == &players[consoleplayer])
localangle = slidemo->angle;
else if (slidemo->player == &players[secondarydisplayplayer])
localangle2 = slidemo->angle;
}*/
if (!slidemo->player->climbing)
{
S_StartSound(slidemo->player->mo, sfx_s3k4a);
slidemo->player->climbing = 5;
}
slidemo->player->pflags &= ~(PF_GLIDING|PF_SPINNING|PF_JUMPED|PF_NOJUMPDAMAGE|PF_THOKKED);
slidemo->player->glidetime = 0;
slidemo->player->secondjump = 0;
if (slidemo->player->climbing > 1)
slidemo->momz = slidemo->momx = slidemo->momy = 0;
if (fofline)
whichside = 0;
if (!whichside)
{
slidemo->player->lastsidehit = checkline->sidenum[whichside];
slidemo->player->lastlinehit = (INT16)(checkline - lines);
}
P_Thrust(slidemo, slidemo->angle, FixedMul(5*FRACUNIT, slidemo->scale));
}
}
}
if (slidemo->player && slidemo->player->charability == CA_GLIDEANDCLIMB
&& (slidemo->player->pflags & PF_GLIDING || slidemo->player->climbing))
PTR_GlideClimbTraverse(li);
if (in->frac < bestslidefrac && (!slidemo->player || !slidemo->player->climbing))
{
@ -3596,9 +3573,7 @@ static void P_CheckLavaWall(mobj_t *mo, sector_t *sec)
if (rover->master->flags & ML_BLOCKMONSTERS)
continue;
topheight = *rover->t_slope ?
P_GetZAt(*rover->t_slope, mo->x, mo->y) :
*rover->topheight;
topheight = P_GetFFloorTopZAt(rover, mo->x, mo->y);
if (mo->eflags & MFE_VERTICALFLIP)
{
@ -3611,9 +3586,7 @@ static void P_CheckLavaWall(mobj_t *mo, sector_t *sec)
continue;
}
bottomheight = *rover->b_slope ?
P_GetZAt(*rover->b_slope, mo->x, mo->y) :
*rover->bottomheight;
bottomheight = P_GetFFloorBottomZAt(rover, mo->x, mo->y);
if (mo->eflags & MFE_VERTICALFLIP)
{
@ -4060,6 +4033,7 @@ static fixed_t bombdamage;
static mobj_t *bombsource;
static mobj_t *bombspot;
static UINT8 bombdamagetype;
static boolean bombsightcheck;
//
// PIT_RadiusAttack
@ -4073,10 +4047,16 @@ static boolean PIT_RadiusAttack(mobj_t *thing)
if (thing == bombspot) // ignore the bomb itself (Deton fix)
return true;
if ((thing->flags & (MF_MONITOR|MF_SHOOTABLE)) != MF_SHOOTABLE)
if (bombsource && thing->type == bombsource->type && !(bombdamagetype & DMG_CANHURTSELF)) // ignore the type of guys who dropped the bomb (Jetty-Syn Bomber or Skim can bomb eachother, but not themselves.)
return true;
if (bombsource && thing->type == bombsource->type && !(bombdamagetype & DMG_CANHURTSELF)) // ignore the type of guys who dropped the bomb (Jetty-Syn Bomber or Skim can bomb eachother, but not themselves.)
if (thing->type == MT_MINUS && !(thing->flags & (MF_SPECIAL|MF_SHOOTABLE)) && !bombsightcheck)
thing->flags = (thing->flags & ~MF_NOCLIPTHING)|MF_SPECIAL|MF_SHOOTABLE;
if (thing->type == MT_EGGGUARD && thing->tracer) //nuke Egg Guard's shield!
P_KillMobj(thing->tracer, bombspot, bombsource, bombdamagetype);
if ((thing->flags & (MF_MONITOR|MF_SHOOTABLE)) != MF_SHOOTABLE)
return true;
dx = abs(thing->x - bombspot->x);
@ -4098,7 +4078,7 @@ static boolean PIT_RadiusAttack(mobj_t *thing)
if (thing->ceilingz < bombspot->z && bombspot->floorz > thing->z)
return true;
if (P_CheckSight(thing, bombspot))
if (!bombsightcheck || P_CheckSight(thing, bombspot))
{ // must be in direct path
P_DamageMobj(thing, bombspot, bombsource, 1, bombdamagetype); // Tails 01-11-2001
}
@ -4110,7 +4090,7 @@ static boolean PIT_RadiusAttack(mobj_t *thing)
// P_RadiusAttack
// Source is the creature that caused the explosion at spot.
//
void P_RadiusAttack(mobj_t *spot, mobj_t *source, fixed_t damagedist, UINT8 damagetype)
void P_RadiusAttack(mobj_t *spot, mobj_t *source, fixed_t damagedist, UINT8 damagetype, boolean sightcheck)
{
INT32 x, y;
INT32 xl, xh, yl, yh;
@ -4128,6 +4108,7 @@ void P_RadiusAttack(mobj_t *spot, mobj_t *source, fixed_t damagedist, UINT8 dama
bombsource = source;
bombdamage = FixedMul(damagedist, spot->scale);
bombdamagetype = damagetype;
bombsightcheck = sightcheck;
for (y = yl; y <= yh; y++)
for (x = xl; x <= xh; x++)
@ -4199,11 +4180,8 @@ static boolean PIT_ChangeSector(mobj_t *thing, boolean realcrush)
topheight = *rover->topheight;
bottomheight = *rover->bottomheight;
/*if (rover->t_slope)
topheight = P_GetZAt(rover->t_slope, thing->x, thing->y);
if (rover->b_slope)
bottomheight = P_GetZAt(rover->b_slope, thing->x, thing->y);*/
//topheight = P_GetFFloorTopZAt (rover, thing->x, thing->y);
//bottomheight = P_GetFFloorBottomZAt(rover, thing->x, thing->y);
delta1 = thing->z - (bottomheight + topheight)/2;
delta2 = thingtop - (bottomheight + topheight)/2;
@ -4980,10 +4958,7 @@ void P_MapEnd(void)
fixed_t P_FloorzAtPos(fixed_t x, fixed_t y, fixed_t z, fixed_t height)
{
sector_t *sec = R_PointInSubsector(x, y)->sector;
fixed_t floorz = sec->floorheight;
if (sec->f_slope)
floorz = P_GetZAt(sec->f_slope, x, y);
fixed_t floorz = P_GetSectorFloorZAt(sec, x, y);
// Intercept the stupid 'fall through 3dfloors' bug Tails 03-17-2002
if (sec->ffloors)
@ -5000,13 +4975,8 @@ fixed_t P_FloorzAtPos(fixed_t x, fixed_t y, fixed_t z, fixed_t height)
if ((!(rover->flags & FF_SOLID || rover->flags & FF_QUICKSAND) || (rover->flags & FF_SWIMMABLE)))
continue;
topheight = *rover->topheight;
bottomheight = *rover->bottomheight;
if (*rover->t_slope)
topheight = P_GetZAt(*rover->t_slope, x, y);
if (*rover->b_slope)
bottomheight = P_GetZAt(*rover->b_slope, x, y);
topheight = P_GetFFloorTopZAt (rover, x, y);
bottomheight = P_GetFFloorBottomZAt(rover, x, y);
if (rover->flags & FF_QUICKSAND)
{

View file

@ -304,45 +304,33 @@ void P_CameraLineOpening(line_t *linedef)
// If you can see through it, why not move the camera through it too?
if (front->camsec >= 0)
{
frontfloor = sectors[front->camsec].floorheight;
frontceiling = sectors[front->camsec].ceilingheight;
if (sectors[front->camsec].f_slope) // SRB2CBTODO: ESLOPE (sectors[front->heightsec].f_slope)
frontfloor = P_GetZAt(sectors[front->camsec].f_slope, camera.x, camera.y);
if (sectors[front->camsec].c_slope)
frontceiling = P_GetZAt(sectors[front->camsec].c_slope, camera.x, camera.y);
// SRB2CBTODO: ESLOPE (sectors[front->heightsec].f_slope)
frontfloor = P_GetSectorFloorZAt (&sectors[front->camsec], camera.x, camera.y);
frontceiling = P_GetSectorCeilingZAt(&sectors[front->camsec], camera.x, camera.y);
}
else if (front->heightsec >= 0)
{
frontfloor = sectors[front->heightsec].floorheight;
frontceiling = sectors[front->heightsec].ceilingheight;
if (sectors[front->heightsec].f_slope) // SRB2CBTODO: ESLOPE (sectors[front->heightsec].f_slope)
frontfloor = P_GetZAt(sectors[front->heightsec].f_slope, camera.x, camera.y);
if (sectors[front->heightsec].c_slope)
frontceiling = P_GetZAt(sectors[front->heightsec].c_slope, camera.x, camera.y);
// SRB2CBTODO: ESLOPE (sectors[front->heightsec].f_slope)
frontfloor = P_GetSectorFloorZAt (&sectors[front->heightsec], camera.x, camera.y);
frontceiling = P_GetSectorCeilingZAt(&sectors[front->heightsec], camera.x, camera.y);
}
else
{
frontfloor = P_CameraGetFloorZ(mapcampointer, front, tmx, tmy, linedef);
frontfloor = P_CameraGetFloorZ (mapcampointer, front, tmx, tmy, linedef);
frontceiling = P_CameraGetCeilingZ(mapcampointer, front, tmx, tmy, linedef);
}
if (back->camsec >= 0)
{
backfloor = sectors[back->camsec].floorheight;
backceiling = sectors[back->camsec].ceilingheight;
if (sectors[back->camsec].f_slope) // SRB2CBTODO: ESLOPE (sectors[front->heightsec].f_slope)
frontfloor = P_GetZAt(sectors[back->camsec].f_slope, camera.x, camera.y);
if (sectors[back->camsec].c_slope)
frontceiling = P_GetZAt(sectors[back->camsec].c_slope, camera.x, camera.y);
// SRB2CBTODO: ESLOPE (sectors[back->heightsec].f_slope)
backfloor = P_GetSectorFloorZAt (&sectors[back->camsec], camera.x, camera.y);
backceiling = P_GetSectorCeilingZAt(&sectors[back->camsec], camera.x, camera.y);
}
else if (back->heightsec >= 0)
{
backfloor = sectors[back->heightsec].floorheight;
backceiling = sectors[back->heightsec].ceilingheight;
if (sectors[back->heightsec].f_slope) // SRB2CBTODO: ESLOPE (sectors[front->heightsec].f_slope)
frontfloor = P_GetZAt(sectors[back->heightsec].f_slope, camera.x, camera.y);
if (sectors[back->heightsec].c_slope)
frontceiling = P_GetZAt(sectors[back->heightsec].c_slope, camera.x, camera.y);
// SRB2CBTODO: ESLOPE (sectors[back->heightsec].f_slope)
backfloor = P_GetSectorFloorZAt (&sectors[back->heightsec], camera.x, camera.y);
backceiling = P_GetSectorCeilingZAt(&sectors[back->heightsec], camera.x, camera.y);
}
else
{

File diff suppressed because it is too large Load diff

View file

@ -312,7 +312,7 @@ typedef struct mobj_s
void *skin; // overrides 'sprite' when non-NULL (for player bodies to 'remember' the skin)
// Player and mobj sprites in multiplayer modes are modified
// using an internal color lookup table for re-indexing.
UINT8 color; // This replaces MF_TRANSLATION. Use 0 for default (no translation).
UINT16 color; // This replaces MF_TRANSLATION. Use 0 for default (no translation).
// Interaction info, by BLOCKMAP.
// Links in blocks (if needed).
@ -373,6 +373,7 @@ typedef struct mobj_s
struct pslope_s *standingslope; // The slope that the object is standing on (shouldn't need synced in savegames, right?)
boolean colorized; // Whether the mobj uses the rainbow colormap
boolean mirrored; // The object's rotations will be mirrored left to right, e.g., see frame AL from the right and AR from the left
fixed_t shadowscale; // If this object casts a shadow, and the size relative to radius
// WARNING: New fields must be added separately to savegame and Lua.

View file

@ -240,6 +240,9 @@ static void Polyobj_GetInfo(polyobj_t *po)
/*if (lines[i].flags & ML_EFFECT5)
po->flags &= ~POF_CLIPPLANES;*/
if (lines[i].flags & ML_EFFECT6)
po->flags |= POF_SPLAT;
if (lines[i].flags & ML_NOCLIMB) // Has a linedef executor
po->flags |= POF_LDEXEC;
}
@ -1039,7 +1042,7 @@ static boolean Polyobj_moveXY(polyobj_t *po, fixed_t x, fixed_t y, boolean check
// The formula for this can be found here:
// http://www.inversereality.org/tutorials/graphics%20programming/2dtransformations.html
// It is, of course, just a vector-matrix multiplication.
static inline void Polyobj_rotatePoint(vertex_t *v, const vertex_t *c, angle_t ang)
static inline void Polyobj_rotatePoint(vertex_t *v, const vector2_t *c, angle_t ang)
{
vertex_t tmp = *v;
@ -1092,7 +1095,7 @@ static void Polyobj_rotateLine(line_t *ld)
}
// Causes objects resting on top of the rotating polyobject to 'ride' with its movement.
static void Polyobj_rotateThings(polyobj_t *po, vertex_t origin, angle_t delta, UINT8 turnthings)
static void Polyobj_rotateThings(polyobj_t *po, vector2_t origin, angle_t delta, UINT8 turnthings)
{
static INT32 pomovecount = 10000;
INT32 x, y;
@ -1156,10 +1159,8 @@ static void Polyobj_rotateThings(polyobj_t *po, vertex_t origin, angle_t delta,
if (turnthings == 2 || (turnthings == 1 && !mo->player)) {
mo->angle += delta;
if (mo->player == &players[consoleplayer])
localangle += delta;
else if (mo->player == &players[secondarydisplayplayer])
localangle2 += delta;
if (mo->player)
P_SetPlayerAngle(mo->player, (angle_t)(mo->player->angleturn << 16) + delta);
}
}
}
@ -1172,7 +1173,7 @@ static boolean Polyobj_rotate(polyobj_t *po, angle_t delta, UINT8 turnthings, bo
{
size_t i;
angle_t angle;
vertex_t origin;
vector2_t origin;
INT32 hitflags = 0;
// don't move bad polyobjects
@ -1569,17 +1570,39 @@ void T_PolyObjMove(polymove_t *th)
}
}
static void T_MovePolyObj(polyobj_t *po, fixed_t distx, fixed_t disty, fixed_t distz)
{
polyobj_t *child;
INT32 start;
Polyobj_moveXY(po, distx, disty, true);
// TODO: use T_MovePlane
po->lines[0]->backsector->floorheight += distz;
po->lines[0]->backsector->ceilingheight += distz;
// Sal: Remember to check your sectors!
// Monster Iestyn: we only need to bother with the back sector, now that P_CheckSector automatically checks the blockmap
// updating objects in the front one too just added teleporting to ground bugs
P_CheckSector(po->lines[0]->backsector, (boolean)(po->damage));
// Apply action to mirroring polyobjects as well
start = 0;
while ((child = Polyobj_GetChild(po, &start)))
{
if (child->isBad)
continue;
Polyobj_moveXY(child, distx, disty, true);
// TODO: use T_MovePlane
child->lines[0]->backsector->floorheight += distz;
child->lines[0]->backsector->ceilingheight += distz;
P_CheckSector(child->lines[0]->backsector, (boolean)(child->damage));
}
}
void T_PolyObjWaypoint(polywaypoint_t *th)
{
mobj_t *mo2;
mobj_t *target = NULL;
mobj_t *waypoint = NULL;
thinker_t *wp;
fixed_t adjustx, adjusty, adjustz;
fixed_t momx, momy, momz, dist;
INT32 start;
polyobj_t *po = Polyobj_GetForNum(th->polyObjNum);
polyobj_t *oldpo = po;
fixed_t speed = th->speed;
if (!po)
#ifdef RANGECHECK
@ -1593,31 +1616,10 @@ void T_PolyObjWaypoint(polywaypoint_t *th)
#endif
// check for displacement due to override and reattach when possible
if (po->thinker == NULL)
if (!po->thinker)
po->thinker = &th->thinker;
/*
// Find out target first.
// We redo this each tic to make savegame compatibility easier.
for (wp = thlist[THINK_MOBJ].next; wp != &thlist[THINK_MOBJ]; wp = wp->next)
{
if (wp->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
continue;
mo2 = (mobj_t *)wp;
if (mo2->type != MT_TUBEWAYPOINT)
continue;
if (mo2->threshold == th->sequence && mo2->health == th->pointnum)
{
target = mo2;
break;
}
}
*/
target = th->target;
target = waypoints[th->sequence][th->pointnum];
if (!target)
{
@ -1625,240 +1627,93 @@ void T_PolyObjWaypoint(polywaypoint_t *th)
return;
}
// Compensate for position offset
adjustx = po->centerPt.x + th->diffx;
adjusty = po->centerPt.y + th->diffy;
adjustz = po->lines[0]->backsector->floorheight + (po->lines[0]->backsector->ceilingheight - po->lines[0]->backsector->floorheight)/2 + th->diffz;
dist = P_AproxDistance(P_AproxDistance(target->x - adjustx, target->y - adjusty), target->z - adjustz);
if (dist < 1)
dist = 1;
momx = FixedMul(FixedDiv(target->x - adjustx, dist), (th->speed));
momy = FixedMul(FixedDiv(target->y - adjusty, dist), (th->speed));
momz = FixedMul(FixedDiv(target->z - adjustz, dist), (th->speed));
// Calculate the distance between the polyobject and the waypoint
// 'dist' already equals this.
// Will the polyobject be FURTHER away if the momx/momy/momz is added to
// its current coordinates, or closer? (shift down to fracunits to avoid approximation errors)
if (dist>>FRACBITS <= P_AproxDistance(P_AproxDistance(target->x - adjustx - momx, target->y - adjusty - momy), target->z - adjustz - momz)>>FRACBITS)
// Move along the waypoint sequence until speed for the current tic is exhausted
while (speed > 0)
{
// If further away, set XYZ of polyobject to waypoint location
fixed_t amtx, amty, amtz;
fixed_t diffz;
amtx = (target->x - th->diffx) - po->centerPt.x;
amty = (target->y - th->diffy) - po->centerPt.y;
Polyobj_moveXY(po, amtx, amty, true);
// TODO: use T_MovePlane
amtz = (po->lines[0]->backsector->ceilingheight - po->lines[0]->backsector->floorheight)/2;
diffz = po->lines[0]->backsector->floorheight - (target->z - amtz);
po->lines[0]->backsector->floorheight = target->z - amtz;
po->lines[0]->backsector->ceilingheight = target->z + amtz;
// Sal: Remember to check your sectors!
// Monster Iestyn: we only need to bother with the back sector, now that P_CheckSector automatically checks the blockmap
// updating objects in the front one too just added teleporting to ground bugs
P_CheckSector(po->lines[0]->backsector, (boolean)(po->damage));
// Apply action to mirroring polyobjects as well
start = 0;
while ((po = Polyobj_GetChild(oldpo, &start)))
mobj_t *waypoint = NULL;
fixed_t pox, poy, poz;
fixed_t distx, disty, distz, dist;
// Current position of polyobject
pox = po->centerPt.x;
poy = po->centerPt.y;
poz = (po->lines[0]->backsector->floorheight + po->lines[0]->backsector->ceilingheight)/2;
// Calculate the distance between the polyobject and the waypoint
distx = target->x - pox;
disty = target->y - poy;
distz = target->z - poz;
dist = P_AproxDistance(P_AproxDistance(distx, disty), distz);
if (dist < 1)
dist = 1;
// Will the polyobject overshoot its target?
if (speed < dist)
{
if (po->isBad)
continue;
// No. Move towards waypoint
fixed_t momx, momy, momz;
Polyobj_moveXY(po, amtx, amty, true);
// TODO: use T_MovePlane
po->lines[0]->backsector->floorheight += diffz; // move up/down by same amount as the parent did
po->lines[0]->backsector->ceilingheight += diffz;
// Sal: Remember to check your sectors!
// Monster Iestyn: we only need to bother with the back sector, now that P_CheckSector automatically checks the blockmap
// updating objects in the front one too just added teleporting to ground bugs
P_CheckSector(po->lines[0]->backsector, (boolean)(po->damage));
}
po = oldpo;
if (!th->stophere)
{
CONS_Debug(DBG_POLYOBJ, "Looking for next waypoint...\n");
// Find next waypoint
for (wp = thlist[THINK_MOBJ].next; wp != &thlist[THINK_MOBJ]; wp = wp->next)
{
if (wp->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
continue;
mo2 = (mobj_t *)wp;
if (mo2->type != MT_TUBEWAYPOINT)
continue;
if (mo2->threshold != th->sequence)
continue;
if (th->direction == -1)
{
if (mo2->health == target->health - 1)
{
waypoint = mo2;
break;
}
}
else
{
if (mo2->health == target->health + 1)
{
waypoint = mo2;
break;
}
}
}
if (!waypoint && th->wrap) // If specified, wrap waypoints
{
if (!th->continuous)
{
th->wrap = 0;
th->stophere = true;
}
for (wp = thlist[THINK_MOBJ].next; wp != &thlist[THINK_MOBJ]; wp = wp->next)
{
if (wp->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
continue;
mo2 = (mobj_t *)wp;
if (mo2->type != MT_TUBEWAYPOINT)
continue;
if (mo2->threshold != th->sequence)
continue;
if (th->direction == -1)
{
if (waypoint == NULL)
waypoint = mo2;
else if (mo2->health > waypoint->health)
waypoint = mo2;
}
else
{
if (mo2->health == 0)
{
waypoint = mo2;
break;
}
}
}
}
else if (!waypoint && th->comeback) // Come back to the start
{
th->direction = -th->direction;
if (!th->continuous)
th->comeback = false;
for (wp = thlist[THINK_MOBJ].next; wp != &thlist[THINK_MOBJ]; wp = wp->next)
{
if (wp->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
continue;
mo2 = (mobj_t *)wp;
if (mo2->type != MT_TUBEWAYPOINT)
continue;
if (mo2->threshold != th->sequence)
continue;
if (th->direction == -1)
{
if (mo2->health == target->health - 1)
{
waypoint = mo2;
break;
}
}
else
{
if (mo2->health == target->health + 1)
{
waypoint = mo2;
break;
}
}
}
}
}
if (waypoint)
{
CONS_Debug(DBG_POLYOBJ, "Found waypoint (sequence %d, number %d).\n", waypoint->threshold, waypoint->health);
target = waypoint;
th->pointnum = target->health;
// Set the mobj as your target! -- Monster Iestyn 27/12/19
P_SetTarget(&th->target, target);
// calculate MOMX/MOMY/MOMZ for next waypoint
// change slope
dist = P_AproxDistance(P_AproxDistance(target->x - adjustx, target->y - adjusty), target->z - adjustz);
if (dist < 1)
dist = 1;
momx = FixedMul(FixedDiv(target->x - adjustx, dist), (th->speed));
momy = FixedMul(FixedDiv(target->y - adjusty, dist), (th->speed));
momz = FixedMul(FixedDiv(target->z - adjustz, dist), (th->speed));
momx = FixedMul(FixedDiv(target->x - pox, dist), speed);
momy = FixedMul(FixedDiv(target->y - poy, dist), speed);
momz = FixedMul(FixedDiv(target->z - poz, dist), speed);
T_MovePolyObj(po, momx, momy, momz);
return;
}
else
{
momx = momy = momz = 0;
// Yes. Teleport to waypoint and look for the next one
T_MovePolyObj(po, distx, disty, distz);
if (!th->stophere)
CONS_Debug(DBG_POLYOBJ, "Next waypoint not found!\n");
{
CONS_Debug(DBG_POLYOBJ, "Looking for next waypoint...\n");
waypoint = (th->direction == -1) ? P_GetPreviousWaypoint(target, false) : P_GetNextWaypoint(target, false);
if (po->thinker == &th->thinker)
po->thinker = NULL;
if (!waypoint && th->returnbehavior == PWR_WRAP) // If specified, wrap waypoints
{
if (!th->continuous)
{
th->returnbehavior = PWR_STOP;
th->stophere = true;
}
P_RemoveThinker(&th->thinker);
return;
waypoint = (th->direction == -1) ? P_GetLastWaypoint(th->sequence) : P_GetFirstWaypoint(th->sequence);
}
else if (!waypoint && th->returnbehavior == PWR_COMEBACK) // Come back to the start
{
th->direction = -th->direction;
if (!th->continuous)
th->returnbehavior = PWR_STOP;
waypoint = (th->direction == -1) ? P_GetPreviousWaypoint(target, false) : P_GetNextWaypoint(target, false);
}
}
if (waypoint)
{
CONS_Debug(DBG_POLYOBJ, "Found waypoint (sequence %d, number %d).\n", waypoint->threshold, waypoint->health);
target = waypoint;
th->pointnum = target->health;
// Calculate remaining speed
speed -= dist;
}
else
{
if (!th->stophere)
CONS_Debug(DBG_POLYOBJ, "Next waypoint not found!\n");
if (po->thinker == &th->thinker)
po->thinker = NULL;
P_RemoveThinker(&th->thinker);
return;
}
}
}
else
{
// momx/momy/momz already equals the right speed
}
// Move the polyobject
Polyobj_moveXY(po, momx, momy, true);
// TODO: use T_MovePlane
po->lines[0]->backsector->floorheight += momz;
po->lines[0]->backsector->ceilingheight += momz;
// Sal: Remember to check your sectors!
// Monster Iestyn: we only need to bother with the back sector, now that P_CheckSector automatically checks the blockmap
// updating objects in the front one too just added teleporting to ground bugs
P_CheckSector(po->lines[0]->backsector, (boolean)(po->damage));
// Apply action to mirroring polyobjects as well
start = 0;
while ((po = Polyobj_GetChild(oldpo, &start)))
{
if (po->isBad)
continue;
Polyobj_moveXY(po, momx, momy, true);
// TODO: use T_MovePlane
po->lines[0]->backsector->floorheight += momz;
po->lines[0]->backsector->ceilingheight += momz;
// Sal: Remember to check your sectors!
// Monster Iestyn: we only need to bother with the back sector, now that P_CheckSector automatically checks the blockmap
// updating objects in the front one too just added teleporting to ground bugs
P_CheckSector(po->lines[0]->backsector, (boolean)(po->damage));
}
}
void T_PolyDoorSlide(polyslidedoor_t *th)
@ -2276,11 +2131,7 @@ boolean EV_DoPolyObjWaypoint(polywaypointdata_t *pwdata)
{
polyobj_t *po;
polywaypoint_t *th;
mobj_t *mo2;
mobj_t *first = NULL;
mobj_t *last = NULL;
mobj_t *target = NULL;
thinker_t *wp;
if (!(po = Polyobj_GetForNum(pwdata->polyObjNum)))
{
@ -2304,56 +2155,16 @@ boolean EV_DoPolyObjWaypoint(polywaypointdata_t *pwdata)
// set fields
th->polyObjNum = pwdata->polyObjNum;
th->speed = pwdata->speed;
th->sequence = pwdata->sequence; // Used to specify sequence #
if (pwdata->reverse)
th->direction = -1;
else
th->direction = 1;
th->sequence = pwdata->sequence;
th->direction = (pwdata->flags & PWF_REVERSE) ? -1 : 1;
th->comeback = pwdata->comeback;
th->continuous = pwdata->continuous;
th->wrap = pwdata->wrap;
th->returnbehavior = pwdata->returnbehavior;
if (pwdata->flags & PWF_LOOP)
th->continuous = true;
th->stophere = false;
// Find the first waypoint we need to use
for (wp = thlist[THINK_MOBJ].next; wp != &thlist[THINK_MOBJ]; wp = wp->next)
{
if (wp->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
continue;
mo2 = (mobj_t *)wp;
if (mo2->type != MT_TUBEWAYPOINT)
continue;
if (mo2->threshold != th->sequence)
continue;
if (th->direction == -1) // highest waypoint #
{
if (mo2->health == 0)
last = mo2;
else
{
if (first == NULL)
first = mo2;
else if (mo2->health > first->health)
first = mo2;
}
}
else // waypoint 0
{
if (mo2->health == 0)
first = mo2;
else
{
if (last == NULL)
last = mo2;
else if (mo2->health > last->health)
last = mo2;
}
}
}
first = (th->direction == -1) ? P_GetLastWaypoint(th->sequence) : P_GetFirstWaypoint(th->sequence);
if (!first)
{
@ -2363,77 +2174,16 @@ boolean EV_DoPolyObjWaypoint(polywaypointdata_t *pwdata)
return false;
}
// Hotfix to not crash on single-waypoint sequences -Red
if (!last)
last = first;
// Set diffx, diffy, diffz
// Put these at 0 for now...might not be needed after all.
th->diffx = 0;//first->x - po->centerPt.x;
th->diffy = 0;//first->y - po->centerPt.y;
th->diffz = 0;//first->z - (po->lines[0]->backsector->floorheight + (po->lines[0]->backsector->ceilingheight - po->lines[0]->backsector->floorheight)/2);
if (last->x == po->centerPt.x
&& last->y == po->centerPt.y
&& last->z == (po->lines[0]->backsector->floorheight + (po->lines[0]->backsector->ceilingheight - po->lines[0]->backsector->floorheight)/2))
// Sanity check: If all waypoints are in the same location,
// don't allow the movement to be continuous so we don't get stuck in an infinite loop.
if (th->continuous && P_IsDegeneratedWaypointSequence(th->sequence))
{
// Already at the destination point...
if (!th->wrap)
{
po->thinker = NULL;
P_RemoveThinker(&th->thinker);
}
CONS_Debug(DBG_POLYOBJ, "EV_DoPolyObjWaypoint: All waypoints are in the same location!\n");
th->continuous = false;
}
// Find the actual target movement waypoint
target = first;
/*for (wp = thlist[THINK_MOBJ].next; wp != &thlist[THINK_MOBJ]; wp = wp->next)
{
if (wp->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
continue;
th->pointnum = first->health;
mo2 = (mobj_t *)wp;
if (mo2->type != MT_TUBEWAYPOINT)
continue;
if (mo2->threshold != th->sequence)
continue;
if (th->direction == -1) // highest waypoint #
{
if (mo2->health == first->health - 1)
{
target = mo2;
break;
}
}
else // waypoint 0
{
if (mo2->health == first->health + 1)
{
target = mo2;
break;
}
}
}*/
if (!target)
{
CONS_Debug(DBG_POLYOBJ, "EV_DoPolyObjWaypoint: Missing target waypoint!\n");
po->thinker = NULL;
P_RemoveThinker(&th->thinker);
return false;
}
// Set pointnum
th->pointnum = target->health;
th->target = NULL; // set to NULL first so the below doesn't go wrong
// Set the mobj as your target! -- Monster Iestyn 27/12/19
P_SetTarget(&th->target, target);
// We don't deal with the mirror crap here, we'll
// handle that in the T_Thinker function.
return true;
}

View file

@ -38,7 +38,7 @@ typedef enum
POF_SOLID = 0x3, ///< Clips things.
POF_TESTHEIGHT = 0x4, ///< Test line collision with heights
POF_RENDERSIDES = 0x8, ///< Renders the sides.
POF_RENDERTOP = 0x10, ///< Renders the top..
POF_RENDERTOP = 0x10, ///< Renders the top.
POF_RENDERBOTTOM = 0x20, ///< Renders the bottom.
POF_RENDERPLANES = 0x30, ///< Renders top and bottom.
POF_RENDERALL = 0x38, ///< Renders everything.
@ -49,6 +49,7 @@ typedef enum
POF_LDEXEC = 0x400, ///< This PO triggers a linedef executor.
POF_ONESIDE = 0x800, ///< Only use the first side of the linedef.
POF_NOSPECIALS = 0x1000, ///< Don't apply sector specials.
POF_SPLAT = 0x2000, ///< Use splat flat renderer (treat cyan pixels as invisible).
} polyobjflags_e;
//
@ -140,26 +141,26 @@ typedef struct polymove_s
UINT32 angle; // angle along which to move
} polymove_t;
// PolyObject waypoint movement return behavior
typedef enum
{
PWR_STOP, // Stop after reaching last waypoint
PWR_WRAP, // Wrap back to first waypoint
PWR_COMEBACK, // Repeat sequence in reverse
} polywaypointreturn_e;
typedef struct polywaypoint_s
{
thinker_t thinker; // must be first
INT32 polyObjNum; // numeric id of polyobject
INT32 speed; // resultant velocity
INT32 sequence; // waypoint sequence #
INT32 pointnum; // waypoint #
INT32 direction; // 1 for normal, -1 for backwards
UINT8 comeback; // reverses and comes back when the end is reached
UINT8 wrap; // Wrap around waypoints
UINT8 continuous; // continuously move - used with COMEBACK or WRAP
UINT8 stophere; // Will stop after it reaches the next waypoint
// Difference between location of PO and location of waypoint (offset)
fixed_t diffx;
fixed_t diffy;
fixed_t diffz;
mobj_t *target; // next waypoint mobj
INT32 polyObjNum; // numeric id of polyobject
INT32 speed; // resultant velocity
INT32 sequence; // waypoint sequence #
INT32 pointnum; // waypoint #
INT32 direction; // 1 for normal, -1 for backwards
UINT8 returnbehavior; // behavior after reaching the last waypoint
UINT8 continuous; // continuously move - used with PWR_WRAP or PWR_COMEBACK
UINT8 stophere; // Will stop after it reaches the next waypoint
} polywaypoint_t;
typedef struct polyslidedoor_s
@ -254,15 +255,19 @@ typedef struct polymovedata_s
UINT8 overRide; // if true, will override any action on the object
} polymovedata_t;
typedef enum
{
PWF_REVERSE = 1, // Move through waypoints in reverse order
PWF_LOOP = 1<<1, // Loop movement (used with PWR_WRAP or PWR_COMEBACK)
} polywaypointflags_e;
typedef struct polywaypointdata_s
{
INT32 polyObjNum; // numeric id of polyobject to affect
INT32 sequence; // waypoint sequence #
fixed_t speed; // linear speed
UINT8 reverse; // if true, will go in reverse waypoint order
UINT8 comeback; // reverses and comes back when the end is reached
UINT8 wrap; // Wrap around waypoints
UINT8 continuous; // continuously move - used with COMEBACK or WRAP
INT32 polyObjNum; // numeric id of polyobject to affect
INT32 sequence; // waypoint sequence #
fixed_t speed; // linear speed
UINT8 returnbehavior; // behavior after reaching the last waypoint
UINT8 flags; // PWF_ flags
} polywaypointdata_t;
// polyobject door types

View file

@ -64,8 +64,10 @@
#define FF_FULLBRIGHT 0x00100000
/// \brief Frame flags: Flip sprite vertically (relative to what it should be for its gravity)
#define FF_VERTICALFLIP 0x00200000
/// \brief Frame flags: Flip sprite horizontally
#define FF_HORIZONTALFLIP 0x00400000
/// \brief Frame flags: Thin, paper-like sprite (for collision equivalent, see MF_PAPERCOLLISION)
#define FF_PAPERSPRITE 0x00400000
#define FF_PAPERSPRITE 0x00800000
/// \brief Frame flags - Animate: Simple stateless animation
#define FF_ANIMATE 0x01000000

View file

@ -105,6 +105,8 @@ static void P_NetArchivePlayers(void)
// no longer send ticcmds, player name, skin, or color
WRITEINT16(save_p, players[i].angleturn);
WRITEINT16(save_p, players[i].oldrelangleturn);
WRITEANGLE(save_p, players[i].aiming);
WRITEANGLE(save_p, players[i].drawangle);
WRITEANGLE(save_p, players[i].viewrollangle);
@ -313,6 +315,8 @@ static void P_NetUnArchivePlayers(void)
// sending player names, skin and color should not be necessary at all!
// (that data is handled in the server config now)
players[i].angleturn = READINT16(save_p);
players[i].oldrelangleturn = READINT16(save_p);
players[i].aiming = READANGLE(save_p);
players[i].drawangle = READANGLE(save_p);
players[i].viewrollangle = READANGLE(save_p);
@ -719,6 +723,34 @@ static void P_NetUnArchiveColormaps(void)
net_colormaps = NULL;
}
static void P_NetArchiveWaypoints(void)
{
INT32 i, j;
for (i = 0; i < NUMWAYPOINTSEQUENCES; i++)
{
WRITEUINT16(save_p, numwaypoints[i]);
for (j = 0; j < numwaypoints[i]; j++)
WRITEUINT32(save_p, waypoints[i][j] ? waypoints[i][j]->mobjnum : 0);
}
}
static void P_NetUnArchiveWaypoints(void)
{
INT32 i, j;
UINT32 mobjnum;
for (i = 0; i < NUMWAYPOINTSEQUENCES; i++)
{
numwaypoints[i] = READUINT16(save_p);
for (j = 0; j < numwaypoints[i]; j++)
{
mobjnum = READUINT32(save_p);
waypoints[i][j] = (mobjnum == 0) ? NULL : P_FindNewPosition(mobjnum);
}
}
}
///
/// World Archiving
///
@ -1264,8 +1296,9 @@ typedef enum
MD2_CEILINGROVER = 1<<10,
MD2_SLOPE = 1<<11,
MD2_COLORIZED = 1<<12,
MD2_ROLLANGLE = 1<<13,
MD2_SHADOWSCALE = 1<<14,
MD2_MIRRORED = 1<<13,
MD2_ROLLANGLE = 1<<14,
MD2_SHADOWSCALE = 1<<15,
} mobj_diff2_t;
typedef enum
@ -1470,6 +1503,8 @@ static void SaveMobjThinker(const thinker_t *th, const UINT8 type)
diff2 |= MD2_SLOPE;
if (mobj->colorized)
diff2 |= MD2_COLORIZED;
if (mobj->mirrored)
diff2 |= MD2_MIRRORED;
if (mobj->rollangle)
diff2 |= MD2_ROLLANGLE;
if (mobj->shadowscale)
@ -1595,7 +1630,7 @@ static void SaveMobjThinker(const thinker_t *th, const UINT8 type)
if (diff2 & MD2_SKIN)
WRITEUINT8(save_p, (UINT8)((skin_t *)mobj->skin - skins));
if (diff2 & MD2_COLOR)
WRITEUINT8(save_p, mobj->color);
WRITEUINT16(save_p, mobj->color);
if (diff2 & MD2_EXTVAL1)
WRITEINT32(save_p, mobj->extravalue1);
if (diff2 & MD2_EXTVAL2)
@ -1608,6 +1643,8 @@ static void SaveMobjThinker(const thinker_t *th, const UINT8 type)
WRITEUINT16(save_p, mobj->standingslope->id);
if (diff2 & MD2_COLORIZED)
WRITEUINT8(save_p, mobj->colorized);
if (diff2 & MD2_MIRRORED)
WRITEUINT8(save_p, mobj->mirrored);
if (diff2 & MD2_ROLLANGLE)
WRITEANGLE(save_p, mobj->rollangle);
if (diff2 & MD2_SHADOWSCALE)
@ -1890,8 +1927,7 @@ static void SaveLaserThinker(const thinker_t *th, const UINT8 type)
{
const laserthink_t *ht = (const void *)th;
WRITEUINT8(save_p, type);
WRITEUINT32(save_p, SaveSector(ht->sector));
WRITEUINT32(save_p, SaveSector(ht->sec));
WRITEINT16(save_p, ht->tag);
WRITEUINT32(save_p, SaveLine(ht->sourceline));
WRITEUINT8(save_p, ht->nobosses);
}
@ -1998,6 +2034,7 @@ static inline void SavePolyrotatetThinker(const thinker_t *th, const UINT8 type)
WRITEINT32(save_p, ht->polyObjNum);
WRITEINT32(save_p, ht->speed);
WRITEINT32(save_p, ht->distance);
WRITEUINT8(save_p, ht->turnobjs);
}
static void SavePolymoveThinker(const thinker_t *th, const UINT8 type)
@ -2021,14 +2058,9 @@ static void SavePolywaypointThinker(const thinker_t *th, UINT8 type)
WRITEINT32(save_p, ht->sequence);
WRITEINT32(save_p, ht->pointnum);
WRITEINT32(save_p, ht->direction);
WRITEUINT8(save_p, ht->comeback);
WRITEUINT8(save_p, ht->wrap);
WRITEUINT8(save_p, ht->returnbehavior);
WRITEUINT8(save_p, ht->continuous);
WRITEUINT8(save_p, ht->stophere);
WRITEFIXED(save_p, ht->diffx);
WRITEFIXED(save_p, ht->diffy);
WRITEFIXED(save_p, ht->diffz);
WRITEUINT32(save_p, SaveMobjnum(ht->target));
}
static void SavePolyslidedoorThinker(const thinker_t *th, const UINT8 type)
@ -2555,11 +2587,6 @@ static thinker_t* LoadMobjThinker(actionf_p1 thinker)
i = READUINT8(save_p);
mobj->player = &players[i];
mobj->player->mo = mobj;
// added for angle prediction
if (consoleplayer == i)
localangle = mobj->angle;
if (secondarydisplayplayer == i)
localangle2 = mobj->angle;
}
if (diff & MD_MOVEDIR)
mobj->movedir = READANGLE(save_p);
@ -2608,7 +2635,7 @@ static thinker_t* LoadMobjThinker(actionf_p1 thinker)
if (diff2 & MD2_SKIN)
mobj->skin = &skins[READUINT8(save_p)];
if (diff2 & MD2_COLOR)
mobj->color = READUINT8(save_p);
mobj->color = READUINT16(save_p);
if (diff2 & MD2_EXTVAL1)
mobj->extravalue1 = READINT32(save_p);
if (diff2 & MD2_EXTVAL2)
@ -2621,6 +2648,8 @@ static thinker_t* LoadMobjThinker(actionf_p1 thinker)
mobj->standingslope = P_SlopeById(READUINT16(save_p));
if (diff2 & MD2_COLORIZED)
mobj->colorized = READUINT8(save_p);
if (diff2 & MD2_MIRRORED)
mobj->mirrored = READUINT8(save_p);
if (diff2 & MD2_ROLLANGLE)
mobj->rollangle = READANGLE(save_p);
if (diff2 & MD2_SHADOWSCALE)
@ -2997,16 +3026,10 @@ static thinker_t* LoadPusherThinker(actionf_p1 thinker)
static inline thinker_t* LoadLaserThinker(actionf_p1 thinker)
{
laserthink_t *ht = Z_Malloc(sizeof (*ht), PU_LEVSPEC, NULL);
ffloor_t *rover = NULL;
ht->thinker.function.acp1 = thinker;
ht->sector = LoadSector(READUINT32(save_p));
ht->sec = LoadSector(READUINT32(save_p));
ht->tag = READINT16(save_p);
ht->sourceline = LoadLine(READUINT32(save_p));
ht->nobosses = READUINT8(save_p);
for (rover = ht->sector->ffloors; rover; rover = rover->next)
if (rover->secnum == (size_t)(ht->sec - sectors)
&& rover->master == ht->sourceline)
ht->ffloor = rover;
return &ht->thinker;
}
@ -3141,6 +3164,7 @@ static inline thinker_t* LoadPolyrotatetThinker(actionf_p1 thinker)
ht->polyObjNum = READINT32(save_p);
ht->speed = READINT32(save_p);
ht->distance = READINT32(save_p);
ht->turnobjs = READUINT8(save_p);
return &ht->thinker;
}
@ -3166,14 +3190,9 @@ static inline thinker_t* LoadPolywaypointThinker(actionf_p1 thinker)
ht->sequence = READINT32(save_p);
ht->pointnum = READINT32(save_p);
ht->direction = READINT32(save_p);
ht->comeback = READUINT8(save_p);
ht->wrap = READUINT8(save_p);
ht->returnbehavior = READUINT8(save_p);
ht->continuous = READUINT8(save_p);
ht->stophere = READUINT8(save_p);
ht->diffx = READFIXED(save_p);
ht->diffy = READFIXED(save_p);
ht->diffz = READFIXED(save_p);
ht->target = LoadMobj(READUINT32(save_p));
return &ht->thinker;
}
@ -3420,7 +3439,6 @@ static void P_NetUnArchiveThinkers(void)
case tc_polywaypoint:
th = LoadPolywaypointThinker((actionf_p1)T_PolyObjWaypoint);
restoreNum = true;
break;
case tc_polyslidedoor:
@ -3480,7 +3498,6 @@ static void P_NetUnArchiveThinkers(void)
if (restoreNum)
{
executor_t *delay = NULL;
polywaypoint_t *polywp = NULL;
UINT32 mobjnum;
for (currentthinker = thlist[THINK_MAIN].next; currentthinker != &thlist[THINK_MAIN]; currentthinker = currentthinker->next)
{
@ -3491,15 +3508,6 @@ static void P_NetUnArchiveThinkers(void)
continue;
delay->caller = P_FindNewPosition(mobjnum);
}
for (currentthinker = thlist[THINK_POLYOBJ].next; currentthinker != &thlist[THINK_POLYOBJ]; currentthinker = currentthinker->next)
{
if (currentthinker->function.acp1 != (actionf_p1)T_PolyObjWaypoint)
continue;
polywp = (void *)currentthinker;
if (!(mobjnum = (UINT32)(size_t)polywp->target))
continue;
polywp->target = P_FindNewPosition(mobjnum);
}
}
}
@ -3786,16 +3794,15 @@ static void P_NetUnArchiveSpecials(void)
// =======================================================================
// Misc
// =======================================================================
static inline void P_ArchiveMisc(void)
static inline void P_ArchiveMisc(INT16 mapnum)
{
//lastmapsaved = mapnum;
lastmaploaded = mapnum;
if (gamecomplete)
WRITEINT16(save_p, gamemap | 8192);
else
WRITEINT16(save_p, gamemap);
//lastmapsaved = gamemap;
lastmaploaded = gamemap;
mapnum |= 8192;
WRITEINT16(save_p, mapnum);
WRITEUINT16(save_p, emeralds+357);
WRITESTRINGN(save_p, timeattackfolder, sizeof(timeattackfolder));
}
@ -3809,10 +3816,10 @@ static inline void P_UnArchiveSPGame(INT16 mapoverride)
if (mapoverride != 0)
{
gamemap = mapoverride;
gamecomplete = true;
gamecomplete = 1;
}
else
gamecomplete = false;
gamecomplete = 0;
// gamemap changed; we assume that its map header is always valid,
// so make it so
@ -4036,9 +4043,9 @@ static inline boolean P_UnArchiveLuabanksAndConsistency(void)
return true;
}
void P_SaveGame(void)
void P_SaveGame(INT16 mapnum)
{
P_ArchiveMisc();
P_ArchiveMisc(mapnum);
P_ArchivePlayer();
P_ArchiveLuabanksAndConsistency();
}
@ -4072,6 +4079,7 @@ void P_SaveNetGame(void)
P_NetArchiveThinkers();
P_NetArchiveSpecials();
P_NetArchiveColormaps();
P_NetArchiveWaypoints();
}
LUA_Archive();
@ -4110,6 +4118,7 @@ boolean P_LoadNetGame(void)
P_NetUnArchiveThinkers();
P_NetUnArchiveSpecials();
P_NetUnArchiveColormaps();
P_NetUnArchiveWaypoints();
P_RelinkPointers();
P_FinishMobjs();
}

View file

@ -21,7 +21,7 @@
// Persistent storage/archiving.
// These are the load / save game routines.
void P_SaveGame(void);
void P_SaveGame(INT16 mapnum);
void P_SaveNetGame(void);
boolean P_LoadGame(INT16 mapoverride);
boolean P_LoadNetGame(void);

View file

@ -145,6 +145,133 @@ mapthing_t *playerstarts[MAXPLAYERS];
mapthing_t *bluectfstarts[MAXPLAYERS];
mapthing_t *redctfstarts[MAXPLAYERS];
// Maintain waypoints
mobj_t *waypoints[NUMWAYPOINTSEQUENCES][WAYPOINTSEQUENCESIZE];
UINT16 numwaypoints[NUMWAYPOINTSEQUENCES];
void P_AddWaypoint(UINT8 sequence, UINT8 id, mobj_t *waypoint)
{
waypoints[sequence][id] = waypoint;
if (id >= numwaypoints[sequence])
numwaypoints[sequence] = id + 1;
}
static void P_ResetWaypoints(void)
{
UINT16 sequence, id;
for (sequence = 0; sequence < NUMWAYPOINTSEQUENCES; sequence++)
{
for (id = 0; id < numwaypoints[sequence]; id++)
waypoints[sequence][id] = NULL;
numwaypoints[sequence] = 0;
}
}
mobj_t *P_GetFirstWaypoint(UINT8 sequence)
{
return waypoints[sequence][0];
}
mobj_t *P_GetLastWaypoint(UINT8 sequence)
{
return waypoints[sequence][numwaypoints[sequence] - 1];
}
mobj_t *P_GetPreviousWaypoint(mobj_t *current, boolean wrap)
{
UINT8 sequence = current->threshold;
UINT8 id = current->health;
if (id == 0)
{
if (!wrap)
return NULL;
id = numwaypoints[sequence] - 1;
}
else
id--;
return waypoints[sequence][id];
}
mobj_t *P_GetNextWaypoint(mobj_t *current, boolean wrap)
{
UINT8 sequence = current->threshold;
UINT8 id = current->health;
if (id == numwaypoints[sequence] - 1)
{
if (!wrap)
return NULL;
id = 0;
}
else
id++;
return waypoints[sequence][id];
}
mobj_t *P_GetClosestWaypoint(UINT8 sequence, mobj_t *mo)
{
UINT8 wp;
mobj_t *mo2, *result = NULL;
fixed_t bestdist = 0;
fixed_t curdist;
for (wp = 0; wp < numwaypoints[sequence]; wp++)
{
mo2 = waypoints[sequence][wp];
if (!mo2)
continue;
curdist = P_AproxDistance(P_AproxDistance(mo->x - mo2->x, mo->y - mo2->y), mo->z - mo2->z);
if (result && curdist > bestdist)
continue;
result = mo2;
bestdist = curdist;
}
return result;
}
// Return true if all waypoints are in the same location
boolean P_IsDegeneratedWaypointSequence(UINT8 sequence)
{
mobj_t *first, *waypoint;
UINT8 wp;
if (numwaypoints[sequence] <= 1)
return true;
first = waypoints[sequence][0];
for (wp = 1; wp < numwaypoints[sequence]; wp++)
{
waypoint = waypoints[sequence][wp];
if (!waypoint)
continue;
if (waypoint->x != first->x)
return false;
if (waypoint->y != first->y)
return false;
if (waypoint->z != first->z)
return false;
}
return true;
}
/** Logs an error about a map being corrupt, then terminate.
* This allows reporting highly technical errors for usefulness, without
* confusing a novice map designer who simply needs to run ZenNode.
@ -218,6 +345,7 @@ static void P_ClearSingleMapHeaderInfo(INT16 i)
mapheaderinfo[num]->actnum = 0;
mapheaderinfo[num]->typeoflevel = 0;
mapheaderinfo[num]->nextlevel = (INT16)(i + 1);
mapheaderinfo[num]->marathonnext = 0;
mapheaderinfo[num]->startrings = 0;
mapheaderinfo[num]->sstimer = 90;
mapheaderinfo[num]->ssspheres = 1;
@ -450,9 +578,9 @@ Ploadflat (levelflat_t *levelflat, const char *flatname, boolean resize)
strupr(levelflat->name);
/* If we can't find a flat, try looking for a texture! */
if (( flatnum = R_GetFlatNumForName(flatname) ) == LUMPERROR)
if (( flatnum = R_GetFlatNumForName(levelflat->name) ) == LUMPERROR)
{
if (( texturenum = R_CheckTextureNumForName(flatname) ) == -1)
if (( texturenum = R_CheckTextureNumForName(levelflat->name) ) == -1)
{
// check for REDWALL
if (( texturenum = R_CheckTextureNumForName("REDWALL") ) != -1)
@ -3036,6 +3164,7 @@ static void P_LoadNightsGhosts(void)
{
const size_t glen = strlen(srb2home)+1+strlen("replay")+1+strlen(timeattackfolder)+1+strlen("MAPXX")+1;
char *gpath = malloc(glen);
INT32 i;
if (!gpath)
return;
@ -3043,16 +3172,43 @@ static void P_LoadNightsGhosts(void)
sprintf(gpath,"%s"PATHSEP"replay"PATHSEP"%s"PATHSEP"%s", srb2home, timeattackfolder, G_BuildMapName(gamemap));
// Best Score ghost
if (cv_ghost_bestscore.value && FIL_FileExists(va("%s-score-best.lmp", gpath)))
G_AddGhost(va("%s-score-best.lmp", gpath));
if (cv_ghost_bestscore.value)
{
for (i = 0; i < numskins; ++i)
{
if (cv_ghost_bestscore.value == 1 && players[consoleplayer].skin != i)
continue;
if (FIL_FileExists(va("%s-%s-score-best.lmp", gpath, skins[i].name)))
G_AddGhost(va("%s-%s-score-best.lmp", gpath, skins[i].name));
}
}
// Best Time ghost
if (cv_ghost_besttime.value && FIL_FileExists(va("%s-time-best.lmp", gpath)))
G_AddGhost(va("%s-time-best.lmp", gpath));
if (cv_ghost_besttime.value)
{
for (i = 0; i < numskins; ++i)
{
if (cv_ghost_besttime.value == 1 && players[consoleplayer].skin != i)
continue;
if (FIL_FileExists(va("%s-%s-time-best.lmp", gpath, skins[i].name)))
G_AddGhost(va("%s-%s-time-best.lmp", gpath, skins[i].name));
}
}
// Last ghost
if (cv_ghost_last.value && FIL_FileExists(va("%s-last.lmp", gpath)))
G_AddGhost(va("%s-last.lmp", gpath));
if (cv_ghost_last.value)
{
for (i = 0; i < numskins; ++i)
{
if (cv_ghost_last.value == 1 && players[consoleplayer].skin != i)
continue;
if (FIL_FileExists(va("%s-%s-last.lmp", gpath, skins[i].name)))
G_AddGhost(va("%s-%s-last.lmp", gpath, skins[i].name));
}
}
// Guest ghost
if (cv_ghost_guest.value && FIL_FileExists(va("%s-guest.lmp", gpath)))
@ -3176,21 +3332,6 @@ static void P_InitCamera(void)
}
}
static boolean CanSaveLevel(INT32 mapnum)
{
if (ultimatemode) // never save in ultimate (probably redundant with cursaveslot also being checked)
return false;
if (G_IsSpecialStage(mapnum) // don't save in special stages
|| mapnum == lastmaploaded) // don't save if the last map loaded was this one
return false;
// Any levels that have the savegame flag can save normally.
// If the game is complete for this save slot, then any level can save!
// On the other side of the spectrum, if lastmaploaded is 0, then the save file has only just been created and needs to save ASAP!
return (mapheaderinfo[mapnum-1]->levelflags & LF_SAVEGAME || gamecomplete || !lastmaploaded);
}
static void P_RunSpecialStageWipe(void)
{
tic_t starttime = I_GetTime();
@ -3353,7 +3494,7 @@ static void P_InitGametype(void)
if (G_TagGametype())
P_InitTagGametype();
else if (gametype == GT_RACE && server)
else if (((gametyperules & (GTR_RACE|GTR_LIVES)) == GTR_RACE) && server)
CV_StealthSetValue(&cv_numlaps,
(cv_basenumlaps.value)
? cv_basenumlaps.value
@ -3552,6 +3693,8 @@ boolean P_LoadLevel(boolean fromnetsave)
P_ResetSpawnpoints();
P_ResetWaypoints();
P_MapStart();
if (!P_LoadMapFromFile())
@ -3625,11 +3768,19 @@ boolean P_LoadLevel(boolean fromnetsave)
P_RunCachedActions();
if (!(netgame || multiplayer || demoplayback || demorecording || metalrecording || modeattacking || players[consoleplayer].lives <= 0)
&& (!modifiedgame || savemoddata) && cursaveslot > 0 && CanSaveLevel(gamemap))
G_SaveGame((UINT32)cursaveslot);
lastmaploaded = gamemap; // HAS to be set after saving!!
// Took me 3 hours to figure out why my progression kept on getting overwritten with the titlemap...
if (!titlemapinaction)
{
if (!lastmaploaded) // Start a new game?
{
// I'd love to do this in the menu code instead of here, but everything's a mess and I can't guarantee saving proper player struct info before the first act's started. You could probably refactor it, but it'd be a lot of effort. Easier to just work off known good code. ~toast 22/06/2020
if (!(ultimatemode || netgame || multiplayer || demoplayback || demorecording || metalrecording || modeattacking)
&& (!modifiedgame || savemoddata) && cursaveslot > 0)
G_SaveGame((UINT32)cursaveslot, gamemap);
// If you're looking for saving sp file progression (distinct from G_SaveGameOver), check G_DoCompleted.
}
lastmaploaded = gamemap; // HAS to be set after saving!!
}
if (!fromnetsave) // uglier hack
{ // to make a newly loaded level start on the second frame.

View file

@ -259,10 +259,10 @@ static boolean P_CrossSubsector(size_t num, register los_t *los)
fracx = los->strace.x + FixedMul(los->strace.dx, frac);
fracy = los->strace.y + FixedMul(los->strace.dy, frac);
// calculate sector heights
frontf = (front->f_slope) ? P_GetZAt(front->f_slope, fracx, fracy) : front->floorheight;
frontc = (front->c_slope) ? P_GetZAt(front->c_slope, fracx, fracy) : front->ceilingheight;
backf = (back->f_slope) ? P_GetZAt(back->f_slope, fracx, fracy) : back->floorheight;
backc = (back->c_slope) ? P_GetZAt(back->c_slope, fracx, fracy) : back->ceilingheight;
frontf = P_GetSectorFloorZAt (front, fracx, fracy);
frontc = P_GetSectorCeilingZAt(front, fracx, fracy);
backf = P_GetSectorFloorZAt (back , fracx, fracy);
backc = P_GetSectorCeilingZAt(back , fracx, fracy);
// crosses a two sided line
// no wall to block sight with?
if (frontf == backf && frontc == backc
@ -312,10 +312,10 @@ static boolean P_CrossSubsector(size_t num, register los_t *los)
continue;
}
topz = (*rover->t_slope) ? P_GetZAt(*rover->t_slope, fracx, fracy) : *rover->topheight;
bottomz = (*rover->b_slope) ? P_GetZAt(*rover->b_slope, fracx, fracy) : *rover->bottomheight;
topslope = FixedDiv(topz - los->sightzstart , frac);
bottomslope = FixedDiv(bottomz - los->sightzstart , frac);
topz = P_GetFFloorTopZAt (rover, fracx, fracy);
bottomz = P_GetFFloorBottomZAt(rover, fracx, fracy);
topslope = FixedDiv( topz - los->sightzstart, frac);
bottomslope = FixedDiv(bottomz - los->sightzstart, frac);
if (topslope >= los->topslope && bottomslope <= los->bottomslope)
return false; // view completely blocked
}
@ -328,10 +328,10 @@ static boolean P_CrossSubsector(size_t num, register los_t *los)
continue;
}
topz = (*rover->t_slope) ? P_GetZAt(*rover->t_slope, fracx, fracy) : *rover->topheight;
bottomz = (*rover->b_slope) ? P_GetZAt(*rover->b_slope, fracx, fracy) : *rover->bottomheight;
topslope = FixedDiv(topz - los->sightzstart , frac);
bottomslope = FixedDiv(bottomz - los->sightzstart , frac);
topz = P_GetFFloorTopZAt (rover, fracx, fracy);
bottomz = P_GetFFloorBottomZAt(rover, fracx, fracy);
topslope = FixedDiv( topz - los->sightzstart, frac);
bottomslope = FixedDiv(bottomz - los->sightzstart, frac);
if (topslope >= los->topslope && bottomslope <= los->bottomslope)
return false; // view completely blocked
}
@ -457,21 +457,10 @@ boolean P_CheckSight(mobj_t *t1, mobj_t *t2)
continue;
}
if (*rover->t_slope)
{
topz1 = P_GetZAt(*rover->t_slope, t1->x, t1->y);
topz2 = P_GetZAt(*rover->t_slope, t2->x, t2->y);
}
else
topz1 = topz2 = *rover->topheight;
if (*rover->b_slope)
{
bottomz1 = P_GetZAt(*rover->b_slope, t1->x, t1->y);
bottomz2 = P_GetZAt(*rover->b_slope, t2->x, t2->y);
}
else
bottomz1 = bottomz2 = *rover->bottomheight;
topz1 = P_GetFFloorTopZAt (rover, t1->x, t1->y);
topz2 = P_GetFFloorTopZAt (rover, t2->x, t2->y);
bottomz1 = P_GetFFloorBottomZAt(rover, t1->x, t1->y);
bottomz2 = P_GetFFloorBottomZAt(rover, t2->x, t2->y);
// Check for blocking floors here.
if ((los.sightzstart < bottomz1 && t2->z >= topz2)

View file

@ -655,17 +655,49 @@ void P_SpawnSlopes(const boolean fromsave) {
// Various utilities related to slopes
//
//
// P_GetZAt
//
// Returns the height of the sloped plane at (x, y) as a fixed_t
//
fixed_t P_GetZAt(pslope_t *slope, fixed_t x, fixed_t y)
fixed_t P_GetSlopeZAt(const pslope_t *slope, fixed_t x, fixed_t y)
{
fixed_t dist = FixedMul(x - slope->o.x, slope->d.x) +
FixedMul(y - slope->o.y, slope->d.y);
fixed_t dist = FixedMul(x - slope->o.x, slope->d.x) +
FixedMul(y - slope->o.y, slope->d.y);
return slope->o.z + FixedMul(dist, slope->zdelta);
return slope->o.z + FixedMul(dist, slope->zdelta);
}
// Like P_GetSlopeZAt but falls back to z if slope is NULL
fixed_t P_GetZAt(const pslope_t *slope, fixed_t x, fixed_t y, fixed_t z)
{
return slope ? P_GetSlopeZAt(slope, x, y) : z;
}
// Returns the height of the sector floor at (x, y)
fixed_t P_GetSectorFloorZAt(const sector_t *sector, fixed_t x, fixed_t y)
{
return sector->f_slope ? P_GetSlopeZAt(sector->f_slope, x, y) : sector->floorheight;
}
// Returns the height of the sector ceiling at (x, y)
fixed_t P_GetSectorCeilingZAt(const sector_t *sector, fixed_t x, fixed_t y)
{
return sector->c_slope ? P_GetSlopeZAt(sector->c_slope, x, y) : sector->ceilingheight;
}
// Returns the height of the FOF top at (x, y)
fixed_t P_GetFFloorTopZAt(const ffloor_t *ffloor, fixed_t x, fixed_t y)
{
return *ffloor->t_slope ? P_GetSlopeZAt(*ffloor->t_slope, x, y) : *ffloor->topheight;
}
// Returns the height of the FOF bottom at (x, y)
fixed_t P_GetFFloorBottomZAt(const ffloor_t *ffloor, fixed_t x, fixed_t y)
{
return *ffloor->b_slope ? P_GetSlopeZAt(*ffloor->b_slope, x, y) : *ffloor->bottomheight;
}
// Returns the height of the light list at (x, y)
fixed_t P_GetLightZAt(const lightlist_t *light, fixed_t x, fixed_t y)
{
return light->slope ? P_GetSlopeZAt(light->slope, x, y) : light->height;
}

Some files were not shown because too many files have changed in this diff Show more