Merge remote-tracking branch 'public/master'

This commit is contained in:
Mitchell Richters 2021-08-23 19:56:59 +10:00
commit c1937e459b
293 changed files with 5241 additions and 2041 deletions

View file

@ -51,7 +51,7 @@ enum
MAXSTATUS = 1024,
// Maximum number of component tiles in a multi-psky:
MAXPSKYTILES = 16,
MAXSPRITESONSCREEN = 2560,
MAXSPRITESONSCREEN = MAXSPRITES >> 2,
MAXUNIQHUDID = 256, //Extra slots so HUD models can store animation state without messing game sprites
TSPR_TEMP = 99,

View file

@ -124,7 +124,7 @@ static void Shape2D_Clear(DShape2D* self, int which)
if (which & C_Verts) self->mVertices.Clear();
if (which & C_Coords) self->mCoords.Clear();
if (which & C_Indices) self->mIndices.Clear();
self->needsVertexUpload = true;
self->bufferInfo->needsVertexUpload = true;
}
DEFINE_ACTION_FUNCTION_NATIVE(DShape2D, Clear, Shape2D_Clear)
@ -138,7 +138,7 @@ DEFINE_ACTION_FUNCTION_NATIVE(DShape2D, Clear, Shape2D_Clear)
static void Shape2D_PushVertex(DShape2D* self, double x, double y)
{
self->mVertices.Push(DVector2(x, y));
self->needsVertexUpload = true;
self->bufferInfo->needsVertexUpload = true;
}
DEFINE_ACTION_FUNCTION_NATIVE(DShape2D, PushVertex, Shape2D_PushVertex)
@ -153,7 +153,7 @@ DEFINE_ACTION_FUNCTION_NATIVE(DShape2D, PushVertex, Shape2D_PushVertex)
static void Shape2D_PushCoord(DShape2D* self, double u, double v)
{
self->mCoords.Push(DVector2(u, v));
self->needsVertexUpload = true;
self->bufferInfo->needsVertexUpload = true;
}
DEFINE_ACTION_FUNCTION_NATIVE(DShape2D, PushCoord, Shape2D_PushCoord)
@ -170,7 +170,7 @@ static void Shape2D_PushTriangle(DShape2D* self, int a, int b, int c)
self->mIndices.Push(a);
self->mIndices.Push(b);
self->mIndices.Push(c);
self->needsVertexUpload = true;
self->bufferInfo->needsVertexUpload = true;
}
DEFINE_ACTION_FUNCTION_NATIVE(DShape2D, PushTriangle, Shape2D_PushTriangle)
@ -528,13 +528,15 @@ void F2DDrawer::AddTexture(FGameTexture* img, DrawParms& parms)
offset = osave;
}
static TArray<RefCountedPtr<DShape2DBufferInfo>> buffersToDestroy;
void DShape2D::OnDestroy() {
if (lastParms) delete lastParms;
lastParms = nullptr;
mIndices.Reset();
mVertices.Reset();
mCoords.Reset();
buffers.Reset();
buffersToDestroy.Push(std::move(bufferInfo));
}
//==========================================================================
@ -567,11 +569,11 @@ void F2DDrawer::AddShape(FGameTexture* img, DShape2D* shape, DrawParms& parms)
shape->lastParms = new DrawParms(parms);
}
else if (shape->lastParms->vertexColorChange(parms)) {
shape->needsVertexUpload = true;
if (!shape->uploadedOnce) {
shape->bufIndex = -1;
shape->buffers.Clear();
shape->lastCommand = -1;
shape->bufferInfo->needsVertexUpload = true;
if (!shape->bufferInfo->uploadedOnce) {
shape->bufferInfo->bufIndex = -1;
shape->bufferInfo->buffers.Clear();
shape->bufferInfo->lastCommand = -1;
}
delete shape->lastParms;
shape->lastParms = new DrawParms(parms);
@ -583,7 +585,7 @@ void F2DDrawer::AddShape(FGameTexture* img, DShape2D* shape, DrawParms& parms)
auto osave = offset;
if (parms.nooffset) offset = { 0,0 };
if (shape->needsVertexUpload)
if (shape->bufferInfo->needsVertexUpload)
{
shape->minx = 16383;
shape->miny = 16383;
@ -622,15 +624,15 @@ void F2DDrawer::AddShape(FGameTexture* img, DShape2D* shape, DrawParms& parms)
dg.transform = shape->transform;
dg.transform.Cells[0][2] += offset.X;
dg.transform.Cells[1][2] += offset.Y;
dg.shape2D = shape;
dg.shape2DBufInfo = shape->bufferInfo;
dg.shape2DIndexCount = shape->mIndices.Size();
if (shape->needsVertexUpload)
if (shape->bufferInfo->needsVertexUpload)
{
shape->bufIndex += 1;
shape->bufferInfo->bufIndex += 1;
shape->buffers.Reserve(1);
shape->bufferInfo->buffers.Reserve(1);
auto buf = &shape->buffers[shape->bufIndex];
auto buf = &shape->bufferInfo->buffers[shape->bufferInfo->bufIndex];
auto verts = TArray<TwoDVertex>(dg.mVertCount, true);
for ( int i=0; i<dg.mVertCount; i++ )
@ -649,12 +651,12 @@ void F2DDrawer::AddShape(FGameTexture* img, DShape2D* shape, DrawParms& parms)
}
buf->UploadData(&verts[0], dg.mVertCount, &shape->mIndices[0], shape->mIndices.Size());
shape->needsVertexUpload = false;
shape->uploadedOnce = true;
shape->bufferInfo->needsVertexUpload = false;
shape->bufferInfo->uploadedOnce = true;
}
dg.shape2DBufIndex = shape->bufIndex;
shape->lastCommand += 1;
dg.shape2DCommandCounter = shape->lastCommand;
dg.shape2DBufIndex = shape->bufferInfo->bufIndex;
shape->bufferInfo->lastCommand += 1;
dg.shape2DCommandCounter = shape->bufferInfo->lastCommand;
AddCommand(&dg);
offset = osave;
}
@ -1082,6 +1084,17 @@ void F2DDrawer::Clear()
screenFade = 1.f;
}
//==========================================================================
//
//
//
//==========================================================================
void F2DDrawer::OnFrameDone()
{
buffersToDestroy.Clear();
}
F2DVertexBuffer::F2DVertexBuffer()
{
mVertexBuffer = screen->CreateVertexBuffer();

View file

@ -7,6 +7,7 @@
#include "textures.h"
#include "renderstyle.h"
#include "dobject.h"
#include "refcounted.h"
struct DrawParms;
struct FColormap;
@ -49,6 +50,7 @@ struct F2DPolygons
};
class DShape2D;
struct DShape2DBufferInfo;
class F2DDrawer
{
@ -123,7 +125,7 @@ public:
bool useTransform;
DMatrix3x3 transform;
DShape2D* shape2D;
RefCountedPtr<DShape2DBufferInfo> shape2DBufInfo;
int shape2DBufIndex;
int shape2DIndexCount;
int shape2DCommandCounter;
@ -136,7 +138,7 @@ public:
// If these fields match, two draw commands can be batched.
bool isCompatible(const RenderCommand &other) const
{
if (shape2D != nullptr || other.shape2D != nullptr) return false;
if (shape2DBufInfo != nullptr || other.shape2DBufInfo != nullptr) return false;
return mTexture == other.mTexture &&
mType == other.mType &&
mTranslationId == other.mTranslationId &&
@ -214,6 +216,7 @@ public:
void Begin(int w, int h) { isIn2D = true; Width = w; Height = h; }
void End() { isIn2D = false; }
bool HasBegun2D() { return isIn2D; }
void OnFrameDone();
void ClearClipRect() { clipleft = cliptop = 0; clipwidth = clipheight = -1; }
void SetClipRect(int x, int y, int w, int h);
@ -240,12 +243,22 @@ public:
bool mIsFirstPass = true;
};
struct DShape2DBufferInfo : NoVirtualRefCountedBase
{
TArray<F2DVertexBuffer> buffers;
bool needsVertexUpload = true;
int bufIndex = -1;
int lastCommand = -1;
bool uploadedOnce = false;
};
class DShape2D : public DObject
{
DECLARE_CLASS(DShape2D,DObject)
public:
DShape2D()
: bufferInfo(new DShape2DBufferInfo)
{
transform.Identity();
}
@ -261,12 +274,8 @@ public:
DMatrix3x3 transform;
TArray<F2DVertexBuffer> buffers;
bool needsVertexUpload = true;
int bufIndex = -1;
int lastCommand = -1;
RefCountedPtr<DShape2DBufferInfo> bufferInfo;
bool uploadedOnce = false;
DrawParms* lastParms;
void OnDestroy() override;

View file

@ -859,7 +859,7 @@ void S_StopMusic (bool force)
CCMD (changemus)
{
if (!nomusic)
if (MusicEnabled())
{
if (argv.argc() > 1)
{

View file

@ -450,7 +450,7 @@ SoundHandle SoundRenderer::LoadSoundVoc(uint8_t *sfxdata, int length)
}
// Second pass to write the data
if (okay)
if (okay && len > 0)
{
data = new uint8_t[len];
i = 26;

View file

@ -291,7 +291,7 @@ void FCommandBuffer::AddString(FString clip)
if (clip.IsNotEmpty())
{
// Only paste the first line.
long brk = clip.IndexOfAny("\r\n\b");
auto brk = clip.IndexOfAny("\r\n\b");
std::u32string build;
if (brk >= 0)
{

View file

@ -839,11 +839,11 @@ int FColorCVar::ToInt2 (UCVarValue value, ECVarType type)
if (string.IsNotEmpty())
{
ret = V_GetColorFromString (NULL, string);
ret = V_GetColorFromString (string);
}
else
{
ret = V_GetColorFromString (NULL, value.String);
ret = V_GetColorFromString (value.String);
}
}
else

View file

@ -533,7 +533,7 @@ FString BuildString (int argc, FString *argv)
else if (strchr(argv[arg], '"'))
{ // If it contains one or more quotes, we need to escape them.
buf << '"';
long substr_start = 0, quotepos;
ptrdiff_t substr_start = 0, quotepos;
while ((quotepos = argv[arg].IndexOf('"', substr_start)) >= 0)
{
if (substr_start < quotepos)

View file

@ -51,6 +51,7 @@
#include "s_music.h"
#include "m_argv.h"
#include "i_interface.h"
#include "gamecontrol.h"
CVAR(Bool, inter_subtitles, false, CVAR_ARCHIVE | CVAR_GLOBALCONFIG);

View file

@ -51,7 +51,7 @@ extern uint8_t IcePalette[16][3];
//
//----------------------------------------------------------------------------
void PaletteContainer::Init(int numslots) // This cannot be a constructor!!!
void PaletteContainer::Init(int numslots, const uint8_t* indexmap) // This cannot be a constructor!!!
{
if (numslots < 1) numslots = 1;
Clear();
@ -63,6 +63,7 @@ void PaletteContainer::Init(int numslots) // This cannot be a constructor!!!
TranslationTables.Resize(numslots);
StoreTranslation(0, &remap); // make sure that translation ID 0 is the identity.
ColorMatcher.SetPalette(BaseColors);
ColorMatcher.SetIndexMap(indexmap);
}
void PaletteContainer::SetPalette(const uint8_t* colors, int transparent_index)

View file

@ -116,7 +116,7 @@ private:
FMemArena remapArena;
TArray<TAutoGrowArray<FRemapTablePtr, FRemapTable*>> TranslationTables;
public:
void Init(int numslots); // This cannot be a constructor!!!
void Init(int numslots, const uint8_t *indexmap); // This cannot be a constructor!!!
void SetPalette(const uint8_t* colors, int transparent_index = -1);
void Clear();
int DetermineTranslucency(FileReader& file);

View file

@ -435,7 +435,7 @@ void FStringTable::InsertString(int lumpnum, int langid, FName label, const FStr
{
const char *strlangid = (const char *)&langid;
TableElement te = { fileSystem.GetFileContainer(lumpnum), { string, string, string, string } };
long index;
ptrdiff_t index;
while ((index = te.strings[0].IndexOf("@[")) >= 0)
{
auto endindex = te.strings[0].IndexOf(']', index);

View file

@ -208,8 +208,9 @@ bool FZipFile::Open(bool quiet, LumpFilterInfo* filter)
char *dirptr = (char*)directory;
FZipLump *lump_p = Lumps;
FString name0;
FString name0, name1;
bool foundspeciallump = false;
bool foundprefix = false;
// Check if all files have the same prefix so that this can be stripped out.
// This will only be done if there is either a MAPINFO, ZMAPINFO or GAMEINFO lump in the subdirectory, denoting a ZDoom mod.
@ -233,32 +234,42 @@ bool FZipFile::Open(bool quiet, LumpFilterInfo* filter)
}
name.ToLower();
if (name.IndexOf("filter/") == 0)
continue; // 'filter' is a reserved name of the file system.
if (name.IndexOf("__macosx") == 0)
continue; // skip Apple garbage. At this stage only the root folder matters,
if (i == 0)
continue; // skip Apple garbage. At this stage only the root folder matters.
if (!foundprefix)
{
// check for special names, if one of these gets found this must be treated as a normal zip.
bool isspecial = name.IndexOf("/") < 0 || (filter && filter->reservedFolders.Find(name) < filter->reservedFolders.Size());
if (isspecial) break;
name0 = name.Left(name.LastIndexOf("/")+1);
name1 = name.Left(name.IndexOf("/") + 1);
foundprefix = true;
}
else
if (name.IndexOf(name0) != 0)
{
if (name.IndexOf(name0) != 0)
if (name1.IsNotEmpty())
{
name0 = "";
break;
name0 = name1;
if (name.IndexOf(name0) != 0)
{
name0 = "";
}
}
else if (!foundspeciallump && filter)
{
// at least one of the more common definition lumps must be present.
for (auto &p : filter->requiredPrefixes)
{
if (name.IndexOf(name0 + p) == 0 || name.LastIndexOf(p) == name.Len() - strlen(p))
{
foundspeciallump = true;
break;
}
if (name0.IsEmpty())
break;
}
if (!foundspeciallump && filter)
{
// at least one of the more common definition lumps must be present.
for (auto &p : filter->requiredPrefixes)
{
if (name.IndexOf(name0 + p) == 0 || name.LastIndexOf(p) == name.Len() - strlen(p))
{
foundspeciallump = true;
break;
}
}
}

View file

@ -113,7 +113,20 @@ struct FileSystem::LumpRecord
if (Namespace == ns_hidden) shortName.qword = 0;
else
{
long slash = longName.LastIndexOf('/');
ptrdiff_t encodedResID = longName.LastIndexOf(".{");
if (resourceId == -1 && encodedResID >= 0)
{
const char* p = longName.GetChars() + encodedResID;
char* q;
int id = (int)strtoull(p+2, &q, 10); // only decimal numbers allowed here.
if (q[0] == '}' && (q[1] == '.' || q[1] == 0))
{
FString toDelete(p, q - p + 1);
longName.Substitute(toDelete, "");
resourceId = id;
}
}
ptrdiff_t slash = longName.LastIndexOf('/');
FString base = (slash >= 0) ? longName.Mid(slash + 1) : longName;
auto dot = base.LastIndexOf('.');
if (dot >= 0) base.Truncate(dot);
@ -899,6 +912,12 @@ LumpShortName& FileSystem::GetShortName(int i)
return FileInfo[i].shortName;
}
FString& FileSystem::GetLongName(int i)
{
if ((unsigned)i >= NumEntries) I_Error("GetLongName: Invalid index");
return FileInfo[i].longName;
}
void FileSystem::RenameFile(int num, const char* newfn)
{
if ((unsigned)num >= NumEntries) I_Error("RenameFile: Invalid index");
@ -1533,7 +1552,7 @@ bool FileSystem::CreatePathlessCopy(const char *name, int id, int /*flags*/)
if (lump < 0) return false; // Does not exist.
auto oldlump = FileInfo[lump];
int slash = oldlump.longName.LastIndexOf('/');
ptrdiff_t slash = oldlump.longName.LastIndexOf('/');
if (slash == -1)
{

View file

@ -117,6 +117,7 @@ public:
}
LumpShortName& GetShortName(int i); // may only be called before the hash chains are set up.
FString& GetLongName(int i); // may only be called before the hash chains are set up.
void RenameFile(int num, const char* fn);
bool CreatePathlessCopy(const char* name, int id, int flags);

View file

@ -348,8 +348,8 @@ void FResourceFile::PostProcessArchive(void *lumps, size_t lumpsize, LumpFilterI
uint32_t max = NumLumps;
max -= FilterLumpsByGameType(filter, lumps, lumpsize, max);
long len;
int lastpos = -1;
ptrdiff_t len;
ptrdiff_t lastpos = -1;
FString file;
FString LumpFilter = filter->dotFilter;
while ((len = LumpFilter.IndexOf('.', lastpos+1)) > 0)

View file

@ -437,7 +437,7 @@ void FFont::ReadSheetFont(TArray<FolderEntry> &folderdata, int width, int height
Chars[i].OriginalPic->CopySize(*lump, true);
if (Chars[i].OriginalPic != *lump) TexMan.AddGameTexture(Chars[i].OriginalPic);
}
Chars[i].XMove = width;
Chars[i].XMove = int(width / Scale.X);
}
if (map1252)

View file

@ -407,6 +407,7 @@ void FSingleLumpFont::LoadBMF(int lump, const uint8_t *data)
count = LastChar - FirstChar + 1;
Chars.Resize(count);
// BMF palettes are only six bits per component. Fix that.
Palette[0] = 0;
for (i = 0; i < ActiveColors; ++i)
{
int r = (data[17 + i * 3] << 2) | (data[17 + i * 3] >> 4);

View file

@ -415,19 +415,19 @@ void V_InitFontColors ()
else if (sc.Compare ("Flat:"))
{
sc.MustGetString();
logcolor = V_GetColor (nullptr, sc);
logcolor = V_GetColor (sc);
}
else
{
// Get first color
c = V_GetColor (nullptr, sc);
c = V_GetColor (sc);
tparm.Start[0] = RPART(c);
tparm.Start[1] = GPART(c);
tparm.Start[2] = BPART(c);
// Get second color
sc.MustGetString();
c = V_GetColor (nullptr, sc);
c = V_GetColor (sc);
tparm.End[0] = RPART(c);
tparm.End[1] = GPART(c);
tparm.End[2] = BPART(c);
@ -681,13 +681,13 @@ void V_ApplyLuminosityTranslation(int translation, uint8_t* pixel, int size)
int index = clamp(lumadjust, 0, 255);
PalEntry newcol = remap[index];
// extend the range if we find colors outside what initial analysis provided.
if (gray < lum_min)
if (gray < lum_min && lum_min != 0)
{
newcol.r = newcol.r * gray / lum_min;
newcol.g = newcol.g * gray / lum_min;
newcol.b = newcol.b * gray / lum_min;
}
else if (gray > lum_max)
else if (gray > lum_max && lum_max != 0)
{
newcol.r = clamp(newcol.r * gray / lum_max, 0, 255);
newcol.g = clamp(newcol.g * gray / lum_max, 0, 255);

View file

@ -88,7 +88,7 @@ TArray<FBrokenLines> V_BreakLines (FFont *font, int maxwidth, const uint8_t *str
{
if (*string == '[')
{
const uint8_t *start = string;
const uint8_t* start = string;
while (*string != ']' && *string != '\0')
{
string++;
@ -97,11 +97,6 @@ TArray<FBrokenLines> V_BreakLines (FFont *font, int maxwidth, const uint8_t *str
{
string++;
}
lastcolor = FString((const char *)start, string - start);
}
else
{
lastcolor = *string++;
}
}
continue;
@ -130,6 +125,33 @@ TArray<FBrokenLines> V_BreakLines (FFont *font, int maxwidth, const uint8_t *str
}
auto index = Lines.Reserve(1);
for (const uint8_t* pos = start; pos < space; pos++)
{
if (*pos == TEXTCOLOR_ESCAPE)
{
pos++;
if (*pos)
{
if (*pos == '[')
{
const uint8_t* cstart = pos;
while (*pos != ']' && *pos != '\0')
{
pos++;
}
if (*pos != '\0')
{
pos++;
}
lastcolor = FString((const char*)cstart, pos - start);
}
else
{
lastcolor = *pos++;
}
}
}
}
breakit (&Lines[index], font, start, space, linecolor);
if (c == '\n' && !preservecolor)
{

View file

@ -1036,6 +1036,7 @@ DEFINE_FIELD(DListMenuDescriptor, mFont)
DEFINE_FIELD(DListMenuDescriptor, mFontColor)
DEFINE_FIELD(DListMenuDescriptor, mFontColor2)
DEFINE_FIELD(DListMenuDescriptor, mAnimatedTransition)
DEFINE_FIELD(DListMenuDescriptor, mAnimated)
DEFINE_FIELD(DListMenuDescriptor, mCenter)
DEFINE_FIELD(DListMenuDescriptor, mVirtWidth)
DEFINE_FIELD(DListMenuDescriptor, mVirtHeight)
@ -1066,6 +1067,7 @@ DEFINE_FIELD(DImageScrollerDescriptor, textBackgroundBrightness)
DEFINE_FIELD(DImageScrollerDescriptor,textFont)
DEFINE_FIELD(DImageScrollerDescriptor, textScale)
DEFINE_FIELD(DImageScrollerDescriptor, mAnimatedTransition)
DEFINE_FIELD(DImageScrollerDescriptor, mAnimated)
DEFINE_FIELD(DImageScrollerDescriptor, virtWidth)
DEFINE_FIELD(DImageScrollerDescriptor, virtHeight)

View file

@ -141,6 +141,7 @@ public:
FFont *textFont;
double textScale;
bool mAnimatedTransition;
bool mAnimated;
int virtWidth, virtHeight;
};

View file

@ -344,6 +344,10 @@ static void DoParseListMenuBody(FScanner &sc, DListMenuDescriptor *desc, bool &s
{
desc->mAnimatedTransition = true;
}
else if (sc.Compare("animated"))
{
desc->mAnimated = true;
}
else if (sc.Compare("MouseWindow"))
{
sc.MustGetNumber();
@ -459,7 +463,7 @@ static void DoParseListMenuBody(FScanner &sc, DListMenuDescriptor *desc, bool &s
}
else if (args[i] == TypeColor)
{
params.Push(V_GetColor(nullptr, sc));
params.Push(V_GetColor(sc));
}
else if (args[i] == TypeFont)
{
@ -1028,7 +1032,7 @@ static void ParseOptionMenuBody(FScanner &sc, DOptionMenuDescriptor *desc, int i
}
else if (args[i] == TypeColor)
{
params.Push(V_GetColor(nullptr, sc));
params.Push(V_GetColor(sc));
}
else if (args[i]->isIntCompatible())
{
@ -1212,6 +1216,10 @@ static void ParseImageScrollerBody(FScanner& sc, DImageScrollerDescriptor* desc)
{
desc->mAnimatedTransition = true;
}
else if (sc.Compare("animated"))
{
desc->mAnimated = true;
}
else if (sc.Compare("textBackground"))
{
sc.MustGetString();
@ -1274,7 +1282,7 @@ static void ParseImageScrollerBody(FScanner& sc, DImageScrollerDescriptor* desc)
}
else if (args[i] == TypeColor)
{
params.Push(V_GetColor(nullptr, sc));
params.Push(V_GetColor(sc));
}
else if (args[i]->isIntCompatible())
{

View file

@ -90,8 +90,8 @@ static int FindGFXFile(FString & fn)
if (lump != -1) return lump;
int best = -1;
int dot = fn.LastIndexOf('.');
int slash = fn.LastIndexOf('/');
auto dot = fn.LastIndexOf('.');
auto slash = fn.LastIndexOf('/');
if (dot > slash) fn.Truncate(dot);
static const char * extensions[] = { ".png", ".jpg", ".tga", ".pcx", nullptr };

View file

@ -44,8 +44,8 @@ bool FOBJModel::Load(const char* fn, int lumpnum, const char* buffer, int length
{
// Ensure usemtl statements remain intact
TArray<FString> mtlUsages;
TArray<long> mtlUsageIdxs;
long bpos = 0, nlpos = 0, slashpos = 0;
TArray<ptrdiff_t> mtlUsageIdxs;
ptrdiff_t bpos = 0, nlpos = 0, slashpos = 0;
while (1)
{
bpos = objBuf.IndexOf("\nusemtl", bpos);
@ -58,7 +58,7 @@ bool FOBJModel::Load(const char* fn, int lumpnum, const char* buffer, int length
}
if (nlpos == -1)
{
nlpos = (long)objBuf.Len();
nlpos = objBuf.Len();
}
FString lineStr(objBuf.GetChars() + bpos, nlpos - bpos);
mtlUsages.Push(lineStr);
@ -76,7 +76,7 @@ bool FOBJModel::Load(const char* fn, int lumpnum, const char* buffer, int length
nlpos = objBuf.IndexOf('\n', bpos);
if (nlpos == -1)
{
nlpos = (long)objBuf.Len();
nlpos = objBuf.Len();
}
memcpy(wObjBuf + bpos, mtlUsages[i].GetChars(), nlpos - bpos);
}

View file

@ -74,7 +74,7 @@ static uint8_t *GetVoxelRemap(const uint8_t *pal)
{
// The voxel palette uses VGA colors, so we have to expand it
// from 6 to 8 bits per component.
remap[i] = BestColor((uint32_t *)GPalette.BaseColors,
remap[i] = ColorMatcher.Pick(
(oldpal[i*3 + 0] << 2) | (oldpal[i*3 + 0] >> 4),
(oldpal[i*3 + 1] << 2) | (oldpal[i*3 + 1] >> 4),
(oldpal[i*3 + 2] << 2) | (oldpal[i*3 + 2] >> 4));

View file

@ -131,7 +131,7 @@ static bool ReadSystemVersionFromPlist(NSOperatingSystemVersion& version)
if (const char *patchVersionString = strstr(minorVersionString, "."))
{
patchVersionString++;
plistVersion.patchVersion = atoi(minorVersionString);
plistVersion.patchVersion = atoi(patchVersionString);
}
}
}

View file

@ -90,7 +90,7 @@ void Mac_I_FatalError(const char* errortext);
void Unix_I_FatalError(const char* errortext)
{
// Close window or exit fullscreen and release mouse capture
SDL_Quit();
SDL_QuitSubSystem(SDL_INIT_VIDEO);
const char *str;
if((str=getenv("KDE_FULL_SESSION")) && strcmp(str, "true") == 0)
@ -204,7 +204,7 @@ void I_PrintStr(const char *cp)
{ // gray
if (v < 0.33) attrib = 0x8;
else if (v < 0.90) attrib = 0x7;
else attrib = 0x15;
else attrib = 0xF;
}
printData.AppendFormat("\033[%um",((attrib & 0x8) ? 90 : 30) + (attrib & 0x7));

View file

@ -116,8 +116,6 @@ CCMD(vid_list_sdl_render_drivers)
namespace Priv
{
static const uint32_t VulkanWindowFlag = SDL_WINDOW_VULKAN;
SDL_Window *window;
bool vulkanEnabled;
bool softpolyEnabled;
@ -409,7 +407,7 @@ SDLVideo::SDLVideo ()
if (Priv::vulkanEnabled)
{
Priv::CreateWindow(Priv::VulkanWindowFlag | SDL_WINDOW_HIDDEN);
Priv::CreateWindow(SDL_WINDOW_VULKAN | SDL_WINDOW_HIDDEN | (vid_fullscreen ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0));
if (Priv::window == nullptr)
{

View file

@ -545,6 +545,20 @@ LRESULT CALLBACK LConProc (HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
//
//==========================================================================
bool restartrequest;
void CheckForRestart()
{
if (restartrequest)
{
HMODULE hModule = GetModuleHandleW(NULL);
WCHAR path[MAX_PATH];
GetModuleFileNameW(hModule, path, MAX_PATH);
ShellExecuteW(NULL, L"open", path, GetCommandLineW(), NULL, SW_SHOWNORMAL);
}
restartrequest = false;
}
INT_PTR CALLBACK ErrorPaneProc (HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch (msg)
@ -559,11 +573,7 @@ INT_PTR CALLBACK ErrorPaneProc (HWND hDlg, UINT msg, WPARAM wParam, LPARAM lPara
{
if (LOWORD(wParam) == IDC_BUTTON1) // we pressed the restart button, so run GZDoom again
{
HMODULE hModule = GetModuleHandleW(NULL);
WCHAR path[MAX_PATH];
GetModuleFileNameW(hModule, path, MAX_PATH);
ShellExecuteW(NULL, L"open", path, GetCommandLineW(), NULL, SW_SHOWNORMAL);
restartrequest = true;
}
PostQuitMessage (0);
return TRUE;
@ -969,6 +979,8 @@ int DoMain (HINSTANCE hInstance)
atexit (UnCOM);
int ret = GameMain ();
CheckForRestart();
DestroyCustomCursor();
if (ret == 1337) // special exit code for 'norun'.
{

View file

@ -52,15 +52,15 @@ static bool IsGlslWhitespace(char c)
}
}
static FString NextGlslToken(const char *chars, long len, long &pos)
static FString NextGlslToken(const char *chars, ptrdiff_t len, ptrdiff_t &pos)
{
// Eat whitespace
long tokenStart = pos;
ptrdiff_t tokenStart = pos;
while (tokenStart != len && IsGlslWhitespace(chars[tokenStart]))
tokenStart++;
// Find token end
long tokenEnd = tokenStart;
ptrdiff_t tokenEnd = tokenStart;
while (tokenEnd != len && !IsGlslWhitespace(chars[tokenEnd]) && chars[tokenEnd] != ';')
tokenEnd++;
@ -82,13 +82,13 @@ FString RemoveLegacyUserUniforms(FString code)
// The following code searches for legacy uniform declarations in the shader itself and replaces them with whitespace.
long len = (long)code.Len();
ptrdiff_t len = code.Len();
char *chars = code.LockBuffer();
long startIndex = 0;
ptrdiff_t startIndex = 0;
while (true)
{
long matchIndex = code.IndexOf("uniform", startIndex);
ptrdiff_t matchIndex = code.IndexOf("uniform", startIndex);
if (matchIndex == -1)
break;
@ -98,7 +98,7 @@ FString RemoveLegacyUserUniforms(FString code)
bool isKeywordEnd = matchIndex + 7 == len || IsGlslWhitespace(chars[matchIndex + 7]);
if (isKeywordStart && isKeywordEnd)
{
long pos = matchIndex + 7;
ptrdiff_t pos = matchIndex + 7;
FString type = NextGlslToken(chars, len, pos);
FString identifier = NextGlslToken(chars, len, pos);
@ -107,10 +107,10 @@ FString RemoveLegacyUserUniforms(FString code)
if (isLegacyUniformName)
{
long statementEndIndex = code.IndexOf(';', matchIndex + 7);
ptrdiff_t statementEndIndex = code.IndexOf(';', matchIndex + 7);
if (statementEndIndex == -1)
statementEndIndex = len;
for (long i = matchIndex; i <= statementEndIndex; i++)
for (ptrdiff_t i = matchIndex; i <= statementEndIndex; i++)
{
if (!IsGlslWhitespace(chars[i]))
chars[i] = ' ';
@ -127,7 +127,7 @@ FString RemoveLegacyUserUniforms(FString code)
// Modern GLSL only allows use of 'texture'.
while (true)
{
long matchIndex = code.IndexOf("texture2d", startIndex);
ptrdiff_t matchIndex = code.IndexOf("texture2d", startIndex);
if (matchIndex == -1)
break;
@ -148,14 +148,14 @@ FString RemoveLegacyUserUniforms(FString code)
FString RemoveSamplerBindings(FString code, TArray<std::pair<FString, int>> &samplerstobind)
{
long len = (long)code.Len();
ptrdiff_t len = code.Len();
char *chars = code.LockBuffer();
long startIndex = 0;
long startpos, endpos;
ptrdiff_t startIndex = 0;
ptrdiff_t startpos, endpos;
while (true)
{
long matchIndex = code.IndexOf("layout(binding", startIndex);
ptrdiff_t matchIndex = code.IndexOf("layout(binding", startIndex);
if (matchIndex == -1)
break;
@ -165,7 +165,7 @@ FString RemoveSamplerBindings(FString code, TArray<std::pair<FString, int>> &sam
bool isKeywordEnd = matchIndex + 14 == len || IsGlslWhitespace(chars[matchIndex + 14]) || chars[matchIndex + 14] == '=';
if (isKeywordStart && isKeywordEnd)
{
long pos = matchIndex + 14;
ptrdiff_t pos = matchIndex + 14;
startpos = matchIndex;
while (IsGlslWhitespace(chars[pos])) pos++;
if (chars[pos] == '=')
@ -175,7 +175,7 @@ FString RemoveSamplerBindings(FString code, TArray<std::pair<FString, int>> &sam
auto val = strtol(&chars[pos], &p, 0);
if (p != &chars[pos])
{
pos = long(p - chars);
pos = (p - chars);
while (IsGlslWhitespace(chars[pos])) pos++;
if (chars[pos] == ')')
{
@ -216,17 +216,17 @@ FString RemoveSamplerBindings(FString code, TArray<std::pair<FString, int>> &sam
FString RemoveLayoutLocationDecl(FString code, const char *inoutkeyword)
{
long len = (long)code.Len();
ptrdiff_t len = code.Len();
char *chars = code.LockBuffer();
long startIndex = 0;
ptrdiff_t startIndex = 0;
while (true)
{
long matchIndex = code.IndexOf("layout(location", startIndex);
ptrdiff_t matchIndex = code.IndexOf("layout(location", startIndex);
if (matchIndex == -1)
break;
long endIndex = matchIndex;
ptrdiff_t endIndex = matchIndex;
// Find end of layout declaration
while (chars[endIndex] != ')' && chars[endIndex] != 0)
@ -255,7 +255,7 @@ FString RemoveLayoutLocationDecl(FString code, const char *inoutkeyword)
if (keywordFound && IsGlslWhitespace(chars[endIndex + i]))
{
// yes - replace declaration with spaces
for (long i = matchIndex; i < endIndex; i++)
for (auto i = matchIndex; i < endIndex; i++)
chars[i] = ' ';
}

View file

@ -98,7 +98,8 @@ bool IShadowMap::PerformUpdate()
LightsProcessed = 0;
LightsShadowmapped = 0;
if (gl_light_shadowmap && (screen->hwcaps & RFL_SHADER_STORAGE_BUFFER) && CollectLights != nullptr)
// CollectLights will be null if the calling code decides that shadowmaps are not needed.
if (CollectLights != nullptr)
{
UpdateCycles.Clock();
UploadAABBTree();

View file

@ -57,6 +57,11 @@ public:
mLights[index + 3] = r;
}
bool Enabled() const
{
return mAABBTree != nullptr;
}
protected:
// Upload the AABB-tree to the GPU
void UploadAABBTree();

View file

@ -33,10 +33,10 @@
#include "i_interface.h"
// Set up 3D-specific console variables:
CVAR(Int, vr_mode, 0, CVAR_GLOBALCONFIG)
CVAR(Int, vr_mode, 0, CVAR_GLOBALCONFIG|CVAR_ARCHIVE)
// switch left and right eye views
CVAR(Bool, vr_swap_eyes, false, CVAR_GLOBALCONFIG)
CVAR(Bool, vr_swap_eyes, false, CVAR_GLOBALCONFIG | CVAR_ARCHIVE)
// intraocular distance in meters
CVAR(Float, vr_ipd, 0.062f, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) // METERS

View file

@ -178,22 +178,22 @@ void Draw2D(F2DDrawer *drawer, FRenderState &state)
state.EnableTexture(false);
}
if (cmd.shape2D != nullptr)
if (cmd.shape2DBufInfo != nullptr)
{
state.SetVertexBuffer(&cmd.shape2D->buffers[cmd.shape2DBufIndex]);
state.SetVertexBuffer(&cmd.shape2DBufInfo->buffers[cmd.shape2DBufIndex]);
state.DrawIndexed(DT_Triangles, 0, cmd.shape2DIndexCount);
state.SetVertexBuffer(&vb);
if (cmd.shape2DCommandCounter == cmd.shape2D->lastCommand)
if (cmd.shape2DCommandCounter == cmd.shape2DBufInfo->lastCommand)
{
cmd.shape2D->lastCommand = -1;
if (cmd.shape2D->bufIndex > 0)
cmd.shape2DBufInfo->lastCommand = -1;
if (cmd.shape2DBufInfo->bufIndex > 0)
{
cmd.shape2D->needsVertexUpload = true;
cmd.shape2D->buffers.Clear();
cmd.shape2D->bufIndex = -1;
cmd.shape2DBufInfo->needsVertexUpload = true;
cmd.shape2DBufInfo->buffers.Clear();
cmd.shape2DBufInfo->bufIndex = -1;
}
}
cmd.shape2D->uploadedOnce = false;
cmd.shape2DBufInfo->uploadedOnce = false;
}
else
{

View file

@ -1328,7 +1328,7 @@ FxExpression *FxColorCast::Resolve(FCompileContext &ctx)
}
else
{
FxExpression *x = new FxConstant(V_GetColor(nullptr, constval.GetString(), &ScriptPosition), ScriptPosition);
FxExpression *x = new FxConstant(V_GetColor(constval.GetString(), &ScriptPosition), ScriptPosition);
delete this;
return x;
}
@ -9823,6 +9823,8 @@ FxExpression *FxIfStatement::Resolve(FCompileContext &ctx)
{
CHECKRESOLVED();
SAFE_RESOLVE(Condition, ctx);
if (WhenTrue == nullptr && WhenFalse == nullptr)
{ // We don't do anything either way, so disappear
delete this;
@ -9830,8 +9832,6 @@ FxExpression *FxIfStatement::Resolve(FCompileContext &ctx)
return new FxNop(ScriptPosition);
}
SAFE_RESOLVE(Condition, ctx);
if (Condition->ValueType != TypeBool)
{
Condition = new FxBoolCast(Condition, false);

View file

@ -419,7 +419,7 @@ DEFINE_ACTION_FUNCTION_NATIVE(FStringStruct, Filter, StringFilter)
static int StringIndexOf(FString *self, const FString &substr, int startIndex)
{
return self->IndexOf(substr, startIndex);
return (int)self->IndexOf(substr, startIndex);
}
DEFINE_ACTION_FUNCTION_NATIVE(FStringStruct, IndexOf, StringIndexOf)
@ -427,12 +427,12 @@ DEFINE_ACTION_FUNCTION_NATIVE(FStringStruct, IndexOf, StringIndexOf)
PARAM_SELF_STRUCT_PROLOGUE(FString);
PARAM_STRING(substr);
PARAM_INT(startIndex);
ACTION_RETURN_INT(self->IndexOf(substr, startIndex));
ACTION_RETURN_INT(StringIndexOf(self, substr, startIndex));
}
static int StringLastIndexOf(FString *self, const FString &substr, int endIndex)
{
return self->LastIndexOfBroken(substr, endIndex);
return (int)self->LastIndexOfBroken(substr, endIndex);
}
DEFINE_ACTION_FUNCTION_NATIVE(FStringStruct, LastIndexOf, StringLastIndexOf)
@ -440,12 +440,12 @@ DEFINE_ACTION_FUNCTION_NATIVE(FStringStruct, LastIndexOf, StringLastIndexOf)
PARAM_SELF_STRUCT_PROLOGUE(FString);
PARAM_STRING(substr);
PARAM_INT(endIndex);
ACTION_RETURN_INT(self->LastIndexOfBroken(substr, endIndex));
ACTION_RETURN_INT(StringLastIndexOf(self, substr, endIndex));
}
static int StringRightIndexOf(FString *self, const FString &substr, int endIndex)
{
return self->LastIndexOf(substr, endIndex);
return (int)self->LastIndexOf(substr, endIndex);
}
DEFINE_ACTION_FUNCTION_NATIVE(FStringStruct, RightIndexOf, StringRightIndexOf)
@ -453,7 +453,7 @@ DEFINE_ACTION_FUNCTION_NATIVE(FStringStruct, RightIndexOf, StringRightIndexOf)
PARAM_SELF_STRUCT_PROLOGUE(FString);
PARAM_STRING(substr);
PARAM_INT(endIndex);
ACTION_RETURN_INT(self->LastIndexOf(substr, endIndex));
ACTION_RETURN_INT(StringRightIndexOf(self, substr, endIndex));
}
static void StringToUpper(FString *self)

View file

@ -49,7 +49,7 @@ static int CastS2I(FString *b) { return (int)b->ToLong(); }
static double CastS2F(FString *b) { return b->ToDouble(); }
static int CastS2N(FString *b) { return b->Len() == 0 ? NAME_None : FName(*b).GetIndex(); }
static void CastN2S(FString *a, int b) { FName name = FName(ENamedName(b)); *a = name.IsValidName() ? name.GetChars() : ""; }
static int CastS2Co(FString *b) { return V_GetColor(nullptr, *b); }
static int CastS2Co(FString *b) { return V_GetColor(*b); }
static void CastCo2S(FString *a, int b) { PalEntry c(b); a->Format("%02x %02x %02x", c.r, c.g, c.b); }
static int CastS2So(FString *b) { return FSoundID(*b); }
static void CastSo2S(FString* a, int b) { *a = soundEngine->GetSoundName(b); }

View file

@ -1826,7 +1826,7 @@ static void DoCast(const VMRegisters &reg, const VMFrame *f, int a, int b, int c
case CAST_S2Co:
ASSERTD(a); ASSERTS(b);
reg.d[a] = V_GetColor(NULL, reg.s[b]);
reg.d[a] = V_GetColor(reg.s[b]);
break;
case CAST_Co2S:

View file

@ -542,7 +542,7 @@ void FMultipatchTextureBuilder::ParsePatch(FScanner &sc, BuildInfo &info, TexPar
if (!sc.CheckNumber())
{
sc.MustGetString();
part.Blend = V_GetColor(NULL, sc);
part.Blend = V_GetColor(sc);
}
else
{

View file

@ -42,23 +42,21 @@
#include "palutil.h"
int BestColor (const uint32_t *pal_in, int r, int g, int b, int first, int num);
int BestColor (const uint32_t *pal_in, int r, int g, int b, int first, int num, const uint8_t* indexmap);
class FColorMatcher
{
public:
FColorMatcher () = default;
FColorMatcher (const uint32_t *palette) { Pal = reinterpret_cast<const PalEntry*>(palette); }
FColorMatcher (const FColorMatcher &other) = default;
void SetPalette(PalEntry* palette) { Pal = palette; }
void SetPalette (const uint32_t *palette) { Pal = reinterpret_cast<const PalEntry*>(palette); }
void SetIndexMap(const uint8_t* index) { indexmap = index; startindex = index ? 0 : 1; }
uint8_t Pick (int r, int g, int b)
{
if (Pal == nullptr)
return 1;
return (uint8_t)BestColor ((uint32_t *)Pal, r, g, b, 1, 255);
return (uint8_t)BestColor ((uint32_t *)Pal, r, g, b, startindex, 255, indexmap);
}
uint8_t Pick (PalEntry pe)
@ -66,10 +64,10 @@ public:
return Pick(pe.r, pe.g, pe.b);
}
FColorMatcher &operator= (const FColorMatcher &other) = default;
private:
const PalEntry *Pal;
const PalEntry *Pal = nullptr;
const uint8_t* indexmap = nullptr;
int startindex = 1;
};
extern FColorMatcher ColorMatcher;

View file

@ -67,22 +67,14 @@ static uint64_t NSToMS(uint64_t ns)
return static_cast<uint64_t>(ns / 1'000'000);
}
static int NSToTic(uint64_t ns)
static int NSToTic(uint64_t ns, double const ticrate)
{
return static_cast<int>(ns * GameTicRate / 1'000'000'000);
return static_cast<int>(ns * ticrate / 1'000'000'000);
}
static int NSToBuildTic(uint64_t ns)
static uint64_t TicToNS(double tic, double const ticrate)
{
return static_cast<int>(ns * 120 / 1'000'000'000);
}
static uint64_t TicToNS(int tic)
{
return static_cast<uint64_t>(tic) * 1'000'000'000 / GameTicRate;
}
static uint64_t BuildTicToNS(int tic)
{
return static_cast<uint64_t>(tic) * 1'000'000'000 / 120;
return static_cast<uint64_t>(tic * 1'000'000'000 / ticrate);
}
void I_SetFrameTime()
@ -111,18 +103,18 @@ void I_WaitVBL(int count)
I_SetFrameTime();
}
int I_WaitForTic(int prevtic)
int I_WaitForTic(int prevtic, double const ticrate)
{
// Waits until the current tic is greater than prevtic. Time must not be frozen.
int time;
while ((time = I_GetTime()) <= prevtic)
while ((time = I_GetTime(ticrate)) <= prevtic)
{
// Windows-specific note:
// The minimum amount of time a thread can sleep is controlled by timeBeginPeriod.
// We set this to 1 ms in DoMain.
const uint64_t next = FirstFrameStartTime + TicToNS(prevtic + 1);
const uint64_t next = FirstFrameStartTime + TicToNS(prevtic + 1, ticrate);
const uint64_t now = I_nsTime();
if (next > now)
@ -166,21 +158,16 @@ uint64_t I_GetTimeNS()
return CurrentFrameStartTime - FirstFrameStartTime;
}
int I_GetTime()
int I_GetTime(double const ticrate)
{
return NSToTic(CurrentFrameStartTime - FirstFrameStartTime);
return NSToTic(CurrentFrameStartTime - FirstFrameStartTime, ticrate);
}
int I_GetBuildTime()
double I_GetTimeFrac(double const ticrate)
{
return NSToBuildTic(CurrentFrameStartTime - FirstFrameStartTime);
}
double I_GetTimeFrac()
{
int currentTic = NSToTic(CurrentFrameStartTime - FirstFrameStartTime);
uint64_t ticStartTime = FirstFrameStartTime + TicToNS(currentTic);
uint64_t ticNextTime = FirstFrameStartTime + TicToNS(currentTic + 1);
int currentTic = NSToTic(CurrentFrameStartTime - FirstFrameStartTime, ticrate);
uint64_t ticStartTime = FirstFrameStartTime + TicToNS(currentTic, ticrate);
uint64_t ticNextTime = FirstFrameStartTime + TicToNS(currentTic + 1, ticrate);
return (CurrentFrameStartTime - ticStartTime) / (double)(ticNextTime - ticStartTime);
}

View file

@ -9,17 +9,14 @@ extern double TimeScale;
void I_SetFrameTime();
// Called by D_DoomLoop, returns current time in tics.
int I_GetTime();
int I_GetTime(double const ticrate = GameTicRate);
// same, but using nanoseconds
uint64_t I_GetTimeNS();
// Called by Build games in lieu of totalclock, returns current time in tics at ticrate of 120.
int I_GetBuildTime();
double I_GetTimeFrac();
double I_GetTimeFrac(double const ticrate = GameTicRate);
// like I_GetTime, except it waits for a new tic before returning
int I_WaitForTic(int);
int I_WaitForTic(int prevtic, double const ticrate = GameTicRate);
// Freezes tic counting temporarily. While frozen, calls to I_GetTime()
// will always return the same value.

View file

@ -46,7 +46,7 @@
/* Palette management stuff */
/****************************/
int BestColor (const uint32_t *pal_in, int r, int g, int b, int first, int num)
int BestColor (const uint32_t *pal_in, int r, int g, int b, int first, int num, const uint8_t* indexmap)
{
const PalEntry *pal = (const PalEntry *)pal_in;
int bestcolor = first;
@ -54,17 +54,18 @@ int BestColor (const uint32_t *pal_in, int r, int g, int b, int first, int num)
for (int color = first; color < num; color++)
{
int x = r - pal[color].r;
int y = g - pal[color].g;
int z = b - pal[color].b;
int co = indexmap ? indexmap[color] : color;
int x = r - pal[co].r;
int y = g - pal[co].g;
int z = b - pal[co].b;
int dist = x*x + y*y + z*z;
if (dist < bestdist)
{
if (dist == 0)
return color;
return co;
bestdist = dist;
bestcolor = color;
bestcolor = co;
}
}
return bestcolor;
@ -523,7 +524,7 @@ PalEntry averageColor(const uint32_t* data, int size, int maxout)
//
//==========================================================================
int V_GetColorFromString(const uint32_t* palette, const char* cstr, FScriptPosition* sc)
int V_GetColorFromString(const char* cstr, FScriptPosition* sc)
{
int c[3], i, p;
char val[3];
@ -609,10 +610,7 @@ int V_GetColorFromString(const uint32_t* palette, const char* cstr, FScriptPosit
}
}
}
if (palette)
return BestColor(palette, c[0], c[1], c[2]);
else
return MAKERGB(c[0], c[1], c[2]);
return MAKERGB(c[0], c[1], c[2]);
}
//==========================================================================
@ -715,26 +713,26 @@ FString V_GetColorStringByName(const char* name, FScriptPosition* sc)
//
//==========================================================================
int V_GetColor(const uint32_t* palette, const char* str, FScriptPosition* sc)
int V_GetColor(const char* str, FScriptPosition* sc)
{
FString string = V_GetColorStringByName(str, sc);
int res;
if (!string.IsEmpty())
{
res = V_GetColorFromString(palette, string, sc);
res = V_GetColorFromString(string, sc);
}
else
{
res = V_GetColorFromString(palette, str, sc);
res = V_GetColorFromString(str, sc);
}
return res;
}
int V_GetColor(const uint32_t* palette, FScanner& sc)
int V_GetColor(FScanner& sc)
{
FScriptPosition scc = sc;
return V_GetColor(palette, sc.String, &scc);
return V_GetColor(sc.String, &scc);
}
//==========================================================================

View file

@ -7,7 +7,7 @@
struct FScriptPosition;
class FScanner;
int BestColor(const uint32_t* pal, int r, int g, int b, int first = 1, int num = 255);
int BestColor(const uint32_t* pal, int r, int g, int b, int first = 1, int num = 255, const uint8_t* indexmap = nullptr);
int PTM_BestColor(const uint32_t* pal_in, int r, int g, int b, bool reverselookup, float powtable, int first = 1, int num = 255);
void DoBlending(const PalEntry* from, PalEntry* to, int count, int r, int g, int b, int a);
@ -22,14 +22,14 @@ void HSVtoRGB (float *r, float *g, float *b, float h, float s, float v);
// Returns the closest color to the one desired. String
// should be of the form "rr gg bb".
int V_GetColorFromString(const uint32_t* palette, const char* colorstring, FScriptPosition* sc = nullptr);
int V_GetColorFromString(const char* colorstring, FScriptPosition* sc = nullptr);
// Scans through the X11R6RGB lump for a matching color
// and returns a color string suitable for V_GetColorFromString.
FString V_GetColorStringByName(const char* name, FScriptPosition* sc = nullptr);
// Tries to get color by name, then by string
int V_GetColor(const uint32_t* palette, const char* str, FScriptPosition* sc = nullptr);
int V_GetColor(const uint32_t* palette, FScanner& sc);
int V_GetColor(const char* str, FScriptPosition* sc = nullptr);
int V_GetColor(FScanner& sc);
PalEntry averageColor(const uint32_t* data, int size, int maxout);
enum

View file

@ -1,20 +1,29 @@
#pragma once
// Simple lightweight reference counting pointer alternative for std::shared_ptr which stores the reference counter in the handled object itself.
// Base class for handled objects
// Base classes for handled objects
class NoVirtualRefCountedBase
{
public:
void IncRef() { refCount++; }
void DecRef() { if (--refCount <= 0) delete this; }
private:
int refCount = 0;
};
class RefCountedBase
{
public:
void IncRef() { refCount++; }
void DecRef() { if (--refCount <= 0) delete this; }
void IncRef() { refCount++; }
void DecRef() { if (--refCount <= 0) delete this; }
private:
int refCount = 0;
int refCount = 0;
protected:
virtual ~RefCountedBase() = default;
virtual ~RefCountedBase() = default;
};
// The actual pointer object
// The actual pointer object
template<class T>
class RefCountedPtr
{
@ -31,10 +40,20 @@ public:
{
if (ptr) ptr->IncRef();
}
RefCountedPtr(const RefCountedPtr& r) : ptr(r.ptr)
{
if (ptr) ptr->IncRef();
}
RefCountedPtr(RefCountedPtr&& r) : ptr(r.ptr)
{
r.ptr = nullptr;
}
RefCountedPtr & operator=(const RefCountedPtr& r)
{
if (ptr != r.ptr)
if (this != &r)
{
if (ptr) ptr->DecRef();
ptr = r.ptr;
@ -54,11 +73,14 @@ public:
return *this;
}
RefCountedPtr & operator=(const RefCountedPtr&& r)
RefCountedPtr & operator=(RefCountedPtr&& r)
{
if (ptr) ptr->DecRef();
ptr = r.ptr;
r.ptr = nullptr;
if (this != &r)
{
if (ptr) ptr->DecRef();
ptr = r.ptr;
r.ptr = nullptr;
}
return *this;
}

View file

@ -95,7 +95,7 @@ bool FPlayList::ChangeList (const char *path)
}
// Check for relative paths.
long slashpos = song.IndexOf('/');
auto slashpos = song.IndexOf('/');
if (slashpos == 0)
{

View file

@ -292,6 +292,17 @@ public:
return i;
}
bool Contains(const T& item) const
{
unsigned int i;
for(i = 0;i < Count;++i)
{
if(Array[i] == item)
return true;
}
return false;
}
template<class Func>
unsigned int FindEx(Func compare) const
{

View file

@ -422,7 +422,7 @@ void FString::Remove(size_t index, size_t remlen)
{
if (index + remlen >= Len())
{
Truncate((long)index);
Truncate(index);
}
else
{
@ -500,12 +500,12 @@ void FString::DeleteLastCharacter()
}
long FString::IndexOf (const FString &substr, long startIndex) const
ptrdiff_t FString::IndexOf (const FString &substr, ptrdiff_t startIndex) const
{
return IndexOf (substr.Chars, startIndex);
}
long FString::IndexOf (const char *substr, long startIndex) const
ptrdiff_t FString::IndexOf (const char *substr, ptrdiff_t startIndex) const
{
if (startIndex > 0 && Len() <= (size_t)startIndex)
{
@ -516,10 +516,10 @@ long FString::IndexOf (const char *substr, long startIndex) const
{
return -1;
}
return long(str - Chars);
return str - Chars;
}
long FString::IndexOf (char subchar, long startIndex) const
ptrdiff_t FString::IndexOf (char subchar, ptrdiff_t startIndex) const
{
if (startIndex > 0 && Len() <= (size_t)startIndex)
{
@ -530,15 +530,15 @@ long FString::IndexOf (char subchar, long startIndex) const
{
return -1;
}
return long(str - Chars);
return str - Chars;
}
long FString::IndexOfAny (const FString &charset, long startIndex) const
ptrdiff_t FString::IndexOfAny (const FString &charset, ptrdiff_t startIndex) const
{
return IndexOfAny (charset.Chars, startIndex);
}
long FString::IndexOfAny (const char *charset, long startIndex) const
ptrdiff_t FString::IndexOfAny (const char *charset, ptrdiff_t startIndex) const
{
if (startIndex > 0 && Len() <= (size_t)startIndex)
{
@ -549,19 +549,19 @@ long FString::IndexOfAny (const char *charset, long startIndex) const
{
return -1;
}
return long(brk - Chars);
return brk - Chars;
}
long FString::LastIndexOf (char subchar) const
ptrdiff_t FString::LastIndexOf (char subchar) const
{
return LastIndexOf (subchar, long(Len()));
return LastIndexOf (subchar, Len());
}
long FString::LastIndexOf (char subchar, long endIndex) const
ptrdiff_t FString::LastIndexOf (char subchar, ptrdiff_t endIndex) const
{
if ((size_t)endIndex > Len())
{
endIndex = long(Len());
endIndex = Len();
}
while (--endIndex >= 0)
{
@ -573,16 +573,16 @@ long FString::LastIndexOf (char subchar, long endIndex) const
return -1;
}
long FString::LastIndexOfBroken (const FString &_substr, long endIndex) const
ptrdiff_t FString::LastIndexOfBroken (const FString &_substr, ptrdiff_t endIndex) const
{
const char *substr = _substr.GetChars();
size_t substrlen = _substr.Len();
if ((size_t)endIndex > Len())
{
endIndex = long(Len());
endIndex = Len();
}
substrlen--;
while (--endIndex >= long(substrlen))
while (--endIndex >= ptrdiff_t(substrlen))
{
if (strncmp (substr, Chars + endIndex - substrlen, substrlen + 1) == 0)
{
@ -592,26 +592,26 @@ long FString::LastIndexOfBroken (const FString &_substr, long endIndex) const
return -1;
}
long FString::LastIndexOfAny (const FString &charset) const
ptrdiff_t FString::LastIndexOfAny (const FString &charset) const
{
return LastIndexOfAny (charset.Chars, long(Len()));
return LastIndexOfAny (charset.Chars, Len());
}
long FString::LastIndexOfAny (const char *charset) const
ptrdiff_t FString::LastIndexOfAny (const char *charset) const
{
return LastIndexOfAny (charset, long(Len()));
}
long FString::LastIndexOfAny (const FString &charset, long endIndex) const
ptrdiff_t FString::LastIndexOfAny (const FString &charset, ptrdiff_t endIndex) const
{
return LastIndexOfAny (charset.Chars, endIndex);
}
long FString::LastIndexOfAny (const char *charset, long endIndex) const
ptrdiff_t FString::LastIndexOfAny (const char *charset, ptrdiff_t endIndex) const
{
if ((size_t)endIndex > Len())
{
endIndex = long(Len());
endIndex = Len();
}
while (--endIndex >= 0)
{
@ -623,31 +623,31 @@ long FString::LastIndexOfAny (const char *charset, long endIndex) const
return -1;
}
long FString::LastIndexOf (const FString &substr) const
ptrdiff_t FString::LastIndexOf (const FString &substr) const
{
return LastIndexOf(substr.Chars, long(Len() - substr.Len()), substr.Len());
return LastIndexOf(substr.Chars, Len() - substr.Len(), substr.Len());
}
long FString::LastIndexOf (const FString &substr, long endIndex) const
ptrdiff_t FString::LastIndexOf (const FString &substr, ptrdiff_t endIndex) const
{
return LastIndexOf(substr.Chars, endIndex, substr.Len());
}
long FString::LastIndexOf (const char *substr) const
ptrdiff_t FString::LastIndexOf (const char *substr) const
{
return LastIndexOf(substr, long(Len() - strlen(substr)), strlen(substr));
return LastIndexOf(substr, Len() - strlen(substr), strlen(substr));
}
long FString::LastIndexOf (const char *substr, long endIndex) const
ptrdiff_t FString::LastIndexOf (const char *substr, ptrdiff_t endIndex) const
{
return LastIndexOf(substr, endIndex, strlen(substr));
}
long FString::LastIndexOf (const char *substr, long endIndex, size_t substrlen) const
ptrdiff_t FString::LastIndexOf (const char *substr, ptrdiff_t endIndex, size_t substrlen) const
{
if ((size_t)endIndex + substrlen > Len())
{
endIndex = long(Len() - substrlen);
endIndex = Len() - substrlen;
}
while (endIndex >= 0)
{
@ -1256,15 +1256,15 @@ void FString::Split(TArray<FString>& tokens, const char *delimiter, EmptyTokenTy
{
assert(nullptr != delimiter);
const long selfLen = static_cast<long>(Len());
const long delimLen = static_cast<long>(strlen(delimiter));
long lastPos = 0;
const auto selfLen = static_cast<ptrdiff_t>(Len());
const auto delimLen = static_cast<ptrdiff_t>(strlen(delimiter));
ptrdiff_t lastPos = 0;
if (selfLen == 0) return; // Empty strings do not contain tokens, even with TOK_KEEPEMPTY.
while (lastPos <= selfLen)
{
long pos = IndexOf(delimiter, lastPos);
auto pos = IndexOf(delimiter, lastPos);
if (-1 == pos)
{

View file

@ -215,28 +215,28 @@ public:
void AppendCharacter(int codepoint);
void DeleteLastCharacter();
long IndexOf (const FString &substr, long startIndex=0) const;
long IndexOf (const char *substr, long startIndex=0) const;
long IndexOf (char subchar, long startIndex=0) const;
ptrdiff_t IndexOf (const FString &substr, ptrdiff_t startIndex=0) const;
ptrdiff_t IndexOf (const char *substr, ptrdiff_t startIndex=0) const;
ptrdiff_t IndexOf (char subchar, ptrdiff_t startIndex=0) const;
long IndexOfAny (const FString &charset, long startIndex=0) const;
long IndexOfAny (const char *charset, long startIndex=0) const;
ptrdiff_t IndexOfAny (const FString &charset, ptrdiff_t startIndex=0) const;
ptrdiff_t IndexOfAny (const char *charset, ptrdiff_t startIndex=0) const;
// This is only kept for backwards compatibility with old ZScript versions that used this function and depend on its bug.
long LastIndexOf (char subchar) const;
long LastIndexOfBroken (const FString &substr, long endIndex) const;
long LastIndexOf (char subchar, long endIndex) const;
ptrdiff_t LastIndexOf (char subchar) const;
ptrdiff_t LastIndexOfBroken (const FString &substr, ptrdiff_t endIndex) const;
ptrdiff_t LastIndexOf (char subchar, ptrdiff_t endIndex) const;
long LastIndexOfAny (const FString &charset) const;
long LastIndexOfAny (const char *charset) const;
long LastIndexOfAny (const FString &charset, long endIndex) const;
long LastIndexOfAny (const char *charset, long endIndex) const;
ptrdiff_t LastIndexOfAny (const FString &charset) const;
ptrdiff_t LastIndexOfAny (const char *charset) const;
ptrdiff_t LastIndexOfAny (const FString &charset, ptrdiff_t endIndex) const;
ptrdiff_t LastIndexOfAny (const char *charset, ptrdiff_t endIndex) const;
long LastIndexOf (const FString &substr) const;
long LastIndexOf (const FString &substr, long endIndex) const;
long LastIndexOf (const char *substr) const;
long LastIndexOf (const char *substr, long endIndex) const;
long LastIndexOf (const char *substr, long endIndex, size_t substrlen) const;
ptrdiff_t LastIndexOf (const FString &substr) const;
ptrdiff_t LastIndexOf (const FString &substr, ptrdiff_t endIndex) const;
ptrdiff_t LastIndexOf (const char *substr) const;
ptrdiff_t LastIndexOf (const char *substr, ptrdiff_t endIndex) const;
ptrdiff_t LastIndexOf (const char *substr, ptrdiff_t endIndex, size_t substrlen) const;
void ToUpper ();
void ToLower ();

View file

@ -69,10 +69,10 @@ static bool CheatAddKey (cheatseq_t *cheat, uint8_t key, bool *eat)
cheat->Pos = cheat->Sequence;
cheat->CurrentArg = 0;
}
if (*cheat->Pos == '#' && key >= '0' && key <= '9')
if (*cheat->Pos == '#' && ((key >= '0' && key <= '9') || key == ' '))
{
*eat = true;
cheat->Args[cheat->CurrentArg++] = key;
cheat->Args[cheat->CurrentArg++] = key == ' ' ? '0' : key;
cheat->Pos++;
}
else if (upperforlower[key] == upperforlower[*cheat->Pos])

View file

@ -212,6 +212,7 @@ void CompleteLevel(MapRecord* map)
gameaction = ga_completed;
g_nextmap = !currentLevel || !(currentLevel->flags & MI_FORCEEOG)? map : nullptr;
g_nextskill = -1; // This does not change the skill
g_bossexit = false;
}
//---------------------------------------------------------------------------
@ -223,6 +224,7 @@ void CompleteLevel(MapRecord* map)
void changeMap(int player, uint8_t** stream, bool skip)
{
int skill = (int8_t)ReadByte(stream);
int bossexit = (int8_t)ReadByte(stream);
auto mapname = ReadStringConst(stream);
if (skip) return;
auto map = FindMapByName(mapname);
@ -231,6 +233,7 @@ void changeMap(int player, uint8_t** stream, bool skip)
gameaction = ga_completed;
g_nextmap = map;
g_nextskill = skill;
g_bossexit = bossexit;
}
}
@ -251,10 +254,11 @@ void endScreenJob(int player, uint8_t** stream, bool skip)
//
//---------------------------------------------------------------------------
void ChangeLevel(MapRecord* map, int skill)
void ChangeLevel(MapRecord* map, int skill, bool bossexit)
{
Net_WriteByte(DEM_CHANGEMAP);
Net_WriteByte(skill);
Net_WriteByte(bossexit);
Net_WriteString(map? map->labelName : nullptr);
}
@ -264,10 +268,11 @@ void ChangeLevel(MapRecord* map, int skill)
//
//---------------------------------------------------------------------------
void DeferedStartGame(MapRecord* map, int skill, bool nostopsound)
void DeferredStartGame(MapRecord* map, int skill, bool nostopsound)
{
g_nextmap = map;
g_nextskill = skill;
g_bossexit = false;
gameaction = nostopsound? ga_newgamenostopsound : ga_newgame;
}
@ -334,7 +339,7 @@ CCMD(levelwarp)
auto map = levelwarp_common(argv, "levelwarp", "warp to");
if (map)
{
ChangeLevel(map, -1);
ChangeLevel(map, g_nextskill);
}
}
@ -355,7 +360,7 @@ CCMD(levelstart)
auto map = levelwarp_common(argv, "start game", "start new game at");
if (map)
{
DeferedStartGame(map, -1);
DeferredStartGame(map, g_nextskill);
}
}
@ -405,7 +410,7 @@ CCMD(changemap)
Printf(PRINT_BOLD, "%s: map file not found\n", map->fileName.GetChars());
}
ChangeLevel(map, -1);
ChangeLevel(map, g_nextskill);
}
//---------------------------------------------------------------------------
@ -445,7 +450,7 @@ CCMD(map)
Printf(PRINT_BOLD, "%s: map file not found\n", map->fileName.GetChars());
}
DeferedStartGame(map, -1);
DeferredStartGame(map, g_nextskill);
}
}
@ -462,7 +467,7 @@ CCMD(restartmap)
Printf("Must be in a game to restart a level.\n");
return;
}
ChangeLevel(currentLevel, -1);
ChangeLevel(currentLevel, g_nextskill);
}
//---------------------------------------------------------------------------
@ -493,5 +498,61 @@ CUSTOM_CVAR(Float, i_timescale, 1.0f, CVAR_NOINITCALL)
CCMD(endofgame)
{
STAT_Update(true);
ChangeLevel(nullptr, -1);
ChangeLevel(nullptr, g_nextskill);
}
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
CCMD(skill)
{
if (gamestate == GS_LEVEL)
{
auto argsCount = argv.argc();
if (argsCount < 2)
{
auto currentSkill = gi->GetCurrentSkill();
if (currentSkill >= 0)
{
Printf("Current skill is %d (%s)\n", currentSkill, GStrings.localize(gSkillNames[currentSkill]));
}
else if (currentSkill == -1)
{
Printf("Current skill is not set (%d)\n");
}
else if (currentSkill == -2)
{
Printf("This game has no skill settings.\n");
}
else
{
Printf("Current skill is an unknown/unsupported value (%d)\n");
}
}
else if (argsCount == 2)
{
auto newSkill = atoi(argv[1]);
if (newSkill >= 0 and newSkill < MAXSKILLS)
{
g_nextskill = newSkill;
Printf("Skill will be changed for next game.\n");
}
else
{
Printf("Please specify a skill level between 0 and %d\n", MAXSKILLS - 1);
}
}
else if (argsCount > 2)
{
Printf(PRINT_BOLD, "skill <newskill>: returns the current skill level, and optionally sets the skill level for the next game.\n");
}
}
else
{
Printf("Currently not in a game.\n");
}
}

View file

@ -326,7 +326,7 @@ static void ShoveChatStr (const char *str, uint8_t who)
if (*str == '#')
{
C_DoCommand(FStringf("activatecheat %s", str + 1));
C_DoCommand(FStringf("activatecheat \"%s\"", str + 1));
}
else
{

View file

@ -40,6 +40,7 @@
#include "buildtiles.h"
#include "bitmap.h"
#include "m_argv.h"
#include "gamestruct.h"
#include "gamecontrol.h"
#include "palettecontainer.h"
#include "mapinfo.h"
@ -2008,6 +2009,193 @@ void parseModel(FScanner& sc, FScriptPosition& pos)
}
}
//===========================================================================
//
//
//
//===========================================================================
static bool parseDefineQAVInterpolateIgnoreBlock(FScanner& sc, const int& res_id, TMap<int, TArray<int>>& ignoredata, const int& numframes)
{
FScanner::SavedPos blockend;
FScriptPosition pos = sc;
FString scframes, sctiles;
TArray<int> framearray, tilearray;
if (sc.StartBraces(&blockend))
{
pos.Message(MSG_ERROR, "defineqav (%d): interpolate: malformed syntax, unable to continue", res_id);
return false;
}
while (!sc.FoundEndBrace(blockend))
{
sc.GetString();
if (sc.Compare("frames")) sc.GetString(scframes);
else if (sc.Compare("tiles")) sc.GetString(sctiles);
}
// Confirm we received something for 'frames' and 'tiles'.
if (scframes.IsEmpty() || sctiles.IsEmpty())
{
pos.Message(MSG_ERROR, "defineqav (%d): interpolate: unable to get any values for 'frames' or 'tiles', unable to continue", res_id);
return false;
}
auto arraybuilder = [&](const FString& input, TArray<int>& output, const int& maxvalue) -> bool
{
if (input.CompareNoCase("all") == 0)
{
// All indices from 0 through to maxvalue are to be added to output array.
output.Push(0);
output.Push(maxvalue);
}
else if (input.IndexOf("-") != -1)
{
// Input is a range of values, split on the hypthen and add each value to the output array.
auto temparray = input.Split("-");
if (temparray.Size() == 2)
{
// Test if keywords 'first' and 'last' have been used.'
output.Push(temparray[0].CompareNoCase("first") == 0 ? 0 : atoi(temparray[0]));
output.Push(temparray[1].CompareNoCase("last") == 0 ? maxvalue : atoi(temparray[1]));
}
}
else
{
// We just have a number. Convert the string into an int and push it twice to the output array.
auto tempvalue = atoi(input);
for (auto i = 0; i < 2; i++) output.Push(tempvalue);
}
if (output.Size() != 2 || output[0] > output[1] || output[0] < 0 || output[1] > maxvalue)
{
pos.Message(MSG_ERROR, "defineqav (%d): interpolate: ignore: value of '%s' is malformed, unable to continue", res_id, input.GetChars());
return false;
}
return true;
};
if (!arraybuilder(scframes, framearray, numframes - 1)) return false;
if (!arraybuilder(sctiles, tilearray, 7)) return false;
// Process arrays and add ignored frames as required.
for (auto i = framearray[0]; i <= framearray[1]; i++)
{
auto& frametiles = ignoredata[i];
for (auto j = tilearray[0]; j <= tilearray[1]; j++)
{
if (!frametiles.Contains(j)) frametiles.Push(j);
}
}
return true;
}
static bool parseDefineQAVInterpolateBlock(FScanner& sc, const int& res_id, const int& numframes)
{
FScanner::SavedPos blockend;
FScriptPosition pos = sc;
FString interptype;
bool loopable = false;
TMap<int, TArray<int>> ignoredata;
if (sc.StartBraces(&blockend))
{
pos.Message(MSG_ERROR, "defineqav (%d): interpolate (%s): malformed syntax, unable to continue", res_id, interptype.GetChars());
return false;
}
while (!sc.FoundEndBrace(blockend))
{
sc.GetString();
if (sc.Compare("type"))
{
if (interptype.IsNotEmpty())
{
pos.Message(MSG_ERROR, "defineqav (%d): interpolate (%s): more than one interpolation type defined, unable to continue", res_id, interptype.GetChars());
return false;
}
sc.GetString(interptype);
if (!gi->IsQAVInterpTypeValid(interptype))
{
pos.Message(MSG_ERROR, "defineqav (%d): interpolate (%s): interpolation type not found", res_id, interptype.GetChars());
return false;
}
}
else if (sc.Compare("loopable")) loopable = true;
else if (sc.Compare("ignore")) if (!parseDefineQAVInterpolateIgnoreBlock(sc, res_id, ignoredata, numframes)) return false;
}
// Add interpolation properties to game for processing while drawing.
gi->AddQAVInterpProps(res_id, interptype, loopable, ignoredata);
return true;
}
void parseDefineQAV(FScanner& sc, FScriptPosition& pos)
{
FScanner::SavedPos blockend;
FString fn;
int res_id = -1;
int numframes = -1;
bool interpolate = false;
if (!sc.GetNumber(res_id, true))
{
pos.Message(MSG_ERROR, "defineqav: invalid or non-defined resource ID");
return;
}
if (sc.StartBraces(&blockend))
{
pos.Message(MSG_ERROR, "defineqav (%d): malformed syntax, unable to continue", res_id);
return;
}
while (!sc.FoundEndBrace(blockend))
{
sc.MustGetString();
if (sc.Compare("file"))
{
if (fn.IsNotEmpty())
{
pos.Message(MSG_ERROR, "defineqav (%d): more than one file defined, unable to continue", res_id);
return;
}
sc.GetString(fn);
// Test file's validity.
FixPathSeperator(fn);
auto lump = fileSystem.FindFile(fn);
if (lump < 0)
{
pos.Message(MSG_ERROR, "defineqav (%d): file '%s' could not be found", res_id, fn.GetChars());
return;
}
// Read file to get number of frames from QAV, skipping first 8 bytes.
auto fr = fileSystem.OpenFileReader(lump);
fr.ReadUInt64();
numframes = fr.ReadInt32();
}
else if (sc.Compare("interpolate"))
{
if (interpolate)
{
pos.Message(MSG_ERROR, "defineqav (%d): more than one interpolate block defined, unable to continue", res_id);
return;
}
interpolate = true;
if (!parseDefineQAVInterpolateBlock(sc, res_id, numframes)) return;
}
}
// If we're not interpolating, remove any reference to interpolation data for this res_id.
if (!interpolate) gi->RemoveQAVInterpProps(res_id);
// Add new file to filesystem.
fileSystem.CreatePathlessCopy(fn, res_id, 0);
}
//===========================================================================
//
//
@ -2098,6 +2286,7 @@ static const dispatch basetokens[] =
{ "shadefactor", parseSkip<1> },
{ "newgamechoices", parseEmptyBlock },
{ "rffdefineid", parseRffDefineId },
{ "defineqav", parseDefineQAV },
{ nullptr, nullptr },
};

View file

@ -408,7 +408,7 @@ DEFINE_MAP_OPTION(fade, true)
{
parse.ParseAssign();
parse.sc.MustGetString();
info->fadeto = V_GetColor(nullptr, parse.sc);
info->fadeto = V_GetColor(parse.sc);
}
DEFINE_MAP_OPTION(partime, true)
@ -645,9 +645,11 @@ MapFlagHandlers[] =
{ "sw_bossmeter_sumo", MITYPE_SETFLAGG,LEVEL_SW_BOSSMETER_SUMO, 0, GAMEFLAG_SW },
{ "sw_bossmeter_zilla", MITYPE_SETFLAGG,LEVEL_SW_BOSSMETER_ZILLA, 0, GAMEFLAG_SW },
{ "sw_deathexit_serpent", MITYPE_SETFLAGG,LEVEL_SW_DEATHEXIT_SERPENT, 0, GAMEFLAG_SW },
{ "sw_deathexit_serpent_next", MITYPE_SETFLAGG,LEVEL_SW_DEATHEXIT_SERPENT | LEVEL_SW_DEATHEXIT_SERPENT_NEXT, 0, GAMEFLAG_SW },
{ "sw_deathexit_sumo", MITYPE_SETFLAGG,LEVEL_SW_DEATHEXIT_SUMO, 0, GAMEFLAG_SW },
{ "sw_deathexit_zilla", MITYPE_SETFLAGG,LEVEL_SW_DEATHEXIT_ZILLA, 0, GAMEFLAG_SW },
{ "sw_spawnmines", MITYPE_SETFLAGG,LEVEL_SW_SPAWNMINES, 0, GAMEFLAG_SW },
{ "bossonlycutscene", MITYPE_SETFLAGG,LEVEL_BOSSONLYCUTSCENE, 0, -1 },
{ NULL, MITYPE_IGNORE, 0, 0}
};

View file

@ -122,6 +122,7 @@ gameaction_t gameaction = ga_nothing;
// gameaction state
MapRecord* g_nextmap;
int g_nextskill;
int g_bossexit;
FILE* hashfile;
@ -343,7 +344,7 @@ void UserConfig::ProcessOptions()
{
gamegrp = "BLOOD.RFF";
DefaultCon = "CRYPTIC.INI";
const char* argv[] = { "cpart07.ar_" , "cpart15.ar_" };
const char* argv[] = { "CPART07.AR_", "CPART15.AR_" };
AddArt.reset(new FArgs(2, argv));
}
@ -697,7 +698,7 @@ static TArray<GrpEntry> SetupGame()
{
auto grplower = grp.FileName.MakeLower();
FixPathSeperator(grplower);
int pos = grplower.LastIndexOf(gamegrplower);
auto pos = grplower.LastIndexOf(gamegrplower);
if (pos >= 0 && pos == grplower.Len() - gamegrplower.Len())
{
groupno = g;
@ -903,6 +904,7 @@ static void InitTextures()
lookups.postLoadLookups();
SetupFontSubstitution();
V_LoadTranslations(); // loading the translations must be delayed until the palettes have been fully set up.
UpdateUpscaleMask();
TileFiles.SetBackup();
}
@ -912,6 +914,8 @@ static void InitTextures()
//
//==========================================================================
static uint8_t palindexmap[256];
int RunGame()
{
GameStartupInfo.FgColor = 0xffffff;
@ -949,11 +953,11 @@ int RunGame()
colorset = true;
}
}
if (grp.FileInfo.mpepisodes.Size())
if (grp.FileInfo.exclepisodes.Size())
{
for (auto& mpepisode : grp.FileInfo.mpepisodes)
for (auto& episode : grp.FileInfo.exclepisodes)
{
gi->AddMultiplayerEpisode(mpepisode);
gi->AddExcludedEpisode(episode);
}
}
}
@ -1021,7 +1025,11 @@ int RunGame()
}
GameTicRate = 30;
CheckUserMap();
GPalette.Init(MAXPALOOKUPS + 2); // one slot for each translation, plus a separate one for the base palettes and the internal one
palindexmap[0] = 255;
for (int i = 1; i <= 255; i++) palindexmap[i] = i;
GPalette.Init(MAXPALOOKUPS + 2, palindexmap); // one slot for each translation, plus a separate one for the base palettes and the internal one
int v = ColorMatcher.Pick(0, 0, 0);
gi->loadPalette();
StartScreen->Progress();
InitTextures();

View file

@ -32,6 +32,7 @@ extern bool crouch_toggle;
struct MapRecord;
extern MapRecord* g_nextmap;
extern int g_nextskill;
extern int g_bossexit;
extern FMemArena dump; // this is for memory blocks than cannot be deallocated without some huge effort. Put them in here so that they do not register on shutdown.
@ -43,8 +44,8 @@ int GetAutomapZoom(int gZoom);
void DrawCrosshair(int deftile, int health, double xdelta, double ydelta, double scale, PalEntry color = 0xffffffff);
void updatePauseStatus();
void DeferedStartGame(MapRecord* map, int skill, bool nostopsound = false);
void ChangeLevel(MapRecord* map, int skill);
void DeferredStartGame(MapRecord* map, int skill, bool nostopsound = false);
void ChangeLevel(MapRecord* map, int skill, bool bossexit = false);
void CompleteLevel(MapRecord* map);
struct UserConfig
@ -132,7 +133,7 @@ struct GrpInfo
TArray<FString> tobedeleted;
TArray<FString> loadfiles;
TArray<FString> loadart;
TArray<FString> mpepisodes;
TArray<FString> exclepisodes;
uint32_t FgColor = 0, BgColor = 0;
};

View file

@ -76,12 +76,19 @@ CVARD(Bool, cl_interpolate, true, CVAR_ARCHIVE, "enable/disable view interpolati
CVARD(Bool, cl_slopetilting, false, CVAR_ARCHIVE, "enable/disable slope tilting") // only implemented in Blood
CVARD(Int, cl_showweapon, 1, CVAR_ARCHIVE, "enable/disable show weapons") // only implemented in Blood
CVARD(Bool, cl_sointerpolation, true, CVAR_ARCHIVE, "enable/disable sector object interpolation") // only implemented in SW
CVARD(Bool, cl_syncinput, false, CVAR_ARCHIVE, "enable/disable synchronized input with game's ticrate") // only implemented in Duke
CVARD(Bool, cl_syncinput, false, CVAR_ARCHIVE, "enable/disable synchronized input with game's tickrate") // only implemented in Duke
CVARD(Bool, cl_swsmoothsway, true, CVAR_ARCHIVE, "move SW weapon left and right smoothly while bobbing")
CVARD(Bool, cl_showmagamt, false, CVAR_ARCHIVE, "show the amount of rounds left in the magazine of your weapon on the modern HUD")
CVARD(Bool, cl_nomeleeblur, false, CVAR_ARCHIVE, "enable/disable blur effect with melee weapons in SW")
CVARD(Bool, cl_exhumedoldturn, false, CVAR_ARCHIVE, "enable/disable legacy turning speed for Powerslave/Exhumed")
CVARD(Bool, cl_hudinterpolation, true, CVAR_ARCHIVE, "enable/disable HUD (weapon drawer) interpolation")
CVARD(Bool, cl_bloodvanillarun, true, CVAR_ARCHIVE, "enable/disable Blood's vanilla run mode")
CVARD(Bool, cl_bloodvanillabobbing, true, CVAR_ARCHIVE, "enable/disable Blood's vanilla bobbing while not using vanilla run mode")
CVARD(Bool, cl_bloodvanillaexplosions, false, CVAR_ARCHIVE, "enable/disable Blood's vanilla explosion behavior")
CVARD(Bool, cl_bloodvanillaenemies, false, CVAR_ARCHIVE, "enable/disable Blood's vanilla enemy behavior")
CVARD(Bool, cl_bloodqavinterp, true, CVAR_ARCHIVE, "enable/disable Blood's QAV interpolation")
CVARD(Bool, cl_bloodweapinterp, false, CVAR_ARCHIVE, "enable/disable Blood's weapon interpolation. Depends on 'cl_bloodqavinterp'")
CVARD(Bool, cl_bloodoldweapbalance, false, CVAR_ARCHIVE, "enable/disable legacy 1.0 weapon handling for Blood")
CUSTOM_CVARD(Int, cl_autoaim, 1, CVAR_ARCHIVE|CVAR_USERINFO, "enable/disable weapon autoaim")

View file

@ -27,6 +27,13 @@ EXTERN_CVAR(Bool, cl_showmagamt)
EXTERN_CVAR(Bool, cl_nomeleeblur)
EXTERN_CVAR(Bool, cl_exhumedoldturn)
EXTERN_CVAR(Bool, cl_hudinterpolation)
EXTERN_CVAR(Bool, cl_bloodvanillarun)
EXTERN_CVAR(Bool, cl_bloodvanillabobbing)
EXTERN_CVAR(Bool, cl_bloodvanillaexplosions)
EXTERN_CVAR(Bool, cl_bloodvanillaenemies)
EXTERN_CVAR(Bool, cl_bloodqavinterp)
EXTERN_CVAR(Bool, cl_bloodweapinterp)
EXTERN_CVAR(Bool, cl_bloodoldweapbalance)
EXTERN_CVAR(Bool, demorec_seeds_cvar)
EXTERN_CVAR(Bool, demoplay_diffs)

View file

@ -156,3 +156,8 @@ inline int spriteGetSlope(int spritenum)
auto spr = &sprite[spritenum];
return ((spr->cstat & CSTAT_SPRITE_ALIGNMENT_MASK) != CSTAT_SPRITE_ALIGNMENT_SLOPE) ? 0 : uint8_t(spr->xoffset) + (uint8_t(spr->yoffset) << 8);
}
inline int I_GetBuildTime()
{
return I_GetTime(120);
}

View file

@ -50,9 +50,9 @@
F2DDrawer twodpsp;
void hud_drawsprite(double sx, double sy, int z, double a, int picnum, int dashade, int dapalnum, int dastat, double alpha)
void hud_drawsprite(double sx, double sy, double sz, double a, int picnum, int dashade, int dapalnum, int dastat, double alpha)
{
double dz = z / 65536.;
sz *= 1. / 65536.;
alpha *= (dastat & RS_TRANS1)? glblend[0].def[!!(dastat & RS_TRANS2)].alpha : 1.;
int palid = TRANSLATION(Translation_Remap + curbasepal, dapalnum);
@ -62,7 +62,7 @@ void hud_drawsprite(double sx, double sy, int z, double a, int picnum, int dasha
auto tex = tileGetTexture(picnum);
DrawTexture(&twodpsp, tex, sx, sy,
DTA_ScaleX, dz, DTA_ScaleY, dz,
DTA_ScaleX, sz, DTA_ScaleY, sz,
DTA_Color, shadeToLight(dashade),
DTA_TranslationIndex, palid,
DTA_ViewportX, windowxy1.x, DTA_ViewportY, windowxy1.y,
@ -107,7 +107,9 @@ static FString statFPS()
frameCount++;
if (frameDelay >= 0)
{
output.AppendFormat("%5.1f fps (%.1f ms)\n", lastFPS, frameDelay);
output.AppendFormat("%5.1f fps", lastFPS);
if (frameDelay < 10) output.AppendFormat(" ");
output.AppendFormat(" (%.1f ms)\n", frameDelay);
if (cumulativeFrameDelay >= 1000.0)
{

View file

@ -5,4 +5,4 @@
extern F2DDrawer twodpsp;
void DrawRateStuff();
void hud_drawsprite(double sx, double sy, int z, double a, int picnum, int dashade, int dapalnum, int dastat, double alpha = 1);
void hud_drawsprite(double sx, double sy, double sz, double a, int picnum, int dashade, int dapalnum, int dastat, double alpha = 1);

View file

@ -246,8 +246,8 @@ void processMovement(InputPacket* currInput, InputPacket* inputBuffer, ControlIn
void PlayerHorizon::applyinput(float const horz, ESyncBits* actions, double const scaleAdjust)
{
// Process only if no targeted horizon set.
if (!targetset())
// Process only if movewment isn't locked.
if (!movementlocked())
{
// Store current horizon as true pitch.
double pitch = horiz.aspitch();
@ -342,7 +342,7 @@ void PlayerAngle::applyinput(float const avel, ESyncBits* actions, double const
rotscrnang += buildfang(scaleAdjust * -(720. / GameTicRate));
}
if (!targetset())
if (!movementlocked())
{
if (*actions & SB_TURNAROUND)
{
@ -450,6 +450,7 @@ FSerializer& Serialize(FSerializer& arc, const char* keyname, PlayerAngle& w, Pl
("lookang", w.look_ang)
("rotscrnang", w.rotscrnang)
("spin", w.spin)
("inputdisabled", w.inputdisabled)
.EndObject();
if (arc.isReading())
@ -457,6 +458,7 @@ FSerializer& Serialize(FSerializer& arc, const char* keyname, PlayerAngle& w, Pl
w.oang = w.ang;
w.olook_ang = w.look_ang;
w.orotscrnang = w.rotscrnang;
w.inputdisabled = w.inputdisabled;
w.resetadjustment();
}
}
@ -469,12 +471,14 @@ FSerializer& Serialize(FSerializer& arc, const char* keyname, PlayerHorizon& w,
{
arc("horiz", w.horiz)
("horizoff", w.horizoff)
("inputdisabled", w.inputdisabled)
.EndObject();
if (arc.isReading())
{
w.ohoriz = w.horiz;
w.ohorizoff = w.horizoff;
w.inputdisabled = w.inputdisabled;
w.resetadjustment();
}
}

View file

@ -13,6 +13,8 @@ struct PlayerHorizon
{
fixedhoriz horiz, ohoriz, horizoff, ohorizoff;
friend FSerializer& Serialize(FSerializer& arc, const char* keyname, PlayerHorizon& w, PlayerHorizon* def);
void backup()
{
ohoriz = horiz;
@ -50,11 +52,26 @@ struct PlayerHorizon
__settarget(value, backup);
}
void lockinput()
{
inputdisabled = true;
}
void unlockinput()
{
inputdisabled = false;
}
bool targetset()
{
return target.asq16();
}
bool movementlocked()
{
return target.asq16() || inputdisabled;
}
void processhelpers(double const scaleAdjust)
{
if (targetset())
@ -103,6 +120,7 @@ struct PlayerHorizon
private:
fixedhoriz target;
double adjustment;
bool inputdisabled;
void __addadjustment(fixedhoriz value)
{
@ -138,6 +156,8 @@ struct PlayerAngle
binangle ang, oang, look_ang, olook_ang, rotscrnang, orotscrnang;
double spin;
friend FSerializer& Serialize(FSerializer& arc, const char* keyname, PlayerAngle& w, PlayerAngle* def);
void backup()
{
oang = ang;
@ -177,11 +197,26 @@ struct PlayerAngle
__settarget(value, backup);
}
void lockinput()
{
inputdisabled = true;
}
void unlockinput()
{
inputdisabled = false;
}
bool targetset()
{
return target.asbam();
}
bool movementlocked()
{
return target.asq16() || inputdisabled;
}
void processhelpers(double const scaleAdjust)
{
if (targetset())
@ -244,6 +279,7 @@ struct PlayerAngle
private:
binangle target;
double adjustment;
bool inputdisabled;
void __addadjustment(binangle value)
{

View file

@ -120,7 +120,11 @@ struct GameInterface
virtual void LeavePortal(spritetype* viewer, int type) {}
virtual bool GetGeoEffect(GeoEffect* eff, int viewsector) { return false; }
virtual int Voxelize(int sprnum) { return -1; }
virtual void AddMultiplayerEpisode(FString name) {}
virtual void AddExcludedEpisode(FString episode) {}
virtual int GetCurrentSkill() { return -1; }
virtual bool IsQAVInterpTypeValid(const FString& type) { return false; }
virtual void AddQAVInterpProps(const int& res_id, const FString& interptype, const bool& loopable, const TMap<int, TArray<int>>& ignoredata) { }
virtual void RemoveQAVInterpProps(const int& res_id) { }
virtual FString statFPS()
{

View file

@ -117,10 +117,10 @@ static TArray<FString> ParseGameInfo(TArray<FString>& pwads, const char* fn, con
else if (!nextKey.CompareNoCase("STARTUPCOLORS"))
{
sc.MustGetString();
GameStartupInfo.FgColor = V_GetColor(NULL, sc);
GameStartupInfo.FgColor = V_GetColor(sc);
sc.MustGetStringName(",");
sc.MustGetString();
GameStartupInfo.BkColor = V_GetColor(NULL, sc);
GameStartupInfo.BkColor = V_GetColor(sc);
}
else if (!nextKey.CompareNoCase("CON"))
{

View file

@ -688,7 +688,7 @@ void MainLoop ()
userConfig.CommandMap = "";
if (maprecord)
{
DeferedStartGame(maprecord, -1);
DeferredStartGame(maprecord, g_nextskill);
}
}

View file

@ -38,6 +38,7 @@
#include "c_dispatch.h"
#include "md4.h"
#include "hw_sections.h"
#include "mapinfo.h"
static TArray<usermaphack_t> usermaphacks;
TArray<int> blockingpairs[MAXWALLS];
@ -249,6 +250,10 @@ static int32_t LoadMapHack(const char *filename)
}
}
}
else if (sc.Compare("sw_serp_continue")) // This is a hack for SW's Last Warrior mod to continue from L4 to L5.
{
currentLevel->gameflags |= LEVEL_SW_DEATHEXIT_SERPENT_NEXT;
}
else if (sc.Compare("angleoff") || sc.Compare("angoff"))
{

View file

@ -71,6 +71,7 @@ CCMD(listmaps)
MapRecord *FindMapByName(const char *nm)
{
if (!nm || !*nm) return nullptr;
for (auto& map : mapList)
{
if (map->labelName.CompareNoCase(nm) == 0)

View file

@ -51,10 +51,11 @@ enum EMapGameFlags
LEVEL_SW_DEATHEXIT_SERPENT = 1024,
LEVEL_SW_DEATHEXIT_SUMO = 2048,
LEVEL_SW_DEATHEXIT_ZILLA = 4096,
LEVEL_SW_DEATHEXIT_SERPENT_NEXT = 8192,
LEVEL_WT_BOSSSPAWN = 8192,
LEVEL_WT_BOSSSPAWN = 16384,
LEVEL_BOSSONLYCUTSCENE = 32768,
};
// These get filled in by the map definition parsers of the front ends.

View file

@ -493,4 +493,18 @@ void setWallSectors()
wall[sector[i].wallptr + w].sector = i;
}
}
// validate 'nextsector' fields. Some maps have these wrong which can cause render glitches and occasionally even crashes.
for (int i = 0; i < numwalls; i++)
{
if (wall[i].nextwall != -1)
{
if (wall[i].nextsector != wall[wall[i].nextwall].sector)
{
DPrintf(DMSG_ERROR, "Bad 'nextsector' reference %d on wall %d\n", wall[i].nextsector, i);
wall[i].nextsector = wall[wall[i].nextwall].sector;
}
}
}
}

View file

@ -105,7 +105,7 @@ static bool DoStartGame(FNewGameStartup& gs)
gi->StartGame(gs); // play game specific effects (like Duke/RR/SW's voice lines when starting a game.)
DeferedStartGame(map, gs.Skill);
DeferredStartGame(map, gs.Skill);
return true;
}

View file

@ -187,6 +187,9 @@ void ShowScoreboard(int numplayers, const CompletionFunc& completion_)
void ShowIntermission(MapRecord* fromMap, MapRecord* toMap, SummaryInfo* info, CompletionFunc completion_)
{
bool bossexit = g_bossexit;
g_bossexit = false;
completion = completion_;
runner = CreateRunner();
GC::WriteBarrier(runner);
@ -200,7 +203,7 @@ void ShowIntermission(MapRecord* fromMap, MapRecord* toMap, SummaryInfo* info, C
try
{
if (fromMap)
if (fromMap && (!(fromMap->gameflags & LEVEL_BOSSONLYCUTSCENE) || bossexit))
{
if (!CreateCutscene(&fromMap->outro, runner, fromMap, !!toMap))
{
@ -212,7 +215,7 @@ void ShowIntermission(MapRecord* fromMap, MapRecord* toMap, SummaryInfo* info, C
if (fromMap || (g_gameType & GAMEFLAG_PSEXHUMED))
CallCreateSummaryFunction(globalCutscenes.SummaryScreen, runner, fromMap, info, toMap);
if (toMap)
if (toMap)
{
if (!CreateCutscene(&toMap->intro, runner, toMap, !!fromMap))
{

View file

@ -588,12 +588,12 @@ static TArray<GrpInfo> ParseGrpInfo(const char *fn, FileReader &fr, TMap<FString
}
while (sc.CheckToken(','));
}
else if (sc.Compare("mpepisodes"))
else if (sc.Compare("exclepisodes"))
{
do
{
sc.MustGetToken(TK_StringConst);
grp.mpepisodes.Push(sc.String);
grp.exclepisodes.Push(sc.String);
}
while (sc.CheckToken(','));
}

View file

@ -463,9 +463,9 @@ bool SectorGeometry::MakeVertices2(unsigned int secnum, int plane, const FVector
nexti:;
}
if (lines.Size() == 0)
if (lines.Size() < 4)
{
// nothing to generate.
// nothing to generate. If line count is < 4 this sector is degenerate and should not be processed further.
auto& entry = data[secnum].planes[plane];
entry.vertices.Clear();
entry.texcoords.Clear();

View file

@ -329,11 +329,6 @@ bool PickTexture(FGameTexture* tex, int paletteid, TexturePick& pick, bool wanti
{
if (!tex->isValid() || tex->GetTexelWidth() <= 0 || tex->GetTexelHeight() <= 0) return false;
if (tex->GetUseType() == ETextureType::FontChar && paletteid != 0)
{
int a = 0;
}
int usepalette = 0, useremap = 0;
if (!IsLuminosityTranslation(paletteid))
{

View file

@ -31,68 +31,71 @@ void collectTSpritesForPortal(int x, int y, int i, int interpolation)
int dx = mirror[j].dx;
int dy = mirror[j].dy;
int dz = mirror[j].dz;
tspritetype* pTSprite = &pm_tsprite[pm_spritesortcnt++];
*pTSprite = {};
pTSprite->type = pSprite->type;
pTSprite->index = pSprite->index;
pTSprite->sectnum = nSector2;
pTSprite->x = pSprite->x + dx;
pTSprite->y = pSprite->y + dy;
pTSprite->z = pSprite->z + dz;
pTSprite->ang = pSprite->ang;
pTSprite->picnum = pSprite->picnum;
pTSprite->shade = pSprite->shade;
pTSprite->pal = pSprite->pal;
pTSprite->xrepeat = pSprite->xrepeat;
pTSprite->yrepeat = pSprite->yrepeat;
pTSprite->xoffset = pSprite->xoffset;
pTSprite->yoffset = pSprite->yoffset;
pTSprite->cstat = pSprite->cstat;
pTSprite->statnum = kStatDecoration;
pTSprite->owner = pSprite->index;
pTSprite->extra = pSprite->extra;
pTSprite->flags = pSprite->hitag | 0x200;
pTSprite->x = dx + interpolatedvalue(pSprite->ox, pSprite->x, interpolation);
pTSprite->y = dy + interpolatedvalue(pSprite->oy, pSprite->y, interpolation);
pTSprite->z = dz + interpolatedvalue(pSprite->oz, pSprite->z, interpolation);
pTSprite->ang = pSprite->interpolatedang(interpolation);
if (pm_spritesortcnt < MAXSPRITESONSCREEN)
{
tspritetype* pTSprite = &pm_tsprite[pm_spritesortcnt++];
*pTSprite = {};
pTSprite->type = pSprite->type;
pTSprite->index = pSprite->index;
pTSprite->sectnum = nSector2;
pTSprite->x = pSprite->x + dx;
pTSprite->y = pSprite->y + dy;
pTSprite->z = pSprite->z + dz;
pTSprite->ang = pSprite->ang;
pTSprite->picnum = pSprite->picnum;
pTSprite->shade = pSprite->shade;
pTSprite->pal = pSprite->pal;
pTSprite->xrepeat = pSprite->xrepeat;
pTSprite->yrepeat = pSprite->yrepeat;
pTSprite->xoffset = pSprite->xoffset;
pTSprite->yoffset = pSprite->yoffset;
pTSprite->cstat = pSprite->cstat;
pTSprite->statnum = kStatDecoration;
pTSprite->owner = pSprite->index;
pTSprite->extra = pSprite->extra;
pTSprite->flags = pSprite->hitag | 0x200;
pTSprite->x = dx + interpolatedvalue(pSprite->ox, pSprite->x, interpolation);
pTSprite->y = dy + interpolatedvalue(pSprite->oy, pSprite->y, interpolation);
pTSprite->z = dz + interpolatedvalue(pSprite->oz, pSprite->z, interpolation);
pTSprite->ang = pSprite->interpolatedang(interpolation);
int nAnim = 0;
switch (picanm[pTSprite->picnum].extra & 7)
{
case 1:
{
int dX = x - pTSprite->x;
int dY = y - pTSprite->y;
RotateVector(&dX, &dY, 128 - pTSprite->ang);
nAnim = GetOctant(dX, dY);
if (nAnim <= 4)
int nAnim = 0;
switch (picanm[pTSprite->picnum].extra & 7)
{
pTSprite->cstat &= ~4;
}
else
case 1:
{
nAnim = 8 - nAnim;
pTSprite->cstat |= 4;
int dX = x - pTSprite->x;
int dY = y - pTSprite->y;
RotateVector(&dX, &dY, 128 - pTSprite->ang);
nAnim = GetOctant(dX, dY);
if (nAnim <= 4)
{
pTSprite->cstat &= ~4;
}
else
{
nAnim = 8 - nAnim;
pTSprite->cstat |= 4;
}
break;
}
case 2:
{
int dX = x - pTSprite->x;
int dY = y - pTSprite->y;
RotateVector(&dX, &dY, 128 - pTSprite->ang);
nAnim = GetOctant(dX, dY);
break;
}
}
while (nAnim > 0)
{
pTSprite->picnum += picanm[pTSprite->picnum].num + 1;
nAnim--;
}
break;
}
case 2:
{
int dX = x - pTSprite->x;
int dY = y - pTSprite->y;
RotateVector(&dX, &dY, 128 - pTSprite->ang);
nAnim = GetOctant(dX, dY);
break;
}
}
while (nAnim > 0)
{
pTSprite->picnum += picanm[pTSprite->picnum].num + 1;
nAnim--;
}
pm_spritesortcnt++;
pm_spritesortcnt++;
}
}
}

View file

@ -2540,7 +2540,7 @@ void actInit(bool bSaveLoad)
{
if (act->s().type == kItemWeaponVoodooDoll)
{
act->s().type = kAmmoItemVoodooDoll;
act->s().type = kItemAmmoVoodooDoll;
break;
}
}
@ -2661,7 +2661,8 @@ void actRadiusDamage(DBloodActor* source, int x, int y, int z, int nSector, int
{
uint8_t sectmap[(kMaxSectors + 7) >> 3];
auto pOwner = source->GetOwner();
GetClosestSpriteSectors(nSector, x, y, nDist, sectmap);
const bool newSectCheckMethod = !cl_bloodvanillaexplosions && pOwner && pOwner->IsDudeActor() && !VanillaMode(); // use new sector checking logic
GetClosestSpriteSectors(nSector, x, y, nDist, sectmap, nullptr, newSectCheckMethod);
nDist <<= 4;
if (flags & 2)
{
@ -3089,6 +3090,18 @@ static bool actKillDudeStage1(DBloodActor* actor, DAMAGE_TYPE damageType)
return true;
}
break;
case kDudeTinyCaleb:
if (cl_bloodvanillaenemies || VanillaMode())
break;
if (damageType == kDamageBurn && pXSprite->medium == kMediumNormal)
{
pSprite->type = kDudeBurningTinyCaleb;
aiNewState(actor, &tinycalebBurnGoto);
actHealDude(actor, dudeInfo[39].startHealth, dudeInfo[39].startHealth);
return true;
}
break;
}
return false;
}
@ -3923,7 +3936,7 @@ static void actImpactMissile(DBloodActor* missileActor, int hitCode)
actBurnSprite(missileActor->GetOwner(), actorHit, 360);
// by NoOne: make Life Leech heal user, just like it was in 1.0x versions
if (gGameOptions.weaponsV10x && !VanillaMode() && !DemoRecordStatus() && pDudeInfo != nullptr)
if (gGameOptions.weaponsV10x && !VanillaMode() && pDudeInfo != nullptr)
{
if (missileOwner->IsDudeActor() && missileOwner->hasX() && missileOwner->x().health != 0)
actHealDude(missileOwner, nDamage >> 2, getDudeInfo(missileOwner->s().type)->startHealth);
@ -3999,7 +4012,7 @@ static void actImpactMissile(DBloodActor* missileActor, int hitCode)
break;
case kMissileFireball:
case kMissileFireballNapam:
case kMissileFireballNapalm:
if (hitCode == 3 && pSpriteHit && (pThingInfo || pDudeInfo))
{
if (pThingInfo && pSpriteHit->type == kThingTNTBarrel && actorHit->x().burnTime == 0)
@ -4029,7 +4042,7 @@ static void actImpactMissile(DBloodActor* missileActor, int hitCode)
actRadiusDamage(missileOwner, pMissile->x, pMissile->y, pMissile->z, pMissile->sectnum, 16, 20, 10, kDamageBullet, 6, 480);
// by NoOne: allow additional bullet damage for Flare Gun
if (gGameOptions.weaponsV10x && !VanillaMode() && !DemoRecordStatus())
if (gGameOptions.weaponsV10x && !VanillaMode())
{
int nDamage = (20 + Random(10)) << 4;
actDamageSprite(missileOwner, actorHit, kDamageBullet, nDamage);
@ -4560,7 +4573,7 @@ static void ProcessTouchObjects(DBloodActor* actor)
}
}
if (actor2->hasX())
if (actor2 && actor2->hasX())
{
XSPRITE* pXHSprite = &actor2->x();
if (pXHSprite->Touch && !pXHSprite->isTriggered && (!pXHSprite->DudeLockout || actor->IsPlayerActor()))
@ -4633,11 +4646,15 @@ int MoveThing(spritetype *pSprite)
assert(nSector >= 0 && nSector < kMaxSectors);
int top, bottom;
GetSpriteExtents(pSprite, &top, &bottom);
const int bakCompat = enginecompatibility_mode;
if (xvel[nSprite] || yvel[nSprite])
{
short bakCstat = pSprite->cstat;
pSprite->cstat &= ~257;
if ((pSprite->owner >= 0) && !cl_bloodvanillaexplosions && !VanillaMode())
enginecompatibility_mode = ENGINECOMPATIBILITY_NONE; // improved clipmove accuracy
v8 = gSpriteHit[nXSprite].hit = ClipMove((int*)&pSprite->x, (int*)&pSprite->y, (int*)&pSprite->z, &nSector, xvel[nSprite]>>12, yvel[nSprite]>>12, pSprite->clipdist<<2, (pSprite->z-top)/4, (bottom-pSprite->z)/4, CLIPMASK0);
enginecompatibility_mode = bakCompat; // restore
pSprite->cstat = bakCstat;
assert(nSector >= 0);
if (pSprite->sectnum != nSector)
@ -4817,6 +4834,8 @@ void MoveDude(spritetype *pSprite)
int tz = (pSprite->z-top)/4;
int wd = pSprite->clipdist<<2;
int nSector = pSprite->sectnum;
int nAiStateType = (pXSprite->aiState) ? pXSprite->aiState->stateType : -1;
assert(nSector >= 0 && nSector < kMaxSectors);
if (xvel[nSprite] || yvel[nSprite])
{
@ -4997,8 +5016,12 @@ void MoveDude(spritetype *pSprite)
}
if (pPlayer && zvel[nSprite] > 0x155555 && !pPlayer->fallScream && pXSprite->height > 0)
{
pPlayer->fallScream = 1;
sfxPlay3DSound(pSprite, 719, 0, 0);
const bool playerAlive = (pXSprite->health > 0) || VanillaMode(); // only trigger falling scream if player is alive or vanilla mode
if (playerAlive)
{
pPlayer->fallScream = 1;
sfxPlay3DSound(pSprite, 719, 0, 0);
}
}
vec3_t const oldpos = pSprite->pos;
int nLink = CheckLink(pSprite);
@ -5028,6 +5051,7 @@ void MoveDude(spritetype *pSprite)
}
sfxPlay3DSound(pSprite, 721, -1, 0);
} else {
switch (pSprite->type) {
case kDudeCultistTommy:
case kDudeCultistShotgun:
@ -5041,6 +5065,11 @@ void MoveDude(spritetype *pSprite)
actKillDude(pSprite->index, pSprite, kDamageFall, 1000<<4);
break;
}
#ifdef NOONE_EXTENSIONS
if (IsDudeSprite(pSprite) && pXSprite->health > 0 && aiInPatrolState(nAiStateType))
aiPatrolState(pSprite, kAiStatePatrolMoveL); // continue patrol when going from water
#endif
}
break;
case kMarkerUpWater:
@ -5058,10 +5087,12 @@ void MoveDude(spritetype *pSprite)
{
#ifdef NOONE_EXTENSIONS
// look for palette in data2 of marker. If value <= 0, use default ones.
pPlayer->nWaterPal = 0;
int nXUpper = sprite[gUpperLink[nSector]].extra;
if (nXUpper >= 0)
pPlayer->nWaterPal = xsprite[nXUpper].data2;
if (gModernMap) {
pPlayer->nWaterPal = 0;
int nXUpper = sprite[gUpperLink[nSector]].extra;
if (nXUpper >= 0)
pPlayer->nWaterPal = xsprite[nXUpper].data2;
}
#endif
pPlayer->posture = 1;
@ -5072,6 +5103,7 @@ void MoveDude(spritetype *pSprite)
}
else
{
switch (pSprite->type) {
case kDudeCultistTommy:
case kDudeCultistShotgun:
@ -5082,22 +5114,17 @@ void MoveDude(spritetype *pSprite)
break;
case kDudeBurningCultist:
{
const bool fixRandomCultist = !cl_bloodvanillaenemies && (pSprite->inittype >= kDudeBase) && (pSprite->inittype < kDudeMax) && !VanillaMode(); // fix burning cultists randomly switching types underwater
if (Chance(chance))
{
pSprite->type = kDudeCultistTommy;
pXSprite->burnTime = 0;
evPost(nSprite, 3, 0, kCallbackEnemeyBubble);
sfxPlay3DSound(pSprite, 720, -1, 0);
aiNewState(&bloodActors[pXSprite->reference], &cultistSwimGoto);
}
else
{
pSprite->type = kDudeCultistShotgun;
pXSprite->burnTime = 0;
evPost(nSprite, 3, 0, kCallbackEnemeyBubble);
sfxPlay3DSound(pSprite, 720, -1, 0);
aiNewState(&bloodActors[pXSprite->reference], &cultistSwimGoto);
}
if (fixRandomCultist) // fix burning cultists randomly switching types underwater
pSprite->type = pSprite->inittype; // restore back to spawned cultist type
pXSprite->burnTime = 0;
evPost(nSprite, 3, 0, kCallbackEnemeyBubble);
sfxPlay3DSound(pSprite, 720, -1, 0);
aiNewState(&bloodActors[pXSprite->reference], &cultistSwimGoto);
break;
}
case kDudeZombieAxeNormal:
@ -5130,14 +5157,26 @@ void MoveDude(spritetype *pSprite)
case kDudeBurningInnocent:
actKillDude(pSprite->index, pSprite, kDamageFall, 1000 << 4);
break;
#ifdef NOONE_EXTENSIONS
case kDudeModernCustom:
evPost(nSprite, 3, 0, kCallbackEnemeyBubble);
if (!canSwim(pSprite)) actKillDude(pSprite->index, pSprite, kDamageFall, 1000 << 4);
break;
#endif
}
#ifdef NOONE_EXTENSIONS
if (gModernMap) {
if (pSprite->type == kDudeModernCustom) {
evPost(nSprite, 3, 0, kCallbackEnemeyBubble);
if (!canSwim(pSprite))
actKillDude(pSprite->index, pSprite, kDamageFall, 1000 << 4);
}
// continue patrol when fall into water
if (IsDudeSprite(pSprite) && pXSprite->health > 0 && aiInPatrolState(nAiStateType))
aiPatrolState(pSprite, kAiStatePatrolMoveW);
}
#endif
}
break;
}
@ -5207,7 +5246,7 @@ void MoveDude(spritetype *pSprite)
case kDudeBat:
case kDudeRat:
case kDudeBurningInnocent:
actKillDude(pSprite->index, pSprite, DAMAGE_TYPE_0, 1000<<4);
actKillDude(pSprite->index, pSprite, kDamageFall, 1000<<4);
break;
}
}
@ -5392,6 +5431,8 @@ int MoveMissile(spritetype *pSprite)
int top, bottom;
GetSpriteExtents(pSprite, &top, &bottom);
int i = 1;
const int bakCompat = enginecompatibility_mode;
const bool isFlameSprite = (pSprite->type == kMissileFlameSpray || pSprite->type == kMissileFlameHound); // do not use accurate clipmove for flame based sprites (changes damage too much)
while (1)
{
int x = pSprite->x;
@ -5399,7 +5440,10 @@ int MoveMissile(spritetype *pSprite)
int z = pSprite->z;
int nSector2 = pSprite->sectnum;
clipmoveboxtracenum = 1;
if ((pSprite->owner >= 0) && !isFlameSprite && !cl_bloodvanillaexplosions && !VanillaMode())
enginecompatibility_mode = ENGINECOMPATIBILITY_NONE; // improved clipmove accuracy
int vdx = ClipMove(&x, &y, &z, &nSector2, vx, vy, pSprite->clipdist<<2, (z-top)/4, (bottom-z)/4, CLIPMASK0);
enginecompatibility_mode = bakCompat; // restore
clipmoveboxtracenum = 3;
short nSector = nSector2;
if (nSector2 < 0)
@ -5512,7 +5556,7 @@ void actExplodeSprite(spritetype *pSprite)
switch (pSprite->type)
{
case kMissileFireballNapam:
case kMissileFireballNapalm:
nType = kExplosionNapalm;
seqSpawn(4, 3, nXSprite, -1);
if (Chance(0x8000))
@ -5939,7 +5983,7 @@ void actProcessSprites(void)
it.Reset(kStatExplosion);
while ((nSprite = it.NextIndex()) >= 0)
{
uint8_t v24c[(kMaxSectors+7)>>3];
uint8_t sectmap[(kMaxSectors+7)>>3];
spritetype *pSprite = &sprite[nSprite];
if (pSprite->flags & 32)
@ -5964,9 +6008,13 @@ void actProcessSprites(void)
if (gModernMap && pXSprite->data4 > 0)
radius = pXSprite->data4;
#endif
// GetClosestSpriteSectors() has issues checking some sectors due to optimizations
// the new flag newSectCheckMethod for GetClosestSpriteSectors() does rectify these issues, but this may cause unintended side effects for level scripted explosions
// so only allow this new checking method for dude spawned explosions
short gAffectedXWalls[kMaxXWalls];
GetClosestSpriteSectors(nSector, x, y, radius, v24c, gAffectedXWalls);
const bool newSectCheckMethod = !cl_bloodvanillaexplosions && pOwner && pOwner->IsDudeActor() && !VanillaMode(); // use new sector checking logic
GetClosestSpriteSectors(nSector, x, y, radius, sectmap, gAffectedXWalls, newSectCheckMethod);
for (int i = 0; i < kMaxXWalls; i++)
{
@ -5986,7 +6034,7 @@ void actProcessSprites(void)
if (pDude->flags & 32)
continue;
if (TestBitString(v24c, pDude->sectnum))
if (TestBitString(sectmap, pDude->sectnum))
{
if (pXSprite->data1 && CheckProximity(pDude, x, y, z, nSector, radius))
{
@ -6017,7 +6065,7 @@ void actProcessSprites(void)
if (pThing->flags & 32)
continue;
if (TestBitString(v24c, pThing->sectnum))
if (TestBitString(sectmap, pThing->sectnum))
{
if (pXSprite->data1 && CheckProximity(pThing, x, y, z, nSector, radius))
{
@ -6061,7 +6109,7 @@ void actProcessSprites(void)
continue;
spritetype* pDebris = &sprite[gPhysSpritesList[i]];
if (!TestBitString(v24c, pDebris->sectnum) || !CheckProximity(pDebris, x, y, z, nSector, radius)) continue;
if (!TestBitString(sectmap, pDebris->sectnum) || !CheckProximity(pDebris, x, y, z, nSector, radius)) continue;
else debrisConcuss(nOwner, i, x, y, z, pExplodeInfo->dmgType);
}
}
@ -6073,8 +6121,11 @@ void actProcessSprites(void)
else if (sprite[gImpactSpritesList[i]].sectnum < 0 || (sprite[gImpactSpritesList[i]].flags & kHitagFree) != 0)
continue;
spritetype* pImpact = &sprite[gImpactSpritesList[i]]; XSPRITE* pXImpact = &xsprite[pImpact->extra];
if (/*pXImpact->state == pXImpact->restState ||*/ !TestBitString(v24c, pImpact->sectnum) || !CheckProximity(pImpact, x, y, z, nSector, radius))
spritetype* pImpact = &sprite[gImpactSpritesList[i]];
if (pImpact->extra <= 0)
continue;
XSPRITE* pXImpact = &xsprite[pImpact->extra];
if (/*pXImpact->state == pXImpact->restState ||*/ !TestBitString(sectmap, pImpact->sectnum) || !CheckProximity(pImpact, x, y, z, nSector, radius))
continue;
trTriggerSprite(pImpact->index, pXImpact, kCmdSpriteImpact);
@ -6155,7 +6206,8 @@ void actProcessSprites(void)
if (nXSprite > 0)
{
XSPRITE *pXSprite = &xsprite[nXSprite];
if (pXSprite->burnTime > 0)
const bool fixBurnGlitch = !cl_bloodvanillaenemies && IsBurningDude(pSprite) && !VanillaMode(); // if enemies are burning, always apply burning damage per tick
if ((pXSprite->burnTime > 0) || fixBurnGlitch)
{
switch (pSprite->type)
{
@ -6667,7 +6719,7 @@ void actBuildMissile(spritetype* pMissile, int nXSprite, int nSprite) {
seqSpawn(2, 3, nXSprite, -1);
sfxPlay3DSound(pMissile, 493, 0, 0);
break;
case kMissileFireballNapam:
case kMissileFireballNapalm:
seqSpawn(61, 3, nXSprite, nNapalmClient);
sfxPlay3DSound(pMissile, 441, 0, 0);
break;
@ -7016,22 +7068,34 @@ void actFireVector(spritetype *pShooter, int a2, int a3, int a4, int a5, int a6,
#ifdef NOONE_EXTENSIONS
// add impulse for sprites from physics list
if (gPhysSpritesCount > 0 && pVectorData->impulse) {
int nIndex = debrisGetIndex(pSprite->index);
if (nIndex != -1 && (xsprite[pSprite->extra].physAttr & kPhysDebrisVector)) {
int impulse = DivScale(pVectorData->impulse, ClipLow(gSpriteMass[pSprite->extra].mass, 10), 6);
xvel[nSprite] += MulScale(a4, impulse, 16);
yvel[nSprite] += MulScale(a5, impulse, 16);
zvel[nSprite] += MulScale(a6, impulse, 16);
if (xspriRangeIsFine(pSprite->extra)) {
XSPRITE* pXSprite = &xsprite[pSprite->extra];
if (pXSprite->physAttr & kPhysDebrisVector) {
int impulse = DivScale(pVectorData->impulse, ClipLow(gSpriteMass[pSprite->extra].mass, 10), 6);
xvel[nSprite] += MulScale(a4, impulse, 16);
yvel[nSprite] += MulScale(a5, impulse, 16);
zvel[nSprite] += MulScale(a6, impulse, 16);
if (pVectorData->burnTime != 0) {
if (!xsprite[nXSprite].burnTime) evPost(nSprite, 3, 0, kCallbackFXFlameLick);
actBurnSprite(sprite[nShooter].owner, &xsprite[nXSprite], pVectorData->burnTime);
}
if (pSprite->type >= kThingBase && pSprite->type < kThingMax) {
pSprite->statnum = kStatThing; // temporary change statnum property
actDamageSprite(nShooter, pSprite, pVectorData->dmgType, pVectorData->dmg << 4);
pSprite->statnum = kStatDecoration; // return statnum property back
}
if (pVectorData->burnTime != 0) {
if (!xsprite[nXSprite].burnTime) evPost(nSprite, 3, 0, kCallbackFXFlameLick);
actBurnSprite(sprite[nShooter].owner, &xsprite[nXSprite], pVectorData->burnTime);
}
//if (pSprite->type >= kThingBase && pSprite->type < kThingMax)
//changespritestat(pSprite->index, kStatThing);
//actPostSprite(pSprite->index, kStatThing); // if it was a thing, return it's statnum back
}
}
#endif
break;
@ -7039,10 +7103,32 @@ void actFireVector(spritetype *pShooter, int a2, int a3, int a4, int a5, int a6,
}
}
assert(nSurf < kSurfMax);
#ifdef NOONE_EXTENSIONS
// let the patrol enemies hear surface hit sounds!
if (pVectorData->surfHit[nSurf].fx2 >= 0) {
spritetype* pFX2 = gFX.fxSpawn(pVectorData->surfHit[nSurf].fx2, nSector, x, y, z, 0);
if (pFX2 && gModernMap)
pFX2->owner = pShooter->index;
}
if (pVectorData->surfHit[nSurf].fx3 >= 0) {
spritetype* pFX3 = gFX.fxSpawn(pVectorData->surfHit[nSurf].fx3, nSector, x, y, z, 0);
if (pFX3 && gModernMap)
pFX3->owner = pShooter->index;
}
#else
if (pVectorData->surfHit[nSurf].fx2 >= 0)
gFX.fxSpawn(pVectorData->surfHit[nSurf].fx2, nSector, x, y, z, 0);
if (pVectorData->surfHit[nSurf].fx3 >= 0)
gFX.fxSpawn(pVectorData->surfHit[nSurf].fx3, nSector, x, y, z, 0);
#endif
if (pVectorData->surfHit[nSurf].fxSnd >= 0)
sfxPlay3DSound(x, y, z, pVectorData->surfHit[nSurf].fxSnd, nSector);
}

View file

@ -209,6 +209,12 @@ void actAddGameLight(int lightRadius, int spriteNum, int zOffset, int lightRange
void actDoLight(int spriteNum);
#endif
void FireballSeqCallback(int, int);
void sub_38938(int, int);
void NapalmSeqCallback(int, int);
void sub_3888C(int, int);
void TreeToGibCallback(int, int);
bool IsUnderwaterSector(int nSector);
void actInit(bool bSaveLoad);
int actWallBounceVector(int *x, int *y, int nWall, int a4);

View file

@ -177,7 +177,7 @@ bool CanMove(spritetype *pSprite, int a2, int nAngle, int nRange)
// It makes ignore danger if enemy immune to N damageType. As result Cerberus start acting like
// in Blood 1.0 so it can move normally to player. It's up to you for adding rest of enemies here as
// i don't think it will broke something in game.
if (!VanillaMode() && Crusher && isImmune(pSprite, pXSector->damageType, 16)) return true;
if (!cl_bloodvanillaenemies && !VanillaMode() && Crusher && isImmune(pSprite, pXSector->damageType, 16)) return true;
fallthrough__;
case kDudeZombieButcher:
case kDudeSpiderBrown:
@ -896,11 +896,24 @@ int aiDamageSprite(DBloodActor* source, DBloodActor* actor, DAMAGE_TYPE nDmgType
spritetype *pSource = &source->s();
int nSource = pSource->index;
if (pSprite == pSource) return 0;
else if (pXSprite->target == -1 || (nSource != pXSprite->target && Chance(pSprite->type == pSource->type ? nDamage*pDudeInfo->changeTargetKin : nDamage*pDudeInfo->changeTarget)))
else if (pXSprite->target == -1) // if no target, give the dude a target
{
aiSetTarget(pXSprite, nSource);
aiActivateDude(&bloodActors[pXSprite->reference]);
}
else if (nSource != pXSprite->target) // if found a new target, retarget
{
int nThresh = nDamage;
if (pSprite->type == pSource->type)
nThresh *= pDudeInfo->changeTargetKin;
else
nThresh *= pDudeInfo->changeTarget;
if (Chance(nThresh))
{
aiSetTarget(pXSprite, nSource);
aiActivateDude(&bloodActors[pXSprite->reference]);
}
}
#ifdef NOONE_EXTENSIONS
if (gModernMap) {
@ -1020,6 +1033,7 @@ int aiDamageSprite(DBloodActor* source, DBloodActor* actor, DAMAGE_TYPE nDmgType
DUDEEXTRA *pDudeExtra = &gDudeExtra[pSprite->extra];
pDudeExtra->recoil = 1;
}
const bool fixRandomCultist = !cl_bloodvanillaenemies && (pSprite->inittype >= kDudeBase) && (pSprite->inittype < kDudeMax) && !VanillaMode(); // fix burning cultists randomly switching types underwater
switch (pSprite->type)
{
case kDudeCultistTommy:
@ -1066,12 +1080,16 @@ int aiDamageSprite(DBloodActor* source, DBloodActor* actor, DAMAGE_TYPE nDmgType
if (Chance(0x600) && (pXSprite->medium == kMediumWater || pXSprite->medium == kMediumGoo))
{
pSprite->type = kDudeCultistTommy;
if (fixRandomCultist) // fix burning cultists randomly switching types underwater
pSprite->type = pSprite->inittype; // restore back to spawned cultist type
pXSprite->burnTime = 0;
aiNewState(actor, &cultistSwimGoto);
}
else if (pXSprite->medium == kMediumWater || pXSprite->medium == kMediumGoo)
{
pSprite->type = kDudeCultistShotgun;
if (fixRandomCultist) // fix burning cultists randomly switching types underwater
pSprite->type = pSprite->inittype; // restore back to spawned cultist type
pXSprite->burnTime = 0;
aiNewState(actor, &cultistSwimGoto);
}
@ -1092,8 +1110,16 @@ int aiDamageSprite(DBloodActor* source, DBloodActor* actor, DAMAGE_TYPE nDmgType
case kDudeTinyCaleb:
if (nDmgType == kDamageBurn && pXSprite->health <= (unsigned int)pDudeInfo->fleeHealth/* && (pXSprite->at17_6 != 1 || pXSprite->at17_6 != 2)*/)
{
pSprite->type = kDudeBurningInnocent;
aiNewState(actor, &cultistBurnGoto);
if (!cl_bloodvanillaenemies && !VanillaMode()) // fix burning sprite for tiny caleb
{
pSprite->type = kDudeBurningTinyCaleb;
aiNewState(actor, &tinycalebBurnGoto);
}
else
{
pSprite->type = kDudeBurningInnocent;
aiNewState(actor, &cultistBurnGoto);
}
aiPlay3DSound(pSprite, 361, AI_SFX_PRIORITY_0, -1);
gDudeExtra[pSprite->extra].time = PlayClock+360;
actHealDude(pXSprite, dudeInfo[39].startHealth, dudeInfo[39].startHealth);
@ -1415,7 +1441,7 @@ void aiThinkTarget(DBloodActor* actor)
}
}
void sub_5F15C(spritetype *pSprite, XSPRITE *pXSprite)
void aiLookForTarget(spritetype *pSprite, XSPRITE *pXSprite)
{
assert(pSprite->type >= kDudeBase && pSprite->type < kDudeMax);
DUDEINFO *pDudeInfo = getDudeInfo(pSprite->type);
@ -1453,8 +1479,9 @@ void sub_5F15C(spritetype *pSprite, XSPRITE *pXSprite)
}
if (pXSprite->state)
{
uint8_t va4[(kMaxSectors+7)>>3];
GetClosestSpriteSectors(pSprite->sectnum, pSprite->x, pSprite->y, 400, va4);
uint8_t sectmap[(kMaxSectors+7)>>3];
const bool newSectCheckMethod = !cl_bloodvanillaenemies && !VanillaMode(); // use new sector checking logic
GetClosestSpriteSectors(pSprite->sectnum, pSprite->x, pSprite->y, 400, sectmap, nullptr, newSectCheckMethod);
int nSprite2;
StatIterator it(kStatDude);
@ -1492,10 +1519,10 @@ void aiProcessDudes(void) {
if (IsPlayerSprite(pSprite) || pXSprite->health == 0) continue;
pXSprite->stateTimer = ClipLow(pXSprite->stateTimer-4, 0);
if (pXSprite->aiState->moveFunc)
if (pXSprite->aiState && pXSprite->aiState->moveFunc)
pXSprite->aiState->moveFunc(&bloodActors[pXSprite->reference]);
if (pXSprite->aiState->thinkFunc && (gFrameCount & 3) == (nSprite & 3))
if (pXSprite->aiState && pXSprite->aiState->thinkFunc && (gFrameCount & 3) == (nSprite & 3))
pXSprite->aiState->thinkFunc(&bloodActors[pXSprite->reference]);
switch (pSprite->type) {
@ -1505,7 +1532,7 @@ void aiProcessDudes(void) {
GENDUDEEXTRA* pExtra = &gGenDudeExtra[pSprite->index];
if (pExtra->slaveCount > 0) updateTargetOfSlaves(pSprite);
if (pExtra->nLifeLeech >= 0) updateTargetOfLeech(pSprite);
if (pXSprite->stateTimer == 0 && pXSprite->aiState->nextState
if (pXSprite->stateTimer == 0 && pXSprite->aiState && pXSprite->aiState->nextState
&& (pXSprite->aiState->stateTicks > 0 || seqGetStatus(3, pSprite->extra) < 0)) {
aiGenDudeNewState(pSprite, pXSprite->aiState->nextState);
}
@ -1517,7 +1544,7 @@ void aiProcessDudes(void) {
}
#endif
default:
if (pXSprite->stateTimer == 0 && pXSprite->aiState->nextState) {
if (pXSprite->stateTimer == 0 && pXSprite->aiState && pXSprite->aiState->nextState) {
if (pXSprite->aiState->stateTicks > 0)
aiNewState(actor, pXSprite->aiState->nextState);
else if (seqGetStatus(3, nXSprite) < 0)
@ -1813,6 +1840,10 @@ void aiInitSprite(spritetype *pSprite)
// make dude follow the markers
bool uwater = spriteIsUnderwater(pSprite);
if (pXSprite->target <= 0 || sprite[pXSprite->target].type != kMarkerPath) {
pXSprite->target = -1; aiPatrolSetMarker(pSprite, pXSprite);
}
if (stateTimer > 0) {
if (uwater) aiPatrolState(pSprite, kAiStatePatrolWaitW);
else if (pXSprite->unused1 & kDudeFlagCrouch) aiPatrolState(pSprite, kAiStatePatrolWaitC);

View file

@ -101,7 +101,7 @@ void aiSetTarget(XSPRITE *pXSprite, int x, int y, int z);
void aiSetTarget(XSPRITE *pXSprite, int nTarget);
int aiDamageSprite(DBloodActor* source, DBloodActor* actor, DAMAGE_TYPE nDmgType, int nDamage);
void aiThinkTarget(DBloodActor* actor);
void sub_5F15C(spritetype *pSprite, XSPRITE *pXSprite);
void aiLookForTarget(spritetype *pSprite, XSPRITE *pXSprite);
void aiProcessDudes(void);
void aiInit(void);
void aiInitSprite(spritetype *pSprite);

View file

@ -82,7 +82,7 @@ void SlashSeqCallback(int, DBloodActor* actor)
void StompSeqCallback(int, DBloodActor* actor1)
{
uint8_t vb8[(kMaxSectors+7)>>3];
uint8_t sectmap[(kMaxSectors+7)>>3];
XSPRITE* pXSprite = &actor1->x();
int nSprite = pXSprite->reference;
spritetype *pSprite = &actor1->s();
@ -95,7 +95,8 @@ void StompSeqCallback(int, DBloodActor* actor1)
int nSector = pSprite->sectnum;
int v1c = 5+2*gGameOptions.nDifficulty;
int v10 = 25+30*gGameOptions.nDifficulty;
GetClosestSpriteSectors(nSector, x, y, vc, vb8);
const bool newSectCheckMethod = !cl_bloodvanillaenemies && !VanillaMode(); // use new sector checking logic
GetClosestSpriteSectors(nSector, x, y, vc, sectmap, nullptr, newSectCheckMethod);
char v4 = 0;
int hit = HitScan(pSprite, pSprite->z, dx, dy, 0, CLIPMASK1, 0);
DBloodActor* actor2 = nullptr;
@ -119,7 +120,7 @@ void StompSeqCallback(int, DBloodActor* actor1)
continue;
if (pSprite2->flags&32)
continue;
if (TestBitString(vb8, pSprite2->sectnum) && CheckProximity(pSprite2, x, y, z, nSector, vc))
if (TestBitString(sectmap, pSprite2->sectnum) && CheckProximity(pSprite2, x, y, z, nSector, vc))
{
int top, bottom;
GetSpriteExtents(pSprite, &top, &bottom);
@ -150,7 +151,7 @@ void StompSeqCallback(int, DBloodActor* actor1)
spritetype *pSprite2 = &sprite[nSprite2];
if (pSprite2->flags&32)
continue;
if (TestBitString(vb8, pSprite2->sectnum) && CheckProximity(pSprite2, x, y, z, nSector, vc))
if (TestBitString(sectmap, pSprite2->sectnum) && CheckProximity(pSprite2, x, y, z, nSector, vc))
{
XSPRITE *pXSprite = &xsprite[pSprite2->extra];
if (pXSprite->locked)

View file

@ -202,7 +202,7 @@ static void cultThinkSearch(DBloodActor* actor)
auto pXSprite = &actor->x();
auto pSprite = &actor->s();
aiChooseDirection(pSprite, pXSprite, pXSprite->goalAng);
sub_5F15C(pSprite, pXSprite);
aiLookForTarget(pSprite, pXSprite);
}
static void cultThinkGoto(DBloodActor* actor)
@ -303,7 +303,6 @@ static void cultThinkChase(DBloodActor* actor)
actor->dudeSlope = DivScale(pTarget->z-pSprite->z, nDist, 10);
switch (pSprite->type) {
case kDudeCultistTommy:
#if 0 // apparently this can never be entered.
if (nDist < 0x1e00 && nDist > 0xe00 && abs(nDeltaAngle) < 85 && !TargetNearExplosion(pTarget)
&& (pTarget->flags&2) && gGameOptions.nDifficulty > 2 && IsPlayerSprite(pTarget) && gPlayer[pTarget->type-kDudePlayer1].isRunning
&& Chance(0x8000))
@ -327,9 +326,7 @@ static void cultThinkChase(DBloodActor* actor)
break;
}
}
else
#endif
if (nDist < 0x4600 && abs(nDeltaAngle) < 28)
else if (nDist < 0x4600 && abs(nDeltaAngle) < 28)
{
int hit = HitScan(pSprite, pSprite->z, dx, dy, 0, CLIPMASK1, 0);
switch (hit)
@ -375,7 +372,7 @@ static void cultThinkChase(DBloodActor* actor)
break;
case kDudeCultistShotgun:
if (nDist < 0x2c00 && nDist > 0x1400 && !TargetNearExplosion(pTarget)
&& (pTarget->flags&2) && gGameOptions.nDifficulty >= 2 && IsPlayerSprite(pTarget) /*&& !gPlayer[pTarget->type-kDudePlayer1].isRunning*/
&& (pTarget->flags&2) && gGameOptions.nDifficulty >= 2 && IsPlayerSprite(pTarget) && !gPlayer[pTarget->type-kDudePlayer1].isRunning
&& Chance(0x8000))
{
int hit = HitScan(pSprite, pSprite->z, dx, dy, 0, CLIPMASK1, 0);
@ -442,7 +439,6 @@ static void cultThinkChase(DBloodActor* actor)
}
break;
case kDudeCultistTesla:
#if 0
if (nDist < 0x1e00 && nDist > 0xe00 && !TargetNearExplosion(pTarget)
&& (pTarget->flags&2) && gGameOptions.nDifficulty > 2 && IsPlayerSprite(pTarget) && gPlayer[pTarget->type-kDudePlayer1].isRunning
&& Chance(0x8000))
@ -466,9 +462,7 @@ static void cultThinkChase(DBloodActor* actor)
break;
}
}
else
#endif
if (nDist < 0x3200 && abs(nDeltaAngle) < 28)
else if (nDist < 0x3200 && abs(nDeltaAngle) < 28)
{
int hit = HitScan(pSprite, pSprite->z, dx, dy, 0, CLIPMASK1, 0);
switch (hit)
@ -557,7 +551,6 @@ static void cultThinkChase(DBloodActor* actor)
}
break;
case kDudeCultistBeast:
#if 0
if (nDist < 0x1e00 && nDist > 0xe00 && !TargetNearExplosion(pTarget)
&& (pTarget->flags&2) && gGameOptions.nDifficulty > 2 && IsPlayerSprite(pTarget) && gPlayer[pTarget->type-kDudePlayer1].isRunning
&& Chance(0x8000))
@ -581,9 +574,7 @@ static void cultThinkChase(DBloodActor* actor)
break;
}
}
else
#endif
if (nDist < 0x3200 && abs(nDeltaAngle) < 28)
else if (nDist < 0x3200 && abs(nDeltaAngle) < 28)
{
int hit = HitScan(pSprite, pSprite->z, dx, dy, 0, CLIPMASK1, 0);
switch (hit)

View file

@ -269,7 +269,7 @@ static void gargThinkSearch(DBloodActor* actor)
auto pXSprite = &actor->x();
auto pSprite = &actor->s();
aiChooseDirection(pSprite, pXSprite, pXSprite->goalAng);
sub_5F15C(pSprite, pXSprite);
aiLookForTarget(pSprite, pXSprite);
}
static void gargThinkGoto(DBloodActor* actor)

View file

@ -42,25 +42,22 @@ AISTATE spidGoto = { kAiStateMove, 7, -1, 600, NULL, aiMoveForward, spidThinkGot
AISTATE spidSearch = { kAiStateSearch, 7, -1, 1800, NULL, aiMoveForward, spidThinkSearch, &spidIdle };
AISTATE spidBite = { kAiStateChase, 6, nSpidBiteClient, 60, NULL, NULL, NULL, &spidChase };
AISTATE spidJump = { kAiStateChase, 8, nSpidJumpClient, 60, NULL, aiMoveForward, NULL, &spidChase };
AISTATE spid13A92C = { kAiStateOther, 0, dword_279B50, 60, NULL, NULL, NULL, &spidIdle };
AISTATE spidBirth = { kAiStateOther, 0, nSpidBirthClient, 60, NULL, NULL, NULL, &spidIdle };
static char sub_70D30(XSPRITE *pXDude, int a2, int a3)
static char SpidPoisonPlayer(XSPRITE *pXDude, int nBlind, int max)
{
assert(pXDude != NULL);
int nDude = pXDude->reference;
spritetype *pDude = &sprite[nDude];
if (IsPlayerSprite(pDude))
{
a2 <<= 4;
a3 <<= 4;
if (IsPlayerSprite(pDude))
nBlind <<= 4;
max <<= 4;
PLAYER *pPlayer = &gPlayer[pDude->type-kDudePlayer1];
if (pPlayer->blindEffect < max)
{
PLAYER *pPlayer = &gPlayer[pDude->type-kDudePlayer1];
if (a3 > pPlayer->blindEffect)
{
pPlayer->blindEffect = ClipHigh(pPlayer->blindEffect+a2, a3);
return 1;
}
pPlayer->blindEffect = ClipHigh(pPlayer->blindEffect+nBlind, max);
return 1;
}
}
return 0;
@ -93,11 +90,11 @@ void SpidBiteSeqCallback(int, DBloodActor* actor)
break;
case kDudeSpiderRed:
actFireVector(pSprite, 0, 0, dx, dy, dz, kVectorSpiderBite);
if (Chance(0x5000)) sub_70D30(pXTarget, 4, 16);
if (Chance(0x5000)) SpidPoisonPlayer(pXTarget, 4, 16);
break;
case kDudeSpiderBlack:
actFireVector(pSprite, 0, 0, dx, dy, dz, kVectorSpiderBite);
sub_70D30(pXTarget, 8, 16);
SpidPoisonPlayer(pXTarget, 8, 16);
break;
case kDudeSpiderMother: {
actFireVector(pSprite, 0, 0, dx, dy, dz, kVectorSpiderBite);
@ -106,9 +103,9 @@ void SpidBiteSeqCallback(int, DBloodActor* actor)
dy += Random2(2000);
dz += Random2(2000);
actFireVector(pSprite, 0, 0, dx, dy, dz, kVectorSpiderBite);
sub_70D30(pXTarget, 8, 16);
}
SpidPoisonPlayer(pXTarget, 8, 16);
break;
}
}
}
@ -141,7 +138,7 @@ void SpidJumpSeqCallback(int, DBloodActor* actor)
}
}
void sub_71370(int, DBloodActor* actor)
void SpidBirthSeqCallback(int, DBloodActor* actor)
{
XSPRITE* pXSprite = &actor->x();
spritetype* pSprite = &actor->s();
@ -249,7 +246,7 @@ static void spidThinkChase(DBloodActor* actor)
if (nDist < 0x733 && nDist > 0x399 && abs(nDeltaAngle) < 85)
aiNewState(actor, &spidJump);
else if (Chance(0x8000))
aiNewState(actor, &spid13A92C);
aiNewState(actor, &spidBirth);
break;
}

View file

@ -313,7 +313,7 @@ extern AISTATE spidGoto;
extern AISTATE spidSearch;
extern AISTATE spidBite;
extern AISTATE spidJump;
extern AISTATE spid13A92C;
extern AISTATE spidBirth;
extern AISTATE tchernobogIdle;
extern AISTATE tchernobogSearch;

View file

@ -104,6 +104,13 @@ const GENDUDESND gCustomDudeSnd[] = {
{ 9008, 0, 17, false, false }, // transforming in other dude
};
// for kModernThingThrowableRock
short gCustomDudeDebrisPics[6] = {
2406, 2280, 2185, 2155, 2620, 3135
};
GENDUDEEXTRA gGenDudeExtra[kMaxSprites]; // savegame handling in ai.cpp
static void forcePunch(DBloodActor* actor)
@ -318,12 +325,7 @@ static void ThrowThing(DBloodActor* actor, bool impact)
impact = true;
break;
case kModernThingThrowableRock:
int sPics[6];
sPics[0] = 2406; sPics[1] = 2280;
sPics[2] = 2185; sPics[3] = 2155;
sPics[4] = 2620; sPics[5] = 3135;
pThing->picnum = sPics[Random(5)];
pThing->picnum = gCustomDudeDebrisPics[Random(5)];
pThing->xrepeat = pThing->yrepeat = 24 + Random(42);
pThing->cstat |= 0x0001;
pThing->pal = 5;
@ -380,7 +382,7 @@ static void unicultThinkSearch(DBloodActor* actor)
//viewSetSystemMessage("IN SEARCH");
aiChooseDirection(pSprite, pXSprite, pXSprite->goalAng);
sub_5F15C(pSprite, pXSprite);
aiLookForTarget(pSprite, pXSprite);
}
static void unicultThinkGoto(DBloodActor* actor)
@ -617,7 +619,7 @@ static void unicultThinkChase(DBloodActor* actor)
mdist = 2500;
fallthrough__;
case kMissileFireball:
case kMissileFireballNapam:
case kMissileFireballNapalm:
case kMissileFireballCerberus:
case kMissileFireballTchernobog:
if (mdist == defDist) mdist = 3000;
@ -832,7 +834,7 @@ static void unicultThinkChase(DBloodActor* actor)
case kMissileTeslaAlt:
case kMissileFlareAlt:
case kMissileFireball:
case kMissileFireballNapam:
case kMissileFireballNapalm:
case kMissileFireballCerberus:
case kMissileFireballTchernobog: {
// allow attack if dude is far from object, but target is close to it
@ -1326,7 +1328,7 @@ void scaleDamage(XSPRITE* pXSprite) {
curScale[kDmgSpirit] = 32 + Random(18);
break;
case kMissileFireball:
case kMissileFireballNapam:
case kMissileFireballNapalm:
case kMissileFireballCerberus:
case kMissileFireballTchernobog:
curScale[kDmgBurn] = 50;
@ -1624,19 +1626,23 @@ bool doExplosion(spritetype* pSprite, int nType) {
// this function allows to spawn new custom dude and inherit spawner settings,
// so custom dude can have different weapons, hp and so on...
spritetype* genDudeSpawn(spritetype* pSprite, int nDist) {
spritetype* genDudeSpawn(XSPRITE* pXSource, spritetype* pSprite, int nDist) {
spritetype* pSource = pSprite; XSPRITE* pXSource = &xsprite[pSource->extra];
spritetype* pDude = actSpawnSprite(pSprite, 6); XSPRITE* pXDude = &xsprite[pDude->extra];
spritetype* pSource = &sprite[pXSource->reference];
spritetype* pDude = actSpawnSprite(pSprite, kStatDude); XSPRITE* pXDude = &xsprite[pDude->extra];
int x, y, z = pSprite->z, nAngle = pSprite->ang, nType = kDudeModernCustom;
if (nDist > 0) {
x = pSprite->x + mulscale30r(Cos(nAngle), nDist);
y = pSprite->y + mulscale30r(Sin(nAngle), nDist);
} else {
x = pSprite->x;
y = pSprite->y;
}
pDude->type = nType; pDude->ang = nAngle;
@ -1656,7 +1662,8 @@ spritetype* genDudeSpawn(spritetype* pSprite, int nDist) {
pXDude->busyTime = pXSource->busyTime;
// inherit clipdist?
if (pSource->clipdist > 0) pDude->clipdist = pSource->clipdist;
if (pSource->clipdist > 0)
pDude->clipdist = pSource->clipdist;
// inherit custom hp settings
if (pXSource->data4 <= 0) pXDude->health = dudeInfo[nType - kDudeBase].startHealth << 4;
@ -1665,29 +1672,29 @@ spritetype* genDudeSpawn(spritetype* pSprite, int nDist) {
if (pSource->flags & kModernTypeFlag1) {
switch (pSource->type) {
case kModernCustomDudeSpawn:
//inherit pal?
if (pDude->pal <= 0) pDude->pal = pSource->pal;
case kModernCustomDudeSpawn:
//inherit pal?
if (pDude->pal <= 0) pDude->pal = pSource->pal;
// inherit spawn sprite trigger settings, so designer can count monsters.
pXDude->txID = pXSource->txID;
pXDude->command = pXSource->command;
pXDude->triggerOn = pXSource->triggerOn;
pXDude->triggerOff = pXSource->triggerOff;
// inherit spawn sprite trigger settings, so designer can count monsters.
pXDude->txID = pXSource->txID;
pXDude->command = pXSource->command;
pXDude->triggerOn = pXSource->triggerOn;
pXDude->triggerOff = pXSource->triggerOff;
// inherit drop items
pXDude->dropMsg = pXSource->dropMsg;
// inherit drop items
pXDude->dropMsg = pXSource->dropMsg;
// inherit required key so it can be dropped
pXDude->key = pXSource->key;
// inherit required key so it can be dropped
pXDude->key = pXSource->key;
// inherit dude flags
pXDude->dudeDeaf = pXSource->dudeDeaf;
pXDude->dudeGuard = pXSource->dudeGuard;
pXDude->dudeAmbush = pXSource->dudeAmbush;
pXDude->dudeFlag4 = pXSource->dudeFlag4;
pXDude->unused1 = pXSource->unused1;
break;
// inherit dude flags
pXDude->dudeDeaf = pXSource->dudeDeaf;
pXDude->dudeGuard = pXSource->dudeGuard;
pXDude->dudeAmbush = pXSource->dudeAmbush;
pXDude->dudeFlag4 = pXSource->dudeFlag4;
pXDude->unused1 = pXSource->unused1;
break;
}
}
@ -1697,6 +1704,7 @@ spritetype* genDudeSpawn(spritetype* pSprite, int nDist) {
pDude->yrepeat = pSource->yrepeat;
}
gKillMgr.AddNewKill(1);
aiInitSprite(pDude);
return pDude;
}

View file

@ -209,7 +209,7 @@ void aiGenDudeNewState(spritetype* pSprite, AISTATE* pAIState);
int getGenDudeMoveSpeed(spritetype* pSprite, int which, bool mul, bool shift);
int checkAttackState(DBloodActor* actor);
bool doExplosion(spritetype* pSprite, int nType);
spritetype* genDudeSpawn(spritetype* pSprite, int nDist);
spritetype* genDudeSpawn(XSPRITE* pXSource, spritetype* pSprite, int nDist);
void genDudeTransform(spritetype* pSprite);
void dudeLeechOperate(spritetype* pSprite, XSPRITE* pXSprite, EVENT a3);
int getDodgeChance(spritetype* pSprite);

View file

@ -88,7 +88,7 @@ static void zombaThinkSearch(DBloodActor* actor)
auto pXSprite = &actor->x();
auto pSprite = &actor->s();
aiChooseDirection(pSprite, pXSprite, pXSprite->goalAng);
sub_5F15C(pSprite, pXSprite);
aiLookForTarget(pSprite, pXSprite);
}
static void zombaThinkGoto(DBloodActor* actor)

View file

@ -70,6 +70,9 @@ static void RotateXZ(int *pX, int *, int *pZ, int ang)
template<typename T> tspritetype* viewInsertTSprite(spritetype* tsprite, int& spritesortcnt, int nSector, int nStatnum, T const * const pSprite)
{
if (spritesortcnt >= MAXSPRITESONSCREEN)
return nullptr;
int nTSprite = spritesortcnt;
tspritetype *pTSprite = &tsprite[nTSprite];
memset(pTSprite, 0, sizeof(tspritetype));
@ -95,7 +98,7 @@ template<typename T> tspritetype* viewInsertTSprite(spritetype* tsprite, int& sp
return pTSprite;
}
static const int effectDetail[] = {
static const int effectDetail[kViewEffectMax] = {
4, 4, 4, 4, 0, 0, 0, 0, 0, 1, 4, 4, 0, 0, 0, 1, 0, 0, 0
};
@ -130,10 +133,37 @@ static tspritetype *viewAddEffect(spritetype* tsprite, int& spritesortcnt, int n
if (gDetail < effectDetail[nViewEffect] || nTSprite >= MAXSPRITESONSCREEN) return NULL;
switch (nViewEffect)
{
case kViewEffectSpotProgress: {
XSPRITE* pXSprite = &xsprite[pTSprite->extra];
int perc = (100 * pXSprite->data3) / kMaxPatrolSpotValue;
int width = (94 * pXSprite->data3) / kMaxPatrolSpotValue;
int top, bottom;
GetSpriteExtents(pTSprite, &top, &bottom);
auto pNSprite2 = viewInsertTSprite(tsprite, spritesortcnt, pTSprite->sectnum, 32767, pTSprite);
if (!pNSprite2)
break;
pNSprite2->picnum = 2203;
pNSprite2->xrepeat = width;
pNSprite2->yrepeat = 20;
pNSprite2->pal = 10;
if (perc >= 75) pNSprite2->pal = 0;
else if (perc >= 50) pNSprite2->pal = 6;
pNSprite2->z = top - 2048;
pNSprite2->shade = -128;
break;
}
case kViewEffectAtom:
for (int i = 0; i < 16; i++)
{
auto pNSprite = viewInsertTSprite(tsprite, spritesortcnt, pTSprite->sectnum, 32767, pTSprite);
if (!pNSprite)
break;
int ang = (PlayClock*2048)/120;
int nRand1 = dword_172CE0[i][0];
int nRand2 = dword_172CE0[i][1];
@ -157,6 +187,9 @@ static tspritetype *viewAddEffect(spritetype* tsprite, int& spritesortcnt, int n
int top, bottom;
GetSpriteExtents(pTSprite, &top, &bottom);
auto pNSprite = viewInsertTSprite(tsprite, spritesortcnt, pTSprite->sectnum, 32767, pTSprite);
if (!pNSprite)
break;
pNSprite->shade = -128;
pNSprite->pal = 0;
pNSprite->z = top;
@ -170,6 +203,9 @@ static tspritetype *viewAddEffect(spritetype* tsprite, int& spritesortcnt, int n
case kViewEffectTesla:
{
auto pNSprite = viewInsertTSprite(tsprite, spritesortcnt, pTSprite->sectnum, 32767, pTSprite);
if (!pNSprite)
break;
pNSprite->z = pTSprite->z;
pNSprite->cstat |= 2;
pNSprite->shade = -128;
@ -181,6 +217,9 @@ static tspritetype *viewAddEffect(spritetype* tsprite, int& spritesortcnt, int n
case kViewEffectShoot:
{
auto pNSprite = viewInsertTSprite(tsprite, spritesortcnt, pTSprite->sectnum, 32767, pTSprite);
if (!pNSprite)
break;
pNSprite->shade = -128;
pNSprite->pal = 0;
pNSprite->xrepeat = pNSprite->yrepeat = 64;
@ -190,6 +229,9 @@ static tspritetype *viewAddEffect(spritetype* tsprite, int& spritesortcnt, int n
case kViewEffectReflectiveBall:
{
auto pNSprite = viewInsertTSprite(tsprite, spritesortcnt, pTSprite->sectnum, 32767, pTSprite);
if (!pNSprite)
break;
pNSprite->shade = 26;
pNSprite->pal = 0;
pNSprite->cstat |= 2;
@ -200,6 +242,9 @@ static tspritetype *viewAddEffect(spritetype* tsprite, int& spritesortcnt, int n
case kViewEffectPhase:
{
auto pNSprite = viewInsertTSprite(tsprite, spritesortcnt, pTSprite->sectnum, 32767, pTSprite);
if (!pNSprite)
break;
int top, bottom;
GetSpriteExtents(pTSprite, &top, &bottom);
pNSprite->shade = 26;
@ -225,6 +270,9 @@ static tspritetype *viewAddEffect(spritetype* tsprite, int& spritesortcnt, int n
{
int nSector = pTSprite->sectnum;
auto pNSprite = viewInsertTSprite<tspritetype>(tsprite, spritesortcnt, nSector, 32767, NULL);
if (!pNSprite)
break;
int nLen = 128+(i<<7);
int x = MulScale(nLen, Cos(nAng), 30);
pNSprite->x = pTSprite->x + x;
@ -249,6 +297,9 @@ static tspritetype *viewAddEffect(spritetype* tsprite, int& spritesortcnt, int n
case kViewEffectFlame:
{
auto pNSprite = viewInsertTSprite(tsprite, spritesortcnt, pTSprite->sectnum, 32767, pTSprite);
if (!pNSprite)
break;
pNSprite->shade = -128;
pNSprite->z = pTSprite->z;
pNSprite->picnum = 908;
@ -259,6 +310,9 @@ static tspritetype *viewAddEffect(spritetype* tsprite, int& spritesortcnt, int n
case kViewEffectSmokeHigh:
{
auto pNSprite = viewInsertTSprite(tsprite, spritesortcnt, pTSprite->sectnum, 32767, pTSprite);
if (!pNSprite)
break;
int top, bottom;
GetSpriteExtents(pTSprite, &top, &bottom);
pNSprite->z = top;
@ -275,6 +329,9 @@ static tspritetype *viewAddEffect(spritetype* tsprite, int& spritesortcnt, int n
case kViewEffectSmokeLow:
{
auto pNSprite = viewInsertTSprite(tsprite, spritesortcnt, pTSprite->sectnum, 32767, pTSprite);
if (!pNSprite)
break;
int top, bottom;
GetSpriteExtents(pTSprite, &top, &bottom);
pNSprite->z = bottom;
@ -291,6 +348,9 @@ static tspritetype *viewAddEffect(spritetype* tsprite, int& spritesortcnt, int n
case kViewEffectTorchHigh:
{
auto pNSprite = viewInsertTSprite(tsprite, spritesortcnt, pTSprite->sectnum, 32767, pTSprite);
if (!pNSprite)
break;
int top, bottom;
GetSpriteExtents(pTSprite, &top, &bottom);
pNSprite->z = top;
@ -302,6 +362,9 @@ static tspritetype *viewAddEffect(spritetype* tsprite, int& spritesortcnt, int n
case kViewEffectTorchLow:
{
auto pNSprite = viewInsertTSprite(tsprite, spritesortcnt, pTSprite->sectnum, 32767, pTSprite);
if (!pNSprite)
break;
int top, bottom;
GetSpriteExtents(pTSprite, &top, &bottom);
pNSprite->z = bottom;
@ -315,6 +378,9 @@ static tspritetype *viewAddEffect(spritetype* tsprite, int& spritesortcnt, int n
if (r_shadows)
{
auto pNSprite = viewInsertTSprite(tsprite, spritesortcnt, pTSprite->sectnum, 32767, pTSprite);
if (!pNSprite)
break;
pNSprite->z = getflorzofslope(pTSprite->sectnum, pNSprite->x, pNSprite->y);
pNSprite->shade = 127;
pNSprite->cstat |= 2;
@ -331,6 +397,9 @@ static tspritetype *viewAddEffect(spritetype* tsprite, int& spritesortcnt, int n
case kViewEffectFlareHalo:
{
auto pNSprite = viewInsertTSprite(tsprite, spritesortcnt, pTSprite->sectnum, 32767, pTSprite);
if (!pNSprite)
break;
pNSprite->shade = -128;
pNSprite->pal = 2;
pNSprite->cstat |= 2;
@ -343,6 +412,9 @@ static tspritetype *viewAddEffect(spritetype* tsprite, int& spritesortcnt, int n
case kViewEffectCeilGlow:
{
auto pNSprite = viewInsertTSprite(tsprite, spritesortcnt, pTSprite->sectnum, 32767, pTSprite);
if (!pNSprite)
break;
sectortype *pSector = &sector[pTSprite->sectnum];
pNSprite->x = pTSprite->x;
pNSprite->y = pTSprite->y;
@ -359,6 +431,9 @@ static tspritetype *viewAddEffect(spritetype* tsprite, int& spritesortcnt, int n
case kViewEffectFloorGlow:
{
auto pNSprite = viewInsertTSprite(tsprite, spritesortcnt, pTSprite->sectnum, 32767, pTSprite);
if (!pNSprite)
break;
sectortype *pSector = &sector[pTSprite->sectnum];
pNSprite->x = pTSprite->x;
pNSprite->y = pTSprite->y;
@ -376,6 +451,9 @@ static tspritetype *viewAddEffect(spritetype* tsprite, int& spritesortcnt, int n
case kViewEffectSpear:
{
auto pNSprite = viewInsertTSprite(tsprite, spritesortcnt, pTSprite->sectnum, 32767, pTSprite);
if (!pNSprite)
break;
pNSprite->z = pTSprite->z;
if (gDetail > 1)
pNSprite->cstat |= 514;
@ -390,29 +468,34 @@ static tspritetype *viewAddEffect(spritetype* tsprite, int& spritesortcnt, int n
assert(pTSprite->type >= kDudePlayer1 && pTSprite->type <= kDudePlayer8);
PLAYER *pPlayer = &gPlayer[pTSprite->type-kDudePlayer1];
WEAPONICON weaponIcon = gWeaponIcon[pPlayer->curWeapon];
const int nTile = weaponIcon.nTile;
auto& nTile = weaponIcon.nTile;
if (nTile < 0) break;
auto pNSprite = viewInsertTSprite(tsprite, spritesortcnt, pTSprite->sectnum, 32767, pTSprite);
if (!pNSprite)
break;
pNSprite->x = pTSprite->x;
pNSprite->y = pTSprite->y;
pNSprite->z = pTSprite->z-(32<<8);
pNSprite->z -= weaponIcon.zOffset<<8; // offset up
pNSprite->picnum = nTile;
pNSprite->shade = pTSprite->shade;
pNSprite->xrepeat = 32;
pNSprite->yrepeat = 32;
const int nVoxel = voxelIndex[nTile];
auto& nVoxel = voxelIndex[nTile];
if (cl_showweapon == 2 && r_voxels && nVoxel != -1)
{
pNSprite->ang = (gView->pSprite->ang + 512) & 2047; // always face viewer
pNSprite->cstat |= 48;
pNSprite->cstat &= ~8;
pNSprite->picnum = nVoxel;
pNSprite->z -= weaponIcon.zOffset<<8;
const int lifeLeech = 9;
if (pPlayer->curWeapon == lifeLeech)
if (pPlayer->curWeapon == kWeapLifeLeech) // position lifeleech behind player
{
pNSprite->x -= MulScale(128, Cos(pNSprite->ang), 30);
pNSprite->y -= MulScale(128, Sin(pNSprite->ang), 30);
pNSprite->x += MulScale(128, Cos(gView->pSprite->ang), 30);
pNSprite->y += MulScale(128, Sin(gView->pSprite->ang), 30);
}
if ((pPlayer->curWeapon == kWeapLifeLeech) || (pPlayer->curWeapon == kWeapVoodooDoll)) // make lifeleech/voodoo doll always face viewer like sprite
pNSprite->ang = (pNSprite->ang + 512) & 2047; // offset angle 90 degrees
}
break;
}
@ -422,7 +505,7 @@ static tspritetype *viewAddEffect(spritetype* tsprite, int& spritesortcnt, int n
static void viewApplyDefaultPal(tspritetype *pTSprite, sectortype const *pSector)
{
int const nXSector = pSector->extra;
auto& nXSector = pSector->extra;
XSECTOR const *pXSector = nXSector >= 0 ? &xsector[nXSector] : NULL;
if (pXSector && pXSector->color && (VanillaMode() || pSector->floorpal != 0))
{
@ -818,6 +901,12 @@ void viewProcessSprites(spritetype* tsprite, int& spritesortcnt, int32_t cX, int
viewAddEffect(tsprite, spritesortcnt, nTSprite, kViewEffectShadow);
}
}
if (gModernMap) { // add target spot indicator for patrol dudes
XSPRITE* pTXSprite = &xsprite[pTSprite->extra];
if (pTXSprite->dudeFlag4 && aiInPatrolState(pTXSprite->aiState) && pTXSprite->data3 > 0 && pTXSprite->data3 <= kMaxPatrolSpotValue)
viewAddEffect(tsprite, spritesortcnt, nTSprite, kViewEffectSpotProgress);
}
break;
}
case kStatTraps: {

View file

@ -218,7 +218,7 @@ int RFS::Open(int lumpnum)
}
int fileSize = (int)hFile.GetLength();
buffer.Resize(fileSize);
buffer.Resize(fileSize+1);
_ptr = buffer.Data();
if (_ptr == NULL) {
Printf("BARF: Not enough memory to read %d", lumpnum);
@ -226,11 +226,12 @@ int RFS::Open(int lumpnum)
}
hFile.Read(_ptr, fileSize);
buffer[fileSize] = '\n';
_curLine = 0;
_pUnknown2 = _ptr;
_curChar = '\n';
_pEnd = &_ptr[fileSize - 1];
_pEnd = &_ptr[fileSize];
return 0;
}

View file

@ -84,7 +84,6 @@ void StartLevel(MapRecord* level, bool newgame)
if (!level) return;
gFrameCount = 0;
PlayClock = 0;
STAT_Update(0);
EndLevel();
inputState.ClearAllInput();
currentLevel = level;
@ -92,7 +91,7 @@ void StartLevel(MapRecord* level, bool newgame)
if (gGameOptions.nGameType == 0)
{
///////
gGameOptions.weaponsV10x = gWeaponsV10x;
gGameOptions.weaponsV10x = cl_bloodoldweapbalance;
///////
}
#if 0
@ -201,6 +200,8 @@ void StartLevel(MapRecord* level, bool newgame)
pPlayer->qavLoop = gPlayerTemp[i].qavLoop;
pPlayer->weaponTimer = gPlayerTemp[i].weaponTimer;
pPlayer->nextWeapon = gPlayerTemp[i].nextWeapon;
pPlayer->qavLastTick = gPlayerTemp[i].qavLastTick;
pPlayer->qavTimer = gPlayerTemp[i].qavTimer;
}
}
PreloadCache();
@ -241,6 +242,12 @@ void GameInterface::NextLevel(MapRecord *map, int skill)
NewLevel(map, skill, false);
}
int GameInterface::GetCurrentSkill()
{
return gGameOptions.nDifficulty;
}
void GameInterface::Ticker()
{
for (int i = connecthead; i >= 0; i = connectpoint2[i])

View file

@ -149,6 +149,10 @@ struct GameInterface : public ::GameInterface
void EnterPortal(spritetype* viewer, int type) override;
void LeavePortal(spritetype* viewer, int type) override;
void LoadGameTextures() override;
int GetCurrentSkill() override;
bool IsQAVInterpTypeValid(const FString& type) override;
void AddQAVInterpProps(const int& res_id, const FString& interptype, const bool& loopable, const TMap<int, TArray<int>>& ignoredata) override;
void RemoveQAVInterpProps(const int& res_id) override;
GameStats getStats() override;
};

View file

@ -172,17 +172,35 @@ enum {
// (weapons)
kItemWeaponBase = 40,
kItemWeaponRandom = kItemWeaponBase,
kItemWeaponSawedoff = 41,
kItemWeaponTommygun = 42,
kItemWeaponFlarePistol = 43,
kItemWeaponVoodooDoll = 44,
kItemWeaponTeslaCannon = 45,
kItemWeaponNapalmLauncher = 46,
kItemWeaponPitchfork = 47,
kItemWeaponSprayCan = 48,
kItemWeaponTNT = 49,
kItemWeaponLifeLeech = 50,
kItemWeaponMax = 51,
// items (ammos)
kItemAmmoBase = 60,
kItemAmmoSprayCan = kItemAmmoBase,
kItemAmmoTNTBundle = 62,
kItemAmmoTNTBox = 63,
kItemAmmoProxBombBundle = 64,
kItemAmmoRemoteBombBundle = 65,
kItemAmmoTrappedSoul = 66,
kItemAmmoSawedoffFew = 67,
kItemAmmoSawedoffBox = 68,
kItemAmmoTommygunFew = 69,
kAmmoItemVoodooDoll = 70,
kItemAmmoVoodooDoll = 70,
kItemAmmoTommygunDrum = 72,
kItemAmmoTeslaCharge = 73,
kItemAmmoFlares = 76,
kItemAmmoGasolineCan = 79,
kItemAmmoMax = 81,
kItemBase = 100,
@ -302,7 +320,7 @@ enum {
kMissilePukeGreen = 309,
kMissileUnused = 310,
kMissileArcGargoyle = 311,
kMissileFireballNapam = 312,
kMissileFireballNapalm = 312,
kMissileFireballCerberus = 313,
kMissileFireballTchernobog = 314,
kMissileLifeLeechRegular = 315,
@ -408,6 +426,9 @@ kAiStatePatrolWaitW,
kAiStatePatrolMoveL,
kAiStatePatrolMoveC,
kAiStatePatrolMoveW,
kAiStatePatrolTurnL,
kAiStatePatrolTurnC,
kAiStatePatrolTurnW,
kAiStatePatrolMax,
};
@ -446,6 +467,25 @@ enum
kAng360 = 2048,
};
// Weapon numbers
enum
{
kWeapNone = 0,
kWeapPitchFork = 1,
kWeapFlareGun = 2,
kWeapShotgun = 3,
kWeapTommyGun = 4,
kWeapNapalm = 5,
kWeapDynamite = 6,
kWeapSpraycan = 7,
kWeapTeslaCannon = 8,
kWeapLifeLeech = 9,
kWeapVoodooDoll = 10,
kWeapProximity = 11,
kWeapRemote = 12,
kWeapBeast = 13,
};
// -------------------------------
#pragma pack(push,1)

View file

@ -41,8 +41,7 @@ BEGIN_BLD_NS
class CGameMenuItemQAV
{
public:
int m_nX, m_nY;
TArray<uint8_t> raw;
QAV* data;
int duration;
int lastTick;
bool bWideScreen;
@ -53,24 +52,19 @@ public:
CGameMenuItemQAV::CGameMenuItemQAV(int a3, int a4, const char* name, bool widescreen, bool clearbackground)
{
m_nY = a4;
m_nX = a3;
bWideScreen = widescreen;
bClearBackground = clearbackground;
if (name)
{
// NBlood read this directly from the file system cache, but let's better store the data locally for robustness.
raw = fileSystem.LoadFile(name, 0);
if (raw.Size() != 0)
data = getQAV(fileSystem.GetResourceId(fileSystem.FindFile(name)));
if (data)
{
auto data = (QAV*)raw.Data();
data->nSprite = -1;
data->x = m_nX;
data->y = m_nY;
//data->Preload();
data->x = a3;
data->y = a4;
duration = data->duration;
lastTick = I_GetBuildTime();
lastTick = I_GetTime(data->ticrate);
}
}
}
@ -80,20 +74,18 @@ void CGameMenuItemQAV::Draw(void)
if (bClearBackground)
twod->ClearScreen();
if (raw.Size() > 0)
if (data)
{
auto data = (QAV*)raw.Data();
int backFC = PlayClock;
int currentclock = I_GetBuildTime();
PlayClock = currentclock;
int nTicks = currentclock - lastTick;
lastTick = currentclock;
duration -= nTicks;
qavProcessTicker(data, &duration, &lastTick);
if (duration <= 0 || duration > data->duration)
{
duration = data->duration;
}
data->Play(data->duration - duration - nTicks, data->duration - duration, -1, NULL);
auto currentDuration = data->duration - duration;
auto smoothratio = I_GetTimeFrac(data->ticrate) * MaxSmoothRatio;
data->Play(currentDuration - data->ticksPerFrame, currentDuration, -1, NULL);
if (bWideScreen)
{
@ -102,15 +94,13 @@ void CGameMenuItemQAV::Draw(void)
int backX = data->x;
for (int i = 0; i < nCount; i++)
{
data->Draw(data->duration - duration, 10 + kQavOrientationLeft, 0, 0, false);
data->Draw(currentDuration, 10 + kQavOrientationLeft, 0, 0, false, smoothratio);
data->x += 320;
}
data->x = backX;
}
else
data->Draw(data->duration - duration, 10, 0, 0, false);
PlayClock = backFC;
data->Draw(currentDuration, 10, 0, 0, false, smoothratio);
}
}

View file

@ -935,7 +935,7 @@ void dbLoadMap(const char *pPath, int *pX, int *pY, int *pZ, short *pAngle, shor
pXSprite->Touch = bitReader.readUnsigned(1);
pXSprite->Sight = bitReader.readUnsigned(1);
pXSprite->Proximity = bitReader.readUnsigned(1);
bitReader.readUnsigned(2);
pXSprite->unused3 = bitReader.readUnsigned(2);
pXSprite->lSkill = bitReader.readUnsigned(5);
pXSprite->lS = bitReader.readUnsigned(1);
pXSprite->lB = bitReader.readUnsigned(1);
@ -950,7 +950,7 @@ void dbLoadMap(const char *pPath, int *pX, int *pY, int *pZ, short *pAngle, shor
pXSprite->medium = bitReader.readUnsigned(2);
pXSprite->respawn = bitReader.readUnsigned(2);
pXSprite->data4 = bitReader.readUnsigned(16);
bitReader.readUnsigned(6);
pXSprite->unused4 = bitReader.readUnsigned(6);
pXSprite->lockMsg = bitReader.readUnsigned(8);
pXSprite->health = bitReader.readUnsigned(12);
pXSprite->dudeDeaf = bitReader.readUnsigned(1);

View file

@ -132,6 +132,8 @@ struct XSPRITE {
uint8_t lockMsg; // Lock msg
int8_t dodgeDir; // Dude dodge direction
uint8_t unused1; // modern flags
uint8_t unused3; // something about sight checks
uint8_t unused4; // patrol turn delay
};

View file

@ -172,9 +172,12 @@ bool CheckProximityPoint(int nX1, int nY1, int nZ1, int nX2, int nY2, int nZ2, i
int oY = abs(nY2-nY1)>>4;
if (oY >= nDist)
return 0;
int oZ = abs(nZ2-nZ1)>>4;
if (oZ >= nDist)
return 0;
if (nZ2 != nZ1)
{
int oZ = abs(nZ2-nZ1)>>8;
if (oZ >= nDist)
return 0;
}
if (approxDist(oX, oY) >= nDist) return 0;
return 1;
}
@ -834,8 +837,12 @@ int GetClosestSectors(int nSector, int x, int y, int nDist, short *pSectors, cha
return n;
}
int GetClosestSpriteSectors(int nSector, int x, int y, int nDist, uint8_t *pSectBit, short *walls)
int GetClosestSpriteSectors(int nSector, int x, int y, int nDist, uint8_t *pSectBit, short *pWalls, bool newSectCheckMethod)
{
// by default this function fails with sectors that linked with wide spans, or there was more than one link to the same sector. for example...
// E6M1: throwing TNT on the stone footpath while standing on the brown road will fail due to the start/end points of the span being too far away. it'll only do damage at one end of the road
// E1M2: throwing TNT at the double doors while standing on the train platform
// by setting newSectCheckMethod to true these issues will be resolved
static short pSectors[kMaxSectors];
uint8_t sectbits[(kMaxSectors+7)>>3];
memset(sectbits, 0, sizeof(sectbits));
@ -848,36 +855,73 @@ int GetClosestSpriteSectors(int nSector, int x, int y, int nDist, uint8_t *pSect
memset(pSectBit, 0, (kMaxSectors+7)>>3);
SetBitString(pSectBit, nSector);
}
while (i < n)
while (i < n) // scan through sectors
{
int nCurSector = pSectors[i];
int nStartWall = sector[nCurSector].wallptr;
int nEndWall = nStartWall + sector[nCurSector].wallnum;
walltype *pWall = &wall[nStartWall];
for (int j = nStartWall; j < nEndWall; j++, pWall++)
const int nCurSector = pSectors[i];
const int nStartWall = sector[nCurSector].wallptr;
const int nEndWall = nStartWall + sector[nCurSector].wallnum;
for (int j = nStartWall; j < nEndWall; j++) // scan each wall of current sector for new sectors
{
int nNextSector = pWall->nextsector;
if (nNextSector < 0)
const walltype *pWall = &wall[j];
const int nNextSector = pWall->nextsector;
if (nNextSector < 0) // if next wall isn't linked to a sector, skip
continue;
if (TestBitString(sectbits, nNextSector))
if (TestBitString(sectbits, nNextSector)) // if we've already checked this sector, skip
continue;
SetBitString(sectbits, nNextSector);
if (CheckProximityWall(wall[j].point2, x, y, nDist))
bool setSectBit = true;
bool withinRange = CheckProximityWall(pWall->point2, x, y, nDist);
if (newSectCheckMethod && !withinRange) // if range check failed, try comparing midpoints/subdivides of wall span
{
for (int k = (j+1); k < nEndWall; k++) // scan through the rest of the sector's walls
{
if (wall[k].nextsector == nNextSector) // if the next walls still reference the sector, then don't flag the sector as checked (yet)
{
setSectBit = false;
break;
}
}
const int nWallA = j;
const int nWallB = wall[nWallA].point2;
int x1 = wall[nWallA].x, y1 = wall[nWallA].y;
int x2 = wall[nWallB].x, y2 = wall[nWallB].y;
int nLength = approxDist(x1-x2, y1-y2);
const int nDist2 = (nDist+(nDist>>1))<<4;
nLength = ClipRange(nLength / nDist2, 1, 4); // never split more than 4 times
for (int k = 0; true; k++) // subdivide span into smaller chunks towards direction
{
const int xcenter = (x1+x2)>>1, ycenter = (y1+y2)>>1;
withinRange = CheckProximityPoint(xcenter, ycenter, 0, x, y, 0, nDist);
if (withinRange)
break;
if (k == (nLength-1)) // reached end
break;
const bool bDir = approxDist(x-x1, y-y1) < approxDist(x-x2, y-y2);
if (bDir) // step closer and check again
x2 = xcenter, y2 = ycenter;
else
x1 = xcenter, y1 = ycenter;
}
}
if (withinRange) // if new sector is within range, set to current sector and test walls
{
setSectBit = true; // sector is within range, set as checked
if (pSectBit)
SetBitString(pSectBit, nNextSector);
pSectors[n++] = nNextSector;
if (walls && pWall->extra > 0)
if (pWalls && pWall->extra > 0)
{
XWALL *pXWall = &xwall[pWall->extra];
if (pXWall->triggerVector && !pXWall->isTriggered && !pXWall->state)
walls[m++] = j;
pWalls[m++] = j;
}
}
if (setSectBit)
SetBitString(sectbits, nNextSector);
}
i++;
}
if (walls) walls[m] = -1;
pSectors[n] = -1;
if (pWalls) pWalls[m] = -1;
return n;
}

View file

@ -82,7 +82,7 @@ void GetZRangeAtXYZ(int x, int y, int z, int nSector, int *ceilZ, int *ceilHit,
int GetDistToLine(int x1, int y1, int x2, int y2, int x3, int y3);
unsigned int ClipMove(int *x, int *y, int *z, int *nSector, int xv, int yv, int wd, int cd, int fd, unsigned int nMask);
int GetClosestSectors(int nSector, int x, int y, int nDist, short *pSectors, char *pSectBit);
int GetClosestSpriteSectors(int nSector, int x, int y, int nDist, uint8_t *pSectBit, short *affx = nullptr);
int GetClosestSpriteSectors(int nSector, int x, int y, int nDist, uint8_t *pSectBit, short *pWalls = nullptr, bool newSectCheckMethod = false);
int picWidth(short nPic, short repeat);
int picHeight(short nPic, short repeat);

View file

@ -39,8 +39,4 @@ bool gInfiniteAmmo;
int32_t gDeliriumBlur = 1;
bool gFogMode = false;
//////////
int gWeaponsV10x;
/////////
END_BLD_NS

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