mirror of
https://github.com/ZDoom/qzdoom.git
synced 2024-11-13 16:07:55 +00:00
This commit is contained in:
commit
53acc28f26
23 changed files with 277 additions and 364 deletions
|
@ -13,7 +13,6 @@ include( CheckIncludeFile )
|
||||||
include( CheckIncludeFiles )
|
include( CheckIncludeFiles )
|
||||||
include( CheckLibraryExists )
|
include( CheckLibraryExists )
|
||||||
include( FindPkgConfig )
|
include( FindPkgConfig )
|
||||||
include( FindOpenGL )
|
|
||||||
|
|
||||||
if( ZD_CMAKE_COMPILER_IS_GNUCXX_COMPATIBLE )
|
if( ZD_CMAKE_COMPILER_IS_GNUCXX_COMPATIBLE )
|
||||||
option( NO_STRIP "Do not strip Release or MinSizeRel builds" )
|
option( NO_STRIP "Do not strip Release or MinSizeRel builds" )
|
||||||
|
@ -162,6 +161,7 @@ if( WIN32 )
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
set( ZDOOM_LIBS
|
set( ZDOOM_LIBS
|
||||||
|
opengl32
|
||||||
wsock32
|
wsock32
|
||||||
winmm
|
winmm
|
||||||
"${DX_dinput8_LIBRARY}"
|
"${DX_dinput8_LIBRARY}"
|
||||||
|
@ -255,15 +255,6 @@ else()
|
||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# Check if we have OpenGL
|
|
||||||
|
|
||||||
if( NOT OPENGL_FOUND )
|
|
||||||
message( FATAL_ERROR "OpenGL is required for building." )
|
|
||||||
endif( NOT OPENGL_FOUND )
|
|
||||||
|
|
||||||
set( ZDOOM_LIBS ${ZDOOM_LIBS} ${OPENGL_LIBRARIES} )
|
|
||||||
include_directories( ${OPENGL_INCLUDE_DIR} )
|
|
||||||
|
|
||||||
if( NOT NO_OPENAL )
|
if( NOT NO_OPENAL )
|
||||||
find_package( OpenAL )
|
find_package( OpenAL )
|
||||||
mark_as_advanced(CLEAR OPENAL_INCLUDE_DIR)
|
mark_as_advanced(CLEAR OPENAL_INCLUDE_DIR)
|
||||||
|
|
|
@ -35,6 +35,7 @@ enum
|
||||||
VARF_Ref = (1<<16), // argument is passed by reference.
|
VARF_Ref = (1<<16), // argument is passed by reference.
|
||||||
VARF_Transient = (1<<17), // don't auto serialize field.
|
VARF_Transient = (1<<17), // don't auto serialize field.
|
||||||
VARF_Meta = (1<<18), // static class data (by necessity read only.)
|
VARF_Meta = (1<<18), // static class data (by necessity read only.)
|
||||||
|
VARF_VarArg = (1<<19), // [ZZ] vararg: don't typecheck values after ... in function signature
|
||||||
};
|
};
|
||||||
|
|
||||||
// Symbol information -------------------------------------------------------
|
// Symbol information -------------------------------------------------------
|
||||||
|
@ -597,8 +598,6 @@ public:
|
||||||
|
|
||||||
class PClass *ClassRestriction;
|
class PClass *ClassRestriction;
|
||||||
|
|
||||||
// this is only here to block PPointer's implementation
|
|
||||||
void SetPointer(void *base, unsigned offset, TArray<size_t> *special = NULL) const override {}
|
|
||||||
bool isCompatible(PType *type);
|
bool isCompatible(PType *type);
|
||||||
|
|
||||||
virtual bool IsMatch(intptr_t id1, intptr_t id2) const;
|
virtual bool IsMatch(intptr_t id1, intptr_t id2) const;
|
||||||
|
|
|
@ -432,10 +432,16 @@ void G_InitNew (const char *mapname, bool bTitleLevel)
|
||||||
StatusBar->Destroy();
|
StatusBar->Destroy();
|
||||||
StatusBar = NULL;
|
StatusBar = NULL;
|
||||||
}
|
}
|
||||||
|
auto cls = PClass::FindClass("DoomStatusBar");
|
||||||
|
|
||||||
if (bTitleLevel)
|
if (bTitleLevel)
|
||||||
{
|
{
|
||||||
StatusBar = new DBaseStatusBar (0);
|
StatusBar = new DBaseStatusBar (0);
|
||||||
}
|
}
|
||||||
|
else if (cls && gameinfo.gametype == GAME_Doom)
|
||||||
|
{
|
||||||
|
StatusBar = (DBaseStatusBar*)cls->CreateNew();
|
||||||
|
}
|
||||||
else if (SBarInfoScript[SCRIPT_CUSTOM] != NULL)
|
else if (SBarInfoScript[SCRIPT_CUSTOM] != NULL)
|
||||||
{
|
{
|
||||||
int cstype = SBarInfoScript[SCRIPT_CUSTOM]->GetGameType();
|
int cstype = SBarInfoScript[SCRIPT_CUSTOM]->GetGameType();
|
||||||
|
|
|
@ -646,8 +646,14 @@ void GLSprite::Process(AActor* thing, sector_t * sector, int thruportal)
|
||||||
sector_t rs;
|
sector_t rs;
|
||||||
sector_t * rendersector;
|
sector_t * rendersector;
|
||||||
|
|
||||||
|
if (thing == nullptr)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// [ZZ] allow CustomSprite-style direct picnum specification
|
||||||
|
bool isPicnumOverride = thing->picnum.isValid();
|
||||||
|
|
||||||
// Don't waste time projecting sprites that are definitely not visible.
|
// Don't waste time projecting sprites that are definitely not visible.
|
||||||
if (thing == nullptr || thing->sprite == 0 || !thing->IsVisibleToPlayer() || !thing->IsInsideVisibleAngles())
|
if ((thing->sprite == 0 && !isPicnumOverride) || !thing->IsVisibleToPlayer() || !thing->IsInsideVisibleAngles())
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -752,13 +758,19 @@ void GLSprite::Process(AActor* thing, sector_t * sector, int thruportal)
|
||||||
z += fz;
|
z += fz;
|
||||||
}
|
}
|
||||||
|
|
||||||
modelframe = gl_FindModelFrame(thing->GetClass(), spritenum, thing->frame, !!(thing->flags & MF_DROPPED));
|
modelframe = isPicnumOverride ? nullptr : gl_FindModelFrame(thing->GetClass(), spritenum, thing->frame, !!(thing->flags & MF_DROPPED));
|
||||||
if (!modelframe)
|
if (!modelframe)
|
||||||
{
|
{
|
||||||
bool mirror;
|
bool mirror;
|
||||||
DAngle ang = (thingpos - ViewPos).Angle();
|
DAngle ang = (thingpos - ViewPos).Angle();
|
||||||
FTextureID patch;
|
FTextureID patch;
|
||||||
if (thing->flags7 & MF7_SPRITEANGLE)
|
// [ZZ] add direct picnum override
|
||||||
|
if (isPicnumOverride)
|
||||||
|
{
|
||||||
|
patch = thing->picnum;
|
||||||
|
mirror = false;
|
||||||
|
}
|
||||||
|
else if (thing->flags7 & MF7_SPRITEANGLE)
|
||||||
{
|
{
|
||||||
patch = gl_GetSpriteFrame(spritenum, thing->frame, -1, (thing->SpriteAngle).BAMs(), &mirror);
|
patch = gl_GetSpriteFrame(spritenum, thing->frame, -1, (thing->SpriteAngle).BAMs(), &mirror);
|
||||||
}
|
}
|
||||||
|
@ -775,7 +787,8 @@ void GLSprite::Process(AActor* thing, sector_t * sector, int thruportal)
|
||||||
if (!patch.isValid()) return;
|
if (!patch.isValid()) return;
|
||||||
int type = thing->renderflags & RF_SPRITETYPEMASK;
|
int type = thing->renderflags & RF_SPRITETYPEMASK;
|
||||||
gltexture = FMaterial::ValidateTexture(patch, (type == RF_FACESPRITE), false);
|
gltexture = FMaterial::ValidateTexture(patch, (type == RF_FACESPRITE), false);
|
||||||
if (!gltexture) return;
|
if (!gltexture)
|
||||||
|
return;
|
||||||
|
|
||||||
vt = gltexture->GetSpriteVT();
|
vt = gltexture->GetSpriteVT();
|
||||||
vb = gltexture->GetSpriteVB();
|
vb = gltexture->GetSpriteVB();
|
||||||
|
|
|
@ -17,11 +17,12 @@ static void* AppleGLGetProcAddress (const char *name)
|
||||||
}
|
}
|
||||||
#endif /* __APPLE__ */
|
#endif /* __APPLE__ */
|
||||||
|
|
||||||
#if defined(__sgi) || defined (__sun)
|
/* BEGINNING OF MANUAL CHANGES, DO NOT REMOVE! */
|
||||||
|
#if defined(__sgi) || defined (__sun) || defined(__unix__)
|
||||||
#include <dlfcn.h>
|
#include <dlfcn.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
|
||||||
static void* SunGetProcAddress (const GLubyte* name)
|
static void* PosixGetProcAddress (const GLubyte* name)
|
||||||
{
|
{
|
||||||
static void* h = NULL;
|
static void* h = NULL;
|
||||||
static void* gpa;
|
static void* gpa;
|
||||||
|
@ -37,7 +38,7 @@ static void* SunGetProcAddress (const GLubyte* name)
|
||||||
else
|
else
|
||||||
return dlsym(h, (const char*)name);
|
return dlsym(h, (const char*)name);
|
||||||
}
|
}
|
||||||
#endif /* __sgi || __sun */
|
#endif /* __sgi || __sun || __unix__ */
|
||||||
|
|
||||||
#if defined(_WIN32)
|
#if defined(_WIN32)
|
||||||
|
|
||||||
|
@ -77,8 +78,9 @@ static PROC WinGetProcAddress(const char *name)
|
||||||
#if defined(__APPLE__)
|
#if defined(__APPLE__)
|
||||||
#define IntGetProcAddress(name) AppleGLGetProcAddress(name)
|
#define IntGetProcAddress(name) AppleGLGetProcAddress(name)
|
||||||
#else
|
#else
|
||||||
#if defined(__sgi) || defined(__sun)
|
#if defined(__sgi) || defined(__sun) || defined(__unix__)
|
||||||
#define IntGetProcAddress(name) SunGetProcAddress(name)
|
#define IntGetProcAddress(name) PosixGetProcAddress((const GLubyte*)name)
|
||||||
|
/* END OF MANUAL CHANGES, DO NOT REMOVE! */
|
||||||
#else /* GLX */
|
#else /* GLX */
|
||||||
#include <GL/glx.h>
|
#include <GL/glx.h>
|
||||||
|
|
||||||
|
|
|
@ -979,7 +979,7 @@ static int DamageMobj (AActor *target, AActor *inflictor, AActor *source, int da
|
||||||
|
|
||||||
if (target == NULL || !((target->flags & MF_SHOOTABLE) || (target->flags6 & MF6_VULNERABLE)))
|
if (target == NULL || !((target->flags & MF_SHOOTABLE) || (target->flags6 & MF6_VULNERABLE)))
|
||||||
{ // Shouldn't happen
|
{ // Shouldn't happen
|
||||||
return -1;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
//Rather than unnecessarily call the function over and over again, let's be a little more efficient.
|
//Rather than unnecessarily call the function over and over again, let's be a little more efficient.
|
||||||
|
@ -991,14 +991,14 @@ static int DamageMobj (AActor *target, AActor *inflictor, AActor *source, int da
|
||||||
{
|
{
|
||||||
if (inflictor == NULL || !(inflictor->flags4 & MF4_SPECTRAL))
|
if (inflictor == NULL || !(inflictor->flags4 & MF4_SPECTRAL))
|
||||||
{
|
{
|
||||||
return -1;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (target->health <= 0)
|
if (target->health <= 0)
|
||||||
{
|
{
|
||||||
if (inflictor && mod == NAME_Ice && !(inflictor->flags7 & MF7_ICESHATTER))
|
if (inflictor && mod == NAME_Ice && !(inflictor->flags7 & MF7_ICESHATTER))
|
||||||
{
|
{
|
||||||
return -1;
|
return 0;
|
||||||
}
|
}
|
||||||
else if (target->flags & MF_ICECORPSE) // frozen
|
else if (target->flags & MF_ICECORPSE) // frozen
|
||||||
{
|
{
|
||||||
|
@ -1006,7 +1006,7 @@ static int DamageMobj (AActor *target, AActor *inflictor, AActor *source, int da
|
||||||
target->flags6 |= MF6_SHATTERING;
|
target->flags6 |= MF6_SHATTERING;
|
||||||
target->Vel.Zero();
|
target->Vel.Zero();
|
||||||
}
|
}
|
||||||
return -1;
|
return 0;
|
||||||
}
|
}
|
||||||
// [MC] Changed it to check rawdamage here for consistency, even though that doesn't actually do anything
|
// [MC] Changed it to check rawdamage here for consistency, even though that doesn't actually do anything
|
||||||
// different here. At any rate, invulnerable is being checked before type factoring, which is then being
|
// different here. At any rate, invulnerable is being checked before type factoring, which is then being
|
||||||
|
@ -1027,7 +1027,7 @@ static int DamageMobj (AActor *target, AActor *inflictor, AActor *source, int da
|
||||||
goto fakepain;
|
goto fakepain;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
return -1;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -1038,7 +1038,7 @@ static int DamageMobj (AActor *target, AActor *inflictor, AActor *source, int da
|
||||||
if (fakedPain)
|
if (fakedPain)
|
||||||
plrDontThrust = 1;
|
plrDontThrust = 1;
|
||||||
else
|
else
|
||||||
return -1;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1068,7 +1068,7 @@ static int DamageMobj (AActor *target, AActor *inflictor, AActor *source, int da
|
||||||
if (target->flags2 & MF2_DORMANT)
|
if (target->flags2 & MF2_DORMANT)
|
||||||
{
|
{
|
||||||
// Invulnerable, and won't wake up
|
// Invulnerable, and won't wake up
|
||||||
return -1;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!telefragDamage || (target->flags7 & MF7_LAXTELEFRAGDMG)) // TELEFRAG_DAMAGE may only be reduced with LAXTELEFRAGDMG or it may not guarantee its effect.
|
if (!telefragDamage || (target->flags7 & MF7_LAXTELEFRAGDMG)) // TELEFRAG_DAMAGE may only be reduced with LAXTELEFRAGDMG or it may not guarantee its effect.
|
||||||
|
@ -1086,19 +1086,19 @@ static int DamageMobj (AActor *target, AActor *inflictor, AActor *source, int da
|
||||||
if (player != NULL)
|
if (player != NULL)
|
||||||
{
|
{
|
||||||
if (!deathmatch && inflictor->FriendPlayer > 0)
|
if (!deathmatch && inflictor->FriendPlayer > 0)
|
||||||
return -1;
|
return 0;
|
||||||
}
|
}
|
||||||
else if (target->flags4 & MF4_SPECTRAL)
|
else if (target->flags4 & MF4_SPECTRAL)
|
||||||
{
|
{
|
||||||
if (inflictor->FriendPlayer == 0 && !target->IsHostile(inflictor))
|
if (inflictor->FriendPlayer == 0 && !target->IsHostile(inflictor))
|
||||||
return -1;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
damage = inflictor->CallDoSpecialDamage(target, damage, mod);
|
damage = inflictor->CallDoSpecialDamage(target, damage, mod);
|
||||||
if (damage < 0)
|
if (damage < 0)
|
||||||
{
|
{
|
||||||
return -1;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1141,7 +1141,7 @@ static int DamageMobj (AActor *target, AActor *inflictor, AActor *source, int da
|
||||||
{
|
{
|
||||||
goto fakepain;
|
goto fakepain;
|
||||||
}
|
}
|
||||||
return -1;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1153,7 +1153,7 @@ static int DamageMobj (AActor *target, AActor *inflictor, AActor *source, int da
|
||||||
if (damage < 0)
|
if (damage < 0)
|
||||||
{
|
{
|
||||||
// any negative value means that something in the above chain has cancelled out all damage and all damage effects, including pain.
|
// any negative value means that something in the above chain has cancelled out all damage and all damage effects, including pain.
|
||||||
return -1;
|
return 0;
|
||||||
}
|
}
|
||||||
// Push the target unless the source's weapon's kickback is 0.
|
// Push the target unless the source's weapon's kickback is 0.
|
||||||
// (i.e. Gauntlets/Chainsaw)
|
// (i.e. Gauntlets/Chainsaw)
|
||||||
|
@ -1244,7 +1244,7 @@ static int DamageMobj (AActor *target, AActor *inflictor, AActor *source, int da
|
||||||
damage = (int)(damage * level.teamdamage);
|
damage = (int)(damage * level.teamdamage);
|
||||||
if (damage < 0)
|
if (damage < 0)
|
||||||
{
|
{
|
||||||
return damage;
|
return 0;
|
||||||
}
|
}
|
||||||
else if (damage == 0)
|
else if (damage == 0)
|
||||||
{
|
{
|
||||||
|
@ -1256,7 +1256,7 @@ static int DamageMobj (AActor *target, AActor *inflictor, AActor *source, int da
|
||||||
{
|
{
|
||||||
goto fakepain;
|
goto fakepain;
|
||||||
}
|
}
|
||||||
return -1;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1294,14 +1294,14 @@ static int DamageMobj (AActor *target, AActor *inflictor, AActor *source, int da
|
||||||
//Make sure no godmodes and NOPAIN flags are found first.
|
//Make sure no godmodes and NOPAIN flags are found first.
|
||||||
//Then, check to see if the player has NODAMAGE or ALLOWPAIN, or inflictor has CAUSEPAIN.
|
//Then, check to see if the player has NODAMAGE or ALLOWPAIN, or inflictor has CAUSEPAIN.
|
||||||
if ((player->cheats & CF_GODMODE) || (player->cheats & CF_GODMODE2) || (player->mo->flags5 & MF5_NOPAIN))
|
if ((player->cheats & CF_GODMODE) || (player->cheats & CF_GODMODE2) || (player->mo->flags5 & MF5_NOPAIN))
|
||||||
return -1;
|
return 0;
|
||||||
else if ((((player->mo->flags7 & MF7_ALLOWPAIN) || (player->mo->flags5 & MF5_NODAMAGE)) || ((inflictor != NULL) && (inflictor->flags7 & MF7_CAUSEPAIN))))
|
else if ((((player->mo->flags7 & MF7_ALLOWPAIN) || (player->mo->flags5 & MF5_NODAMAGE)) || ((inflictor != NULL) && (inflictor->flags7 & MF7_CAUSEPAIN))))
|
||||||
{
|
{
|
||||||
invulpain = true;
|
invulpain = true;
|
||||||
goto fakepain;
|
goto fakepain;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
return -1;
|
return 0;
|
||||||
}
|
}
|
||||||
// Armor for players.
|
// Armor for players.
|
||||||
if (!(flags & DMG_NO_ARMOR) && player->mo->Inventory != NULL)
|
if (!(flags & DMG_NO_ARMOR) && player->mo->Inventory != NULL)
|
||||||
|
@ -1321,7 +1321,7 @@ static int DamageMobj (AActor *target, AActor *inflictor, AActor *source, int da
|
||||||
{
|
{
|
||||||
// [MC] Godmode doesn't need checking here, it's already being handled above.
|
// [MC] Godmode doesn't need checking here, it's already being handled above.
|
||||||
if ((target->flags5 & MF5_NOPAIN) || (inflictor && (inflictor->flags5 & MF5_PAINLESS)))
|
if ((target->flags5 & MF5_NOPAIN) || (inflictor && (inflictor->flags5 & MF5_PAINLESS)))
|
||||||
return damage;
|
return 0;
|
||||||
|
|
||||||
// If MF6_FORCEPAIN is set, make the player enter the pain state.
|
// If MF6_FORCEPAIN is set, make the player enter the pain state.
|
||||||
if ((inflictor && (inflictor->flags6 & MF6_FORCEPAIN)))
|
if ((inflictor && (inflictor->flags6 & MF6_FORCEPAIN)))
|
||||||
|
@ -1332,7 +1332,7 @@ static int DamageMobj (AActor *target, AActor *inflictor, AActor *source, int da
|
||||||
invulpain = true;
|
invulpain = true;
|
||||||
goto fakepain;
|
goto fakepain;
|
||||||
}
|
}
|
||||||
return damage;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1396,7 +1396,7 @@ static int DamageMobj (AActor *target, AActor *inflictor, AActor *source, int da
|
||||||
if (fakedPain)
|
if (fakedPain)
|
||||||
goto fakepain;
|
goto fakepain;
|
||||||
else
|
else
|
||||||
return damage;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1468,7 +1468,7 @@ static int DamageMobj (AActor *target, AActor *inflictor, AActor *source, int da
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
target->CallDie (source, inflictor, flags);
|
target->CallDie (source, inflictor, flags);
|
||||||
return damage;
|
return MAX(0, damage);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1480,7 +1480,7 @@ static int DamageMobj (AActor *target, AActor *inflictor, AActor *source, int da
|
||||||
if (target->health <= woundhealth)
|
if (target->health <= woundhealth)
|
||||||
{
|
{
|
||||||
target->SetState (woundstate);
|
target->SetState (woundstate);
|
||||||
return damage;
|
return MAX(0, damage);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1579,9 +1579,9 @@ dopain:
|
||||||
|
|
||||||
if (invulpain) //Note that this takes into account all the cheats a player has, in terms of invulnerability.
|
if (invulpain) //Note that this takes into account all the cheats a player has, in terms of invulnerability.
|
||||||
{
|
{
|
||||||
return -1; //NOW we return -1!
|
return 0; //NOW we return -1!
|
||||||
}
|
}
|
||||||
return damage;
|
return MAX(0, damage);
|
||||||
}
|
}
|
||||||
|
|
||||||
DEFINE_ACTION_FUNCTION(AActor, DamageMobj)
|
DEFINE_ACTION_FUNCTION(AActor, DamageMobj)
|
||||||
|
|
|
@ -5011,7 +5011,7 @@ void P_RailAttack(FRailParams *p)
|
||||||
DAngle angle = source->Angles.Yaw + p->angleoffset;
|
DAngle angle = source->Angles.Yaw + p->angleoffset;
|
||||||
|
|
||||||
DVector3 vec(DRotator(-pitch, angle, angle));
|
DVector3 vec(DRotator(-pitch, angle, angle));
|
||||||
double shootz = source->Center() - source->FloatSpeed + p->offset_z;
|
double shootz = source->Center() - source->FloatSpeed + p->offset_z - source->Floorclip;
|
||||||
|
|
||||||
if (!(p->flags & RAF_CENTERZ))
|
if (!(p->flags & RAF_CENTERZ))
|
||||||
{
|
{
|
||||||
|
|
|
@ -1041,7 +1041,7 @@ AInventory *AActor::DropInventory (AInventory *item)
|
||||||
AInventory *drop = nullptr;
|
AInventory *drop = nullptr;
|
||||||
IFVIRTUALPTR(item, AInventory, CreateTossable)
|
IFVIRTUALPTR(item, AInventory, CreateTossable)
|
||||||
{
|
{
|
||||||
VMValue params[1] = { (DObject*)this };
|
VMValue params[1] = { (DObject*)item };
|
||||||
VMReturn ret((void**)&drop);
|
VMReturn ret((void**)&drop);
|
||||||
GlobalVMStack.Call(func, params, 1, &ret, 1, nullptr);
|
GlobalVMStack.Call(func, params, 1, &ret, 1, nullptr);
|
||||||
}
|
}
|
||||||
|
|
|
@ -291,11 +291,13 @@ static CheckFunc funcs[] = { CheckWad, CheckZip, Check7Z, CheckPak, CheckGRP, Ch
|
||||||
|
|
||||||
FResourceFile *FResourceFile::OpenResourceFile(const char *filename, FileReader *file, bool quiet, bool containeronly)
|
FResourceFile *FResourceFile::OpenResourceFile(const char *filename, FileReader *file, bool quiet, bool containeronly)
|
||||||
{
|
{
|
||||||
|
bool mustclose = false;
|
||||||
if (file == NULL)
|
if (file == NULL)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
file = new FileReader(filename);
|
file = new FileReader(filename);
|
||||||
|
mustclose = true;
|
||||||
}
|
}
|
||||||
catch (CRecoverableError &)
|
catch (CRecoverableError &)
|
||||||
{
|
{
|
||||||
|
@ -307,6 +309,7 @@ FResourceFile *FResourceFile::OpenResourceFile(const char *filename, FileReader
|
||||||
FResourceFile *resfile = funcs[i](filename, file, quiet);
|
FResourceFile *resfile = funcs[i](filename, file, quiet);
|
||||||
if (resfile != NULL) return resfile;
|
if (resfile != NULL) return resfile;
|
||||||
}
|
}
|
||||||
|
if (mustclose) delete file;
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -171,6 +171,7 @@ std2:
|
||||||
'export' { RET(TK_Export); }
|
'export' { RET(TK_Export); }
|
||||||
'virtual' { RET(TK_Virtual); }
|
'virtual' { RET(TK_Virtual); }
|
||||||
'override' { RET(TK_Override); }
|
'override' { RET(TK_Override); }
|
||||||
|
'vararg' { RET(TK_VarArg); }
|
||||||
'super' { RET(TK_Super); }
|
'super' { RET(TK_Super); }
|
||||||
'global' { RET(TK_Global); }
|
'global' { RET(TK_Global); }
|
||||||
'stop' { RET(TK_Stop); }
|
'stop' { RET(TK_Stop); }
|
||||||
|
|
|
@ -112,6 +112,7 @@ xx(TK_Iterator, "'iterator'")
|
||||||
xx(TK_Optional, "'optional'")
|
xx(TK_Optional, "'optional'")
|
||||||
xx(TK_Export, "'expert'")
|
xx(TK_Export, "'expert'")
|
||||||
xx(TK_Virtual, "'virtual'")
|
xx(TK_Virtual, "'virtual'")
|
||||||
|
xx(TK_VarArg, "'vararg'")
|
||||||
xx(TK_Override, "'override'")
|
xx(TK_Override, "'override'")
|
||||||
xx(TK_Super, "'super'")
|
xx(TK_Super, "'super'")
|
||||||
xx(TK_Null, "'null'")
|
xx(TK_Null, "'null'")
|
||||||
|
|
|
@ -7188,14 +7188,6 @@ FxExpression *FxFunctionCall::Resolve(FCompileContext& ctx)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// [ZZ] string formatting function
|
|
||||||
if (MethodName == NAME_Format)
|
|
||||||
{
|
|
||||||
FxExpression *x = new FxFormat(ArgList, ScriptPosition);
|
|
||||||
delete this;
|
|
||||||
return x->Resolve(ctx);
|
|
||||||
}
|
|
||||||
|
|
||||||
int min, max, special;
|
int min, max, special;
|
||||||
if (MethodName == NAME_ACS_NamedExecuteWithResult || MethodName == NAME_CallACS)
|
if (MethodName == NAME_ACS_NamedExecuteWithResult || MethodName == NAME_CallACS)
|
||||||
{
|
{
|
||||||
|
@ -7466,6 +7458,9 @@ FxExpression *FxMemberFunctionCall::Resolve(FCompileContext& ctx)
|
||||||
{
|
{
|
||||||
if (ccls != nullptr)
|
if (ccls != nullptr)
|
||||||
{
|
{
|
||||||
|
// [ZZ] substitute ccls for String internal type.
|
||||||
|
if (ccls->TypeName == NAME_String)
|
||||||
|
ccls = TypeStringStruct;
|
||||||
if (!ccls->IsKindOf(RUNTIME_CLASS(PClass)) || static_cast<PClass *>(ccls)->bExported)
|
if (!ccls->IsKindOf(RUNTIME_CLASS(PClass)) || static_cast<PClass *>(ccls)->bExported)
|
||||||
{
|
{
|
||||||
cls = ccls;
|
cls = ccls;
|
||||||
|
@ -8056,6 +8051,7 @@ FxExpression *FxVMFunctionCall::Resolve(FCompileContext& ctx)
|
||||||
for (unsigned i = 0; i < ArgList.Size(); i++)
|
for (unsigned i = 0; i < ArgList.Size(); i++)
|
||||||
{
|
{
|
||||||
// Varargs must all have the same type as the last typed argument. A_Jump is the only function using it.
|
// Varargs must all have the same type as the last typed argument. A_Jump is the only function using it.
|
||||||
|
// [ZZ] Varargs MAY have arbitrary types if the method is marked vararg.
|
||||||
if (!foundvarargs)
|
if (!foundvarargs)
|
||||||
{
|
{
|
||||||
if (argtypes[i + implicit] == nullptr) foundvarargs = true;
|
if (argtypes[i + implicit] == nullptr) foundvarargs = true;
|
||||||
|
@ -8128,8 +8124,21 @@ FxExpression *FxVMFunctionCall::Resolve(FCompileContext& ctx)
|
||||||
flag = argflags[i + implicit];
|
flag = argflags[i + implicit];
|
||||||
}
|
}
|
||||||
|
|
||||||
FxExpression *x;
|
FxExpression *x = nullptr;
|
||||||
if (!(flag & (VARF_Ref|VARF_Out)))
|
if (foundvarargs && (Function->Variants[0].Flags & VARF_VarArg))
|
||||||
|
{
|
||||||
|
// only cast implicit-string types for vararg, leave everything else as-is
|
||||||
|
// this was outright copypasted from FxFormat
|
||||||
|
ArgList[i] = ArgList[i]->Resolve(ctx);
|
||||||
|
if (ArgList[i]->ValueType == TypeName ||
|
||||||
|
ArgList[i]->ValueType == TypeSound)
|
||||||
|
{
|
||||||
|
x = new FxStringCast(ArgList[i]);
|
||||||
|
x = x->Resolve(ctx);
|
||||||
|
}
|
||||||
|
else x = ArgList[i];
|
||||||
|
}
|
||||||
|
else if (!(flag & (VARF_Ref|VARF_Out)))
|
||||||
{
|
{
|
||||||
x = new FxTypeCast(ArgList[i], type, false);
|
x = new FxTypeCast(ArgList[i], type, false);
|
||||||
x = x->Resolve(ctx);
|
x = x->Resolve(ctx);
|
||||||
|
@ -8475,280 +8484,6 @@ ExpEmit FxFlopFunctionCall::Emit(VMFunctionBuilder *build)
|
||||||
return to;
|
return to;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
//==========================================================================
|
|
||||||
//
|
|
||||||
//
|
|
||||||
//
|
|
||||||
//==========================================================================
|
|
||||||
|
|
||||||
FxFormat::FxFormat(FArgumentList &args, const FScriptPosition &pos)
|
|
||||||
: FxExpression(EFX_Format, pos)
|
|
||||||
{
|
|
||||||
EmitTail = false;
|
|
||||||
ArgList = std::move(args);
|
|
||||||
}
|
|
||||||
|
|
||||||
//==========================================================================
|
|
||||||
//
|
|
||||||
//
|
|
||||||
//
|
|
||||||
//==========================================================================
|
|
||||||
|
|
||||||
FxFormat::~FxFormat()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
//==========================================================================
|
|
||||||
//
|
|
||||||
//
|
|
||||||
//
|
|
||||||
//==========================================================================
|
|
||||||
|
|
||||||
PPrototype *FxFormat::ReturnProto()
|
|
||||||
{
|
|
||||||
EmitTail = true;
|
|
||||||
return FxExpression::ReturnProto();
|
|
||||||
}
|
|
||||||
|
|
||||||
//==========================================================================
|
|
||||||
//
|
|
||||||
//
|
|
||||||
//
|
|
||||||
//==========================================================================
|
|
||||||
|
|
||||||
FxExpression *FxFormat::Resolve(FCompileContext& ctx)
|
|
||||||
{
|
|
||||||
CHECKRESOLVED();
|
|
||||||
|
|
||||||
for (unsigned i = 0; i < ArgList.Size(); i++)
|
|
||||||
{
|
|
||||||
ArgList[i] = ArgList[i]->Resolve(ctx);
|
|
||||||
if (ArgList[i] == nullptr)
|
|
||||||
{
|
|
||||||
delete this;
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
// first argument should be a string
|
|
||||||
if (!i && ArgList[i]->ValueType != TypeString)
|
|
||||||
{
|
|
||||||
ScriptPosition.Message(MSG_ERROR, "String was expected for format");
|
|
||||||
delete this;
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ArgList[i]->ValueType == TypeName ||
|
|
||||||
ArgList[i]->ValueType == TypeSound)
|
|
||||||
{
|
|
||||||
FxExpression* x = new FxStringCast(ArgList[i]);
|
|
||||||
x = x->Resolve(ctx);
|
|
||||||
if (x == nullptr)
|
|
||||||
{
|
|
||||||
delete this;
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
ArgList[i] = x;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ValueType = TypeString;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
//==========================================================================
|
|
||||||
//
|
|
||||||
//
|
|
||||||
//==========================================================================
|
|
||||||
|
|
||||||
static int BuiltinFormat(VMValue *args, TArray<VMValue> &defaultparam, int numparam, VMReturn *ret, int numret)
|
|
||||||
{
|
|
||||||
assert(args[0].Type == REGT_STRING);
|
|
||||||
FString fmtstring = args[0].s().GetChars();
|
|
||||||
|
|
||||||
// note: we don't need a real printf format parser.
|
|
||||||
// enough to simply find the subtitution tokens and feed them to the real printf after checking types.
|
|
||||||
// https://en.wikipedia.org/wiki/Printf_format_string#Format_placeholder_specification
|
|
||||||
FString output;
|
|
||||||
bool in_fmt = false;
|
|
||||||
FString fmt_current;
|
|
||||||
int argnum = 1;
|
|
||||||
int argauto = 1;
|
|
||||||
// % = starts
|
|
||||||
// [0-9], -, +, \s, 0, #, . continue
|
|
||||||
// %, s, d, i, u, fF, eE, gG, xX, o, c, p, aA terminate
|
|
||||||
// various type flags are not supported. not like stuff like 'hh' modifier is to be used in the VM.
|
|
||||||
// the only combination that is parsed locally is %n$...
|
|
||||||
bool haveargnums = false;
|
|
||||||
for (size_t i = 0; i < fmtstring.Len(); i++)
|
|
||||||
{
|
|
||||||
char c = fmtstring[i];
|
|
||||||
if (in_fmt)
|
|
||||||
{
|
|
||||||
if ((c >= '0' && c <= '9') ||
|
|
||||||
c == '-' || c == '+' || (c == ' ' && fmt_current[fmt_current.Len() - 1] != ' ') || c == '#' || c == '.')
|
|
||||||
{
|
|
||||||
fmt_current += c;
|
|
||||||
}
|
|
||||||
else if (c == '$') // %number$format
|
|
||||||
{
|
|
||||||
if (!haveargnums && argauto > 1)
|
|
||||||
ThrowAbortException(X_FORMAT_ERROR, "Cannot mix explicit and implicit arguments.");
|
|
||||||
FString argnumstr = fmt_current.Mid(1);
|
|
||||||
if (!argnumstr.IsInt()) ThrowAbortException(X_FORMAT_ERROR, "Expected a numeric value for argument number, got '%s'.", argnumstr.GetChars());
|
|
||||||
argnum = argnumstr.ToLong();
|
|
||||||
if (argnum < 1 || argnum >= numparam) ThrowAbortException(X_FORMAT_ERROR, "Not enough arguments for format (tried to access argument %d, %d total).", argnum, numparam);
|
|
||||||
fmt_current = "%";
|
|
||||||
haveargnums = true;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
fmt_current += c;
|
|
||||||
|
|
||||||
switch (c)
|
|
||||||
{
|
|
||||||
// string
|
|
||||||
case 's':
|
|
||||||
{
|
|
||||||
if (argnum < 0 && haveargnums)
|
|
||||||
ThrowAbortException(X_FORMAT_ERROR, "Cannot mix explicit and implicit arguments.");
|
|
||||||
in_fmt = false;
|
|
||||||
// fail if something was found, but it's not a string
|
|
||||||
if (argnum >= numparam) ThrowAbortException(X_FORMAT_ERROR, "Not enough arguments for format.");
|
|
||||||
if (args[argnum].Type != REGT_STRING) ThrowAbortException(X_FORMAT_ERROR, "Expected a string for format %s.", fmt_current.GetChars());
|
|
||||||
// append
|
|
||||||
output.AppendFormat(fmt_current.GetChars(), args[argnum].s().GetChars());
|
|
||||||
if (!haveargnums) argnum = ++argauto;
|
|
||||||
else argnum = -1;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// pointer
|
|
||||||
case 'p':
|
|
||||||
{
|
|
||||||
if (argnum < 0 && haveargnums)
|
|
||||||
ThrowAbortException(X_FORMAT_ERROR, "Cannot mix explicit and implicit arguments.");
|
|
||||||
in_fmt = false;
|
|
||||||
// fail if something was found, but it's not a string
|
|
||||||
if (argnum >= numparam) ThrowAbortException(X_FORMAT_ERROR, "Not enough arguments for format.");
|
|
||||||
if (args[argnum].Type != REGT_POINTER) ThrowAbortException(X_FORMAT_ERROR, "Expected a pointer for format %s.", fmt_current.GetChars());
|
|
||||||
// append
|
|
||||||
output.AppendFormat(fmt_current.GetChars(), args[argnum].a);
|
|
||||||
if (!haveargnums) argnum = ++argauto;
|
|
||||||
else argnum = -1;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// int formats (including char)
|
|
||||||
case 'd':
|
|
||||||
case 'i':
|
|
||||||
case 'u':
|
|
||||||
case 'x':
|
|
||||||
case 'X':
|
|
||||||
case 'o':
|
|
||||||
case 'c':
|
|
||||||
{
|
|
||||||
if (argnum < 0 && haveargnums)
|
|
||||||
ThrowAbortException(X_FORMAT_ERROR, "Cannot mix explicit and implicit arguments.");
|
|
||||||
in_fmt = false;
|
|
||||||
// fail if something was found, but it's not an int
|
|
||||||
if (argnum >= numparam) ThrowAbortException(X_FORMAT_ERROR, "Not enough arguments for format.");
|
|
||||||
if (args[argnum].Type != REGT_INT &&
|
|
||||||
args[argnum].Type != REGT_FLOAT) ThrowAbortException(X_FORMAT_ERROR, "Expected a numeric value for format %s.", fmt_current.GetChars());
|
|
||||||
// append
|
|
||||||
output.AppendFormat(fmt_current.GetChars(), args[argnum].ToInt());
|
|
||||||
if (!haveargnums) argnum = ++argauto;
|
|
||||||
else argnum = -1;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// double formats
|
|
||||||
case 'f':
|
|
||||||
case 'F':
|
|
||||||
case 'e':
|
|
||||||
case 'E':
|
|
||||||
case 'g':
|
|
||||||
case 'G':
|
|
||||||
case 'a':
|
|
||||||
case 'A':
|
|
||||||
{
|
|
||||||
if (argnum < 0 && haveargnums)
|
|
||||||
ThrowAbortException(X_FORMAT_ERROR, "Cannot mix explicit and implicit arguments.");
|
|
||||||
in_fmt = false;
|
|
||||||
// fail if something was found, but it's not a float
|
|
||||||
if (argnum >= numparam) ThrowAbortException(X_FORMAT_ERROR, "Not enough arguments for format.");
|
|
||||||
if (args[argnum].Type != REGT_INT &&
|
|
||||||
args[argnum].Type != REGT_FLOAT) ThrowAbortException(X_FORMAT_ERROR, "Expected a numeric value for format %s.", fmt_current.GetChars());
|
|
||||||
// append
|
|
||||||
output.AppendFormat(fmt_current.GetChars(), args[argnum].ToDouble());
|
|
||||||
if (!haveargnums) argnum = ++argauto;
|
|
||||||
else argnum = -1;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
default:
|
|
||||||
// invalid character
|
|
||||||
output += fmt_current;
|
|
||||||
in_fmt = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (c == '%')
|
|
||||||
{
|
|
||||||
if (i + 1 < fmtstring.Len() && fmtstring[i + 1] == '%')
|
|
||||||
{
|
|
||||||
output += '%';
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
in_fmt = true;
|
|
||||||
fmt_current = "%";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
output += c;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ACTION_RETURN_STRING(output);
|
|
||||||
}
|
|
||||||
|
|
||||||
ExpEmit FxFormat::Emit(VMFunctionBuilder *build)
|
|
||||||
{
|
|
||||||
// Call DecoRandom to generate a random number.
|
|
||||||
VMFunction *callfunc;
|
|
||||||
PSymbol *sym = FindBuiltinFunction(NAME_BuiltinFormat, BuiltinFormat);
|
|
||||||
|
|
||||||
assert(sym->IsKindOf(RUNTIME_CLASS(PSymbolVMFunction)));
|
|
||||||
assert(((PSymbolVMFunction *)sym)->Function != nullptr);
|
|
||||||
callfunc = ((PSymbolVMFunction *)sym)->Function;
|
|
||||||
|
|
||||||
if (build->FramePointer.Fixed) EmitTail = false; // do not tail call if the stack is in use
|
|
||||||
int opcode = (EmitTail ? OP_TAIL_K : OP_CALL_K);
|
|
||||||
|
|
||||||
for (unsigned i = 0; i < ArgList.Size(); i++)
|
|
||||||
EmitParameter(build, ArgList[i], ScriptPosition);
|
|
||||||
build->Emit(opcode, build->GetConstantAddress(callfunc, ATAG_OBJECT), ArgList.Size(), 1);
|
|
||||||
|
|
||||||
if (EmitTail)
|
|
||||||
{
|
|
||||||
ExpEmit call;
|
|
||||||
call.Final = true;
|
|
||||||
return call;
|
|
||||||
}
|
|
||||||
|
|
||||||
ExpEmit out(build, REGT_STRING);
|
|
||||||
build->Emit(OP_RESULT, 0, REGT_STRING, out.RegNum);
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//==========================================================================
|
//==========================================================================
|
||||||
//
|
//
|
||||||
//
|
//
|
||||||
|
|
|
@ -255,7 +255,6 @@ enum EFxType
|
||||||
EFX_MemberFunctionCall,
|
EFX_MemberFunctionCall,
|
||||||
EFX_ActionSpecialCall,
|
EFX_ActionSpecialCall,
|
||||||
EFX_FlopFunctionCall,
|
EFX_FlopFunctionCall,
|
||||||
EFX_Format,
|
|
||||||
EFX_VMFunctionCall,
|
EFX_VMFunctionCall,
|
||||||
EFX_Sequence,
|
EFX_Sequence,
|
||||||
EFX_CompoundStatement,
|
EFX_CompoundStatement,
|
||||||
|
@ -1534,26 +1533,6 @@ public:
|
||||||
ExpEmit Emit(VMFunctionBuilder *build);
|
ExpEmit Emit(VMFunctionBuilder *build);
|
||||||
};
|
};
|
||||||
|
|
||||||
//==========================================================================
|
|
||||||
//
|
|
||||||
// FxFormat
|
|
||||||
//
|
|
||||||
//==========================================================================
|
|
||||||
|
|
||||||
class FxFormat : public FxExpression
|
|
||||||
{
|
|
||||||
FArgumentList ArgList;
|
|
||||||
bool EmitTail;
|
|
||||||
|
|
||||||
public:
|
|
||||||
|
|
||||||
FxFormat(FArgumentList &args, const FScriptPosition &pos);
|
|
||||||
~FxFormat();
|
|
||||||
FxExpression *Resolve(FCompileContext&);
|
|
||||||
PPrototype *ReturnProto();
|
|
||||||
ExpEmit Emit(VMFunctionBuilder *build);
|
|
||||||
};
|
|
||||||
|
|
||||||
//==========================================================================
|
//==========================================================================
|
||||||
//
|
//
|
||||||
// FxVectorBuiltin
|
// FxVectorBuiltin
|
||||||
|
|
|
@ -945,3 +945,176 @@ DEFINE_ACTION_FUNCTION(FString, Replace)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static FString FStringFormat(VM_ARGS)
|
||||||
|
{
|
||||||
|
assert(param[0].Type == REGT_STRING);
|
||||||
|
FString fmtstring = param[0].s().GetChars();
|
||||||
|
|
||||||
|
// note: we don't need a real printf format parser.
|
||||||
|
// enough to simply find the subtitution tokens and feed them to the real printf after checking types.
|
||||||
|
// https://en.wikipedia.org/wiki/Printf_format_string#Format_placeholder_specification
|
||||||
|
FString output;
|
||||||
|
bool in_fmt = false;
|
||||||
|
FString fmt_current;
|
||||||
|
int argnum = 1;
|
||||||
|
int argauto = 1;
|
||||||
|
// % = starts
|
||||||
|
// [0-9], -, +, \s, 0, #, . continue
|
||||||
|
// %, s, d, i, u, fF, eE, gG, xX, o, c, p, aA terminate
|
||||||
|
// various type flags are not supported. not like stuff like 'hh' modifier is to be used in the VM.
|
||||||
|
// the only combination that is parsed locally is %n$...
|
||||||
|
bool haveargnums = false;
|
||||||
|
for (size_t i = 0; i < fmtstring.Len(); i++)
|
||||||
|
{
|
||||||
|
char c = fmtstring[i];
|
||||||
|
if (in_fmt)
|
||||||
|
{
|
||||||
|
if ((c >= '0' && c <= '9') ||
|
||||||
|
c == '-' || c == '+' || (c == ' ' && fmt_current[fmt_current.Len() - 1] != ' ') || c == '#' || c == '.')
|
||||||
|
{
|
||||||
|
fmt_current += c;
|
||||||
|
}
|
||||||
|
else if (c == '$') // %number$format
|
||||||
|
{
|
||||||
|
if (!haveargnums && argauto > 1)
|
||||||
|
ThrowAbortException(X_FORMAT_ERROR, "Cannot mix explicit and implicit arguments.");
|
||||||
|
FString argnumstr = fmt_current.Mid(1);
|
||||||
|
if (!argnumstr.IsInt()) ThrowAbortException(X_FORMAT_ERROR, "Expected a numeric value for argument number, got '%s'.", argnumstr.GetChars());
|
||||||
|
argnum = argnumstr.ToLong();
|
||||||
|
if (argnum < 1 || argnum >= numparam) ThrowAbortException(X_FORMAT_ERROR, "Not enough arguments for format (tried to access argument %d, %d total).", argnum, numparam);
|
||||||
|
fmt_current = "%";
|
||||||
|
haveargnums = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
fmt_current += c;
|
||||||
|
|
||||||
|
switch (c)
|
||||||
|
{
|
||||||
|
// string
|
||||||
|
case 's':
|
||||||
|
{
|
||||||
|
if (argnum < 0 && haveargnums)
|
||||||
|
ThrowAbortException(X_FORMAT_ERROR, "Cannot mix explicit and implicit arguments.");
|
||||||
|
in_fmt = false;
|
||||||
|
// fail if something was found, but it's not a string
|
||||||
|
if (argnum >= numparam) ThrowAbortException(X_FORMAT_ERROR, "Not enough arguments for format.");
|
||||||
|
if (param[argnum].Type != REGT_STRING) ThrowAbortException(X_FORMAT_ERROR, "Expected a string for format %s.", fmt_current.GetChars());
|
||||||
|
// append
|
||||||
|
output.AppendFormat(fmt_current.GetChars(), param[argnum].s().GetChars());
|
||||||
|
if (!haveargnums) argnum = ++argauto;
|
||||||
|
else argnum = -1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// pointer
|
||||||
|
case 'p':
|
||||||
|
{
|
||||||
|
if (argnum < 0 && haveargnums)
|
||||||
|
ThrowAbortException(X_FORMAT_ERROR, "Cannot mix explicit and implicit arguments.");
|
||||||
|
in_fmt = false;
|
||||||
|
// fail if something was found, but it's not a string
|
||||||
|
if (argnum >= numparam) ThrowAbortException(X_FORMAT_ERROR, "Not enough arguments for format.");
|
||||||
|
if (param[argnum].Type != REGT_POINTER) ThrowAbortException(X_FORMAT_ERROR, "Expected a pointer for format %s.", fmt_current.GetChars());
|
||||||
|
// append
|
||||||
|
output.AppendFormat(fmt_current.GetChars(), param[argnum].a);
|
||||||
|
if (!haveargnums) argnum = ++argauto;
|
||||||
|
else argnum = -1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// int formats (including char)
|
||||||
|
case 'd':
|
||||||
|
case 'i':
|
||||||
|
case 'u':
|
||||||
|
case 'x':
|
||||||
|
case 'X':
|
||||||
|
case 'o':
|
||||||
|
case 'c':
|
||||||
|
{
|
||||||
|
if (argnum < 0 && haveargnums)
|
||||||
|
ThrowAbortException(X_FORMAT_ERROR, "Cannot mix explicit and implicit arguments.");
|
||||||
|
in_fmt = false;
|
||||||
|
// fail if something was found, but it's not an int
|
||||||
|
if (argnum >= numparam) ThrowAbortException(X_FORMAT_ERROR, "Not enough arguments for format.");
|
||||||
|
if (param[argnum].Type != REGT_INT &&
|
||||||
|
param[argnum].Type != REGT_FLOAT) ThrowAbortException(X_FORMAT_ERROR, "Expected a numeric value for format %s.", fmt_current.GetChars());
|
||||||
|
// append
|
||||||
|
output.AppendFormat(fmt_current.GetChars(), param[argnum].ToInt());
|
||||||
|
if (!haveargnums) argnum = ++argauto;
|
||||||
|
else argnum = -1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// double formats
|
||||||
|
case 'f':
|
||||||
|
case 'F':
|
||||||
|
case 'e':
|
||||||
|
case 'E':
|
||||||
|
case 'g':
|
||||||
|
case 'G':
|
||||||
|
case 'a':
|
||||||
|
case 'A':
|
||||||
|
{
|
||||||
|
if (argnum < 0 && haveargnums)
|
||||||
|
ThrowAbortException(X_FORMAT_ERROR, "Cannot mix explicit and implicit arguments.");
|
||||||
|
in_fmt = false;
|
||||||
|
// fail if something was found, but it's not a float
|
||||||
|
if (argnum >= numparam) ThrowAbortException(X_FORMAT_ERROR, "Not enough arguments for format.");
|
||||||
|
if (param[argnum].Type != REGT_INT &&
|
||||||
|
param[argnum].Type != REGT_FLOAT) ThrowAbortException(X_FORMAT_ERROR, "Expected a numeric value for format %s.", fmt_current.GetChars());
|
||||||
|
// append
|
||||||
|
output.AppendFormat(fmt_current.GetChars(), param[argnum].ToDouble());
|
||||||
|
if (!haveargnums) argnum = ++argauto;
|
||||||
|
else argnum = -1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
// invalid character
|
||||||
|
output += fmt_current;
|
||||||
|
in_fmt = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (c == '%')
|
||||||
|
{
|
||||||
|
if (i + 1 < fmtstring.Len() && fmtstring[i + 1] == '%')
|
||||||
|
{
|
||||||
|
output += '%';
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
in_fmt = true;
|
||||||
|
fmt_current = "%";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
output += c;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
DEFINE_ACTION_FUNCTION(FString, Format)
|
||||||
|
{
|
||||||
|
PARAM_PROLOGUE;
|
||||||
|
FString s = FStringFormat(param, defaultparam, numparam, ret, numret);
|
||||||
|
ACTION_RETURN_STRING(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
DEFINE_ACTION_FUNCTION(FString, AppendFormat)
|
||||||
|
{
|
||||||
|
PARAM_SELF_STRUCT_PROLOGUE(FString);
|
||||||
|
// first parameter is the self pointer
|
||||||
|
FString s = FStringFormat(param+1, defaultparam, numparam-1, ret, numret);
|
||||||
|
(*self) += s;
|
||||||
|
return 0;
|
||||||
|
}
|
|
@ -722,7 +722,7 @@ type_name1(X) ::= BOOL(T). { X.Int = ZCC_Bool; X.SourceLoc = T.SourceLoc; }
|
||||||
type_name1(X) ::= int_type(X).
|
type_name1(X) ::= int_type(X).
|
||||||
type_name1(X) ::= FLOAT(T). { X.Int = ZCC_FloatAuto; X.SourceLoc = T.SourceLoc; }
|
type_name1(X) ::= FLOAT(T). { X.Int = ZCC_FloatAuto; X.SourceLoc = T.SourceLoc; }
|
||||||
type_name1(X) ::= DOUBLE(T). { X.Int = ZCC_Float64; X.SourceLoc = T.SourceLoc; }
|
type_name1(X) ::= DOUBLE(T). { X.Int = ZCC_Float64; X.SourceLoc = T.SourceLoc; }
|
||||||
type_name1(X) ::= STRING(T). { X.Int = ZCC_String; X.SourceLoc = T.SourceLoc; }
|
//type_name1(X) ::= STRING(T). { X.Int = ZCC_String; X.SourceLoc = T.SourceLoc; } // [ZZ] it's handled elsewhere. this particular line only causes troubles in the form of String.Format being invalid.
|
||||||
type_name1(X) ::= VECTOR2(T). { X.Int = ZCC_Vector2; X.SourceLoc = T.SourceLoc; }
|
type_name1(X) ::= VECTOR2(T). { X.Int = ZCC_Vector2; X.SourceLoc = T.SourceLoc; }
|
||||||
type_name1(X) ::= VECTOR3(T). { X.Int = ZCC_Vector3; X.SourceLoc = T.SourceLoc; }
|
type_name1(X) ::= VECTOR3(T). { X.Int = ZCC_Vector3; X.SourceLoc = T.SourceLoc; }
|
||||||
type_name1(X) ::= NAME(T). { X.Int = ZCC_Name; X.SourceLoc = T.SourceLoc; }
|
type_name1(X) ::= NAME(T). { X.Int = ZCC_Name; X.SourceLoc = T.SourceLoc; }
|
||||||
|
@ -994,6 +994,7 @@ decl_flag(X) ::= READONLY(T). { X.Int = ZCC_ReadOnly; X.SourceLoc = T.SourceLoc
|
||||||
decl_flag(X) ::= DEPRECATED(T). { X.Int = ZCC_Deprecated; X.SourceLoc = T.SourceLoc; }
|
decl_flag(X) ::= DEPRECATED(T). { X.Int = ZCC_Deprecated; X.SourceLoc = T.SourceLoc; }
|
||||||
decl_flag(X) ::= VIRTUAL(T). { X.Int = ZCC_Virtual; X.SourceLoc = T.SourceLoc; }
|
decl_flag(X) ::= VIRTUAL(T). { X.Int = ZCC_Virtual; X.SourceLoc = T.SourceLoc; }
|
||||||
decl_flag(X) ::= OVERRIDE(T). { X.Int = ZCC_Override; X.SourceLoc = T.SourceLoc; }
|
decl_flag(X) ::= OVERRIDE(T). { X.Int = ZCC_Override; X.SourceLoc = T.SourceLoc; }
|
||||||
|
decl_flag(X) ::= VARARG(T). { X.Int = ZCC_VarArg; X.SourceLoc = T.SourceLoc; }
|
||||||
|
|
||||||
func_const(X) ::= . { X.Int = 0; X.SourceLoc = stat->sc->GetMessageLine(); }
|
func_const(X) ::= . { X.Int = 0; X.SourceLoc = stat->sc->GetMessageLine(); }
|
||||||
func_const(X) ::= CONST(T). { X.Int = ZCC_FuncConst; X.SourceLoc = T.SourceLoc; }
|
func_const(X) ::= CONST(T). { X.Int = ZCC_FuncConst; X.SourceLoc = T.SourceLoc; }
|
||||||
|
|
|
@ -1423,10 +1423,11 @@ bool ZCCCompiler::CompileProperties(PClass *type, TArray<ZCC_Property *> &Proper
|
||||||
|
|
||||||
FString ZCCCompiler::FlagsToString(uint32_t flags)
|
FString ZCCCompiler::FlagsToString(uint32_t flags)
|
||||||
{
|
{
|
||||||
const char *flagnames[] = { "native", "static", "private", "protected", "latent", "final", "meta", "action", "deprecated", "readonly", "funcconst", "abstract" };
|
|
||||||
|
const char *flagnames[] = { "native", "static", "private", "protected", "latent", "final", "meta", "action", "deprecated", "readonly", "funcconst", "abstract", "extension", "virtual", "override", "transient", "vararg" };
|
||||||
FString build;
|
FString build;
|
||||||
|
|
||||||
for (int i = 0; i < 12; i++)
|
for (size_t i = 0; i < countof(flagnames); i++)
|
||||||
{
|
{
|
||||||
if (flags & (1 << i))
|
if (flags & (1 << i))
|
||||||
{
|
{
|
||||||
|
@ -2287,6 +2288,11 @@ void ZCCCompiler::CompileFunction(ZCC_StructWork *c, ZCC_FuncDeclarator *f, bool
|
||||||
if (f->Flags & ZCC_Deprecated) varflags |= VARF_Deprecated;
|
if (f->Flags & ZCC_Deprecated) varflags |= VARF_Deprecated;
|
||||||
if (f->Flags & ZCC_Virtual) varflags |= VARF_Virtual;
|
if (f->Flags & ZCC_Virtual) varflags |= VARF_Virtual;
|
||||||
if (f->Flags & ZCC_Override) varflags |= VARF_Override;
|
if (f->Flags & ZCC_Override) varflags |= VARF_Override;
|
||||||
|
if (f->Flags & ZCC_VarArg) varflags |= VARF_VarArg;
|
||||||
|
if ((f->Flags & ZCC_VarArg) && !(f->Flags & ZCC_Native))
|
||||||
|
{
|
||||||
|
Error(f, "'VarArg' can only be used with native methods");
|
||||||
|
}
|
||||||
if (f->Flags & ZCC_Action)
|
if (f->Flags & ZCC_Action)
|
||||||
{
|
{
|
||||||
// Non-Actors cannot have action functions.
|
// Non-Actors cannot have action functions.
|
||||||
|
|
|
@ -136,6 +136,7 @@ static void InitTokenMap()
|
||||||
TOKENDEF (TK_Protected, ZCC_PROTECTED);
|
TOKENDEF (TK_Protected, ZCC_PROTECTED);
|
||||||
TOKENDEF (TK_Latent, ZCC_LATENT);
|
TOKENDEF (TK_Latent, ZCC_LATENT);
|
||||||
TOKENDEF (TK_Virtual, ZCC_VIRTUAL);
|
TOKENDEF (TK_Virtual, ZCC_VIRTUAL);
|
||||||
|
TOKENDEF (TK_VarArg, ZCC_VARARG);
|
||||||
TOKENDEF (TK_Override, ZCC_OVERRIDE);
|
TOKENDEF (TK_Override, ZCC_OVERRIDE);
|
||||||
TOKENDEF (TK_Final, ZCC_FINAL);
|
TOKENDEF (TK_Final, ZCC_FINAL);
|
||||||
TOKENDEF (TK_Meta, ZCC_META);
|
TOKENDEF (TK_Meta, ZCC_META);
|
||||||
|
|
|
@ -36,6 +36,7 @@ enum
|
||||||
ZCC_Virtual = 1 << 13,
|
ZCC_Virtual = 1 << 13,
|
||||||
ZCC_Override = 1 << 14,
|
ZCC_Override = 1 << 14,
|
||||||
ZCC_Transient = 1 << 15,
|
ZCC_Transient = 1 << 15,
|
||||||
|
ZCC_VarArg = 1 << 16
|
||||||
};
|
};
|
||||||
|
|
||||||
// Function parameter modifiers
|
// Function parameter modifiers
|
||||||
|
|
|
@ -308,6 +308,8 @@ enum EPickStart
|
||||||
struct String native
|
struct String native
|
||||||
{
|
{
|
||||||
native void Replace(String pattern, String replacement);
|
native void Replace(String pattern, String replacement);
|
||||||
|
native static vararg String Format(String fmt, ...);
|
||||||
|
native vararg void AppendFormat(String fmt, ...);
|
||||||
}
|
}
|
||||||
|
|
||||||
class Floor : Thinker native
|
class Floor : Thinker native
|
||||||
|
|
|
@ -90,19 +90,19 @@ class BetaSkull : LostSoul
|
||||||
|
|
||||||
extend class Actor
|
extend class Actor
|
||||||
{
|
{
|
||||||
const SKULLSPEED = 20;
|
const DEFSKULLSPEED = 20;
|
||||||
|
|
||||||
void A_SkullAttack(double skullspeed = SKULLSPEED)
|
void A_SkullAttack(double skullspeed = DEFSKULLSPEED)
|
||||||
{
|
{
|
||||||
if (target == null) return;
|
if (target == null) return;
|
||||||
|
|
||||||
if (skullspeed <= 0) skullspeed = SKULLSPEED;
|
if (skullspeed <= 0) skullspeed = DEFSKULLSPEED;
|
||||||
|
|
||||||
bSkullfly = true;
|
bSkullfly = true;
|
||||||
A_PlaySound(AttackSound, CHAN_VOICE);
|
A_PlaySound(AttackSound, CHAN_VOICE);
|
||||||
A_FaceTarget();
|
A_FaceTarget();
|
||||||
VelFromAngle(skullspeed);
|
VelFromAngle(skullspeed);
|
||||||
Vel.Z = (target.pos.Z + target.Height/2 - pos.Z) / DistanceBySpeed(target, speed);
|
Vel.Z = (target.pos.Z + target.Height/2 - pos.Z) / DistanceBySpeed(target, skullspeed);
|
||||||
}
|
}
|
||||||
|
|
||||||
void A_BetaSkullAttack()
|
void A_BetaSkullAttack()
|
||||||
|
|
|
@ -50,7 +50,7 @@ extend class PlayerPawn
|
||||||
let player = self.player;
|
let player = self.player;
|
||||||
|
|
||||||
if (PlayerNumber() != consoleplayer)
|
if (PlayerNumber() != consoleplayer)
|
||||||
A_Log(format ("%s is a cheater: give %s\n", player.GetUserName(), name));
|
A_Log(String.Format ("%s is a cheater: give %s\n", player.GetUserName(), name));
|
||||||
|
|
||||||
if (player.mo == NULL || player.health <= 0)
|
if (player.mo == NULL || player.health <= 0)
|
||||||
{
|
{
|
||||||
|
@ -254,7 +254,7 @@ extend class PlayerPawn
|
||||||
if (type == NULL)
|
if (type == NULL)
|
||||||
{
|
{
|
||||||
if (PlayerNumber() == consoleplayer)
|
if (PlayerNumber() == consoleplayer)
|
||||||
A_Log(format("Unknown item \"%s\"\n", name));
|
A_Log(String.Format("Unknown item \"%s\"\n", name));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -391,7 +391,7 @@ extend class PlayerPawn
|
||||||
if (type == NULL)
|
if (type == NULL)
|
||||||
{
|
{
|
||||||
if (PlayerNumber() == consoleplayer)
|
if (PlayerNumber() == consoleplayer)
|
||||||
A_Log(format("Unknown item \"%s\"\n", name));
|
A_Log(String.Format("Unknown item \"%s\"\n", name));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
|
@ -156,10 +156,9 @@ class CustomSprite : Actor
|
||||||
|
|
||||||
override void BeginPlay ()
|
override void BeginPlay ()
|
||||||
{
|
{
|
||||||
String name;
|
|
||||||
Super.BeginPlay ();
|
Super.BeginPlay ();
|
||||||
|
|
||||||
format(name, "BTIL%04d", args[0] & 0xffff);
|
String name = String.Format("BTIL%04d", args[0] & 0xffff);
|
||||||
picnum = TexMan.CheckForTexture (name, TexMan.TYPE_Build);
|
picnum = TexMan.CheckForTexture (name, TexMan.TYPE_Build);
|
||||||
if (!picnum.Exists())
|
if (!picnum.Exists())
|
||||||
{
|
{
|
||||||
|
|
|
@ -115,7 +115,7 @@ class SkyPicker : Actor
|
||||||
|
|
||||||
if (box == null && args[0] != 0)
|
if (box == null && args[0] != 0)
|
||||||
{
|
{
|
||||||
A_Log(format("Can't find SkyViewpoint %d for sector %d\n", args[0], CurSector.Index()));
|
A_Log(String.Format("Can't find SkyViewpoint %d for sector %d\n", args[0], CurSector.Index()));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in a new issue