From 990cf3eafccc74e7caad5ede12f7cfb449c070ff Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Thu, 9 Nov 2023 18:48:59 +0100 Subject: [PATCH] gave translations a dedicated scripted type. This is needed for implementing reliable serialization of custom translations. As long as they are merely ints they cannot be restored on loading a savegame because the serialization code does not know that these variables are special. --- source/common/engine/namedef.h | 2 + source/common/engine/palettecontainer.cpp | 5 + source/common/engine/palettecontainer.h | 40 +++++ source/common/engine/sc_man_scanner.re | 2 +- source/common/engine/serializer.cpp | 15 ++ source/common/engine/serializer.h | 2 + source/common/scripting/backend/codegen.cpp | 155 +++++++++++++++++- source/common/scripting/backend/codegen.h | 14 ++ source/common/scripting/core/types.cpp | 45 +++++ source/common/scripting/core/types.h | 10 ++ .../common/scripting/frontend/zcc_compile.cpp | 6 + .../common/scripting/interface/vmnatives.cpp | 35 ++++ source/core/version.h | 4 +- wadsrc/static/zscript.txt | 10 +- wadsrc/static/zscript/alt_hud.zs | 6 +- wadsrc/static/zscript/engine/base.zs | 11 +- wadsrc/static/zscript/engine/screenjob.zs | 14 +- wadsrc/static/zscript/engine/service.zs | 155 ++++++++++++++++++ wadsrc/static/zscript/engine/ui/menu/menu.zs | 12 +- .../zscript/engine/ui/menu/optionmenuitems.zs | 8 +- .../engine/ui/statusbar/statusbarcore.zs | 10 +- wadsrc/static/zscript/games/blood/ui/menu.zs | 2 +- wadsrc/static/zscript/games/blood/ui/sbar.zs | 19 ++- wadsrc/static/zscript/games/duke/ui/menu.zs | 2 +- .../static/zscript/games/duke/ui/screens.zs | 10 +- .../zscript/games/exhumed/ui/screens.zs | 2 +- wadsrc/static/zscript/games/sw/ui/screens.zs | 2 +- 27 files changed, 541 insertions(+), 57 deletions(-) create mode 100644 wadsrc/static/zscript/engine/service.zs diff --git a/source/common/engine/namedef.h b/source/common/engine/namedef.h index 70080a965..e76186f21 100644 --- a/source/common/engine/namedef.h +++ b/source/common/engine/namedef.h @@ -182,6 +182,7 @@ xx(Voidptr) xx(StateLabel) xx(SpriteID) xx(TextureID) +xx(TranslationID) xx(Overlay) xx(IsValid) xx(IsNull) @@ -272,6 +273,7 @@ xx(BuiltinFRandom) xx(BuiltinNameToClass) xx(BuiltinClassCast) xx(BuiltinFunctionPtrCast) +xx(BuiltinFindTranslation) xx(ScreenJobRunner) xx(Action) diff --git a/source/common/engine/palettecontainer.cpp b/source/common/engine/palettecontainer.cpp index 800dc31c6..40c73e4a4 100644 --- a/source/common/engine/palettecontainer.cpp +++ b/source/common/engine/palettecontainer.cpp @@ -820,4 +820,9 @@ bool FRemapTable::AddColors(int start, int count, const uint8_t*colors, int tran } +// placeholder +int R_FindCustomTranslation(FName name) +{ + return -1; +} diff --git a/source/common/engine/palettecontainer.h b/source/common/engine/palettecontainer.h index a761ae33c..96413fe1f 100644 --- a/source/common/engine/palettecontainer.h +++ b/source/common/engine/palettecontainer.h @@ -42,6 +42,46 @@ struct FRemapTable private: }; + +struct FTranslationID +{ +public: + FTranslationID() = default; + +private: + constexpr FTranslationID(int id) : ID(id) + { + } +public: + static constexpr FTranslationID fromInt(int i) + { + return FTranslationID(i); + } + FTranslationID(const FTranslationID& other) = default; + FTranslationID& operator=(const FTranslationID& other) = default; + bool operator !=(FTranslationID other) const + { + return ID != other.ID; + } + bool operator ==(FTranslationID other) const + { + return ID == other.ID; + } + bool operator ==(int other) const = delete; + bool operator !=(int other) const = delete; + constexpr int index() const + { + return ID; + } + constexpr bool isvalid() const + { + return ID > 0; + } +private: + + int ID; +}; + // A class that initializes unusued pointers to NULL. This is used so that when // the TAutoGrowArray below is expanded, the new elements will be NULLed. class FRemapTablePtr diff --git a/source/common/engine/sc_man_scanner.re b/source/common/engine/sc_man_scanner.re index 8bca2ccc6..5af7e62f9 100644 --- a/source/common/engine/sc_man_scanner.re +++ b/source/common/engine/sc_man_scanner.re @@ -167,7 +167,7 @@ std2: 'vector2' { RET(TK_Vector2); } 'vector3' { RET(TK_Vector3); } 'map' { RET(TK_Map); } - 'mapiterator' { RET(TK_MapIterator); } + 'mapiterator' { RET(ParseVersion >= MakeVersion(4, 10, 0)? TK_MapIterator : TK_Identifier); } 'array' { RET(TK_Array); } 'function' { RET(ParseVersion >= MakeVersion(4, 12, 0)? TK_FunctionType : TK_Identifier); } 'in' { RET(TK_In); } diff --git a/source/common/engine/serializer.cpp b/source/common/engine/serializer.cpp index 34fd6956b..71a0031b6 100644 --- a/source/common/engine/serializer.cpp +++ b/source/common/engine/serializer.cpp @@ -1196,6 +1196,21 @@ FSerializer &Serialize(FSerializer &arc, const char *key, FTextureID &value, FTe return arc; } +//========================================================================== +// +// +// +//========================================================================== + +FSerializer& Serialize(FSerializer& arc, const char* key, FTranslationID& value, FTranslationID* defval) +{ + int v = value.index(); + int* defv = (int*)defval; + Serialize(arc, key, v, defv); + value = FTranslationID::fromInt(v); + return arc; +} + //========================================================================== // // This never uses defval and instead uses 'null' as default diff --git a/source/common/engine/serializer.h b/source/common/engine/serializer.h index 9328f2f22..ec1d10949 100644 --- a/source/common/engine/serializer.h +++ b/source/common/engine/serializer.h @@ -20,6 +20,7 @@ class FSoundID; union FRenderStyle; class DObject; class FTextureID; +struct FTranslationID; inline bool nullcmp(const void *buffer, size_t length) { @@ -240,6 +241,7 @@ FSerializer &Serialize(FSerializer &arc, const char *key, FSoundID &sid, FSoundI FSerializer &Serialize(FSerializer &arc, const char *key, FString &sid, FString *def); FSerializer &Serialize(FSerializer &arc, const char *key, NumericValue &sid, NumericValue *def); FSerializer &Serialize(FSerializer &arc, const char *key, struct ModelOverride &sid, struct ModelOverride *def); +FSerializer& Serialize(FSerializer& arc, const char* key, FTranslationID& value, FTranslationID* defval); void SerializeFunctionPointer(FSerializer &arc, const char *key, FunctionPointerValue *&p); diff --git a/source/common/scripting/backend/codegen.cpp b/source/common/scripting/backend/codegen.cpp index 3ff45c335..7f3a410ca 100644 --- a/source/common/scripting/backend/codegen.cpp +++ b/source/common/scripting/backend/codegen.cpp @@ -43,11 +43,13 @@ #include "texturemanager.h" #include "m_random.h" #include "v_font.h" +#include "palettecontainer.h" extern FRandom pr_exrandom; FMemArena FxAlloc(65536); CompileEnvironment compileEnvironment; +int R_FindCustomTranslation(FName name); struct FLOP { @@ -161,6 +163,12 @@ void FCompileContext::CheckReturn(PPrototype *proto, FScriptPosition &pos) PType* expected = ReturnProto->ReturnTypes[i]; PType* actual = proto->ReturnTypes[i]; if (swapped) std::swap(expected, actual); + // this must pass for older ZScripts. + if (Version < MakeVersion(4, 12, 0)) + { + if (expected == TypeTranslationID) expected = TypeSInt32; + if (actual == TypeTranslationID) actual = TypeSInt32; + } if (expected != actual && !AreCompatiblePointerTypes(expected, actual)) { // Incompatible @@ -993,6 +1001,19 @@ FxExpression *FxIntCast::Resolve(FCompileContext &ctx) if (basex->ValueType->GetRegType() == REGT_INT) { + if (basex->ValueType == TypeTranslationID) + { + // translation IDs must be entirely incompatible with ints, not even allowing an explicit conversion, + // but since the type was only introduced in version 4.12, older ZScript versions must allow this conversion. + if (ctx.Version < MakeVersion(4, 12, 0)) + { + FxExpression* x = basex; + x->ValueType = ValueType; + basex = nullptr; + delete this; + return x; + } + } if (basex->ValueType->isNumeric() || Explicit) // names can be converted to int, but only with an explicit type cast. { FxExpression *x = basex; @@ -1006,7 +1027,7 @@ FxExpression *FxIntCast::Resolve(FCompileContext &ctx) // Ugh. This should abort, but too many mods fell into this logic hole somewhere, so this serious error needs to be reduced to a warning. :( // At least in ZScript, MSG_OPTERROR always means to report an error, not a warning so the problem only exists in DECORATE. if (!basex->isConstant()) - ScriptPosition.Message(MSG_OPTERROR, "Numeric type expected, got a name"); + ScriptPosition.Message(MSG_OPTERROR, "Numeric type expected, got a %s", basex->ValueType->DescriptiveName()); else ScriptPosition.Message(MSG_OPTERROR, "Numeric type expected, got \"%s\"", static_cast(basex)->GetValue().GetName().GetChars()); FxExpression * x = new FxConstant(0, ScriptPosition); delete this; @@ -1127,7 +1148,8 @@ FxExpression *FxFloatCast::Resolve(FCompileContext &ctx) { // Ugh. This should abort, but too many mods fell into this logic hole somewhere, so this seroious error needs to be reduced to a warning. :( // At least in ZScript, MSG_OPTERROR always means to report an error, not a warning so the problem only exists in DECORATE. - if (!basex->isConstant()) ScriptPosition.Message(MSG_OPTERROR, "Numeric type expected, got a name"); + if (!basex->isConstant()) + ScriptPosition.Message(MSG_OPTERROR, "Numeric type expected, got a %s", basex->ValueType->DescriptiveName()); else ScriptPosition.Message(MSG_OPTERROR, "Numeric type expected, got \"%s\"", static_cast(basex)->GetValue().GetName().GetChars()); FxExpression *x = new FxConstant(0.0, ScriptPosition); delete this; @@ -1534,6 +1556,107 @@ ExpEmit FxSoundCast::Emit(VMFunctionBuilder *build) return to; } +//========================================================================== +// +// +// +//========================================================================== + +FxTranslationCast::FxTranslationCast(FxExpression* x) + : FxExpression(EFX_TranslationCast, x->ScriptPosition) +{ + basex = x; + ValueType = TypeTranslationID; +} + +//========================================================================== +// +// +// +//========================================================================== + +FxTranslationCast::~FxTranslationCast() +{ + SAFE_DELETE(basex); +} + +//========================================================================== +// +// +// +//========================================================================== + +FxExpression* FxTranslationCast::Resolve(FCompileContext& ctx) +{ + CHECKRESOLVED(); + SAFE_RESOLVE(basex, ctx); + + if (basex->ValueType->isInt()) + { + // 0 is a valid constant for translations, meaning 'no translation at all'. note that this conversion ONLY allows a constant! + if (basex->isConstant() && static_cast(basex)->GetValue().GetInt() == 0) + { + FxExpression* x = basex; + x->ValueType = TypeTranslationID; + basex = nullptr; + delete this; + return x; + } + if (ctx.Version < MakeVersion(4, 12, 0)) + { + // only allow this conversion as a fallback + FxExpression* x = basex; + x->ValueType = TypeTranslationID; + basex = nullptr; + delete this; + return x; + } + } + else if (basex->ValueType == TypeString || basex->ValueType == TypeName) + { + if (basex->isConstant()) + { + ExpVal constval = static_cast(basex)->GetValue(); + FxExpression* x = new FxConstant(R_FindCustomTranslation(constval.GetName()), ScriptPosition); + x->ValueType = TypeTranslationID; + delete this; + return x; + } + else if (basex->ValueType == TypeString) + { + basex = new FxNameCast(basex, true); + basex = basex->Resolve(ctx); + } + return this; + } + ScriptPosition.Message(MSG_ERROR, "Cannot convert to translation ID"); + delete this; + return nullptr; +} + +//========================================================================== +// +// +// +//========================================================================== + +ExpEmit FxTranslationCast::Emit(VMFunctionBuilder* build) +{ + ExpEmit to(build, REGT_POINTER); + + VMFunction* callfunc; + auto sym = FindBuiltinFunction(NAME_BuiltinFindTranslation); + + assert(sym); + callfunc = sym->Variants[0].Implementation; + + FunctionCallEmitter emitters(callfunc); + emitters.AddParameter(build, basex); + emitters.AddReturn(REGT_INT); + return emitters.EmitCall(build); +} + + //========================================================================== // // @@ -1766,6 +1889,14 @@ FxExpression *FxTypeCast::Resolve(FCompileContext &ctx) delete this; return x; } + else if (ValueType == TypeTranslationID) + { + FxExpression* x = new FxTranslationCast(basex); + x = x->Resolve(ctx); + basex = nullptr; + delete this; + return x; + } else if (ValueType == TypeColor) { FxExpression *x = new FxColorCast(basex); @@ -4639,6 +4770,7 @@ ExpEmit FxConcat::Emit(VMFunctionBuilder *build) else if (left->ValueType == TypeColor) cast = CAST_Co2S; else if (left->ValueType == TypeSpriteID) cast = CAST_SID2S; else if (left->ValueType == TypeTextureID) cast = CAST_TID2S; + else if (left->ValueType == TypeTranslationID) cast = CAST_U2S; else if (op1.RegType == REGT_POINTER) cast = CAST_P2S; else if (op1.RegType == REGT_INT) cast = CAST_I2S; else assert(false && "Bad type for string concatenation"); @@ -4672,6 +4804,7 @@ ExpEmit FxConcat::Emit(VMFunctionBuilder *build) else if (right->ValueType == TypeColor) cast = CAST_Co2S; else if (right->ValueType == TypeSpriteID) cast = CAST_SID2S; else if (right->ValueType == TypeTextureID) cast = CAST_TID2S; + else if (right->ValueType == TypeTranslationID) cast = CAST_U2S; else if (op2.RegType == REGT_POINTER) cast = CAST_P2S; else if (op2.RegType == REGT_INT) cast = CAST_I2S; else assert(false && "Bad type for string concatenation"); @@ -5143,11 +5276,24 @@ FxExpression *FxConditional::Resolve(FCompileContext& ctx) ValueType = truex->ValueType; else if (falsex->IsPointer() && truex->ValueType == TypeNullPtr) ValueType = falsex->ValueType; + // translation IDs need a bit of glue for compatibility and the 0 literal. + else if (truex->IsInteger() && falsex->ValueType == TypeTranslationID) + { + truex = new FxTranslationCast(truex); + truex = truex->Resolve(ctx); + ValueType = ctx.Version < MakeVersion(4, 12, 0)? TypeSInt32 : TypeTranslationID; + } + else if (falsex->IsInteger() && truex->ValueType == TypeTranslationID) + { + falsex = new FxTranslationCast(falsex); + falsex = falsex->Resolve(ctx); + ValueType = ctx.Version < MakeVersion(4, 12, 0) ? TypeSInt32 : TypeTranslationID; + } + else ValueType = TypeVoid; - //else if (truex->ValueType != falsex->ValueType) - if (ValueType->GetRegType() == REGT_NIL) + if (truex == nullptr || falsex == nullptr || ValueType->GetRegType() == REGT_NIL) { ScriptPosition.Message(MSG_ERROR, "Incompatible types for ?: operator"); delete this; @@ -8279,6 +8425,7 @@ FxExpression *FxFunctionCall::Resolve(FCompileContext& ctx) MethodName == NAME_Name ? TypeName : MethodName == NAME_SpriteID ? TypeSpriteID : MethodName == NAME_TextureID ? TypeTextureID : + MethodName == NAME_TranslationID ? TypeTranslationID : MethodName == NAME_State ? TypeState : MethodName == NAME_Color ? TypeColor : (PType*)TypeSound; diff --git a/source/common/scripting/backend/codegen.h b/source/common/scripting/backend/codegen.h index 6aff3b75d..e1ad37856 100644 --- a/source/common/scripting/backend/codegen.h +++ b/source/common/scripting/backend/codegen.h @@ -232,6 +232,7 @@ enum EFxType EFX_StringCast, EFX_ColorCast, EFX_SoundCast, + EFX_TranslationCast, EFX_TypeCast, EFX_PlusSign, EFX_MinusSign, @@ -715,6 +716,19 @@ public: ExpEmit Emit(VMFunctionBuilder *build); }; +class FxTranslationCast : public FxExpression +{ + FxExpression* basex; + +public: + + FxTranslationCast(FxExpression* x); + ~FxTranslationCast(); + FxExpression* Resolve(FCompileContext&); + + ExpEmit Emit(VMFunctionBuilder* build); +}; + class FxFontCast : public FxExpression { FxExpression *basex; diff --git a/source/common/scripting/core/types.cpp b/source/common/scripting/core/types.cpp index efe4dfec5..b9949a5ea 100644 --- a/source/common/scripting/core/types.cpp +++ b/source/common/scripting/core/types.cpp @@ -41,6 +41,7 @@ #include "printf.h" #include "textureid.h" #include "maps.h" +#include "palettecontainer.h" FTypeTable TypeTable; @@ -58,6 +59,7 @@ PName *TypeName; PSound *TypeSound; PColor *TypeColor; PTextureID *TypeTextureID; +PTranslationID* TypeTranslationID; PSpriteID *TypeSpriteID; PStatePointer *TypeState; PPointer *TypeFont; @@ -322,6 +324,7 @@ void PType::StaticInit() TypeTable.AddType(TypeNullPtr = new PPointer, NAME_Pointer); TypeTable.AddType(TypeSpriteID = new PSpriteID, NAME_SpriteID); TypeTable.AddType(TypeTextureID = new PTextureID, NAME_TextureID); + TypeTable.AddType(TypeTranslationID = new PTranslationID, NAME_TranslationID); TypeVoidPtr = NewPointer(TypeVoid, false); TypeRawFunction = new PPointer; @@ -1321,6 +1324,48 @@ bool PTextureID::ReadValue(FSerializer &ar, const char *key, void *addr) const return true; } +/* PTranslationID ******************************************************************/ + +//========================================================================== +// +// PTranslationID Default Constructor +// +//========================================================================== + +PTranslationID::PTranslationID() + : PInt(sizeof(FTranslationID), true, false) +{ + mDescriptiveName = "TranslationID"; + Flags |= TYPE_IntNotInt; + static_assert(sizeof(FTranslationID) == alignof(FTranslationID), "TranslationID not properly aligned"); +} + +//========================================================================== +// +// PTranslationID :: WriteValue +// +//========================================================================== + +void PTranslationID::WriteValue(FSerializer& ar, const char* key, const void* addr) const +{ + FTranslationID val = *(FTranslationID*)addr; + ar(key, val); +} + +//========================================================================== +// +// PTranslationID :: ReadValue +// +//========================================================================== + +bool PTranslationID::ReadValue(FSerializer& ar, const char* key, void* addr) const +{ + FTranslationID val; + ar(key, val); + *(FTranslationID*)addr = val; + return true; +} + /* PSound *****************************************************************/ //========================================================================== diff --git a/source/common/scripting/core/types.h b/source/common/scripting/core/types.h index e24bcd38b..e892a8b12 100644 --- a/source/common/scripting/core/types.h +++ b/source/common/scripting/core/types.h @@ -383,6 +383,15 @@ public: bool ReadValue(FSerializer &ar, const char *key, void *addr) const override; }; +class PTranslationID : public PInt +{ +public: + PTranslationID(); + + void WriteValue(FSerializer& ar, const char* key, const void* addr) const override; + bool ReadValue(FSerializer& ar, const char* key, void* addr) const override; +}; + class PColor : public PInt { public: @@ -712,6 +721,7 @@ extern PName *TypeName; extern PSound *TypeSound; extern PColor *TypeColor; extern PTextureID *TypeTextureID; +extern PTranslationID* TypeTranslationID; extern PSpriteID *TypeSpriteID; extern PStruct* TypeVector2; extern PStruct* TypeVector3; diff --git a/source/common/scripting/frontend/zcc_compile.cpp b/source/common/scripting/frontend/zcc_compile.cpp index c29a580a3..d7a1a54b7 100644 --- a/source/common/scripting/frontend/zcc_compile.cpp +++ b/source/common/scripting/frontend/zcc_compile.cpp @@ -1886,6 +1886,12 @@ PType *ZCCCompiler::DetermineType(PType *outertype, ZCC_TreeNode *field, FName n retval = TypeTextureID; break; + case NAME_TranslationID: + retval = TypeTranslationID; + break; + + + default: retval = ResolveUserType(btype, btype->UserType, outertype ? &outertype->Symbols : nullptr, false); break; diff --git a/source/common/scripting/interface/vmnatives.cpp b/source/common/scripting/interface/vmnatives.cpp index 5ab3051d4..6242d556b 100644 --- a/source/common/scripting/interface/vmnatives.cpp +++ b/source/common/scripting/interface/vmnatives.cpp @@ -1372,3 +1372,38 @@ DEFINE_ACTION_FUNCTION_NATIVE(DObject, FindFunction, FindFunctionPointer) ACTION_RETURN_POINTER(FindFunctionPointer(cls, fn.GetIndex())); } +int R_FindCustomTranslation(FName name); + +static int ZFindTranslation(int intname) +{ + return R_FindCustomTranslation(ENamedName(intname)); +} + +static int MakeTransID(int g, int s) +{ + return TRANSLATION(g, s); +} + +DEFINE_ACTION_FUNCTION_NATIVE(_Translation, GetID, ZFindTranslation) +{ + PARAM_PROLOGUE; + PARAM_INT(t); + ACTION_RETURN_INT(ZFindTranslation(t)); +} + +// same as above for the compiler which needs a class to look this up. +DEFINE_ACTION_FUNCTION_NATIVE(DObject, BuiltinFindTranslation, ZFindTranslation) +{ + PARAM_PROLOGUE; + PARAM_INT(t); + ACTION_RETURN_INT(ZFindTranslation(t)); +} + +DEFINE_ACTION_FUNCTION_NATIVE(_Translation, MakeID, MakeTransID) +{ + PARAM_PROLOGUE; + PARAM_INT(g); + PARAM_INT(t); + ACTION_RETURN_INT(MakeTransID(g, t)); +} + diff --git a/source/core/version.h b/source/core/version.h index 98c186eca..2430e9fb2 100644 --- a/source/core/version.h +++ b/source/core/version.h @@ -49,8 +49,8 @@ const char *GetVersionString(); #define RC_PRODUCTVERSION2 VERSIONSTR // These are for content versioning. #define VER_MAJOR 4 -#define VER_MINOR 11 -#define VER_REVISION 100 +#define VER_MINOR 12 +#define VER_REVISION 0 #define ENG_MAJOR 1 #define ENG_MINOR 7 diff --git a/wadsrc/static/zscript.txt b/wadsrc/static/zscript.txt index aab2ff7b3..87554aa49 100644 --- a/wadsrc/static/zscript.txt +++ b/wadsrc/static/zscript.txt @@ -1,10 +1,14 @@ -version "4.11.100" +version "4.12" + +// Generic engine code #include "zscript/engine/base.zs" #include "zscript/engine/dynarrays.zs" -#include "zscript/engine/inputevents.zs" +#include "zscript/engine/maps.zs" #include "zscript/engine/dictionary.zs" -#include "zscript/engine/screenjob.zs" +#include "zscript/engine/inputevents.zs" +#include "zscript/engine/service.zs" #include "zscript/engine/ppshader.zs" +#include "zscript/engine/screenjob.zs" #include "zscript/engine/ui/menu/colorpickermenu.zs" #include "zscript/engine/ui/menu/joystickmenu.zs" diff --git a/wadsrc/static/zscript/alt_hud.zs b/wadsrc/static/zscript/alt_hud.zs index d7060a6ff..25bccc490 100644 --- a/wadsrc/static/zscript/alt_hud.zs +++ b/wadsrc/static/zscript/alt_hud.zs @@ -53,7 +53,7 @@ struct HudStats int ammoselect; Array keyicons; - Array keytranslations; + Array keytranslations; Array inventoryicons; Array inventoryamounts; // negative values can be used for special states (-1: "ON", -2: "OFF", -3: "AUTO") @@ -128,7 +128,7 @@ class AltHud ui // //--------------------------------------------------------------------------- - void DrawImageToBox(TextureID tex, int x, int y, int w, int h, double trans = 0.75, bool animate = false, int translation = 0) + void DrawImageToBox(TextureID tex, int x, int y, int w, int h, double trans = 0.75, bool animate = false, TranslationID translation = 0) { double scale1, scale2; @@ -328,7 +328,7 @@ class AltHud ui if (icon.isValid()) { - int trans = 0; + TranslationID trans = 0; if (currentStats.keytranslations.Size()) trans = currentStats.keytranslations[keyindex]; DrawImageToBox(icon, x, y, 8, 10, 0.75, false, trans); return true; diff --git a/wadsrc/static/zscript/engine/base.zs b/wadsrc/static/zscript/engine/base.zs index 751477a33..c79c7abaa 100644 --- a/wadsrc/static/zscript/engine/base.zs +++ b/wadsrc/static/zscript/engine/base.zs @@ -184,6 +184,7 @@ struct Vector3 struct _ native // These are the global variables, the struct is only here to avoid extending the parser for this. { native readonly Array AllClasses; + native internal readonly Map AllServices; native readonly bool multiplayer; native @KeyBindings Bindings; native @KeyBindings AutomapBindings; @@ -752,6 +753,7 @@ class Object native private native static void BuiltinRandomSeed(voidptr rng, int seed); private native static Class BuiltinNameToClass(Name nm, Class filter); private native static Object BuiltinClassCast(Object inptr, Class test); + private native static Function BuiltinFunctionPtrCast(Function inptr, voidptr newtype); native static uint MSTime(); native static double MSTimeF(); @@ -922,10 +924,11 @@ struct StringStruct native struct Translation version("2.4") { - static int MakeID(int group, int num) - { - return (group << 16) + num; - } + Color colors[256]; + + //native TranslationID AddTranslation(); // this still needs work. + native static TranslationID MakeID(int group, int num); + native static TranslationID GetID(Name transname); } // Convenient way to attach functions to Quat diff --git a/wadsrc/static/zscript/engine/screenjob.zs b/wadsrc/static/zscript/engine/screenjob.zs index a65516a48..5dd2018eb 100644 --- a/wadsrc/static/zscript/engine/screenjob.zs +++ b/wadsrc/static/zscript/engine/screenjob.zs @@ -141,12 +141,12 @@ class BlackScreen : ScreenJob class ImageScreen : SkippableScreenJob { int tilenum; - int trans; + TranslationID trans; int waittime; // in ms. bool cleared; TextureID texid; - ScreenJob Init(TextureID tile, int fade = fadein | fadeout, int wait = 3000, int translation = 0) + ScreenJob Init(TextureID tile, int fade = fadein | fadeout, int wait = 3000, TranslationID translation = 0) { Super.Init(fade); waittime = wait; @@ -156,7 +156,7 @@ class ImageScreen : SkippableScreenJob return self; } - ScreenJob InitNamed(String tex, int fade = fadein | fadeout, int wait = 3000, int translation = 0) + ScreenJob InitNamed(String tex, int fade = fadein | fadeout, int wait = 3000, TranslationID translation = 0) { Super.Init(fade); waittime = wait; @@ -166,12 +166,12 @@ class ImageScreen : SkippableScreenJob return self; } - static ScreenJob Create(TextureID tile, int fade = fadein | fadeout, int wait = 3000, int translation = 0) + static ScreenJob Create(TextureID tile, int fade = fadein | fadeout, int wait = 3000, TranslationID translation = 0) { return new("ImageScreen").Init(tile, fade, wait, translation); } - static ScreenJob CreateNamed(String tex, int fade = fadein | fadeout, int wait = 3000, int translation = 0) + static ScreenJob CreateNamed(String tex, int fade = fadein | fadeout, int wait = 3000, TranslationID translation = 0) { return new("ImageScreen").InitNamed(tex, fade, wait, translation); } @@ -622,13 +622,13 @@ class TextOverlay { int nHeight; double nCrawlY; - int palette; + TranslationID palette; int crange; bool drawclean; BrokenLines screentext; Font myfont; - void Init(String text, int cr = Font.CR_NATIVEPAL, int pal = 0, bool clean = false) + void Init(String text, int cr = Font.CR_NATIVEPAL, TranslationID pal = 0, bool clean = false) { myfont = SmallFont; // todo screentext = myfont.BreakLines(StringTable.Localize(text), 320); diff --git a/wadsrc/static/zscript/engine/service.zs b/wadsrc/static/zscript/engine/service.zs new file mode 100644 index 000000000..6df17f126 --- /dev/null +++ b/wadsrc/static/zscript/engine/service.zs @@ -0,0 +1,155 @@ +/** + * This is Service interface. + */ +class Service abstract +{ + deprecated("4.6.1", "Use GetString() instead") virtual play String Get(String request) + { + return ""; + } + + deprecated("4.6.1", "Use GetStringUI() instead") virtual ui String UiGet(String request) + { + return ""; + } + + // Play variants + virtual play String GetString(String request, string stringArg = "", int intArg = 0, double doubleArg = 0, Object objectArg = null, Name nameArg = '') + { + return ""; + } + + virtual play int GetInt(String request, string stringArg = "", int intArg = 0, double doubleArg = 0, Object objectArg = null, Name nameArg = '') + { + return 0; + } + + virtual play double GetDouble(String request, string stringArg = "", int intArg = 0, double doubleArg = 0, Object objectArg = null, Name nameArg = '') + { + return 0.0; + } + + virtual play Object GetObject(String request, string stringArg = "", int intArg = 0, double doubleArg = 0, Object objectArg = null, Name nameArg = '') + { + return null; + } + + virtual play Name GetName(String request, string stringArg = "", int intArg = 0, double doubleArg = 0, Object objectArg = null, Name nameArg = '') + { + return ''; + } + + + // UI variants + virtual ui String GetStringUI(String request, string stringArg = "", int intArg = 0, double doubleArg = 0, Object objectArg = null, Name nameArg = '') + { + return ""; + } + + virtual ui int GetIntUI(String request, string stringArg = "", int intArg = 0, double doubleArg = 0, Object objectArg = null, Name nameArg = '') + { + return 0; + } + + virtual ui double GetDoubleUI(String request, string stringArg = "", int intArg = 0, double doubleArg = 0, Object objectArg = null, Name nameArg = '') + { + return 0.0; + } + + virtual ui Object GetObjectUI(String request, string stringArg = "", int intArg = 0, double doubleArg = 0, Object objectArg = null, Name nameArg = '') + { + return null; + } + + virtual ui Name GetNameUI(String request, string stringArg = "", int intArg = 0, double doubleArg = 0, Object objectArg = null, Name nameArg = '') + { + return ''; + } + + // data/clearscope variants + virtual clearscope String GetStringData(String request, string stringArg = "", int intArg = 0, double doubleArg = 0, Object objectArg = null, Name nameArg = '') + { + return ""; + } + + virtual clearscope int GetIntData(String request, string stringArg = "", int intArg = 0, double doubleArg = 0, Object objectArg = null, Name nameArg = '') + { + return 0; + } + + virtual clearscope double GetDoubleData(String request, string stringArg = "", int intArg = 0, double doubleArg = 0, Object objectArg = null, Name nameArg = '') + { + return 0.0; + } + + virtual clearscope Object GetObjectData(String request, string stringArg = "", int intArg = 0, double doubleArg = 0, Object objectArg = null, Name nameArg = '') + { + return null; + } + + virtual clearscope Name GetNameData(String request, string stringArg = "", int intArg = 0, double doubleArg = 0, Object objectArg = null, Name nameArg = '') + { + return ''; + } + + static Service Find(class serviceName){ + return AllServices.GetIfExists(serviceName.GetClassName()); + } +} + +/** + * Use this class to find and iterate over services. + * + * Example usage: + * + * @code + * ServiceIterator i = ServiceIterator.Find("MyService"); + * + * Service s; + * while (s = i.Next()) + * { + * String request = ... + * String answer = s.Get(request); + * ... + * } + * @endcode + * + * If no services are found, the all calls to Next() will return NULL. + */ +class ServiceIterator +{ + /** + * Creates a Service iterator for a service name. It will iterate over all existing Services + * with names that match @a serviceName or have it as a part of their names. + * + * Matching is case-independent. + * + * @param serviceName class name of service to find. + */ + static ServiceIterator Find(String serviceName) + { + let result = new("ServiceIterator"); + result.mServiceName = serviceName.MakeLower(); + result.it.Init(AllServices); + return result; + } + + /** + * Gets the service and advances the iterator. + * + * @returns service instance, or NULL if no more services found. + */ + Service Next() + { + while(it.Next()) + { + String cName = it.GetKey(); + if(cName.MakeLower().IndexOf(mServiceName) != -1) + return it.GetValue(); + } + return null; + } + + private MapIterator it; + private String mServiceName; +} diff --git a/wadsrc/static/zscript/engine/ui/menu/menu.zs b/wadsrc/static/zscript/engine/ui/menu/menu.zs index 1c1877041..488629b5d 100644 --- a/wadsrc/static/zscript/engine/ui/menu/menu.zs +++ b/wadsrc/static/zscript/engine/ui/menu/menu.zs @@ -36,7 +36,7 @@ struct KeyBindings native version("2.4") { native static String NameKeys(int k1, int k2); - native static String NameAllKeys(array list); + native static String NameAllKeys(array list, bool colors = true); native int, int GetKeysForCommand(String cmd); native void GetAllKeysForCommand(out array list, String cmd); @@ -349,16 +349,16 @@ class Menu : Object native ui version("2.4") return OptionFont().GetHeight(); } - static int OptionWidth(String s) + static int OptionWidth(String s, bool localize = true) { - return OptionFont().StringWidth(s); + return OptionFont().StringWidth(s, localize); } - static void DrawOptionText(int x, int y, int color, String text, bool grayed = false) + static void DrawOptionText(int x, int y, int color, String text, bool grayed = false, bool localize = true) { - String label = Stringtable.Localize(text); + String label = localize ? Stringtable.Localize(text) : text; int overlay = grayed? Color(96,48,0,0) : 0; - screen.DrawText (OptionFont(), color, x, y, text, DTA_CleanNoMove_1, true, DTA_ColorOverlay, overlay); + screen.DrawText (OptionFont(), color, x, y, text, DTA_CleanNoMove_1, true, DTA_ColorOverlay, overlay, DTA_Localize, localize); } diff --git a/wadsrc/static/zscript/engine/ui/menu/optionmenuitems.zs b/wadsrc/static/zscript/engine/ui/menu/optionmenuitems.zs index 4733d5b3b..898dd1fac 100644 --- a/wadsrc/static/zscript/engine/ui/menu/optionmenuitems.zs +++ b/wadsrc/static/zscript/engine/ui/menu/optionmenuitems.zs @@ -61,9 +61,9 @@ class OptionMenuItem : MenuItemBase return x; } - protected void drawValue(int indent, int y, int color, String text, bool grayed = false) + protected void drawValue(int indent, int y, int color, String text, bool grayed = false, bool localize = true) { - Menu.DrawOptionText(indent + CursorSpace(), y, color, text, grayed); + Menu.DrawOptionText(indent + CursorSpace(), y, color, text, grayed, localize); } @@ -999,7 +999,7 @@ class OptionMenuFieldBase : OptionMenuItem { bool grayed = mGrayCheck != null && !mGrayCheck.GetInt(); drawLabel(indent, y, selected ? OptionMenuSettings.mFontColorSelection : OptionMenuSettings.mFontColor, grayed); - drawValue(indent, y, OptionMenuSettings.mFontColorValue, Represent(), grayed); + drawValue(indent, y, OptionMenuSettings.mFontColorValue, Represent(), grayed, false); return indent; } @@ -1068,7 +1068,7 @@ class OptionMenuItemTextField : OptionMenuFieldBase { // reposition the text so that the cursor is visible when in entering mode. String text = Represent(); - int tlen = Menu.OptionWidth(text) * CleanXfac_1; + int tlen = Menu.OptionWidth(text, false) * CleanXfac_1; int newindent = screen.GetWidth() - tlen - CursorSpace(); if (newindent < indent) indent = newindent; } diff --git a/wadsrc/static/zscript/engine/ui/statusbar/statusbarcore.zs b/wadsrc/static/zscript/engine/ui/statusbar/statusbarcore.zs index b00af3fde..bbcf23343 100644 --- a/wadsrc/static/zscript/engine/ui/statusbar/statusbarcore.zs +++ b/wadsrc/static/zscript/engine/ui/statusbar/statusbarcore.zs @@ -108,13 +108,13 @@ class StatusBarCore native ui native static String FormatNumber(int number, int minsize = 0, int maxsize = 0, int format = 0, String prefix = ""); native double, double, double, double StatusbarToRealCoords(double x, double y=0, double w=0, double h=0); - native void DrawTexture(TextureID texture, Vector2 pos, int flags = 0, double Alpha = 1., Vector2 box = (-1, -1), Vector2 scale = (1, 1), ERenderStyle style = STYLE_Translucent, Color col = 0xffffffff, int translation = 0, double clipwidth = -1); - native void DrawImage(String texture, Vector2 pos, int flags = 0, double Alpha = 1., Vector2 box = (-1, -1), Vector2 scale = (1, 1), ERenderStyle style = STYLE_Translucent, Color col = 0xffffffff, int translation = 0, double clipwidth = -1); + native void DrawTexture(TextureID texture, Vector2 pos, int flags = 0, double Alpha = 1., Vector2 box = (-1, -1), Vector2 scale = (1, 1), ERenderStyle style = STYLE_Translucent, Color col = 0xffffffff, TranslationID translation = 0, double clipwidth = -1); + native void DrawImage(String texture, Vector2 pos, int flags = 0, double Alpha = 1., Vector2 box = (-1, -1), Vector2 scale = (1, 1), ERenderStyle style = STYLE_Translucent, Color col = 0xffffffff, TranslationID translation = 0, double clipwidth = -1); - native void DrawTextureRotated(TextureID texid, Vector2 pos, int flags, double angle, double alpha = 1, Vector2 scale = (1, 1), ERenderStyle style = STYLE_Translucent, Color col = 0xffffffff, int translation = 0); - native void DrawImageRotated(String texid, Vector2 pos, int flags, double angle, double alpha = 1, Vector2 scale = (1, 1), ERenderStyle style = STYLE_Translucent, Color col = 0xffffffff, int translation = 0); + native void DrawTextureRotated(TextureID texid, Vector2 pos, int flags, double angle, double alpha = 1, Vector2 scale = (1, 1), ERenderStyle style = STYLE_Translucent, Color col = 0xffffffff, TranslationID translation = 0); + native void DrawImageRotated(String texid, Vector2 pos, int flags, double angle, double alpha = 1, Vector2 scale = (1, 1), ERenderStyle style = STYLE_Translucent, Color col = 0xffffffff, TranslationID translation = 0); - native void DrawString(HUDFont font, String string, Vector2 pos, int flags = 0, int translation = Font.CR_UNTRANSLATED, double Alpha = 1., int wrapwidth = -1, int linespacing = 4, Vector2 scale = (1, 1), int pt = 0, ERenderStyle style = STYLE_Translucent); + native void DrawString(HUDFont font, String string, Vector2 pos, int flags = 0, int translation = Font.CR_UNTRANSLATED, double Alpha = 1., int wrapwidth = -1, int linespacing = 4, Vector2 scale = (1, 1), TranslationID pt = 0, ERenderStyle style = STYLE_Translucent); native double, double, double, double TransformRect(double x, double y, double w, double h, int flags = 0); native void Fill(Color col, double x, double y, double w, double h, int flags = 0); native void SetClipRect(double x, double y, double w, double h, int flags = 0); diff --git a/wadsrc/static/zscript/games/blood/ui/menu.zs b/wadsrc/static/zscript/games/blood/ui/menu.zs index 5f5b9054f..7cbff7baa 100644 --- a/wadsrc/static/zscript/games/blood/ui/menu.zs +++ b/wadsrc/static/zscript/games/blood/ui/menu.zs @@ -62,7 +62,7 @@ class ListMenuItemBloodTextItem : ListMenuItemTextItem let gamefont = Raze.PickBigFont(); int xpos = mXpos - gamefont.StringWidth(mText) / 2; int cr = generic_ui? Font.CR_GRAY : Font.CR_NATIVEPAL; - int trans = generic_ui? 0 : Translation.MakeID(Translation_Remap, pal); + TranslationID trans = generic_ui? 0 : Translation.MakeID(Translation_Remap, pal); if (selected) shade = 32 - ((MSTime() * 120 / 1000) & 63); diff --git a/wadsrc/static/zscript/games/blood/ui/sbar.zs b/wadsrc/static/zscript/games/blood/ui/sbar.zs index 86bb9baab..a3b5a6c5a 100644 --- a/wadsrc/static/zscript/games/blood/ui/sbar.zs +++ b/wadsrc/static/zscript/games/blood/ui/sbar.zs @@ -232,7 +232,7 @@ class BloodStatusBar : RazeStatusBar DrawTexture(Blood.PowerUpIcon(power), (x, y + powerYoffs[order]), DI_SCREEN_LEFT_CENTER | DI_ITEM_RELCENTER, scale:(powerScale[order], powerScale[order])); } - DrawStatNumber("%d", remainingSeconds, "SBarNumberInv", x + 15, y, 0, remainingSeconds > warningTime ? 0 : Translation.MakeID(Translation_Remap, 2), 0.5, DI_SCREEN_LEFT_CENTER); + DrawStatNumber("%d", remainingSeconds, "SBarNumberInv", x + 15, y, 0, remainingSeconds > warningTime ? 0 : 2, 0.5, DI_SCREEN_LEFT_CENTER); y += 20; } } @@ -452,8 +452,8 @@ class BloodStatusBar : RazeStatusBar } bool meHaveBlueFlag = pPlayer.hasFlag & 1; - int trans10 = Translation.MakeID(Translation_Remap, 10); - int trans2 = Translation.MakeID(Translation_Remap, 2); + let trans10 = Translation.MakeID(Translation_Remap, 10); + let trans2 = Translation.MakeID(Translation_Remap, 2); DrawImage(meHaveBlueFlag ? "FlagHave" : "FlagHaveNot", (0, 75 - 100), DI_SCREEN_RIGHT_CENTER|DI_ITEM_RELCENTER, scale:(0.35, 0.35), translation:trans10); if (gBlueFlagDropped) DrawImage("FlagDropped", (305 - 320, 83 - 100), DI_SCREEN_RIGHT_CENTER|DI_ITEM_RELCENTER, translation:trans10); @@ -507,7 +507,7 @@ class BloodStatusBar : RazeStatusBar // //--------------------------------------------------------------------------- - int DrawStatusBar(BloodPlayer pPlayer, int nPalette) + int DrawStatusBar(BloodPlayer pPlayer, TranslationID nPalette) { int th = texHeight("Statusbar"); BeginStatusBar(false, 320, 200, th); @@ -583,7 +583,7 @@ class BloodStatusBar : RazeStatusBar // //--------------------------------------------------------------------------- - int DrawHUD1(BloodPlayer pPlayer, int nPalette) + int DrawHUD1(BloodPlayer pPlayer, TranslationID nPalette) { BeginHUD(1, false, 320, 200); DrawImage("FullHUD", (34, 187 - 200), DI_ITEM_RELCENTER, style:STYLE_Normal, col:0xffc0c0c0, translation:nPalette); @@ -726,19 +726,20 @@ class BloodStatusBar : RazeStatusBar override void UpdateStatusBar(SummaryInfo summary) { - int nPalette = 0; + TranslationID nPalette = 0; let pPlayer = Blood.GetViewPlayer(); int y = 0; int nGameType = Blood.getGameType(); if (nGameType == 3) { + int pal; if (pPlayer.teamId & 1) - nPalette = 7; + pal = 7; else - nPalette = 10; + pal = 10; - nPalette = Translation.MakeID(Translation_Remap, nPalette); + nPalette = Translation.MakeID(Translation_Remap, pal); } if (hud_size == Hud_Full) diff --git a/wadsrc/static/zscript/games/duke/ui/menu.zs b/wadsrc/static/zscript/games/duke/ui/menu.zs index cf3fa8475..169a6bb9e 100644 --- a/wadsrc/static/zscript/games/duke/ui/menu.zs +++ b/wadsrc/static/zscript/games/duke/ui/menu.zs @@ -168,7 +168,7 @@ class ListMenuItemDukeTextItem : ListMenuItemTextItem override void Draw(bool selected, ListMenuDescriptor desc) { let font = Raze.PickBigFont(); - int trans = mColorSelected? Translation.MakeID(Translation_Remap, 1) : 0; + TranslationID trans = mColorSelected? Translation.MakeID(Translation_Remap, 1) : 0; Color pe; double scale = (gameinfo.gametype & GAMEFLAG_RRALL) ? 0.4 : 1.; let xpos = 160 - font.StringWidth(mText) * scale * 0.5; diff --git a/wadsrc/static/zscript/games/duke/ui/screens.zs b/wadsrc/static/zscript/games/duke/ui/screens.zs index 295108823..dbdf5337c 100644 --- a/wadsrc/static/zscript/games/duke/ui/screens.zs +++ b/wadsrc/static/zscript/games/duke/ui/screens.zs @@ -55,7 +55,7 @@ class DRealmsScreen : SkippableScreenJob override void Draw(double smoothratio) { let tex = TexMan.CheckForTexture("DREALMS"); - int translation = TexMan.UseGamePalette(tex)? Translation.MakeID(Translation_BasePalette, Duke.DREALMSPAL) : 0; + TranslationID translation = TexMan.UseGamePalette(tex)? Translation.MakeID(Translation_BasePalette, Duke.DREALMSPAL) : 0; screen.DrawTexture(tex, true, 0, 0, DTA_FullscreenEx, FSMode_ScaleToFit43, DTA_TranslationIndex, translation, DTA_LegacyRenderStyle, STYLE_Normal); } @@ -116,11 +116,11 @@ class DukeTitleScreen : SkippableScreenJob override void Draw(double smoothratio) { int clock = (ticks + smoothratio) * 120 / GameTicRate; - int etrans = Translation.MakeID(Translation_BasePalette, Duke.TITLEPAL); + TranslationID etrans = Translation.MakeID(Translation_BasePalette, Duke.TITLEPAL); // Only translate if the image depends on the global palette. let tex = TexMan.CheckForTexture("BETASCREEN"); - int trans = TexMan.UseGamePalette(tex)? etrans : 0; + TranslationID trans = TexMan.UseGamePalette(tex)? etrans : 0; screen.DrawTexture(tex, true, 0, 0, DTA_FullscreenEx, FSMode_ScaleToFit43, DTA_TranslationIndex, trans, DTA_LegacyRenderStyle, STYLE_Normal); double scale = clamp(clock - 120, 0, 60) / 64.; @@ -251,10 +251,10 @@ class Episode1End1 : SkippableScreenJob override void Draw(double sr) { - int etrans = Translation.MakeID(Translation_BasePalette, Duke.ENDINGPAL); + TranslationID etrans = Translation.MakeID(Translation_BasePalette, Duke.ENDINGPAL); let tex = TexMan.CheckForTexture("VICTORY1"); - int trans = TexMan.UseGamePalette(tex)? etrans : 0; + TranslationID trans = TexMan.UseGamePalette(tex)? etrans : 0; screen.DrawTexture(tex, false, 0, 50, DTA_FullscreenScale, FSMode_Fit320x200, DTA_TranslationIndex, trans, DTA_LegacyRenderStyle, STYLE_Normal, DTA_TopLeft, true); if (bossani.isValid()) diff --git a/wadsrc/static/zscript/games/exhumed/ui/screens.zs b/wadsrc/static/zscript/games/exhumed/ui/screens.zs index 2578eebed..de24a1660 100644 --- a/wadsrc/static/zscript/games/exhumed/ui/screens.zs +++ b/wadsrc/static/zscript/games/exhumed/ui/screens.zs @@ -462,7 +462,7 @@ class Cinema : SkippableScreenJob TextureID cinematile; int currentCinemaPalette; int cdtrack; - int palette; + TranslationID palette; bool done; ScreenJob Init(String bgTexture, String text, int pal, int cdtrk) diff --git a/wadsrc/static/zscript/games/sw/ui/screens.zs b/wadsrc/static/zscript/games/sw/ui/screens.zs index 3274fa61c..dba7c260b 100644 --- a/wadsrc/static/zscript/games/sw/ui/screens.zs +++ b/wadsrc/static/zscript/games/sw/ui/screens.zs @@ -55,7 +55,7 @@ class SWDRealmsScreen : SkippableScreenJob override void Draw(double sm) { let tex = TexMan.CheckForTexture("THREED_REALMS_PIC", TexMan.Type_Any); - int translation = TexMan.UseGamePalette(tex) ? Translation.MakeID(Translation_BasePalette, DREALMSPAL) : 0; + TranslationID translation = TexMan.UseGamePalette(tex) ? Translation.MakeID(Translation_BasePalette, DREALMSPAL) : 0; Screen.DrawTexture(tex, false, 0, 0, DTA_FullscreenEx, FSMode_ScaleToFit43, DTA_TranslationIndex, translation, DTA_LegacyRenderStyle, STYLE_Normal); } }