diff --git a/src/common/audio/sound/i_sound.cpp b/src/common/audio/sound/i_sound.cpp index 2f40333c0..f859ae0a8 100644 --- a/src/common/audio/sound/i_sound.cpp +++ b/src/common/audio/sound/i_sound.cpp @@ -62,9 +62,6 @@ CUSTOM_CVAR(Int, snd_samplerate, 0, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) CVAR(Int, snd_buffersize, 0, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) CVAR(Int, snd_hrtf, -1, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) -CVAR(Float, snd_footstepvolume, 1.f, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) - - #if !defined(NO_OPENAL) #define DEF_BACKEND "openal" #else diff --git a/src/common/engine/serializer.h b/src/common/engine/serializer.h index 902d4226c..fb906aee8 100644 --- a/src/common/engine/serializer.h +++ b/src/common/engine/serializer.h @@ -253,7 +253,6 @@ FSerializer &Serialize(FSerializer &arc, const char *key, struct AnimModelOverri FSerializer &Serialize(FSerializer &arc, const char *key, ModelAnim &ao, ModelAnim *def); FSerializer &Serialize(FSerializer &arc, const char *key, ModelAnimFrame &ao, ModelAnimFrame *def); FSerializer &Serialize(FSerializer& arc, const char* key, FTranslationID& value, FTranslationID* defval); -FSerializer &Serialize(FSerializer& arc, const char* key, struct FStandaloneAnimation& value, struct FStandaloneAnimation* defval); void SerializeFunctionPointer(FSerializer &arc, const char *key, FunctionPointerValue *&p); diff --git a/src/common/filesystem/include/fs_filesystem.h b/src/common/filesystem/include/fs_filesystem.h index 858b74837..e817bc12d 100644 --- a/src/common/filesystem/include/fs_filesystem.h +++ b/src/common/filesystem/include/fs_filesystem.h @@ -42,8 +42,8 @@ public: void SetMaxIwadNum(int x) { MaxIwadIndex = x; } bool InitSingleFile(const char *filename, FileSystemMessageFunc Printf = nullptr); - bool InitMultipleFiles (std::vector& filenames, LumpFilterInfo* filter = nullptr, FileSystemMessageFunc Printf = nullptr, bool allowduplicates = false, FILE* hashfile = nullptr); - void AddFile (const char *filename, FileReader *wadinfo, LumpFilterInfo* filter, FileSystemMessageFunc Printf, FILE* hashfile); + bool InitMultipleFiles (std::vector& filenames, LumpFilterInfo* filter = nullptr, FileSystemMessageFunc Printf = nullptr, bool allowduplicates = false); + void AddFile (const char *filename, FileReader *wadinfo, LumpFilterInfo* filter, FileSystemMessageFunc Printf); int CheckIfResourceFileLoaded (const char *name) noexcept; void AddAdditionalFile(const char* filename, FileReader* wadinfo = NULL) {} diff --git a/src/common/filesystem/source/filesystem.cpp b/src/common/filesystem/source/filesystem.cpp index fee5f1011..0bc0dd5fe 100644 --- a/src/common/filesystem/source/filesystem.cpp +++ b/src/common/filesystem/source/filesystem.cpp @@ -238,7 +238,7 @@ bool FileSystem::InitSingleFile(const char* filename, FileSystemMessageFunc Prin return InitMultipleFiles(filenames, nullptr, Printf); } -bool FileSystem::InitMultipleFiles (std::vector& filenames, LumpFilterInfo* filter, FileSystemMessageFunc Printf, bool allowduplicates, FILE* hashfile) +bool FileSystem::InitMultipleFiles (std::vector& filenames, LumpFilterInfo* filter, FileSystemMessageFunc Printf, bool allowduplicates) { int numfiles; @@ -269,7 +269,7 @@ bool FileSystem::InitMultipleFiles (std::vector& filenames, LumpFil for(size_t i=0;igetName(i); auto embedded = resfile->GetEntryReader(i, READER_CACHED); - AddFile(path.c_str(), &embedded, filter, Printf, hashfile); + AddFile(path.c_str(), &embedded, filter, Printf); } } - if (hashfile) - { - uint8_t cksum[16]; - char cksumout[33]; - memset(cksumout, 0, sizeof(cksumout)); - - if (filereader.isOpen()) - { - filereader.Seek(0, FileReader::SeekSet); - md5Hash(filereader, cksum); - - for (size_t j = 0; j < sizeof(cksum); ++j) - { - snprintf(cksumout + (j * 2), 3, "%02X", cksum[j]); - } - - fprintf(hashfile, "file: %s, hash: %s, size: %td\n", filename, cksumout, filereader.GetLength()); - } - - else - fprintf(hashfile, "file: %s, Directory structure\n", filename); - - for (int i = 0; i < resfile->EntryCount(); i++) - { - int flags = resfile->GetEntryFlags(i); - if (!(flags & RESFF_EMBEDDED)) - { - auto reader = resfile->GetEntryReader(i, READER_SHARED, 0); - md5Hash(filereader, cksum); - - for (size_t j = 0; j < sizeof(cksum); ++j) - { - snprintf(cksumout + (j * 2), 3, "%02X", cksum[j]); - } - - fprintf(hashfile, "file: %s, lump: %s, hash: %s, size: %zu\n", filename, resfile->getName(i), cksumout, (uint64_t)resfile->Length(i)); - } - } - } return; } } diff --git a/src/common/objects/dobjtype.cpp b/src/common/objects/dobjtype.cpp index a3172c5e5..0d1bfdf9a 100644 --- a/src/common/objects/dobjtype.cpp +++ b/src/common/objects/dobjtype.cpp @@ -46,8 +46,6 @@ #include "symbols.h" #include "types.h" -#include "p_visualthinker.h" - // MACROS ------------------------------------------------------------------ // TYPES ------------------------------------------------------------------- @@ -423,7 +421,7 @@ PClass *PClass::FindClass (FName zaname) // //========================================================================== -DObject *PClass::CreateNew(int *statnum) +DObject *PClass::CreateNew() { uint8_t *mem = (uint8_t *)M_Malloc (Size); assert (mem != nullptr); @@ -446,12 +444,6 @@ DObject *PClass::CreateNew(int *statnum) ((DObject *)mem)->SetClass (const_cast(this)); InitializeSpecials(mem, Defaults, &PClass::SpecialInits); - - if(statnum && ((DObject *)mem)->IsKindOf(RUNTIME_CLASS(DVisualThinker))) - { - *statnum = STAT_VISUALTHINKER; - } - return (DObject *)mem; } diff --git a/src/common/objects/dobjtype.h b/src/common/objects/dobjtype.h index 17ad29862..62e6f5ccc 100644 --- a/src/common/objects/dobjtype.h +++ b/src/common/objects/dobjtype.h @@ -90,7 +90,7 @@ public: PClass(); ~PClass(); void InsertIntoHash(bool native); - DObject *CreateNew(int *statnum = nullptr); + DObject *CreateNew(); PClass *CreateDerivedClass(FName name, unsigned int size, bool *newlycreated = nullptr, int fileno = 0); void InitializeActorInfo(); diff --git a/src/common/scripting/backend/codegen.cpp b/src/common/scripting/backend/codegen.cpp index 86bd606f9..03a41f3a8 100644 --- a/src/common/scripting/backend/codegen.cpp +++ b/src/common/scripting/backend/codegen.cpp @@ -8993,7 +8993,7 @@ FxExpression *FxMemberFunctionCall::Resolve(FCompileContext& ctx) } else { - if (PFunction **Override; ctx.Version >= MakeVersion(4, 11, 0) && (Override = static_cast(Self->ValueType)->FnOverrides.CheckKey(MethodName))) + if (PFunction **Override; (Override = static_cast(Self->ValueType)->FnOverrides.CheckKey(MethodName))) { afd_override = *Override; } diff --git a/src/common/scripting/backend/vmbuilder.cpp b/src/common/scripting/backend/vmbuilder.cpp index 5437ca848..c9bfc63af 100644 --- a/src/common/scripting/backend/vmbuilder.cpp +++ b/src/common/scripting/backend/vmbuilder.cpp @@ -40,6 +40,7 @@ #include "filesystem.h" CVAR(Bool, strictdecorate, false, CVAR_GLOBALCONFIG | CVAR_ARCHIVE) +CVAR(Bool, warningstoerrors, false, CVAR_GLOBALCONFIG | CVAR_ARCHIVE) EXTERN_CVAR(Bool, vm_jit) EXTERN_CVAR(Bool, vm_jit_aot) diff --git a/src/common/scripting/interface/vmnatives.cpp b/src/common/scripting/interface/vmnatives.cpp index c778364ed..2b57ceb25 100644 --- a/src/common/scripting/interface/vmnatives.cpp +++ b/src/common/scripting/interface/vmnatives.cpp @@ -41,7 +41,6 @@ #include "c_cvars.h" #include "c_bind.h" #include "c_dispatch.h" -#include "m_misc.h" #include "menu.h" #include "vm.h" @@ -1033,17 +1032,6 @@ DEFINE_ACTION_FUNCTION(_CVar, FindCVar) ACTION_RETURN_POINTER(FindCVar(name.GetChars(), nullptr)); } -static int SaveConfig() -{ - return M_SaveDefaults(nullptr); -} - -DEFINE_ACTION_FUNCTION_NATIVE(_CVar, SaveConfig, SaveConfig) -{ - PARAM_PROLOGUE; - ACTION_RETURN_INT(M_SaveDefaults(nullptr)); -} - //============================================================================= // // diff --git a/src/common/textures/m_png.cpp b/src/common/textures/m_png.cpp index 331b35e20..83714a6ac 100644 --- a/src/common/textures/m_png.cpp +++ b/src/common/textures/m_png.cpp @@ -54,7 +54,7 @@ // MACROS ------------------------------------------------------------------ // The maximum size of an IDAT chunk ZDoom will write. This is also the -// size of the compression buffer it allocates on the stack. +// size of the compression buffer it allocates on the heap. #define PNG_WRITE_SIZE 32768 // Set this to 1 to use a simple heuristic to select the filter to apply @@ -926,8 +926,7 @@ bool M_SaveBitmap(const uint8_t *from, ESSType color_type, int width, int height temprow[i] = &temprow_storage[temprow_size * i]; } - TArray array(PNG_WRITE_SIZE, true); - auto buffer = array.data(); + TArray buffer(PNG_WRITE_SIZE, true); z_stream stream; int err; int y; @@ -944,8 +943,8 @@ bool M_SaveBitmap(const uint8_t *from, ESSType color_type, int width, int height } y = height; - stream.next_out = buffer; - stream.avail_out = sizeof(buffer); + stream.next_out = buffer.data(); + stream.avail_out = buffer.size(); temprow[0][0] = 0; #if USE_FILTER_HEURISTIC @@ -1007,12 +1006,12 @@ bool M_SaveBitmap(const uint8_t *from, ESSType color_type, int width, int height } while (stream.avail_out == 0) { - if (!WriteIDAT (file, buffer, sizeof(buffer))) + if (!WriteIDAT (file, buffer.data(), buffer.size())) { return false; } - stream.next_out = buffer; - stream.avail_out = sizeof(buffer); + stream.next_out = buffer.data(); + stream.avail_out = buffer.size(); if (stream.avail_in != 0) { err = deflate (&stream, (y == 0) ? Z_FINISH : 0); @@ -1033,12 +1032,12 @@ bool M_SaveBitmap(const uint8_t *from, ESSType color_type, int width, int height } if (stream.avail_out == 0) { - if (!WriteIDAT (file, buffer, sizeof(buffer))) + if (!WriteIDAT (file, buffer.data(), buffer.size())) { return false; } - stream.next_out = buffer; - stream.avail_out = sizeof(buffer); + stream.next_out = buffer.data(); + stream.avail_out = buffer.size(); } } @@ -1048,7 +1047,7 @@ bool M_SaveBitmap(const uint8_t *from, ESSType color_type, int width, int height { return false; } - return WriteIDAT (file, buffer, sizeof(buffer)-stream.avail_out); + return WriteIDAT (file, buffer.data(), buffer.size() - stream.avail_out); } //========================================================================== diff --git a/src/d_iwad.cpp b/src/d_iwad.cpp index 1eb0c0ea0..6ce60d30b 100644 --- a/src/d_iwad.cpp +++ b/src/d_iwad.cpp @@ -824,7 +824,7 @@ int FIWadManager::IdentifyVersion (std::vector&wadfiles, const char // [SP] Load non-free assets if available. This must be done before the IWAD. int iwadnum = 1; - if (D_AddFile(wadfiles, optional_wad, true, -1, GameConfig)) + if (optional_wad && D_AddFile(wadfiles, optional_wad, true, -1, GameConfig)) { iwadnum++; } diff --git a/src/d_main.cpp b/src/d_main.cpp index d5eb38180..63ed53fba 100644 --- a/src/d_main.cpp +++ b/src/d_main.cpp @@ -3071,39 +3071,6 @@ static void System_HudScaleChanged() bool CheckSkipGameOptionBlock(const char* str); -//========================================================================== -// -// -// -//========================================================================== - -static FILE* D_GetHashFile() -{ - FILE *hashfile = nullptr; - - if (Args->CheckParm("-hashfiles")) - { - const char *filename = "fileinfo.txt"; - Printf("Hashing loaded content to: %s\n", filename); - hashfile = fopen(filename, "w"); - if (hashfile) - { - Printf("Notice: File hashing is incredibly verbose. Expect loading files to take much longer than usual.\n"); - fprintf(hashfile, "%s version %s (%s)\n", GAMENAME, GetVersionString(), GetGitHash()); -#ifdef __VERSION__ - fprintf(hashfile, "Compiler version: %s\n", __VERSION__); -#endif - fprintf(hashfile, "Command line:"); - for (int i = 0; i < Args->NumArgs(); ++i) - { - fprintf(hashfile, " %s", Args->GetArg(i)); - } - fprintf(hashfile, "\n"); - } - } - return hashfile; -} - // checks if a file within a directory is allowed to be added to the file system. static bool FileNameCheck(const char* base, const char* path) { @@ -3260,8 +3227,7 @@ static int D_InitGame(const FIWADInfo* iwad_info, std::vector& allw ); bool allowduplicates = Args->CheckParm("-allowduplicates"); - auto hashfile = D_GetHashFile(); - if (!fileSystem.InitMultipleFiles(allwads, &lfi, FileSystemPrintf, allowduplicates, hashfile)) + if (!fileSystem.InitMultipleFiles(allwads, &lfi, FileSystemPrintf, allowduplicates)) { I_FatalError("FileSystem: no files found"); } diff --git a/src/events.cpp b/src/events.cpp index 2f7b12091..e4d7d1d1a 100755 --- a/src/events.cpp +++ b/src/events.cpp @@ -719,6 +719,27 @@ bool EventManager::WorldHitscanPreFired(AActor* actor, DAngle angle, double dist return ret; } +bool EventManager::WorldRailgunPreFired(FName damageType, PClassActor* pufftype, FRailParams* param) +{ + auto actor = param->source; + + // don't call anything if actor was destroyed on PostBeginPlay/BeginPlay/whatever. + if (actor->ObjectFlags & OF_EuthanizeMe) + return false; + + bool ret = false; + + if (ShouldCallStatic(true)) ret = staticEventManager.WorldRailgunPreFired(damageType, pufftype, param); + + if (!ret) + { + for (DStaticEventHandler* handler = FirstEventHandler; handler && ret == false; handler = handler->next) + ret = handler->WorldRailgunPreFired(damageType, pufftype, param); + } + + return ret; +} + void EventManager::WorldHitscanFired(AActor* actor, const DVector3& AttackPos, const DVector3& DamagePosition, AActor* Inflictor, int flags) { // don't call anything if actor was destroyed on PostBeginPlay/BeginPlay/whatever. @@ -731,6 +752,18 @@ void EventManager::WorldHitscanFired(AActor* actor, const DVector3& AttackPos, c handler->WorldHitscanFired(actor, AttackPos, DamagePosition, Inflictor, flags); } +void EventManager::WorldRailgunFired(AActor* actor, const DVector3& AttackPos, const DVector3& DamagePosition, AActor* Inflictor, int flags) +{ + // don't call anything if actor was destroyed on PostBeginPlay/BeginPlay/whatever. + if (actor->ObjectFlags & OF_EuthanizeMe) + return; + + if (ShouldCallStatic(true)) staticEventManager.WorldRailgunFired(actor, AttackPos, DamagePosition, Inflictor, flags); + + for (DStaticEventHandler* handler = FirstEventHandler; handler; handler = handler->next) + handler->WorldRailgunFired(actor, AttackPos, DamagePosition, Inflictor, flags); +} + void EventManager::WorldThingGround(AActor* actor, FState* st) { // don't call anything if actor was destroyed on PostBeginPlay/BeginPlay/whatever. @@ -1064,6 +1097,8 @@ DEFINE_FIELD_X(WorldEvent, FWorldEvent, AttackOffsetForward); DEFINE_FIELD_X(WorldEvent, FWorldEvent, AttackOffsetSide); DEFINE_FIELD_X(WorldEvent, FWorldEvent, AttackZ); DEFINE_FIELD_X(WorldEvent, FWorldEvent, AttackPuffType); +DEFINE_FIELD_X(WorldEvent, FWorldEvent, RailParams); +DEFINE_FIELD_X(WorldEvent, FWorldEvent, AttackLineFlags); DEFINE_FIELD_X(PlayerEvent, FPlayerEvent, PlayerNumber); DEFINE_FIELD_X(PlayerEvent, FPlayerEvent, IsReturn); @@ -1784,7 +1819,37 @@ bool DStaticEventHandler::WorldHitscanPreFired(AActor* actor, DAngle angle, doub e.AttackOffsetForward = offsetforward; e.AttackOffsetSide = offsetside; e.AttackZ = sz; - e.DamageFlags = flags; + e.AttackLineFlags = flags; + int processed; + VMReturn results[1] = { &processed }; + VMValue params[2] = { (DStaticEventHandler*)this, &e }; + VMCall(func, params, 2, results, 1); + return !!processed; + } + + return false; +} + +bool DStaticEventHandler::WorldRailgunPreFired(FName damageType, PClassActor* pufftype, FRailParams* param) +{ + IFVIRTUAL(DStaticEventHandler, WorldRailgunPreFired) + { + // don't create excessive DObjects if not going to be processed anyway + if (isEmpty(func)) return false; + FWorldEvent e = owner->SetupWorldEvent(); + e.Thing = param->source; + e.AttackPuffType = pufftype; + e.DamageType = damageType; + e.Damage = param->damage; + e.AttackOffsetForward = 0; + e.AttackOffsetSide = param->offset_xy; + e.AttackDistance = param->distance; + e.AttackZ = param->offset_z; + e.AttackAngle = e.Thing->Angles.Yaw + param->angleoffset; + e.AttackPitch = e.Thing->Angles.Pitch + param->pitchoffset; + e.RailParams = *param; + e.RailParams.puff = pufftype; + int processed; VMReturn results[1] = { &processed }; VMValue params[2] = { (DStaticEventHandler*)this, &e }; @@ -1806,7 +1871,24 @@ void DStaticEventHandler::WorldHitscanFired(AActor* actor, const DVector3& Attac e.AttackPos = AttackPos; e.DamagePosition = DamagePosition; e.Inflictor = Inflictor; - e.DamageFlags = flags; + e.AttackLineFlags = flags; + VMValue params[2] = { (DStaticEventHandler*)this, &e }; + VMCall(func, params, 2, nullptr, 0); + } +} + +void DStaticEventHandler::WorldRailgunFired(AActor* actor, const DVector3& AttackPos, const DVector3& DamagePosition, AActor* Inflictor, int flags) +{ + IFVIRTUAL(DStaticEventHandler, WorldRailgunFired) + { + // don't create excessive DObjects if not going to be processed anyway + if (isEmpty(func)) return; + FWorldEvent e = owner->SetupWorldEvent(); + e.Thing = actor; + e.AttackPos = AttackPos; + e.DamagePosition = DamagePosition; + e.Inflictor = Inflictor; + e.AttackLineFlags = flags; VMValue params[2] = { (DStaticEventHandler*)this, &e }; VMCall(func, params, 2, nullptr, 0); } diff --git a/src/events.h b/src/events.h index c3d05d485..4a73406f8 100644 --- a/src/events.h +++ b/src/events.h @@ -4,6 +4,7 @@ #include "dobject.h" #include "serializer.h" #include "d_event.h" +#include "p_local.h" #include "sbar.h" #include "info.h" #include "vm.h" @@ -312,7 +313,9 @@ public: void WorldThingDamaged(AActor* actor, AActor* inflictor, AActor* source, int damage, FName mod, int flags, DAngle angle); void WorldThingDestroyed(AActor* actor); bool WorldHitscanPreFired(AActor* actor, DAngle angle, double distance, DAngle pitch, int damage, FName damageType, PClassActor *pufftype, int flags, double sz, double offsetforward, double offsetside); + bool WorldRailgunPreFired(FName damageType, PClassActor* pufftype, FRailParams* param); void WorldHitscanFired(AActor* actor, const DVector3& AttackPos, const DVector3& DamagePosition, AActor* Inflictor, int flags); + void WorldRailgunFired(AActor* actor, const DVector3& AttackPos, const DVector3& DamagePosition, AActor* Inflictor, int flags); void WorldLinePreActivated(line_t* line, AActor* actor, int activationType, bool* shouldactivate); void WorldLineActivated(line_t* line, AActor* actor, int activationType); int WorldSectorDamaged(sector_t* sector, AActor* source, int damage, FName damagetype, int part, DVector3 position, bool isradius); @@ -403,6 +406,8 @@ struct FWorldEvent double AttackOffsetSide = 0; double AttackZ = 0; PClassActor* AttackPuffType = nullptr; + FRailParams RailParams; + int AttackLineFlags = 0; }; struct FPlayerEvent @@ -481,6 +486,10 @@ struct EventManager bool WorldHitscanPreFired(AActor* actor, DAngle angle, double distance, DAngle pitch, int damage, FName damageType, PClassActor *pufftype, int flags, double sz, double offsetforward, double offsetside); // called when a hitscan attack has been fired void WorldHitscanFired(AActor* actor, const DVector3& AttackPos, const DVector3& DamagePosition, AActor* Inflictor, int flags); + // called when a railgun attack has been fired + void WorldRailgunFired(AActor* actor, const DVector3& AttackPos, const DVector3& DamagePosition, AActor* Inflictor, int flags); + // called when a railgun attack has been fired (can be overridden to block it) + bool WorldRailgunPreFired(FName damageType, PClassActor* pufftype, FRailParams* param); // called inside AActor::Grind just before the corpse is destroyed void WorldThingGround(AActor* actor, FState* st); // called after AActor::Revive. diff --git a/src/g_game.cpp b/src/g_game.cpp index 8a189bf17..70110aca1 100644 --- a/src/g_game.cpp +++ b/src/g_game.cpp @@ -1438,6 +1438,7 @@ void FLevelLocals::PlayerReborn (int player) p->cheats |= chasecam; p->Bot = Bot; //Added by MC: p->settings_controller = settings_controller; + p->LastSafePos = p->mo->Pos(); p->oldbuttons = ~0, p->attackdown = true; p->usedown = true; // don't do anything immediately p->original_oldbuttons = ~0; diff --git a/src/g_levellocals.h b/src/g_levellocals.h index a1d6c94fa..9ae2997eb 100644 --- a/src/g_levellocals.h +++ b/src/g_levellocals.h @@ -56,6 +56,7 @@ #include "r_data/r_interpolate.h" #include "doom_aabbtree.h" #include "doom_levelmesh.h" +#include "p_visualthinker.h" //============================================================================ // @@ -425,7 +426,12 @@ public: DThinker *CreateThinker(PClass *cls, int statnum = STAT_DEFAULT) { - DThinker *thinker = static_cast(cls->CreateNew(&statnum)); + DThinker *thinker = static_cast(cls->CreateNew()); + if (statnum && thinker->IsKindOf(RUNTIME_CLASS(DVisualThinker))) + { + statnum = STAT_VISUALTHINKER; + } + assert(thinker->IsKindOf(RUNTIME_CLASS(DThinker))); thinker->ObjectFlags |= OF_JustSpawned; Thinkers.Link(thinker, statnum); diff --git a/src/g_statusbar/sbarinfo.cpp b/src/g_statusbar/sbarinfo.cpp index 6bb27d179..82aba4263 100644 --- a/src/g_statusbar/sbarinfo.cpp +++ b/src/g_statusbar/sbarinfo.cpp @@ -381,6 +381,7 @@ enum //Key words SBARINFO_MUGSHOT, SBARINFO_CREATEPOPUP, SBARINFO_PROTRUSION, + SBARINFO_APPENDSTATUSBAR, }; enum //Bar types @@ -410,6 +411,7 @@ static const char *SBarInfoTopLevel[] = "mugshot", "createpopup", "protrusion", + "appendstatusbar", NULL }; @@ -488,7 +490,9 @@ void SBarInfo::ParseSBarInfo(int lump) continue; } int baselump = -2; - switch(sc.MustMatchString(SBarInfoTopLevel)) + // Store the command, used for the switch statement and case SBARINFO_APPENDSTATUSBAR. + const int command = sc.MustMatchString(SBarInfoTopLevel); + switch(command) { case SBARINFO_BASE: baseSet = true; @@ -629,6 +633,7 @@ void SBarInfo::ParseSBarInfo(int lump) sc.MustGetToken(';'); break; case SBARINFO_STATUSBAR: + case SBARINFO_APPENDSTATUSBAR: { if(!baseSet) //If the user didn't explicitly define a base, do so now. gameType = GAME_Any; @@ -638,11 +643,19 @@ void SBarInfo::ParseSBarInfo(int lump) sc.MustGetToken(TK_Identifier); barNum = sc.MustMatchString(StatusBars); } - if (this->huds[barNum] != NULL) + // SBARINFO_APPENDSTATUSBAR shouldn't delete the old HUD if it exists. + if(command != SBARINFO_APPENDSTATUSBAR) { - delete this->huds[barNum]; + if (this->huds[barNum] != NULL) + { + delete this->huds[barNum]; + } + this->huds[barNum] = new SBarInfoMainBlock(this); + } + else if(this->huds[barNum] == NULL) + { + sc.ScriptError("Status bar '%s' has not been created and cannot be appended to. Use 'StatusBar' instead.", StatusBars[barNum]); } - this->huds[barNum] = new SBarInfoMainBlock(this); if(barNum == STBAR_AUTOMAP) { automapbar = true; diff --git a/src/gamedata/r_defs.h b/src/gamedata/r_defs.h index b235292a9..b94ec5cc3 100644 --- a/src/gamedata/r_defs.h +++ b/src/gamedata/r_defs.h @@ -808,6 +808,8 @@ public: void RemoveForceField(); int Index() const { return sectornum; } + bool IsDangerous(const DVector3& pos, double height) const; + void AdjustFloorClip () const; void SetColor(PalEntry pe, int desat); void SetFade(PalEntry pe); diff --git a/src/m_misc.cpp b/src/m_misc.cpp index 1b451486b..f8bafabad 100644 --- a/src/m_misc.cpp +++ b/src/m_misc.cpp @@ -66,6 +66,7 @@ #include "gameconfigfile.h" #include "gstrings.h" +#include "vm.h" FGameConfigFile *GameConfig; @@ -691,3 +692,14 @@ CCMD(openscreenshots) I_OpenShellFolder(autoname.GetChars()); } +static int SaveConfig() +{ + return M_SaveDefaults(nullptr); +} + +DEFINE_ACTION_FUNCTION_NATIVE(_CVar, SaveConfig, SaveConfig) +{ + PARAM_PROLOGUE; + ACTION_RETURN_INT(M_SaveDefaults(nullptr)); +} + diff --git a/src/playsim/a_decals.cpp b/src/playsim/a_decals.cpp index 758b16a5d..40468ade0 100644 --- a/src/playsim/a_decals.cpp +++ b/src/playsim/a_decals.cpp @@ -849,7 +849,7 @@ void SprayDecal(AActor *shooter, const char *name, double distance, DVector3 off { if (trace.HitType == TRACE_HitWall) { - DImpactDecal::StaticCreate(shooter->Level, name, trace.HitPos, trace.Line->sidedef[trace.Side], NULL, entry, bloodTrans); + DImpactDecal::StaticCreate(shooter->Level, name, trace.HitPos, trace.Line->sidedef[trace.Side], trace.ffloor, entry, bloodTrans); } } } diff --git a/src/playsim/actor.h b/src/playsim/actor.h index 4c5ca980b..c4669afdd 100644 --- a/src/playsim/actor.h +++ b/src/playsim/actor.h @@ -756,12 +756,6 @@ public: int Flags; // Functions - DViewPosition() - { - Offset = { 0,0,0 }; - Flags = 0; - } - void Set(DVector3 &off, int f = -1) { ZeroSubnormalsF(off.X); @@ -777,6 +771,8 @@ public: { return Offset.isZero(); } + + void Serialize(FSerializer& arc) override; }; const double MinVel = EQUAL_EPSILON; @@ -873,7 +869,7 @@ public: void PlayPushSound(); // Called when an actor with MF_MISSILE and MF2_FLOORBOUNCE hits the floor - bool FloorBounceMissile (secplane_t &plane); + bool FloorBounceMissile (secplane_t &plane, bool is3DFloor); // Called by RoughBlockCheck bool IsOkayToAttack (AActor *target); diff --git a/src/playsim/d_player.h b/src/playsim/d_player.h index a717bb42c..862890f07 100644 --- a/src/playsim/d_player.h +++ b/src/playsim/d_player.h @@ -306,7 +306,7 @@ void WriteUserInfo(FSerializer &arc, userinfo_t &info); class player_t { public: - player_t() = default; + player_t() { angleOffsetTargets.Zero(); } ~player_t(); player_t &operator= (const player_t &p) = delete; void CopyFrom(player_t &src, bool copyPSP); @@ -322,8 +322,8 @@ public: AActor *mo = nullptr; uint8_t playerstate = 0; ticcmd_t cmd = {}; - usercmd_t original_cmd; - uint32_t original_oldbuttons; + usercmd_t original_cmd = {}; + uint32_t original_oldbuttons = 0; userinfo_t userinfo; // [RH] who is this? @@ -340,7 +340,7 @@ public: // mo->velx and mo->vely represent true velocity experienced by player. // This only represents the thrust that the player applies himself. // This avoids anomalies with such things as Boom ice and conveyors. - DVector2 Vel = { 0,0 }; + DVector2 Vel = { 0.0, 0.0 }; bool centering = false; uint8_t turnticks = 0; @@ -419,7 +419,7 @@ public: FString SoundClass; FString LogText; // [RH] Log for Strife FString SubtitleText; - int SubtitleCounter; + int SubtitleCounter = 0; DAngle MinPitch = nullAngle; // Viewpitch limits (negative is up, positive is down) DAngle MaxPitch = nullAngle; @@ -435,6 +435,8 @@ public: DAngle ConversationNPCAngle = nullAngle; bool ConversationFaceTalker = false; + DVector3 LastSafePos = {}; // Mark the last known safe location the player was standing. + double GetDeltaViewHeight() const { return (mo->FloatVar(NAME_ViewHeight) + crouchviewdelta - viewheight) / 8; diff --git a/src/playsim/p_actionfunctions.cpp b/src/playsim/p_actionfunctions.cpp index ea7090c1d..8d50993d9 100644 --- a/src/playsim/p_actionfunctions.cpp +++ b/src/playsim/p_actionfunctions.cpp @@ -5130,6 +5130,7 @@ enum ESetAnimationFlags void SetAnimationInternal(AActor * self, FName animName, double framerate, int startFrame, int loopFrame, int endFrame, int interpolateTics, int flags, double ticFrac) { + if(!self) ThrowAbortException(X_READ_NIL, "In function parameter self"); if(!(self->flags9 & MF9_DECOUPLEDANIMATIONS)) @@ -5176,43 +5177,46 @@ void SetAnimationInternal(AActor * self, FName animName, double framerate, int s if(!(flags & SAF_INSTANT)) { - if(self->modelData->curAnim.startTic > tic) - { - ModelAnimFrameInterp to; - float inter; - - calcFrames(self->modelData->curAnim, tic, to, inter); - - const TArray* animationData = nullptr; - - int animationid = -1; - - const FSpriteModelFrame * smf = &BaseSpriteModelFrames[self->GetClass()]; - - if (self->modelData->animationIDs.Size() > 0 && self->modelData->animationIDs[0] >= 0) + if((self->modelData->curAnim.startTic - self->modelData->curAnim.switchOffset) != int(floor(tic))) + { // don't change interpolation data if animation switch happened in the same tic + if(self->modelData->curAnim.startTic > tic) { - animationid = self->modelData->animationIDs[0]; + ModelAnimFrameInterp to; + float inter; + + calcFrames(self->modelData->curAnim, tic, to, inter); + + const TArray* animationData = nullptr; + + int animationid = -1; + + const FSpriteModelFrame * smf = &BaseSpriteModelFrames[self->GetClass()]; + + if (self->modelData->animationIDs.Size() > 0 && self->modelData->animationIDs[0] >= 0) + { + animationid = self->modelData->animationIDs[0]; + } + else if(smf->modelsAmount > 0) + { + animationid = smf->animationIDs[0]; + } + + FModel* animation = mdl; + + if (animationid >= 0) + { + animation = Models[animationid]; + animationData = animation->AttachAnimationData(); + } + + self->modelData->prevAnim = animation->PrecalculateFrame(self->modelData->prevAnim, to, inter, animationData, self->boneComponentData, 0); } - else if(smf->modelsAmount > 0) + else { - animationid = smf->animationIDs[0]; + self->modelData->prevAnim = ModelAnimFrameInterp{}; + + calcFrame(self->modelData->curAnim, tic, std::get(self->modelData->prevAnim)); } - - FModel* animation = mdl; - - if (animationid >= 0) - { - animation = Models[animationid]; - animationData = animation->AttachAnimationData(); - } - - self->modelData->prevAnim = animation->PrecalculateFrame(self->modelData->prevAnim, to, inter, animationData, self->boneComponentData, 0); - } - else - { - self->modelData->prevAnim = ModelAnimFrameInterp{}; - - calcFrame(self->modelData->curAnim, tic, std::get(self->modelData->prevAnim)); } } else @@ -5524,7 +5528,7 @@ DEFINE_ACTION_FUNCTION_NATIVE(AActor, A_ChangeModel, ChangeModelNative) DEFINE_ACTION_FUNCTION_NATIVE(AActor, SetAnimation, SetAnimationNative) { - PARAM_ACTION_PROLOGUE(AActor); + PARAM_SELF_PROLOGUE(AActor); PARAM_NAME(animName); PARAM_FLOAT(framerate); PARAM_INT(startFrame); @@ -5540,7 +5544,7 @@ DEFINE_ACTION_FUNCTION_NATIVE(AActor, SetAnimation, SetAnimationNative) DEFINE_ACTION_FUNCTION_NATIVE(AActor, SetAnimationUI, SetAnimationUINative) { - PARAM_ACTION_PROLOGUE(AActor); + PARAM_SELF_PROLOGUE(AActor); PARAM_NAME(animName); PARAM_FLOAT(framerate); PARAM_INT(startFrame); @@ -5556,7 +5560,7 @@ DEFINE_ACTION_FUNCTION_NATIVE(AActor, SetAnimationUI, SetAnimationUINative) DEFINE_ACTION_FUNCTION_NATIVE(AActor, SetAnimationFrameRate, SetAnimationFrameRateNative) { - PARAM_ACTION_PROLOGUE(AActor); + PARAM_SELF_PROLOGUE(AActor); PARAM_FLOAT(framerate); SetAnimationFrameRateInternal(self, framerate, 1); @@ -5566,7 +5570,7 @@ DEFINE_ACTION_FUNCTION_NATIVE(AActor, SetAnimationFrameRate, SetAnimationFrameRa DEFINE_ACTION_FUNCTION_NATIVE(AActor, SetAnimationFrameRateUI, SetAnimationFrameRateUINative) { - PARAM_ACTION_PROLOGUE(AActor); + PARAM_SELF_PROLOGUE(AActor); PARAM_FLOAT(framerate); SetAnimationFrameRateInternal(self, framerate, I_GetTimeFrac()); @@ -5576,7 +5580,7 @@ DEFINE_ACTION_FUNCTION_NATIVE(AActor, SetAnimationFrameRateUI, SetAnimationFrame DEFINE_ACTION_FUNCTION_NATIVE(AActor, SetModelFlag, SetModelFlag) { - PARAM_ACTION_PROLOGUE(AActor); + PARAM_SELF_PROLOGUE(AActor); PARAM_INT(flag); SetModelFlag(self, flag); @@ -5586,7 +5590,7 @@ DEFINE_ACTION_FUNCTION_NATIVE(AActor, SetModelFlag, SetModelFlag) DEFINE_ACTION_FUNCTION_NATIVE(AActor, ClearModelFlag, ClearModelFlag) { - PARAM_ACTION_PROLOGUE(AActor); + PARAM_SELF_PROLOGUE(AActor); PARAM_INT(flag); ClearModelFlag(self, flag); @@ -5596,7 +5600,7 @@ DEFINE_ACTION_FUNCTION_NATIVE(AActor, ClearModelFlag, ClearModelFlag) DEFINE_ACTION_FUNCTION_NATIVE(AActor, ResetModelFlags, ResetModelFlags) { - PARAM_ACTION_PROLOGUE(AActor); + PARAM_SELF_PROLOGUE(AActor); ResetModelFlags(self); diff --git a/src/playsim/p_interaction.cpp b/src/playsim/p_interaction.cpp index 0ae77e914..60b6f3059 100644 --- a/src/playsim/p_interaction.cpp +++ b/src/playsim/p_interaction.cpp @@ -1158,7 +1158,7 @@ static int DamageMobj (AActor *target, AActor *inflictor, AActor *source, int da // Special damage types if (inflictor) { - if (inflictor->flags4 & MF4_SPECTRAL) + if (inflictor->flags4 & MF4_SPECTRAL && !(inflictor->flags9 & MF9_ISPUFF)) { if (player != NULL) { diff --git a/src/playsim/p_map.cpp b/src/playsim/p_map.cpp index 2aea97407..0cde7aac9 100644 --- a/src/playsim/p_map.cpp +++ b/src/playsim/p_map.cpp @@ -1343,46 +1343,44 @@ static bool CanAttackHurt(AActor *victim, AActor *shooter) // //========================================================================== -void P_DoMissileDamage(AActor* inflictor, AActor* target) +void P_DoMissileDamage(AActor* self, AActor* victim) { + const bool ripper = (self->flags2 & MF2_RIP); + // Do poisoning (if using new style poison) - if (inflictor->PoisonDamage > 0 && inflictor->PoisonDuration != INT_MIN) - { - P_PoisonMobj(target, inflictor, inflictor->target, inflictor->PoisonDamage, inflictor->PoisonDuration, inflictor->PoisonPeriod, inflictor->PoisonDamageType); - } + if (self->PoisonDamage > 0 && self->PoisonDuration != INT_MIN) + P_PoisonMobj(victim, self, self->target, self->PoisonDamage, self->PoisonDuration, self->PoisonPeriod, self->PoisonDamageType); // Do damage - int damage = inflictor->GetMissileDamage((inflictor->flags4 & MF4_STRIFEDAMAGE) ? 3 : 7, 1); - if ((damage > 0) || (inflictor->flags6 & MF6_FORCEPAIN) || (inflictor->flags7 & MF7_CAUSEPAIN)) + int damage = ripper ? self->GetMissileDamage(3, 2) : self->GetMissileDamage((self->flags4 & MF4_STRIFEDAMAGE) ? 3 : 7, 1); + if (damage > 0 || (self->flags6 & MF6_FORCEPAIN) || (self->flags7 & MF7_CAUSEPAIN)) { - int newdam = P_DamageMobj(target, inflictor, inflictor->target, damage, inflictor->DamageType); - if (damage > 0) + if (ripper) + S_Sound(self, CHAN_BODY, 0, self->SoundVar(NAME_RipSound), 1.0f, ATTN_IDLE); + + int dealt = P_DamageMobj(victim, self, self->target, damage, self->DamageType); + if (damage > 0 && !(self->flags3 & MF3_BLOODLESSIMPACT) + && !(victim->flags & MF_NOBLOOD) + && !(victim->flags2 & (MF2_INVULNERABLE | MF2_DORMANT | MF2_REFLECTIVE))) { - if ((inflictor->flags5 & MF5_BLOODSPLATTER) && - !(target->flags & MF_NOBLOOD) && - !(target->flags2 & MF2_REFLECTIVE) && - !(target->flags2 & (MF2_INVULNERABLE | MF2_DORMANT)) && - !(inflictor->flags3 & MF3_BLOODLESSIMPACT) && - (pr_checkthing() < 192)) - { - P_BloodSplatter(inflictor->Pos(), target, inflictor->AngleTo(target)); - } - if (!(inflictor->flags3 & MF3_BLOODLESSIMPACT)) - { - P_TraceBleed(newdam > 0 ? newdam : damage, target, inflictor); - } + if (ripper) + P_RipperBlood(self, victim); + else if ((self->flags5 & MF5_BLOODSPLATTER) && pr_checkthing() < 192) + P_BloodSplatter(self->Pos(), victim, self->AngleTo(victim)); + + P_TraceBleed(dealt > 0 ? dealt : damage, victim, self); } } else { - P_GiveBody(target, -damage); + P_GiveBody(victim, -damage); } } -DEFINE_ACTION_FUNCTION(AActor, DoMissileDamage) +DEFINE_ACTION_FUNCTION_NATIVE(AActor, DoMissileDamage, P_DoMissileDamage) { PARAM_SELF_PROLOGUE(AActor); - PARAM_OBJECT_NOT_NULL(target, AActor); - P_DoMissileDamage(self, target); + PARAM_OBJECT_NOT_NULL(victim, AActor); + P_DoMissileDamage(self, victim); return 0; } //========================================================================== @@ -1687,27 +1685,7 @@ bool PIT_CheckThing(FMultiBlockThingsIterator &it, FMultiBlockThingsIterator::Ch if (check == NULL || !*check) { tm.LastRipped[thing] = true; - if (!(thing->flags & MF_NOBLOOD) && - !(thing->flags2 & MF2_REFLECTIVE) && - !(tm.thing->flags3 & MF3_BLOODLESSIMPACT) && - !(thing->flags2 & (MF2_INVULNERABLE | MF2_DORMANT))) - { // Ok to spawn blood - P_RipperBlood(tm.thing, thing); - } - S_Sound(tm.thing, CHAN_BODY, 0, tm.thing->SoundVar(NAME_RipSound), 1, ATTN_IDLE); - - // Do poisoning (if using new style poison) - if (tm.thing->PoisonDamage > 0 && tm.thing->PoisonDuration != INT_MIN) - { - P_PoisonMobj(thing, tm.thing, tm.thing->target, tm.thing->PoisonDamage, tm.thing->PoisonDuration, tm.thing->PoisonPeriod, tm.thing->PoisonDamageType); - } - - damage = tm.thing->GetMissileDamage(3, 2); - int newdam = P_DamageMobj(thing, tm.thing, tm.thing->target, damage, tm.thing->DamageType); - if (!(tm.thing->flags3 & MF3_BLOODLESSIMPACT)) - { - P_TraceBleed(newdam > 0 ? newdam : damage, thing, tm.thing); - } + P_DoMissileDamage(tm.thing, thing); if (thing->flags2 & MF2_PUSHABLE && !(tm.thing->flags2 & MF2_CANNOTPUSH)) { // Push thing @@ -2486,7 +2464,9 @@ bool P_TryMove(AActor *thing, const DVector2 &pos, // If it's a bouncer, let it bounce off its new floor, too. if (thing->BounceFlags & BOUNCE_Floors) { - thing->FloorBounceMissile(tm.floorsector->floorplane); + F3DFloor* ff = nullptr; + NextLowestFloorAt(tm.sector, tm.pos.X, tm.pos.Y, tm.pos.Z, 0, thing->MaxStepHeight, nullptr, &ff); + thing->FloorBounceMissile(ff != nullptr ? *ff->top.plane : tm.floorsector->floorplane, ff != nullptr); } else { @@ -3572,7 +3552,9 @@ bool FSlide::BounceWall(AActor *mo) double movelen; line_t *line; - if (!(mo->BounceFlags & BOUNCE_Walls)) + // The plane bounce flags need to be checked here in case it hit a ramp while + // moving along the xy axes. + if (!(mo->BounceFlags & (BOUNCE_Walls | BOUNCE_Ceilings | BOUNCE_Floors))) { return false; } @@ -3603,17 +3585,22 @@ bool FSlide::BounceWall(AActor *mo) { // Could not find a wall, so bounce off the floor/ceiling instead. double floordist = mo->Z() - mo->floorz; double ceildist = mo->ceilingz - mo->Z(); + F3DFloor* ff = nullptr; if (floordist <= ceildist) { - mo->FloorBounceMissile(mo->Sector->floorplane); - return true; + NextLowestFloorAt(mo->Sector, mo->X(), mo->Y(), mo->Z(), 0, mo->MaxStepHeight, nullptr, &ff); + return !mo->FloorBounceMissile(ff != nullptr ? *ff->top.plane : mo->floorsector->floorplane, ff != nullptr); } else { - mo->FloorBounceMissile(mo->Sector->ceilingplane); - return true; + NextHighestCeilingAt(mo->Sector, mo->X(), mo->Y(), mo->Z(), mo->Top(), 0, nullptr, &ff); + return !mo->FloorBounceMissile(ff != nullptr ? *ff->bottom.plane : mo->ceilingsector->ceilingplane, ff != nullptr); } } + + if (!(mo->BounceFlags & BOUNCE_Walls)) + return false; + line = bestslideline; if (mo->flags & MF_MISSILE) @@ -3787,7 +3774,8 @@ bool P_BounceActor(AActor *mo, AActor *BlockingMobj, bool ontop) else // Don't run through this for MBF-style bounces { // The reflected velocity keeps only about 70% of its original speed - mo->Vel.Z = (mo->Vel.Z - 2. * dot) * mo->bouncefactor; + mo->Vel.Z -= 2. * dot; + mo->Vel *= mo->bouncefactor; } mo->PlayBounceSound(true); @@ -3798,8 +3786,11 @@ bool P_BounceActor(AActor *mo, AActor *BlockingMobj, bool ontop) } else if (mo->BounceFlags & (BOUNCE_AutoOff | BOUNCE_AutoOffFloorOnly)) { - if (!(mo->flags & MF_NOGRAVITY) && (mo->Vel.Z < 3.)) - mo->BounceFlags &= ~BOUNCE_TypeMask; + if (mo->Vel.Z >= 0 || (mo->BounceFlags & BOUNCE_AutoOff)) + { + if (!(mo->flags & MF_NOGRAVITY) && (mo->Vel.Z < 3.)) + mo->BounceFlags &= ~BOUNCE_TypeMask; + } } } if (mo->BounceFlags & BOUNCE_UseBounceState) @@ -5469,7 +5460,7 @@ void P_RailAttack(FRailParams *p) // disabled because not complete yet. flags = (puffDefaults->flags6 & MF6_NOTRIGGER) ? TRACE_ReportPortals : TRACE_PCross | TRACE_Impact | TRACE_ReportPortals; - if (source->Level->localEventManager->WorldHitscanPreFired(source, source->Angles.Yaw + p->angleoffset, p->distance, source->Angles.Pitch + p->pitchoffset, p->damage, damagetype, puffclass, flags, p->offset_z, 0, p->offset_xy)) + if (source->Level->localEventManager->WorldRailgunPreFired(damagetype, puffclass, p)) { return; } @@ -5630,7 +5621,7 @@ void P_RailAttack(FRailParams *p) } } - source->Level->localEventManager->WorldHitscanFired(source, start, trace.HitPos, thepuff, flags); + source->Level->localEventManager->WorldRailgunFired(source, start, trace.HitPos, thepuff, flags); if (thepuff != NULL) { diff --git a/src/playsim/p_mobj.cpp b/src/playsim/p_mobj.cpp index adb770c71..1a400a65a 100644 --- a/src/playsim/p_mobj.cpp +++ b/src/playsim/p_mobj.cpp @@ -1545,6 +1545,13 @@ void DActorModelData::OnDestroy() animationIDs.Reset(); } +void DViewPosition::Serialize(FSerializer& arc) +{ + Super::Serialize(arc); + arc("offset", Offset) + ("flags", Flags); +} + //---------------------------------------------------------------------------- // // PROC P_ExplodeMissile @@ -1732,7 +1739,7 @@ void AActor::PlayBounceSound(bool onfloor) // Returns true if the missile was destroyed //---------------------------------------------------------------------------- -bool AActor::FloorBounceMissile (secplane_t &plane) +bool AActor::FloorBounceMissile (secplane_t &plane, bool is3DFloor) { if (flags & MF_MISSILE) { @@ -1775,9 +1782,13 @@ bool AActor::FloorBounceMissile (secplane_t &plane) } } + DVector3 norm = plane.Normal(); + if (is3DFloor) + norm = -norm; + bool onsky; - if (plane.fC() < 0) + if (norm.Z < 0) { // on ceiling if (!(BounceFlags & BOUNCE_Ceilings)) return true; @@ -1808,11 +1819,11 @@ bool AActor::FloorBounceMissile (secplane_t &plane) return true; } - double dot = (Vel | plane.Normal()) * 2; + double dot = (Vel | norm) * 2; if (BounceFlags & (BOUNCE_HereticType | BOUNCE_MBF)) { - Vel -= plane.Normal() * dot; + Vel -= norm * dot; AngleFromVel(); if (!(BounceFlags & BOUNCE_MBF)) // Heretic projectiles die, MBF projectiles don't. { @@ -1826,7 +1837,7 @@ bool AActor::FloorBounceMissile (secplane_t &plane) else // Don't run through this for MBF-style bounces { // The reflected velocity keeps only about 70% of its original speed - Vel = (Vel - plane.Normal() * dot) * bouncefactor; + Vel = (Vel - norm * dot) * bouncefactor; AngleFromVel(); } @@ -1835,7 +1846,7 @@ bool AActor::FloorBounceMissile (secplane_t &plane) // Set bounce state if (BounceFlags & BOUNCE_UseBounceState) { - FName names[2] = { NAME_Bounce, plane.fC() < 0 ? NAME_Ceiling : NAME_Floor }; + FName names[2] = { NAME_Bounce, norm.Z < 0 ? NAME_Ceiling : NAME_Floor }; FState *bouncestate = FindState(2, names); if (bouncestate != nullptr) { @@ -1850,10 +1861,10 @@ bool AActor::FloorBounceMissile (secplane_t &plane) } else if (BounceFlags & (BOUNCE_AutoOff|BOUNCE_AutoOffFloorOnly)) { - if (plane.fC() > 0 || (BounceFlags & BOUNCE_AutoOff)) + if (norm.Z > 0 || (BounceFlags & BOUNCE_AutoOff)) { // AutoOff only works when bouncing off a floor, not a ceiling (or in compatibility mode.) - if (!(flags & MF_NOGRAVITY) && (Vel.Z < 3)) + if (!(flags & MF_NOGRAVITY) && (norm.Z < 0 || ((Vel | norm) < 3))) BounceFlags &= ~BOUNCE_TypeMask; } } @@ -2646,7 +2657,9 @@ static void P_ZMovement (AActor *mo, double oldfloorz) mo->SetZ(mo->floorz); if (mo->BounceFlags & BOUNCE_Floors) { - mo->FloorBounceMissile (mo->floorsector->floorplane); + F3DFloor* ff = nullptr; + NextLowestFloorAt(mo->Sector, mo->X(), mo->Y(), mo->Z(), 0, mo->MaxStepHeight, nullptr, &ff); + mo->FloorBounceMissile (ff != nullptr ? *ff->top.plane : mo->floorsector->floorplane, ff != nullptr); /* if (!CanJump(mo)) */ return; } else if (mo->flags3 & MF3_NOEXPLODEFLOOR) @@ -2682,7 +2695,9 @@ static void P_ZMovement (AActor *mo, double oldfloorz) } else if (mo->BounceFlags & BOUNCE_MBF && mo->Vel.Z) // check for MBF-like bounce on non-missiles { - mo->FloorBounceMissile(mo->floorsector->floorplane); + F3DFloor* ff = nullptr; + NextLowestFloorAt(mo->Sector, mo->X(), mo->Y(), mo->Z(), 0, mo->MaxStepHeight, nullptr, &ff); + mo->FloorBounceMissile(ff != nullptr ? *ff->top.plane : mo->floorsector->floorplane, ff != nullptr); } if (mo->flags3 & MF3_ISMONSTER) // Blasted mobj falling { @@ -2753,7 +2768,9 @@ static void P_ZMovement (AActor *mo, double oldfloorz) mo->SetZ(mo->ceilingz - mo->Height); if (mo->BounceFlags & BOUNCE_Ceilings) { // ceiling bounce - mo->FloorBounceMissile (mo->ceilingsector->ceilingplane); + F3DFloor* ff = nullptr; + NextHighestCeilingAt(mo->Sector, mo->X(), mo->Y(), mo->Z(), mo->Top(), 0, nullptr, &ff); + mo->FloorBounceMissile(ff != nullptr ? *ff->bottom.plane : mo->ceilingsector->ceilingplane, ff != nullptr); /* if (!CanJump(mo)) */ return; } if (mo->flags & MF_SKULLFLY) @@ -4362,14 +4379,13 @@ void AActor::Tick () } if (Vel.Z != 0 && (BounceFlags & BOUNCE_Actors)) { - bool res = P_BounceActor(this, onmo, true); + if (flags & MF_MISSILE) + P_DoMissileDamage(this, onmo); + // If the bouncer is a missile and has hit the other actor it needs to be exploded here // to be in line with the case when an actor's side is hit. - if (!res && (flags & MF_MISSILE)) - { - P_DoMissileDamage(this, onmo); + if (!P_BounceActor(this, onmo, true) && (flags & MF_MISSILE)) P_ExplodeMissile(this, nullptr, onmo); - } } else { @@ -5594,16 +5610,13 @@ AActor *FLevelLocals::SpawnPlayer (FPlayerStart *mthing, int playernum, int flag PlayerSpawnPickClass(playernum); - if (( dmflags2 & DF2_SAME_SPAWN_SPOT ) && - ( p->playerstate == PST_REBORN ) && - ( deathmatch == false ) && - ( gameaction != ga_worlddone ) && - ( p->mo != NULL ) && - ( !(p->mo->Sector->Flags & SECF_NORESPAWN) ) && - ( NULL != p->attacker ) && // don't respawn on damaging floors - ( p->mo->Sector->damageamount < TELEFRAG_DAMAGE )) // this really should be a bit smarter... + if ((dmflags2 & DF2_SAME_SPAWN_SPOT) && !deathmatch + && p->mo != nullptr && p->playerstate == PST_REBORN + && gameaction != ga_worlddone + && !(p->mo->Sector->Flags & SECF_NORESPAWN) + && p->LastDamageType != NAME_Suicide) { - spawn = p->mo->Pos(); + spawn = p->LastSafePos; SpawnAngle = p->mo->Angles.Yaw; } else diff --git a/src/playsim/p_sectors.cpp b/src/playsim/p_sectors.cpp index a9014c2ce..581a5350a 100644 --- a/src/playsim/p_sectors.cpp +++ b/src/playsim/p_sectors.cpp @@ -129,6 +129,28 @@ DEFINE_ACTION_FUNCTION_NATIVE(_Sector, NextSpecialSector, P_NextSpecialSector) ACTION_RETURN_POINTER(P_NextSpecialSector(self, type, nogood)); } +bool sector_t::IsDangerous(const DVector3& pos, double height) const +{ + if (damageamount > 0) + return true; + + auto cl = dyn_cast(ceilingdata.Get()); + if (cl != nullptr && cl->getCrush() > 0) + return true; + + for (auto rover : e->XFloor.ffloors) + { + if ((rover->flags & FF_EXISTS) && rover->model->damageamount > 0 + && pos.Z <= rover->top.plane->ZatPoint(pos) + && pos.Z + height >= rover->bottom.plane->ZatPoint(pos)) + { + return true; + } + } + + return false; +} + // // P_FindLowestFloorSurrounding() // FIND LOWEST FLOOR HEIGHT IN SURROUNDING SECTORS diff --git a/src/playsim/p_trace.cpp b/src/playsim/p_trace.cpp index b0873c552..cf9f20793 100644 --- a/src/playsim/p_trace.cpp +++ b/src/playsim/p_trace.cpp @@ -496,6 +496,10 @@ bool FTraceInfo::LineCheck(intercept_t *in, double dist, DVector3 hit, bool spec entersector = &DummySector[sectorsel]; sectorsel ^= 1; + // We need to make sure that 3D floors clipping underneath the ground/above the ceiling don't + // accidentally get ignored. + bool setFloor = false, setCeiling = false; + for (auto rover : entersector->e->XFloor.ffloors) { int entershootthrough = !!(rover->flags&FF_SHOOTTHROUGH); @@ -508,8 +512,8 @@ bool FTraceInfo::LineCheck(intercept_t *in, double dist, DVector3 hit, bool spec // clip to the part of the sector we are in if (hit.Z > ff_top) { - // 3D floor height is the same as the floor height. We need to test a second spot to see if it is above or below - if (fabs(bf - ff_top) < EQUAL_EPSILON) + // 3D floor height is the same as the floor height or underground. We need to test a second spot to see if it is above or below + if ((ff_top < bf && !setFloor) || fabs(bf - ff_top) < EQUAL_EPSILON) { double cf = entersector->floorplane.ZatPoint(entersector->centerspot); double ffc = rover->top.plane->ZatPoint(entersector->centerspot); @@ -522,6 +526,7 @@ bool FTraceInfo::LineCheck(intercept_t *in, double dist, DVector3 hit, bool spec // above if (bf < ff_top) { + setFloor = true; entersector->floorplane = *rover->top.plane; entersector->SetTexture(sector_t::floor, *rover->top.texture, false, false); entersector->ClearPortal(sector_t::floor); @@ -530,8 +535,8 @@ bool FTraceInfo::LineCheck(intercept_t *in, double dist, DVector3 hit, bool spec } else if (hit.Z < ff_bottom) { - // 3D floor height is the same as the ceiling height. We need to test a second spot to see if it is above or below - if (fabs(bc - ff_bottom) < EQUAL_EPSILON) + // 3D floor height is the same as the ceiling height or above it. We need to test a second spot to see if it is above or below + if ((ff_bottom > bc && !setCeiling) || fabs(bc - ff_bottom) < EQUAL_EPSILON) { double cc = entersector->ceilingplane.ZatPoint(entersector->centerspot); double fcc = rover->bottom.plane->ZatPoint(entersector->centerspot); @@ -544,6 +549,7 @@ bool FTraceInfo::LineCheck(intercept_t *in, double dist, DVector3 hit, bool spec //below if (bc > ff_bottom) { + setCeiling = true; entersector->ceilingplane = *rover->bottom.plane; entersector->SetTexture(sector_t::ceiling, *rover->bottom.texture, false, false); entersector->ClearPortal(sector_t::ceiling); diff --git a/src/playsim/p_user.cpp b/src/playsim/p_user.cpp index 4ed0c0bfd..65f892903 100644 --- a/src/playsim/p_user.cpp +++ b/src/playsim/p_user.cpp @@ -99,6 +99,7 @@ static FRandom pr_skullpop ("SkullPop"); // [SP] Allows respawn in single player CVAR(Bool, sv_singleplayerrespawn, false, CVAR_SERVERINFO | CVAR_CHEAT) +CVAR(Float, snd_footstepvolume, 1.f, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) // Variables for prediction CVAR(Bool, cl_predict_specials, true, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) @@ -360,6 +361,7 @@ void player_t::CopyFrom(player_t &p, bool copyPSP) MUSINFOactor = p.MUSINFOactor; MUSINFOtics = p.MUSINFOtics; SoundClass = p.SoundClass; + LastSafePos = p.LastSafePos; angleOffsetTargets = p.angleOffsetTargets; if (copyPSP) { @@ -1290,6 +1292,13 @@ void P_PlayerThink (player_t *player) player->SubtitleCounter--; } + if (player->playerstate == PST_LIVE + && player->mo->Z() <= player->mo->floorz + && !player->mo->Sector->IsDangerous(player->mo->Pos(), player->mo->Height)) + { + player->LastSafePos = player->mo->Pos(); + } + // Bots do not think in freeze mode. if (player->mo->Level->isFrozen() && player->Bot != nullptr) { @@ -1778,7 +1787,9 @@ void player_t::Serialize(FSerializer &arc) ("onground", onground) ("musinfoactor", MUSINFOactor) ("musinfotics", MUSINFOtics) - ("soundclass", SoundClass); + ("soundclass", SoundClass) + ("angleoffsettargets", angleOffsetTargets) + ("lastsafepos", LastSafePos); if (arc.isWriting ()) { diff --git a/src/scripting/thingdef.cpp b/src/scripting/thingdef.cpp index 6affab4c3..bdec840f5 100644 --- a/src/scripting/thingdef.cpp +++ b/src/scripting/thingdef.cpp @@ -65,6 +65,7 @@ static TMap StateSourceLines; static FScriptPosition unknownstatesource("unknown file", 0); EXTERN_CVAR(Bool, strictdecorate); +EXTERN_CVAR(Bool, warningstoerrors); //========================================================================== // @@ -464,8 +465,27 @@ void LoadActors() if (FScriptPosition::ErrorCounter > 0) { - I_Error("%d errors while parsing DECORATE scripts", FScriptPosition::ErrorCounter); + if (FScriptPosition::WarnCounter > 0) + { + I_Error("%d errors, %d warnings while parsing scripts", FScriptPosition::ErrorCounter, FScriptPosition::WarnCounter); + } + else + { + I_Error("%d errors while parsing scripts", FScriptPosition::ErrorCounter); + } } + else if (FScriptPosition::WarnCounter > 0) + { + if(warningstoerrors) + { + I_Error("%d warnings while parsing scripts\n", FScriptPosition::WarnCounter); + } + else + { + Printf(TEXTCOLOR_ORANGE "%d warnings while parsing scripts\n", FScriptPosition::WarnCounter); + } + } + FScriptPosition::ResetErrorCounter(); // AllActorClasses hasn'T been set up yet. for (int i = PClass::AllClasses.Size() - 1; i >= 0; i--) diff --git a/src/scripting/vmthunks_actors.cpp b/src/scripting/vmthunks_actors.cpp index 18cd8aa32..2cf04fd80 100644 --- a/src/scripting/vmthunks_actors.cpp +++ b/src/scripting/vmthunks_actors.cpp @@ -1851,8 +1851,9 @@ DEFINE_ACTION_FUNCTION(AActor, BouncePlane) { PARAM_SELF_PROLOGUE(AActor); PARAM_POINTER(plane, secplane_t); + PARAM_BOOL(is3DFloor); - ACTION_RETURN_BOOL(self->FloorBounceMissile(*plane)); + ACTION_RETURN_BOOL(self->FloorBounceMissile(*plane, is3DFloor)); } DEFINE_ACTION_FUNCTION(AActor, PlayBounceSound) diff --git a/src/serializer_doom.h b/src/serializer_doom.h index b399568c2..40b6710a1 100644 --- a/src/serializer_doom.h +++ b/src/serializer_doom.h @@ -42,6 +42,7 @@ FSerializer& Serialize(FSerializer& arc, const char* key, char& value, char* def FSerializer &Serialize(FSerializer &arc, const char *key, ticcmd_t &sid, ticcmd_t *def); FSerializer &Serialize(FSerializer &arc, const char *key, usercmd_t &cmd, usercmd_t *def); FSerializer &Serialize(FSerializer &arc, const char *key, FInterpolator &rs, FInterpolator *def); +FSerializer& Serialize(FSerializer& arc, const char* key, struct FStandaloneAnimation& value, struct FStandaloneAnimation* defval); template<> FSerializer &Serialize(FSerializer &arc, const char *key, FPolyObj *&value, FPolyObj **defval); template<> FSerializer &Serialize(FSerializer &arc, const char *key, sector_t *&value, sector_t **defval); diff --git a/src/version.h b/src/version.h index dee38c893..f63ca3fea 100644 --- a/src/version.h +++ b/src/version.h @@ -99,6 +99,7 @@ const char *GetVersionString(); // This is so that derivates can use the same savegame versions without worrying about engine compatibility #define GAMESIG "VKDOOM" #define BASEWAD "vkdoom.pk3" +// Set OPTIONALWAD to "" (null) to disable searching for it #define OPTIONALWAD "game_support.pk3" #define GZDOOM 1 #define VR3D_ENABLED diff --git a/wadsrc/static/language.0 b/wadsrc/static/language.0 index cf004c828..3eefd93d7 100644 --- a/wadsrc/static/language.0 +++ b/wadsrc/static/language.0 @@ -873,12 +873,12 @@ Appearance,DSPLYMNU_APPEARANCE,,,,Vzhled,Udseende,Spieldarstellung,,Aspekto,Apar Advanced Display Options,DSPLYMNU_ADVANCED,,,,Grafika (pokročilé),Avancerede visningsindstillinger,Erweiterte Anzeigeoptionen,,Altnivelaj ekran-agordoj,Opciones avanzadas de visualización,,Näytön lisäasetukset,Options d'affichage avancées,Napredne postavke zaslona,Speciális megjelenítési beállítások,Opzioni di visualizzazione avanzate,高度なディスプレイオプション,고급 디스플레이 옵션,Geavanceerde Weergave Opties,Avanserte visningsalternativer,Zaawansowane Opcje Wyświetlania,Opções de vídeo avançadas,,Opțiuni avansate de afișare,Расширенные настройки экрана,Напредне опције приказа,Avancerade visningsalternativ,Gelişmiş Görüntüleme Seçenekleri,Додаткові параметри відображення, ,,IWAD/Game picker,,,,,,,,,,,,,,,,,,,,,,,,,,,, Select which game file to run.,PICKER_SELECT,,,,"Vyberte, jaký herní soubor spustit.","Vælg, hvilket spil du vil spille",Bitte wähle ein Spiel aus.,,Elektu kiun ludodosieron ruli.,,,Valitse suoritettava pelitiedosto.,Sélectionner le jeu à jouer,Odaberi koju igru želiš pokrenuti.,"Válassza ki, hogy melyik játékfájlt futtassa.",Selezionare il file di gioco da eseguire.,実行するゲームファイルを選択します。,실행할 게임 파일을 선택합니다.,Selecteer welk spel je wilt spelen,Velg hvilket spill du vil spille,"Wybierz, który plik gry uruchomić.",Selecione qual arquivo de jogo rodar.,,Selectați ce fișier de joc să rulați.,Выбор файла игры для запуска.,Изаберите коју датотеку игре желите да покренете.,Välj vilket spel du vill spela,Hangi oyunu oynayacağınızı seçin,Виберіть файл гри для запуску., -Play Game,PICKER_PLAY,,,,Hrát hru,Start spil,Spielen,,Ludi ludon,,,Pelin pelaaminen,Démarrer le jeu,Zaigraj igru,Játék lejátszása,Esegui gioco,ゲームをプレイする,게임 플레이,Spel starten,Start spill,Graj,Jogar,,Joacă jocul,Играть,Играј игру,Starta spel,Oyunu Başlat,Запустити гру, +Play Game,PICKER_PLAY,,,,Hrát hru,Start spil,Spielen,,Ludi ludon,,,Pelin pelaaminen,Démarrer le jeu,Zaigraj igru,Játék lejátszása,Esegui gioco,プレイ,게임 플레이,Spel starten,Start spill,Graj,Jogar,,Joacă jocul,Играть,Играј игру,Starta spel,Oyunu Başlat,Запустити гру, Exit,PICKER_EXIT,,,,Odejít,Afslut,Verlassen,,Eliri,,,Poistu,Quitter,Izlaz,Kilépés,Esci,終了,종료,Verlaten,Avslutt,Wyjdź,Sair,,Ieșire,Выход,Изађи,Avsluta,Çıkış,Вихід, General,PICKER_GENERAL,,,,Obecné,Generelt,Allgemein,,Ĝenerala,,,Yleistä,Général,Općenito,Általános,Generale,一般,일반,Algemeen,Generelt,Ogólne,Geral,,,Общее,Генерал,Allmänt,Genel,Загальні, Extra Graphics,PICKER_EXTRA,,,,Grafické doplňky,Ekstra grafik,Extragrafiken,,Ekstra grafiko,,,Extra Graphics,Graphiques supplémentaires,Dodatna grafika,Extra grafika,Grafica extra,追加グラフィックス,추가 그래픽,Extra afbeeldingen,Ekstra grafikk,Ekstra grafiki,Gráficos extras,,Grafică suplimentară,Доп. графика,Ектра Грапхицс,Extra grafik,Ekstra Grafikler,Додаткова графіка, Fullscreen,PICKER_FULLSCREEN,,,,Přes celou obrazovku,Fuld skærm,Vollbild,,Plena ekrano,,,Koko näyttö,Plein écran,Puni zaslon,Teljes képernyő,Schermo intero,フルスクリーン,전체 화면,Volledig scherm,Fullskjerm,Pełny ekran,Tela cheia,,Ecran complet,Полный экран,Цео екран,Fullskärm,Tam Ekran,Повноекранний режим, -Disable autoload,PICKER_NOAUTOLOAD,,,,Zakázat autoload,Deaktiver autoload,Autoload deaktivieren,,Malvalidigi aŭtoŝargon,,,Poista automaattinen lataus käytöstä,Désactiver le chargement automatique,Onemogući automatsko učitavanje,Automatikus betöltés kikapcsolása,Disabilita il caricamento automatico,オートロードを無効にする,자동 로드 비활성화,Autoload uitschakelen,Deaktiver autolading,Wyłącz auto-ładowanie,Desativar autocarregam.,,Dezactivați încărcarea automată,Откл. автозагрузку,Онемогући аутоматско учитавање,Inaktivera autoload,Otomatik yükleme yok,Вимкнути автозавантаження, +Disable autoload,PICKER_NOAUTOLOAD,,,,Zakázat autoload,Deaktiver autoload,Autoload deaktivieren,,Malvalidigi aŭtoŝargon,,,Poista automaattinen lataus käytöstä,Désactiver le chargement automatique,Onemogući automatsko učitavanje,Automatikus betöltés kikapcsolása,Disabilita il caricamento automatico,オートロード無効,자동 로드 비활성화,Autoload uitschakelen,Deaktiver autolading,Wyłącz auto-ładowanie,Desativar autocarregam.,,Dezactivați încărcarea automată,Откл. автозагрузку,Онемогући аутоматско учитавање,Inaktivera autoload,Otomatik yükleme yok,Вимкнути автозавантаження, Don't ask me again,PICKER_DONTASK,,,,Již se neptat,Spørg mig ikke igen,Nicht nochmal fragen,,Ne demandu min denove,,,Älä kysy uudestaan,Ne me demandez plus rien,Više me ne pitaj,Ne kérdezz újra,Non chiedermelo più,再度聞かない,다시 묻지 마세요,Vraag me niet opnieuw,Ikke spør meg igjen,Nie pytaj ponownie,Não perguntar de novo,,Nu mă mai întrebați din nou,Не спрашивать снова,Не питај ме поново,Fråga mig inte igen,Bir daha sorma.,Не запитуйте мене більше, Lights,PICKER_LIGHTS,,,,Světla,Lys,Lichtdefinitionen,,Lumoj,,,Valot,Lumières,Svjetla,Fények,Luci,ライト,조명,Verlichting,Lysdefinisjoner,Oświetlenie,Luzes,,Lumini,Освещение,Светла,Definitioner av ljus,Işık tanımları,Освітлення, Brightmaps,PICKER_BRIGHTMAPS,,,,,,,,Helomapoj,,,Brightmaps,Cartes lumineuses,Svijetle karte,Brightmaps,Mappe luminose,ブライトマップ,브라이트맵,Heldermaps,Lyskart,Mapowanie świateł,,,,Карты освещения,Бригхтмапс,Ljuskartor,Brightmaps,Яскраві карти, @@ -891,4 +891,4 @@ Game,PICKER_TAB_PLAY,,,,Hra,Spil,Spiel,,Ludo,Juego,,Peli,Jeu,Igra,Játék,Gioco, Enable this controller,JOYMNU_JOYENABLE,Option to enable or disable individual controllers/joysticks when configuring it in the Controller Options menu,,,Povolit tento ovladač,Aktivér denne controller,Diesen Controller aktivieren,,Ŝalti ĉi tiun ludregilon,Habilitar este controlador,,Aktivoi tämä ohjain,Activer ce contrôleur,Omogući ovaj kontroler,Engedélyezze ezt a vezérlőt,Attiva questo controller,このコントローラーを有効にする,이 컨트롤러 활성화,Deze controller inschakelen,Aktiver denne kontrolleren,Włącz ten kontroler,Habilitar este controle,,Activați acest controler,Включить этот контроллер,Омогућите овај контролер,Aktivera denna styrenhet,Bu denetleyiciyi etkinleştirin,Увімкніть цей контролер, Open Main Menu,CNTRLMNU_OPEN_MAIN,,,,Otevřít hlavní menu,Åbn hovedmenuen,Hauptmenü öffnen,,Malfermi ĉefan menuon,Abrir el menú principal,,Avaa päävalikko,Ouvrir le menu principal,Otvori glavni izbornik,Főmenü megnyitása,Apri il menu principale,メインメニューを開く,메인 메뉴 열기,Hoofdmenu openen,Åpne hovedmenyen,Otwórz meny główne,Abrir menu principal,,Deschideți meniul principal,Открыть главное меню,Отворите главни мени,Öppna huvudmenyn,Ana Menüyü Aç,Відкрити головне меню, ,,New content,,,,,,,,,,,,,,,,,,,,,,,,,,,, -Module replayer,MODMNU_REPLAYER,,,,Nastavení přehrávače modulů,Modulafspiller,Modul-Spieler,,Legilo de moduloj,Reproductor de módulos,,,Lecteur de module,,,Module replayer,,,Module replayer,Modulavspiller,,Reprodutor de módulos,,Redare a modulelor,Модуль-повторитель,,Modulåterspelare,,Module replayer, \ No newline at end of file +Module replayer,MODMNU_REPLAYER,,,,Nastavení přehrávače modulů,Modulafspiller,Modul-Spieler,,Legilo de moduloj,Reproductor de módulos,,,Lecteur de module,Ponavljač modula,,Module replayer,モジュール リプレイヤー,,Module replayer,Modulavspiller,,Reprodutor de módulos,,Redare a modulelor,Модуль-повторитель,,Modulåterspelare,,Module replayer, \ No newline at end of file diff --git a/wadsrc/static/language.csv b/wadsrc/static/language.csv index eaa712791..4661e4598 100644 --- a/wadsrc/static/language.csv +++ b/wadsrc/static/language.csv @@ -1911,7 +1911,7 @@ armură.",Игрока %o обслюнявила бронированная дв %o was hit by %k's propulsor.,OB_MPP_SPLASH,,,,%o byl@[ao_cs] zasažen@[ao_cs] propulzorem hráčem %k.,%o blev ramt af %k's propulsor.,%o wurde von %ks Propeller getroffen,,%o estas trafita de la propulsilo de %k.,%o fue impactad@[ao_esp] por el propulsor de %k.,,%k osui %o parkaan työntimellään.,%o à été frappé@[e_fr] par le propulseur de %k.,,%o elérte %k elhárítóját.,%o è stato colpito dal propulsore di %k.,%o に %k のロケットゾーチャーがとどいた。,%o 은(는) %k 의 저치 추진기의 방사 능력을 우습게 봤습니다.,%o werd geraakt door %k's propulsor.,%o ble truffet av %k's propulsor.,%o został@[ao_pl] trafion@[adj_pl] pędnikiem %k.,%o foi atingid@[ao_ptb] pelo propulsor de %k.,,%o a fost învins de acceleratorul lui %k.,Игрок %o подстрелен из ускорителя зорча %k.,%o је ударен %k погоном.,%o blev träffad av %ks propulsor.,"%o, %k tarafından yenildi.",%k врази@[adj_1_ua] %o з прискореного зорчера. %o was phase zorched by %k.,OB_MPPHASEZORCH,,,,%o byl@[ao_cs] bzukr-vyfázován@[ao_cs] hráčem %k.,%o blev fasezorched af %k.,%o wurde von %k wegsynchronisiert,,%o estas fluktuantzorĉita de %k.,%o fue electrizad@[ao_esp] en fase por %k.,,%k vaiheiszorchasi %o paran.,%o à été phasé@[e_fr] par %k.,,%o fázis zorker áldozata lett %k által.,%o è stato phase-zorchato da %k.,%o は %k によってスベスベにされた。,%o 은(는) %k 의 전자 자쳐에 의해 쓰러졌습니다.,%o was fase zorched door %k.,%o ble fasebrent av %k.,%o został@[ao_pl] został sfazowan@[adj_pl] przez %k.,%o foi atingid@[ao_ptb] pelo zorcher fásico por %k.,,%o a fost înfrânt de zorcherul fazat al lui %k.,Игрок %o получил заряд из фазерного зорчера %k.,%o је фазно торчован %k.,%o blev faszorchad av %k.,"%o, %k tarafından yenildi.",%k фазорчну@[adj_1_ua] %o. %o fell prey to %k's LAZ device.,OB_MPLAZ_BOOM,,,,%o padl@[ao_cs] velkodosahovému bzukru hráče %k.,%o blev offer for %k's LAZ-enhed.,%o fiel %ks Flächenzorcher zum Opfer,,%o iĝis predo de la LAZ-aparato de %k.,%o cayó pres@[ao_esp] ante el dispositivo LAZ de %k.,,%o lankesi pelaajan %k SAZ-laitteen uhriksi.,%o est devenu@[e_fr] la proie du ZZL de %k.,,%o prédául lett ejtve %k LAZ készülékével.,%o è stato preda del dispositivo LAZ di %k.,%o は %k のLAZデバイスのえじきになった。,%k 이(가) LAZ 장치를 들기 전에 %o 은(는) 도망쳐야 했습니다.,%o viel ten prooi aan %k's LAZ apparaat.,%o ble offer for %k's LAZ-enhet.,%o stał@[ao_pl] się ofiarą urządzenia LAZ %k.,%o foi vítima do dispositivo LAZ de %k.,,%o a căzut pradă dispozitivului LAZ al lui %k.,Игрок %o склонился перед «ЗБР» игрока %k.,%o је постао жртва %k ЛАЗ уређаја.,%o föll offer för %ks LAZ-enhet.,"%o, %k tarafından lazzlandı.",%o па@[adj_1_ua] жертвою LAZ девайсу %k. -%o was lazzed by %k.,OB_MPLAZ_SPLASH,,,,%o byl@[ao_cs] odbzukrován hráčem %k.,%o blev lazzed af %k.,%o wurde von %k weggebeamt.,,%o estas LAZ-ita de %k.,%o fue LAZeado por %k.,,%k sazzasi pelaajaan %o.,%o à été pris@[e_fr] par le ZZL de %k.,,%o el lett lazzázva %k által.,%o è stato lazzato da %k.,%o は %k にとかされた。,%o 은(는) %k 의 LAZ 장치 범위를 벗어날 수 없었습니다.,%o werd gelazed door %k.,%o ble lazzed av %k.,%o został@[ao_pl] zLAZowan@[adj_pl] przez %k.,%o foi LAZead@[ao_ptb] por %k.,,%o a fost stropit de dispozitivul LAZ al lui %k.,Игрок %o получил заряд из «ЗБР» игрока %k.,%o је ЛАЗ-овао %k.,%o blev lazzad av %k.,"%o, %k tarafından lazzlandı.",%k LAZну@[adj_1_ua] %o. +%o was lazzed by %k.,OB_MPBFG_SPLASH,,chex,,%o byl@[ao_cs] odbzukrován hráčem %k.,%o blev lazzed af %k.,%o wurde von %k weggebeamt.,,%o estas LAZ-ita de %k.,%o fue LAZeado por %k.,,%k sazzasi pelaajaan %o.,%o à été pris@[e_fr] par le ZZL de %k.,,%o el lett lazzázva %k által.,%o è stato lazzato da %k.,%o は %k にとかされた。,%o 은(는) %k 의 LAZ 장치 범위를 벗어날 수 없었습니다.,%o werd gelazed door %k.,%o ble lazzed av %k.,%o został@[ao_pl] zLAZowan@[adj_pl] przez %k.,%o foi LAZead@[ao_ptb] por %k.,,%o a fost stropit de dispozitivul LAZ al lui %k.,Игрок %o получил заряд из «ЗБР» игрока %k.,%o је ЛАЗ-овао %k.,%o blev lazzad av %k.,"%o, %k tarafından lazzlandı.",%k LAZну@[adj_1_ua] %o. ,,Miscellaneous,,,,,,,,,,,,,,,,,,,,,,,,,,, E1M1: Landing Zone,CHUSTR_E1M1,,,,E1M1: Přistávací zóna,E1M1: Landingszone,E1M1: Landezone,,E1M1: Surterejo,E1M1: Zona de Aterrizaje,,E1M1: Laskeutumisalue,E1M1: La piste d'atterissage,,E1M1: Leszállópálya,E1M1: Zona di Atterraggio,E1M1: ちゃくりく ちてん,E1M1: 착륙 지점,E1M1: Landingszone,E1M1: Landingssone,E1M1: Strefa Lądowania,E1M1: Zona de Pouso,E1M1: Zona de Aterragem,E1M1: Pista de Aterizare,E1M1: Зона приземления,Е1М1: Зона слетања,E1M1: Landningszon,E1M1: İniş Bölgesi,E1M1: Зона висадки E1M2: Storage Facility,CHUSTR_E1M2,,,,E1M2: Skladiště,E1M2: Opbevaringsanlæg,E1M2: Lagerhalle,,E1M2: Stokejo,E1M2: Instalación de Almacenamiento,,E1M2: Varastolaitos,E1M2: Le centre de stockage,,E1M2: Raktárépület,E1M2: Struttura di Immagazzinamento,E1M2: ほかん しせつ,E1M2: 저장 시설,E1M2: Opslagfaciliteit,E1M2: Lagringsanlegg,E1M2: Magazyn,E1M2: Depósito,E1M2: Armazém,E1M2: Depozitul,E1M2: Хранилище,Е1М2: Складиште,E1M2: Förvaringsanläggning,E1M2: Depolama Tesisi,E1M2: Сховище @@ -3415,4 +3415,5 @@ Restart level on Death,MISCMNU_RESTARTONDEATH,Disables autoloading save on death Pistol Start,GMPLYMNU_PISTOLSTART,Resets inventory on every map,,,Začít jen s pistolí,Start med pistol,Pistolenstart,,Komenci kun pistolo,,,,Démarrage du pistolet,,,Avvio pistola,ピストルスタート,,Start met pistool,Start med pistol,Start z pistoletem,Iniciar com a pistola,,,Пистолет в начале,,Starta med pistol,Tabanca ile başlayın, Allow creation of zombie players,CMPTMNU_VOODOOZOMBIES,,,,Povolit zombie hráče,,Erlaube Zombieplayer,,Permesi kreadon de zombiaj ludantoj,,,,,,,,ゾンビプレイヤーの生成許可,,,,Pozwól na tworzenie graczy-zombie,Permitir criação de jogadores zumbi,,,Разрешить создание игроков-зомби,,,, ignore floor z when teleporting,CMPTMNU_FDTELEPORT,,,,Při teleportu ignorovat výšku podlahy,,Ignoriere Fußbodenhöhe bem Teleportieren,,Ignori altecon de planko dum teleportado,,,,,,,,テレポート時にfloor z値を無視する,,,,Ignoruj wysokość podłogi podczas teleportacji,Ignorar altura do chão ao teletransportar,,,игнорирование диагонали пола (Z) при телепортации,,,, -Forced-Perspective,OPTVAL_ANAMORPHIC,"Hardware Sprite Clipping mode that gives the illusion of a sprite being in a different position than it really is, allowing it to be rendered under the floor despite clipping",,,,,Erzwungene Perspektive,,,,,,,,,,,,,,,,,,Перспективный (анаморфотный),,,, \ No newline at end of file +Forced-Perspective,OPTVAL_ANAMORPHIC,"Hardware Sprite Clipping mode that gives the illusion of a sprite being in a different position than it really is, allowing it to be rendered under the floor despite clipping",,,Vynucená perspektiva,,Erzwungene Perspektive,,Devigita perspektivo,,,,,,,,,,,,,Perspectiva forçada,,,Принудительно перспективный,,,, +Footstep Volume,SNDMENU_FOOTSTEPVOLUME,,,,Hlasitost kroků,,Schrittlautstärke,,,,,,,,,,,,,,,Volume dos passos,,,Громкость шагов,,,, \ No newline at end of file diff --git a/wadsrc/static/zscript/actors/actor.zs b/wadsrc/static/zscript/actors/actor.zs index 8c50fa738..47db8c4b5 100644 --- a/wadsrc/static/zscript/actors/actor.zs +++ b/wadsrc/static/zscript/actors/actor.zs @@ -866,7 +866,7 @@ class Actor : Thinker native native void PlayPushSound(); native bool BounceActor(Actor blocking, bool onTop); native bool BounceWall(Line l = null); - native bool BouncePlane(readonly plane); + native bool BouncePlane(readonly plane, bool is3DFloor = false); native void PlayBounceSound(bool onFloor); native bool ReflectOffActor(Actor blocking); diff --git a/wadsrc/static/zscript/events.zs b/wadsrc/static/zscript/events.zs index 72cf23a51..b1aaf30c8 100644 --- a/wadsrc/static/zscript/events.zs +++ b/wadsrc/static/zscript/events.zs @@ -97,14 +97,16 @@ struct WorldEvent native play version("2.4") native readonly bool DamageIsRadius; native int NewDamage; native readonly State CrushedState; - native readonly double AttackAngle; - native readonly double AttackPitch; - native readonly double AttackDistance; - native readonly vector3 AttackPos; - native readonly double AttackOffsetForward; - native readonly double AttackOffsetSide; - native readonly double AttackZ; - native readonly class AttackPuffType; + native readonly int AttackLineFlags; + native readonly double AttackAngle; + native readonly double AttackPitch; + native readonly double AttackDistance; + native readonly vector3 AttackPos; + native readonly double AttackOffsetForward; + native readonly double AttackOffsetSide; + native readonly double AttackZ; + native readonly class AttackPuffType; + native readonly FRailParams RailParams; } struct PlayerEvent native play version("2.4") @@ -163,7 +165,9 @@ class StaticEventHandler : Object native play version("2.4") virtual void WorldThingRevived(WorldEvent e) {} virtual void WorldThingDamaged(WorldEvent e) {} virtual void WorldThingDestroyed(WorldEvent e) {} - virtual bool WorldHitscanPreFired(WorldEvent e) { return false; } + virtual bool WorldHitscanPreFired(WorldEvent e) { return false; } + virtual bool WorldRailgunPreFired(WorldEvent e) { return false; } + virtual void WorldRailgunFired(WorldEvent e) {} virtual void WorldHitscanFired(WorldEvent e) {} virtual void WorldLinePreActivated(WorldEvent e) {} virtual void WorldLineActivated(WorldEvent e) {} diff --git a/wadsrc_extra/static/language.csv b/wadsrc_extra/static/language.csv index 2a62c0fcc..c2bd82f36 100644 --- a/wadsrc_extra/static/language.csv +++ b/wadsrc_extra/static/language.csv @@ -10701,8 +10701,8 @@ Where's the illegal tap?,TXT_RPLY2_SCRIPT02_D98540_WHERE,〃 (〃),,,Kde je nele どれをイジるかまでは知らん。 配電機はここの下水処理場にある。",그게 어딨는지 말해주지. 왜 그 동력선에 손을 대려고 하는지는 모르겠지마는. 저 앞 하수도 안에 있다네.,"Ik zal je vertellen waar hij is, maar ik weet niet met wie je met de koppeling zult knoeien. Het is hier in de rioolwaterzuiveringsinstallatie.","Jeg skal si deg hvor den er, men jeg vet ikke hvem sin kobling du skal tukle med. Den er her i kloakkanlegget.","Powiem ci gdzie jest, ale nie wiem czyj to odbierak, przy którym będziesz majstrować. Jest w oczyszczalni ścieków.","Vou te dizer onde está, mas não sei em qual ligação você vai acabar mexendo. Fica bem aqui na usina de tratamento de esgoto.",,"Îți voi spune unde este, dar nu știu de care cuplaj te vei lega. Este chiar aici, în sistemul de canalizare.","Я скажу тебе, где она, но не знаю, которая тебе нужна. Она прямо здесь, в очистной системе.",,"Jag ska berätta var den är, men jag vet inte vems koppling du kommer att mixtra med. Den finns här i reningsverket.","Nerede olduğunu söyleyeceğim, ama kimin bağlantılarını kurcalayacağınızı bilmiyorum. Tam burada, kanalizasyon tesisinde." Thanks,TXT_RPLY0_SCRIPT02_D100056_THANK,〃 (〃),,,Díky.,Tak,Danke,,Dankon.,Gracias.,,Kiitos,Merci.,,Kösz.,Grazie,ありがとう。,그렇군요.,Bedankt,Takk skal du ha.,Dzięki,Obrigado,,Mulțumesc,Спасибо.,,Tack,Teşekkürler. -That's right here in the sewage plant. But it's the Front's coupling. Whoever told you that it was the Order's was wrong.,TXT_DLG_SCRIPT02_D101572_THATS,〃 (〃),,,"Síť je přímo tady, v čističce. Ale je to spojka Fronty. Kdo ti řekl, že je Řádu, se mýlil.","Det er lige her i kloakanlægget. Men det er Fronts kobling. Den, der fortalte dig, at det var Ordenens, tog fejl.","Genau hier, in der Kläranlage. Aber sie gehört der Front. Wer auch immer dir gesagt hat, sie gehöre dem Orden, hat gelogen.",,"Ĝi estas ĉi tie, en la kloakpurigejo, sed ĝi estas la kuplado de la Fronto. Tiu ajn, kiu diris, ke ĝi estas de La Ordeno, eraris.","Está justo aquí en la planta de aguas residuales, pero es el acoplamiento del Frente. Quien te haya dicho que era de La Orden se ha equivocado. -","Está justo aquí en la planta de aguas residuales, pero es el acoplamiento del Frente. Quien te haya dicho que era de La Orden te dijo mal. +That's right here in the sewage plant. But it's the Front's coupling. Whoever told you that it was the Order's was wrong.,TXT_DLG_SCRIPT02_D101572_THATS,〃 (〃),,,"Síť je přímo tady, v čističce. Ale je to spojka Fronty. Kdo ti řekl, že je Řádu, se mýlil.","Det er lige her i kloakanlægget. Men det er Fronts kobling. Den, der fortalte dig, at det var Ordenens, tog fejl.","Genau hier, in der Kläranlage. Aber sie gehört der Front. Wer auch immer dir gesagt hat, sie gehöre dem Orden, hat gelogen.",,"Ĝi estas ĉi tie, en la kloakpurigejo, sed ĝi estas la kuplado de la Fronto. Kiu diris, ke ĝi estas de La Ordeno, tiu ajn eraris.","Está justo aquí en la planta de aguas residuales, aunque es el acoplamiento del Frente. Quien te haya dicho que era de La Orden se ha equivocado. +","Está justo aquí en la planta de aguas residuales, aunque es el acoplamiento del Frente. Quien te haya dicho que era de La Orden te dijo mal. ","Se on täällä jätelaitoksella. Mutta se on Rintaman kytkentä. Kuka sinulle kertoikaan sen kuuluvan Veljeskunnalle, oli väärässä.","Il se trouve ici dans le traitement des égouts, mais c'est le coupleur du Front . Celui qui vous a dit qu'il appartient à l'Ordre se plante.",,"Igen, jól hallottad, a szennyvíz tisztítóban. Ez azonban a Front kapcsolója. Akárki mondta, hogy a Rendé, hazudott neked.",È proprio qui nell'impianto fognario. Ma è il giunto del Fronte. Chiunque ti abbia detto che fosse il giunto dell'Ordine si sbagliava.,"正にここの下水処理場にあるさ。 だがそれはフロントにも繋いでいる。 そいつがオーダーの所有物と言われてたが、 @@ -12900,10 +12900,10 @@ What an up guy.,TXT_BBSUB_SCRIPT05_D4548_THEOR,MAP05: Prison Guard.,,,Takový ve Great. Now we're shopping for the Rat King.,TXT_BBSUB_SCRIPT06_D3032_BRING,〃,,,"Super, teď máme nakupovat pro Krysího krále.",Alle er så trængende. Nu skal vi købe ind til rottekongen.,"Klasse, jetzt machen wir die Arbeit für den Rattenkönig.",,,Genial. Ahora vamos de compras para el Rey de las Ratas.,,"Loistavaa, hoidamme nyt Rottakuninkaan ostoksia.","Parfait, maintenant on fait du shopping pour le roi des rats.",,Kiváló. A Patkány Király bevásárlói lettünk.,Grandioso. Adesso stiamo facendo shopping con il re dei ratti.,素敵ね。私達がラットキングのために買い物とか。,끝내준다. 이제 시궁쥐 왕의 심부름도 해야 한다니.,Geweldig. Nu zijn we aan het winkelen voor de rattenkoning.,Flott. Nå handler vi til Rottekongen.,Świetnie. Teraz robimy zakupy dla Króla Szczurów.,Que beleza. Agora estamos fazendo comprinhas pro Rato-Rei.,,Grozav. Acum facem cumpărături pentru Regele Șobolanilor.,Классно. Мы идём за покупками для Крысиного короля.,,Toppen. Nu ska vi shoppa för råttkungen.,Harika. Şimdi de Fare Kral için alışveriş yapıyoruz. "I bet this guy smells really bad, too.",TXT_BBSUB_SCRIPT06_D4548_OPENT,〃,,,"Hádám, že tenhle chlápek taky pěkně smrdí.","Jeg vil vædde med, at han også lugter rigtig dårligt.","Ich wette, dieser Kerl riecht auch übel.",,,"Apuesto a que este tipo huele muy mal, también.",,"Lyön vetoa, että tämä tyyppi myös haisee tosi pahalta.","Je parie que ce type sent salement mauvais, aussi.",,"Le merném fogadni, hogy ez a pacák is büdös.",Scommetto che questo tizio ha pure un cattivo odore.,この男も酷い悪臭がすると賭けるわ。,얘도 분명 냄새 하나는 죽여줄 거야.,Ik durf te wedden dat deze kerel ook heel erg stinkt.,Jeg vedder på at denne fyren også lukter fælt.,"Założę się, że ten facet też bardzo śmierdzi.",Aposto que esse cara fede pra caramba também.,,Pun pariu că tipul ăsta mai și pute.,"Уверена, этот парень воняет особенно ужасно.",,Jag slår vad om att den här killen luktar riktigt illa också.,Eminim bu adam da çok kötü kokuyordur. More sewers... Great. Wonder what lives down there.,TXT_BBSUB_SCRIPT06_D9096_YOUHA,〃,,,"Další stoky... skvělý. Zajímalo by mě, co tam dole žije.",Flere kloakker... Fedt. Gad vide hvad der bor dernede.,"Mehr Kanäle, toll... Ich frage mich, was da unten lebt.",,,Más alcantarillas... Genial. Me pregunto qué vivirá ahí abajo.,,Lisää viemäreitä... Mahtavaa. Kukakohan elää täällä alhaalla.,Plus d'égouts.. Youpi. Je me demande qui vit là dedans.,,Még több kanális... kiváló. Mi lakhat odalent?,Altre fogne... fantastico. Mi domando cosa vive qua giù.,更なる下水道...ヤッバ。下に何が住んでるのか不思議に思うわ。,또 하수도라... 끝내준다. 저 아래에 또 뭐가 살고 있을까?,Meer riolering.... Geweldig. Ik vraag me af wat daar beneden leeft.,Mer kloakk... Flott. Lurer på hva som bor der nede.,Więcej kanałów... Świetnie. Ciekawe co tam mieszka.,Mais esgotos... Que legal. O que será que vive lá embaixo?,,Mai multe canale... Grozav. Mă întreb ce-o mai trăi jos aici.,"Снова канализация... Замечательно. Интересно, кого мы там встретим?",,Mer kloaker... Toppen. Undrar vad som bor där nere.,Daha fazla kanalizasyon. Harika. Acaba orada ne yaşıyor? -Dead end here. We'd better keep looking.,TXT_BBSUB_SCRIPT07_D6064_PROGR,MAP07: Computer room.,,,Tudy cesta nevede. Měli bychom se podívat dál.,Det er en blindgyde her. Vi må hellere lede videre.,Sackgasse. Wir sollten es woanders versuchen.,,,Un callejón sin salida. Deberíamos seguir buscando.,,Umpikuja tässä. Parasta jatkaa etsintää.,"Une impasse. Bon, on continue de chercher.",,Zsákutca. Folytatnunk kell a keresést.,Qui c'è un vicolo cieco. Meglio continuare a guardare.,行き止まりだ。私達はまだこの景色を楽しめるわ。,여기가 아닌가 봐. 다른 곳을 찾아봐야겠어.,Doodlopend hier. We kunnen maar beter blijven zoeken.,Blindvei her. Vi får lete videre.,Tu jest ślepy zaułek. Lepiej szukajmy dalej.,Sem saída aqui. Melhor seguirmos procurando.,,Capăt de linie. Mai bine ne mai uităm împrejur.,Тупик. Давай продолжать поиски.,,Det är en återvändsgränd här. Det är bäst att fortsätta leta.,Burası çıkmaz sokak. Aramaya devam etsek iyi olur. +Dead end here. We'd better keep looking.,TXT_BBSUB_SCRIPT07_D6064_PROGR,MAP07: Computer room.,,,Tudy cesta nevede. Měli bychom se podívat dál.,Det er en blindgyde her. Vi må hellere lede videre.,Sackgasse. Wir sollten es woanders versuchen.,,,Un camino sin salida. Deberíamos seguir buscando.,,Umpikuja tässä. Parasta jatkaa etsintää.,"Une impasse. Bon, on continue de chercher.",,Zsákutca. Folytatnunk kell a keresést.,Qui c'è un vicolo cieco. Meglio continuare a guardare.,行き止まりだ。私達はまだこの景色を楽しめるわ。,여기가 아닌가 봐. 다른 곳을 찾아봐야겠어.,Doodlopend hier. We kunnen maar beter blijven zoeken.,Blindvei her. Vi får lete videre.,Tu jest ślepy zaułek. Lepiej szukajmy dalej.,Sem saída aqui. Melhor seguirmos procurando.,,Capăt de linie. Mai bine ne mai uităm împrejur.,Тупик. Давай продолжать поиски.,,Det är en återvändsgränd här. Det är bäst att fortsätta leta.,Burası çıkmaz sokak. Aramaya devam etsek iyi olur. "Onward, my champion. Let's make Mr. Ugly twist and shout. ",TXT_BBSUB_SCRIPT10_D10612_YOUHA,MAP10: Macil (Possible reference to a song of The Beatles).,,,"Kupředu, můj šampióne. Pojďme pana Ošklivu hezky nakopat.","Fremad, min mester. Lad os få hr. Grimme til at vride sig og skrige. -",Und weiter gehts. Lass die Hackfresse zittern!,,,"Adelante, mi campeón. Hagamos al Sr. Feo retorcerse y gritar.",,"Eteenpäin, sotaurhoni. Pannaan Herra Rumilus vääntelehtimään ja huutamaan.",Allons-y mon champion. Allons tordre de douleur Monsieur Sale-geule.,,Gyerünk bajnokom. Ne várakoztassuk meg Ocsmány urat.,"Vai avanti, mio campione. Facciamo agitare e urlare il signor Brutto.",進み出よ、わがチャンピオン。ミスター生塵を捻りつぶしてやれ。,"어서 가자, 나의 영웅. 그 못생긴 녀석에게 본때를 보여주자고.","Voorwaarts, mijn kampioen. Laten we Mr Lelijk laten draaien en schreeuwen.","Fremad, min mester. La oss få herr Stygg til å vri seg og rope.","Naprzód, mój mistrzu. Sprawmy, by Pan Brzydki skręcał się i krzyczał. +",Und weiter gehts. Lass die Hackfresse zittern!,,,"Adelante, campeón. Hagamos que el Sr. Feo se retuerza y grite.",,"Eteenpäin, sotaurhoni. Pannaan Herra Rumilus vääntelehtimään ja huutamaan.",Allons-y mon champion. Allons tordre de douleur Monsieur Sale-geule.,,Gyerünk bajnokom. Ne várakoztassuk meg Ocsmány urat.,"Vai avanti, mio campione. Facciamo agitare e urlare il signor Brutto.",進み出よ、わがチャンピオン。ミスター生塵を捻りつぶしてやれ。,"어서 가자, 나의 영웅. 그 못생긴 녀석에게 본때를 보여주자고.","Voorwaarts, mijn kampioen. Laten we Mr Lelijk laten draaien en schreeuwen.","Fremad, min mester. La oss få herr Stygg til å vri seg og rope.","Naprzód, mój mistrzu. Sprawmy, by Pan Brzydki skręcał się i krzyczał. ","Vai em frente, meu campeão. Vamos fazer o Sr. Feioso soltar a franga.",,"Drept în față, campionul meu. Să-l facem pe dl. Urât să țipe.","Вперёд, мой защитник. Заставим Г-на Урода сплясать и спеть.",,"Framåt, min mästare. Låt oss få herr Ful att vrida sig och skrika.","İleri, şampiyonum. Bay Çirkin'i döndürelim ve bağırtalım. " "Biomechanical? Well, that explains their seemingly endless supply of troops! And their bad attitude.