diff --git a/.gitignore b/.gitignore index 268e3632906a9b840747e6c015b32848ba45b50f..1dd1c19d51a706b7b792ba2e32895ed9d88f8228 100644 --- a/.gitignore +++ b/.gitignore @@ -23,4 +23,5 @@ Win32_LIB_ASM_Release /bin /build /build/* -/CMakeUserPresets.json \ No newline at end of file +/CMakeUserPresets.json +/out \ No newline at end of file diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 3fed07bcc2457ce00fcf7d571590b8d02f68cf2d..8a2086f46f73292a5d7cc1b6308165707403a66b 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -37,6 +37,7 @@ add_executable(SRB2SDL2 MACOSX_BUNDLE WIN32 m_random.c m_tokenizer.c m_queue.c + m_vector.c info.c p_ceilng.c p_enemy.c diff --git a/src/Sourcefile b/src/Sourcefile index 68ffe0f3f603b580c7c47fa3070909f5f3f82b15..3e926e31850a2055d335326096de6034a87b0a69 100644 --- a/src/Sourcefile +++ b/src/Sourcefile @@ -31,6 +31,7 @@ m_perfstats.c m_random.c m_tokenizer.c m_queue.c +m_vector.c info.c p_ceilng.c p_enemy.c diff --git a/src/console.c b/src/console.c index 0917d916b02c8adaf3b74dc90ddaaf57ef0689c7..751a6e5cad06a57a1c3d3e7f3653dac13e2e2964 100644 --- a/src/console.c +++ b/src/console.c @@ -983,7 +983,7 @@ boolean CON_Responder(event_t *ev) if (modeattacking || metalrecording || marathonmode) return false; - if ((key == gamecontrol[GC_CONSOLE][0] || key == gamecontrol[GC_CONSOLE][1]) && !shiftdown) + if (ev->type == ev_keydown && ((key == gamecontrol[GC_CONSOLE][0] || key == gamecontrol[GC_CONSOLE][1]) && !shiftdown)) { if (consdown) // ignore repeat return true; diff --git a/src/g_game.c b/src/g_game.c index 90ccf29c1854904a8dd06003ddbd40b99de00ca6..99bb0f73c8a4d39cd34721bee240d0b7b5938f68 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -4612,6 +4612,8 @@ void G_LoadGameData(gamedata_t *data) // Saves the main data file, which stores information such as emblems found, etc. void G_SaveGameData(gamedata_t *data) { + UINT8 *data_p; + size_t length; INT32 i, j; UINT8 btemp; @@ -4621,8 +4623,8 @@ void G_SaveGameData(gamedata_t *data) if (!data->loaded) return; // If never loaded (-nodata), don't save - save_p = savebuffer = (UINT8 *)malloc(GAMEDATASIZE); - if (!save_p) + data_p = savebuffer = (UINT8 *)malloc(GAMEDATASIZE); + if (!data_p) { CONS_Alert(CONS_ERROR, M_GetText("No more free memory for saving game data\n")); return; @@ -4631,20 +4633,20 @@ void G_SaveGameData(gamedata_t *data) if (usedCheats) { free(savebuffer); - save_p = savebuffer = NULL; + savebuffer = NULL; return; } // Version test - WRITEUINT32(save_p, GAMEDATA_ID); + WRITEUINT32(data_p, GAMEDATA_ID); - WRITEUINT32(save_p, data->totalplaytime); + WRITEUINT32(data_p, data->totalplaytime); - WRITEUINT32(save_p, quickncasehash(timeattackfolder, sizeof timeattackfolder)); + WRITEUINT32(data_p, quickncasehash(timeattackfolder, sizeof timeattackfolder)); // TODO put another cipher on these things? meh, I don't care... for (i = 0; i < NUMMAPS; i++) - WRITEUINT8(save_p, (data->mapvisited[i] & MV_MAX)); + WRITEUINT8(data_p, (data->mapvisited[i] & MV_MAX)); // To save space, use one bit per collected/achieved/unlocked flag for (i = 0; i < MAXEMBLEMS;) @@ -4652,7 +4654,7 @@ void G_SaveGameData(gamedata_t *data) btemp = 0; for (j = 0; j < 8 && j+i < MAXEMBLEMS; ++j) btemp |= (data->collected[j+i] << j); - WRITEUINT8(save_p, btemp); + WRITEUINT8(data_p, btemp); i += j; } for (i = 0; i < MAXEXTRAEMBLEMS;) @@ -4660,7 +4662,7 @@ void G_SaveGameData(gamedata_t *data) btemp = 0; for (j = 0; j < 8 && j+i < MAXEXTRAEMBLEMS; ++j) btemp |= (data->extraCollected[j+i] << j); - WRITEUINT8(save_p, btemp); + WRITEUINT8(data_p, btemp); i += j; } for (i = 0; i < MAXUNLOCKABLES;) @@ -4668,7 +4670,7 @@ void G_SaveGameData(gamedata_t *data) btemp = 0; for (j = 0; j < 8 && j+i < MAXUNLOCKABLES; ++j) btemp |= (data->unlocked[j+i] << j); - WRITEUINT8(save_p, btemp); + WRITEUINT8(data_p, btemp); i += j; } for (i = 0; i < MAXCONDITIONSETS;) @@ -4676,30 +4678,30 @@ void G_SaveGameData(gamedata_t *data) btemp = 0; for (j = 0; j < 8 && j+i < MAXCONDITIONSETS; ++j) btemp |= (data->achieved[j+i] << j); - WRITEUINT8(save_p, btemp); + WRITEUINT8(data_p, btemp); i += j; } - WRITEUINT32(save_p, data->timesBeaten); - WRITEUINT32(save_p, data->timesBeatenWithEmeralds); - WRITEUINT32(save_p, data->timesBeatenUltimate); + WRITEUINT32(data_p, data->timesBeaten); + WRITEUINT32(data_p, data->timesBeatenWithEmeralds); + WRITEUINT32(data_p, data->timesBeatenUltimate); // Main records for (i = 0; i < NUMMAPS; i++) { if (data->mainrecords[i]) { - WRITEUINT32(save_p, data->mainrecords[i]->score); - WRITEUINT32(save_p, data->mainrecords[i]->time); - WRITEUINT16(save_p, data->mainrecords[i]->rings); + WRITEUINT32(data_p, data->mainrecords[i]->score); + WRITEUINT32(data_p, data->mainrecords[i]->time); + WRITEUINT16(data_p, data->mainrecords[i]->rings); } else { - WRITEUINT32(save_p, 0); - WRITEUINT32(save_p, 0); - WRITEUINT16(save_p, 0); + WRITEUINT32(data_p, 0); + WRITEUINT32(data_p, 0); + WRITEUINT16(data_p, 0); } - WRITEUINT8(save_p, 0); // compat + WRITEUINT8(data_p, 0); // compat } // NiGHTS records @@ -4707,25 +4709,25 @@ void G_SaveGameData(gamedata_t *data) { if (!data->nightsrecords[i] || !data->nightsrecords[i]->nummares) { - WRITEUINT8(save_p, 0); + WRITEUINT8(data_p, 0); continue; } - WRITEUINT8(save_p, data->nightsrecords[i]->nummares); + WRITEUINT8(data_p, data->nightsrecords[i]->nummares); for (curmare = 0; curmare < (data->nightsrecords[i]->nummares + 1); ++curmare) { - WRITEUINT32(save_p, data->nightsrecords[i]->score[curmare]); - WRITEUINT8(save_p, data->nightsrecords[i]->grade[curmare]); - WRITEUINT32(save_p, data->nightsrecords[i]->time[curmare]); + WRITEUINT32(data_p, data->nightsrecords[i]->score[curmare]); + WRITEUINT8(data_p, data->nightsrecords[i]->grade[curmare]); + WRITEUINT32(data_p, data->nightsrecords[i]->time[curmare]); } } - length = save_p - savebuffer; + length = data_p - savebuffer; FIL_WriteFile(va(pandf, srb2home, gamedatafilename), savebuffer, length); free(savebuffer); - save_p = savebuffer = NULL; + savebuffer = NULL; } #define VERSIONSIZE 16 diff --git a/src/hardware/hw_main.c b/src/hardware/hw_main.c index be0c7ba629087e872b01ac51c8996ada8e78b314..828f247674b4a85aae18de777aa558b0c9f6790d 100644 --- a/src/hardware/hw_main.c +++ b/src/hardware/hw_main.c @@ -5514,7 +5514,10 @@ static void HWR_ProjectSprite(mobj_t *thing) translation = thing->translation; //Hurdler: 25/04/2000: now support colormap in hardware mode - vis->colormap = R_GetTranslationForThing(vis->mobj, color, translation); + if ((thing->flags2 & MF2_LINKDRAW) && thing->tracer) + vis->colormap = R_GetTranslationForThing(thing->tracer, color, translation); + else + vis->colormap = R_GetTranslationForThing(thing, color, translation); // set top/bottom coords vis->gzt = gzt; diff --git a/src/lua_blockmaplib.c b/src/lua_blockmaplib.c index 8d47f3dc1e31a092f8a9a25e9e9fe667ca178092..6b4b6229f68ffc869e1f6eccdd6999018d150ae0 100644 --- a/src/lua_blockmaplib.c +++ b/src/lua_blockmaplib.c @@ -34,46 +34,28 @@ typedef UINT8 (*blockmap_func)(lua_State *, INT32, INT32, mobj_t *); static boolean blockfuncerror = false; // errors should only print once per search blockmap call // Helper function for "objects" search -static UINT8 lib_searchBlockmap_Objects(lua_State *L, INT32 x, INT32 y, mobj_t *thing) +static UINT8 lib_searchBlockmap_Objects(lua_State *L, mobj_t *thing, mobj_t *mobj) { - mobj_t *mobj, *bnext = NULL; - - if (x < 0 || y < 0 || x >= bmapwidth || y >= bmapheight) - return 0; - - // Check interaction with the objects in the blockmap. - for (mobj = blocklinks[y*bmapwidth + x]; mobj; mobj = bnext) - { - P_SetTarget(&bnext, mobj->bnext); // We want to note our reference to bnext here incase it is MF_NOTHINK and gets removed! - if (mobj == thing) - continue; // our thing just found itself, so move on - lua_pushvalue(L, 1); // push function - LUA_PushUserdata(L, thing, META_MOBJ); - LUA_PushUserdata(L, mobj, META_MOBJ); - if (lua_pcall(gL, 2, 1, 0)) { - if (!blockfuncerror || cv_debug & DBG_LUA) - CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1)); - lua_pop(gL, 1); - blockfuncerror = true; - P_SetTarget(&bnext, NULL); - return 0; // *shrugs* - } - if (!lua_isnil(gL, -1)) - { // if nil, continue - P_SetTarget(&bnext, NULL); - if (lua_toboolean(gL, -1)) - return 2; // stop whole search - else - return 1; // stop block search - } + lua_pushvalue(L, 1); // push function + LUA_PushUserdata(L, thing, META_MOBJ); + LUA_PushUserdata(L, mobj, META_MOBJ); + if (lua_pcall(gL, 2, 1, 0)) { + if (!blockfuncerror || cv_debug & DBG_LUA) + CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1)); lua_pop(gL, 1); - if (P_MobjWasRemoved(thing) // func just popped our thing, cannot continue. - || (bnext && P_MobjWasRemoved(bnext))) // func just broke blockmap chain, cannot continue. - { - P_SetTarget(&bnext, NULL); - return (P_MobjWasRemoved(thing)) ? 2 : 1; - } + blockfuncerror = true; + return 0; // *shrugs* } + if (!lua_isnil(gL, -1)) + { // if nil, continue + if (lua_toboolean(gL, -1)) + return 2; // stop whole search + else + return 1; // stop block search + } + lua_pop(gL, 1); + if (P_MobjWasRemoved(thing)) // func just popped our thing, cannot continue. + return P_MobjWasRemoved(thing) ? 2 : 1; return 0; } @@ -233,6 +215,7 @@ static int lib_searchBlockmap(lua_State *L) boolean retval = true; UINT8 funcret = 0; blockmap_func searchFunc; + boolean searchObjects = false; lua_remove(L, 1); // remove searchtype, stack is now function, mobj, [x1, x2, y1, y2] luaL_checktype(L, 1, LUA_TFUNCTION); @@ -241,7 +224,8 @@ static int lib_searchBlockmap(lua_State *L) { case 0: // "objects" default: - searchFunc = lib_searchBlockmap_Objects; + searchObjects = true; + searchFunc = NULL; break; case 1: // "lines" searchFunc = lib_searchBlockmap_Lines; @@ -286,24 +270,66 @@ static int lib_searchBlockmap(lua_State *L) BMBOUNDFIX(xl, xh, yl, yh); blockfuncerror = false; // reset - validcount++; - for (bx = xl; bx <= xh; bx++) - for (by = yl; by <= yh; by++) - { - funcret = searchFunc(L, bx, by, mobj); - // return value of searchFunc determines searchFunc's return value and/or when to stop - if (funcret == 2){ // stop whole search - lua_pushboolean(L, false); // return false - return 1; + + if (!searchObjects) { + validcount++; + + for (bx = xl; bx <= xh; bx++) + for (by = yl; by <= yh; by++) + { + funcret = searchFunc(L, bx, by, mobj); + + // return value of searchFunc determines searchFunc's return value and/or when to stop + if (funcret == 2) { // stop whole search + lua_pushboolean(L, false); // return false + return 1; + } + else if (funcret == 1) // search was interrupted for this block + retval = false; // this changes the return value, but doesn't stop the whole search + + // else don't do anything, continue as normal + if (P_MobjWasRemoved(mobj)) { // ...unless the original object was removed + lua_pushboolean(L, false); // in which case we have to stop now regardless + return 1; + } } - else if (funcret == 1) // search was interrupted for this block - retval = false; // this changes the return value, but doesn't stop the whole search - // else don't do anything, continue as normal - if (P_MobjWasRemoved(mobj)){ // ...unless the original object was removed - lua_pushboolean(L, false); // in which case we have to stop now regardless - return 1; + } + else { + bthingit_t *it = P_NewBlockThingsIterator(xl, yl, xh, yh); + if (!it) { + lua_pushboolean(L, false); + return 1; + } + + mobj_t *itmobj = NULL; + + do + { + itmobj = P_BlockThingsIteratorNext(it, false); + if (itmobj) + { + if (mobj == itmobj) + continue; // our thing just found itself, so move on + + funcret = lib_searchBlockmap_Objects(L, mobj, itmobj); + if (funcret == 2) { + lua_pushboolean(L, false); + return 1; + } + else if (funcret == 1) + retval = false; + + if (P_MobjWasRemoved(mobj)) { + retval = false; + break; + } } } + while (itmobj != NULL); + + P_FreeBlockThingsIterator(it); + } + lua_pushboolean(L, retval); return 1; } diff --git a/src/lua_maplib.c b/src/lua_maplib.c index 5b80d4d38c9db705c59c7003f37af2ce87bb0272..6b489f22b1939941c0ebf83a5a3ae0b7e215de5a 100644 --- a/src/lua_maplib.c +++ b/src/lua_maplib.c @@ -486,6 +486,8 @@ static int lib_iterateSectorThinglist(lua_State *L) if (!lua_isnil(L, 1)) { thing = *((mobj_t **)luaL_checkudata(L, 1, META_MOBJ)); + if (P_MobjWasRemoved(thing)) + return luaL_error(L, "current entry in thinglist was removed; avoid calling P_RemoveMobj on entries!"); thing = thing->snext; } else @@ -2609,12 +2611,18 @@ static int slope_set(lua_State *L) slope->o.z = luaL_checkfixed(L, -1); else slope->o.z = 0; + DVector3_Load(&slope->dorigin, + FixedToDouble(slope->o.x), + FixedToDouble(slope->o.y), + FixedToDouble(slope->o.z) + ); lua_pop(L, 1); break; } - case slope_zdelta: { // zdelta, this is temp until i figure out wtf to do + case slope_zdelta: { // zdelta slope->zdelta = luaL_checkfixed(L, 3); slope->zangle = R_PointToAngle2(0, 0, FRACUNIT, -slope->zdelta); + slope->dzdelta = FixedToDouble(slope->zdelta); P_CalculateSlopeNormal(slope); break; } @@ -2624,6 +2632,7 @@ static int slope_set(lua_State *L) return luaL_error(L, "invalid zangle for slope!"); slope->zangle = zangle; slope->zdelta = -FINETANGENT(((slope->zangle+ANGLE_90)>>ANGLETOFINESHIFT) & 4095); + slope->dzdelta = FixedToDouble(slope->zdelta); P_CalculateSlopeNormal(slope); break; } @@ -2631,6 +2640,8 @@ static int slope_set(lua_State *L) slope->xydirection = luaL_checkangle(L, 3); slope->d.x = -FINECOSINE((slope->xydirection>>ANGLETOFINESHIFT) & FINEMASK); slope->d.y = -FINESINE((slope->xydirection>>ANGLETOFINESHIFT) & FINEMASK); + slope->dnormdir.x = FixedToDouble(slope->d.x); + slope->dnormdir.y = FixedToDouble(slope->d.y); P_CalculateSlopeNormal(slope); break; } diff --git a/src/lua_mobjlib.c b/src/lua_mobjlib.c index 85e4590c571e23cfa701f6f9fb15b5948350e1f0..5a3f8ad114b9f1269e3a49b76cb4aa9dd76417ad 100644 --- a/src/lua_mobjlib.c +++ b/src/lua_mobjlib.c @@ -355,8 +355,12 @@ static int mobj_get(lua_State *L) lua_pushinteger(L, mo->blendmode); break; case mobj_bnext: - LUA_PushUserdata(L, mo->bnext, META_MOBJ); - break; + if (mo->blocknode && mo->blocknode->bnext) { + LUA_PushUserdata(L, mo->blocknode->bnext->mobj, META_MOBJ); + break; + } + else + return 0; case mobj_bprev: // bprev -- same deal as sprev above, but for the blockmap. return UNIMPLEMENTED; @@ -669,7 +673,6 @@ static int mobj_set(lua_State *L) sector_list = NULL; } mo->snext = NULL, mo->sprev = NULL; - mo->bnext = NULL, mo->bprev = NULL; P_SetThingPosition(mo); } else diff --git a/src/lua_script.c b/src/lua_script.c index 7db9624407818feaba654f6326690a79e5d243b6..2e65dfc2522c590c5cdb82651ffc5dbc474e6f3d 100644 --- a/src/lua_script.c +++ b/src/lua_script.c @@ -1501,8 +1501,11 @@ static void ArchiveTables(void) { // Write key e = ArchiveValue(TABLESINDEX, -2); // key should be either a number or a string, ArchiveValue can handle this. - if (e == 2) // invalid key type (function, thread, lightuserdata, or anything we don't recognise) + if (e == 1) + n++; // the table contained a new table we'll have to archive. :( + else if (e == 2) // invalid key type (function, thread, lightuserdata, or anything we don't recognise) CONS_Alert(CONS_ERROR, "Index '%s' (%s) of table %d could not be archived!\n", lua_tostring(gL, -2), luaL_typename(gL, -2), i); + // Write value e = ArchiveValue(TABLESINDEX, -1); if (e == 1) @@ -1717,10 +1720,15 @@ static void UnArchiveTables(void) lua_rawgeti(gL, TABLESINDEX, i); while (true) { - if (UnArchiveValue(TABLESINDEX) == 1) // read key + UINT8 e = UnArchiveValue(TABLESINDEX); // read key + if (e == 1) // End of table break; + else if (e == 2) // Key contains a new table + n++; + if (UnArchiveValue(TABLESINDEX) == 2) // read value n++; + if (lua_isnil(gL, -2)) // if key is nil (if a function etc was accidentally saved) { CONS_Alert(CONS_ERROR, "A nil key in table %d was found! (Invalid key type or corrupted save?)\n", i); diff --git a/src/m_fixed.c b/src/m_fixed.c index b674e3b2c8e230524d57ea540dada83b8bf75eb6..19c1e80919315071fe40086dc93ec42b937ffb26 100644 --- a/src/m_fixed.c +++ b/src/m_fixed.c @@ -3,6 +3,7 @@ // Copyright (C) 1993-1996 by id Software, Inc. // Copyright (C) 1998-2000 by DooM Legacy Team. // Copyright (C) 1999-2023 by Sonic Team Junior. +// Copyright (C) 2009 by Stephen McGranahan. // // This program is free software distributed under the // terms of the GNU General Public License, version 2. diff --git a/src/m_fixed.h b/src/m_fixed.h index 4e644c9b6d5db3687a211526e3a50a5f03e9702f..f40c7b30870baffdd978cec0ecb447b65f74ba6f 100644 --- a/src/m_fixed.h +++ b/src/m_fixed.h @@ -50,6 +50,20 @@ FUNCMATH FUNCINLINE static ATTRINLINE fixed_t FloatToFixed(float f) return (fixed_t)(f * FRACUNIT); } +/*! + \brief convert fixed_t into double-precision floating number +*/ + +FUNCMATH FUNCINLINE static ATTRINLINE double FixedToDouble(fixed_t x) +{ + return x / (double)FRACUNIT; +} + +FUNCMATH FUNCINLINE static ATTRINLINE fixed_t DoubleToFixed(double f) +{ + return (fixed_t)(f * FRACUNIT); +} + // for backwards compat #define FIXED_TO_FLOAT(x) FixedToFloat(x) // (((float)(x)) / ((float)FRACUNIT)) #define FLOAT_TO_FIXED(f) FloatToFixed(f) // (fixed_t)((f) * ((float)FRACUNIT)) diff --git a/src/m_vector.c b/src/m_vector.c new file mode 100644 index 0000000000000000000000000000000000000000..3132a869d458c83aac517ac609258b506b6395b8 --- /dev/null +++ b/src/m_vector.c @@ -0,0 +1,53 @@ +// SONIC ROBO BLAST 2 +//----------------------------------------------------------------------------- +// Copyright (C) 2024 by Sonic Team Junior. +// Copyright (C) 2009 by Stephen McGranahan. +// +// 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 m_vector.c +/// \brief Basic vector functions + +#include "doomdef.h" + +#include "m_vector.h" + +void DVector3_Load(dvector3_t *vec, double x, double y, double z) +{ + vec->x = x; + vec->y = y; + vec->z = z; +} + +double DVector3_Magnitude(const dvector3_t *a_normal) +{ + double xs = a_normal->x * a_normal->x; + double ys = a_normal->y * a_normal->y; + double zs = a_normal->z * a_normal->z; + return sqrt(xs + ys + zs); +} + +double DVector3_Normalize(dvector3_t *a_normal) +{ + double magnitude = DVector3_Magnitude(a_normal); + a_normal->x /= magnitude; + a_normal->y /= magnitude; + a_normal->z /= magnitude; + return magnitude; +} + +void DVector3_Negate(dvector3_t *a_o) +{ + a_o->x = -a_o->x; + a_o->y = -a_o->y; + a_o->z = -a_o->z; +} + +void DVector3_Cross(const dvector3_t *a_1, const dvector3_t *a_2, dvector3_t *a_o) +{ + a_o->x = (a_1->y * a_2->z) - (a_1->z * a_2->y); + a_o->y = (a_1->z * a_2->x) - (a_1->x * a_2->z); + a_o->z = (a_1->x * a_2->y) - (a_1->y * a_2->x); +} diff --git a/src/m_vector.h b/src/m_vector.h new file mode 100644 index 0000000000000000000000000000000000000000..55669be037c9ce54ffd70bf3782225e913e1a1b4 --- /dev/null +++ b/src/m_vector.h @@ -0,0 +1,27 @@ +// SONIC ROBO BLAST 2 +//----------------------------------------------------------------------------- +// Copyright (C) 2024 by Sonic Team Junior. +// Copyright (C) 2009 by Stephen McGranahan. +// +// 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 m_vector.h +/// \brief Basic vector functions + +#ifndef __M_VECTOR__ +#define __M_VECTOR__ + +typedef struct +{ + double x, y, z; +} dvector3_t; + +void DVector3_Load(dvector3_t *vec, double x, double y, double z); +double DVector3_Magnitude(const dvector3_t *a_normal); +double DVector3_Normalize(dvector3_t *a_normal); +void DVector3_Negate(dvector3_t *a_o); +void DVector3_Cross(const dvector3_t *a_1, const dvector3_t *a_2, dvector3_t *a_o); + +#endif diff --git a/src/netcode/client_connection.c b/src/netcode/client_connection.c index 907021e7dafddfb3f31b2a2dd850f15d4890a16e..36ed718265b3cfa42acc3ef238c436882bb918a1 100644 --- a/src/netcode/client_connection.c +++ b/src/netcode/client_connection.c @@ -1,7 +1,7 @@ // SONIC ROBO BLAST 2 //----------------------------------------------------------------------------- // Copyright (C) 1998-2000 by DooM Legacy Team. -// Copyright (C) 1999-2023 by Sonic Team Junior. +// Copyright (C) 1999-2024 by Sonic Team Junior. // // This program is free software distributed under the // terms of the GNU General Public License, version 2. @@ -43,11 +43,45 @@ tic_t firstconnectattempttime = 0; UINT8 mynode; static void *snake = NULL; -static void CL_DrawConnectionStatusBox(void) +static boolean IsDownloadingFile(void) +{ + if (cl_mode == CL_DOWNLOADFILES || cl_mode == CL_DOWNLOADHTTPFILES) + return filedownload.current != -1; + + return false; +} + +static void DrawConnectionStatusBox(void) { M_DrawTextBox(BASEVIDWIDTH/2-128-8, BASEVIDHEIGHT-16-8, 32, 1); - if (cl_mode != CL_CONFIRMCONNECT) - V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT-16-16, V_YELLOWMAP, "Press ESC to abort"); + + if (cl_mode == CL_CONFIRMCONNECT || IsDownloadingFile()) + return; + + V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT-16-16, V_YELLOWMAP, "Press ESC to abort"); +} + +static void DrawFileProgress(fileneeded_t *file, int y) +{ + Net_GetNetStat(); + + INT32 dldlength = (INT32)((file->currentsize/(double)file->totalsize) * 256); + if (dldlength > 256) + dldlength = 256; + V_DrawFill(BASEVIDWIDTH/2-128, y, 256, 8, 111); + V_DrawFill(BASEVIDWIDTH/2-128, y, dldlength, 8, 96); + + const char *progress_str; + if (file->totalsize >= 1024*1024) + progress_str = va(" %.2fMiB/%.2fMiB", (double)file->currentsize / (1024*1024), (double)file->totalsize / (1024*1024)); + else if (file->totalsize < 1024) + progress_str = va(" %4uB/%4uB", file->currentsize, file->totalsize); + else + progress_str = va(" %.2fKiB/%.2fKiB", (double)file->currentsize / 1024, (double)file->totalsize / 1024); + + V_DrawString(BASEVIDWIDTH/2-128, y, V_20TRANS|V_ALLOWLOWERCASE, progress_str); + + V_DrawRightAlignedString(BASEVIDWIDTH/2+128, y, V_20TRANS|V_MONOSPACE, va("%3.1fK/s ", ((double)getbps)/1024)); } // @@ -55,21 +89,21 @@ static void CL_DrawConnectionStatusBox(void) // // Keep the local client informed of our status. // -static inline void CL_DrawConnectionStatus(void) +static void CL_DrawConnectionStatus(void) { INT32 ccstime = I_GetTime(); // Draw background fade V_DrawFadeScreen(0xFF00, 16); // force default - if (cl_mode != CL_DOWNLOADFILES && cl_mode != CL_LOADFILES) + if (cl_mode != CL_DOWNLOADFILES && cl_mode != CL_DOWNLOADHTTPFILES && cl_mode != CL_LOADFILES) { INT32 animtime = ((ccstime / 4) & 15) + 16; UINT8 palstart; const char *cltext; // Draw the bottom box. - CL_DrawConnectionStatusBox(); + DrawConnectionStatusBox(); if (cl_mode == CL_SEARCHING) palstart = 32; // Red @@ -78,33 +112,20 @@ static inline void CL_DrawConnectionStatus(void) else palstart = 96; // Green - if (!(cl_mode == CL_DOWNLOADSAVEGAME && lastfilenum != -1)) + if (!(cl_mode == CL_DOWNLOADSAVEGAME && filedownload.current != -1)) for (INT32 i = 0; i < 16; ++i) // 15 pal entries total. V_DrawFill((BASEVIDWIDTH/2-128) + (i * 16), BASEVIDHEIGHT-16, 16, 8, palstart + ((animtime - i) & 15)); switch (cl_mode) { case CL_DOWNLOADSAVEGAME: - if (fileneeded && lastfilenum != -1) + if (fileneeded && filedownload.current != -1) { - UINT32 currentsize = fileneeded[lastfilenum].currentsize; - UINT32 totalsize = fileneeded[lastfilenum].totalsize; - INT32 dldlength; + fileneeded_t *file = &fileneeded[filedownload.current]; cltext = M_GetText("Downloading game state..."); - Net_GetNetStat(); - - dldlength = (INT32)((currentsize/(double)totalsize) * 256); - if (dldlength > 256) - dldlength = 256; - V_DrawFill(BASEVIDWIDTH/2-128, BASEVIDHEIGHT-16, 256, 8, 111); - V_DrawFill(BASEVIDWIDTH/2-128, BASEVIDHEIGHT-16, dldlength, 8, 96); - V_DrawString(BASEVIDWIDTH/2-128, BASEVIDHEIGHT-16, V_20TRANS|V_MONOSPACE, - va(" %4uK/%4uK",currentsize>>10,totalsize>>10)); - - V_DrawRightAlignedString(BASEVIDWIDTH/2+128, BASEVIDHEIGHT-16, V_20TRANS|V_MONOSPACE, - va("%3.1fK/s ", ((double)getbps)/1024)); + DrawFileProgress(file, BASEVIDHEIGHT-16); } else cltext = M_GetText("Waiting to download game state..."); @@ -156,12 +177,11 @@ static inline void CL_DrawConnectionStatus(void) V_DrawFill(BASEVIDWIDTH/2-128, BASEVIDHEIGHT-16, 256, 8, 111); V_DrawFill(BASEVIDWIDTH/2-128, BASEVIDHEIGHT-16, totalfileslength, 8, 96); V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT-16, V_20TRANS|V_MONOSPACE, - va(" %2u/%2u Files",loadcompletednum,fileneedednum)); + va(" %2u/%2u files",loadcompletednum,fileneedednum)); } - else if (lastfilenum != -1) + else if (filedownload.current != -1) { - INT32 dldlength; - static char tempname[28]; + char tempname[28]; fileneeded_t *file; char *filename; @@ -169,24 +189,16 @@ static inline void CL_DrawConnectionStatus(void) Snake_Draw(snake); // Draw the bottom box. - CL_DrawConnectionStatusBox(); + DrawConnectionStatusBox(); if (fileneeded) { - file = &fileneeded[lastfilenum]; + file = &fileneeded[filedownload.current]; filename = file->filename; } else return; - Net_GetNetStat(); - dldlength = (INT32)((file->currentsize/(double)file->totalsize) * 256); - if (dldlength > 256) - dldlength = 256; - V_DrawFill(BASEVIDWIDTH/2-128, BASEVIDHEIGHT-16, 256, 8, 111); - V_DrawFill(BASEVIDWIDTH/2-128, BASEVIDHEIGHT-16, dldlength, 8, 96); - - memset(tempname, 0, sizeof(tempname)); // offset filename to just the name only part filename += strlen(filename) - nameonlylength(filename); @@ -199,22 +211,56 @@ static inline void CL_DrawConnectionStatus(void) } else // we can copy the whole thing in safely { - strncpy(tempname, filename, sizeof(tempname)-1); + strlcpy(tempname, filename, sizeof(tempname)); } - V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT-16-24, V_YELLOWMAP, - va(M_GetText("Downloading \"%s\""), tempname)); - V_DrawString(BASEVIDWIDTH/2-128, BASEVIDHEIGHT-16, V_20TRANS|V_MONOSPACE, - va(" %4uK/%4uK",fileneeded[lastfilenum].currentsize>>10,file->totalsize>>10)); - V_DrawRightAlignedString(BASEVIDWIDTH/2+128, BASEVIDHEIGHT-16, V_20TRANS|V_MONOSPACE, - va("%3.1fK/s ", ((double)getbps)/1024)); + // Lactozilla: Disabled, see below change + // (also it doesn't really fit on a typical SRB2 screen) +#if 0 + const char *download_str = cl_mode == CL_DOWNLOADHTTPFILES + ? M_GetText("HTTP downloading \"%s\"") + : M_GetText("Downloading \"%s\""); +#else + const char *download_str = M_GetText("Downloading \"%s\""); +#endif + + V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT-16-24, V_ALLOWLOWERCASE|V_YELLOWMAP, + va(download_str, tempname)); + + // Rusty: actually lets do this instead + if (cl_mode == CL_DOWNLOADHTTPFILES) + { + const char *http_source = filedownload.http_source; + + if (strlen(http_source) > sizeof(tempname)-1) // too long to display fully + { + size_t endhalfpos = strlen(http_source)-10; + // display as first 14 chars + ... + last 10 chars + // which should add up to 27 if our math(s) is correct + snprintf(tempname, sizeof(tempname), "%.14s...%.10s", http_source, http_source+endhalfpos); + } + else // we can copy the whole thing in safely + { + strlcpy(tempname, http_source, sizeof(tempname)); + } + + V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT-16-16, V_ALLOWLOWERCASE|V_YELLOWMAP, + va(M_GetText("from %s"), tempname)); + } + else + { + V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT-16-16, V_ALLOWLOWERCASE|V_YELLOWMAP, + M_GetText("from the server")); + } + + DrawFileProgress(file, BASEVIDHEIGHT-16); } else { if (snake) Snake_Draw(snake); - CL_DrawConnectionStatusBox(); + DrawConnectionStatusBox(); V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT-16-24, V_YELLOWMAP, M_GetText("Waiting to download files...")); } @@ -479,23 +525,98 @@ void CL_UpdateServerList(boolean internetsearch, INT32 room) #endif // MASTERSERVER } +static boolean IsFileDownloadable(fileneeded_t *file) +{ + return file->status == FS_NOTFOUND || file->status == FS_MD5SUMBAD; +} + +static boolean UseDirectDownloader(void) +{ + return filedownload.http_source[0] == '\0' || filedownload.http_failed; +} + +static void DoLoadFiles(void) +{ + Snake_Free(&snake); + + cl_mode = CL_LOADFILES; +} + +static void AbortConnection(void) +{ + Snake_Free(&snake); + + D_QuitNetGame(); + CL_Reset(); + D_StartTitle(); + + // Will be reset by caller. Signals refusal. + cl_mode = CL_ABORTED; +} + +static void BeginDownload(boolean direct) +{ + filedownload.current = 0; + filedownload.remaining = 0; + + for (int i = 0; i < fileneedednum; i++) + { + // Lactozilla: Rusty had fixed this SLIGHTLY incorrectly. + // Since [redacted] doesn't HAVE its own file transmission code - it spins an HTTP server - it wasn't + // instantly obvious to us where to do this, and we didn't want to check the original implementation. + if (fileneeded[i].status == FS_FALLBACK) + fileneeded[i].status = FS_NOTFOUND; + + if (IsFileDownloadable(&fileneeded[i])) + filedownload.remaining++; + } + + if (!filedownload.remaining) + { + DoLoadFiles(); + return; + } + + if (!direct) + { + cl_mode = CL_DOWNLOADHTTPFILES; + Snake_Allocate(&snake); + + // Discard any paused downloads + CL_AbortDownloadResume(); + } + else + { + // do old LEGACY request + if (CL_SendFileRequest()) + { + cl_mode = CL_DOWNLOADFILES; + + // don't alloc snake if already alloced + if (!snake) + Snake_Allocate(&snake); + } + else + { + AbortConnection(); + + // why was this its own cl_mode_t? + M_StartMessage(M_GetText( + "The direct downloader encountered an error.\n" + "See the logfile for more info.\n\n" + "Press ESC\n" + ), NULL, MM_NOTHING); + } + } +} + static void M_ConfirmConnect(event_t *ev) { if (ev->type == ev_keydown) { if (ev->key == ' ' || ev->key == 'y' || ev->key == KEY_ENTER || ev->key == KEY_JOY1) { - if (totalfilesrequestednum > 0) - { - if (CL_SendFileRequest()) - { - cl_mode = CL_DOWNLOADFILES; - Snake_Allocate(&snake); - } - } - else - cl_mode = CL_LOADFILES; - + BeginDownload(UseDirectDownloader()); M_ClearMenus(true); } else if (ev->key == 'n' || ev->key == KEY_ESCAPE || ev->key == KEY_JOY1 + 3) @@ -506,22 +627,122 @@ static void M_ConfirmConnect(event_t *ev) } } -static boolean CL_FinishedFileList(void) +static const char *GetPrintableFileSize(UINT64 filesize) { - INT32 i; - char *downloadsize = NULL; + static char downloadsize[32]; + + if (filesize >= 1024*1024) + snprintf(downloadsize, sizeof(downloadsize), "%.2fMiB", (double)filesize / (1024*1024)); + else if (filesize < 1024) + snprintf(downloadsize, sizeof(downloadsize), "%sB", sizeu1(filesize)); + else + snprintf(downloadsize, sizeof(downloadsize), "%.2fKiB", (double)filesize / 1024); + + return downloadsize; +} + +static void ShowDownloadConsentMessage(void) +{ + UINT64 totalsize = 0; + + filedownload.completednum = 0; + filedownload.completedsize = 0; + + if (fileneeded == NULL) + I_Error("CL_FinishedFileList: fileneeded == NULL"); + + for (int i = 0; i < fileneedednum; i++) + { + if (IsFileDownloadable(&fileneeded[i])) + totalsize += fileneeded[i].totalsize; + } + + const char *downloadsize = GetPrintableFileSize(totalsize); + + if (serverisfull) + M_StartMessage(va(M_GetText( + "This server is full!\n" + "Download of %s of additional\ncontent is required to join.\n" + "\n" + "You may download server addons,\nand wait for a slot.\n" + "\n" + "Press ENTER to continue\nor ESC to cancel.\n" + ), downloadsize), M_ConfirmConnect, MM_EVENTHANDLER); + else + M_StartMessage(va(M_GetText( + "Download of %s of additional\ncontent is required to join.\n" + "\n" + "Press ENTER to continue\nor ESC to cancel.\n" + ), downloadsize), M_ConfirmConnect, MM_EVENTHANDLER); + + cl_mode = CL_CONFIRMCONNECT; + curfadevalue = 0; +} + +static const char *GetDirectDownloadFailReason(UINT8 dlstatus) +{ + switch (dlstatus) + { + case DLSTATUS_TOOLARGE: + return M_GetText("Some addons are larger than the server is willing to send."); + case DLSTATUS_WONTSEND: + return M_GetText("The server is not allowing download requests."); + case DLSTATUS_NODOWNLOAD: + return M_GetText("All addons downloadable, but you have chosen to disable addon downloading."); + case DLSTATUS_FOLDER: + return M_GetText("One or more addons were added as a folder, which the server cannot send."); + } + + return "Unknown reason"; +} + +static void HandleDirectDownloadFail(UINT8 dlstatus) +{ + // not downloadable, put reason in console + CONS_Alert(CONS_NOTICE, M_GetText("You need additional addons to connect to this server:\n")); + + for (UINT8 i = 0; i < fileneedednum; i++) + { + if (fileneeded[i].folder || (fileneeded[i].status != FS_FOUND && fileneeded[i].status != FS_OPEN)) + { + CONS_Printf(" * \"%s\" ", fileneeded[i].filename); - //CONS_Printf(M_GetText("Checking files...\n")); - i = CL_CheckFiles(); + if (fileneeded[i].folder) + { + CONS_Printf("(folder)"); + } + else + { + CONS_Printf("(%s)", GetPrintableFileSize(fileneeded[i].totalsize)); + + if (fileneeded[i].status == FS_NOTFOUND) + CONS_Printf(M_GetText(" not found, md5: ")); + else if (fileneeded[i].status == FS_MD5SUMBAD) + CONS_Printf(M_GetText(" wrong version, md5: ")); + + char md5tmp[33]; + for (INT32 j = 0; j < 16; j++) + sprintf(&md5tmp[j*2], "%02x", fileneeded[i].md5sum[j]); + CONS_Printf("%s", md5tmp); + } + + CONS_Printf("\n"); + } + } + + CONS_Printf("%s\n", GetDirectDownloadFailReason(dlstatus)); +} + +static boolean CL_FinishedFileList(void) +{ + INT32 i = CL_CheckFiles(); if (i == 4) // still checking ... { return true; } else if (i == 3) // too many files { - D_QuitNetGame(); - CL_Reset(); - D_StartTitle(); + AbortConnection(); M_StartMessage(M_GetText( "You have too many WAD files loaded\n" "to add ones the server is using.\n" @@ -532,15 +753,15 @@ static boolean CL_FinishedFileList(void) } else if (i == 2) // cannot join for some reason { - D_QuitNetGame(); - CL_Reset(); - D_StartTitle(); + AbortConnection(); M_StartMessage(M_GetText( - "You have the wrong addons loaded.\n\n" + "You have the wrong addons loaded.\n" + "\n" "To play on this server, restart\n" "the game and don't load any addons.\n" "SRB2 will automatically add\n" - "everything you need when you join.\n\n" + "everything you need when you join.\n" + "\n" "Press ESC\n" ), NULL, MM_NOTHING); return false; @@ -566,63 +787,38 @@ static boolean CL_FinishedFileList(void) { // must download something // can we, though? - if (!CL_CheckDownloadable()) // nope! + // Rusty: always check downloadable + UINT8 status = CL_CheckDownloadable(UseDirectDownloader()); + if (status != DLSTATUS_OK) { - D_QuitNetGame(); - CL_Reset(); - D_StartTitle(); + HandleDirectDownloadFail(status); + AbortConnection(); M_StartMessage(M_GetText( "An error occurred when trying to\n" "download missing addons.\n" "(This is almost always a problem\n" - "with the server, not your game.)\n\n" + "with the server, not your game.)\n" + "\n" "See the console or log file\n" - "for additional details.\n\n" + "for additional details.\n" + "\n" "Press ESC\n" ), NULL, MM_NOTHING); return false; } - downloadcompletednum = 0; - downloadcompletedsize = 0; - totalfilesrequestednum = 0; - totalfilesrequestedsize = 0; - - if (fileneeded == NULL) - I_Error("CL_FinishedFileList: fileneeded == NULL"); - - for (i = 0; i < fileneedednum; i++) - if (fileneeded[i].status == FS_NOTFOUND || fileneeded[i].status == FS_MD5SUMBAD) - { - totalfilesrequestednum++; - totalfilesrequestedsize += fileneeded[i].totalsize; - } - - if (totalfilesrequestedsize>>20 >= 100) - downloadsize = Z_StrDup(va("%uM",totalfilesrequestedsize>>20)); - else - downloadsize = Z_StrDup(va("%uK",totalfilesrequestedsize>>10)); - - if (serverisfull) - M_StartMessage(va(M_GetText( - "This server is full!\n" - "Download of %s additional content\nis required to join.\n" - "\n" - "You may download, load server addons,\nand wait for a slot.\n" - "\n" - "Press ENTER to continue\nor ESC to cancel.\n" - ), downloadsize), M_ConfirmConnect, MM_EVENTHANDLER); + if (!filedownload.http_failed) + { + // show download consent modal ONCE! + ShowDownloadConsentMessage(); + } else - M_StartMessage(va(M_GetText( - "Download of %s additional content\nis required to join.\n" - "\n" - "Press ENTER to continue\nor ESC to cancel.\n" - ), downloadsize), M_ConfirmConnect, MM_EVENTHANDLER); - - Z_Free(downloadsize); - cl_mode = CL_CONFIRMCONNECT; - curfadevalue = 0; + { + // do a direct download + BeginDownload(true); + } } + return true; } @@ -734,15 +930,18 @@ static boolean CL_ServerConnectionSearchTicker(tic_t *asksent) if (reason) { char *message = Z_StrDup(reason); - D_QuitNetGame(); - CL_Reset(); - D_StartTitle(); + AbortConnection(); M_StartMessage(message, NULL, MM_NOTHING); Z_Free(message); return false; } } + if (serverlist[i].info.httpsource[0]) + strlcpy(filedownload.http_source, serverlist[i].info.httpsource, MAX_MIRROR_LENGTH); + else + filedownload.http_source[0] = '\0'; + D_ParseFileneeded(info->fileneedednum, info->fileneeded, 0); if (info->flags & SV_LOTSOFADDONS) @@ -773,6 +972,42 @@ static boolean CL_ServerConnectionSearchTicker(tic_t *asksent) return true; } +static void HandleHTTPDownloadFail(void) +{ + char filename[MAX_WADPATH]; + + CONS_Alert(CONS_WARNING, M_GetText("One or more addons failed to download:\n")); + + for (int i = 0; i < fileneedednum; i++) + { + if (fileneeded[i].failed == FDOWNLOAD_FAIL_NONE) + continue; + + strlcpy(filename, fileneeded[i].filename, sizeof filename); + nameonly(filename); + + CONS_Printf(" * \"%s\" (%s)", filename, GetPrintableFileSize(fileneeded[i].totalsize)); + + if (fileneeded[i].failed == FDOWNLOAD_FAIL_NOTFOUND) + CONS_Printf(M_GetText(" not found, md5: ")); + else if (fileneeded[i].failed == FDOWNLOAD_FAIL_MD5SUMBAD) + CONS_Printf(M_GetText(" wrong version, md5: ")); + else + CONS_Printf(M_GetText(" other error, md5: ")); + + char md5tmp[33]; + for (INT32 j = 0; j < 16; j++) + snprintf(&md5tmp[j*2], sizeof(md5tmp), "%02x", fileneeded[i].md5sum[j]); + CONS_Printf("%s\n", md5tmp); + + fileneeded[i].failed = FDOWNLOAD_FAIL_NONE; + } + + CONS_Printf(M_GetText("Falling back to direct downloader.\n")); + + cl_mode = CL_CHECKFILES; +} + /** Called by CL_ConnectToServer * * \param tmpsave The name of the gamestate file??? @@ -810,21 +1045,43 @@ static boolean CL_ServerConnectionTicker(const char *tmpsave, tic_t *oldtic, tic if (!CL_FinishedFileList()) return false; break; - case CL_DOWNLOADFILES: + + case CL_DOWNLOADHTTPFILES: waitmore = false; - for (INT32 i = 0; i < fileneedednum; i++) - if (fileneeded[i].status == FS_DOWNLOADING - || fileneeded[i].status == FS_REQUESTED) + for (int i = filedownload.current; i < fileneedednum; i++) + { + if (IsFileDownloadable(&fileneeded[i])) { + if (!filedownload.http_running) + { + if (!CURLPrepareFile(filedownload.http_source, i)) + HandleHTTPDownloadFail(); + } waitmore = true; break; } + } + + // Rusty TODO: multithread + if (filedownload.http_running) + CURLGetFile(); + if (waitmore) break; // exit the case - Snake_Free(&snake); - - cl_mode = CL_LOADFILES; + // Done downloading files + if (!filedownload.remaining) + { + if (filedownload.http_failed) + HandleHTTPDownloadFail(); + else + DoLoadFiles(); + } + break; + case CL_DOWNLOADFILES: + // Done downloading files + if (!filedownload.remaining) + DoLoadFiles(); break; case CL_LOADFILES: if (CL_LoadServerFiles()) @@ -839,10 +1096,7 @@ static boolean CL_ServerConnectionTicker(const char *tmpsave, tic_t *oldtic, tic if (firstconnectattempttime + NEWTICRATE*300 < I_GetTime() && !server) { CONS_Printf(M_GetText("5 minute wait time exceeded.\n")); - CONS_Printf(M_GetText("Network game synchronization aborted.\n")); - D_QuitNetGame(); - CL_Reset(); - D_StartTitle(); + AbortConnection(); M_StartMessage(M_GetText( "5 minute wait time exceeded.\n" "You may retry connection.\n" @@ -914,15 +1168,12 @@ static boolean CL_ServerConnectionTicker(const char *tmpsave, tic_t *oldtic, tic CONS_Printf(M_GetText("Network game synchronization aborted.\n")); M_StartMessage(M_GetText("Network game synchronization aborted.\n\nPress ESC\n"), NULL, MM_NOTHING); - Snake_Free(&snake); + AbortConnection(); - D_QuitNetGame(); - CL_Reset(); - D_StartTitle(); memset(gamekeydown, 0, NUMKEYS); return false; } - else if (cl_mode == CL_DOWNLOADFILES && snake) + else if ((cl_mode == CL_DOWNLOADFILES || cl_mode == CL_DOWNLOADHTTPFILES) && snake) Snake_Update(snake); if (client && (cl_mode == CL_DOWNLOADFILES || cl_mode == CL_DOWNLOADSAVEGAME)) @@ -973,7 +1224,7 @@ void CL_ConnectToServer(void) sprintf(tmpsave, "%s" PATHSEP TMPSAVENAME, srb2home); - lastfilenum = -1; + filedownload.current = -1; cl_mode = CL_SEARCHING; @@ -1121,14 +1372,9 @@ void PT_ServerRefuse(SINT8 node) M_StartMessage(va(M_GetText("Server refuses connection\n\nReason:\n%s"), reason), NULL, MM_NOTHING); - D_QuitNetGame(); - CL_Reset(); - D_StartTitle(); + AbortConnection(); free(reason); - - // Will be reset by caller. Signals refusal. - cl_mode = CL_ABORTED; } } diff --git a/src/netcode/client_connection.h b/src/netcode/client_connection.h index 4d75160d4a280061f08410b1f834df049a1b2a3e..ff054236b6ed8ca1b20a3f283c48522d46f8ac10 100644 --- a/src/netcode/client_connection.h +++ b/src/netcode/client_connection.h @@ -1,7 +1,7 @@ // SONIC ROBO BLAST 2 //----------------------------------------------------------------------------- // Copyright (C) 1998-2000 by DooM Legacy Team. -// Copyright (C) 1999-2023 by Sonic Team Junior. +// Copyright (C) 1999-2024 by Sonic Team Junior. // // This program is free software distributed under the // terms of the GNU General Public License, version 2. @@ -36,7 +36,8 @@ typedef enum CL_CONNECTED, CL_ABORTED, CL_ASKFULLFILELIST, - CL_CONFIRMCONNECT + CL_CONFIRMCONNECT, + CL_DOWNLOADHTTPFILES } cl_mode_t; extern serverelem_t serverlist[MAXSERVERLIST]; diff --git a/src/netcode/commands.c b/src/netcode/commands.c index e7d51437e42d381827a3e702f9c49a8f9d1780b2..46dfbc741d9c8f036bf53e0ba6d77bed60ac39f3 100644 --- a/src/netcode/commands.c +++ b/src/netcode/commands.c @@ -274,7 +274,9 @@ void Command_BanIP(void) if (server) // Only the server can use this, otherwise does nothing. { + char *addrbuf = NULL; const char *address = (COM_Argv(1)); + const char *mask = strchr(address, '/'); const char *reason; if (COM_Argc() == 2) @@ -282,8 +284,16 @@ void Command_BanIP(void) else reason = COM_Argv(2); + if (mask != NULL) + { + addrbuf = Z_Malloc(mask - address + 1, PU_STATIC, NULL); + memcpy(addrbuf, address, mask - address); + addrbuf[mask - address] = '\0'; + address = addrbuf; + mask++; + } - if (I_SetBanAddress && I_SetBanAddress(address, NULL)) + if (I_SetBanAddress && I_SetBanAddress(address, mask)) { if (reason) CONS_Printf("Banned IP address %s for: %s\n", address, reason); @@ -295,8 +305,9 @@ void Command_BanIP(void) } else { - return; + CONS_Printf("Unable to apply ban: address in malformed or invalid, or too many bans are applied\n"); } + Z_Free(addrbuf); } } diff --git a/src/netcode/d_clisrv.c b/src/netcode/d_clisrv.c index 0d5d3fa90b95075ac7657c8a9dc3f38458593970..d735e8132172e66a5c808ef572b7edca1ae8a8cc 100644 --- a/src/netcode/d_clisrv.c +++ b/src/netcode/d_clisrv.c @@ -1,7 +1,7 @@ // SONIC ROBO BLAST 2 //----------------------------------------------------------------------------- // Copyright (C) 1998-2000 by DooM Legacy Team. -// Copyright (C) 1999-2023 by Sonic Team Junior. +// Copyright (C) 1999-2024 by Sonic Team Junior. // // This program is free software distributed under the // terms of the GNU General Public License, version 2. @@ -116,6 +116,8 @@ consvar_t cv_playbackspeed = CVAR_INIT ("playbackspeed", "1", 0, playbackspeed_c consvar_t cv_idletime = CVAR_INIT ("idletime", "0", CV_SAVE, CV_Unsigned, NULL); consvar_t cv_dedicatedidletime = CVAR_INIT ("dedicatedidletime", "10", CV_SAVE, CV_Unsigned, NULL); +consvar_t cv_httpsource = CVAR_INIT ("http_source", "", CV_SAVE, NULL, NULL); + void ResetNode(INT32 node) { memset(&netnodes[node], 0, sizeof(*netnodes)); @@ -153,12 +155,15 @@ void CL_Reset(void) FreeFileNeeded(); fileneedednum = 0; - totalfilesrequestednum = 0; - totalfilesrequestedsize = 0; firstconnectattempttime = 0; serverisfull = false; connectiontimeout = (tic_t)cv_nettimeout.value; //reset this temporary hack + filedownload.remaining = 0; + filedownload.http_failed = false; + filedownload.http_running = false; + filedownload.http_source[0] = '\0'; + // D_StartTitle should get done now, but the calling function will handle it } @@ -892,6 +897,74 @@ static void PT_Login(SINT8 node, INT32 netconsole) #endif } +/** Add a login for HTTP downloads. If the + * user/password is missing, remove it. + * + * \sa Command_list_http_logins + */ +static void Command_set_http_login (void) +{ + HTTP_login *login; + HTTP_login **prev_next; + + if (COM_Argc() < 2) + { + CONS_Printf( + "set_http_login <URL> [user:password]: Set or remove a login to " + "authenticate HTTP downloads.\n" + ); + return; + } + + login = CURLGetLogin(COM_Argv(1), &prev_next); + + if (COM_Argc() == 2) + { + if (login) + { + (*prev_next) = login->next; + CONS_Printf("Login for '%s' removed.\n", login->url); + Z_Free(login); + } + } + else + { + if (login) + Z_Free(login->auth); + else + { + login = ZZ_Alloc(sizeof *login); + login->url = Z_StrDup(COM_Argv(1)); + } + + login->auth = Z_StrDup(COM_Argv(2)); + + login->next = curl_logins; + curl_logins = login; + } +} + +/** List logins for HTTP downloads. + * + * \sa Command_set_http_login + */ +static void Command_list_http_logins (void) +{ + HTTP_login *login; + + for ( + login = curl_logins; + login; + login = login->next + ){ + CONS_Printf( + "'%s' -> '%s'\n", + login->url, + login->auth + ); + } +} + static void PT_AskLuaFile(SINT8 node) { if (server && luafiletransfers && luafiletransfers->nodestatus[node] == LFTNS_ASKED) @@ -1568,6 +1641,8 @@ void D_ClientServerInit(void) COM_AddCommand("reloadbans", Command_ReloadBan, COM_LUA); COM_AddCommand("connect", Command_connect, COM_LUA); COM_AddCommand("nodes", Command_Nodes, COM_LUA); + COM_AddCommand("set_http_login", Command_set_http_login, 0); + COM_AddCommand("list_http_logins", Command_list_http_logins, 0); COM_AddCommand("resendgamestate", Command_ResendGamestate, COM_LUA); #ifdef PACKETDROP COM_AddCommand("drop", Command_Drop, COM_LUA); diff --git a/src/netcode/d_clisrv.h b/src/netcode/d_clisrv.h index 61823e65d29f3856a807877478490d9e91084c1a..5aac4693d2fdd166cee7a796166ba7c798e183c8 100644 --- a/src/netcode/d_clisrv.h +++ b/src/netcode/d_clisrv.h @@ -1,7 +1,7 @@ // SONIC ROBO BLAST 2 //----------------------------------------------------------------------------- // Copyright (C) 1998-2000 by DooM Legacy Team. -// Copyright (C) 1999-2023 by Sonic Team Junior. +// Copyright (C) 1999-2024 by Sonic Team Junior. // // This program is free software distributed under the // terms of the GNU General Public License, version 2. @@ -74,6 +74,7 @@ extern UINT32 playerpingtable[MAXPLAYERS]; extern tic_t servermaxping; extern consvar_t cv_netticbuffer, cv_resynchattempts, cv_blamecfail, cv_playbackspeed, cv_idletime, cv_dedicatedidletime; +extern consvar_t cv_httpsource; // Used in d_net, the only dependence void D_ClientServerInit(void); diff --git a/src/netcode/d_netcmd.c b/src/netcode/d_netcmd.c index 3cda178959cfc930bae14d1c6fc7ae0e86a70110..87f0110a9490d8a85e88c39d51d745ccede2af0d 100644 --- a/src/netcode/d_netcmd.c +++ b/src/netcode/d_netcmd.c @@ -1,7 +1,7 @@ // SONIC ROBO BLAST 2 //----------------------------------------------------------------------------- // Copyright (C) 1998-2000 by DooM Legacy Team. -// Copyright (C) 1999-2023 by Sonic Team Junior. +// Copyright (C) 1999-2024 by Sonic Team Junior. // // This program is free software distributed under the // terms of the GNU General Public License, version 2. @@ -606,6 +606,7 @@ void D_RegisterServerCommands(void) CV_RegisterVar(&cv_blamecfail); CV_RegisterVar(&cv_dedicatedidletime); CV_RegisterVar(&cv_idletime); + CV_RegisterVar(&cv_httpsource); COM_AddCommand("ping", Command_Ping_f, COM_LUA); CV_RegisterVar(&cv_nettimeout); diff --git a/src/netcode/d_netfil.c b/src/netcode/d_netfil.c index 7782939c3804d4ab044eedeb9aabe428fd81a55e..03ad8303e6571a477b9f107154d9ed3102ac58a9 100644 --- a/src/netcode/d_netfil.c +++ b/src/netcode/d_netfil.c @@ -1,7 +1,7 @@ // SONIC ROBO BLAST 2 //----------------------------------------------------------------------------- // Copyright (C) 1998-2000 by DooM Legacy Team. -// Copyright (C) 1999-2023 by Sonic Team Junior. +// Copyright (C) 1999-2024 by Sonic Team Junior. // // This program is free software distributed under the // terms of the GNU General Public License, version 2. @@ -10,6 +10,10 @@ /// \file d_netfil.c /// \brief Transfer a file using HSendPacket. +#ifdef HAVE_CURL +#include <curl/curl.h> +#endif + #include <stdio.h> #include <sys/stat.h> @@ -104,12 +108,19 @@ typedef struct } pauseddownload_t; static pauseddownload_t *pauseddownload = NULL; -// for cl loading screen -INT32 lastfilenum = -1; -INT32 downloadcompletednum = 0; -UINT32 downloadcompletedsize = 0; -INT32 totalfilesrequestednum = 0; -UINT32 totalfilesrequestedsize = 0; +file_download_t filedownload; + +static CURL *http_handle; +static CURLM *multi_handle; +static UINT32 curl_dlnow; +static UINT32 curl_dltotal; +static time_t curl_starttime; +static int curl_runninghandles = 0; +static UINT32 curl_origfilesize; +static UINT32 curl_origtotalfilesize; +static char *curl_realname = NULL; +static fileneeded_t *curl_curfile = NULL; +HTTP_login *curl_logins; luafiletransfer_t *luafiletransfers = NULL; boolean waitingforluafiletransfer = false; @@ -140,6 +151,13 @@ static UINT16 GetWadNumFromFileNeededId(UINT8 id) return UINT16_MAX; } +enum +{ + WILLSEND_YES, + WILLSEND_NO, + WILLSEND_TOOLARGE +}; + /** Fills a serverinfo packet with information about wad files loaded. * * \todo Give this function a better name since it is in global scope. @@ -186,9 +204,9 @@ UINT8 *PutFileNeeded(UINT16 firstfile) { // Store in the upper four bits if (!cv_downloading.value) - filestatus += (2 << 4); // Won't send + filestatus += (WILLSEND_NO << 4); // Won't send else if (wadfiles[i]->filesize <= (UINT32)cv_maxsend.value * 1024) - filestatus += (1 << 4); // Will send if requested + filestatus += (WILLSEND_YES << 4); // Will send if requested // else // filestatus += (0 << 4); -- Won't send, too big } @@ -250,6 +268,7 @@ void D_ParseFileneeded(INT32 fileneedednum_parm, UINT8 *fileneededstr, UINT16 fi 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 + fileneeded[i].failed = FDOWNLOAD_FAIL_NONE; 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 } @@ -257,7 +276,7 @@ void D_ParseFileneeded(INT32 fileneedednum_parm, UINT8 *fileneededstr, UINT16 fi void CL_PrepareDownloadSaveGame(const char *tmpsave) { - lastfilenum = -1; + filedownload.current = -1; FreeFileNeeded(); AllocFileNeeded(1); @@ -275,86 +294,37 @@ void CL_PrepareDownloadSaveGame(const char *tmpsave) /** Checks the server to see if we CAN download all the files, * before starting to create them and requesting. * + * \param direct Game will do a direct download * \return True if we can download all the files * */ -boolean CL_CheckDownloadable(void) +UINT8 CL_CheckDownloadable(boolean direct) { - UINT8 dlstatus = 0; + UINT8 dlstatus = DLSTATUS_OK; for (UINT8 i = 0; i < fileneedednum; i++) if (fileneeded[i].status != FS_FOUND && fileneeded[i].status != FS_OPEN) { if (fileneeded[i].folder) { - dlstatus = 4; + dlstatus = DLSTATUS_FOLDER; break; } - if (fileneeded[i].willsend == 1) + if (!direct || fileneeded[i].willsend == WILLSEND_YES) continue; - if (fileneeded[i].willsend == 0) - dlstatus = 1; - else //if (fileneeded[i].willsend == 2) - dlstatus = 2; + if (fileneeded[i].willsend == WILLSEND_TOOLARGE) + dlstatus = DLSTATUS_TOOLARGE; + else //if (fileneeded[i].willsend == WILLSEND_NO) + dlstatus = DLSTATUS_WONTSEND; } // Downloading locally disabled - if (!dlstatus && M_CheckParm("-nodownload")) - dlstatus = 3; - - if (!dlstatus) - return true; - - // not downloadable, put reason in console - CONS_Alert(CONS_NOTICE, M_GetText("You need additional addons to connect to this server:\n")); - - for (UINT8 i = 0; i < fileneedednum; i++) - { - if (fileneeded[i].folder || (fileneeded[i].status != FS_FOUND && fileneeded[i].status != FS_OPEN)) - { - CONS_Printf(" * \"%s\" ", fileneeded[i].filename); - - if (fileneeded[i].folder) - { - CONS_Printf("(folder)"); - } - else - { - CONS_Printf("(%dK)", fileneeded[i].totalsize >> 10); - - if (fileneeded[i].status == FS_NOTFOUND) - CONS_Printf(M_GetText(" not found, md5: ")); - else if (fileneeded[i].status == FS_MD5SUMBAD) - CONS_Printf(M_GetText(" wrong version, md5: ")); - - char md5tmp[33]; - for (INT32 j = 0; j < 16; j++) - sprintf(&md5tmp[j*2], "%02x", fileneeded[i].md5sum[j]); - CONS_Printf("%s", md5tmp); - } - - CONS_Printf("\n"); - } - } + if (direct && !dlstatus && M_CheckParm("-nodownload")) + dlstatus = DLSTATUS_NODOWNLOAD; - switch (dlstatus) - { - case 1: - CONS_Printf(M_GetText("Some addons are larger than the server is willing to send.\n")); - break; - case 2: - CONS_Printf(M_GetText("The server is not allowing download requests.\n")); - break; - case 3: - CONS_Printf(M_GetText("All addons downloadable, but you have chosen to disable addon downloading.\n")); - break; - case 4: - CONS_Printf(M_GetText("One or more addons were added as a folder, which the server cannot send.\n")); - break; - } - return false; + return dlstatus; } /** Returns true if a needed file transfer can be resumed @@ -396,23 +366,42 @@ boolean CL_SendFileRequest(void) #ifdef PARANOIA if (M_CheckParm("-nodownload")) - I_Error("Attempted to download files in -nodownload mode"); + { + CONS_Printf("Attempted to download files in -nodownload mode"); + return false; + } +#endif for (INT32 i = 0; i < fileneedednum; i++) + { if (fileneeded[i].status != FS_FOUND && fileneeded[i].status != FS_OPEN - && (fileneeded[i].willsend == 0 || fileneeded[i].willsend == 2)) + && (fileneeded[i].willsend == WILLSEND_TOOLARGE || fileneeded[i].willsend == WILLSEND_NO || fileneeded[i].folder)) { - I_Error("Attempted to download files that were not sendable"); + CONS_Printf("Attempted to download files that were not sendable"); + return false; } -#endif + + if (fileneeded[i].status == FS_NOTFOUND || fileneeded[i].status == FS_MD5SUMBAD) + { + // Error check for the first time around. + totalfreespaceneeded += fileneeded[i].totalsize; + } + } + + I_GetDiskFreeSpace(&availablefreespace); + if (totalfreespaceneeded > availablefreespace) + { + CONS_Printf("To play on this server you must download %s KB,\n" + "but you have only %s KB free space on your device\n", + sizeu1((size_t)(totalfreespaceneeded>>10)), sizeu2((size_t)(availablefreespace>>10))); + return false; + } netbuffer->packettype = PT_REQUESTFILE; p = (char *)netbuffer->u.textcmd; for (INT32 i = 0; i < fileneedednum; i++) if (fileneeded[i].status == FS_NOTFOUND || fileneeded[i].status == FS_MD5SUMBAD) { - totalfreespaceneeded += fileneeded[i].totalsize; - WRITEUINT8(p, i); // fileid // put it in download dir @@ -424,15 +413,16 @@ boolean CL_SendFileRequest(void) WRITEUINT8(p, 0xFF); - I_GetDiskFreeSpace(&availablefreespace); - if (totalfreespaceneeded > availablefreespace) - I_Error("To play on this server you must download %s KB,\n" - "but you have only %s KB free space on this drive\n", - sizeu1((size_t)(totalfreespaceneeded>>10)), sizeu2((size_t)(availablefreespace>>10))); + if (!HSendPacket(servernode, true, 0, p - (char *)netbuffer->u.textcmd)) + { + CONS_Printf("Could not send download request packet to server\n"); + return false; + } // prepare to download I_mkdir(downloaddir, 0755); - return HSendPacket(servernode, true, 0, p - (char *)netbuffer->u.textcmd); + + return true; } // get request filepak and put it on the send queue @@ -514,7 +504,7 @@ INT32 CL_CheckFiles(void) for (i = 0; i < fileneedednum; i++) { - if (fileneeded[i].status == FS_NOTFOUND || fileneeded[i].status == FS_MD5SUMBAD) + if (fileneeded[i].status == FS_NOTFOUND || fileneeded[i].status == FS_MD5SUMBAD || fileneeded[i].status == FS_FALLBACK) downloadrequired = true; if (fileneeded[i].status != FS_OPEN) @@ -932,8 +922,6 @@ 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 - //INT32 i; - //char wadfilename[MAX_WADPATH]; luafiletransfers->nodestatus[node] = LFTNS_SENDING; @@ -1306,6 +1294,21 @@ void FileReceiveTicker(void) } } +static void OpenNewFileForDownload(fileneeded_t *file, const char *filename) +{ + file->file = fopen(filename, "wb"); + if (!file->file) + I_Error("Can't create file %s: %s", filename, strerror(errno)); + + file->currentsize = 0; + file->totalsize = LONG(netbuffer->u.filetxpak.filesize); + file->ackresendposition = UINT32_MAX; // Only used for resumed downloads + + file->receivedfragments = calloc(file->totalsize / file->fragmentsize + 1, sizeof(*file->receivedfragments)); + if (!file->receivedfragments) + I_Error("FileSendTicker: No more memory\n"); +} + void PT_FileFragment(SINT8 node, INT32 netconsole) { if (netnodes[node].ingame) @@ -1348,8 +1351,6 @@ void PT_FileFragment(SINT8 node, INT32 netconsole) )) I_Error("Tried to download \"%s\"", filename); - filename = file->filename; - if (filenum >= fileneedednum) { DEBFILE(va("fileframent not needed %d>%d\n", filenum, fileneedednum)); @@ -1372,15 +1373,24 @@ void PT_FileFragment(SINT8 node, INT32 netconsole) if (CL_CanResumeDownload(file)) { - file->file = fopen(filename, "r+b"); + file->file = fopen(file->filename, "r+b"); if (!file->file) - I_Error("Can't reopen file %s: %s", filename, strerror(errno)); - CONS_Printf("\r%s...\n", filename); + { + CONS_Alert(CONS_ERROR, "Couldn't reopen file %s: %s\n", file->filename, strerror(errno)); + + free(pauseddownload->receivedfragments); - CONS_Printf("Resuming download...\n"); - file->currentsize = pauseddownload->currentsize; - file->receivedfragments = pauseddownload->receivedfragments; - file->ackresendposition = 0; + CONS_Printf("Restarting download of addon \"%s\"...\n", filename); + + OpenNewFileForDownload(file, file->filename); + } + else + { + CONS_Printf("Resuming download of addon \"%s\"...\n", filename); + file->currentsize = pauseddownload->currentsize; + file->receivedfragments = pauseddownload->receivedfragments; + file->ackresendposition = 0; + } free(pauseddownload); pauseddownload = NULL; @@ -1388,20 +1398,8 @@ void PT_FileFragment(SINT8 node, INT32 netconsole) 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"); + OpenNewFileForDownload(file, file->filename); + CONS_Printf("Downloading addon \"%s\" from the server...\n", filename); } lasttimeackpacketsent = I_GetTime(); @@ -1421,7 +1419,7 @@ void PT_FileFragment(SINT8 node, INT32 netconsole) // 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)); + I_Error("Can't write to %s: %s\n",file->filename, M_FileError(file->file)); file->currentsize += boundedfragmentsize; AddFragmentToAckPacket(file->ackpacket, file->iteration, fragmentpos / fragmentsize, filenum); @@ -1435,8 +1433,6 @@ void PT_FileFragment(SINT8 node, INT32 netconsole) 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_FILERECEIVED; @@ -1450,6 +1446,14 @@ void PT_FileFragment(SINT8 node, INT32 netconsole) HSendPacket(servernode, true, 0, 0); FreeFileNeeded(); } + else + { + filedownload.completednum++; + filedownload.completedsize += file->totalsize; + filedownload.remaining--; + } + + CONS_Printf(M_GetText("Finished download of \"%s\"\n"), filename); } } else // Already received @@ -1483,7 +1487,7 @@ void PT_FileFragment(SINT8 node, INT32 netconsole) I_Error("Received a file not requested (file id: %d, file status: %s)\n", filenum, s); } - lastfilenum = filenum; + filedownload.current = filenum; } /** \brief Checks if a node is downloading a file @@ -1581,6 +1585,224 @@ void Command_Downloads_f(void) } } +static size_t curlwrite_data(void *ptr, size_t size, size_t nmemb, FILE *stream) +{ + return fwrite(ptr, size, nmemb, stream); +} + +static int curlprogress_callback(void *clientp, curl_off_t dltotal, curl_off_t dlnow, curl_off_t ultotal, curl_off_t ulnow) +{ + (void)clientp; + (void)ultotal; + (void)ulnow; // Function prototype requires these but we won't use, so just discard + curl_dlnow = (UINT32)dlnow; + curl_dltotal = (UINT32)dltotal; + getbytes = ((double)dlnow) / (time(NULL) - curl_starttime); // To-do: Make this more accurate??? + return 0; +} + +boolean CURLPrepareFile(const char* url, int dfilenum) +{ + HTTP_login *login; + +#ifdef PARANOIA + if (M_CheckParm("-nodownload")) + I_Error("Attempted to download files in -nodownload mode"); +#endif + + curl_global_init(CURL_GLOBAL_ALL); + + http_handle = curl_easy_init(); + multi_handle = curl_multi_init(); + + if (http_handle && multi_handle) + { + I_mkdir(downloaddir, 0755); + + curl_curfile = &fileneeded[dfilenum]; + curl_realname = curl_curfile->filename; + nameonly(curl_realname); + + curl_origfilesize = curl_curfile->currentsize; + curl_origtotalfilesize = curl_curfile->totalsize; + + char md5tmp[33]; + for (INT32 j = 0; j < 16; j++) + sprintf(&md5tmp[j*2], "%02x", curl_curfile->md5sum[j]); + + curl_easy_setopt(http_handle, CURLOPT_URL, va("%s/%s?md5=%s", url, curl_realname, md5tmp)); + + // Only allow HTTP and HTTPS +#if (LIBCURL_VERSION_MAJOR <= 7) && (LIBCURL_VERSION_MINOR < 85) + curl_easy_setopt(http_handle, CURLOPT_PROTOCOLS, CURLPROTO_HTTP|CURLPROTO_HTTPS); +#else + curl_easy_setopt(http_handle, CURLOPT_PROTOCOLS_STR, "http,https"); +#endif + + // Set user agent, as some servers won't accept invalid user agents. + curl_easy_setopt(http_handle, CURLOPT_USERAGENT, va("Sonic Robo Blast 2/v%d.%d", VERSION, SUBVERSION)); + + // Authenticate if the user so wishes + login = CURLGetLogin(url, NULL); + + if (login) + { + curl_easy_setopt(http_handle, CURLOPT_USERPWD, login->auth); + } + + // Follow a redirect request, if sent by the server. + curl_easy_setopt(http_handle, CURLOPT_FOLLOWLOCATION, 1L); + + curl_easy_setopt(http_handle, CURLOPT_FAILONERROR, 1L); + + CONS_Printf("Downloading addon \"%s\" from %s\n", curl_realname, url); + + strcatbf(curl_curfile->filename, downloaddir, "/"); + curl_curfile->file = fopen(curl_curfile->filename, "wb"); + curl_easy_setopt(http_handle, CURLOPT_WRITEDATA, curl_curfile->file); + curl_easy_setopt(http_handle, CURLOPT_WRITEFUNCTION, curlwrite_data); + curl_easy_setopt(http_handle, CURLOPT_NOPROGRESS, 0L); + curl_easy_setopt(http_handle, CURLOPT_XFERINFOFUNCTION, curlprogress_callback); + + curl_curfile->status = FS_DOWNLOADING; + curl_multi_add_handle(multi_handle, http_handle); + + curl_multi_perform(multi_handle, &curl_runninghandles); + curl_starttime = time(NULL); + + filedownload.current = dfilenum; + filedownload.http_running = true; + + return true; + } + + filedownload.http_running = false; + + return false; +} + +void CURLGetFile(void) +{ + CURLMcode mc; /* return code used by curl_multi_wait() */ + CURLcode easyres; /* Return from easy interface */ + int numfds; + CURLMsg *m; /* for picking up messages with the transfer status */ + CURL *e; + int msgs_left; /* how many messages are left */ + const char *easy_handle_error; + + if (curl_runninghandles) + { + curl_multi_perform(multi_handle, &curl_runninghandles); + + /* wait for activity, timeout or "nothing" */ + mc = curl_multi_wait(multi_handle, NULL, 0, 1000, &numfds); + + if (mc != CURLM_OK) + { + CONS_Alert(CONS_WARNING, "curl_multi_wait() failed, code %d.\n", mc); + return; + } + curl_curfile->currentsize = curl_dlnow; + curl_curfile->totalsize = curl_dltotal; + } + + /* See how the transfers went */ + while ((m = curl_multi_info_read(multi_handle, &msgs_left))) + { + if (m && (m->msg == CURLMSG_DONE)) + { + e = m->easy_handle; + easyres = m->data.result; + + char *filename = Z_StrDup(curl_realname); + nameonly(filename); + + if (easyres != CURLE_OK) + { + long response_code = 0; + + if (easyres == CURLE_HTTP_RETURNED_ERROR) + curl_easy_getinfo(e, CURLINFO_RESPONSE_CODE, &response_code); + + if (response_code == 404) + curl_curfile->failed = FDOWNLOAD_FAIL_NOTFOUND; + else + curl_curfile->failed = FDOWNLOAD_FAIL_OTHER; + + easy_handle_error = (response_code) ? va("HTTP response code %ld", response_code) : curl_easy_strerror(easyres); + curl_curfile->status = FS_FALLBACK; + curl_curfile->currentsize = curl_origfilesize; + curl_curfile->totalsize = curl_origtotalfilesize; + filedownload.http_failed = true; + fclose(curl_curfile->file); + remove(curl_curfile->filename); + CONS_Alert(CONS_ERROR, M_GetText("Failed to download addon \"%s\" (%s)\n"), filename, easy_handle_error); + } + else + { + fclose(curl_curfile->file); + + CONS_Printf(M_GetText("Finished download of \"%s\"\n"), filename); + + if (checkfilemd5(curl_curfile->filename, curl_curfile->md5sum) == FS_MD5SUMBAD) + { + CONS_Alert(CONS_WARNING, M_GetText("File \"%s\" does not match the version used by the server\n"), filename); + curl_curfile->status = FS_FALLBACK; + curl_curfile->failed = FDOWNLOAD_FAIL_MD5SUMBAD; + filedownload.http_failed = true; + } + else + { + filedownload.completednum++; + filedownload.completedsize += curl_curfile->totalsize; + curl_curfile->status = FS_FOUND; + } + } + + Z_Free(filename); + + curl_curfile->file = NULL; + filedownload.http_running = false; + filedownload.remaining--; + curl_multi_remove_handle(multi_handle, e); + curl_easy_cleanup(e); + + if (!filedownload.remaining) + break; + } + } + + if (!filedownload.remaining) + { + curl_multi_cleanup(multi_handle); + curl_global_cleanup(); + } +} + +HTTP_login * +CURLGetLogin (const char *url, HTTP_login ***return_prev_next) +{ + HTTP_login * login; + HTTP_login ** prev_next; + + for ( + prev_next = &curl_logins; + ( login = (*prev_next)); + prev_next = &login->next + ){ + if (strcmp(login->url, url) == 0) + { + if (return_prev_next) + (*return_prev_next) = prev_next; + + return login; + } + } + + return NULL; +} + // Functions cut and pasted from Doomatic :) void nameonly(char *s) diff --git a/src/netcode/d_netfil.h b/src/netcode/d_netfil.h index fdbec8c5396fa1619efccfa6f200fbd7a17903c6..4039b5e2d5cc4b0d81fbdc04e6d7c7bef46f724a 100644 --- a/src/netcode/d_netfil.h +++ b/src/netcode/d_netfil.h @@ -1,7 +1,7 @@ // SONIC ROBO BLAST 2 //----------------------------------------------------------------------------- // Copyright (C) 1998-2000 by DooM Legacy Team. -// Copyright (C) 1999-2023 by Sonic Team Junior. +// Copyright (C) 1999-2024 by Sonic Team Junior. // // This program is free software distributed under the // terms of the GNU General Public License, version 2. @@ -25,6 +25,23 @@ typedef enum SF_NOFREERAM } freemethod_t; +typedef enum +{ + DLSTATUS_OK, + DLSTATUS_TOOLARGE, + DLSTATUS_WONTSEND, + DLSTATUS_NODOWNLOAD, + DLSTATUS_FOLDER +} dlstatus_t; + +typedef enum +{ + FDOWNLOAD_FAIL_NONE, + FDOWNLOAD_FAIL_NOTFOUND, + FDOWNLOAD_FAIL_MD5SUMBAD, + FDOWNLOAD_FAIL_OTHER +} filedownloadfail_t; + typedef enum { FS_NOTCHECKED, @@ -33,7 +50,8 @@ typedef enum FS_REQUESTED, FS_DOWNLOADING, FS_OPEN, // Is opened and used in w_wad - FS_MD5SUMBAD + FS_MD5SUMBAD, + FS_FALLBACK } filestatus_t; typedef enum @@ -51,6 +69,7 @@ typedef struct UINT8 willsend; // Is the server willing to send it? UINT8 folder; // File is a folder fileneededtype_t type; + filedownloadfail_t failed; boolean justdownloaded; // To prevent late fragments from causing an I_Error // Used only for download @@ -70,11 +89,30 @@ extern INT32 fileneedednum; extern fileneeded_t *fileneeded; extern char downloaddir[512]; -extern INT32 lastfilenum; -extern INT32 downloadcompletednum; -extern UINT32 downloadcompletedsize; -extern INT32 totalfilesrequestednum; -extern UINT32 totalfilesrequestedsize; +typedef struct +{ + INT32 current; + INT32 remaining; + INT32 completednum; + UINT32 completedsize; + + boolean http_failed; + boolean http_running; + + char http_source[MAX_MIRROR_LENGTH]; +} file_download_t; + +extern file_download_t filedownload; + +typedef struct HTTP_login HTTP_login; + +extern struct HTTP_login +{ + char * url; + char * auth; + HTTP_login * next; +} +*curl_logins; extern consvar_t cv_maxsend, cv_noticedownload, cv_downloadspeed; @@ -97,10 +135,14 @@ boolean SendingFile(INT32 node); void FileReceiveTicker(void); void PT_FileFragment(SINT8 node, INT32 netconsole); -boolean CL_CheckDownloadable(void); +UINT8 CL_CheckDownloadable(boolean direct); boolean CL_SendFileRequest(void); void PT_RequestFile(SINT8 node); +boolean CURLPrepareFile(const char* url, int dfilenum); +void CURLGetFile(void); +HTTP_login * CURLGetLogin (const char *url, HTTP_login ***return_prev_next); + typedef enum { LFTNS_NONE, // This node is not connected diff --git a/src/netcode/http-mserv.c b/src/netcode/http-mserv.c index 10137e67b042cef0cd192d00d7d4fcbb62d5b842..2b52380cf506f00f119dc01b5f0ab3b47ff4e6f6 100644 --- a/src/netcode/http-mserv.c +++ b/src/netcode/http-mserv.c @@ -493,7 +493,8 @@ HMS_unlist (void) if (! hms) return 0; - curl_easy_setopt(hms->curl, CURLOPT_CUSTOMREQUEST, "POST"); + curl_easy_setopt(hms->curl, CURLOPT_POST, 1); + curl_easy_setopt(hms->curl, CURLOPT_POSTFIELDSIZE, 0); ok = HMS_do(hms); HMS_end(hms); @@ -508,7 +509,8 @@ HMS_unlist (void) if (! hms) return 0; - curl_easy_setopt(hms->curl, CURLOPT_CUSTOMREQUEST, "POST"); + curl_easy_setopt(hms->curl, CURLOPT_POST, 1); + curl_easy_setopt(hms->curl, CURLOPT_POSTFIELDSIZE, 0); ok = HMS_do(hms); HMS_end(hms); diff --git a/src/netcode/protocol.h b/src/netcode/protocol.h index c084d920cfa4dc14d3e0cf475b3c62ea7226020d..4b39fab664995c4e171b95a38b776a8b4df1ea4f 100644 --- a/src/netcode/protocol.h +++ b/src/netcode/protocol.h @@ -1,7 +1,7 @@ // SONIC ROBO BLAST 2 //----------------------------------------------------------------------------- // Copyright (C) 1998-2000 by DooM Legacy Team. -// Copyright (C) 1999-2023 by Sonic Team Junior. +// Copyright (C) 1999-2024 by Sonic Team Junior. // // This program is free software distributed under the // terms of the GNU General Public License, version 2. @@ -26,7 +26,7 @@ packet versions. If you change the struct or the meaning of a field therein, increment this number. */ -#define PACKETVERSION 4 +#define PACKETVERSION 5 // Network play related stuff. // There is a data struct that stores network @@ -200,6 +200,7 @@ enum { #define MAXSERVERNAME 32 #define MAXFILENEEDED 915 +#define MAX_MIRROR_LENGTH 256 // This packet is too large typedef struct @@ -230,6 +231,7 @@ typedef struct unsigned char mapmd5[16]; UINT8 actnum; UINT8 iszone; + char httpsource[MAX_MIRROR_LENGTH]; UINT8 fileneeded[MAXFILENEEDED]; // is filled with writexxx (byteptr.h) } ATTRPACK serverinfo_pak; diff --git a/src/netcode/server_connection.c b/src/netcode/server_connection.c index 376700f0db1a300fee1abf21b48462dab1103df5..bbabc8f1dc9a5d82030159a281c2ffae137d1054 100644 --- a/src/netcode/server_connection.c +++ b/src/netcode/server_connection.c @@ -1,7 +1,7 @@ // SONIC ROBO BLAST 2 //----------------------------------------------------------------------------- // Copyright (C) 1998-2000 by DooM Legacy Team. -// Copyright (C) 1999-2023 by Sonic Team Junior. +// Copyright (C) 1999-2024 by Sonic Team Junior. // // This program is free software distributed under the // terms of the GNU General Public License, version 2. @@ -127,6 +127,8 @@ static void SV_SendServerInfo(INT32 node, tic_t servertime) memset(netbuffer->u.serverinfo.maptitle, 0, sizeof netbuffer->u.serverinfo.maptitle); + memset(netbuffer->u.serverinfo.httpsource, 0, MAX_MIRROR_LENGTH); + if (mapheaderinfo[gamemap-1] && *mapheaderinfo[gamemap-1]->lvlttl) { char *read = mapheaderinfo[gamemap-1]->lvlttl, *writ = netbuffer->u.serverinfo.maptitle; @@ -153,6 +155,17 @@ static void SV_SendServerInfo(INT32 node, tic_t servertime) if (mapheaderinfo[gamemap-1]) netbuffer->u.serverinfo.actnum = mapheaderinfo[gamemap-1]->actnum; + const char *httpurl = cv_httpsource.string; + size_t mirror_length = strlen(httpurl); + if (mirror_length > MAX_MIRROR_LENGTH) + mirror_length = MAX_MIRROR_LENGTH; + + if (snprintf(netbuffer->u.serverinfo.httpsource, mirror_length+1, "%s", httpurl) < 0) + // If there's an encoding error, send nothing, we accept that the above may be truncated + strncpy(netbuffer->u.serverinfo.httpsource, "", mirror_length); + + netbuffer->u.serverinfo.httpsource[MAX_MIRROR_LENGTH-1] = '\0'; + p = PutFileNeeded(0); HSendPacket(node, false, 0, p - ((UINT8 *)&netbuffer->u)); diff --git a/src/p_enemy.c b/src/p_enemy.c index 4d268117f7f72c4d2de3582e9ae5f15a8c0fa24e..5314de8abfa7807fee22d2f2e08dde8d50ac33ec 100644 --- a/src/p_enemy.c +++ b/src/p_enemy.c @@ -5821,15 +5821,12 @@ void A_MinusDigging(mobj_t *actor) fixed_t yl = (unsigned)(actor->y - radius - bmaporgy) >> MAPBLOCKSHIFT; fixed_t xh = (unsigned)(actor->x + radius - bmaporgx) >> MAPBLOCKSHIFT; fixed_t xl = (unsigned)(actor->x - radius - bmaporgx) >> MAPBLOCKSHIFT; - fixed_t bx, by; BMBOUNDFIX(xl, xh, yl, yh); minus = actor; - for (bx = xl; bx <= xh; bx++) - for (by = yl; by <= yh; by++) - P_BlockThingsIterator(bx, by, PIT_MinusCarry); + P_DoBlockThingsIterate(xl, yl, xh, yh, PIT_MinusCarry); } else { @@ -13889,7 +13886,7 @@ void A_DustDevilThink(mobj_t *actor) { fixed_t scale = actor->scale; mobj_t *layer = actor->tracer; - INT32 bx, by, xl, xh, yl, yh; + INT32 xl, xh, yl, yh; fixed_t radius = actor->radius; if (LUA_CallAction(A_DUSTDEVILTHINK, actor)) @@ -13953,9 +13950,7 @@ void A_DustDevilThink(mobj_t *actor) dustdevil = actor; - for (bx = xl; bx <= xh; bx++) - for (by = yl; by <= yh; by++) - P_BlockThingsIterator(bx, by, PIT_DustDevilLaunch); + P_DoBlockThingsIterate(xl, yl, xh, yh, PIT_DustDevilLaunch); //Whirlwind sound effect. if (leveltime % 70 == 0) @@ -14035,7 +14030,6 @@ static boolean PIT_TNTExplode(mobj_t *nearby) void A_TNTExplode(mobj_t *actor) { INT32 locvar1 = var1; - INT32 x, y; INT32 xl, xh, yl, yh; static mappoint_t epicenter = {0,0,0}; @@ -14072,9 +14066,7 @@ void A_TNTExplode(mobj_t *actor) barrel = actor; - for (x = xl; x <= xh; x++) - for (y = yl; y <= yh; y++) - P_BlockThingsIterator(x, y, PIT_TNTExplode); + P_DoBlockThingsIterate(xl, yl, xh, yh, PIT_TNTExplode); // cause a quake -- P_StartQuake does not exist yet epicenter.x = actor->x; diff --git a/src/p_local.h b/src/p_local.h index 2be99ff4a50d43b59c76cea60e08b785dd46dee9..9bc8b4a6597b310b6a762ee545fa3878164a4709 100644 --- a/src/p_local.h +++ b/src/p_local.h @@ -464,7 +464,7 @@ extern INT32 bmapwidth; extern INT32 bmapheight; // in mapblocks extern fixed_t bmaporgx; extern fixed_t bmaporgy; // origin of block map -extern mobj_t **blocklinks; // for thing chains +extern blocknode_t **blocklinks; // for thing chains // // P_INTER diff --git a/src/p_map.c b/src/p_map.c index bfb3ccf10c6d54ab30de9a12ddab549057c3b2e7..3ff7d9d90f5ead16ce4fa14ca1b28994d376f6b2 100644 --- a/src/p_map.c +++ b/src/p_map.c @@ -752,20 +752,25 @@ static void P_PlayerBarrelCollide(mobj_t *toucher, mobj_t *barrel) P_DamageMobj(barrel, toucher, toucher, 1, 0); } -// -// PIT_CheckThing -// -static boolean PIT_CheckThing(mobj_t *thing) +enum +{ + CHECKTHING_NOCOLLIDE, + CHECKTHING_COLLIDE, + CHECKTHING_DONE, + CHECKTHING_IGNORE +}; + +static unsigned PIT_DoCheckThing(mobj_t *thing) { fixed_t blockdist; // don't clip against self if (thing == tmthing) - return true; + return CHECKTHING_IGNORE; // Ignore... things. if (!tmthing || !thing || P_MobjWasRemoved(thing)) - return true; + return CHECKTHING_IGNORE; I_Assert(!P_MobjWasRemoved(tmthing)); I_Assert(!P_MobjWasRemoved(thing)); @@ -773,7 +778,7 @@ static boolean PIT_CheckThing(mobj_t *thing) // Ignore spectators if ((tmthing->player && tmthing->player->spectator) || (thing->player && thing->player->spectator)) - return true; + return CHECKTHING_IGNORE; // Do name checks all the way up here // So that NOTHING ELSE can see MT_NAMECHECK because it is client-side. @@ -781,24 +786,24 @@ static boolean PIT_CheckThing(mobj_t *thing) { // Ignore things that aren't players, ignore spectators, ignore yourself. if (!thing->player || !(tmthing->target && tmthing->target->player) || thing->player->spectator || (tmthing->target && thing->player == tmthing->target->player)) - return true; + return CHECKTHING_IGNORE; // Now check that you actually hit them. blockdist = thing->radius + tmthing->radius; if (abs(thing->x - tmx) >= blockdist || abs(thing->y - tmy) >= blockdist) - return true; // didn't hit it + return CHECKTHING_NOCOLLIDE; // didn't hit it // see if it went over / under if (tmthing->z > thing->z + thing->height) - return true; // overhead + return CHECKTHING_NOCOLLIDE; // overhead if (tmthing->z + tmthing->height < thing->z) - return true; // underneath + return CHECKTHING_NOCOLLIDE; // underneath - // REX HAS SEEN YOU + // Call any SeenPlayer Lua hooks if (!LUA_HookSeenPlayer(tmthing->target->player, thing->player)) - return false; + return CHECKTHING_DONE; seenplayer = thing->player; - return false; + return CHECKTHING_DONE; } // Metal Sonic destroys tiny baby objects. @@ -808,15 +813,15 @@ static boolean PIT_CheckThing(mobj_t *thing) || thing->type == MT_WALLSPIKE))) { if ((thing->flags & (MF_ENEMY|MF_BOSS)) && (thing->health <= 0 || !(thing->flags & MF_SHOOTABLE))) - return true; + return CHECKTHING_IGNORE; blockdist = thing->radius + tmthing->radius; if (abs(thing->x - tmx) >= blockdist || abs(thing->y - tmy) >= blockdist) - return true; // didn't hit it + return CHECKTHING_NOCOLLIDE; // didn't hit it // see if it went over / under if (tmthing->z > thing->z + thing->height) - return true; // overhead + return CHECKTHING_NOCOLLIDE; // overhead if (tmthing->z + tmthing->height < thing->z) - return true; // underneath + return CHECKTHING_NOCOLLIDE; // underneath if (thing->type == MT_SPIKE || thing->type == MT_WALLSPIKE) { @@ -832,28 +837,28 @@ static boolean PIT_CheckThing(mobj_t *thing) thing->health = 0; P_KillMobj(thing, tmthing, tmthing, 0); } - return true; + return CHECKTHING_COLLIDE; } // STR_SPIKE users destroy spikes if ((tmthing->player) && ((tmthing->player->powers[pw_strong] & STR_SPIKE) && (thing->type == MT_SPIKE || thing->type == MT_WALLSPIKE))) { mobj_t *iter; - blockdist = thing->radius + tmthing->radius; - if (abs(thing->x - tmx) >= blockdist || abs(thing->y - tmy) >= blockdist) - return true; // didn't hit it - // see if it went over / under - if (tmthing->z > thing->z + thing->height) - return true; // overhead - if (tmthing->z + tmthing->height < thing->z) - return true; // underneath + blockdist = thing->radius + tmthing->radius; + if (abs(thing->x - tmx) >= blockdist || abs(thing->y - tmy) >= blockdist) + return CHECKTHING_NOCOLLIDE; // didn't hit it + // see if it went over / under + if (tmthing->z > thing->z + thing->height) + return CHECKTHING_NOCOLLIDE; // overhead + if (tmthing->z + tmthing->height < thing->z) + return CHECKTHING_NOCOLLIDE; // underneath if (thing->flags & MF_SOLID) S_StartSound(tmthing, thing->info->deathsound); for (iter = thing->subsector->sector->thinglist; iter; iter = iter->snext) if (iter->type == thing->type && iter->health > 0 && iter->flags & MF_SOLID && (iter == thing || P_AproxDistance(P_AproxDistance(thing->x - iter->x, thing->y - iter->y), thing->z - iter->z) < 56*thing->scale))//FixedMul(56*FRACUNIT, thing->scale)) P_KillMobj(iter, tmthing, tmthing, 0); - return true; + return CHECKTHING_COLLIDE; } // vectorise metal - done in a special case as at this point neither has the right flags for touching @@ -865,30 +870,30 @@ static boolean PIT_CheckThing(mobj_t *thing) blockdist = thing->radius + tmthing->radius; if (abs(thing->x - tmx) >= blockdist || abs(thing->y - tmy) >= blockdist) - return true; // didn't hit it + return CHECKTHING_NOCOLLIDE; // didn't hit it if (tmthing->z > thing->z + thing->height) - return true; // overhead + return CHECKTHING_NOCOLLIDE; // overhead if (tmthing->z + tmthing->height < thing->z) - return true; // underneath + return CHECKTHING_NOCOLLIDE; // underneath thing->flags2 |= MF2_CLASSICPUSH; - return true; + return CHECKTHING_COLLIDE; } if ((thing->flags & MF_NOCLIPTHING) || !(thing->flags & (MF_SOLID|MF_SPECIAL|MF_PAIN|MF_SHOOTABLE|MF_SPRING))) - return true; + return CHECKTHING_IGNORE; // Don't collide with your buddies while NiGHTS-flying. if (tmthing->player && thing->player && (maptol & TOL_NIGHTS) && ((tmthing->player->powers[pw_carry] == CR_NIGHTSMODE) || (thing->player->powers[pw_carry] == CR_NIGHTSMODE))) - return true; + return CHECKTHING_IGNORE; blockdist = thing->radius + tmthing->radius; if (abs(thing->x - tmx) >= blockdist || abs(thing->y - tmy) >= blockdist) - return true; // didn't hit it + return CHECKTHING_NOCOLLIDE; // didn't hit it if (thing->flags & MF_PAPERCOLLISION) // CAUTION! Very easy to get stuck inside MF_SOLID objects. Giving the player MF_PAPERCOLLISION is a bad idea unless you know what you're doing. { @@ -915,23 +920,23 @@ static boolean PIT_CheckThing(mobj_t *thing) fixed_t tmcosradius = FixedMul(tmthing->radius, FINECOSINE(tmthing->angle>>ANGLETOFINESHIFT)); fixed_t tmsinradius = FixedMul(tmthing->radius, FINESINE(tmthing->angle>>ANGLETOFINESHIFT)); if (abs(thing->x - tmx) >= (abs(tmcosradius) + abs(cosradius)) || abs(thing->y - tmy) >= (abs(tmsinradius) + abs(sinradius))) - return true; // didn't hit it + return CHECKTHING_NOCOLLIDE; // didn't hit it check1 = P_PointOnLineSide(tmx - tmcosradius, tmy - tmsinradius, &junk); check2 = P_PointOnLineSide(tmx + tmcosradius, tmy + tmsinradius, &junk); check3 = P_PointOnLineSide(tmx + tmthing->momx - tmcosradius, tmy + tmthing->momy - tmsinradius, &junk); check4 = P_PointOnLineSide(tmx + tmthing->momx + tmcosradius, tmy + tmthing->momy + tmsinradius, &junk); if ((check1 == check2) && (check2 == check3) && (check3 == check4)) - return true; // the line doesn't cross between collider's start or end + return CHECKTHING_NOCOLLIDE; // the line doesn't cross between collider's start or end } else { if (abs(thing->x - tmx) >= (tmthing->radius + abs(cosradius)) || abs(thing->y - tmy) >= (tmthing->radius + abs(sinradius))) - return true; // didn't hit it + return CHECKTHING_NOCOLLIDE; // didn't hit it if ((P_PointOnLineSide(tmx - tmthing->radius, tmy - tmthing->radius, &junk) == P_PointOnLineSide(tmx + tmthing->radius, tmy + tmthing->radius, &junk)) && (P_PointOnLineSide(tmx + tmthing->radius, tmy - tmthing->radius, &junk) == P_PointOnLineSide(tmx - tmthing->radius, tmy + tmthing->radius, &junk))) - return true; // the line doesn't cross between either pair of opposite corners + return CHECKTHING_NOCOLLIDE; // the line doesn't cross between either pair of opposite corners } } else if (tmthing->flags & MF_PAPERCOLLISION) @@ -944,7 +949,7 @@ static boolean PIT_CheckThing(mobj_t *thing) tmsinradius = FixedMul(tmthing->radius, FINESINE(tmthing->angle>>ANGLETOFINESHIFT)); if (abs(thing->x - tmx) >= (thing->radius + abs(tmcosradius)) || abs(thing->y - tmy) >= (thing->radius + abs(tmsinradius))) - return true; // didn't hit it + return CHECKTHING_NOCOLLIDE; // didn't hit it v1.x = tmx - tmcosradius; v1.y = tmy - tmsinradius; @@ -961,32 +966,32 @@ static boolean PIT_CheckThing(mobj_t *thing) == P_PointOnLineSide(thing->x + thing->radius, thing->y + thing->radius, &junk)) && (P_PointOnLineSide(thing->x + thing->radius, thing->y - thing->radius, &junk) == P_PointOnLineSide(thing->x - thing->radius, thing->y + thing->radius, &junk))) - return true; // the line doesn't cross between either pair of opposite corners + return CHECKTHING_NOCOLLIDE; // the line doesn't cross between either pair of opposite corners } { UINT8 shouldCollide = LUA_Hook2Mobj(thing, tmthing, MOBJ_HOOK(MobjCollide)); // checks hook for thing's type if (P_MobjWasRemoved(tmthing) || P_MobjWasRemoved(thing)) - return true; // one of them was removed??? + return CHECKTHING_NOCOLLIDE; // one of them was removed??? if (shouldCollide == 1) - return false; // force collide + return CHECKTHING_DONE; // force collide else if (shouldCollide == 2) - return true; // force no collide + return CHECKTHING_NOCOLLIDE; // force no collide shouldCollide = LUA_Hook2Mobj(tmthing, thing, MOBJ_HOOK(MobjMoveCollide)); // checks hook for tmthing's type if (P_MobjWasRemoved(tmthing) || P_MobjWasRemoved(thing)) - return true; // one of them was removed??? + return CHECKTHING_NOCOLLIDE; // one of them was removed??? if (shouldCollide == 1) - return false; // force collide + return CHECKTHING_DONE; // force collide else if (shouldCollide == 2) - return true; // force no collide + return CHECKTHING_NOCOLLIDE; // force no collide } if (tmthing->type == MT_LAVAFALL_LAVA && (thing->type == MT_RING || thing->type == MT_REDTEAMRING || thing->type == MT_BLUETEAMRING || thing->type == MT_FLINGRING)) { //height check if (tmthing->z > thing->z + thing->height || thing->z > tmthing->z + tmthing->height || !(thing->health)) - return true; + return CHECKTHING_NOCOLLIDE; P_KillMobj(thing, tmthing, tmthing, DMG_FIRE); } @@ -995,7 +1000,7 @@ static boolean PIT_CheckThing(mobj_t *thing) { //height check if (tmthing->z > thing->z + thing->height || thing->z > tmthing->z + tmthing->height || !(thing->health)) - return true; + return CHECKTHING_NOCOLLIDE; if (thing->type == MT_TNTBARREL) P_KillMobj(thing, tmthing, tmthing->target, 0); @@ -1018,7 +1023,7 @@ static boolean PIT_CheckThing(mobj_t *thing) fixed_t s = FINESINE((ang >> ANGLETOFINESHIFT) & FINEMASK); S_StartSound(tmthing, thing->info->activesound); thing->extravalue2 += 2*FixedMul(s, dm)/3; - return true; + return CHECKTHING_COLLIDE; } } @@ -1026,14 +1031,14 @@ static boolean PIT_CheckThing(mobj_t *thing) { if (((thing->flags2 & MF2_AMBUSH) && (tmthing->z <= thing->z + thing->height) && (tmthing->z + tmthing->height >= thing->z)) || (tmthing->player->powers[pw_carry] == CR_MINECART && tmthing->tracer && !P_MobjWasRemoved(tmthing->tracer))) - return true; + return CHECKTHING_COLLIDE; } if (thing->type == MT_ROLLOUTROCK && tmthing->player && tmthing->health) { if (tmthing->player->powers[pw_carry] == CR_ROLLOUT) { - return true; + return CHECKTHING_NOCOLLIDE; } if ((thing->flags & MF_PUSHABLE) // not carrying a player && (tmthing->player->powers[pw_carry] == CR_NONE) // player is not already riding something @@ -1052,26 +1057,26 @@ static boolean PIT_CheckThing(mobj_t *thing) P_SetTarget(&tmthing->tracer, thing); if (!P_IsObjectOnGround(thing)) thing->momz += tmthing->momz; - return true; + return CHECKTHING_COLLIDE; } } else if (tmthing->type == MT_ROLLOUTROCK) { if (tmthing->z > thing->z + thing->height || thing->z > tmthing->z + tmthing->height || !thing->health) - return true; + return CHECKTHING_NOCOLLIDE; if (thing == tmthing->tracer) // don't collide with rider - return true; + return CHECKTHING_IGNORE; if (thing->flags & MF_SPRING) // bounce on springs { P_DoSpring(thing, tmthing); - return true; + return CHECKTHING_COLLIDE; } else if ((thing->flags & (MF_MONITOR|MF_SHOOTABLE)) == (MF_MONITOR|MF_SHOOTABLE) && !(tmthing->flags & MF_PUSHABLE)) // pop monitors while carrying a player { P_KillMobj(thing, tmthing, tmthing->tracer, 0); - return true; + return CHECKTHING_COLLIDE; } if (thing->type == tmthing->type // bounce against other rollout rocks @@ -1106,11 +1111,11 @@ static boolean PIT_CheckThing(mobj_t *thing) if (tmthing->type == MT_FANG && thing->type == MT_FSGNB) { if (thing->z > tmthing->z + tmthing->height) - return true; // overhead + return CHECKTHING_NOCOLLIDE; // overhead if (thing->z + thing->height < tmthing->z) - return true; // underneath + return CHECKTHING_NOCOLLIDE; // underneath if (!thing->tracer || !thing->tracer->tracer) - return true; + return CHECKTHING_IGNORE; P_SlapStick(tmthing, thing); // no return value was used in the original prototype script at this point, // so I'm assuming we fall back on the solid code to determine how it all ends? @@ -1123,13 +1128,13 @@ static boolean PIT_CheckThing(mobj_t *thing) if (tmthing->type == MT_BIGMINE) { if (!tmthing->momx && !tmthing->momy) - return true; + return CHECKTHING_IGNORE; if ((statenum_t)(thing->state-states) >= thing->info->meleestate) - return true; + return CHECKTHING_IGNORE; if (thing->z > tmthing->z + tmthing->height) - return true; // overhead + return CHECKTHING_NOCOLLIDE; // overhead if (thing->z + thing->height < tmthing->z) - return true; // underneath + return CHECKTHING_NOCOLLIDE; // underneath thing->momx = tmthing->momx/3; thing->momy = tmthing->momy/3; @@ -1141,18 +1146,18 @@ static boolean PIT_CheckThing(mobj_t *thing) S_StartSound(thing, thing->info->activesound); P_SetMobjState(thing, thing->info->meleestate); P_SetTarget(&thing->tracer, tmthing->tracer); - return true; + return CHECKTHING_COLLIDE; } else if (tmthing->type == MT_CRUSHCLAW) { if (tmthing->extravalue1 <= 0) - return true; + return CHECKTHING_IGNORE; if ((statenum_t)(thing->state-states) >= thing->info->meleestate) - return true; + return CHECKTHING_IGNORE; if (thing->z > tmthing->z + tmthing->height) - return true; // overhead + return CHECKTHING_NOCOLLIDE; // overhead if (thing->z + thing->height < tmthing->z) - return true; // underneath + return CHECKTHING_NOCOLLIDE; // underneath thing->momx = P_ReturnThrustX(tmthing, tmthing->angle, 2*tmthing->extravalue1*tmthing->scale/3); thing->momy = P_ReturnThrustY(tmthing, tmthing->angle, 2*tmthing->extravalue1*tmthing->scale/3); @@ -1161,7 +1166,7 @@ static boolean PIT_CheckThing(mobj_t *thing) P_SetMobjState(thing, thing->info->meleestate); if (tmthing->tracer) P_SetTarget(&thing->tracer, tmthing->tracer->target); - return false; + return CHECKTHING_DONE; } } @@ -1169,9 +1174,9 @@ static boolean PIT_CheckThing(mobj_t *thing) if (tmthing->type == MT_SPIKE && (thing->flags & MF_SOLID) && (tmthing->flags & MF_SOLID)) { if (thing->z > tmthing->z + tmthing->height) - return true; // overhead + return CHECKTHING_NOCOLLIDE; // overhead if (thing->z + thing->height < tmthing->z) - return true; // underneath + return CHECKTHING_NOCOLLIDE; // underneath if (tmthing->eflags & MFE_VERTICALFLIP) P_SetOrigin(thing, thing->x, thing->y, tmthing->z - thing->height - FixedMul(FRACUNIT, tmthing->scale)); @@ -1179,16 +1184,16 @@ static boolean PIT_CheckThing(mobj_t *thing) P_SetOrigin(thing, thing->x, thing->y, tmthing->z + tmthing->height + FixedMul(FRACUNIT, tmthing->scale)); if (thing->flags & MF_SHOOTABLE) P_DamageMobj(thing, tmthing, tmthing, 1, DMG_SPIKE); - return true; + return CHECKTHING_COLLIDE; } if (thing->flags & MF_PAIN && tmthing->player) { // Player touches painful thing sitting on the floor // see if it went over / under if (thing->z > tmthing->z + tmthing->height) - return true; // overhead + return CHECKTHING_NOCOLLIDE; // overhead if (thing->z + thing->height < tmthing->z) - return true; // underneath + return CHECKTHING_NOCOLLIDE; // underneath if (tmthing->flags & MF_SHOOTABLE && thing->health > 0) { UINT32 damagetype = (thing->info->mass & 0xFF); @@ -1196,16 +1201,17 @@ static boolean PIT_CheckThing(mobj_t *thing) damagetype = DMG_FIRE; if (P_DamageMobj(tmthing, thing, thing, 1, damagetype) && (damagetype = (thing->info->mass>>8))) S_StartSound(thing, damagetype); + return CHECKTHING_COLLIDE; } - return true; + return CHECKTHING_NOCOLLIDE; } else if (tmthing->flags & MF_PAIN && thing->player) { // Painful thing splats player in the face // see if it went over / under if (tmthing->z > thing->z + thing->height) - return true; // overhead + return CHECKTHING_NOCOLLIDE; // overhead if (tmthing->z + tmthing->height < thing->z) - return true; // underneath + return CHECKTHING_NOCOLLIDE; // underneath if (thing->flags & MF_SHOOTABLE && tmthing->health > 0) { UINT32 damagetype = (tmthing->info->mass & 0xFF); @@ -1213,32 +1219,33 @@ static boolean PIT_CheckThing(mobj_t *thing) damagetype = DMG_FIRE; if (P_DamageMobj(thing, tmthing, tmthing, 1, damagetype) && (damagetype = (tmthing->info->mass>>8))) S_StartSound(tmthing, damagetype); + return CHECKTHING_COLLIDE; } - return true; + return CHECKTHING_NOCOLLIDE; } if (thing->type == MT_HOOPCOLLIDE && thing->flags & MF_SPECIAL && tmthing->player) { P_TouchSpecialThing(thing, tmthing, true); - return true; + return CHECKTHING_COLLIDE; } // check for skulls slamming into things if (tmthing->flags2 & MF2_SKULLFLY) { if (tmthing->type == MT_EGGMOBILE) // Don't make Eggman stop! - return true; // Let him RUN YOU RIGHT OVER. >:3 + return CHECKTHING_COLLIDE; // Let him RUN YOU RIGHT OVER. >:3 else { // see if it went over / under if (tmthing->z > thing->z + thing->height) - return true; // overhead + return CHECKTHING_NOCOLLIDE; // overhead if (tmthing->z + tmthing->height < thing->z) - return true; // underneath + return CHECKTHING_NOCOLLIDE; // underneath tmthing->flags2 &= ~MF2_SKULLFLY; tmthing->momx = tmthing->momy = tmthing->momz = 0; - return false; // stop moving + return CHECKTHING_DONE; // stop moving } } @@ -1254,10 +1261,10 @@ static boolean PIT_CheckThing(mobj_t *thing) if ((tmznext <= thzh && tmz > thzh) || (tmznext > thzh - sprarea && tmznext < thzh)) { P_DoSpring(thing, tmthing); - return true; + return CHECKTHING_COLLIDE; } else if (tmz > thzh - sprarea && tmz < thzh) // Don't damage people springing up / down - return true; + return CHECKTHING_NOCOLLIDE; } // missiles can hit other things @@ -1265,31 +1272,31 @@ static boolean PIT_CheckThing(mobj_t *thing) { // see if it went over / under if (tmthing->z > thing->z + thing->height) - return true; // overhead + return CHECKTHING_NOCOLLIDE; // overhead if (tmthing->z + tmthing->height < thing->z) - return true; // underneath + return CHECKTHING_NOCOLLIDE; // underneath if (tmthing->type != MT_SHELL && tmthing->target && tmthing->target->type == thing->type) { // Don't hit same species as originator. if (thing == tmthing->target) - return true; + return CHECKTHING_IGNORE; if (thing->type != MT_PLAYER) { // Explode, but do no damage. // Let players missile other players. - return false; + return CHECKTHING_DONE; } } // Special case for bounce rings so they don't get caught behind solid objects. if ((tmthing->type == MT_THROWNBOUNCE && tmthing->fuse > 8*TICRATE) && thing->flags & MF_SOLID) - return true; + return CHECKTHING_IGNORE; // Missiles ignore Brak's helper. if (thing->type == MT_BLACKEGGMAN_HELPER) - return true; + return CHECKTHING_IGNORE; // Hurting Brak if (tmthing->type == MT_BLACKEGGMAN_MISSILE @@ -1298,19 +1305,22 @@ static boolean PIT_CheckThing(mobj_t *thing) // Not if Brak's already in pain if (!(thing->state >= &states[S_BLACKEGG_PAIN1] && thing->state <= &states[S_BLACKEGG_PAIN35])) P_SetMobjState(thing, thing->info->painstate); - return false; + return CHECKTHING_DONE; } if (!(thing->flags & MF_SHOOTABLE) && !(thing->type == MT_EGGSHIELD)) { // didn't do any damage - return !(thing->flags & MF_SOLID); + if (thing->flags & MF_SOLID) + return CHECKTHING_COLLIDE; + else + return CHECKTHING_NOCOLLIDE; } if (tmthing->flags & MF_MISSILE && thing->player && tmthing->target && tmthing->target->player && thing->player->ctfteam == tmthing->target->player->ctfteam && thing->player->powers[pw_carry] == CR_PLAYER && thing->tracer == tmthing->target) - return true; // Don't give rings to your carry player by accident. + return CHECKTHING_IGNORE; // Don't give rings to your carry player by accident. if (thing->type == MT_EGGSHIELD) { @@ -1318,7 +1328,7 @@ static boolean PIT_CheckThing(mobj_t *thing) if (angle < ANGLE_180) // hit shield from behind, shield is destroyed! P_KillMobj(thing, tmthing, tmthing, 0); - return false; + return CHECKTHING_DONE; } // damage / explode @@ -1344,7 +1354,7 @@ static boolean PIT_CheckThing(mobj_t *thing) if (!demoplayback || P_ControlStyle(thing->player) == CS_LMAOGALOG) P_SetPlayerAngle(thing->player, thing->angle); - return true; + return CHECKTHING_COLLIDE; } else if (tmthing->type == MT_BLACKEGGMAN_MISSILE && thing->player && ((thing->player->powers[pw_carry] == CR_GENERIC) || (thing->player->pflags & PF_JUMPED))) { @@ -1371,11 +1381,10 @@ static boolean PIT_CheckThing(mobj_t *thing) P_KillMobj(tmthing, NULL, NULL, 0); // don't traverse any more - if (tmthing->type == MT_SHELL) - return true; + return CHECKTHING_COLLIDE; else - return false; + return CHECKTHING_DONE; } if (thing->flags & MF_PUSHABLE && (tmthing->player || tmthing->flags & MF_PUSHABLE) @@ -1478,13 +1487,13 @@ static boolean PIT_CheckThing(mobj_t *thing) if (thing->flags & MF_SPECIAL && tmthing->player) { P_TouchSpecialThing(thing, tmthing, true); // can remove thing - return true; + return CHECKTHING_COLLIDE; } // check again for special pickup if (tmthing->flags & MF_SPECIAL && thing->player) { P_TouchSpecialThing(tmthing, thing, true); // can remove thing - return true; + return CHECKTHING_COLLIDE; } // Sprite Spikes! @@ -1544,7 +1553,7 @@ static boolean PIT_CheckThing(mobj_t *thing) if (playerangle > ANGLE_180) playerangle = InvAngle(playerangle); if (playerangle < ANGLE_90) - return true; // Yes, this is intentionally outside the z-height check. No standing on spikes whilst moving away from them. + return CHECKTHING_IGNORE; // Yes, this is intentionally outside the z-height check. No standing on spikes whilst moving away from them. } bottomz = thing->z; @@ -1578,15 +1587,15 @@ static boolean PIT_CheckThing(mobj_t *thing) if (thing->type == MT_FAN || thing->type == MT_STEAM) { P_DoFanAndGasJet(thing, tmthing); - return true; + return CHECKTHING_COLLIDE; } else if (thing->flags & MF_SPRING) { if ( thing->z <= tmthing->z + tmthing->height && tmthing->z <= thing->z + thing->height) if (P_DoSpring(thing, tmthing)) - return false; - return true; + return CHECKTHING_DONE; + return CHECKTHING_COLLIDE; } } @@ -1595,7 +1604,7 @@ static boolean PIT_CheckThing(mobj_t *thing) { if ((thing->z + thing->height >= tmthing->z) && (tmthing->z + tmthing->height >= thing->z)) - return false; + return CHECKTHING_DONE; } // Damage other players when invincible @@ -1630,7 +1639,7 @@ static boolean PIT_CheckThing(mobj_t *thing) if (tmthing->player && thing->player) { P_DoTailsCarry(thing->player, tmthing->player); - return true; + return CHECKTHING_COLLIDE; } } else if (thing->player) { @@ -1677,7 +1686,7 @@ static boolean PIT_CheckThing(mobj_t *thing) if (tmthing->player) // Is the moving/interacting object the player? { if (!tmthing->health) - return true; + return CHECKTHING_IGNORE; if (thing->type == MT_FAN || thing->type == MT_STEAM) P_DoFanAndGasJet(thing, tmthing); @@ -1686,14 +1695,16 @@ static boolean PIT_CheckThing(mobj_t *thing) if ( thing->z <= tmthing->z + tmthing->height && tmthing->z <= thing->z + thing->height) if (P_DoSpring(thing, tmthing)) - return false; - return true; + return CHECKTHING_DONE; + return CHECKTHING_COLLIDE; } // Monitor? else if (thing->flags & MF_MONITOR && !((thing->type == MT_RING_REDBOX && tmthing->player->ctfteam != 1) || (thing->type == MT_RING_BLUEBOX && tmthing->player->ctfteam != 2)) && (!(thing->flags & MF_SOLID) || P_PlayerCanDamage(tmthing->player, thing))) { + unsigned collide = CHECKTHING_NOCOLLIDE; + if (thing->z - thing->scale <= tmthing->z + tmthing->height && thing->z + thing->height + thing->scale >= tmthing->z) { @@ -1725,21 +1736,25 @@ static boolean PIT_CheckThing(mobj_t *thing) *momz /= 2; *momz -= (*momz/(underwater ? 8 : 4)); // Cap the height! } + collide = CHECKTHING_COLLIDE; } if (!(elementalpierce == 1 && thing->flags & MF_GRENADEBOUNCE)) // prevent gold monitor clipthrough. { if (player->pflags & PF_BOUNCING) P_DoAbilityBounce(player, false); - return false; + collide = CHECKTHING_DONE; } else *z -= *momz; // to ensure proper collision. } - return true; + return collide; } } + // not solid not blocked + unsigned collide = CHECKTHING_NOCOLLIDE; + if ((tmthing->flags & MF_SPRING || tmthing->type == MT_STEAM || tmthing->type == MT_SPIKE || tmthing->type == MT_WALLSPIKE) && (thing->player)) ; // springs, gas jets and springs should never be able to step up onto a player // z checking at last @@ -1762,25 +1777,27 @@ static boolean PIT_CheckThing(mobj_t *thing) tmfloorrover = NULL; tmfloorslope = NULL; } - return true; + return CHECKTHING_COLLIDE; } topz = thing->z - thing->scale; // FixedMul(FRACUNIT, thing->scale), but thing->scale == FRACUNIT in base scale anyways // block only when jumping not high enough, // (dont climb max. 24units while already in air) - // since return false doesn't handle momentum properly, + // since return CHECKTHING_DONE doesn't handle momentum properly, // we lie to P_TryMove() so it's always too high if (tmthing->player && tmthing->z + tmthing->height > topz && tmthing->z + tmthing->height < tmthing->ceilingz) { if (thing->flags & MF_GRENADEBOUNCE && (thing->flags & MF_MONITOR || thing->info->flags & MF_MONITOR)) // Gold monitor hack... - return false; + return CHECKTHING_DONE; tmfloorz = tmceilingz = topz; // block while in air tmceilingrover = NULL; tmceilingslope = NULL; tmfloorthing = thing; // needed for side collision + + collide = CHECKTHING_COLLIDE; } else if (topz < tmceilingz && tmthing->z <= thing->z+thing->height) { @@ -1788,6 +1805,8 @@ static boolean PIT_CheckThing(mobj_t *thing) tmceilingrover = NULL; tmceilingslope = NULL; tmfloorthing = thing; // thing we may stand on + + collide = CHECKTHING_COLLIDE; } } else @@ -1803,25 +1822,27 @@ static boolean PIT_CheckThing(mobj_t *thing) tmceilingrover = NULL; tmceilingslope = NULL; } - return true; + return CHECKTHING_COLLIDE; } topz = thing->z + thing->height + thing->scale; // FixedMul(FRACUNIT, thing->scale), but thing->scale == FRACUNIT in base scale anyways // block only when jumping not high enough, // (dont climb max. 24units while already in air) - // since return false doesn't handle momentum properly, + // since return CHECKTHING_DONE doesn't handle momentum properly, // we lie to P_TryMove() so it's always too high if (tmthing->player && tmthing->z < topz && tmthing->z > tmthing->floorz) { if (thing->flags & MF_GRENADEBOUNCE && (thing->flags & MF_MONITOR || thing->info->flags & MF_MONITOR)) // Gold monitor hack... - return false; + return CHECKTHING_DONE; tmfloorz = tmceilingz = topz; // block while in air tmfloorrover = NULL; tmfloorslope = NULL; tmfloorthing = thing; // needed for side collision + + collide = CHECKTHING_COLLIDE; } else if (topz > tmfloorz && tmthing->z+tmthing->height >= thing->z) { @@ -1829,12 +1850,18 @@ static boolean PIT_CheckThing(mobj_t *thing) tmfloorrover = NULL; tmfloorslope = NULL; tmfloorthing = thing; // thing we may stand on + + collide = CHECKTHING_COLLIDE; } } } - // not solid not blocked - return true; + return collide; +} + +static boolean PIT_CheckThing(mobj_t *thing) +{ + return PIT_DoCheckThing(thing) != CHECKTHING_DONE; } // PIT_CheckCameraLine @@ -2010,38 +2037,6 @@ static boolean PIT_CheckLine(line_t *ld) // ========================================================================= // MOVEMENT CLIPPING // ========================================================================= - -// -// P_CheckPosition -// This is purely informative, nothing is modified -// (except things picked up). -// -// in: -// a mobj_t (can be valid or invalid) -// a position to be checked -// (doesn't need to be related to the mobj_t->x,y) -// -// during: -// special things are touched if MF_PICKUP -// early out on solid lines? -// -// out: -// newsubsec -// tmfloorz -// tmceilingz -// tmdropoffz -// tmdrpoffceilz -// the lowest point contacted -// (monsters won't move to a dropoff) -// speciallines[] -// numspeciallines -// - -// tmfloorz -// the nearest floor or thing's top under tmthing -// tmceilingz -// the nearest ceiling or thing's bottom over tmthing -// boolean P_CheckPosition(mobj_t *thing, fixed_t x, fixed_t y) { INT32 xl, xh, yl, yh, bx, by; @@ -2282,9 +2277,7 @@ boolean P_CheckPosition(mobj_t *thing, fixed_t x, fixed_t y) if (tmflags & MF_NOCLIP) return true; - // Check things first, possibly picking things up. - - // MF_NOCLIPTHING: used by camera to not be blocked by things + // Check things first. if (!(thing->flags & MF_NOCLIPTHING)) { for (bx = xl; bx <= xh; bx++) @@ -2893,7 +2886,7 @@ boolean P_TryMove(mobj_t *thing, fixed_t x, fixed_t y, boolean allowdropoff) // standing on top and move it, too. if (thing->flags & MF_PUSHABLE) { - INT32 bx, by, xl, xh, yl, yh; + INT32 xl, xh, yl, yh; yh = (unsigned)(thing->y + MAXRADIUS - bmaporgy)>>MAPBLOCKSHIFT; yl = (unsigned)(thing->y - MAXRADIUS - bmaporgy)>>MAPBLOCKSHIFT; @@ -2906,9 +2899,7 @@ boolean P_TryMove(mobj_t *thing, fixed_t x, fixed_t y, boolean allowdropoff) standx = x; standy = y; - for (by = yl; by <= yh; by++) - for (bx = xl; bx <= xh; bx++) - P_BlockThingsIterator(bx, by, PIT_PushableMoved); + P_DoBlockThingsIterate(xl, yl, xh, yh, PIT_PushableMoved); } // Link the thing into its new position @@ -4217,7 +4208,6 @@ static boolean PIT_RadiusAttack(mobj_t *thing) // void P_RadiusAttack(mobj_t *spot, mobj_t *source, fixed_t damagedist, UINT8 damagetype, boolean sightcheck) { - INT32 x, y; INT32 xl, xh, yl, yh; fixed_t dist; @@ -4235,9 +4225,7 @@ void P_RadiusAttack(mobj_t *spot, mobj_t *source, fixed_t damagedist, UINT8 dama bombdamagetype = damagetype; bombsightcheck = sightcheck; - for (y = yl; y <= yh; y++) - for (x = xl; x <= xh; x++) - P_BlockThingsIterator(x, y, PIT_RadiusAttack); + P_DoBlockThingsIterate(xl, yl, xh, yh, PIT_RadiusAttack); } // @@ -4392,16 +4380,18 @@ static boolean P_CheckSectorPolyObjects(sector_t *sector, boolean realcrush, boo for (x = po->blockbox[BOXLEFT]; x <= po->blockbox[BOXRIGHT]; ++x) { mobj_t *mo; + blocknode_t *block; if (x < 0 || y < 0 || x >= bmapwidth || y >= bmapheight) continue; - mo = blocklinks[y * bmapwidth + x]; + block = blocklinks[y * bmapwidth + x]; - for (; mo; mo = mo->bnext) + for (; block; block = block->mnext) { - // Monster Iestyn: do we need to check if a mobj has already been checked? ...probably not I suspect + mo = block->mobj; + // Monster Iestyn: do we need to check if a mobj has already been checked? ...probably not I suspect if (!P_MobjInsidePolyobj(po, mo)) continue; diff --git a/src/p_maputl.c b/src/p_maputl.c index 2d9f23c7b9bce90d87f43af3ad0ff49b3ecebd95..010aad6eef3c1b34df8d0db05aff0e24739acdd1 100644 --- a/src/p_maputl.c +++ b/src/p_maputl.c @@ -680,6 +680,35 @@ void P_LineOpening(line_t *linedef, mobj_t *mobj) openrange = opentop - openbottom; } +static blocknode_t *freeblocks; + +static blocknode_t *P_CreateBlockNode(mobj_t *thing, int x, int y) +{ + blocknode_t *block; + + if (freeblocks != NULL) + { + block = freeblocks; + freeblocks = block->bnext; + } + else + block = Z_Malloc(sizeof(blocknode_t), PU_LEVEL, NULL); + + block->blockindex = x + y*bmapwidth; + block->mobj = thing; + block->mnext = NULL; + block->mprev = NULL; + block->bprev = NULL; + block->bnext = NULL; + + return block; +} + +static void P_ReleaseBlockNode(blocknode_t *node) +{ + node->bnext = freeblocks; + freeblocks = node; +} // // THING POSITION SETTING @@ -730,20 +759,20 @@ void P_UnsetThingPosition(mobj_t *thing) if (!(thing->flags & MF_NOBLOCKMAP)) { - /* inert things don't need to be in blockmap - * - * killough 8/11/98: simpler scheme using pointers-to-pointers for prev - * pointers, allows head node pointers to be treated like everything else - * - * Also more robust, since it doesn't depend on current position for - * unlinking. Old method required computing head node based on position - * at time of unlinking, assuming it was the same position as during - * linking. - */ + // [RH] Unlink from all blocks this actor uses + blocknode_t *block = thing->blocknode; - mobj_t *bnext, **bprev = thing->bprev; - if (bprev && (*bprev = bnext = thing->bnext) != NULL) // unlink from block map - bnext->bprev = bprev; + while (block != NULL) + { + if (block->mnext != NULL) + block->mnext->mprev = block->mprev; + *(block->mprev) = block->mnext; + blocknode_t *next = block->bnext; + P_ReleaseBlockNode(block); + block = next; + } + + thing->blocknode = NULL; } } @@ -814,24 +843,45 @@ void P_SetThingPosition(mobj_t *thing) if (!(thing->flags & MF_NOBLOCKMAP)) { // inert things don't need to be in blockmap - const INT32 blockx = (unsigned)(thing->x - bmaporgx)>>MAPBLOCKSHIFT; - const INT32 blocky = (unsigned)(thing->y - bmaporgy)>>MAPBLOCKSHIFT; - if (blockx >= 0 && blockx < bmapwidth - && blocky >= 0 && blocky < bmapheight) + INT32 x1 = (unsigned)(thing->x - thing->radius - bmaporgx)>>MAPBLOCKSHIFT; + INT32 y1 = (unsigned)(thing->y - thing->radius - bmaporgy)>>MAPBLOCKSHIFT; + INT32 x2 = (unsigned)(thing->x + thing->radius - bmaporgx)>>MAPBLOCKSHIFT; + INT32 y2 = (unsigned)(thing->y + thing->radius - bmaporgy)>>MAPBLOCKSHIFT; + + thing->blocknode = NULL; + + blocknode_t **alink = &thing->blocknode; + + if (!(x1 >= bmapwidth || x2 < 0 || y1 >= bmapheight || y2 < 0)) { - // killough 8/11/98: simpler scheme using - // pointer-to-pointer prev pointers -- - // allows head nodes to be treated like everything else - - mobj_t **link = &blocklinks[blocky*bmapwidth + blockx]; - mobj_t *bnext = *link; - if ((thing->bnext = bnext) != NULL) - bnext->bprev = &thing->bnext; - thing->bprev = link; - *link = thing; + // [RH] Link into every block this actor touches, not just the center one + x1 = max(0, x1); + y1 = max(0, y1); + x2 = min(bmapwidth - 1, x2); + y2 = min(bmapheight - 1, y2); + for (int y = y1; y <= y2; ++y) + { + for (int x = x1; x <= x2; ++x) + { + blocknode_t **link = &blocklinks[y*bmapwidth + x]; + blocknode_t *node = P_CreateBlockNode(thing, x, y); + + // Link in to block + if ((node->mnext = *link) != NULL) + { + (*link)->mprev = &node->mnext; + } + node->mprev = link; + *link = node; + + // Link in to actor + node->bprev = alink; + node->bnext = NULL; + (*alink) = node; + alink = &node->bnext; + } + } } - else // thing is off the map - thing->bnext = NULL, thing->bprev = NULL; } // Allows you to 'step' on a new linedef exec when the previous @@ -971,34 +1021,220 @@ boolean P_BlockLinesIterator(INT32 x, INT32 y, boolean (*func)(line_t *)) return true; // Everything was checked. } - // // P_BlockThingsIterator // boolean P_BlockThingsIterator(INT32 x, INT32 y, boolean (*func)(mobj_t *)) { - mobj_t *mobj, *bnext = NULL; + mobj_t *mobj; + blocknode_t *block; if (x < 0 || y < 0 || x >= bmapwidth || y >= bmapheight) return true; // Check interaction with the objects in the blockmap. - for (mobj = blocklinks[y*bmapwidth + x]; mobj; mobj = bnext) + for (block = blocklinks[y*bmapwidth + x]; block; block = block->mnext) { - P_SetTarget(&bnext, mobj->bnext); // We want to note our reference to bnext here incase it is MF_NOTHINK and gets removed! + mobj = block->mobj; + if (!func(mobj)) - { - P_SetTarget(&bnext, NULL); return false; + if (P_MobjWasRemoved(tmthing)) // func just broke blockmap chain, cannot continue. + return true; + } + + return true; +} + +boolean P_DoBlockThingsIterate(int x1, int y1, int x2, int y2, boolean (*func)(mobj_t *)) +{ + boolean status = true; + + for (INT32 bx = x1; bx <= x2; bx++) + for (INT32 by = y1; by <= y2; by++) + if (!P_BlockThingsIterator(bx, by, func)) + status = false; + + return status; +} + +static bthingit_hash_entry_t *GetHashEntryForIterator(bthingit_t *it, int i) +{ + if (i < NUM_BTHINGIT_FIXEDHASH) + return &it->fixedhash[i]; + else + return &it->dynhash[i - NUM_BTHINGIT_FIXEDHASH]; +} + +static blocknode_t *GetBlockmapBlock(int x, int y) +{ + if (x >= 0 && y >= 0 && x < bmapwidth && y < bmapheight) + { + return blocklinks[y*bmapwidth + x]; + } + else + { + // invalid block + return NULL; + } +} + +static bthingit_t *freeiters; + +bthingit_t *P_NewBlockThingsIterator(int x1, int y1, int x2, int y2) +{ + bthingit_t *it; + blocknode_t *block; + + x1 = max(0, x1); + y1 = max(0, y1); + x2 = min(bmapwidth - 1, x2); + y2 = min(bmapheight - 1, y2); + + if (x1 > x2 || y1 > y2) + return NULL; + + block = GetBlockmapBlock(x1, y1); + if (!block) + return NULL; + + if (freeiters != NULL) + { + it = freeiters; + freeiters = it->freechain; + } + else + it = Z_Calloc(sizeof(bthingit_t), PU_LEVEL, NULL); + + it->x1 = x1; + it->y1 = y1; + it->x2 = x2; + it->y2 = y2; + it->curx = x1; + it->cury = y1; + it->block = block; + it->freechain = NULL; + it->numfixedhash = 0; + it->dynhashcount = 0; + + for (size_t i = 0; i < NUM_BTHINGIT_BUCKETS; i++) + it->buckets[i] = -1; + + return it; +} + +mobj_t *P_BlockThingsIteratorNext(bthingit_t *it, boolean centeronly) +{ + for (;;) + { + while (it->block != NULL) + { + mobj_t *mobj = it->block->mobj; + blocknode_t *node = it->block; + + it->block = it->block->mnext; + + // Don't recheck things that were already checked + if (node->bnext == NULL && node->bprev == &mobj->blocknode) + { + // This actor doesn't span blocks, so we know it can only ever be checked once. + return mobj; + } + else + { + // Block boundaries for compatibility mode + if (centeronly) + { + fixed_t blockleft = (it->curx * MAPBLOCKUNITS) + bmaporgx; + fixed_t blockright = blockleft + MAPBLOCKUNITS; + fixed_t blockbottom = (it->cury * MAPBLOCKUNITS) + bmaporgy; + fixed_t blocktop = blockbottom + MAPBLOCKUNITS; + + // only return actors with the center in this block + if (mobj->x >= blockleft && mobj->x < blockright && + mobj->y >= blockbottom && mobj->y < blocktop) + { + return mobj; + } + } + else + { + bthingit_hash_entry_t *entry; + int i; + size_t hash = ((size_t)mobj >> 3) % NUM_BTHINGIT_BUCKETS; + + for (i = it->buckets[hash]; i >= 0; ) + { + entry = GetHashEntryForIterator(it, i); + if (entry->mobj == mobj) + { + // I've already been checked. Skip to the next mobj. + break; + } + i = entry->next; + } + + if (i < 0) + { + // Add mobj to the hash table and return it. + if (it->numfixedhash < NUM_BTHINGIT_FIXEDHASH) + { + entry = &it->fixedhash[it->numfixedhash]; + entry->next = it->buckets[hash]; + it->buckets[hash] = it->numfixedhash++; + } + else + { + if (!it->dynhash) + { + it->dynhashcapacity = 50; + Z_Calloc(it->dynhashcapacity * sizeof(it->dynhashcapacity), PU_LEVEL, &it->dynhash); + } + if (it->dynhashcount == it->dynhashcapacity) + { + it->dynhashcapacity *= 2; + it->dynhash = Z_Realloc(it->dynhash, it->dynhashcapacity * sizeof(it->dynhashcapacity), PU_LEVEL, &it->dynhash); + } + i = (int)it->dynhashcount; + it->dynhashcount++; + entry = &it->dynhash[i]; + entry->next = it->buckets[hash]; + it->buckets[hash] = i + NUM_BTHINGIT_FIXEDHASH; + } + + entry->mobj = mobj; + return mobj; + } + } + } } - if (P_MobjWasRemoved(tmthing) // func just popped our tmthing, cannot continue. - || (bnext && P_MobjWasRemoved(bnext))) // func just broke blockmap chain, cannot continue. + + if (++it->curx > it->x2) { - P_SetTarget(&bnext, NULL); - return true; + it->curx = it->x1; + if (++it->cury > it->y2) + return NULL; } + + it->block = GetBlockmapBlock(it->curx, it->cury); } - return true; + + return NULL; +} + +void P_FreeBlockThingsIterator(bthingit_t *it) +{ + if (it) + { + it->freechain = freeiters; + freeiters = it; + } +} + +void P_ClearBlockNodes(void) +{ + freeblocks = NULL; + freeiters = NULL; } // diff --git a/src/p_maputl.h b/src/p_maputl.h index 08de0cb0b5f43bff5609c3cea58b06b14cfd36bb..e894c08a2461caf82397c569549e5bd3213ae3b7 100644 --- a/src/p_maputl.h +++ b/src/p_maputl.h @@ -63,6 +63,39 @@ void P_LineOpening(line_t *plinedef, mobj_t *mobj); boolean P_BlockLinesIterator(INT32 x, INT32 y, boolean(*func)(line_t *)); boolean P_BlockThingsIterator(INT32 x, INT32 y, boolean(*func)(mobj_t *)); +void P_ClearBlockNodes(void); + +typedef struct +{ + mobj_t *mobj; + int next; +} bthingit_hash_entry_t; + +#define NUM_BTHINGIT_BUCKETS 32 +#define NUM_BTHINGIT_FIXEDHASH 10 + +typedef struct bthingit_s +{ + int x1, y1, x2, y2; + int curx, cury; + blocknode_t *block; + + int buckets[NUM_BTHINGIT_BUCKETS]; + bthingit_hash_entry_t fixedhash[NUM_BTHINGIT_FIXEDHASH]; + int numfixedhash; + + bthingit_hash_entry_t *dynhash; + size_t dynhashcount; + size_t dynhashcapacity; + + struct bthingit_s *freechain; +} bthingit_t; + +bthingit_t *P_NewBlockThingsIterator(int x1, int y1, int x2, int y2); +mobj_t *P_BlockThingsIteratorNext(bthingit_t *it, boolean centeronly); +void P_FreeBlockThingsIterator(bthingit_t *it); +boolean P_DoBlockThingsIterate(int x1, int y1, int x2, int y2, boolean (*func)(mobj_t *)); + #define PT_ADDLINES 1 #define PT_ADDTHINGS 2 #define PT_EARLYOUT 4 diff --git a/src/p_mobj.c b/src/p_mobj.c index f16fef2f00312ef6dd7d8a0cc60bffb447377ccc..28dd9304f6ed390fe23861c66200e6d51dfcb656 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -9455,7 +9455,7 @@ static inline boolean PIT_PushThing(mobj_t *thing) static void P_PointPushThink(mobj_t *mobj) { - INT32 xl, xh, yl, yh, bx, by; + INT32 xl, xh, yl, yh; fixed_t radius; if (!mobj->spawnpoint) @@ -9470,9 +9470,8 @@ static void P_PointPushThink(mobj_t *mobj) xh = (unsigned)(mobj->x + radius - bmaporgx + MAXRADIUS)>>MAPBLOCKSHIFT; yl = (unsigned)(mobj->y - radius - bmaporgy - MAXRADIUS)>>MAPBLOCKSHIFT; yh = (unsigned)(mobj->y + radius - bmaporgy + MAXRADIUS)>>MAPBLOCKSHIFT; - for (bx = xl; bx <= xh; bx++) - for (by = yl; by <= yh; by++) - P_BlockThingsIterator(bx, by, PIT_PushThing); + + P_DoBlockThingsIterate(xl, yl, xh, yh, PIT_PushThing); } static boolean P_MobjRegularThink(mobj_t *mobj) diff --git a/src/p_mobj.h b/src/p_mobj.h index f2e4cbf3d63ff1675825078c1e6fc165aa515af8..f833415ed1d0af3ee377ed6d4ced943d2782f499 100644 --- a/src/p_mobj.h +++ b/src/p_mobj.h @@ -273,6 +273,19 @@ typedef enum { PCF_THUNK = 32, } precipflag_t; +// [RH] Like msecnode_t, but for the blockmap +typedef struct blocknode_s +{ + struct mobj_s *mobj; + + int blockindex; // index into blocklinks for the block this node is in + + struct blocknode_s **mprev; // previous actor in this block + struct blocknode_s *mnext; // next actor in this block + struct blocknode_s **bprev; // previous block this actor is in + struct blocknode_s *bnext; // next block this actor is in +} blocknode_t; + // Map Object definition. typedef struct mobj_s { @@ -344,8 +357,7 @@ typedef struct mobj_s // Interaction info, by BLOCKMAP. // Links in blocks (if needed). - struct mobj_s *bnext; - struct mobj_s **bprev; // killough 8/11/98: change to ptr-to-ptr + blocknode_t *blocknode; // Additional pointers for NiGHTS hoops struct mobj_s *hnext; diff --git a/src/p_polyobj.c b/src/p_polyobj.c index 331bc5c7f0397876d10c92ad6c316d2ffeb56d41..e779956e8d92092bf41a97db8efa5a7e7fd1d973 100644 --- a/src/p_polyobj.c +++ b/src/p_polyobj.c @@ -877,14 +877,17 @@ static void Polyobj_carryThings(polyobj_t *po, fixed_t dx, fixed_t dy) for (x = po->blockbox[BOXLEFT]; x <= po->blockbox[BOXRIGHT]; ++x) { mobj_t *mo; + blocknode_t *block; if (x < 0 || y < 0 || x >= bmapwidth || y >= bmapheight) continue; - mo = blocklinks[y * bmapwidth + x]; + block = blocklinks[y * bmapwidth + x]; - for (; mo; mo = mo->bnext) + for (; block; block = block->mnext) { + mo = block->mobj; + if (mo->lastlook == pomovecount) continue; @@ -937,10 +940,12 @@ static INT32 Polyobj_clipThings(polyobj_t *po, line_t *line) { if (!(x < 0 || y < 0 || x >= bmapwidth || y >= bmapheight)) { - mobj_t *mo = blocklinks[y * bmapwidth + x]; + mobj_t *mo = NULL; + blocknode_t *block = blocklinks[y * bmapwidth + x]; - for (; mo; mo = mo->bnext) + for (; block; block = block->mnext) { + mo = block->mobj; // Don't scroll objects that aren't affected by gravity if (mo->flags & MF_NOGRAVITY) @@ -1109,14 +1114,17 @@ static void Polyobj_rotateThings(polyobj_t *po, vector2_t origin, angle_t delta, for (x = po->blockbox[BOXLEFT]; x <= po->blockbox[BOXRIGHT]; ++x) { mobj_t *mo; + blocknode_t *block; if (x < 0 || y < 0 || x >= bmapwidth || y >= bmapheight) continue; - mo = blocklinks[y * bmapwidth + x]; + block = blocklinks[y * bmapwidth + x]; - for (; mo; mo = mo->bnext) + for (; block; block = block->mnext) { + mo = block->mobj; + if (mo->lastlook == pomovecount) continue; diff --git a/src/p_saveg.c b/src/p_saveg.c index 8e2517473de63c166a1867518485495174b9fed9..6c6548c567f195cfc7bbc884e1e45dea18afde1a 100644 --- a/src/p_saveg.c +++ b/src/p_saveg.c @@ -3221,6 +3221,8 @@ static thinker_t* LoadMobjThinker(actionf_p1 thinker) slope->normal.x = READFIXED(save_p); slope->normal.y = READFIXED(save_p); slope->normal.z = READFIXED(save_p); + + slope->moved = true; } if (diff2 & MD2_DRAWONLYFORPLAYER) mobj->drawonlyforplayer = &players[READUINT8(save_p)]; diff --git a/src/p_setup.c b/src/p_setup.c index 851231c423dbd77ba1df777e7de2edb6015faf00..614a820806ca714ba3d86c39b630b9b1ba6ab1bf 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -138,7 +138,7 @@ INT32 *blockmaplump; // Big blockmap // origin of block map fixed_t bmaporgx, bmaporgy; // for thing chains -mobj_t **blocklinks; +blocknode_t **blocklinks; // REJECT // For fast sight rejection. @@ -1650,15 +1650,15 @@ textmap_colormap_t textmap_colormap = { false, 0, 25, 0, 25, 0, 31, 0 }; typedef enum { - PD_A = 1, - PD_B = 1<<1, - PD_C = 1<<2, - PD_D = 1<<3, + PD_A = 1, + PD_B = 1<<1, + PD_C = 1<<2, + PD_D = 1<<3, } planedef_t; typedef struct textmap_plane_s { - UINT8 defined; - fixed_t a, b, c, d; + UINT8 defined; + double a, b, c, d; } textmap_plane_t; textmap_plane_t textmap_planefloor = {0, 0, 0, 0, 0}; @@ -1719,42 +1719,42 @@ static void ParseTextmapSectorParameter(UINT32 i, const char *param, const char else if (fastcmp(param, "floorplane_a")) { textmap_planefloor.defined |= PD_A; - textmap_planefloor.a = FLOAT_TO_FIXED(atof(val)); + textmap_planefloor.a = atof(val); } else if (fastcmp(param, "floorplane_b")) { textmap_planefloor.defined |= PD_B; - textmap_planefloor.b = FLOAT_TO_FIXED(atof(val)); + textmap_planefloor.b = atof(val); } else if (fastcmp(param, "floorplane_c")) { textmap_planefloor.defined |= PD_C; - textmap_planefloor.c = FLOAT_TO_FIXED(atof(val)); + textmap_planefloor.c = atof(val); } else if (fastcmp(param, "floorplane_d")) { textmap_planefloor.defined |= PD_D; - textmap_planefloor.d = FLOAT_TO_FIXED(atof(val)); + textmap_planefloor.d = atof(val); } else if (fastcmp(param, "ceilingplane_a")) { textmap_planeceiling.defined |= PD_A; - textmap_planeceiling.a = FLOAT_TO_FIXED(atof(val)); + textmap_planeceiling.a = atof(val); } else if (fastcmp(param, "ceilingplane_b")) { textmap_planeceiling.defined |= PD_B; - textmap_planeceiling.b = FLOAT_TO_FIXED(atof(val)); + textmap_planeceiling.b = atof(val); } else if (fastcmp(param, "ceilingplane_c")) { textmap_planeceiling.defined |= PD_C; - textmap_planeceiling.c = FLOAT_TO_FIXED(atof(val)); + textmap_planeceiling.c = atof(val); } else if (fastcmp(param, "ceilingplane_d")) { textmap_planeceiling.defined |= PD_D; - textmap_planeceiling.d = FLOAT_TO_FIXED(atof(val)); + textmap_planeceiling.d = atof(val); } else if (fastcmp(param, "lightcolor")) { @@ -2992,13 +2992,13 @@ static void P_LoadTextmap(void) if (textmap_planefloor.defined == (PD_A|PD_B|PD_C|PD_D)) { - sc->f_slope = MakeViaEquationConstants(textmap_planefloor.a, textmap_planefloor.b, textmap_planefloor.c, textmap_planefloor.d); + sc->f_slope = P_MakeSlopeViaEquationConstants(textmap_planefloor.a, textmap_planefloor.b, textmap_planefloor.c, textmap_planefloor.d); sc->hasslope = true; } if (textmap_planeceiling.defined == (PD_A|PD_B|PD_C|PD_D)) { - sc->c_slope = MakeViaEquationConstants(textmap_planeceiling.a, textmap_planeceiling.b, textmap_planeceiling.c, textmap_planeceiling.d); + sc->c_slope = P_MakeSlopeViaEquationConstants(textmap_planeceiling.a, textmap_planeceiling.b, textmap_planeceiling.c, textmap_planeceiling.d); sc->hasslope = true; } @@ -7869,6 +7869,8 @@ boolean P_LoadLevel(boolean fromnetsave, boolean reloadinggamestate) Z_Free(ss->attachedsolid); } + P_ClearBlockNodes(); + // Clear pointers that would be left dangling by the purge R_FlushTranslationColormapCache(); @@ -7913,6 +7915,24 @@ boolean P_LoadLevel(boolean fromnetsave, boolean reloadinggamestate) if (!P_LoadMapFromFile()) return false; + if (!demoplayback) + { + clientGamedata->mapvisited[gamemap-1] |= MV_VISITED; + serverGamedata->mapvisited[gamemap-1] |= MV_VISITED; + + M_SilentUpdateUnlockablesAndEmblems(serverGamedata); + + if (M_UpdateUnlockablesAndExtraEmblems(clientGamedata)) + { + S_StartSound(NULL, sfx_s3k68); + G_SaveGameData(clientGamedata); + } + else if (!reloadinggamestate) + { + G_SaveGameData(clientGamedata); + } + } + // init anything that P_SpawnSlopes/P_LoadThings needs to know P_InitSpecials(); @@ -7971,24 +7991,6 @@ boolean P_LoadLevel(boolean fromnetsave, boolean reloadinggamestate) nextmapoverride = 0; skipstats = 0; - if (!demoplayback) - { - clientGamedata->mapvisited[gamemap-1] |= MV_VISITED; - serverGamedata->mapvisited[gamemap-1] |= MV_VISITED; - - M_SilentUpdateUnlockablesAndEmblems(serverGamedata); - - if (M_UpdateUnlockablesAndExtraEmblems(clientGamedata)) - { - S_StartSound(NULL, sfx_s3k68); - G_SaveGameData(clientGamedata); - } - else if (!reloadinggamestate) - { - G_SaveGameData(clientGamedata); - } - } - levelloading = false; P_RunCachedActions(); diff --git a/src/p_slopes.c b/src/p_slopes.c index 1c0ee81a7e9453d204049d2f0ab5986a07849edb..e75d36edefe8d8484cb0c9bebb4d02d5f1b1e174 100644 --- a/src/p_slopes.c +++ b/src/p_slopes.c @@ -1,6 +1,6 @@ // SONIC ROBO BLAST 2 //----------------------------------------------------------------------------- -// Copyright (C) 2004 by Stephen McGranahan +// Copyright (C) 2009 by Stephen McGranahan. // Copyright (C) 2015-2023 by Sonic Team Junior. // // This program is free software distributed under the @@ -14,6 +14,7 @@ #include "r_defs.h" #include "r_state.h" #include "m_bbox.h" +#include "m_vector.h" #include "z_zone.h" #include "p_local.h" #include "p_spec.h" @@ -28,12 +29,40 @@ pslope_t *slopelist = NULL; UINT16 slopecount = 0; // Calculate line normal -void P_CalculateSlopeNormal(pslope_t *slope) { +void P_CalculateSlopeNormal(pslope_t *slope) +{ slope->normal.z = FINECOSINE(slope->zangle>>ANGLETOFINESHIFT); slope->normal.x = FixedMul(FINESINE(slope->zangle>>ANGLETOFINESHIFT), slope->d.x); slope->normal.y = FixedMul(FINESINE(slope->zangle>>ANGLETOFINESHIFT), slope->d.y); } +static void CalculateNormalDir(pslope_t *slope, dvector3_t *dnormal) +{ + double hyp = hypot(dnormal->x, dnormal->y); + + if (fpclassify(hyp) == FP_NORMAL) + { + slope->dnormdir.x = -dnormal->x / hyp; + slope->dnormdir.y = -dnormal->y / hyp; + slope->dzdelta = hyp / dnormal->z; + } + else + { + slope->dnormdir.x = slope->dnormdir.y = 0.0; + slope->dzdelta = 0.0; + } +} + +void P_CalculateSlopeVectors(pslope_t *slope) +{ + dvector3_t dnormal; + + DVector3_Load(&dnormal, FixedToDouble(slope->normal.x), FixedToDouble(slope->normal.y), FixedToDouble(slope->normal.z)); + DVector3_Load(&slope->dorigin, FixedToDouble(slope->o.x), FixedToDouble(slope->o.y), FixedToDouble(slope->o.z)); + + CalculateNormalDir(slope, &dnormal); +} + /// Setup slope via 3 vertexes. static void ReconfigureViaVertexes (pslope_t *slope, const vector3_t v1, const vector3_t v2, const vector3_t v3) { @@ -89,22 +118,31 @@ static void ReconfigureViaVertexes (pslope_t *slope, const vector3_t v1, const v slope->xydirection = R_PointToAngle2(0, 0, slope->d.x, slope->d.y)+ANGLE_180; slope->zangle = InvAngle(R_PointToAngle2(0, 0, FRACUNIT, slope->zdelta)); } + + P_CalculateSlopeVectors(slope); } /// Setup slope via constants. -static void ReconfigureViaConstants (pslope_t *slope, const fixed_t a, const fixed_t b, const fixed_t c, const fixed_t d) +static void ReconfigureViaConstants (pslope_t *slope, const double pa, const double pb, const double pc, const double pd) { fixed_t m; fixed_t o = 0; - vector3_t *normal = &slope->normal; + double d_o = 0.0; + + fixed_t a = DoubleToFixed(pa), b = DoubleToFixed(pb), c = DoubleToFixed(pc), d = DoubleToFixed(pd); if (c) + { + d_o = abs(c) <= FRACUNIT ? -(pd * (1.0 / pc)) : -(pd / pc); o = abs(c) <= FRACUNIT ? -FixedMul(d, FixedDiv(FRACUNIT, c)) : -FixedDiv(d, c); + } // Set origin. FV3_Load(&slope->o, 0, 0, o); // Get slope's normal. + vector3_t *normal = &slope->normal; + FV3_Load(normal, a, b, c); FV3_Normalize(normal); @@ -123,6 +161,17 @@ static void ReconfigureViaConstants (pslope_t *slope, const fixed_t a, const fix // Get angles slope->xydirection = R_PointToAngle2(0, 0, slope->d.x, slope->d.y)+ANGLE_180; slope->zangle = InvAngle(R_PointToAngle2(0, 0, FRACUNIT, slope->zdelta)); + + dvector3_t dnormal; + + DVector3_Load(&dnormal, pa, pb, pc); + DVector3_Normalize(&dnormal); + if (dnormal.z < 0) + DVector3_Negate(&dnormal); + + DVector3_Load(&slope->dorigin, 0, 0, d_o); + + CalculateNormalDir(slope, &dnormal); } /// Recalculate dynamic slopes. @@ -161,6 +210,7 @@ void T_DynamicSlopeLine (dynlineplanethink_t* th) if (slope->zdelta != FixedDiv(zdelta, th->extent)) { slope->zdelta = FixedDiv(zdelta, th->extent); slope->zangle = R_PointToAngle2(0, 0, th->extent, -zdelta); + slope->moved = true; P_CalculateSlopeNormal(slope); } } @@ -392,6 +442,7 @@ static void line_SpawnViaLine(const int linenum, const boolean spawnthinker) fslope->xydirection = R_PointToAngle2(origin.x, origin.y, point.x, point.y); P_CalculateSlopeNormal(fslope); + P_CalculateSlopeVectors(fslope); if (spawnthinker && (flags & SL_DYNAMIC)) P_AddDynLineSlopeThinker(fslope, DP_FRONTFLOOR, line, extent); @@ -409,6 +460,7 @@ static void line_SpawnViaLine(const int linenum, const boolean spawnthinker) cslope->xydirection = R_PointToAngle2(origin.x, origin.y, point.x, point.y); P_CalculateSlopeNormal(cslope); + P_CalculateSlopeVectors(cslope); if (spawnthinker && (flags & SL_DYNAMIC)) P_AddDynLineSlopeThinker(cslope, DP_FRONTCEIL, line, extent); @@ -449,6 +501,7 @@ static void line_SpawnViaLine(const int linenum, const boolean spawnthinker) fslope->xydirection = R_PointToAngle2(origin.x, origin.y, point.x, point.y); P_CalculateSlopeNormal(fslope); + P_CalculateSlopeVectors(fslope); if (spawnthinker && (flags & SL_DYNAMIC)) P_AddDynLineSlopeThinker(fslope, DP_BACKFLOOR, line, extent); @@ -466,6 +519,7 @@ static void line_SpawnViaLine(const int linenum, const boolean spawnthinker) cslope->xydirection = R_PointToAngle2(origin.x, origin.y, point.x, point.y); P_CalculateSlopeNormal(cslope); + P_CalculateSlopeVectors(cslope); if (spawnthinker && (flags & SL_DYNAMIC)) P_AddDynLineSlopeThinker(cslope, DP_BACKCEIL, line, extent); @@ -695,7 +749,7 @@ pslope_t *P_SlopeById(UINT16 id) } /// Creates a new slope from equation constants. -pslope_t *MakeViaEquationConstants(const fixed_t a, const fixed_t b, const fixed_t c, const fixed_t d) +pslope_t *P_MakeSlopeViaEquationConstants(const double a, const double b, const double c, const double d) { pslope_t* ret = Slope_Add(0); diff --git a/src/p_slopes.h b/src/p_slopes.h index 096bf8f82a3342494b692a5e16cb6fbde6647438..fdc07f67e257ba05eef35fc481c3be3a0ebdfd7c 100644 --- a/src/p_slopes.h +++ b/src/p_slopes.h @@ -1,6 +1,6 @@ // SONIC ROBO BLAST 2 //----------------------------------------------------------------------------- -// Copyright (C) 2004 by Stephen McGranahan +// Copyright (C) 2009 by Stephen McGranahan. // Copyright (C) 2015-2023 by Sonic Team Junior. // // This program is free software distributed under the @@ -13,7 +13,7 @@ #ifndef P_SLOPES_H__ #define P_SLOPES_H__ -#include "m_fixed.h" // Vectors +#include "m_fixed.h" extern pslope_t *slopelist; extern UINT16 slopecount; @@ -51,6 +51,7 @@ typedef enum void P_LinkSlopeThinkers (void); void P_CalculateSlopeNormal(pslope_t *slope); +void P_CalculateSlopeVectors(pslope_t *slope); void P_InitSlopes(void); void P_SpawnSlopes(const boolean fromsave); @@ -88,7 +89,7 @@ fixed_t P_GetWallTransferMomZ(mobj_t *mo, pslope_t *slope); void P_HandleSlopeLanding(mobj_t *thing, pslope_t *slope); void P_ButteredSlope(mobj_t *mo); -pslope_t *MakeViaEquationConstants(const fixed_t a, const fixed_t b, const fixed_t c, const fixed_t d); +pslope_t *P_MakeSlopeViaEquationConstants(const double a, const double b, const double c, const double d); /// Dynamic plane type enum for the thinker. Will have a different functionality depending on this. typedef enum { diff --git a/src/p_user.c b/src/p_user.c index 7e19e8d6367dbb874c7b21b105dd5f18e6dba9fb..200b7673659eb4f4975cb76723c998da7f3c449c 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -3807,6 +3807,8 @@ static boolean PIT_CheckSolidsTeeter(mobj_t *thing) if (abs(thing->x - teeterer->x) >= blockdist || abs(thing->y - teeterer->y) >= blockdist) return true; // didn't hit it + highesttop = INT32_MIN; + if (teeterer->eflags & MFE_VERTICALFLIP) { if (thingtop < teeterer->z) @@ -4110,13 +4112,8 @@ static void P_DoTeeter(player_t *player) teeteryl = teeteryh = player->mo->y; couldteeter = false; solidteeter = teeter; - for (by = yl; by <= yh; by++) - for (bx = xl; bx <= xh; bx++) - { - highesttop = INT32_MIN; - if (!P_BlockThingsIterator(bx, by, PIT_CheckSolidsTeeter)) - goto teeterdone; // we've found something that stops us teetering at all, how about we stop already - } + if (!P_DoBlockThingsIterate(xl, yl, xh, yh, PIT_CheckSolidsTeeter)) + goto teeterdone; // we've found something that stops us teetering at all teeterdone: teeter = solidteeter; P_SetTarget(&tmthing, oldtmthing); // restore old tmthing, goodness knows what the game does with this before mobj thinkers diff --git a/src/r_defs.h b/src/r_defs.h index 16c660b01938ed86669550f709f7b65336cfaebc..6e0375e615aed5596e68eb67b113fe61a907e477 100644 --- a/src/r_defs.h +++ b/src/r_defs.h @@ -16,6 +16,7 @@ // Some more or less basic data types we depend on. #include "m_fixed.h" +#include "m_vector.h" // We rely on the thinker data struct to handle sound origins in sectors. #include "d_think.h" @@ -343,6 +344,13 @@ typedef struct pslope_s angle_t zangle; /// Precomputed angle of the plane going up from the ground (not measured in degrees). angle_t xydirection;/// Precomputed angle of the normal's projection on the XY plane. + dvector3_t dorigin; + dvector3_t dnormdir; + + double dzdelta; + + boolean moved : 1; + UINT8 flags; // Slope options } pslope_t; diff --git a/src/r_draw.c b/src/r_draw.c index ff2e43df31d9291ed6ae07facf2da0c1beaaa4e1..380bacdf9f565bba613f698cd05d70368a187838 100644 --- a/src/r_draw.c +++ b/src/r_draw.c @@ -113,8 +113,9 @@ UINT8 *ds_source; // points to the start of a flat UINT8 *ds_transmap; // one of the translucency tables // Vectors for Software's tilted slope drawers -floatv3_t ds_su, ds_sv, ds_sz, ds_slopelight; -float focallengthf, zeroheight; +dvector3_t ds_su, ds_sv, ds_sz, ds_slopelight; +double zeroheight; +float focallengthf; /** \brief Variable flat sizes */ @@ -129,7 +130,7 @@ static colorcache_t **translationtablecache[TT_CACHE_SIZE] = {NULL}; boolean skincolor_modified[MAXSKINCOLORS]; -static INT32 SkinToCacheIndex(INT32 translation) +INT32 R_SkinTranslationToCacheIndex(INT32 translation) { switch (translation) { @@ -556,7 +557,7 @@ UINT8* R_GetTranslationColormap(INT32 skinnum, skincolornum_t color, UINT8 flags else if (skinnum <= TC_DEFAULT) { // Do default translation - index = SkinToCacheIndex(skinnum); + index = R_SkinTranslationToCacheIndex(skinnum); } else I_Error("Invalid translation %d", skinnum); diff --git a/src/r_draw.h b/src/r_draw.h index 29370015a1c46cb6405bf5b698d6c23425ee79e2..0af911233bfdb6097c8326bf014be84ee7418cad 100644 --- a/src/r_draw.h +++ b/src/r_draw.h @@ -66,13 +66,10 @@ extern boolean ds_powersoftwo, ds_solidcolor, ds_fog; extern UINT8 *ds_source; extern UINT8 *ds_transmap; -typedef struct { - float x, y, z; -} floatv3_t; - // Vectors for Software's tilted slope drawers -extern floatv3_t ds_su, ds_sv, ds_sz, ds_slopelight; -extern float focallengthf, zeroheight; +extern dvector3_t ds_su, ds_sv, ds_sz, ds_slopelight; +extern double zeroheight; +extern float focallengthf; // Variable flat sizes extern UINT32 nflatxshift; @@ -117,6 +114,8 @@ enum TC_DEFAULT }; +INT32 R_SkinTranslationToCacheIndex(INT32 translation); + // Amount of colors in the palette #define NUM_PALETTE_ENTRIES 256 diff --git a/src/r_fps.c b/src/r_fps.c index de450aaa7f465b8d47891baec585c757de361c60..83fd0eec15f4c9fd0f41fa60104be022b9b4c7eb 100644 --- a/src/r_fps.c +++ b/src/r_fps.c @@ -19,9 +19,9 @@ #include "i_video.h" #include "r_plane.h" #include "p_spec.h" +#include "p_slopes.h" #include "r_state.h" #include "z_zone.h" -#include "console.h" // con_startup_loadprogress #include "m_perfstats.h" // ps_metric_t #ifdef HWRENDER #include "hardware/hw_main.h" // for cv_glshearing @@ -482,6 +482,7 @@ void R_CreateInterpolator_Polyobj(thinker_t *thinker, polyobj_t *polyobj) interp->polyobj.oldcx = interp->polyobj.bakcx = polyobj->centerPt.x; interp->polyobj.oldcy = interp->polyobj.bakcy = polyobj->centerPt.y; + interp->polyobj.oldangle = interp->polyobj.bakangle = polyobj->angle; } void R_CreateInterpolator_DynSlope(thinker_t *thinker, pslope_t *slope) @@ -505,6 +506,15 @@ void R_InitializeLevelInterpolators(void) levelinterpolators = NULL; } +static void RecalculatePolyobjectSegAngles(polyobj_t *polyobj) +{ + for (size_t i = 0; i < polyobj->segCount; i++) + { + seg_t *seg = polyobj->segs[i]; + seg->angle = R_PointToAngle2(seg->v1->x, seg->v1->y, seg->v2->x, seg->v2->y); + } +} + static void UpdateLevelInterpolatorState(levelinterpolator_t *interp) { size_t i; @@ -535,10 +545,13 @@ static void UpdateLevelInterpolatorState(levelinterpolator_t *interp) interp->polyobj.bakvertices[i * 2 ] = interp->polyobj.polyobj->vertices[i]->x; interp->polyobj.bakvertices[i * 2 + 1] = interp->polyobj.polyobj->vertices[i]->y; } + RecalculatePolyobjectSegAngles(interp->polyobj.polyobj); interp->polyobj.oldcx = interp->polyobj.bakcx; interp->polyobj.oldcy = interp->polyobj.bakcy; + interp->polyobj.oldangle = interp->polyobj.bakangle; interp->polyobj.bakcx = interp->polyobj.polyobj->centerPt.x; interp->polyobj.bakcy = interp->polyobj.polyobj->centerPt.y; + interp->polyobj.bakangle = interp->polyobj.polyobj->angle; break; case LVLINTERP_DynSlope: FV3_Copy(&interp->dynslope.oldo, &interp->dynslope.bako); @@ -624,13 +637,16 @@ void R_ApplyLevelInterpolators(fixed_t frac) interp->polyobj.polyobj->vertices[ii]->x = R_LerpFixed(interp->polyobj.oldvertices[ii * 2 ], interp->polyobj.bakvertices[ii * 2 ], frac); interp->polyobj.polyobj->vertices[ii]->y = R_LerpFixed(interp->polyobj.oldvertices[ii * 2 + 1], interp->polyobj.bakvertices[ii * 2 + 1], frac); } + RecalculatePolyobjectSegAngles(interp->polyobj.polyobj); interp->polyobj.polyobj->centerPt.x = R_LerpFixed(interp->polyobj.oldcx, interp->polyobj.bakcx, frac); interp->polyobj.polyobj->centerPt.y = R_LerpFixed(interp->polyobj.oldcy, interp->polyobj.bakcy, frac); + interp->polyobj.polyobj->angle = R_LerpAngle(interp->polyobj.oldangle, interp->polyobj.bakangle, frac); break; case LVLINTERP_DynSlope: R_LerpVector3(&interp->dynslope.oldo, &interp->dynslope.bako, frac, &interp->dynslope.slope->o); R_LerpVector2(&interp->dynslope.oldd, &interp->dynslope.bakd, frac, &interp->dynslope.slope->d); interp->dynslope.slope->zdelta = R_LerpFixed(interp->dynslope.oldzdelta, interp->dynslope.bakzdelta, frac); + interp->dynslope.slope->moved = true; break; } } @@ -679,8 +695,10 @@ void R_RestoreLevelInterpolators(void) interp->polyobj.polyobj->vertices[ii]->x = interp->polyobj.bakvertices[ii * 2 ]; interp->polyobj.polyobj->vertices[ii]->y = interp->polyobj.bakvertices[ii * 2 + 1]; } + RecalculatePolyobjectSegAngles(interp->polyobj.polyobj); interp->polyobj.polyobj->centerPt.x = interp->polyobj.bakcx; interp->polyobj.polyobj->centerPt.y = interp->polyobj.bakcy; + interp->polyobj.polyobj->angle = interp->polyobj.bakangle; break; case LVLINTERP_DynSlope: FV3_Copy(&interp->dynslope.slope->o, &interp->dynslope.bako); diff --git a/src/r_fps.h b/src/r_fps.h index f43d29f300a8a6707a3e4c6f5fa24e1e3f0ea37f..cd40b0a9a572b23b97bb9b2cc493ea6a34a243a7 100644 --- a/src/r_fps.h +++ b/src/r_fps.h @@ -108,6 +108,7 @@ typedef struct levelinterpolator_s { fixed_t *bakvertices; size_t vertices_size; fixed_t oldcx, oldcy, bakcx, bakcy; + angle_t oldangle, bakangle; } polyobj; struct { pslope_t *slope; diff --git a/src/r_plane.c b/src/r_plane.c index 612c650fcb0dd8c66cab3dcfaaefbbe15477b446..11aa6c941ae916cfcd924d0ea55c20709f1f3081 100644 --- a/src/r_plane.c +++ b/src/r_plane.c @@ -84,11 +84,11 @@ fixed_t yslopetab[MAXVIDHEIGHT*16]; fixed_t *yslope; static fixed_t xoffs, yoffs; -static floatv3_t slope_origin, slope_u, slope_v; -static floatv3_t slope_lightu, slope_lightv; +static dvector3_t slope_origin, slope_u, slope_v; +static dvector3_t slope_lightu, slope_lightv; static void CalcSlopePlaneVectors(visplane_t *pl, fixed_t xoff, fixed_t yoff); -static void CalcSlopeLightVectors(pslope_t *slope, fixed_t xpos, fixed_t ypos, fixed_t height, float ang, angle_t plangle); +static void CalcSlopeLightVectors(pslope_t *slope, fixed_t xpos, fixed_t ypos, double height, float ang, angle_t plangle); static void DoSlopeCrossProducts(void); static void DoSlopeLightCrossProduct(void); @@ -660,16 +660,18 @@ static void R_DrawSkyPlane(visplane_t *pl) } } -// Returns the height of the sloped plane at (x, y) as a 32.16 fixed_t -static INT64 R_GetSlopeZAt(const pslope_t *slope, fixed_t x, fixed_t y) +// Returns the height of the sloped plane at (x, y) as a double +static double R_GetSlopeZAt(const pslope_t *slope, fixed_t x, fixed_t y) { - INT64 x64 = ((INT64)x - (INT64)slope->o.x); - INT64 y64 = ((INT64)y - (INT64)slope->o.y); + // If you want to reimplement this using just the equation constants, use this instead: + // (d + a*x + b*y) * -(1.0 / c) - x64 = (x64 * (INT64)slope->d.x) / FRACUNIT; - y64 = (y64 * (INT64)slope->d.y) / FRACUNIT; + double px = FixedToDouble(x) - slope->dorigin.x; + double py = FixedToDouble(y) - slope->dorigin.y; - return (INT64)slope->o.z + ((x64 + y64) * (INT64)slope->zdelta) / FRACUNIT; + double dist = (px * slope->dnormdir.x) + (py * slope->dnormdir.y); + + return slope->dorigin.z + (dist * slope->dzdelta); } // Sets the texture origin vector of the sloped plane. @@ -687,19 +689,25 @@ static void R_SetSlopePlaneOrigin(pslope_t *slope, fixed_t xpos, fixed_t ypos, f // errors if the flat is rotated. slope_origin.x = vxf * cos(ang) - vyf * sin(ang); slope_origin.z = vxf * sin(ang) + vyf * cos(ang); - slope_origin.y = (R_GetSlopeZAt(slope, -xoff, yoff) - zpos) / (float)FRACUNIT; + slope_origin.y = R_GetSlopeZAt(slope, -xoff, yoff) - FixedToDouble(zpos); } // This function calculates all of the vectors necessary for drawing a sloped plane. void R_SetSlopePlane(pslope_t *slope, fixed_t xpos, fixed_t ypos, fixed_t zpos, fixed_t xoff, fixed_t yoff, angle_t angle, angle_t plangle) { // I copied ZDoom's code and adapted it to SRB2... -Red - fixed_t height, z_at_xy; + double height, z_at_xy; float ang; + if (slope->moved) + { + P_CalculateSlopeVectors(slope); + slope->moved = false; + } + R_SetSlopePlaneOrigin(slope, xpos, ypos, zpos, xoff, yoff, angle); - height = P_GetSlopeZAt(slope, xpos, ypos); - zeroheight = FixedToFloat(height - zpos); + height = R_GetSlopeZAt(slope, xpos, ypos); + zeroheight = height - FixedToDouble(zpos); ang = ANG2RAD(ANGLE_180 - (angle + plangle)); @@ -711,19 +719,19 @@ void R_SetSlopePlane(pslope_t *slope, fixed_t xpos, fixed_t ypos, fixed_t zpos, return; } - // slope_v is the v direction vector in view space + // the v direction vector in view space slope_v.x = cos(ang); slope_v.z = sin(ang); - // slope_u is the u direction vector in view space + // the u direction vector in view space slope_u.x = sin(ang); slope_u.z = -cos(ang); plangle >>= ANGLETOFINESHIFT; - z_at_xy = P_GetSlopeZAt(slope, xpos + FINESINE(plangle), ypos + FINECOSINE(plangle)); - slope_v.y = FixedToFloat(z_at_xy - height); - z_at_xy = P_GetSlopeZAt(slope, xpos + FINECOSINE(plangle), ypos - FINESINE(plangle)); - slope_u.y = FixedToFloat(z_at_xy - height); + z_at_xy = R_GetSlopeZAt(slope, xpos + FINESINE(plangle), ypos + FINECOSINE(plangle)); + slope_v.y = z_at_xy - height; + z_at_xy = R_GetSlopeZAt(slope, xpos + FINECOSINE(plangle), ypos - FINESINE(plangle)); + slope_u.y = z_at_xy - height; DoSlopeCrossProducts(); DoSlopeLightCrossProduct(); @@ -732,13 +740,18 @@ void R_SetSlopePlane(pslope_t *slope, fixed_t xpos, fixed_t ypos, fixed_t zpos, // This function calculates all of the vectors necessary for drawing a sloped and scaled plane. void R_SetScaledSlopePlane(pslope_t *slope, fixed_t xpos, fixed_t ypos, fixed_t zpos, fixed_t xs, fixed_t ys, fixed_t xoff, fixed_t yoff, angle_t angle, angle_t plangle) { - fixed_t height, z_at_xy; - + double height, z_at_xy; float ang; + if (slope->moved) + { + P_CalculateSlopeVectors(slope); + slope->moved = false; + } + R_SetSlopePlaneOrigin(slope, xpos, ypos, zpos, xoff, yoff, angle); - height = P_GetSlopeZAt(slope, xpos, ypos); - zeroheight = FixedToFloat(height - zpos); + height = R_GetSlopeZAt(slope, xpos, ypos); + zeroheight = height - FixedToDouble(zpos); ang = ANG2RAD(ANGLE_180 - (angle + plangle)); @@ -753,27 +766,27 @@ void R_SetScaledSlopePlane(pslope_t *slope, fixed_t xpos, fixed_t ypos, fixed_t float xscale = FixedToFloat(xs); float yscale = FixedToFloat(ys); - // m is the v direction vector in view space + // the v direction vector in view space slope_v.x = yscale * cos(ang); slope_v.z = yscale * sin(ang); - // n is the u direction vector in view space + // the u direction vector in view space slope_u.x = xscale * sin(ang); slope_u.z = -xscale * cos(ang); ang = ANG2RAD(plangle); - z_at_xy = P_GetSlopeZAt(slope, xpos + FloatToFixed(yscale * sin(ang)), ypos + FloatToFixed(yscale * cos(ang))); - slope_v.y = FixedToFloat(z_at_xy - height); - z_at_xy = P_GetSlopeZAt(slope, xpos + FloatToFixed(xscale * cos(ang)), ypos - FloatToFixed(xscale * sin(ang))); - slope_u.y = FixedToFloat(z_at_xy - height); + z_at_xy = R_GetSlopeZAt(slope, xpos + FloatToFixed(yscale * sin(ang)), ypos + FloatToFixed(yscale * cos(ang))); + slope_v.y = z_at_xy - height; + z_at_xy = R_GetSlopeZAt(slope, xpos + FloatToFixed(xscale * cos(ang)), ypos - FloatToFixed(xscale * sin(ang))); + slope_u.y = z_at_xy - height; DoSlopeCrossProducts(); DoSlopeLightCrossProduct(); } -static void CalcSlopeLightVectors(pslope_t *slope, fixed_t xpos, fixed_t ypos, fixed_t height, float ang, angle_t plangle) +static void CalcSlopeLightVectors(pslope_t *slope, fixed_t xpos, fixed_t ypos, double height, float ang, angle_t plangle) { - fixed_t z_at_xy; + double z_at_xy; slope_lightv.x = cos(ang); slope_lightv.z = sin(ang); @@ -782,25 +795,17 @@ static void CalcSlopeLightVectors(pslope_t *slope, fixed_t xpos, fixed_t ypos, f slope_lightu.z = -cos(ang); plangle >>= ANGLETOFINESHIFT; - z_at_xy = P_GetSlopeZAt(slope, xpos + FINESINE(plangle), ypos + FINECOSINE(plangle)); - slope_lightv.y = FixedToFloat(z_at_xy - height); - z_at_xy = P_GetSlopeZAt(slope, xpos + FINECOSINE(plangle), ypos - FINESINE(plangle)); - slope_lightu.y = FixedToFloat(z_at_xy - height); + z_at_xy = R_GetSlopeZAt(slope, xpos + FINESINE(plangle), ypos + FINECOSINE(plangle)); + slope_lightv.y = z_at_xy - height; + z_at_xy = R_GetSlopeZAt(slope, xpos + FINECOSINE(plangle), ypos - FINESINE(plangle)); + slope_lightu.y = z_at_xy - height; } -// Eh. I tried making this stuff fixed-point and it exploded on me. Here's a macro for the only floating-point vector function I recall using. -#define CROSS(d, v1, v2) \ -d.x = (v1.y * v2.z) - (v1.z * v2.y);\ -d.y = (v1.z * v2.x) - (v1.x * v2.z);\ -d.z = (v1.x * v2.y) - (v1.y * v2.x) - static void DoSlopeCrossProducts(void) { - float sfmult = 65536.f; - - CROSS(ds_su, slope_origin, slope_v); - CROSS(ds_sv, slope_origin, slope_u); - CROSS(ds_sz, slope_v, slope_u); + DVector3_Cross(&slope_origin, &slope_v, &ds_su); + DVector3_Cross(&slope_origin, &slope_u, &ds_sv); + DVector3_Cross(&slope_v, &slope_u, &ds_sz); ds_su.z *= focallengthf; ds_sv.z *= focallengthf; @@ -810,6 +815,8 @@ static void DoSlopeCrossProducts(void) return; // Premultiply the texture vectors with the scale factors + float sfmult = 65536.f; + if (ds_powersoftwo) sfmult *= 1 << nflatshiftup; @@ -823,13 +830,11 @@ static void DoSlopeCrossProducts(void) static void DoSlopeLightCrossProduct(void) { - CROSS(ds_slopelight, slope_lightv, slope_lightu); + DVector3_Cross(&slope_lightv, &slope_lightu, &ds_slopelight); ds_slopelight.z *= focallengthf; } -#undef CROSS - static void CalcSlopePlaneVectors(visplane_t *pl, fixed_t xoff, fixed_t yoff) { if (!ds_fog && (pl->xscale != FRACUNIT || pl->yscale != FRACUNIT)) diff --git a/src/r_segs.c b/src/r_segs.c index 9340ca50c7cf2e01f4c4a40f65df869d8d72dfc1..7877f1353e5b24739d86a62bb940dbe729d85afc 100644 --- a/src/r_segs.c +++ b/src/r_segs.c @@ -1705,26 +1705,23 @@ void R_StoreWallRange(INT32 start, INT32 stop) // left temp = xtoviewangle[start]+viewangle; -#define FIXED_TO_DOUBLE(x) (((double)(x)) / ((double)FRACUNIT)) -#define DOUBLE_TO_FIXED(x) (fixed_t)((x) * ((double)FRACUNIT)) - { // Both lines can be written in slope-intercept form, so figure out line intersection double a1, b1, c1, a2, b2, c2, det; // 1 is the seg, 2 is the view angle vector... ///TODO: convert to fixed point - a1 = FIXED_TO_DOUBLE(curline->v2->y-curline->v1->y); - b1 = FIXED_TO_DOUBLE(curline->v1->x-curline->v2->x); - c1 = a1*FIXED_TO_DOUBLE(curline->v1->x) + b1*FIXED_TO_DOUBLE(curline->v1->y); + a1 = FixedToDouble(curline->v2->y-curline->v1->y); + b1 = FixedToDouble(curline->v1->x-curline->v2->x); + c1 = a1*FixedToDouble(curline->v1->x) + b1*FixedToDouble(curline->v1->y); - a2 = -FIXED_TO_DOUBLE(FINESINE(temp>>ANGLETOFINESHIFT)); - b2 = FIXED_TO_DOUBLE(FINECOSINE(temp>>ANGLETOFINESHIFT)); - c2 = a2*FIXED_TO_DOUBLE(viewx) + b2*FIXED_TO_DOUBLE(viewy); + a2 = -FixedToDouble(FINESINE(temp>>ANGLETOFINESHIFT)); + b2 = FixedToDouble(FINECOSINE(temp>>ANGLETOFINESHIFT)); + c2 = a2*FixedToDouble(viewx) + b2*FixedToDouble(viewy); det = a1*b2 - a2*b1; - ds_p->leftpos.x = segleft.x = DOUBLE_TO_FIXED((b2*c1 - b1*c2)/det); - ds_p->leftpos.y = segleft.y = DOUBLE_TO_FIXED((a1*c2 - a2*c1)/det); + ds_p->leftpos.x = segleft.x = DoubleToFixed((b2*c1 - b1*c2)/det); + ds_p->leftpos.y = segleft.y = DoubleToFixed((a1*c2 - a2*c1)/det); } // right @@ -1735,26 +1732,21 @@ void R_StoreWallRange(INT32 start, INT32 stop) double a1, b1, c1, a2, b2, c2, det; // 1 is the seg, 2 is the view angle vector... ///TODO: convert to fixed point - a1 = FIXED_TO_DOUBLE(curline->v2->y-curline->v1->y); - b1 = FIXED_TO_DOUBLE(curline->v1->x-curline->v2->x); - c1 = a1*FIXED_TO_DOUBLE(curline->v1->x) + b1*FIXED_TO_DOUBLE(curline->v1->y); + a1 = FixedToDouble(curline->v2->y-curline->v1->y); + b1 = FixedToDouble(curline->v1->x-curline->v2->x); + c1 = a1*FixedToDouble(curline->v1->x) + b1*FixedToDouble(curline->v1->y); - a2 = -FIXED_TO_DOUBLE(FINESINE(temp>>ANGLETOFINESHIFT)); - b2 = FIXED_TO_DOUBLE(FINECOSINE(temp>>ANGLETOFINESHIFT)); - c2 = a2*FIXED_TO_DOUBLE(viewx) + b2*FIXED_TO_DOUBLE(viewy); + a2 = -FixedToDouble(FINESINE(temp>>ANGLETOFINESHIFT)); + b2 = FixedToDouble(FINECOSINE(temp>>ANGLETOFINESHIFT)); + c2 = a2*FixedToDouble(viewx) + b2*FixedToDouble(viewy); det = a1*b2 - a2*b1; - ds_p->rightpos.x = segright.x = DOUBLE_TO_FIXED((b2*c1 - b1*c2)/det); - ds_p->rightpos.y = segright.y = DOUBLE_TO_FIXED((a1*c2 - a2*c1)/det); + ds_p->rightpos.x = segright.x = DoubleToFixed((b2*c1 - b1*c2)/det); + ds_p->rightpos.y = segright.y = DoubleToFixed((a1*c2 - a2*c1)/det); } - -#undef FIXED_TO_DOUBLE -#undef DOUBLE_TO_FIXED - } - #define SLOPEPARAMS(slope, end1, end2, normalheight) \ end1 = P_GetZAt(slope, segleft.x, segleft.y, normalheight); \ end2 = P_GetZAt(slope, segright.x, segright.y, normalheight); diff --git a/src/r_things.c b/src/r_things.c index 03e94d9fc32737c41a555f003f9bf28476c7f9f5..07420e34ad284e9dea9998e443baa37613e071a8 100644 --- a/src/r_things.c +++ b/src/r_things.c @@ -770,6 +770,22 @@ UINT8 *R_GetTranslationForThing(mobj_t *mobj, skincolornum_t color, UINT16 trans if (is_player) // This thing is a player! skinnum = ((skin_t*)mobj->skin)->skinnum; + if (color != SKINCOLOR_NONE) + { + // New colormap stuff for skins Tails 06-07-2002 + if (mobj->colorized) + skinnum = TC_RAINBOW; + else if (mobj->player && mobj->player->dashmode >= DASHMODE_THRESHOLD + && (mobj->player->charflags & SF_DASHMODE) + && ((leveltime/2) & 1)) + { + if (mobj->player->charflags & SF_MACHINE) + skinnum = TC_DASHMODE; + else + skinnum = TC_RAINBOW; + } + } + if (R_ThingIsFlashing(mobj)) // Bosses "flash" { if (mobj->type == MT_CYBRAKDEMON || mobj->colorized) @@ -786,22 +802,7 @@ UINT8 *R_GetTranslationForThing(mobj_t *mobj, skincolornum_t color, UINT16 trans return tr; } else if (color != SKINCOLOR_NONE) - { - // New colormap stuff for skins Tails 06-07-2002 - if (mobj->colorized) - return R_GetTranslationColormap(TC_RAINBOW, color, GTC_CACHE); - else if (mobj->player && mobj->player->dashmode >= DASHMODE_THRESHOLD - && (mobj->player->charflags & SF_DASHMODE) - && ((leveltime/2) & 1)) - { - if (mobj->player->charflags & SF_MACHINE) - return R_GetTranslationColormap(TC_DASHMODE, 0, GTC_CACHE); - else - return R_GetTranslationColormap(TC_RAINBOW, color, GTC_CACHE); - } - else - return R_GetTranslationColormap(skinnum, color, GTC_CACHE); - } + return R_GetTranslationColormap(skinnum, color, GTC_CACHE); else if (mobj->sprite == SPR_PLAY) // Looks like a player, but doesn't have a color? Get rid of green sonic syndrome. return R_GetTranslationColormap(TC_DEFAULT, SKINCOLOR_BLUE, GTC_CACHE); diff --git a/src/r_translation.c b/src/r_translation.c index 7e1e30d0cdec7afd2d810f6b8d1a6b26da0c42bb..3b123f14ca1732e2e9605dad3153fd5f6d0e2338 100644 --- a/src/r_translation.c +++ b/src/r_translation.c @@ -1126,17 +1126,19 @@ UINT8 *R_GetTranslationRemap(int id, skincolornum_t skincolor, INT32 skinnum) if (!tr->skincolor_remaps) Z_Calloc(sizeof(*tr->skincolor_remaps) * TT_CACHE_SIZE, PU_LEVEL, &tr->skincolor_remaps); - if (!tr->skincolor_remaps[skinnum]) - tr->skincolor_remaps[skinnum] = Z_Calloc(NUM_PALETTE_ENTRIES * MAXSKINCOLORS, PU_LEVEL, NULL); + INT32 index = R_SkinTranslationToCacheIndex(skinnum); - colorcache_t *cache = tr->skincolor_remaps[skinnum][skincolor]; + if (!tr->skincolor_remaps[index]) + tr->skincolor_remaps[index] = Z_Calloc(NUM_PALETTE_ENTRIES * (MAXSKINCOLORS - 1), PU_LEVEL, NULL); + + colorcache_t *cache = tr->skincolor_remaps[index][skincolor - 1]; if (!cache) { cache = Z_Calloc(sizeof(colorcache_t), PU_LEVEL, NULL); R_ApplyTranslationRemap(tr, cache->colors, skincolor, skinnum); - tr->skincolor_remaps[skinnum][skincolor] = cache; + tr->skincolor_remaps[index][skincolor - 1] = cache; } return cache->colors; diff --git a/src/sdl/Srb2SDL-vc10.vcxproj b/src/sdl/Srb2SDL-vc10.vcxproj index d282b3b4a1caee6d39a8746b6523d88b7443fadf..7237c491e8c3353e8216ec9478aa652e1d58e40b 100644 --- a/src/sdl/Srb2SDL-vc10.vcxproj +++ b/src/sdl/Srb2SDL-vc10.vcxproj @@ -295,6 +295,7 @@ <ClInclude Include="..\m_tokenizer.h" /> <ClInclude Include="..\m_perfstats.h" /> <ClInclude Include="..\m_queue.h" /> + <ClInclude Include="..\m_vector.h" /> <ClInclude Include="..\m_random.h" /> <ClInclude Include="..\m_swap.h" /> <ClInclude Include="..\netcode\client_connection.h" /> @@ -474,6 +475,7 @@ <ClCompile Include="..\m_tokenizer.c" /> <ClCompile Include="..\m_perfstats.c" /> <ClCompile Include="..\m_queue.c" /> + <ClCompile Include="..\m_vector.c" /> <ClCompile Include="..\m_random.c" /> <ClCompile Include="..\netcode\client_connection.c" /> <ClCompile Include="..\netcode\commands.c" /> diff --git a/src/sdl/Srb2SDL-vc10.vcxproj.filters b/src/sdl/Srb2SDL-vc10.vcxproj.filters index 4a2a5d5950924a079e2141359c96e3c88baa84c0..ecaaa5d2e2568f81eacfc25de71449f82af41bc4 100644 --- a/src/sdl/Srb2SDL-vc10.vcxproj.filters +++ b/src/sdl/Srb2SDL-vc10.vcxproj.filters @@ -348,6 +348,9 @@ <ClInclude Include="..\m_queue.h"> <Filter>M_Misc</Filter> </ClInclude> + <ClInclude Include="..\m_vector.h"> + <Filter>M_Misc</Filter> + </ClInclude> <ClInclude Include="..\m_random.h"> <Filter>M_Misc</Filter> </ClInclude> @@ -849,6 +852,9 @@ <ClCompile Include="..\m_queue.c"> <Filter>M_Misc</Filter> </ClCompile> + <ClCompile Include="..\m_vector.c"> + <Filter>M_Misc</Filter> + </ClCompile> <ClCompile Include="..\m_random.c"> <Filter>M_Misc</Filter> </ClCompile> diff --git a/src/st_stuff.c b/src/st_stuff.c index 378d3ba7d0dee09aff27902d61646c880b43aad9..f3a73ce2616186574f3b390ce96dd79b7f0f717f 100644 --- a/src/st_stuff.c +++ b/src/st_stuff.c @@ -2113,7 +2113,7 @@ static void ST_drawNiGHTSHUD(void) V_DrawString(160 + numbersize + 8, 24, V_SNAPTOTOP|((realnightstime < 10) ? V_REDMAP : V_YELLOWMAP), va("%02d", G_TicsToCentiseconds(stplyr->nightstime))); } - if (oldspecialstage) + if (oldspecialstage && LUA_HudEnabled(hud_nightsrecords)) { if (leveltime < 5*TICRATE) { diff --git a/src/tables.c b/src/tables.c index 315fe1d7a81717974e51538e6fc3fcd7210ab84b..d0fb428ba8909653cc7f1d340caa9590f1373391 100644 --- a/src/tables.c +++ b/src/tables.c @@ -3,6 +3,7 @@ // Copyright (C) 1993-1996 by id Software, Inc. // Copyright (C) 1998-2000 by DooM Legacy Team. // Copyright (C) 1999-2023 by Sonic Team Junior. +// Copyright (C) 2009 by Stephen McGranahan. // // This program is free software distributed under the // terms of the GNU General Public License, version 2.