diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 789d0aa33..860efc6bc 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1154,6 +1154,7 @@ set (PCH_SOURCES r_data/models/models_md2.cpp r_data/models/models_voxel.cpp r_data/models/models_ue1.cpp + r_data/models/models_obj.cpp scripting/symbols.cpp scripting/types.cpp scripting/thingdef.cpp diff --git a/src/configfile.cpp b/src/configfile.cpp index 2319f2822..b841e8d3d 100644 --- a/src/configfile.cpp +++ b/src/configfile.cpp @@ -38,6 +38,7 @@ #include "doomtype.h" #include "configfile.h" +#include "files.h" #include "m_random.h" #define READBUFFERSIZE 256 @@ -601,17 +602,16 @@ FConfigFile::FConfigEntry *FConfigFile::NewConfigEntry ( void FConfigFile::LoadConfigFile () { - FILE *file = fopen (PathName, "r"); + FileReader file; bool succ; FileExisted = false; - if (file == NULL) + if (!file.OpenFile (PathName)) { return; } - succ = ReadConfig (file); - fclose (file); + succ = ReadConfig (&file); FileExisted = succ; } @@ -787,7 +787,7 @@ FConfigFile::FConfigEntry *FConfigFile::ReadMultiLineValue(void *file, FConfigSe char *FConfigFile::ReadLine (char *string, int n, void *file) const { - return fgets (string, n, (FILE *)file); + return ((FileReader *)file)->Gets (string, n); } //==================================================================== @@ -805,7 +805,7 @@ bool FConfigFile::WriteConfigFile () const return true; } - FILE *file = fopen (PathName, "w"); + FileWriter *file = FileWriter::Open (PathName); FConfigSection *section; FConfigEntry *entry; @@ -820,27 +820,27 @@ bool FConfigFile::WriteConfigFile () const entry = section->RootEntry; if (section->Note.IsNotEmpty()) { - fputs (section->Note.GetChars(), file); + file->Write (section->Note.GetChars(), section->Note.Len()); } - fprintf (file, "[%s]\n", section->SectionName.GetChars()); + file->Printf ("[%s]\n", section->SectionName.GetChars()); while (entry != NULL) { if (strpbrk(entry->Value, "\r\n") == NULL) { // Single-line value - fprintf (file, "%s=%s\n", entry->Key, entry->Value); + file->Printf ("%s=%s\n", entry->Key, entry->Value); } else { // Multi-line value const char *endtag = GenerateEndTag(entry->Value); - fprintf (file, "%s=<<<%s\n%s\n>>>%s\n", entry->Key, + file->Printf ("%s=<<<%s\n%s\n>>>%s\n", entry->Key, endtag, entry->Value, endtag); } entry = entry->Next; } section = section->Next; - fputs ("\n", file); + file->Write ("\n", 1); } - fclose (file); + delete file; return true; } @@ -890,7 +890,7 @@ const char *FConfigFile::GenerateEndTag(const char *value) // //==================================================================== -void FConfigFile::WriteCommentHeader (FILE *file) const +void FConfigFile::WriteCommentHeader (FileWriter *file) const { } diff --git a/src/configfile.h b/src/configfile.h index a48ddfad6..ab6866430 100644 --- a/src/configfile.h +++ b/src/configfile.h @@ -35,6 +35,7 @@ #define __CONFIGFILE_H__ #include +#include "files.h" #include "zstring.h" class FConfigFile @@ -73,7 +74,7 @@ public: bool WriteConfigFile () const; protected: - virtual void WriteCommentHeader (FILE *file) const; + virtual void WriteCommentHeader (FileWriter *file) const; virtual char *ReadLine (char *string, int n, void *file) const; bool ReadConfig (void *file); diff --git a/src/d_player.h b/src/d_player.h index 1ae74de32..ee1418f57 100644 --- a/src/d_player.h +++ b/src/d_player.h @@ -468,7 +468,7 @@ public: TObjPtr MUSINFOactor = nullptr; // For MUSINFO purposes int8_t MUSINFOtics = 0; - bool settings_controller = false; // Player can control game settings. + bool settings_controller = true; // Player can control game settings. int8_t crouching = 0; int8_t crouchdir = 0; diff --git a/src/dobjgc.h b/src/dobjgc.h index 269dffc15..41ae9ba23 100644 --- a/src/dobjgc.h +++ b/src/dobjgc.h @@ -167,22 +167,19 @@ class TObjPtr DObject *o; }; public: - TObjPtr() throw() - { - } + TObjPtr() = default; + TObjPtr(const TObjPtr &q) = default; + TObjPtr(T q) throw() : pp(q) { } - TObjPtr(const TObjPtr &q) throw() - : pp(q.pp) + T operator=(T q) { + pp = q; + return *this; } - T operator=(T q) throw() - { - return pp = q; - // The caller must now perform a write barrier. - } + operator T() throw() { return GC::ReadBarrier(pp); diff --git a/src/doomtype.h b/src/doomtype.h index 6e0ad770e..04fae16ff 100644 --- a/src/doomtype.h +++ b/src/doomtype.h @@ -103,7 +103,7 @@ enum struct PalEntry { - PalEntry () {} + PalEntry() = default; PalEntry (uint32_t argb) { d = argb; } operator uint32_t () const { return d; } void SetRGB(PalEntry other) @@ -146,6 +146,7 @@ struct PalEntry { return (d & 0xffffff) == 0xffffff; } + PalEntry &operator= (const PalEntry &other) = default; PalEntry &operator= (uint32_t other) { d = other; return *this; } PalEntry InverseColor() const { PalEntry nc; nc.a = a; nc.r = 255 - r; nc.g = 255 - g; nc.b = 255 - b; return nc; } #ifdef __BIG_ENDIAN__ @@ -205,7 +206,7 @@ class FTextureID friend void R_InitSpriteDefs(); public: - FTextureID() throw() {} + FTextureID() = default; bool isNull() const { return texnum == 0; } bool isValid() const { return texnum > 0; } bool Exists() const { return texnum >= 0; } diff --git a/src/gameconfigfile.cpp b/src/gameconfigfile.cpp index cb47c3597..37abce9d7 100644 --- a/src/gameconfigfile.cpp +++ b/src/gameconfigfile.cpp @@ -167,9 +167,9 @@ FGameConfigFile::~FGameConfigFile () { } -void FGameConfigFile::WriteCommentHeader (FILE *file) const +void FGameConfigFile::WriteCommentHeader (FileWriter *file) const { - fprintf (file, "# This file was generated by " GAMENAME " %s on %s\n", GetVersionString(), myasctime()); + file->Printf ("# This file was generated by " GAMENAME " %s on %s\n", GetVersionString(), myasctime()); } void FGameConfigFile::DoAutoloadSetup (FIWadManager *iwad_man) diff --git a/src/gameconfigfile.h b/src/gameconfigfile.h index 1ba066d05..9ef468489 100644 --- a/src/gameconfigfile.h +++ b/src/gameconfigfile.h @@ -58,7 +58,7 @@ public: void ReadNetVars (); protected: - void WriteCommentHeader (FILE *file) const; + void WriteCommentHeader (FileWriter *file) const; void CreateStandardAutoExec (const char *section, bool start); private: diff --git a/src/gl/scene/gl_portal.cpp b/src/gl/scene/gl_portal.cpp index 62c2d21c7..6f721a7dc 100644 --- a/src/gl/scene/gl_portal.cpp +++ b/src/gl/scene/gl_portal.cpp @@ -399,7 +399,6 @@ void GLHorizonPortal::DrawContents(HWDrawInfo *hwdi) Clocker c(PortalAll); FMaterial * gltexture; - PalEntry color; player_t * player=&players[consoleplayer]; GLSectorPlane * sp = &origin->plane; auto &vp = di->Viewpoint; diff --git a/src/hwrenderer/dynlights/hw_aabbtree.cpp b/src/hwrenderer/dynlights/hw_aabbtree.cpp index 5df3c4ac7..3d2365bf9 100644 --- a/src/hwrenderer/dynlights/hw_aabbtree.cpp +++ b/src/hwrenderer/dynlights/hw_aabbtree.cpp @@ -228,7 +228,6 @@ int LevelAABBTree::GenerateTreeNode(int *lines, int num_lines, const FVector2 *c // Try sort at longest axis, then if that fails then the other one. // We place the sorted lines into work_buffer and then move the result back to the lines list when done. int left_count, right_count; - FVector2 axis; for (int attempt = 0; attempt < 2; attempt++) { // Find the sort plane for axis diff --git a/src/hwrenderer/textures/hw_material.cpp b/src/hwrenderer/textures/hw_material.cpp index 26276c7cf..905860204 100644 --- a/src/hwrenderer/textures/hw_material.cpp +++ b/src/hwrenderer/textures/hw_material.cpp @@ -319,7 +319,6 @@ void FMaterial::SetSpriteRect() bool FMaterial::TrimBorders(uint16_t *rect) { - PalEntry col; int w; int h; diff --git a/src/name.h b/src/name.h index 575e42d06..8dbb63444 100644 --- a/src/name.h +++ b/src/name.h @@ -46,7 +46,7 @@ class FString; class FName { public: - FName() = default;// : Index(0) {} + FName() = default; FName (const char *text) { Index = NameData.FindName (text, false); } FName (const char *text, bool noCreate) { Index = NameData.FindName (text, noCreate); } FName (const char *text, size_t textlen, bool noCreate) { Index = NameData.FindName (text, textlen, noCreate); } @@ -63,7 +63,7 @@ public: FName &operator = (const char *text) { Index = NameData.FindName (text, false); return *this; } FName &operator = (const FString &text); - FName &operator = (const FName &other) { Index = other.Index; return *this; } + FName &operator = (const FName &other) = default; FName &operator = (ENamedName index) { Index = index; return *this; } int SetName (const char *text, bool noCreate=false) { return Index = NameData.FindName (text, noCreate); } diff --git a/src/p_mobj.cpp b/src/p_mobj.cpp index 16396f29c..070745df4 100644 --- a/src/p_mobj.cpp +++ b/src/p_mobj.cpp @@ -2345,7 +2345,6 @@ double P_XYMovement (AActor *mo, DVector2 scroll) { static int pushtime = 0; bool bForceSlide = !scroll.isZero(); - DAngle Angle; DVector2 ptry; player_t *player; DVector2 move; diff --git a/src/p_slopes.cpp b/src/p_slopes.cpp index cec49003d..de5eeab7f 100644 --- a/src/p_slopes.cpp +++ b/src/p_slopes.cpp @@ -306,7 +306,7 @@ static void P_SetSlopesFromVertexHeights(FMapThing *firstmt, FMapThing *lastmt, { if (sec.Lines.Size() != 3) continue; // only works with triangular sectors - DVector3 vt1, vt2, vt3, cross; + DVector3 vt1, vt2, vt3; DVector3 vec1, vec2; int vi1, vi2, vi3; diff --git a/src/posix/i_steam.cpp b/src/posix/i_steam.cpp index 5764a0dac..29dc9b845 100644 --- a/src/posix/i_steam.cpp +++ b/src/posix/i_steam.cpp @@ -153,13 +153,13 @@ static struct SteamAppInfo const int AppID; } AppInfo[] = { - /*{"doom 2/base", 2300}, - {"final doom/base", 2290}, - {"heretic shadow of the serpent riders/base", 2390}, - {"hexen/base", 2360}, - {"hexen deathkings of the dark citadel/base", 2370}, - {"ultimate doom/base", 2280}, - {"DOOM 3 BFG Edition/base/wads", 208200},*/ + {"Doom 2/base", 2300}, + {"Final Doom/base", 2290}, + {"Heretic Shadow of the Serpent Riders/base", 2390}, + {"Hexen/base", 2360}, + {"Hexen Deathkings of the Dark Citadel/base", 2370}, + {"Ultimate Doom/base", 2280}, + {"DOOM 3 BFG Edition/base/wads", 208200}, {"Strife", 317040} }; @@ -190,7 +190,13 @@ TArray I_GetSteamPath() if(home != NULL && *home != '\0') { FString regPath; - regPath.Format("%s/.local/share/Steam/config/config.vdf", home); + regPath.Format("%s/.steam/config/config.vdf", home); + // [BL] The config seems to have moved from the more modern .local to + // .steam at some point. Not sure if it's just my setup so I guess we + // can fall back on it? + if(!FileExists(regPath)) + regPath.Format("%s/.local/share/Steam/config/config.vdf", home); + try { SteamInstallFolders = ParseSteamRegistry(regPath); diff --git a/src/r_data/models/models.cpp b/src/r_data/models/models.cpp index 44d70b493..178aabb6c 100644 --- a/src/r_data/models/models.cpp +++ b/src/r_data/models/models.cpp @@ -40,6 +40,7 @@ #include "r_utility.h" #include "r_data/models/models.h" #include "r_data/models/models_ue1.h" +#include "r_data/models/models_obj.h" #include "i_time.h" #ifdef _MSC_VER @@ -419,7 +420,7 @@ static unsigned FindModel(const char * path, const char * modelfile) FMemLump lumpd = Wads.ReadLump(lump); char * buffer = (char*)lumpd.GetMem(); - if ( (size_t)fullname.IndexOf("_d.3d") == fullname.Len()-5 ) + if ( (size_t)fullname.LastIndexOf("_d.3d") == fullname.Len()-5 ) { FString anivfile = fullname.GetChars(); anivfile.Substitute("_d.3d","_a.3d"); @@ -428,7 +429,7 @@ static unsigned FindModel(const char * path, const char * modelfile) model = new FUE1Model; } } - else if ( (size_t)fullname.IndexOf("_a.3d") == fullname.Len()-5 ) + else if ( (size_t)fullname.LastIndexOf("_a.3d") == fullname.Len()-5 ) { FString datafile = fullname.GetChars(); datafile.Substitute("_a.3d","_d.3d"); @@ -437,6 +438,10 @@ static unsigned FindModel(const char * path, const char * modelfile) model = new FUE1Model; } } + else if ( (size_t)fullname.LastIndexOf(".obj") == fullname.Len() - 4 ) + { + model = new FOBJModel; + } else if (!memcmp(buffer, "DMDM", 4)) { model = new FDMDModel; diff --git a/src/r_data/models/models_obj.cpp b/src/r_data/models/models_obj.cpp new file mode 100644 index 000000000..a3124539b --- /dev/null +++ b/src/r_data/models/models_obj.cpp @@ -0,0 +1,588 @@ +// +//--------------------------------------------------------------------------- +// +// Copyright(C) 2018 Kevin Caccamo +// All rights reserved. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this program. If not, see http://www.gnu.org/licenses/ +// +//-------------------------------------------------------------------------- + +#include "w_wad.h" +#include "r_data/models/models_obj.h" + +/** + * Load an OBJ model + * + * @param fn The path to the model file + * @param lumpnum The lump index in the wad collection + * @param buffer The contents of the model file + * @param length The size of the model file + * @return Whether or not the model was parsed successfully + */ +bool FOBJModel::Load(const char* fn, int lumpnum, const char* buffer, int length) +{ + FString objName = Wads.GetLumpFullPath(lumpnum); + FString objBuf(buffer, length); + + // Do some replacements before we parse the OBJ string + { + // Ensure usemtl statements remain intact + TArray mtlUsages; + TArray mtlUsageIdxs; + long bpos = 0, nlpos = 0, slashpos = 0; + while (1) + { + bpos = objBuf.IndexOf("\nusemtl", bpos); + if (bpos == -1) break; + slashpos = objBuf.IndexOf('/', bpos); + nlpos = objBuf.IndexOf('\n', ++bpos); + if (slashpos > nlpos || slashpos == -1) + { + continue; + } + if (nlpos == -1) + { + nlpos = objBuf.Len(); + } + FString lineStr(objBuf.GetChars() + bpos, nlpos - bpos); + mtlUsages.Push(lineStr); + mtlUsageIdxs.Push(bpos); + } + + // Replace forward slashes with percent signs so they aren't parsed as line comments + objBuf.ReplaceChars('/', *newSideSep); + char* wObjBuf = objBuf.LockBuffer(); + + // Substitute broken usemtl statements with old ones + for (size_t i = 0; i < mtlUsages.Size(); i++) + { + bpos = mtlUsageIdxs[i]; + nlpos = objBuf.IndexOf('\n', bpos); + if (nlpos == -1) + { + nlpos = objBuf.Len(); + } + memcpy(wObjBuf + bpos, mtlUsages[i].GetChars(), nlpos - bpos); + } + + bpos = 0; + // Find each OBJ line comment, and convert each to a C-style line comment + while (1) + { + bpos = objBuf.IndexOf('#', bpos); + if (bpos == -1) break; + if (objBuf[(unsigned int)bpos + 1] == '\n') + { + wObjBuf[bpos] = ' '; + } + else + { + wObjBuf[bpos] = '/'; + wObjBuf[bpos+1] = '/'; + } + bpos += 1; + } + wObjBuf = nullptr; + objBuf.UnlockBuffer(); + } + sc.OpenString(objName, objBuf); + + FTextureID curMtl = FNullTextureID(); + OBJSurface *curSurface = nullptr; + int aggSurfFaceCount = 0; + int curSurfFaceCount = 0; + + while(sc.GetString()) + { + if (sc.Compare("v")) // Vertex + { + ParseVector(this->verts); + } + else if (sc.Compare("vn")) // Vertex normal + { + ParseVector(this->norms); + } + else if (sc.Compare("vt")) // UV Coordinates + { + ParseVector(this->uvs); + } + else if (sc.Compare("usemtl")) + { + // Get material name and try to load it + sc.MustGetString(); + + curMtl = LoadSkin("", sc.String); + if (!curMtl.isValid()) + { + // Relative to model file path? + curMtl = LoadSkin(fn, sc.String); + } + + if (!curMtl.isValid()) + { + sc.ScriptMessage("Material %s (#%u) not found.", sc.String, surfaces.Size()); + } + + // Build surface... + if (curSurface == nullptr) + { + // First surface + curSurface = new OBJSurface(curMtl); + } + else + { + if (curSurfFaceCount > 0) + { + // Add previous surface + curSurface->numFaces = curSurfFaceCount; + curSurface->faceStart = aggSurfFaceCount; + surfaces.Push(*curSurface); + delete curSurface; + // Go to next surface + curSurface = new OBJSurface(curMtl); + aggSurfFaceCount += curSurfFaceCount; + } + else + { + curSurface->skin = curMtl; + } + } + curSurfFaceCount = 0; + } + else if (sc.Compare("f")) + { + FString sides[4]; + OBJFace face; + for (int i = 0; i < 3; i++) + { + // A face must have at least 3 sides + sc.MustGetString(); + sides[i] = sc.String; + if (!ParseFaceSide(sides[i], face, i)) return false; + } + face.sideCount = 3; + if (sc.GetString()) + { + if (!sc.Compare("f") && FString(sc.String).IndexOfAny("-0123456789") == 0) + { + sides[3] = sc.String; + face.sideCount += 1; + if (!ParseFaceSide(sides[3], face, 3)) return false; + } + else + { + sc.UnGet(); // No 4th side, move back + } + } + faces.Push(face); + curSurfFaceCount += 1; + } + } + sc.Close(); + + if (curSurface == nullptr) + { // No valid materials detected + FTextureID dummyMtl = LoadSkin("", "-NOFLAT-"); // Built-in to GZDoom + curSurface = new OBJSurface(dummyMtl); + } + curSurface->numFaces = curSurfFaceCount; + curSurface->faceStart = aggSurfFaceCount; + surfaces.Push(*curSurface); + delete curSurface; + + if (uvs.Size() == 0) + { // Needed so that OBJs without UVs can work + uvs.Push(FVector2(0.0, 0.0)); + } + + return true; +} + +/** + * Parse an x-Dimensional vector + * + * @tparam T A subclass of TVector2 to be used + * @tparam L The length of the vector to parse + * @param[out] array The array to append the parsed vector to + */ +template void FOBJModel::ParseVector(TArray &array) +{ + float *coord = new float[L]; + for (size_t axis = 0; axis < L; axis++) + { + sc.MustGetFloat(); + coord[axis] = (float)sc.Float; + } + T vec(coord); + array.Push(vec); + delete[] coord; +} + +/** + * Parse a side of a face + * + * @param[in] sideStr The side definition string + * @param[out] face The face to assign the parsed side data to + * @param sidx The 0-based index of the side + * @return Whether or not the face side was parsed successfully + */ +bool FOBJModel::ParseFaceSide(const FString &sideStr, OBJFace &face, int sidx) +{ + OBJFaceSide side; + int origIdx; + if (sideStr.IndexOf(newSideSep) >= 0) + { + TArray sides = sideStr.Split(newSideSep, FString::TOK_KEEPEMPTY); + + if (sides[0].Len() > 0) + { + origIdx = atoi(sides[0].GetChars()); + side.vertref = ResolveIndex(origIdx, FaceElement::VertexIndex); + } + else + { + sc.ScriptError("Vertex reference is not optional!"); + return false; + } + + if (sides[1].Len() > 0) + { + origIdx = atoi(sides[1].GetChars()); + side.uvref = ResolveIndex(origIdx, FaceElement::UVIndex); + } + else + { + side.uvref = -1; + } + + if (sides.Size() > 2) + { + if (sides[2].Len() > 0) + { + origIdx = atoi(sides[2].GetChars()); + side.normref = ResolveIndex(origIdx, FaceElement::VNormalIndex); + } + else + { + side.normref = -1; + } + } + else + { + side.normref = -1; + } + } + else + { + origIdx = atoi(sideStr.GetChars()); + side.vertref = ResolveIndex(origIdx, FaceElement::VertexIndex); + side.normref = -1; + side.uvref = -1; + } + face.sides[sidx] = side; + return true; +} + +/** + * Resolve an OBJ index to an absolute index + * + * OBJ indices are 1-based, and can also be negative + * + * @param origIndex The original OBJ index to resolve + * @param el What type of element the index references + * @return The absolute index of the element + */ +int FOBJModel::ResolveIndex(int origIndex, FaceElement el) +{ + if (origIndex > 0) + { + return origIndex - 1; // OBJ indices start at 1 + } + else if (origIndex < 0) + { + if (el == FaceElement::VertexIndex) + { + return verts.Size() + origIndex; // origIndex is negative + } + else if (el == FaceElement::UVIndex) + { + return uvs.Size() + origIndex; + } + else if (el == FaceElement::VNormalIndex) + { + return norms.Size() + origIndex; + } + } + return -1; +} + +/** + * Construct the vertex buffer for this model + * + * @param renderer A pointer to the model renderer. Used to allocate the vertex buffer. + */ +void FOBJModel::BuildVertexBuffer(FModelRenderer *renderer) +{ + if (GetVertexBuffer(renderer)) + { + return; + } + + unsigned int vbufsize = 0; + + for (size_t i = 0; i < surfaces.Size(); i++) + { + ConstructSurfaceTris(surfaces[i]); + surfaces[i].vbStart = vbufsize; + vbufsize += surfaces[i].numTris * 3; + } + + auto vbuf = renderer->CreateVertexBuffer(false,true); + SetVertexBuffer(renderer, vbuf); + + FModelVertex *vertptr = vbuf->LockVertexBuffer(vbufsize); + + for (size_t i = 0; i < surfaces.Size(); i++) + { + for (size_t j = 0; j < surfaces[i].numTris; j++) + { + for (size_t side = 0; side < 3; side++) + { + FModelVertex *mdv = vertptr + + side + j * 3 + // Current surface and previous triangles + surfaces[i].vbStart; // Previous surfaces + + OBJFaceSide &curSide = surfaces[i].tris[j].sides[side]; + + int vidx = curSide.vertref; + int uvidx = (curSide.uvref >= 0 && (unsigned int)curSide.uvref < uvs.Size()) ? curSide.uvref : 0; + int nidx = curSide.normref; + + FVector3 curVvec = RealignVector(verts[vidx]); + FVector2 curUvec = FixUV(uvs[uvidx]); + FVector3 *nvec = nullptr; + + mdv->Set(curVvec.X, curVvec.Y, curVvec.Z, curUvec.X, curUvec.Y); + + if (nidx >= 0 && (unsigned int)nidx < norms.Size()) + { + nvec = new FVector3(RealignVector(norms[nidx])); + } + else + { + // https://www.khronos.org/opengl/wiki/Calculating_a_Surface_Normal + // Find other sides of triangle + int nextSidx = side + 2; + if (nextSidx >= 3) nextSidx -= 3; + + int lastSidx = side + 1; + if (lastSidx >= 3) lastSidx -= 3; + + OBJFaceSide &nextSide = surfaces[i].tris[j].sides[nextSidx]; + OBJFaceSide &lastSide = surfaces[i].tris[j].sides[lastSidx]; + + // Cross-multiply the U-vector and V-vector + FVector3 uvec = RealignVector(verts[nextSide.vertref]) - curVvec; + FVector3 vvec = RealignVector(verts[lastSide.vertref]) - curVvec; + + nvec = new FVector3(uvec ^ vvec); + } + mdv->SetNormal(nvec->X, nvec->Y, nvec->Z); + delete nvec; + } + } + delete[] surfaces[i].tris; + } + vbuf->UnlockVertexBuffer(); +} + +/** + * Fill in the triangle data for a surface + * + * @param[in,out] surf The surface to fill in the triangle data for + */ +void FOBJModel::ConstructSurfaceTris(OBJSurface &surf) +{ + unsigned int triCount = 0; + + size_t start = surf.faceStart; + size_t end = start + surf.numFaces; + for (size_t i = start; i < end; i++) + { + triCount += faces[i].sideCount - 2; + } + + surf.numTris = triCount; + surf.tris = new OBJFace[triCount]; + + for (size_t i = start, triIdx = 0; i < end; i++, triIdx++) + { + surf.tris[triIdx].sideCount = 3; + if (faces[i].sideCount == 3) + { + memcpy(surf.tris[triIdx].sides, faces[i].sides, sizeof(OBJFaceSide) * 3); + } + else if (faces[i].sideCount == 4) // Triangulate face + { + OBJFace *triangulated = new OBJFace[2]; + TriangulateQuad(faces[i], triangulated); + memcpy(surf.tris[triIdx].sides, triangulated[0].sides, sizeof(OBJFaceSide) * 3); + memcpy(surf.tris[triIdx+1].sides, triangulated[1].sides, sizeof(OBJFaceSide) * 3); + delete[] triangulated; + triIdx += 1; // Filling out two faces + } + } +} + +/** + * Triangulate a 4-sided face + * + * @param[in] quad The 4-sided face to triangulate + * @param[out] tris The resultant triangle data + */ +void FOBJModel::TriangulateQuad(const OBJFace &quad, OBJFace *tris) +{ + tris[0].sideCount = 3; + tris[1].sideCount = 3; + + int tsidx[2][3] = {{0, 1, 3}, {1, 2, 3}}; + + for (int i = 0; i < 3; i++) + { + for (int j = 0; j < 2; j++) + { + tris[j].sides[i].vertref = quad.sides[tsidx[j][i]].vertref; + tris[j].sides[i].uvref = quad.sides[tsidx[j][i]].uvref; + tris[j].sides[i].normref = quad.sides[tsidx[j][i]].normref; + } + } +} + +/** + * Re-align a vector to match MD3 alignment + * + * @param vecToRealign The vector to re-align + * @return The re-aligned vector + */ +inline FVector3 FOBJModel::RealignVector(FVector3 vecToRealign) +{ + vecToRealign.Z *= -1; + return vecToRealign; +} + +/** + * Fix UV coordinates of a UV vector + * + * @param vecToRealign The vector to fix + * @return The fixed UV coordinate vector + */ +inline FVector2 FOBJModel::FixUV(FVector2 vecToRealign) +{ + vecToRealign.Y *= -1; + return vecToRealign; +} + +/** + * Find the index of the frame with the given name + * + * OBJ models are not animated, so this always returns 0 + * + * @param name The name of the frame + * @return The index of the frame + */ +int FOBJModel::FindFrame(const char* name) +{ + return 0; // OBJs are not animated. +} + +/** + * Render the model + * + * @param renderer The model renderer + * @param skin The loaded skin for the surface + * @param frameno Unused + * @param frameno2 Unused + * @param inter Unused + * @param translation The translation for the skin + */ +void FOBJModel::RenderFrame(FModelRenderer *renderer, FTexture * skin, int frameno, int frameno2, double inter, int translation) +{ + for (unsigned int i = 0; i < surfaces.Size(); i++) + { + OBJSurface *surf = &surfaces[i]; + + FTexture *userSkin = skin; + if (!userSkin) + { + if (i < MD3_MAX_SURFACES && curSpriteMDLFrame->surfaceskinIDs[curMDLIndex][i].isValid()) + { + userSkin = TexMan(curSpriteMDLFrame->surfaceskinIDs[curMDLIndex][i]); + } + else if (surf->skin.isValid()) + { + userSkin = TexMan(surf->skin); + } + } + + // Still no skin after checking for one? + if (!userSkin) + { + continue; + } + + renderer->SetMaterial(userSkin, false, translation); + GetVertexBuffer(renderer)->SetupFrame(renderer, surf->vbStart, surf->vbStart, surf->numTris * 3); + renderer->DrawArrays(0, surf->numTris * 3); + } +} + +/** + * Pre-cache skins for the model + * + * @param hitlist The list of textures + */ +void FOBJModel::AddSkins(uint8_t* hitlist) +{ + for (size_t i = 0; i < surfaces.Size(); i++) + { + if (i < MD3_MAX_SURFACES && curSpriteMDLFrame->surfaceskinIDs[curMDLIndex][i].isValid()) + { + // Precache skins manually reassigned by the user. + // On OBJs with lots of skins, such as Doom map OBJs exported from GZDB, + // there may be too many skins for the user to manually change, unless + // the limit is bumped or surfaceskinIDs is changed to a TArray. + hitlist[curSpriteMDLFrame->surfaceskinIDs[curMDLIndex][i].GetIndex()] |= FTextureManager::HIT_Flat; + return; // No need to precache skin that was replaced + } + + OBJSurface * surf = &surfaces[i]; + if (surf->skin.isValid()) + { + hitlist[surf->skin.GetIndex()] |= FTextureManager::HIT_Flat; + } + } +} + +/** + * Remove the data that was loaded + */ +FOBJModel::~FOBJModel() +{ + verts.Clear(); + norms.Clear(); + uvs.Clear(); + faces.Clear(); + surfaces.Clear(); +} diff --git a/src/r_data/models/models_obj.h b/src/r_data/models/models_obj.h new file mode 100644 index 000000000..d6d68a58e --- /dev/null +++ b/src/r_data/models/models_obj.h @@ -0,0 +1,87 @@ +// +//--------------------------------------------------------------------------- +// +// Copyright(C) 2018 Kevin Caccamo +// All rights reserved. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this program. If not, see http://www.gnu.org/licenses/ +// +//-------------------------------------------------------------------------- +// + +#ifndef __GL_MODELS_OBJ_H__ +#define __GL_MODELS_OBJ_H__ + +#include "models.h" +#include "sc_man.h" + +class FOBJModel : public FModel +{ +private: + const char *newSideSep = "$"; // OBJ side separator is /, which is parsed as a line comment by FScanner if two of them are next to each other. + + enum class FaceElement + { + VertexIndex, + UVIndex, + VNormalIndex + }; + + struct OBJFaceSide + { + int vertref; + int normref; + int uvref; + }; + struct OBJFace + { + unsigned int sideCount; + OBJFaceSide sides[4]; + }; + struct OBJSurface // 1 surface per 'usemtl' + { + unsigned int numTris; // Number of triangulated faces + unsigned int numFaces; // Number of faces + unsigned int vbStart; // First index in vertex buffer + unsigned int faceStart; // Index of first face in faces array + OBJFace* tris; // Triangles + FTextureID skin; + OBJSurface(FTextureID skin): numTris(0), numFaces(0), vbStart(0), faceStart(0), tris(nullptr), skin(skin) {} + }; + + TArray verts; + TArray norms; + TArray uvs; + TArray faces; + TArray surfaces; + FScanner sc; + + template void ParseVector(TArray &array); + bool ParseFaceSide(const FString &side, OBJFace &face, int sidx); + void ConstructSurfaceTris(OBJSurface &surf); + int ResolveIndex(int origIndex, FaceElement el); + void TriangulateQuad(const OBJFace &quad, OBJFace *tris); + FVector3 RealignVector(FVector3 vecToRealign); + FVector2 FixUV(FVector2 vecToRealign); +public: + FOBJModel() {} + ~FOBJModel(); + bool Load(const char* fn, int lumpnum, const char* buffer, int length) override; + int FindFrame(const char* name) override; + void RenderFrame(FModelRenderer* renderer, FTexture* skin, int frame, int frame2, double inter, int translation=0) override; + void BuildVertexBuffer(FModelRenderer* renderer) override; + void AddSkins(uint8_t* hitlist) override; +}; + +#endif diff --git a/src/s_sound.cpp b/src/s_sound.cpp index 6b6e4b74f..30e6f918e 100644 --- a/src/s_sound.cpp +++ b/src/s_sound.cpp @@ -776,8 +776,8 @@ static void CalcPosVel(int type, const AActor *actor, const sector_t *sector, { DVector2 disp = level.Displacements.getOffset(pgroup, poly->CenterSubsector->sector->PortalGroup); CalcPolyobjSoundOrg(listenpos + disp, poly, *pos); - pos->X += (float)disp.X; - pos->Z += (float)disp.Y; + pos->X -= (float)disp.X; + pos->Z -= (float)disp.Y; } break; } diff --git a/src/s_sound.h b/src/s_sound.h index 3a64ba76f..1d42cb2ae 100644 --- a/src/s_sound.h +++ b/src/s_sound.h @@ -114,15 +114,8 @@ public: { ID = S_FindSound(name.GetChars()); } - FSoundID(const FSoundID &other) - { - ID = other.ID; - } - FSoundID &operator=(const FSoundID &other) - { - ID = other.ID; - return *this; - } + FSoundID(const FSoundID &other) = default; + FSoundID &operator=(const FSoundID &other) = default; FSoundID &operator=(const char *name) { ID = S_FindSound(name); @@ -168,19 +161,7 @@ class FSoundIDNoInit : public FSoundID { public: FSoundIDNoInit() : FSoundID(NoInit) {} - - FSoundID &operator=(const FSoundID &other) - { - return FSoundID::operator=(other); - } - FSoundID &operator=(const char *name) - { - return FSoundID::operator=(name); - } - FSoundID &operator=(const FString &name) - { - return FSoundID::operator=(name); - } + using FSoundID::operator=; }; extern FRolloffInfo S_Rolloff; diff --git a/src/scripting/thingdef_data.cpp b/src/scripting/thingdef_data.cpp index ca6bee2a4..a10d05710 100644 --- a/src/scripting/thingdef_data.cpp +++ b/src/scripting/thingdef_data.cpp @@ -327,7 +327,7 @@ static FFlagDef ActorFlagDefs[]= DEFINE_FLAG(RF, INVISIBLE, AActor, renderflags), DEFINE_FLAG(RF, FORCEYBILLBOARD, AActor, renderflags), DEFINE_FLAG(RF, FORCEXYBILLBOARD, AActor, renderflags), - DEFINE_FLAG(RF, ROLLSPRITE, AActor, renderflags), // [marrub] roll the sprite billboard + DEFINE_FLAG(RF, ROLLSPRITE, AActor, renderflags), // [marrub] roll the sprite billboard // [fgsfds] Flat sprites DEFINE_FLAG(RF, FLATSPRITE, AActor, renderflags), DEFINE_FLAG(RF, WALLSPRITE, AActor, renderflags), @@ -593,7 +593,7 @@ FFlagDef *FindFlag (const PClass *type, const char *part1, const char *part2, bo //========================================================================== // -// Gets the name of an actor flag +// Gets the name of an actor flag // //========================================================================== @@ -1266,6 +1266,14 @@ DEFINE_ACTION_FUNCTION(FStringStruct, IndexOf) } DEFINE_ACTION_FUNCTION(FStringStruct, LastIndexOf) +{ + PARAM_SELF_STRUCT_PROLOGUE(FString); + PARAM_STRING(substr); + PARAM_INT_DEF(endIndex); + ACTION_RETURN_INT(self->LastIndexOfBroken(substr, endIndex)); +} + +DEFINE_ACTION_FUNCTION(FStringStruct, RightIndexOf) { PARAM_SELF_STRUCT_PROLOGUE(FString); PARAM_STRING(substr); diff --git a/src/stats.h b/src/stats.h index 4eefdc9a9..ec963f534 100644 --- a/src/stats.h +++ b/src/stats.h @@ -57,12 +57,6 @@ public: class cycle_t { public: - cycle_t &operator= (const cycle_t &o) - { - Sec = o.Sec; - return *this; - } - void Reset() { Sec = 0; @@ -153,12 +147,6 @@ inline uint64_t rdtsc() class cycle_t { public: - cycle_t &operator= (const cycle_t &o) - { - Counter = o.Counter; - return *this; - } - void Reset() { Counter = 0; diff --git a/src/textures/texture.cpp b/src/textures/texture.cpp index bd259b8af..88ae5b176 100644 --- a/src/textures/texture.cpp +++ b/src/textures/texture.cpp @@ -961,8 +961,6 @@ PalEntry FTexture::averageColor(const uint32_t *data, int size, int maxout) PalEntry FTexture::GetSkyCapColor(bool bottom) { - PalEntry col; - if (!bSWSkyColorDone) { bSWSkyColorDone = true; diff --git a/src/v_2ddrawer.cpp b/src/v_2ddrawer.cpp index ad5effb30..a09ecd5fd 100644 --- a/src/v_2ddrawer.cpp +++ b/src/v_2ddrawer.cpp @@ -594,6 +594,46 @@ void F2DDrawer::AddLine(int x1, int y1, int x2, int y2, int palcolor, uint32_t c // //========================================================================== +void F2DDrawer::AddThickLine(int x1, int y1, int x2, int y2, double thickness, uint32_t color) +{ + PalEntry p = (PalEntry)color; + + DVector2 point0(x1, y1); + DVector2 point1(x2, y2); + + DVector2 delta = point1 - point0; + DVector2 perp(-delta.Y, delta.X); + perp.MakeUnit(); + perp *= thickness / 2; + + DVector2 corner0 = point0 + perp; + DVector2 corner1 = point0 - perp; + DVector2 corner2 = point1 + perp; + DVector2 corner3 = point1 - perp; + + RenderCommand dg; + + dg.mType = DrawTypeTriangles; + dg.mVertCount = 4; + dg.mVertIndex = (int)mVertices.Reserve(4); + dg.mRenderStyle = LegacyRenderStyles[STYLE_Translucent]; + auto ptr = &mVertices[dg.mVertIndex]; + ptr->Set(corner0.X, corner0.Y, 0, 0, 0, p); ptr++; + ptr->Set(corner1.X, corner1.Y, 0, 0, 0, p); ptr++; + ptr->Set(corner2.X, corner2.Y, 0, 0, 0, p); ptr++; + ptr->Set(corner3.X, corner3.Y, 0, 0, 0, p); ptr++; + dg.mIndexIndex = mIndices.Size(); + dg.mIndexCount += 6; + AddIndices(dg.mVertIndex, 6, 0, 1, 2, 1, 3, 2); + AddCommand(&dg); +} + +//========================================================================== +// +// +// +//========================================================================== + void F2DDrawer::AddPixel(int x1, int y1, int palcolor, uint32_t color) { PalEntry p = color ? (PalEntry)color : GPalette.BaseColors[palcolor]; diff --git a/src/v_2ddrawer.h b/src/v_2ddrawer.h index ec88dc908..f60665e9e 100644 --- a/src/v_2ddrawer.h +++ b/src/v_2ddrawer.h @@ -149,6 +149,7 @@ public: void AddLine(int x1, int y1, int x2, int y2, int palcolor, uint32_t color); + void AddThickLine(int x1, int y1, int x2, int y2, double thickness, uint32_t color); void AddPixel(int x1, int y1, int palcolor, uint32_t color); void Clear(); diff --git a/src/v_draw.cpp b/src/v_draw.cpp index e3ebe63bc..765b20003 100644 --- a/src/v_draw.cpp +++ b/src/v_draw.cpp @@ -1088,6 +1088,24 @@ DEFINE_ACTION_FUNCTION(_Screen, DrawLine) return 0; } +void DFrameBuffer::DrawThickLine(int x0, int y0, int x1, int y1, double thickness, uint32_t realcolor) { + m2DDrawer.AddThickLine(x0, y0, x1, y1, thickness, realcolor); +} + +DEFINE_ACTION_FUNCTION(_Screen, DrawThickLine) +{ + PARAM_PROLOGUE; + PARAM_INT(x0); + PARAM_INT(y0); + PARAM_INT(x1); + PARAM_INT(y1); + PARAM_FLOAT(thickness); + PARAM_INT(color); + if (!screen->HasBegun2D()) ThrowAbortException(X_OTHER, "Attempt to draw to screen outside a draw function"); + screen->DrawThickLine(x0, y0, x1, y1, thickness, color); + return 0; +} + //========================================================================== // // Draw a single pixel diff --git a/src/v_font.cpp b/src/v_font.cpp index d9e624270..0a7d74215 100644 --- a/src/v_font.cpp +++ b/src/v_font.cpp @@ -1995,7 +1995,14 @@ FSpecialFont::FSpecialFont (const char *name, int first, int count, FTexture **l FixXMoves(); - if (!noTranslate) LoadTranslations(); + if (noTranslate) + { + ActiveColors = 0; + } + else + { + LoadTranslations(); + } delete[] charlumps; } diff --git a/src/v_video.h b/src/v_video.h index 1e456e6b8..ec0e43f3d 100644 --- a/src/v_video.h +++ b/src/v_video.h @@ -498,6 +498,9 @@ public: // Draws a line void DrawLine(int x0, int y0, int x1, int y1, int palColor, uint32_t realcolor); + // Draws a line with thickness + void DrawThickLine(int x0, int y0, int x1, int y1, double thickness, uint32_t realcolor); + // Draws a single pixel void DrawPixel(int x, int y, int palcolor, uint32_t rgbcolor); diff --git a/src/vectors.h b/src/vectors.h index 65a30345f..002062f8c 100644 --- a/src/vectors.h +++ b/src/vectors.h @@ -66,19 +66,14 @@ struct TVector2 { vec_t X, Y; - TVector2 () - { - } + TVector2() = default; TVector2 (vec_t a, vec_t b) : X(a), Y(b) { } - TVector2 (const TVector2 &other) - : X(other.X), Y(other.Y) - { - } + TVector2(const TVector2 &other) = default; TVector2 (const TVector3 &other) // Copy the X and Y from the 3D vector and discard the Z : X(other.X), Y(other.Y) @@ -95,18 +90,7 @@ struct TVector2 return X == 0 && Y == 0; } - TVector2 &operator= (const TVector2 &other) - { - // This might seem backwards, but this helps produce smaller code when a newly - // created vector is assigned, because the components can just be popped off - // the FPU stack in order without the need for fxch. For platforms with a - // more sensible registered-based FPU, of course, the order doesn't matter. - // (And, yes, I know fxch can improve performance in the right circumstances, - // but this isn't one of those times. Here, it's little more than a no-op that - // makes the exe 2 bytes larger whenever you assign one vector to another.) - Y = other.Y, X = other.X; - return *this; - } + TVector2 &operator= (const TVector2 &other) = default; // Access X and Y as an array vec_t &operator[] (int index) @@ -317,9 +301,7 @@ struct TVector3 vec_t X, Y, Z; - TVector3 () - { - } + TVector3() = default; TVector3 (vec_t a, vec_t b, vec_t c) : X(a), Y(b), Z(c) @@ -331,10 +313,7 @@ struct TVector3 { } - TVector3 (const TVector3 &other) - : X(other.X), Y(other.Y), Z(other.Z) - { - } + TVector3(const TVector3 &other) = default; TVector3 (const Vector2 &xy, vec_t z) : X(xy.X), Y(xy.Y), Z(z) @@ -353,11 +332,7 @@ struct TVector3 return X == 0 && Y == 0 && Z == 0; } - TVector3 &operator= (const TVector3 &other) - { - Z = other.Z, Y = other.Y, X = other.X; - return *this; - } + TVector3 &operator= (const TVector3 &other) = default; // Access X and Y and Z as an array vec_t &operator[] (int index) @@ -546,13 +521,12 @@ struct TVector3 { right = { 0.f, 0.f, 1.f }; } - - if (major == 1 || (major == 2 && n[2] > 0.f)) + else if (major == 1 || (major == 2 && n[2] > 0.f)) { right = { 1.f, 0.f, 0.f }; } - - if (major == 2 && n[2] < 0.0f) + // Unconditional to ease static analysis + else // major == 2 && n[2] <= 0.0f { right = { -1.f, 0.f, 0.f }; } @@ -662,9 +636,7 @@ struct TVector4 vec_t X, Y, Z, W; - TVector4() - { - } + TVector4() = default; TVector4(vec_t a, vec_t b, vec_t c, vec_t d) : X(a), Y(b), Z(c), W(d) @@ -676,10 +648,7 @@ struct TVector4 { } - TVector4(const TVector4 &other) - : X(other.X), Y(other.Y), Z(other.Z), W(other.W) - { - } + TVector4(const TVector4 &other) = default; TVector4(const Vector3 &xyz, vec_t w) : X(xyz.X), Y(xyz.Y), Z(xyz.Z), W(w) @@ -696,11 +665,7 @@ struct TVector4 return X == 0 && Y == 0 && Z == 0 && W == 0; } - TVector4 &operator= (const TVector4 &other) - { - W = other.W, Z = other.Z, Y = other.Y, X = other.X; - return *this; - } + TVector4 &operator= (const TVector4 &other) = default; // Access X and Y and Z as an array vec_t &operator[] (int index) @@ -939,16 +904,8 @@ struct TMatrix3x3 vec_t Cells[3][3]; - TMatrix3x3() - { - } - - TMatrix3x3(const TMatrix3x3 &other) - { - (*this)[0] = other[0]; - (*this)[1] = other[1]; - (*this)[2] = other[2]; - } + TMatrix3x3() = default; + TMatrix3x3(const TMatrix3x3 &other) = default; TMatrix3x3(const Vector3 &row1, const Vector3 &row2, const Vector3 &row3) { @@ -1151,32 +1108,15 @@ struct TAngle TAngle &operator= (long other) = delete; TAngle &operator= (unsigned long other) = delete; - TAngle () - { - } + TAngle() = default; TAngle (vec_t amt) : Degrees(amt) { } - /* - TAngle (int amt) - : Degrees(vec_t(amt)) - { - } - */ - - TAngle (const TAngle &other) - : Degrees(other.Degrees) - { - } - - TAngle &operator= (const TAngle &other) - { - Degrees = other.Degrees; - return *this; - } + TAngle(const TAngle &other) = default; + TAngle &operator= (const TAngle &other) = default; TAngle &operator= (double other) { @@ -1491,25 +1431,15 @@ struct TRotator Angle Roll; // rotation about the forward axis. Angle CamRoll; // Roll specific to actor cameras. Used by quakes. - TRotator () - { - } + TRotator() = default; TRotator (const Angle &p, const Angle &y, const Angle &r) : Pitch(p), Yaw(y), Roll(r) { } - TRotator (const TRotator &other) - : Pitch(other.Pitch), Yaw(other.Yaw), Roll(other.Roll) - { - } - - TRotator &operator= (const TRotator &other) - { - Roll = other.Roll, Yaw = other.Yaw, Pitch = other.Pitch; - return *this; - } + TRotator(const TRotator &other) = default; + TRotator &operator= (const TRotator &other) = default; // Access angles as an array Angle &operator[] (int index) diff --git a/src/version.h b/src/version.h index 9e36f91fe..e014d54ed 100644 --- a/src/version.h +++ b/src/version.h @@ -55,9 +55,9 @@ const char *GetVersionString(); #define RC_FILEVERSION 3,5,9999,0 #define RC_PRODUCTVERSION 3,5,9999,0 #define RC_PRODUCTVERSION2 VERSIONSTR -// These are for content versioning. The current state is '3.5'. +// These are for content versioning. #define VER_MAJOR 3 -#define VER_MINOR 5 +#define VER_MINOR 6 #define VER_REVISION 0 // Version identifier for network games. diff --git a/src/zstring.cpp b/src/zstring.cpp index ea9c249bb..ff17fd607 100644 --- a/src/zstring.cpp +++ b/src/zstring.cpp @@ -507,31 +507,11 @@ long FString::IndexOfAny (const char *charset, long startIndex) const return long(brk - Chars); } -long FString::LastIndexOf (const FString &substr) const -{ - return LastIndexOf (substr.Chars, long(Len()), substr.Len()); -} - -long FString::LastIndexOf (const char *substr) const -{ - return LastIndexOf (substr, long(Len()), strlen(substr)); -} - long FString::LastIndexOf (char subchar) const { return LastIndexOf (subchar, long(Len())); } -long FString::LastIndexOf (const FString &substr, long endIndex) const -{ - return LastIndexOf (substr.Chars, endIndex, substr.Len()); -} - -long FString::LastIndexOf (const char *substr, long endIndex) const -{ - return LastIndexOf (substr, endIndex, strlen(substr)); -} - long FString::LastIndexOf (char subchar, long endIndex) const { if ((size_t)endIndex > Len()) @@ -548,8 +528,10 @@ long FString::LastIndexOf (char subchar, long endIndex) const return -1; } -long FString::LastIndexOf (const char *substr, long endIndex, size_t substrlen) const +long FString::LastIndexOfBroken (const FString &_substr, long endIndex) const { + const char *substr = _substr.GetChars(); + size_t substrlen = _substr.Len(); if ((size_t)endIndex > Len()) { endIndex = long(Len()); @@ -596,6 +578,43 @@ long FString::LastIndexOfAny (const char *charset, long endIndex) const return -1; } +long FString::LastIndexOf (const FString &substr) const +{ + return LastIndexOf(substr.Chars, long(Len() - substr.Len()), substr.Len()); +} + +long FString::LastIndexOf (const FString &substr, long endIndex) const +{ + return LastIndexOf(substr.Chars, endIndex, substr.Len()); +} + +long FString::LastIndexOf (const char *substr) const +{ + return LastIndexOf(substr, long(Len() - strlen(substr)), strlen(substr)); +} + +long FString::LastIndexOf (const char *substr, long endIndex) const +{ + return LastIndexOf(substr, endIndex, strlen(substr)); +} + +long FString::LastIndexOf (const char *substr, long endIndex, size_t substrlen) const +{ + if ((size_t)endIndex + substrlen > Len()) + { + endIndex = long(Len() - substrlen); + } + while (endIndex >= 0) + { + if (strncmp (substr, Chars + endIndex, substrlen) == 0) + { + return endIndex; + } + endIndex--; + } + return -1; +} + void FString::ToUpper () { LockBuffer(); @@ -1003,7 +1022,7 @@ void FString::Substitute (const char *oldstr, const char *newstr, size_t oldstrl bool FString::IsInt () const { - // String must match: [whitespace] [{+ | –}] [0 [{ x | X }]] [digits] [whitespace] + // String must match: [whitespace] [{+ | �}] [0 [{ x | X }]] [digits] [whitespace] /* This state machine is based on a simplification of re2c's output for this input: digits = [0-9]; diff --git a/src/zstring.h b/src/zstring.h index 5ab267414..67fef4a18 100644 --- a/src/zstring.h +++ b/src/zstring.h @@ -201,19 +201,22 @@ public: long IndexOfAny (const FString &charset, long startIndex=0) const; long IndexOfAny (const char *charset, long startIndex=0) const; - long LastIndexOf (const FString &substr) const; - long LastIndexOf (const char *substr) 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 LastIndexOf (const FString &substr, long endIndex) const; - long LastIndexOf (const char *substr, long endIndex) const; + long LastIndexOfBroken (const FString &substr, long endIndex) const; long LastIndexOf (char subchar, long endIndex) const; - long LastIndexOf (const char *substr, long endIndex, size_t substrlen) 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; + 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; + void ToUpper (); void ToLower (); void SwapCase (); @@ -463,4 +466,3 @@ template<> struct THashTraits // Compares two keys, returning zero if they are the same. int Compare(const FString &left, const FString &right) { return left.Compare(right); } }; - diff --git a/wadsrc/static/language.enu b/wadsrc/static/language.enu index c843f5fcd..3e20e1e03 100644 --- a/wadsrc/static/language.enu +++ b/wadsrc/static/language.enu @@ -2820,6 +2820,8 @@ GLPREFMNU_DITHER = "Dither output"; GLPREFMNU_PALTONEMAPORDER = "Tonemap Palette Order"; GLPREFMNU_PALTONEMAPPOWER = "Tonemap Palette Exponent"; GLPREFMNU_SWLMBANDED = "Banded SW Lightmode"; +GLPREFMNU_VRIPD = "Distance Between Your Eyes"; +GLPREFMNU_VRSCREENDIST = "Distance From Your Screen"; // Option Values OPTVAL_SMART = "Smart"; diff --git a/wadsrc/static/language.fr b/wadsrc/static/language.fr index fa49cc07e..181c9e0db 100644 --- a/wadsrc/static/language.fr +++ b/wadsrc/static/language.fr @@ -34,7 +34,7 @@ QUITMSG10 = "Tirez vous de là et retournez\nà vos programmes ennuyeux."; QUITMSG11 = "Si j'étais votre patron, je vous\n collerai un Deathmatch dans la minute!"; QUITMSG12 = "Regardez l'ami. Vous partez maintenant\net vous pouvez oublier votre score!"; QUITMSG13 = "Allez, partez. Quand vous reviendrez\nje vous attendrai avec une batte."; -QUITMSG14 = "Vous êtes chanceux que je ne vous \ncolle pas une parce que vous pensez partir."; +QUITMSG14 = "Vous êtes chanceux que je ne vous en \ncolle pas une parce que vous pensez partir."; // Quit Strife messages QUITMSG15 = "Où allez vous?\nQue va devenir la rébellion?"; @@ -206,7 +206,7 @@ HUSTR_23 = "NIVEAU 23: Une Tonne-eau de plaisir"; HUSTR_24 = "NIVEAU 24: Le Gouffre"; HUSTR_25 = "NIVEAU 25: Chutes de Sang"; HUSTR_26 = "NIVEAU 26: Les Mines Abandonnées"; -HUSTR_27 = "NIVEAU 27: Monstrueuse Résiden,ce"; +HUSTR_27 = "NIVEAU 27: Monstrueuse Résidence"; HUSTR_28 = "NIVEAU 28: Le Monde Spirituel"; HUSTR_29 = "NIVEAU 29: La Limite"; HUSTR_30 = "NIVEAU 30: L'Icône du Péché"; @@ -257,7 +257,7 @@ PHUSTR_26 = "NIVEAU 26: Bunker"; PHUSTR_27 = "NIVEAU 27: Anti-Christ"; PHUSTR_28 = "NIVEAU 28: Les Egouts"; PHUSTR_29 = "NIVEAU 29: Odysée de Bruits"; -PHUSTR_30 = "NIVEAU 30: La Porte des Enferts"; +PHUSTR_30 = "NIVEAU 30: La Porte des Enfers"; PHUSTR_31 = "NIVEAU 31: Cyber-Antre"; PHUSTR_32 = "NIVEAU 32: GO 2 IT"; @@ -348,10 +348,10 @@ TXT_DEFAULTPICKUPMSG = "Quelque chose récupéré."; // F_Finale.C // E1TEXT = - "APRES AVOIR VAINCU LES GROS MECHANTS\n" + "APRES AVOIR VAINCU CES SALES PESTES\n" "ET NETTOYE LA BASE LUNAIRE, VOUS AVEZ\n" "GAGNE, NON? PAS VRAI? OU EST DONC VOTRE\n" - " RECOMPENSE ET VOTRE BILLET DE\n" + "RECOMPENSE ET VOTRE BILLET DE\n" "RETOUR? QU'EST-QUE CA VEUT DIRE? CE" "N'EST PAS LA FIN ESPEREE!\n" "\n" @@ -381,7 +381,7 @@ E2TEXT = "LA SURFACE DE L'ENFER.\n" "\n" "VOICI MAINTENANT LE CHAPITRE FINAL DE\n" - "DOOM! -- L'ENFER."; + "DOOM! -- INFERNO."; E3TEXT = "LE DEMON ARACHNEEN ET REPUGNANT\n" @@ -399,28 +399,28 @@ E3TEXT = "VOUS VOUS DEMANDEZ CE QUI S'EST PASSE\n" "SUR TERRE PENDANT QUE VOUS AVEZ\n" "COMBATTU LE DEMON. HEUREUSEMENT,\n" - "AUCUN GERME DU MAL N'A FRANCHI\n" + "QU'AUCUN GERME DU MAL N'A FRANCHI\n" "CETTE PORTE AVEC VOUS..."; E4TEXT = - "l'araignee cerveau doit avoir envoye\n" - "ses legions de creatures de l' enfer \n" + "l'araignee cerveau doit avoir envoyé\n" + "ses légions de créatures de l'enfer \n" "avant votre confrontation finale avec\n" - "cette terrible bete de l'enfer.\n" + "cette terrible bête infernale.\n" "Mais vous avancez et mettez de suite\n" "la damnation eternelle et souffrances\n" - "a la meute comme un hero le fairait\n" - "face de quelque chose de si malefique.\n" - "D'ailleurs,quelqu'un va devoir payer \n" - "pour ce qui est arrive a margherite, \n" + "a la meute comme un héros le fairait\n" + "face à quelque chose de si maléfique.\n" + "D'ailleurs, quelqu'un va devoir payer \n" + "pour ce qui est arrivé a Daisy, \n" "votre lapin domestique.\n" "\n" "mais maintenant, vous voyez devant vous \n" "encore plus de nouvelles souffrances \n" - "potencielles et de chairs broyees\n" - "comme une horde de demons devunus \n" - "vindicatifs parmi nos villes.\n\n" - "prochain arret,l'enfer sur terre!"; + "potentielles et de chairs broyees\n" + "comme une horde de demons déversant \n" + "la mort parmi nos villes.\n\n" + "prochain arrêt, l'enfer sur terre!"; // after level 6, put this: @@ -479,7 +479,7 @@ C4TEXT = "TAILLE INCROYABLE S'EFFONDRE DEVANT\n" "VOUS LORSQUE VOUS TIREZ UNE SALVE DE\n" "ROQUETTES DANS SON CERVEAU. LE MONSTRE\n" - "SE RATATINE, SES MEMBRES DECHIQUETES\n" + "S'ECRASE, SES MEMBRES DECHIQUETES\n" "SE REPANDANT SUR DES CENTAINES DE\n" "KILOMETRES A LA SURFACE DE L'ENFER.\n" "\n" @@ -508,151 +508,170 @@ C6TEXT = "MIEUX DE FONCER DANS CELUI-LA!\n"; P1TEXT = - "Vous jubilez sur la carcass brulante \n" - "du Guardien. Avec sa mort, vous avez \n" - "arrache l'accelerateur des griffes \n" - "fetides de l'enfer. Vous vous reposez \n" - "et regardez aux alentours de l'endroit. \n" - "Mince! Il etait suppose qu'il y ait\n" - "au mois un prototype fonctionnant, \n" - "mais vous ne le voyez pas. les demons \n" - "l'on surement pris. \n\n" - "Vous devez trouver le prototype ,ou \n" - "tous vos combats auront ete vains. \n" - "continez a avancer, vous battre, tuer. \n" - "Oh oui ,a vivre ,aussi."; + "Vous jubilez sur la carcasse brûlante \n" + "du Gardien. Avec sa mort, vous avez \n" + "arraché l'accélerateur des griffes \n" + "fétides de l'enfer. Vous vous reposez \n" + "et regardez aux alentours du lieu. \n" + "Mince! Il était supposé qu'il y ait\n" + "au mois un prototype en fonctionnement, \n" + "mais vous ne le trouvez pas. les démons \n" + "l'on sûrement pris avec eux. \n\n" + "Vous devez trouver le prototype, ou \n" + "tous vos combats auront été vains. \n" + "continez à avancer, à vous battre, à tuer. \n" + "Oh oui, à vivre, aussi."; P2TEXT = - "Meme le labyrinthe mortifere des \n" - "archi-infames ne vous arreta pas,\n" - "et vous arrivez au prototype \n" - "d'accelerateur qui est proche \n" - "desactive efficacement et pour toujours.\n" + "Même le labyrinthe mortifère des \n" + "arche-viles ne vous a pas arrêté,\n" + "et vous obtenez le prototype \n" + "d'accelerateur et parvenez à le \n" + "désactiver efficacement et pour toujours.\n" "\n" - "Vous etes doue pour ces choses la."; + "Vous êtes doué pour ces choses là."; P3TEXT = - "Vous vous etes fraye votre chemin a \n" + "Vous vous êtes frayé votre chemin à \n" "travers le coeur d'une ruche de \n" "diables. Il est temps pour une mission\n" "de recherche et destruction visant le \n" - "guardien de la porte ,avec sa \n" - "progeniture repugnante descendant en \n" + "gardien de la porte, avec sa \n" + "progéniture répugnante descendant en \n" "cascade sur terre. Oui ,il est cruel. \n" "Mais vous savez qui est pire!\n\n" - "Sourriant malefiquement, vous verifiez \n" - "l'equipement et vous vous preparez a\n" + "Souriant malicieusement, vous vérifiez \n" + "l'equipement et vous vous préparez a\n" "faire voir a ce fumier un petit enfer\n" - "fait de votre propre main!"; + "fait de vos propres mains!"; P4TEXT = - "La face malefique du gardien de la porte \n" - "est eparpillee aux alentours. Comme son \n" - "corps en lambeaux vacille ,un portail \n" - "inverse se forme et aspire les eclat du \n" - "dernier prototype d'accelerateur, sans \n" + "La face maléfique du gardien de la porte \n" + "est éparpillee aux alentours. Comme son \n" + "corps en lambeaux vacille, un portail \n" + "inverse se forme et aspire les éclats du \n" + "dernier prototype d'accélerateur, sans \n" "parler des quelques demons restants.\n" - "Vous avez termine. L'enfer a fini de\n" - "liberer des mort-vivants au lieu de \n" + "Vous avez gagné. L'enfer a fini de\n" + "libérer des mort-vivants au lieu de \n" "gens bien vivants.\n" "Souvenez vous de dire a vos enfants de\n" "mettre un lance-roquette dans votre\n" "cercueil. Si vous allez en enfer,\n" "quand vous serez mort, vous en aurez \n" - "besoin ,pour un nettoyage definitif..."; + "besoin, pour un nettoyage définitif..."; P5TEXT = - "Vous avez trouver notre second niveau\n" + "Vous avez trouvé le deuxième niveau\n" "le plus difficile que nous avons.\n" - "J'espere que vous avez sauvegarde\n" - "au deux precendants.\n" - "Sinon, soyez prets a mourrir souvent.\n" - "Pour maitre marine seulement."; + "J'espère que vous avez sauvegardé\n" + "lors du précédant...\n" + "Sinon, soyez prêt a mourir souvent.\n" + "Pour les pros seulement."; P6TEXT = - "peut etre, vous demandiez vous quel\n" - "ETAIT le niveau le plus dure, que nous\n" - "avions prepare pour vous? Maintenant,\n" + "Alors, vous vous demandiez quel\n" + "était le niveau le plus dur que nous\n" + "avions préparé pour vous? Maintenant,\n" "vous savez. Nul n'en sortira vivant.\n"; T1TEXT = - "Vous vous etes fraye un chemin vers la\n" - "sortie des labos experimentaux infestes.\n" - "Il semble que l'UAC les a encore vides. \n" - "Avec leur chiffres d'affaires eleve,\n" - "ca doit etre dur pour la pauvre vieille\n" - "UAC d' acheter des societes \n" - "d'assurance vie ,de nos jours...\n" + "Vous vous êtes frayé un chemin vers la\n" + "sortie des labos expérimentaux infestés.\n" + "Il semble que l'UAC les a encore vidés. \n" + "Avec leur chiffres d'affaires élevé,\n" + "ca doit etre dûr pour la pauvre vieille\n" + "UAC d'acheter des societes \n" + "d'assurance vie de nos jours...\n" "\n" "Vous avez devant vous le complexe \n" - "militaire ,cela grouille d'horreurs \n" - "morbides pres a vous enfoncer leur dents.\n" - "Avec un peu de chance ,le complexe a \n" - "encore quelques munitions de guerre,\n" + "militaire, cela grouille d'horreurs \n" + "morbides prètes a vous estropier.\n" + "Avec un peu de chance, le complexe a \n" + "encore quelques munitions de guerre\n" "reposant autour."; T2TEXT = "Vous entendez le grincement d'une grosse\n" - "machinerie devant.Vous esperez surement\n" + "machinerie devant. Vous esperez surement\n" "qu'ils ne sont pas en train de fabriquer\n" "de nouvelles creatures de l'enfer,\n" - "mais vous etes pret a balayer tout\n" + "mais vous etes prêt a balayer tout\n" "un troupeau si vous le deviez.\n" - "Ils pourraient etre en train de preparer\n" - "un festin de sang ,mais vous vous sentez\n" + "Ils pourraient etre en train de préparer\n" + "un festin de sang, mais vous vous sentez\n" "pareil a deux milliers de fous dans le \n" - "corps d'un seul et meme tueur\n" + "corps d'un seul et même tueur.\n" "\n" - "Vous ne pensez pas tomber si facilement."; + "Vous ne pensez pas mourir si facilement."; T3TEXT = "La vue s'ouvrant devant vous semble \n" - "sacrement ,diablement ,familiere.\n" - "Les odeurs familieres, trop -- comme\n" - "des excrements frits. Vous n'aimiez \n" + "sacrément, diablement familière.\n" + "Les odeurs familières, trop -- comme\n" + "des excrèments frits. Vous n'aimiez \n" "pas cet endroit avant, et vous etes \n" - "terriblement sur de ne pas envisager\n" + "terriblement sûr de ne pas envisager\n" "l'aimer maintenant.\n" - "Plus vous pensiez a cette idee ,\n" - "plus vous sombriez dans la folie.\n" - "Saisissant votre arme ,un sourrire\n" - "malefique apparait sur votre visage.\n" - "Il est temps de prendre quelques noms."; + "Plus vous pensiez a cette idée,\n" + "plus vous sombrez dans la folie.\n" + "Saisissant votre arme, un sourrire\n" + "maléfique apparait sur votre visage.\n" + "Il est l'heure de buter du démon...."; T4TEXT = "Tout a coup, tout est silencieux ,\n" "d'un horizon a l'autre.\n" - "L'echo agonisant de l'enfer s'estompe,\n" + "L'écho agonisant de l'enfer s'estompe,\n" "le ciel cauchemardeque tourne au bleu,\n" "les tas de cadavres de monstres \n" - "commencent a s'evaporer avec une puanteur\n" - "nauseabonde qui remplissait l'air.\n" - "Doux jesus ,peut etre l'avez vous fait.\n" - "Avez vous reellement gagne?\n\n" + "commencent a s'évaporer avec une puanteur\n" + "nauséabonde qui remplit l'air.\n" + "Doux jésus, peut être l'avez vous fait.\n" + "Avez vous réellement gagné?\n\n" "quelque chose gronde au loin.\n" - "une lumiere bleue a commence a luire\n" - "dans le crane defonce du demon\n" + "une lumière bleue a commence a luire\n" + "dans le crâne défonce du démon\n" "cracheur."; T5TEXT = "Maintenant quoi? Cela semble \n" - "completement different.\n" - "Une Sorte de lottissement du\n" - "roi Tut.\n" - "Bon, rien ne peut-etre pire\n" + "complètement different.\n" + "Une sorte de lotissement au\n" + "bon vieux Toutankhamon.\n" + "Bon, rien ne peut être pire\n" "que d'habitude. N'est-ce pas?\n" - "Ou peut etre est ce mieux\n" + "Ou peut être est ce mieux\n" "de laisser dormir les dieux,\n" - "la ou ils reposent\n" + "la ou ils reposent..\n" T6TEXT = - "Vous avez besoin de vacances.\n" - "Vous avez eclates les entrailles\n" - "de l'enfer et pour sur vous etes \n" - "pres pour une pause.\n" - "Vous marmonner a vous meme:\n" - "peut-etre quelqu'un pourrait botter\n" + "Vous aviez besoin de vacances.\n" + "Vous avez éclaté les entrailles\n" + "de l'enfer et pour sûr vous êtes \n" + "prét pour une pause.\n" + "Vous marmonnez à vous même:\n" + "Peut-etre quelqu'un pourrait botter\n" "le cul de l'enfer a votre place,\n" "la prochaine fois.\n" - "Une ville tranquille reside devant, \n" + "Une ville tranquille réside devant, \n" "avec le flot paisible de l'eau,\n" "les batiments pittoresques, et\n" - "probablement plus de creatures\n" + "probablement plus de créatures\n" "de l'enfer.\n" "Quand vous descendez au dehors du \n" "transport, vous entendez le bruit \n" "du sabot d'acier d'un \n" - "demon-cybernetique."; + "Cyberdémon."; + +NERVETEXT = + "Quelqu'un avait décidé de foutre le bordel\n" + "sur votre lieu de vacances préféré: L'Enfer.\n" + "Un cyberdémon à la noix avait décidé d'en\n" + "faire son parc d'attraction, et la terre\n" + "son petit guichet.\n" + "\n" + "Cet abruti méchanique ne s'attendait pas\n" + "à ce que vous visitiez son chantier,\n" + "rien de mieux qu'un stand de tir plein de\n" + "démons pour faire chauffer l'ambiance..\n" + "\n" + "Maintenant que le cadavre croulant du démon\n" + "résonne en s'écrasant au sol, ses gémissements\n" + "d'agonie se déversent de son visage\n" + "que vous avez laissé en charpie.\n" + "\n" + "Le manège est fermé!\n"; // @@ -709,7 +728,7 @@ TXT_FRAGLIMIT = "Limite de frags atteinte."; TXT_TIMELIMIT = "Limite de temps atteinte."; // Spree messages -SPREEKILLSELF = "%o était en pleine folie meurtrière que %g ne se bute!"; +SPREEKILLSELF = "%o était en pleine folie meurtrière avant que %g ne se bute!"; SPREEOVER = "La folie meurtrière de %o à été terminée par %!k"; SPREE5 = "%k est en folie meurtrière!"; SPREE10 = "%k est en plein massacre!"; @@ -732,7 +751,7 @@ OB_EXIT = "%o a tenté de partir."; OB_WATER = "%o ne sait pas nager."; OB_SLIME = "%o a muté."; OB_LAVA = "%o a fondu."; -OB_BARREL = "%o a sauté."; +OB_BARREL = "%o s'est pété la face."; OB_SPLASH = "%o s'est tenu au mauvais endroit."; OB_R_SPLASH = "%o aurait dû garder ses distances."; OB_ROCKET = "%o aurait dû garder ses distances."; @@ -788,7 +807,7 @@ OB_IRONLICH = "%o a été dévasté par une Liche de Fer."; OB_IRONLICHHIT = "%o a fait ami-ami avec une Liche de Fer."; OB_BONEKNIGHT = "%o s'est pris la hache d'un guerrier mort-vivant."; OB_BONEKNIGHTHIT = "%o s'est fait pourfendre par un guerrier mort-vivant."; -OB_MINOTAUR = "%o was blasted into cinders by a Maulotaur."; +OB_MINOTAUR = "%o s'est fait incinérer par un Massetaure."; OB_MINOTAURHIT = "%o s'est fait éclater par un Massetaure."; OB_MUMMY = "%o a été défoncé par un golem."; OB_MUMMYLEADER = "%o s'est fait percer les tympans par un nitrogolem."; @@ -796,7 +815,7 @@ OB_SNAKE = "%o s est fait secouer par un ophidien."; OB_WIZARD = "%o a été maudit par un sorcier."; OB_WIZARDHIT = "%o a été palpé par un sorcier."; -OB_FIREDEMON = "%o a gouté au feu d'un Afrit."; +OB_FIREDEMON = "%o a goûté au feu d'un Afrit."; OB_DEMON1 = "%o a été brûlé par un Serpent."; OB_DEMON2 = "%o a été empoisonné par un Serpent."; OB_ETTIN = "%o a été réduit en purée par un Ettin."; @@ -818,10 +837,10 @@ OB_HERESIARCH = "%o s'est fait rouler les os par l'Hérésiarche."; OB_ACOLYTE = "%o a souffert d'une bavure policière."; OB_MACIL = "%o n'aurait jamais du se rebeller contre Macil."; -OB_REBEL = "%o a été abattu par un Rebel."; +OB_REBEL = "%o a été abattu par un rebelle."; OB_BEGGAR = "%o a été battu a mort par un pauvre."; OB_PEASANT = "%o n'aurait jamais du chercher des noises a un civil."; -OB_ALIENSPECTRE = "%o a été terrasse par le Spectre."; +OB_ALIENSPECTRE = "%o a été terrassé par le Spectre."; OB_ENTITY = "%o a senti le courroux du Seul Dieu."; OB_LOREMASTER = "%o n'a pu échapper a l'emprise du Maître des Traditions."; OB_PROGRAMMER = "%o a été effacé par le Programmeur."; @@ -882,14 +901,14 @@ OB_MPCWEAPWRAITHVERGE = "%o wa été purifié par la Verge Phantasmale de %k."; OB_MPMWEAPWAND = "%o s'est pris un rayon saphirique en trop de la part de %k."; OB_MPMWEAPFROST = "%o s'est fait transformer en glaçon par %k."; OB_MPMWEAPLIGHTNING = "%o a reçu un sacré coup de jus de la part de %k."; -OB_MPMWEAPBLOODSCOURGE = "%o s'est fait effacer de l'univers par la Menace Sanglante de %k."; +OB_MPMWEAPBLOODSCOURGE = "%o s'est fait effacer de l'univers par le Fléau Sanglant de %k."; OB_MPPUNCHDAGGER = "%o s'est fait planter un lame dans le dos de la part de %k."; OB_MPELECTRICBOLT = "%o s'est fait clouer au mur par %k."; OB_MPPOISONBOLT = "%o a recu une dose létale de la colère de %k."; OB_MPASSAULTGUN = "%o s'est fait couvrir de trous par le fusil d'assaut de %k."; OB_MPMINIMISSILELAUNCHER = "%o a avalé le missile de %k."; -OB_MPSTRIFEGRENADE = "%o a été mis sens dessus dessous pa la grenade explosive de %k."; +OB_MPSTRIFEGRENADE = "%o a été mis sens dessus dessous par la grenade explosive de %k."; OB_MPPHOSPHOROUSGRENADE = "%o s'est permis une pyroclave dans les flammes phosphoriques de %k."; OB_MPFLAMETHROWER = "%o est passé au barbecue de %k."; OB_MPMAULER1 = "%o s'est fait électrocuter par %k."; @@ -897,7 +916,7 @@ OB_MPMAULER = "%o à été vicieusement vaporisé par %k."; OB_MPSIGIL = "%o s'est prosterné face à la toute puissance du Sigil de %k."; // Same as OB_MPTELEFRAG, but shown when a monster telefrags you -OB_MONTELEFRAG = "%o a été telefrague."; +OB_MONTELEFRAG = "%o a été telefragué."; OB_DEFAULT = "%o est mort."; OB_FRIENDLY1 = "%k a tué un de ses équipiers."; @@ -905,21 +924,12 @@ OB_FRIENDLY2 = "%k vérifie ses lunettes."; OB_FRIENDLY3 = "%k marque un point pour l'autre équipe."; OB_FRIENDLY4 = "%k a perdu un autre ami."; -SAVEGAMENAME = "zdoomsv"; -STARTUP1 = ""; -STARTUP2 = ""; -STARTUP3 = ""; -STARTUP4 = ""; -STARTUP5 = ""; SCORE_ITEMS = "OBJETS"; -SCORE_BONUS = "BONUS"; SCORE_COLOR = "COULEUR"; -SCORE_SECRET = "SECRET"; SCORE_NAME = "NOM"; SCORE_DELAY = "DELAI(ms)"; SCORE_KILLS = "VICTIMES"; -SCORE_FRAGS = "FRAGS"; SCORE_DEATHS = "MORTS"; SCORE_MISSED = "RATES"; SCORE_TOTAL = "TOTAL"; @@ -971,7 +981,7 @@ TAG_ARTITORCH = "Torche"; TAG_CWEAPMACE = "Masse de Pénitence"; TAG_CWEAPSTAFF = "Sceptre du Serpent"; TAG_CWEAPFLAME = "Tempête de Feu"; -TAG_CWEAPWRAITHVERGE = "Bâton du Courroux"; +TAG_CWEAPWRAITHVERGE = "Verge Phantasmale"; TAG_FWEAPFIST = "Gantelets à Pointes"; TAG_FWEAPAXE = "Hache de Timon"; TAG_FWEAPHAMMER = "Marteau de la Rétribution"; @@ -1033,7 +1043,7 @@ TAG_SIGIL = "SIGIL"; TAG_COIN = "Pièce"; TAG_MEDPATCH = "Pansement"; TAG_MEDICALKIT = "Kit Médical"; -TAG_SURGERYKIT = "Kit de Chirurgie"; // "full_health" in the Teaser +TAG_SURGERYKIT = "Kit de Chirurgie"; TAG_BELDINSRING = "Anneau"; TAG_OFFERINGCHALICE = "Calice d'Obole"; TAG_EAR = "Oreille"; @@ -1059,20 +1069,20 @@ TAG_TELEPORTERBEACON = "Balise de téléportation"; TAG_METALARMOR = "Armure en Métal"; TAG_LEATHER = "Armure en Cuir"; TAG_HEGRENADES = "Grenades Explosives"; -TAG_PHGRENADES = "Grenades Incendiaires"; // "Fire-Grenade_Rounds" in the Teaser -TAG_CLIPOFBULLETS = "Chargeur d'Assaut"; // "bullets" in the Teaser +TAG_PHGRENADES = "Grenades Incendiaires"; +TAG_CLIPOFBULLETS = "Chargeur d'Assaut"; TAG_BOXOFBULLETS = "Bôite de Munitions"; -TAG_MINIMISSILES = "Mini-Missiles"; //"rocket" in the Teaser -TAG_CRATEOFMISSILES = "Caisse de Mini-Missiles"; //"box_of_rockets" in the Teaser +TAG_MINIMISSILES = "Mini-Missiles"; +TAG_CRATEOFMISSILES = "Caisse de Mini-Missiles"; TAG_ENERGYPOD = "Cellule Energétique"; TAG_ENERGYPACK = "Pack Energétique"; -TAG_POISONBOLTS = "Carreaux Empoisonnés"; // "poison_arrows" in the Teaser -TAG_ELECTRICBOLTS = "Carreaux Electriques"; // "electric_arrows" in the Teaser -TAG_AMMOSATCHEL = "Sacoche à munitions"; // "Back_pack" in the Teaser +TAG_POISONBOLTS = "Carreaux Empoisonnés"; +TAG_ELECTRICBOLTS = "Carreaux Electriques"; +TAG_AMMOSATCHEL = "Sacoche à munitions"; + -// Item tags: Strife keys TAG_BASEKEY = "Clé de la base"; -TAG_GOVSKEY = "Clé du Gouverneur"; // "Rebel_Key" in the Teaser +TAG_GOVSKEY = "Clé du Gouverneur"; TAG_PASSCARD = "Passe"; TAG_IDBADGE = "Badge d'Identification"; TAG_PRISONKEY = "Clé de la Prison"; @@ -1091,13 +1101,12 @@ TAG_BRASSKEY = "Clé en Bronze"; TAG_REDCRYSTALKEY = "Clé de Cristal Rouge"; TAG_BLUECRYSTALKEY = "Clé de Crisal Bleu"; TAG_CHAPELKEY = "Clé de la Chapelle"; -TAG_CATACOMBKEY = "Clé des Catacombes"; // "Tunnel_Key" in the Teaser +TAG_CATACOMBKEY = "Clé des Catacombes"; TAG_SECURITYKEY = "Clé de la Sécurité"; -TAG_COREKEY = "Clé du Réacteur"; // "New_Key1" in the Teaser -TAG_MAULERKEY = "Clé du Broyeur"; // "New_Key2" in the Teaser -TAG_FACTORYKEY = "Clé de l'Usine"; // "New_Key3" in the Teaser -TAG_MINEKEY = "Clé de la Mine"; // "New_Key4" in the Teaser -TAG_NEWKEY5 = "New Key5"; +TAG_COREKEY = "Clé du Réacteur"; +TAG_MAULERKEY = "Clé du Broyeur"; +TAG_FACTORYKEY = "Clé de l'Usine"; +TAG_MINEKEY = "Clé de la Mine"; TAG_ORACLEPASS = "Passe de l'Oracle"; // Item tags: misc Strife stuff @@ -1105,9 +1114,6 @@ TAG_10GOLD = "10 Pièces"; TAG_25GOLD = "25 Pièces"; TAG_50GOLD = "50 Pièces"; TAG_300GOLD = "300 Pièces"; -TAG_QUEST4 = "quest4"; -TAG_QUEST5 = "quest5"; -TAG_QUEST6 = "quest4"; // Item tags: Strife NPCs TAG_ACOLYTE = "Acolyte"; @@ -1130,7 +1136,7 @@ TAG_SPORK = "Super Fourchette"; TAG_MINIZORCHER = "Mini Zorcheur"; TAG_LARGEZORCHER = "Zorcheur Large"; TAG_SUPERLARGEZORCHER = "Zorcheur Extra-large"; -TAG_RAPIDZORCHER = "Zorcheur"; +TAG_RAPIDZORCHER = "Zorcheur Rapide"; TAG_ZORCHPROPULSOR = "Propulseur de Zorch"; TAG_PHASINGZORCHER = "Zorcheur à Phase"; TAG_LAZDEVICE = "ZZL"; @@ -1138,113 +1144,113 @@ TAG_LAZDEVICE = "ZZL"; // Heretic strings HE1TEXT = - "avec la destruction des sangsues\n" - "de fer et leur seides, les derniers\n" - "des mort-vivants furent nettoye de\n" - "cette face de l'existence.\n\n" - "ces creatures durent venir de \n" - "quelquepart, bien que,vous eutes le\n" - "soupcon sournois que l ardent\n" - "portail de l'enfer put conduire\n" + "Avec la destruction des liches\n" + "de fer et leur serviteurs, les derniers\n" + "mort-vivants furent eliminés de\n" + "cette plaine de l'existence.\n\n" + "ces créatures doivent venir de \n" + "quelque part, bien que vous ayez\n" + "un soupçon que ce portail\n" + "ardent de l'enfer puisse conduire\n" "dans leur dimension.\n\n" - "pour etre sur que beaucoup de\n" - "mort-vivants ou pire ne viennent\n" - "par son biais, vous dutes scelle le\n" - "portail de l enfer de l'autre cote.\n" - "bien sur cela veut dire que vous\n" - "eutes pu rester coince dans un monde\n" - "tres hostile, mais personne n'a\n" - "jamais dis qu'etre un heretic eut\n" - "ete facile!"; + "pour etre sûr qu'aucun des\n" + "mort-vivants ou pire ne suivent\n" + "ce chemin, vous scemllez dûment le\n" + "portail de l'autre côté.\n" + "Bien sur cela veut dire que vous\n" + "auriez pû rester coincé dans un monde\n" + "très hostile, mais personne n'a\n" + "jamais dit qu'être un hérétique\n" + "soit facile!"; HE2TEXT = - "les puissants massotaures eurent \n" - "prouve ne pas vous correspondre,\n" - "et leurs cadavres vaporeux glissant sur\n" - "le sol, vous sentites l effroyable\n" - "satisfaction qu ils furent\n" - "detruit.\n\n" - "les passerelles qu'ils garderent\n" - "furent ouvertes, revelant ce que vous\n" - "esperates etre le chemin du retour.\n" - "mais au fur et a mesure que vous \n" - "avancates ,un rire moqueur sonna a\n" - "vos oreilles.\n\n" - "etait ce quelqu'autre force controlant\n" - "les massotaures? eusse t'elles pu etre\n" - "plus atroces les creatures dans\n" - "cette porte? le balayage d'un dome\n" - "de crystal\n" - "depasse ou le ciel aurait du etre\n" - "ce ne fut pas de bon augure...."; + "les puissants massetaures ont fini par\n" + "prouver qu'il n'étaient pas votre égal,\n" + "et leurs cadavres fumants étalés sur\n" + "le sol, vous sentez l'infernale\n" + "satisfaction de les avoir complètement\n" + "détruits.\n\n" + "la passage qu'ils gardaient\n" + "s'ouvrit, révélant ce que vous\n" + "espérez être le chemin du retour.\n" + "Mais au fur et a mesure que vous \n" + "avancez, un rire moqueur sonne\n" + "à vos oreilles.\n\n" + "était-ce quelque autre force contrôlant\n" + "les massetaures? Ait elle pû être\n" + "plus atroce que les créatures de\n" + "ce portail? La forme d'un dôme\n" + "de cristal dépasse de\n" + "l'horizon, couvrant le ciel,\n" + "ce n'est pas de bon augure...."; HE3TEXT = - "la mort de d'sparil a liberes\n" + "la mort de d'sparil a liberé\n" "les liens magiques liant\n" - "les creatures de cette face,\n" - "leurs cri en mourrant surpasserent\n" - "ses propres cris d agonie.\n\n" + "les créatures de ce monde,\n" + "leurs cris d'agonie surpassant\n" + "ses propres hurlements.\n\n" "votre serment de vengeance rempli,\n" - "vous entrate dans le portail pour\n" - "votre monde, moments insignifiants\n" - "avant que la coupole ne volasse en \neclats.\n" - "mais si le pouvoir de d'sparil\n" - "fut rompu a jamais, pourquoi ne pas\n" - "ne pas vous sentir en securite?\n" - "fut ce son dernier cri\n" - "celui qui resonna comme une\n" - "malediction? une invoquation? vous\n" - "ne putes en etre sur vraiment, mais\n" - "il s'eut put n'etre qu un cri.\n\n" - "puis de nouveau, que devenirent\n" - "les autres serpents coureurs?"; + "vous pénétrez dans le portail vers\n" + "votre monde, un court moment\n" + "avant que la coupole vole en éclats.\n" + "Mais avec le pouvoir de d'sparil\n" + "rompu a jamais, pourquoi vous ne\n" + "pouvez pas vous sentir en sécurite?\n" + "Serai-çe à cause de son dernier cri\n" + "celui qui résonna comme une\n" + "malédiction? une invocation? vous\n" + "ne pouvez pas en être sûr, mais\n" + "ce n'est peut être qu un cri.\n\n" + "De plus, qu'adviendrat-il\n" + "des autres chevaucheurs de serpent?"; HE4TEXT = - "vous crutes pouvoir retourner dans\n" + "vous aviez cru pouvoir retourner dans\n" "votre monde apres que d'sparil\n" - "trepassa , mais son acte\n" + "ne trépasse, mais son acte\n" "final fut de vous bannir dans sa\n" - "propre dimension. ici vous entrates\n" - " dans des restes effondres de terres\n" - "conquisent par d'sparil. vous eutes\n" + "propre dimension. Ici vous errez\n" + "dans les territoires ruinés des\n" + "conquêtes de d'sparil. vous êtes\n" "le dernier gardien de ces terres,\n" - "mais a ce moment vous vous tenites\n" + "mais a ce moment vous vous tenez\n" "devant le bastion de d'sparil." - "jusqua ce moment vous n'eutes \n" + "jusqu'à ce moment vous n'aviez\n" "aucun doute sur votre capacite\n" - "d affronter tout ce que vous pouviez\n" - "rencontrer,mais derriere ce portail\n" - "git le coeur profond du mal\n" + "à affronter tout ce que vous pouviez\n" + "rencontrer, mais derrière ce portail\n" + "gît le coeur du mal profond\n" "qui envahit votre monde. d'sparil\n" - "eut pu etre mort,mais le puit ou\n" - "il fut ne demeura. maintenant vous\n" - "dutes entrer dans ce puit dans l'espoir\n" - "de trouver une sortie.et quelque part,\n" + "peut être mort, mais le puis d'où\n" + "il est sorti ne l'est pas. Maintenant\n" + "vous devez y entrer dans l'espoir\n" + "de trouver une sortie, quelque part,\n" "dans la plus sombre partie du\n" - "royaume de d'sparil,\n" - "sa garde personnelle attendit votre\n" - "arrivee ..."; + "royaume de d'sparil,\n" + "Sa garde personnelle attend votre\n" + "arrivée ..."; HE5TEXT = - "des que le massotaure final eu mugit\n" - "son agonie mortelle, vous realisates que\n" - "vous n'eutes jamais ete aussi proche de\n" - "votre propre destruction.meme le \n" + "dès que le massetaure final eu mugit\n" + "son râle d'agonie, vous réalisez que\n" + "vous n'avez jamais été aussi proche de\n" + "votre propre mort. Même le \n" "combat contre d'sparil et ses\n" "disciples n'eut ete a ce point \n" - "desespere. souriant, quand vous \n" - "regardates fixement le portail s'ouvrir \n" - "devant vous ,vous demandant s'il menait \n" - "chez vous,ou si il ouvrait sur une\n" - "innimaginable horreur. vous vous \n" - "demandates si vous eutes la force d'y aller,\n" + "éreintant. Souriant, vous \n" + "regardez fixement le portail s'ouvrir \n" + "devant vous, vous demandant s'il mêne\n" + "chez vous, où si il ouvre sur une\n" + "horreur inimaginable. Vous n'êtes \n" + "pas sûr d'avoir la force d'y aller,\n" "si rien d'autre que souffrance\n" - "et mort vous eurent attendu.\n" - "mais que putes vous faire d'autre, si\n" - "la volonte de vous battre s'eut enfuie?\n" - "putes vous,vous meme vous forcer a \n" - "continuer face a un tel desespoir? \n" - "eutes vous le courage? vous trouvates,\n" - "a la fin ,ce ne fut pas dans votre nature\n" - "de vous rendre sans un combat. les yeux\n" - "larges ,vous partates rencontrer votre\n" - "destinee."; + "et mort ne vous y attendent.\n" + "mais que faire d'autre, si la\n" + "volonté de vous battre disparaît?\n" + "Comment voulez-vous vous forcer à\n" + "continuer face a un tel désespoir? \n" + "En avez vous le courage? Vous pensez,\n" + "a la fin que ce n'est pas dans votre nature\n" + "de vous rendre sans combattre. les yeux\n" + "larges, vous partez rencontrer votre\n" + "destinée."; // EPISODE 1 - LA CITE DES DAMNES HHUSTR_E1M1 = "LES DOCKS"; @@ -1352,7 +1358,7 @@ TXT_WPNSKULLROD = "BATON INFERNAL"; TXT_WPNPHOENIXROD = "BATON INFERNAL"; TXT_WPNGAUNTLETS = "GANTELETS DU NECROMANCIEN"; -TXT_NEEDBLUEKEY = "CETTE PORTE NESCESSITE UNE CLE BLEU POUR S'OUVRIR"; +TXT_NEEDBLUEKEY = "CETTE PORTE NESCESSITE UNE CLE BLEUE POUR S'OUVRIR"; TXT_NEEDGREENKEY = "CETTE PORTE NESCESSITE UNE CLE VERTE POUR S'OUVRIR"; TXT_NEEDYELLOWKEY = "CETTE PORTE NESCESSITE UNE CLE JAUNE POUR S'OUVRIR"; @@ -1367,7 +1373,6 @@ TXT_CHEATIDKFA = "TRICHEUR - TU NE MERITE PAS D'ARMES!"; TXT_CHEATTICKERON = "COMPTEUR ALLUME"; TXT_CHEATTICKEROFF = "COMPTER ETEINT"; TXT_CHEATARTIFACTS3 = "VOUS L'AVEZ"; -TXT_MIDASTOUCH = "YOU GOT THE MIDAS TOUCH, BABY!"; TXT_GOTSTUFF = "Voilà ton équipement!"; TXT_FREEZEON = "Temps arrêté."; TXT_FREEZEOFF = "Le temps reprend son cours.."; @@ -1385,6 +1390,19 @@ TXT_IMTIME = "TEMPS"; RAVENQUITMSG = "ETES VOUS SUR DE VOULOIR QUITTER?"; +FN_CHICKEN = "Poulet"; +FN_BEAST = "Dragon-garou"; +FN_CLINK = "Sabregriffe"; +FN_DSPARIL = "D'Sparil"; +FN_HERETICIMP = "Gargouille"; +FN_IRONLICH = "Liche de Fer"; +FN_BONEKNIGHT = "Guerrier Mort-Vivant"; +FN_MINOTAUR = "Massetaure"; +FN_MUMMY = "Golem"; +FN_MUMMYLEADER = "Nitrogolem"; +FN_SNAKE = "Ophidien"; +FN_WIZARD = "Magicien"; + // Hexen strings // Mana @@ -1400,7 +1418,7 @@ TXT_KEY_CAVE = "CLE DE LA CAVE"; TXT_KEY_AXE = "CLE DE LA HACHE"; TXT_KEY_FIRE = "CLE DU FEU"; TXT_KEY_EMERALD = "CLE D'EMERAUDE"; -TXT_KEY_DUNGEON = "CLE DU DONGEON"; +TXT_KEY_DUNGEON = "CLE DU DONJON"; TXT_KEY_SILVER = "CLE D'ARGENT"; TXT_KEY_RUSTED = "CLE ROUILLEE"; TXT_KEY_HORN = "CLE CORNE"; @@ -1412,10 +1430,10 @@ TXT_NEED_KEY_CAVE = "Vous avez besoin de la CLE DE LA CAVE"; TXT_NEED_KEY_AXE = "Vous avez besoin de la CLE DE LA HACHE"; TXT_NEED_KEY_FIRE = "Vous avez besoin de la CLE DU FEU"; TXT_NEED_KEY_EMERALD = "Vous avez besoin de la CLE D'EMERAUDE"; -TXT_NEED_KEY_DUNGEON = "Vous avez besoin de la CLE DU DONGEON"; +TXT_NEED_KEY_DUNGEON = "Vous avez besoin de la CLE DU DONJON"; TXT_NEED_KEY_SILVER = "Vous avez besoin de la CLE D'ARGENT"; TXT_NEED_KEY_RUSTED = "Vous avez besoin de la CLE ROUILLEE"; -TXT_NEED_KEY_HORN = "Vous avez besoin de la CLE ROUILLEE"; +TXT_NEED_KEY_HORN = "Vous avez besoin de la CLE CORNE"; TXT_NEED_KEY_SWAMP = "Vous avez besoin de la CLE DES MARECAGES"; TXT_NEED_KEY_CASTLE = "Vous avez besoin de la CLE DU CHATEAU"; @@ -1464,13 +1482,13 @@ TXT_WEAPON_F3 = "MARTEAU DE LA RETRIBUTION"; TXT_WEAPON_F4 = "QUIETUS ASSEMBLE"; TXT_WEAPON_C2 = "SCEPTRE DU SERPENT"; TXT_WEAPON_C3 = "TEMPETE DE FEU"; -TXT_WEAPON_C4 = "BATON DU COURROUX ASSEMBLE"; +TXT_WEAPON_C4 = "VERGE PHANTASMALE ASSEMBLE"; TXT_WEAPON_M2 = "ECLATS DE GIVRE"; TXT_WEAPON_M3 = "FOUDRE MORTELLE"; TXT_WEAPON_M4 = "FLEAU SANGLANT ASSEMBLE"; TXT_WEAPONPIECE = "Une piece d'arme! C'est votre jour de chance!"; TXT_QUIETUS_PIECE = "SEGMENT DE QUIETUS"; -TXT_WRAITHVERGE_PIECE = "SEGMENT DU BATON DU COURROUX"; +TXT_WRAITHVERGE_PIECE = "SEGMENT DE LA VERGE PHANTASMALE"; TXT_BLOODSCOURGE_PIECE = "SEGMENT DU FLEAU SANGLANT"; // Strife locks @@ -1488,6 +1506,8 @@ TXT_NEED_SILVERKEY = "Vous avez besoin de la clé en argent."; TXT_NEED_BRASSKEY = "Vous avez besoin de la clé en bronze."; TXT_NEED_REDCRYSTAL = "Vous avez besoin du cristal rouge."; TXT_NEED_BLUECRYSTAL = "Vous avez besoin du cristal bleu."; +TXT_RETAIL_ONLY = "CETTE ZONE N'EST ACCESSIBLE QUE DANS LA VERSION COMPLETE DE STRIFE"; +TXT_DOES_NOT_WORK = "Cela ne semble pas fonctionner."; // Strife Quest messages @@ -1599,8 +1619,8 @@ TXT_RANDOM_REBEL_03 = "On couvre tes arrières, t'inquiète pas."; TXT_RANDOM_REBEL_04 = "Ne vous approchez pas trop des gros robots. Ils brûleront jusqu'à l'os!"; TXT_RANDOM_REBEL_05 = "Le jour de gloire arrivera bientôt, et ceux qui s'opposeront a nous seront écrasés!"; TXT_RANDOM_REBEL_06 = "Ne vous reposez pas sur vos lauriers. Du travail nous attend encore."; -TXT_RANDOM_REBEL_07 = "Macil nous dis que tu es notre nouvel espoir. Garde ca à l'esprit."; -TXT_RANDOM_REBEL_08 = "Une fois que nous nous serons débarassés de ces charlatans, nous serons capable de rebatir le monde tel qu'il devrait être."; +TXT_RANDOM_REBEL_07 = "Macil nous dit que tu es notre nouvel espoir. Garde ca à l'esprit."; +TXT_RANDOM_REBEL_08 = "Une fois que nous nous serons débarassés de ces charlatans, nous serons capable de rebâtir le monde tel qu'il devrait être."; TXT_RANDOM_REBEL_09 = "Souviens-toi, tu ne te bas pas seulement pour toi, mais pour tous le monde ici et ailleurs."; TXT_RANDOM_REBEL_10 = "Aussi longtemps qu'un de nous est vivant, nous vaincrons."; @@ -1619,14 +1639,14 @@ TXT_RANDOM_BEGGAR_01 = "L'aumône pour les pauvres?"; TXT_RANDOM_BEGGAR_02 = "Tu regarde qui, habitant de la surface?"; TXT_RANDOM_BEGGAR_03 = "Aurais-tu de la nourriture en trop sur toi?"; TXT_RANDOM_BEGGAR_04 = "Vous, les gens de la surface, vous ne comprendrez jamais."; -TXT_RANDOM_BEGGAR_05 = "Ha, les gardes ne nous trouveront jamaiss, ces idios ne savent même pas que l'on existe!"; +TXT_RANDOM_BEGGAR_05 = "Ha, les gardes ne nous trouveront jamais, ces idiots ne savent même pas qu'on existe!"; TXT_RANDOM_BEGGAR_06 = "Un jour, tous sauf les membres de l'ordre seront forcés de nous rejoindre."; -TXT_RANDOM_BEGGAR_07 = "Amuse toi à nous regarder autant que tu veus, mais tu sais que ceci sera aussi ton sort un jour."; -TXT_RANDOM_BEGGAR_08 = "Il n'y rien de plus barbant qu'un type de la surface et son attitude!"; +TXT_RANDOM_BEGGAR_07 = "Moque nous si tu veux, mais tu sais que ceci sera aussi ton sort un jour."; +TXT_RANDOM_BEGGAR_08 = "Il n'y rien de plus barbant qu'un type de la surface et son orgeuil!"; TXT_RANDOM_BEGGAR_09 = "L'ordre se débarrassera de vos soldats pathétiques."; TXT_RANDOM_BEGGAR_10 = "Prend guarde a toi, habitant de la surface. Nous connaîssons nos ennemis!"; -TXT_RANDOM_PGUARD_01 = "Nous sommes les mains du destin. Apprendre notre colere est l'égal de trouver l'oubli!"; +TXT_RANDOM_PGUARD_01 = "Nous sommes les mains du destin. Apprendre notre colère est l'égal de trouver l'oubli!"; TXT_RANDOM_PGUARD_02 = "L'ordre nettoira le monde des faibles et corrompus!"; TXT_RANDOM_PGUARD_03 = "Obéissez à la volonté de vos maîtres!"; TXT_RANDOM_PGUARD_04 = "Longue vie aux membres de l'Ordre!"; @@ -1687,7 +1707,7 @@ MNU_DEMESNE = "LE DOMAINE STAGNANT"; MNU_WETNURSE = "VOUS AVEZ BESOIN D'UNE NOURRICE"; MNU_YELLOWBELLIES = "UN TROUILLARD-TU-EST"; -MNU_BRINGEST = "AMMENEZ LES MOI!"; +MNU_BRINGEST = "AMENEZ LES MOI!"; MNU_SMITE = "TU EST UN MAITRE-MEURTRIER"; MNU_BLACKPLAGUE = "LA PESTE NOIRE ME POSSEDE"; @@ -1721,7 +1741,6 @@ MNU_WARRIOR = "GUERRIER"; MNU_ALTARBOY = "ENFANT DE CHOEUR"; MNU_PRIEST = "PRETRE"; -MNU_CARDINAL = "CARDINAL"; MNU_POPE = "PAPE"; MNU_APPRENTICE = "APPRENTI"; @@ -1754,7 +1773,7 @@ OPTMNU_CHANGERENDER = "Changer de Moteur de Rendu"; OPTMNU_DEFAULTS = "Réinitialiser les paramètres"; OPTMNU_RESETTOSAVED = "Recharger dernière config."; OPTMNU_CONSOLE = "Ouvrir la console"; - +OPTMNU_REVERB = "Editeur environement de révérb."; // Controls Menu CNTRLMNU_TITLE = "MODIFIER CONTROLES"; @@ -1821,7 +1840,7 @@ CNTRLMNU_OTHER = "Autres"; CNTRLMNU_AUTOMAP = "Activer Carte"; CNTRLMNU_CHASECAM = "Caméra 3ième personne"; CNTRLMNU_COOPSPY = "Espionner Coéquiper"; -CNTRLMNU_SCREENSHOT = "Capture d'Ecran"; +CNTRLMNU_SCREENSHOT = "Capture d'écran"; CNTRLMNU_CONSOLE = "Ouvrir Console"; CNTRLMNU_POPUPS = "Popups de Strife"; CNTRLMNU_MISSION = "Objectifs de Mission"; @@ -1836,7 +1855,7 @@ MOUSEMNU_MOUSEINMENU = "Activer Souris dans les Menus"; MOUSEMNU_SHOWBACKBUTTON = "Afficher le bouton retour"; MOUSEMNU_CURSOR = "Curseur"; MOUSEMNU_SENSITIVITY = "Sensibilité générale"; -MOUSEMNU_NOPRESCALE = "Prescaling movement souris"; +MOUSEMNU_NOPRESCALE = "Prescaling mouvement souris"; MOUSEMNU_SMOOTHMOUSE = "Lissage Souris"; MOUSEMNU_TURNSPEED = "Vitesse pour tourner"; MOUSEMNU_MOUSELOOKSPEED = "Vitesse Vue Souris"; @@ -1985,7 +2004,7 @@ MISCMNU_QUERYIWAD = "Afficher la séléction d'IWAD"; MISCMNU_ALLCHEATS = "Activer cheats de tous les jeux"; MISCMNU_ENABLEAUTOSAVES = "Activer Sauvegardes auto"; MISCMNU_AUTOSAVECOUNT = "Total de sauvegardes auto"; -MISCMNU_SAVELOADCONFIRMATION = "Confirmation C/S"; +MISCMNU_SAVELOADCONFIRMATION = "Confirmation C/S"; MISCMNU_DEHLOAD = "Charger fichiers *.deh/*.bex"; MISCMNU_CACHENODES = "Mise en cache des nodes"; MISCMNU_CACHETIME = "Limite cache des nodes"; @@ -2119,7 +2138,7 @@ GMPLYMNU_ALLOWSPYING = "Autoriser espionnage"; GMPLYMNU_CHASECAM = "Cheat caméra 3ième personne"; GMPLYMNU_DONTCHECKAMMO = "Vérifier munitions pour changement arme"; GMPLYMNU_KILLBOSSSPAWNS = "Tuer L'Icône tue tous ses monstres"; -GMPLYMNU_NOCOUNTENDMONSTER = "Ennemis dans secteur de sortie comptent"; +GMPLYMNU_NOCOUNTENDMONSTER = "Ennemis du secteur sortie comptés"; GMPLYMNU_DEATHMATCH = "Options Deathmatch"; GMPLYMNU_WEAPONSSTAY = "Armes restent au sol quand récupérées"; GMPLYMNU_ALLOWPOWERUPS = "Autoriser powerups"; @@ -2149,21 +2168,21 @@ GMPLYMNU_SPAWNWHEREDIED = "Réapparaitre sur lieu de mort"; CMPTMNU_TITLE = "OPTIONS COMPATIBILITE"; CMPTMNU_MODE = "Mode de compatibilité"; CMPTMNU_ACTORBEHAVIOR = "Comportement des Acteurs"; -CMPTMNU_CORPSEGIBS = "Monstres écrasés peuvent être ressucités"; -CMPTMNU_NOBLOCKFRIENDS = "Monstres amicaux ne sont pas bloqués"; -CMPTMNU_LIMITPAIN = "Limite sur les âmes des élémentaires"; -CMPTMNU_MBFMONSTERMOVE = "Mouvement monstre affecté par les effets"; +CMPTMNU_CORPSEGIBS = "Monstres écrasés résucitables"; +CMPTMNU_NOBLOCKFRIENDS = "Monstres amicaux non bloqués"; +CMPTMNU_LIMITPAIN = "Limiter âmes des élémentaires"; +CMPTMNU_MBFMONSTERMOVE = "Mouvement monstre affecté par effets"; CMPTMNU_CROSSDROPOFF = "Monstres ne passent pas les corniches"; CMPTMNU_DROPOFF = "Monstres bloqués par les corniches"; CMPTMNU_INVISIBILITY = "Monstres voient joueurs invisibles"; CMPTMNU_MINOTAUR = "Pas de feu de Massetaur sur l'eau"; -CMPTMNU_NOTOSSDROPS = "OBjets lâchés sont direct au sol"; +CMPTMNU_NOTOSSDROPS = "Objets lâchés direct au sol"; CMPTMNU_DEHACKEDBEHAVIOR = "Comportement DeHackEd"; CMPTMNU_DEHHEALTH = "Niveaux de santé DEH comme Doom2.EXE"; CMPTMNU_MUSHROOM = "Vitesse A_Mushroom originale pour DEH"; CMPTMNU_MAPACTIONBEHAVIOR = "Comportement Niveau/Actions"; CMPTMNU_USEBLOCKING = "Toute ligne d'action bloque "; -CMPTMNU_ANYBOSSDEATH = "N'importe quel boss peut activer actions boss"; +CMPTMNU_ANYBOSSDEATH = "N'importe quel boss active actions boss"; CMPTMNU_NODOORLIGHT = "Pas d'effet de lumière BOOM sur portes"; CMPTMNU_LIGHT = "Trouver prochaine texture comme DOOM"; CMPTMNU_SHORTTEX = "Trouver plus petite texture comme Doom"; @@ -2200,7 +2219,7 @@ SNDMNU_SFXVOLUME = "Volume des Sons"; SNDMNU_MENUVOLUME = "Volume du Menu"; SNDMNU_MUSICVOLUME = "Volume Musique"; SNDMNU_MIDIDEVICE = "Sortie MIDI"; -SNDMNU_BACKGROUND = "Sons activé en arrière plan"; +SNDMNU_BACKGROUND = "Son activé en arrière plan"; SNDMNU_UNDERWATERREVERB = "Reverbération sous l'eau"; SNDMNU_RANDOMIZEPITCHES = "Tons sonores aléatoires"; SNDMNU_CHANNELS = "Canaux sonores"; @@ -2218,7 +2237,7 @@ OPENALMNU_RESAMPLER = "Resampler"; // Advanced Sound Options ADVSNDMNU_TITLE = "OPTIONS SONORES AVANCEES"; -ADVSNDMNU_SAMPLERATE = "Sample rate"; +ADVSNDMNU_SAMPLERATE = "Cadence de Sampling"; ADVSNDMNU_HRTF = "HRTF"; ADVSNDMNU_OPLSYNTHESIS = "Synthèse OPL"; ADVSNDMNU_OPLNUMCHIPS = "Puces OPL émulées"; @@ -2229,18 +2248,24 @@ ADVSNDMNU_GUSCONFIG = "Fichier Config. GUS"; ADVSNDMNU_MIDIVOICES = "Voix MIDI"; ADVSNDMNU_DMXGUS = "Lire fichiers DMXGUS"; ADVSNDMNU_GUSMEMSIZE = "Taille mémoire GUS"; -ADVSNDMNU_FLUIDSYNTH = "FluidSynth"; -ADVSNDMNU_FLUIDPATCHSET = "Patchset"; -ADVSNDMNU_FLUIDGAIN = "Gain"; -ADVSNDMNU_REVERB = "Reverb"; -ADVSNDMNU_CHORUS = "Chorus"; -ADVSNDMNU_TIMIDITY = "Timidity++"; ADVSNDMNU_TIMIDITYEXE = "Chemin exécutable"; ADVSNDMNU_TIMIDITYCONFIG = "Fichier de config. TiMidity"; ADVSNDMNU_TIMIDITYVOLUME = "Volume Relatif"; -ADVSNDMNU_WILDMIDI = "WildMidi"; ADVSNDMNU_WILDMIDICONFIG = "Fichier config. WildMidi"; ADVSNDMNU_SELCONFIG = "Sélectionner configuration"; +ADVSNDMNU_ADVRESAMPLING = "Resampling Avancé"; +ADVSNDMNU_OPLBANK = "Banque OPL"; +ADVSNDMNU_ADLOPLCORES = "Coeur Emulateur OPL"; +ADVSNDMNU_RUNPCMRATE = "Emulateur utilise cadence PCM"; +ADVSNDMNU_ADLNUMCHIPS = "Puces OPL émulées"; +ADVSNDMNU_VLMODEL = "Modèle de Volume"; +ADVSNDMNU_OPNNUMCHIPS = "Puces OPN émulées"; + +// ADLMIDI's volume models +ADLVLMODEL_AUTO = "Auto (Utiliser paramètre banque)"; +ADLVLMODEL_GENERIC = "Générique"; +ADLVLMODEL_NATIVE = "OPL Natif"; + // Module Replayer Options MODMNU_TITLE = "OPTIONS LECTEUR MODULES"; @@ -2248,7 +2273,6 @@ MODMNU_REPLAYERENGINE = "Moteur de lecture"; MODMNU_MASTERVOLUME = "Volume maître"; MODMNU_QUALITY = "Qualité"; MODMNU_VOLUMERAMPING = "Rampe du volume"; -MODMNU_CHIPOMATIC = "Chip-o-matic"; // Renderer Options RNDMNU_TITLE = "SELECTION MOTEUR RENDU"; @@ -2259,6 +2283,7 @@ RNDMNU_CANVAS = "Canvas Software"; // Video Options VIDMNU_TITLE = "MODE VIDEO"; +IDMNU_RENDERMODE = "Mode de Rendu"; VIDMNU_FULLSCREEN = "Plein écran"; VIDMNU_HIDPI = "Support Retina/HiDPI "; VIDMNU_BRDLSS = "Mode fenêtré sans bordures"; @@ -2272,6 +2297,16 @@ VIDMNU_ENTERTEXT = "Appuyez sur ENTREE pour choisir un mode"; VIDMNU_TESTTEXT1 = "Appuyez sur T pour tester ce mode pendant 5 secondes."; VIDMNU_TESTTEXT2 = "Veuillez attendre 5 secondes..."; +VIDMNU_USELINEAR = "Mise à l'échelle Linéaire (Plein écran)"; +VIDMNU_CUSTOMRES = "Résolution Personalisée"; +VIDMNU_CUSTOMX = "Largeur Personalisée"; +VIDMNU_CUSTOMY = "Hauteur Personalisée"; +VIDMNU_APPLYW = "Appliquer Changements (Fenêtre)"; +VIDMNU_APPLYFS = "Appliquer Changements (Plein écran)"; +VIDMNU_RESPRESET = "Choisir paramètre personalisé"; +VIDMNU_RESPRESETTTL = "Résolutions Personalisée"; +VIDMNU_RESPRESETHEAD = "Choisir mode de Résolution"; + // Network Options NETMNU_TITLE = "OPTIONS RESEAU"; NETMNU_LOCALOPTIONS = "Options Locales"; @@ -2405,6 +2440,33 @@ OPTVAL_VTAZDOOM = "Auto (ZDoom Préféré)"; OPTVAL_VTAVANILLA = "Auto (Vanilla Préféré)"; OPTVAL_SCALENEAREST = "Mise à l'échelle (Proche Voisin)"; OPTVAL_SCALELINEAR = "Mise à l'échelle (Linéaire)"; +OPTVAL_LINEAR = "Linéaire"; +OPTVAL_CUBIC = "Cubique"; +OPTVAL_BLEP = "Step limité par bande"; +OPTVAL_LINEARSLOW = "Linéaire (Lent)"; +OPTVAL_BLAM = "Linéaire limité par bande"; +OPTVAL_CUBICSLOW = "Cubique (Lent)"; +OPTVAL_NOTEONOFFONLY = "Note on/off seulement"; +OPTVAL_FULLRAMPING = "Rampe complète"; +OPTVAL_ALLUNACKNOWLEDGED = "Tout non-acknowledged"; +OPTVAL_ERRORS = "Erreurs"; +OPTVAL_WARNINGS = "Avertissements"; +OPTVAL_EVERYTHING = "Tout"; +OPTVAL_FULLSCREENONLY = "Plein écran seulement"; +OPTVAL_HWPOLY = "Accéléré par OpenGL"; +OPTVAL_SWDOOM = "Rendu Software Doom"; +OPTVAL_SWDOOMTC = "Rendu Software Couleurs Réeles"; +OPTVAL_SWPOLY = "Rendu Softpoly"; +OPTVAL_SWPOLYTC = "Softpoly Couleurs Réeles"; +OPTVAL_DEDICATED = "Hautes Performances"; +OPTVAL_INTEGRATED = "Economie d'Energie"; +OPTVAL_VTFZDOOM = "ZDoom (Forcé)"; +OPTVAL_VTFVANILLA = "Vanilla (Forcé)"; +OPTVAL_VTAZDOOM = "Auto (ZDoom Préféré)"; +OPTVAL_VTAVANILLA = "Auto (Vanilla Préféré)"; +OPTVAL_SCALENEAREST = "Mis à l'échelle (Proche Voisin)"; +OPTVAL_SCALELINEAR = "Mis à l'échelle (Linéaire)"; + // Colors C_BRICK = "\cabrique"; diff --git a/wadsrc/static/menudef.txt b/wadsrc/static/menudef.txt index 218182c07..d8d3c9a4c 100644 --- a/wadsrc/static/menudef.txt +++ b/wadsrc/static/menudef.txt @@ -2290,6 +2290,8 @@ OptionMenu "OpenGLOptions" protected { Option "$GLPREFMNU_VRQUADSTEREO", vr_enable_quadbuffered, "OnOff" } + Slider "$GLPREFMNU_VRIPD", vr_ipd, 0.041, 0.073, 0.001, 3 + Slider "$GLPREFMNU_VRSCREENDIST", vr_screendist, 0.2, 10.0, 0.1, 1 StaticText " " Option "$GLPREFMNU_MULTISAMPLE", gl_multisample, "Multisample" Option "$GLPREFMNU_TONEMAP", gl_tonemap, "TonemapModes" diff --git a/wadsrc/static/zscript/base.txt b/wadsrc/static/zscript/base.txt index 498f6ae71..e648febd1 100644 --- a/wadsrc/static/zscript/base.txt +++ b/wadsrc/static/zscript/base.txt @@ -194,6 +194,7 @@ struct Screen native native static vararg void DrawChar(Font font, int normalcolor, double x, double y, int character, ...); native static vararg void DrawText(Font font, int normalcolor, double x, double y, String text, ...); native static void DrawLine(int x0, int y0, int x1, int y1, Color color); + native static void DrawThickLine(int x0, int y0, int x1, int y1, double thickness, Color color); native static void DrawFrame(int x, int y, int w, int h); native static Vector2, Vector2 VirtualToRealCoords(Vector2 pos, Vector2 size, Vector2 vsize, bool vbottom=false, bool handleaspect=true); native static double GetAspectRatio(); @@ -815,7 +816,8 @@ struct StringStruct native native int CharCodeAt(int pos) const; native String Filter(); native int IndexOf(String substr, int startIndex = 0) const; - native int LastIndexOf(String substr, int endIndex = 2147483647) const; + deprecated("3.5.1") native int LastIndexOf(String substr, int endIndex = 2147483647) const; + native int RightIndexOf(String substr, int endIndex = 2147483647) const; native void ToUpper(); native void ToLower(); native int ToInt(int base = 0) const;