mirror of
https://github.com/ZDoom/qzdoom-gpl.git
synced 2024-11-22 12:01:33 +00:00
- implemented the parser basics of a ZScript versioning system.
Note that this completely disables the newly added keywords 'play' and 'ui' for unversioned code to allow using them as identifiers as I have found at least one mod that uses a variable named 'play' that would have been rendered broken otherwise. This also disables many ZScript only keywords for other parsing jobs.
This commit is contained in:
parent
5d3244c3a7
commit
7df698dad8
20 changed files with 268 additions and 144 deletions
|
@ -42,6 +42,31 @@
|
|||
|
||||
// CODE --------------------------------------------------------------------
|
||||
|
||||
void VersionInfo::operator=(const char *string)
|
||||
{
|
||||
char *endp;
|
||||
major = (int16_t)clamp<unsigned long long>(strtoull(string, &endp, 10), 0, USHRT_MAX);
|
||||
if (*endp == '.')
|
||||
{
|
||||
minor = (int16_t)clamp<unsigned long long>(strtoull(endp + 1, &endp, 10), 0, USHRT_MAX);
|
||||
if (*endp == '.')
|
||||
{
|
||||
revision = (int16_t)clamp<unsigned long long>(strtoull(endp + 1, &endp, 10), 0, USHRT_MAX);
|
||||
if (*endp != 0) major = USHRT_MAX;
|
||||
}
|
||||
else if (*endp == 0)
|
||||
{
|
||||
revision = 0;
|
||||
}
|
||||
else major = USHRT_MAX;
|
||||
}
|
||||
else if (*endp == 0)
|
||||
{
|
||||
minor = revision = 0;
|
||||
}
|
||||
else major = USHRT_MAX;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// FScanner Constructor
|
||||
|
|
22
src/sc_man.h
22
src/sc_man.h
|
@ -1,6 +1,23 @@
|
|||
#ifndef __SC_MAN_H__
|
||||
#define __SC_MAN_H__
|
||||
|
||||
struct VersionInfo
|
||||
{
|
||||
uint16_t major;
|
||||
uint16_t minor;
|
||||
uint32_t revision;
|
||||
|
||||
bool Check(unsigned int major, unsigned int minor)
|
||||
{
|
||||
return major < this->major || (major == this->major && minor <= this->minor);
|
||||
}
|
||||
bool Check(unsigned int major, unsigned int minor, unsigned int revision)
|
||||
{
|
||||
return major < this->major || (major == this->major && minor < this->minor) || (major == this->major && minor == this->minor && revision <= this->revision);
|
||||
}
|
||||
void operator=(const char *string);
|
||||
};
|
||||
|
||||
class FScanner
|
||||
{
|
||||
public:
|
||||
|
@ -24,6 +41,10 @@ public:
|
|||
void OpenString(const char *name, FString buffer);
|
||||
void OpenLumpNum(int lump);
|
||||
void Close();
|
||||
void SetParseVersion(VersionInfo ver)
|
||||
{
|
||||
ParseVersion = ver;
|
||||
}
|
||||
|
||||
void SetCMode(bool cmode);
|
||||
void SetEscape(bool esc);
|
||||
|
@ -102,6 +123,7 @@ protected:
|
|||
BYTE StateMode;
|
||||
bool StateOptions;
|
||||
bool Escape;
|
||||
VersionInfo ParseVersion = { 0, 0 }; // no ZScript extensions by default
|
||||
};
|
||||
|
||||
enum
|
||||
|
|
|
@ -143,50 +143,38 @@ std2:
|
|||
'false' { RET(TK_False); }
|
||||
'none' { RET(TK_None); }
|
||||
'auto' { RET(TK_Auto); }
|
||||
'exec' { RET(TK_Exec); }
|
||||
'property' { RET(TK_Property); }
|
||||
'native' { RET(TK_Native); }
|
||||
'var' { RET(TK_Var); }
|
||||
'out' { RET(TK_Out); }
|
||||
'ref' { RET(TK_Ref); }
|
||||
'event' { RET(TK_Event); }
|
||||
'out' { RET(ParseVersion.Check(1,0)? TK_Out : TK_Identifier); }
|
||||
'static' { RET(TK_Static); }
|
||||
'transient' { RET(TK_Transient); }
|
||||
'final' { RET(TK_Final); }
|
||||
'throws' { RET(TK_Throws); }
|
||||
'extend' { RET(TK_Extend); }
|
||||
'public' { RET(TK_Public); }
|
||||
'protected' { RET(TK_Protected); }
|
||||
'private' { RET(TK_Private); }
|
||||
'transient' { RET(ParseVersion.Check(1,0)? TK_Transient : TK_Identifier); }
|
||||
'final' { RET(ParseVersion.Check(1,0)? TK_Final : TK_Identifier); }
|
||||
'extend' { RET(ParseVersion.Check(1,0)? TK_Extend : TK_Identifier); }
|
||||
'protected' { RET(ParseVersion.Check(1,0)? TK_Protected : TK_Identifier); }
|
||||
'private' { RET(ParseVersion.Check(1,0)? TK_Private : TK_Identifier); }
|
||||
'dot' { RET(TK_Dot); }
|
||||
'cross' { RET(TK_Cross); }
|
||||
'localized' { RET(TK_Localized); }
|
||||
'latent' { RET(TK_Latent); }
|
||||
'singular' { RET(TK_Singular); }
|
||||
'config' { RET(TK_Config); }
|
||||
'coerce' { RET(TK_Coerce); }
|
||||
'optional' { RET(TK_Optional); }
|
||||
'export' { RET(TK_Export); }
|
||||
'virtual' { RET(TK_Virtual); }
|
||||
'override' { RET(TK_Override); }
|
||||
'vararg' { RET(TK_VarArg); }
|
||||
'ui' { RET(TK_UI); }
|
||||
'play' { RET(TK_Play); }
|
||||
'clearscope' { RET(TK_ClearScope); }
|
||||
'virtualscope' { RET(TK_VirtualScope); }
|
||||
'super' { RET(TK_Super); }
|
||||
'global' { RET(TK_Global); }
|
||||
'virtual' { RET(ParseVersion.Check(1,0)? TK_Virtual : TK_Identifier); }
|
||||
'override' { RET(ParseVersion.Check(1,0)? TK_Override : TK_Identifier); }
|
||||
'vararg' { RET(ParseVersion.Check(1,0)? TK_VarArg : TK_Identifier); }
|
||||
'ui' { RET(ParseVersion.Check(2,4)? TK_UI : TK_Identifier); }
|
||||
'play' { RET(ParseVersion.Check(2,4)? TK_Play : TK_Identifier); }
|
||||
'clearscope' { RET(ParseVersion.Check(2,4)? TK_ClearScope : TK_Identifier); }
|
||||
'virtualscope' { RET(ParseVersion.Check(2,4)? TK_VirtualScope : TK_Identifier); }
|
||||
'super' { RET(ParseVersion.Check(1,0)? TK_Super : TK_Identifier); }
|
||||
'stop' { RET(TK_Stop); }
|
||||
'null' { RET(TK_Null); }
|
||||
|
||||
'is' { RET(TK_Is); }
|
||||
'replaces' { RET(TK_Replaces); }
|
||||
'is' { RET(ParseVersion.Check(1,0)? TK_Is : TK_Identifier); }
|
||||
'replaces' { RET(ParseVersion.Check(1,0)? TK_Replaces : TK_Identifier); }
|
||||
'states' { RET(TK_States); }
|
||||
'meta' { RET(TK_Meta); }
|
||||
'deprecated' { RET(TK_Deprecated); }
|
||||
'action' { RET(TK_Action); }
|
||||
'readonly' { RET(TK_ReadOnly); }
|
||||
'let' { RET(TK_Let); }
|
||||
'meta' { RET(ParseVersion.Check(1,0)? TK_Meta : TK_Identifier); }
|
||||
'deprecated' { RET(ParseVersion.Check(1,0)? TK_Deprecated : TK_Identifier); }
|
||||
'version' { RET(ParseVersion.Check(2,4)? TK_Version : TK_Identifier); }
|
||||
'action' { RET(ParseVersion.Check(1,0)? TK_Action : TK_Identifier); }
|
||||
'readonly' { RET(ParseVersion.Check(1,0)? TK_ReadOnly : TK_Identifier); }
|
||||
'let' { RET(ParseVersion.Check(1,0)? TK_Let : TK_Identifier); }
|
||||
|
||||
/* Actor state options */
|
||||
'bright' { RET(StateOptions ? TK_Bright : TK_Identifier); }
|
||||
|
|
|
@ -108,8 +108,6 @@ xx(TK_Singular, "'singular'")
|
|||
xx(TK_Config, "'config'")
|
||||
xx(TK_Coerce, "'coerce'")
|
||||
xx(TK_Iterator, "'iterator'")
|
||||
xx(TK_Optional, "'optional'")
|
||||
xx(TK_Export, "'expert'")
|
||||
xx(TK_Virtual, "'virtual'")
|
||||
xx(TK_VarArg, "'vararg'")
|
||||
xx(TK_UI, "'ui'")
|
||||
|
@ -138,6 +136,7 @@ xx(TK_Fail, "'fail'")
|
|||
xx(TK_Wait, "'wait'")
|
||||
xx(TK_Meta, "'meta'")
|
||||
xx(TK_Deprecated, "'deprecated'")
|
||||
xx(TK_Version, "'version'")
|
||||
xx(TK_ReadOnly, "'readonly'")
|
||||
|
||||
xx(TK_CanRaise, "'canraise'")
|
||||
|
|
|
@ -40,6 +40,7 @@ static void SetNodeLine(ZCC_TreeNode *name, int line)
|
|||
struct ClassFlagsBlock {
|
||||
VM_UWORD Flags;
|
||||
ZCC_Identifier *Replaces;
|
||||
VersionInfo Version;
|
||||
};
|
||||
|
||||
struct StateOpts {
|
||||
|
@ -188,6 +189,7 @@ class_head(X) ::= EXTEND CLASS(T) IDENTIFIER(A).
|
|||
head->ParentName = nullptr;
|
||||
head->Flags = ZCC_Extension;
|
||||
head->Replaces = nullptr;
|
||||
head->Version = {0, 0};
|
||||
head->Type = nullptr;
|
||||
head->Symbol = nullptr;
|
||||
X = head;
|
||||
|
@ -200,6 +202,7 @@ class_head(X) ::= CLASS(T) IDENTIFIER(A) class_ancestry(B) class_flags(C).
|
|||
head->ParentName = B;
|
||||
head->Flags = C.Flags;
|
||||
head->Replaces = C.Replaces;
|
||||
head->Version = C.Version;
|
||||
head->Type = nullptr;
|
||||
head->Symbol = nullptr;
|
||||
X = head;
|
||||
|
@ -210,12 +213,13 @@ class_ancestry(X) ::= . { X = NULL; }
|
|||
class_ancestry(X) ::= COLON dottable_id(A). { X = A; /*X-overwrites-A*/ }
|
||||
|
||||
%type class_flags{ClassFlagsBlock}
|
||||
class_flags(X) ::= . { X.Flags = 0; X.Replaces = NULL; }
|
||||
class_flags(X) ::= . { X.Flags = 0; X.Replaces = NULL; X.Version = {0,0}; }
|
||||
class_flags(X) ::= class_flags(A) ABSTRACT. { X.Flags = A.Flags | ZCC_Abstract; X.Replaces = A.Replaces; }
|
||||
class_flags(X) ::= class_flags(A) NATIVE. { X.Flags = A.Flags | ZCC_Native; X.Replaces = A.Replaces; }
|
||||
class_flags(X) ::= class_flags(A) UI. { X.Flags = A.Flags | ZCC_UIFlag; X.Replaces = A.Replaces; }
|
||||
class_flags(X) ::= class_flags(A) PLAY. { X.Flags = A.Flags | ZCC_Play; X.Replaces = A.Replaces; }
|
||||
class_flags(X) ::= class_flags(A) REPLACES dottable_id(B). { X.Flags = A.Flags; X.Replaces = B; }
|
||||
class_flags(X) ::= class_flags(A) VERSION LPAREN STRCONST(C) RPAREN. { X.Flags = A.Flags | ZCC_Version; X.Replaces = A.Replaces; X.Version = C.String->GetChars(); }
|
||||
|
||||
/*----- Dottable Identifier -----*/
|
||||
// This can be either a single identifier or two identifiers connected by a .
|
||||
|
@ -322,16 +326,18 @@ struct_def(X) ::= STRUCT(T) IDENTIFIER(A) struct_flags(S) LBRACE opt_struct_body
|
|||
def->Body = B;
|
||||
def->Type = nullptr;
|
||||
def->Symbol = nullptr;
|
||||
def->Version = S.Version;
|
||||
def->Flags = S.Flags;
|
||||
X = def;
|
||||
}
|
||||
|
||||
%type struct_flags{ClassFlagsBlock}
|
||||
struct_flags(X) ::= . { X.Flags = 0; }
|
||||
struct_flags(X) ::= . { X.Flags = 0; X.Version = {0, 0}; }
|
||||
struct_flags(X) ::= struct_flags(A) UI. { X.Flags = A.Flags | ZCC_UIFlag; }
|
||||
struct_flags(X) ::= struct_flags(A) PLAY. { X.Flags = A.Flags | ZCC_Play; }
|
||||
struct_flags(X) ::= struct_flags(A) CLEARSCOPE. { X.Flags = A.Flags | ZCC_ClearScope; }
|
||||
struct_flags(X) ::= struct_flags(A) NATIVE. { X.Flags = A.Flags | ZCC_Native; }
|
||||
struct_flags(X) ::= struct_flags(A) VERSION LPAREN STRCONST(C) RPAREN. { X.Flags = A.Flags | ZCC_Version; X.Version = C.String->GetChars(); }
|
||||
|
||||
opt_struct_body(X) ::= . { X = NULL; }
|
||||
opt_struct_body(X) ::= struct_body(X).
|
||||
|
@ -966,6 +972,7 @@ decl_flags(X) ::= decl_flags(F) decl_flag(A).
|
|||
X = nil_f;
|
||||
X->Id = nullptr;
|
||||
X->Flags = A.Int;
|
||||
X->Version = { 0, 0 };
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -982,6 +989,8 @@ decl_flags(X) ::= decl_flags(F) ACTION(B) states_opts(A).
|
|||
NEW_AST_NODE(DeclFlags,nil_f,B.SourceLoc);
|
||||
X = nil_f;
|
||||
X->Flags = ZCC_Action;
|
||||
X->Id = nullptr;
|
||||
X->Version = { 0, 0 };
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -991,6 +1000,42 @@ decl_flags(X) ::= decl_flags(F) ACTION(B) states_opts(A).
|
|||
X->Id = A;
|
||||
}
|
||||
|
||||
decl_flags(X) ::= decl_flags(F) DEPRECATED(B) LPAREN STRCONST(A) RPAREN.
|
||||
{
|
||||
if (F == nullptr)
|
||||
{
|
||||
NEW_AST_NODE(DeclFlags,nil_f,B.SourceLoc);
|
||||
X = nil_f;
|
||||
X->Flags = ZCC_Deprecated;
|
||||
X->Id = nullptr;
|
||||
X->Version = { 0, 0 };
|
||||
}
|
||||
else
|
||||
{
|
||||
X = F;
|
||||
X->Flags |= ZCC_Deprecated;
|
||||
}
|
||||
X->Version = A.String->GetChars();
|
||||
}
|
||||
|
||||
decl_flags(X) ::= decl_flags(F) VERSION(B) LPAREN STRINGCONST(A) RPAREN.
|
||||
{
|
||||
if (F == nullptr)
|
||||
{
|
||||
NEW_AST_NODE(DeclFlags,nil_f,B.SourceLoc);
|
||||
X = nil_f;
|
||||
X->Flags = ZCC_Version;
|
||||
X->Id = nullptr;
|
||||
X->Version = { 0, 0 };
|
||||
}
|
||||
else
|
||||
{
|
||||
X = F;
|
||||
X->Flags |= ZCC_Version;
|
||||
}
|
||||
X->Version = A.String->GetChars();
|
||||
}
|
||||
|
||||
decl_flag(X) ::= NATIVE(T). { X.Int = ZCC_Native; X.SourceLoc = T.SourceLoc; }
|
||||
decl_flag(X) ::= STATIC(T). { X.Int = ZCC_Static; X.SourceLoc = T.SourceLoc; }
|
||||
decl_flag(X) ::= PRIVATE(T). { X.Int = ZCC_Private; X.SourceLoc = T.SourceLoc; }
|
||||
|
@ -1000,7 +1045,6 @@ decl_flag(X) ::= FINAL(T). { X.Int = ZCC_Final; X.SourceLoc = T.SourceLoc; }
|
|||
decl_flag(X) ::= META(T). { X.Int = ZCC_Meta; X.SourceLoc = T.SourceLoc; }
|
||||
decl_flag(X) ::= TRANSIENT(T). { X.Int = ZCC_Transient; X.SourceLoc = T.SourceLoc; }
|
||||
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) ::= 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) ::= VARARG(T). { X.Int = ZCC_VarArg; X.SourceLoc = T.SourceLoc; }
|
||||
|
|
|
@ -41,6 +41,7 @@
|
|||
#include "i_system.h"
|
||||
#include "m_argv.h"
|
||||
#include "v_text.h"
|
||||
#include "version.h"
|
||||
#include "zcc_parser.h"
|
||||
#include "zcc_compile.h"
|
||||
|
||||
|
@ -145,6 +146,7 @@ static void InitTokenMap()
|
|||
TOKENDEF (TK_Final, ZCC_FINAL);
|
||||
TOKENDEF (TK_Meta, ZCC_META);
|
||||
TOKENDEF (TK_Deprecated, ZCC_DEPRECATED);
|
||||
TOKENDEF (TK_Version, ZCC_VERSION);
|
||||
TOKENDEF (TK_ReadOnly, ZCC_READONLY);
|
||||
TOKENDEF ('{', ZCC_LBRACE);
|
||||
TOKENDEF ('}', ZCC_RBRACE);
|
||||
|
@ -179,7 +181,6 @@ static void InitTokenMap()
|
|||
TOKENDEF (']', ZCC_RBRACKET);
|
||||
TOKENDEF (TK_In, ZCC_IN);
|
||||
TOKENDEF (TK_Out, ZCC_OUT);
|
||||
TOKENDEF (TK_Optional, ZCC_OPTIONAL);
|
||||
TOKENDEF (TK_Super, ZCC_SUPER);
|
||||
TOKENDEF (TK_Null, ZCC_NULLPTR);
|
||||
TOKENDEF ('~', ZCC_TILDE);
|
||||
|
@ -232,29 +233,36 @@ static void InitTokenMap()
|
|||
|
||||
//**--------------------------------------------------------------------------
|
||||
|
||||
static void ParseSingleFile(const char *filename, int lump, void *parser, ZCCParseState &state)
|
||||
static void ParseSingleFile(FScanner *pSC, const char *filename, int lump, void *parser, ZCCParseState &state)
|
||||
{
|
||||
int tokentype;
|
||||
//bool failed;
|
||||
ZCCToken value;
|
||||
FScanner sc;
|
||||
FScanner lsc;
|
||||
|
||||
if (filename != nullptr)
|
||||
if (pSC == nullptr)
|
||||
{
|
||||
lump = Wads.CheckNumForFullName(filename, true);
|
||||
if (lump >= 0)
|
||||
if (filename != nullptr)
|
||||
{
|
||||
sc.OpenLumpNum(lump);
|
||||
lump = Wads.CheckNumForFullName(filename, true);
|
||||
if (lump >= 0)
|
||||
{
|
||||
lsc.OpenLumpNum(lump);
|
||||
}
|
||||
else
|
||||
{
|
||||
Printf("Could not find script lump '%s'\n", filename);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Printf("Could not find script lump '%s'\n", filename);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else sc.OpenLumpNum(lump);
|
||||
else lsc.OpenLumpNum(lump);
|
||||
|
||||
pSC = &lsc;
|
||||
}
|
||||
FScanner &sc = *pSC;
|
||||
sc.SetParseVersion(state.ParseVersion);
|
||||
state.sc = ≻
|
||||
|
||||
while (sc.GetToken())
|
||||
{
|
||||
value.SourceLoc = sc.GetMessageLine();
|
||||
|
@ -343,9 +351,48 @@ static void DoParse(int lumpnum)
|
|||
#endif
|
||||
|
||||
sc.OpenLumpNum(lumpnum);
|
||||
sc.SetParseVersion({ 2, 4 }); // To get 'version' we need parse version 2.4 for the initial test
|
||||
auto saved = sc.SavePos();
|
||||
|
||||
ParseSingleFile(nullptr, lumpnum, parser, state);
|
||||
if (sc.GetToken())
|
||||
{
|
||||
if (sc.TokenType == TK_Version)
|
||||
{
|
||||
char *endp;
|
||||
sc.MustGetString();
|
||||
state.ParseVersion.major = (int16_t)clamp<long long>(strtoll(sc.String, &endp, 10), -1, USHRT_MAX);
|
||||
if (*endp != '.')
|
||||
{
|
||||
sc.ScriptError("Bad version directive");
|
||||
}
|
||||
state.ParseVersion.minor = (int16_t)clamp<long long>(strtoll(endp + 1, &endp, 10), -1, USHRT_MAX);
|
||||
if (*endp == '.')
|
||||
{
|
||||
state.ParseVersion.revision = (int16_t)clamp<long long>(strtoll(endp + 1, &endp, 10), -1, USHRT_MAX);
|
||||
}
|
||||
else state.ParseVersion.revision = 0;
|
||||
if (*endp != 0)
|
||||
{
|
||||
sc.ScriptError("Bad version directive");
|
||||
}
|
||||
if (state.ParseVersion.major < 0 || state.ParseVersion.minor < 0 || state.ParseVersion.revision < 0)
|
||||
{
|
||||
sc.ScriptError("Bad version directive");
|
||||
}
|
||||
if (!state.ParseVersion.Check(VER_MAJOR, VER_MINOR, VER_REVISION))
|
||||
{
|
||||
sc.ScriptError("Version mismatch. %d.%d expected but only %d.%d supported", state.ParseVersion.major, state.ParseVersion.minor, VER_MAJOR, VER_MINOR);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
state.ParseVersion.major = 1; // 0 excludes all ZSCRIPT keywords from the parser.
|
||||
state.ParseVersion.minor = 0;
|
||||
sc.RestorePos(saved);
|
||||
}
|
||||
}
|
||||
|
||||
ParseSingleFile(&sc, nullptr, lumpnum, parser, state);
|
||||
for (unsigned i = 0; i < Includes.Size(); i++)
|
||||
{
|
||||
lumpnum = Wads.CheckNumForFullName(Includes[i], true);
|
||||
|
@ -362,7 +409,7 @@ static void DoParse(int lumpnum)
|
|||
Wads.GetWadFullName(Wads.GetLumpFile(baselump)), Includes[i].GetChars());
|
||||
}
|
||||
|
||||
ParseSingleFile(nullptr, lumpnum, parser, state);
|
||||
ParseSingleFile(nullptr, nullptr, lumpnum, parser, state);
|
||||
}
|
||||
}
|
||||
Includes.Clear();
|
||||
|
@ -436,16 +483,6 @@ void ParseScripts()
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
CCMD(parse)
|
||||
{
|
||||
if (argv.argc() == 2)
|
||||
{
|
||||
DoParse(argv[1]);
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
static FString ZCCTokenName(int terminal)
|
||||
{
|
||||
if (terminal == ZCC_EOF)
|
||||
|
|
|
@ -41,6 +41,7 @@ enum
|
|||
ZCC_Play = 1 << 18,
|
||||
ZCC_ClearScope = 1 << 19,
|
||||
ZCC_VirtualScope = 1 << 20,
|
||||
ZCC_Version = 1 << 21,
|
||||
};
|
||||
|
||||
// Function parameter modifiers
|
||||
|
@ -194,6 +195,7 @@ struct ZCC_Struct : ZCC_NamedNode
|
|||
VM_UWORD Flags;
|
||||
ZCC_TreeNode *Body;
|
||||
PStruct *Type;
|
||||
VersionInfo Version;
|
||||
};
|
||||
|
||||
struct ZCC_Property : ZCC_NamedNode
|
||||
|
@ -483,6 +485,7 @@ struct ZCC_FuncParamDecl : ZCC_TreeNode
|
|||
struct ZCC_DeclFlags : ZCC_TreeNode
|
||||
{
|
||||
ZCC_Identifier *Id;
|
||||
VersionInfo Version;
|
||||
int Flags;
|
||||
};
|
||||
|
||||
|
@ -542,6 +545,7 @@ struct ZCC_AST
|
|||
FSharedStringArena Strings;
|
||||
FMemArena SyntaxArena;
|
||||
struct ZCC_TreeNode *TopNode;
|
||||
VersionInfo ParseVersion;
|
||||
};
|
||||
|
||||
struct ZCCParseState : public ZCC_AST
|
||||
|
|
|
@ -53,6 +53,10 @@ const char *GetVersionString();
|
|||
#define RC_FILEVERSION 2,3,9999,0
|
||||
#define RC_PRODUCTVERSION 2,3,9999,0
|
||||
#define RC_PRODUCTVERSION2 VERSIONSTR
|
||||
// These are for content versioning. The current state is '2.4'.
|
||||
#define VER_MAJOR 2
|
||||
#define VER_MINOR 4
|
||||
#define VER_REVISION 0
|
||||
|
||||
// Version identifier for network games.
|
||||
// Bump it every time you do a release unless you're certain you
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
version "2.4"
|
||||
#include "zscript/base.txt"
|
||||
#include "zscript/sounddata.txt"
|
||||
#include "zscript/mapdata.txt"
|
||||
|
|
|
@ -237,17 +237,17 @@ class Actor : Thinker native
|
|||
//int ConversationRoot; // THe root of the current dialogue
|
||||
|
||||
// deprecated things.
|
||||
native readonly deprecated double X;
|
||||
native readonly deprecated double Y;
|
||||
native readonly deprecated double Z;
|
||||
native readonly deprecated double VelX;
|
||||
native readonly deprecated double VelY;
|
||||
native readonly deprecated double VelZ;
|
||||
native readonly deprecated double MomX;
|
||||
native readonly deprecated double MomY;
|
||||
native readonly deprecated double MomZ;
|
||||
native deprecated double ScaleX;
|
||||
native deprecated double ScaleY;
|
||||
native readonly deprecated("2.3") double X;
|
||||
native readonly deprecated("2.3") double Y;
|
||||
native readonly deprecated("2.3") double Z;
|
||||
native readonly deprecated("2.3") double VelX;
|
||||
native readonly deprecated("2.3") double VelY;
|
||||
native readonly deprecated("2.3") double VelZ;
|
||||
native readonly deprecated("2.3") double MomX;
|
||||
native readonly deprecated("2.3") double MomY;
|
||||
native readonly deprecated("2.3") double MomZ;
|
||||
native deprecated("2.3") double ScaleX;
|
||||
native deprecated("2.3") double ScaleY;
|
||||
|
||||
//FStrifeDialogueNode *Conversation; // [RH] The dialogue to show when this actor is used.;
|
||||
|
||||
|
@ -716,7 +716,7 @@ class Actor : Thinker native
|
|||
return true;
|
||||
}
|
||||
|
||||
deprecated void A_FaceConsolePlayer(double MaxTurnAngle = 0) {}
|
||||
deprecated("2.3") void A_FaceConsolePlayer(double MaxTurnAngle = 0) {}
|
||||
|
||||
void A_SetSpecial(int spec, int arg0 = 0, int arg1 = 0, int arg2 = 0, int arg3 = 0, int arg4 = 0)
|
||||
{
|
||||
|
@ -867,18 +867,18 @@ class Actor : Thinker native
|
|||
}
|
||||
}
|
||||
|
||||
deprecated void A_MeleeAttack()
|
||||
deprecated("2.3") void A_MeleeAttack()
|
||||
{
|
||||
DoAttack(true, false, MeleeDamage, MeleeSound, NULL, 0);
|
||||
}
|
||||
|
||||
deprecated void A_MissileAttack()
|
||||
deprecated("2.3") void A_MissileAttack()
|
||||
{
|
||||
Class<Actor> MissileType = MissileName;
|
||||
DoAttack(false, true, 0, 0, MissileType, MissileHeight);
|
||||
}
|
||||
|
||||
deprecated void A_ComboAttack()
|
||||
deprecated("2.3") void A_ComboAttack()
|
||||
{
|
||||
Class<Actor> MissileType = MissileName;
|
||||
DoAttack(true, true, MeleeDamage, MeleeSound, MissileType, MissileHeight);
|
||||
|
@ -918,13 +918,13 @@ class Actor : Thinker native
|
|||
native void A_Wander(int flags = 0);
|
||||
native void A_Look2();
|
||||
|
||||
deprecated native void A_BulletAttack();
|
||||
deprecated("2.3") native void A_BulletAttack();
|
||||
native void A_WolfAttack(int flags = 0, sound whattoplay = "weapons/pistol", double snipe = 1.0, int maxdamage = 64, int blocksize = 128, int pointblank = 2, int longrange = 4, double runspeed = 160.0, class<Actor> pufftype = "BulletPuff");
|
||||
native void A_PlaySound(sound whattoplay = "weapons/pistol", int slot = CHAN_BODY, double volume = 1.0, bool looping = false, double attenuation = ATTN_NORM, bool local = false);
|
||||
deprecated void A_PlayWeaponSound(sound whattoplay) { A_PlaySound(whattoplay, CHAN_WEAPON); }
|
||||
deprecated("2.3") void A_PlayWeaponSound(sound whattoplay) { A_PlaySound(whattoplay, CHAN_WEAPON); }
|
||||
native void A_StopSound(int slot = CHAN_VOICE); // Bad default but that's what is originally was...
|
||||
deprecated native void A_PlaySoundEx(sound whattoplay, name slot, bool looping = false, int attenuation = 0);
|
||||
deprecated native void A_StopSoundEx(name slot);
|
||||
deprecated("2.3") native void A_PlaySoundEx(sound whattoplay, name slot, bool looping = false, int attenuation = 0);
|
||||
deprecated("2.3") native void A_StopSoundEx(name slot);
|
||||
native void A_SeekerMissile(int threshold, int turnmax, int flags = 0, int chance = 50, int distance = 10);
|
||||
native action state A_Jump(int chance, statelabel label, ...);
|
||||
native Actor A_SpawnProjectile(class<Actor> missiletype, double spawnheight = 32, double spawnofs_xy = 0, double angle = 0, int flags = 0, double pitch = 0, int ptr = AAPTR_TARGET);
|
||||
|
@ -950,7 +950,7 @@ class Actor : Thinker native
|
|||
native void A_ExtChase(bool usemelee, bool usemissile, bool playactive = true, bool nightmarefast = false);
|
||||
native void A_DropInventory(class<Inventory> itemtype, int amount = -1);
|
||||
native void A_SetBlend(color color1, double alpha, int tics, color color2 = 0);
|
||||
deprecated native void A_ChangeFlag(string flagname, bool value);
|
||||
deprecated("2.3") native void A_ChangeFlag(string flagname, bool value);
|
||||
native void A_ChangeCountFlags(int kill = FLAG_NO_CHANGE, int item = FLAG_NO_CHANGE, int secret = FLAG_NO_CHANGE);
|
||||
native void A_RaiseMaster(int flags = 0);
|
||||
native void A_RaiseChildren(int flags = 0);
|
||||
|
@ -989,10 +989,10 @@ class Actor : Thinker native
|
|||
native void A_SetRoll(double roll, int flags = 0, int ptr = AAPTR_DEFAULT);
|
||||
native void A_ScaleVelocity(double scale, int ptr = AAPTR_DEFAULT);
|
||||
native void A_ChangeVelocity(double x = 0, double y = 0, double z = 0, int flags = 0, int ptr = AAPTR_DEFAULT);
|
||||
deprecated native void A_SetUserVar(name varname, int value);
|
||||
deprecated native void A_SetUserArray(name varname, int index, int value);
|
||||
deprecated native void A_SetUserVarFloat(name varname, double value);
|
||||
deprecated native void A_SetUserArrayFloat(name varname, int index, double value);
|
||||
deprecated("2.3") native void A_SetUserVar(name varname, int value);
|
||||
deprecated("2.3") native void A_SetUserArray(name varname, int index, int value);
|
||||
deprecated("2.3") native void A_SetUserVarFloat(name varname, double value);
|
||||
deprecated("2.3") native void A_SetUserArrayFloat(name varname, int index, double value);
|
||||
native void A_Quake(int intensity, int duration, int damrad, int tremrad, sound sfx = "world/quake");
|
||||
native void A_QuakeEx(int intensityX, int intensityY, int intensityZ, int duration, int damrad, int tremrad, sound sfx = "world/quake", int flags = 0, double mulWaveX = 1, double mulWaveY = 1, double mulWaveZ = 1, int falloff = 0, int highpoint = 0, double rollIntensity = 0, double rollWave = 0);
|
||||
action native void A_SetTics(int tics);
|
||||
|
@ -1090,7 +1090,7 @@ class Actor : Thinker native
|
|||
}
|
||||
|
||||
// Internal functions
|
||||
deprecated private native int __decorate_internal_int__(int i);
|
||||
deprecated private native bool __decorate_internal_bool__(bool b);
|
||||
deprecated private native double __decorate_internal_float__(double f);
|
||||
deprecated("2.3") private native int __decorate_internal_int__(int i);
|
||||
deprecated("2.3") private native bool __decorate_internal_bool__(bool b);
|
||||
deprecated("2.3") private native double __decorate_internal_float__(double f);
|
||||
}
|
||||
|
|
|
@ -170,7 +170,7 @@ extend class Actor
|
|||
|
||||
private native bool CheckFlag(string flagname, int check_pointer = AAPTR_DEFAULT);
|
||||
|
||||
deprecated action state A_CheckFlag(string flagname, statelabel label, int check_pointer = AAPTR_DEFAULT)
|
||||
deprecated("2.3") action state A_CheckFlag(string flagname, statelabel label, int check_pointer = AAPTR_DEFAULT)
|
||||
{
|
||||
return CheckFlag(flagname, check_pointer)? ResolveState(label) : null;
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
struct InputEventData native
|
||||
struct InputEventData native version("2.4")
|
||||
{
|
||||
native uint8 type;
|
||||
native uint8 subtype;
|
||||
|
@ -244,7 +244,7 @@ struct Font native
|
|||
native BrokenLines BreakLines(String text, int maxlen);
|
||||
}
|
||||
|
||||
struct Translation
|
||||
struct Translation version("2.4")
|
||||
{
|
||||
Color colors[256];
|
||||
|
||||
|
@ -290,7 +290,7 @@ struct CVar native
|
|||
native int ResetToDefault();
|
||||
}
|
||||
|
||||
struct GIFont
|
||||
struct GIFont version("2.4")
|
||||
{
|
||||
Name fontname;
|
||||
Name color;
|
||||
|
@ -337,7 +337,7 @@ class Object native
|
|||
virtual virtualscope void OnDestroy() {}
|
||||
}
|
||||
|
||||
class BrokenLines : Object native
|
||||
class BrokenLines : Object native version("2.4")
|
||||
{
|
||||
native int Count();
|
||||
native int StringWidth(int line);
|
||||
|
@ -458,23 +458,23 @@ struct LevelLocals native
|
|||
native int found_items;
|
||||
native int total_monsters;
|
||||
native int killed_monsters;
|
||||
native double gravity;
|
||||
native double aircontrol;
|
||||
native double airfriction;
|
||||
native int airsupply;
|
||||
native double teamdamage;
|
||||
native bool monsterstelefrag;
|
||||
native bool actownspecial;
|
||||
native bool sndseqtotalctrl;
|
||||
native play double gravity;
|
||||
native play double aircontrol;
|
||||
native play double airfriction;
|
||||
native play int airsupply;
|
||||
native readonly double teamdamage;
|
||||
native readonly bool monsterstelefrag;
|
||||
native readonly bool actownspecial;
|
||||
native readonly bool sndseqtotalctrl;
|
||||
native bool allmap;
|
||||
native bool missilesactivateimpact;
|
||||
native bool monsterfallingdamage;
|
||||
native bool checkswitchrange;
|
||||
native bool polygrind;
|
||||
native bool nomonsters;
|
||||
native readonly bool missilesactivateimpact;
|
||||
native readonly bool monsterfallingdamage;
|
||||
native readonly bool checkswitchrange;
|
||||
native readonly bool polygrind;
|
||||
native readonly bool nomonsters;
|
||||
native bool frozen;
|
||||
native bool infinite_flight;
|
||||
native bool no_dlg_freeze;
|
||||
native readonly bool infinite_flight;
|
||||
native readonly bool no_dlg_freeze;
|
||||
// level_info_t *info cannot be done yet.
|
||||
|
||||
native String GetUDMFString(int type, int index, Name key);
|
||||
|
@ -618,7 +618,7 @@ class Floor : Thinker native
|
|||
floorRaiseInstant,
|
||||
floorMoveToValue,
|
||||
floorRaiseToLowestCeiling,
|
||||
floorRaiseuint8xture,
|
||||
floorRaiseByTexture,
|
||||
|
||||
floorLowerAndChange,
|
||||
floorRaiseAndChange,
|
||||
|
@ -626,7 +626,7 @@ class Floor : Thinker native
|
|||
floorRaiseToLowest,
|
||||
floorRaiseToCeiling,
|
||||
floorLowerToLowestCeiling,
|
||||
floorLoweruint8xture,
|
||||
floorLowerByTexture,
|
||||
floorLowerToCeiling,
|
||||
|
||||
donutRaise,
|
||||
|
@ -669,8 +669,8 @@ class Ceiling : Thinker native
|
|||
ceilLowerToNearest,
|
||||
ceilRaiseToHighestFloor,
|
||||
ceilRaiseToFloor,
|
||||
ceilRaiseuint8xture,
|
||||
ceilLoweruint8xture,
|
||||
ceilRaiseByTexture,
|
||||
ceilLowerByTexture,
|
||||
|
||||
genCeilingChg0,
|
||||
genCeilingChgT,
|
||||
|
|
|
@ -2,12 +2,12 @@
|
|||
|
||||
extend class Object
|
||||
{
|
||||
deprecated static int GameType() // deprecated for 2.4.x
|
||||
deprecated("2.4") static int GameType()
|
||||
{
|
||||
return gameinfo.gametype;
|
||||
}
|
||||
|
||||
deprecated static void C_MidPrint(string fontname, string textlabel, bool bold = false) // deprecated for 2.4.x
|
||||
deprecated("2.4") static void C_MidPrint(string fontname, string textlabel, bool bold = false)
|
||||
{
|
||||
let f = Font.GetFont(fontname);
|
||||
if (f == null) return;
|
||||
|
@ -18,7 +18,7 @@ extend class Object
|
|||
|
||||
extend class Actor
|
||||
{
|
||||
deprecated void A_CustomMissile(class<Actor> missiletype, double spawnheight = 32, double spawnofs_xy = 0, double angle = 0, int flags = 0, double pitch = 0, int ptr = AAPTR_TARGET)
|
||||
deprecated("2.3") void A_CustomMissile(class<Actor> missiletype, double spawnheight = 32, double spawnofs_xy = 0, double angle = 0, int flags = 0, double pitch = 0, int ptr = AAPTR_TARGET)
|
||||
{
|
||||
A_SpawnProjectile(missiletype, spawnheight, spawnofs_xy, angle, flags|CMF_BADPITCH, pitch, ptr);
|
||||
}
|
||||
|
@ -26,7 +26,7 @@ extend class Actor
|
|||
|
||||
extend class StateProvider
|
||||
{
|
||||
deprecated action void A_FireCustomMissile(class<Actor> missiletype, double angle = 0, bool useammo = true, double spawnofs_xy = 0, double spawnheight = 0, int flags = 0, double pitch = 0)
|
||||
deprecated("2.3") action void A_FireCustomMissile(class<Actor> missiletype, double angle = 0, bool useammo = true, double spawnofs_xy = 0, double spawnheight = 0, int flags = 0, double pitch = 0)
|
||||
{
|
||||
A_FireProjectile(missiletype, angle, useammo, spawnofs_xy, spawnheight, flags, -pitch);
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
class BaseEvent native { } // just a base class. it doesn't inherit from Object on the scripting side so you can't call Destroy() on it and break everything.
|
||||
class BaseEvent native version("2.4") { } // just a base class. it doesn't inherit from Object on the scripting side so you can't call Destroy() on it and break everything.
|
||||
|
||||
class RenderEvent : BaseEvent native ui
|
||||
class RenderEvent : BaseEvent native ui version("2.4")
|
||||
{
|
||||
native readonly Vector3 ViewPos;
|
||||
native readonly double ViewAngle;
|
||||
|
@ -10,7 +10,7 @@ class RenderEvent : BaseEvent native ui
|
|||
native readonly Actor Camera;
|
||||
}
|
||||
|
||||
class WorldEvent : BaseEvent native play
|
||||
class WorldEvent : BaseEvent native play version("2.4")
|
||||
{
|
||||
// for loaded/unloaded
|
||||
native readonly bool IsSaveGame;
|
||||
|
@ -28,7 +28,7 @@ class WorldEvent : BaseEvent native play
|
|||
native readonly double DamageAngle;
|
||||
}
|
||||
|
||||
class PlayerEvent : BaseEvent native play
|
||||
class PlayerEvent : BaseEvent native play version("2.4")
|
||||
{
|
||||
// this is the player number that caused the event.
|
||||
// note: you can get player struct from this by using players[e.PlayerNumber]
|
||||
|
@ -37,7 +37,7 @@ class PlayerEvent : BaseEvent native play
|
|||
native readonly bool IsReturn;
|
||||
}
|
||||
|
||||
class UiEvent : BaseEvent native ui
|
||||
class UiEvent : BaseEvent native ui version("2.4")
|
||||
{
|
||||
// d_gui.h
|
||||
enum EGUIEvent
|
||||
|
@ -121,7 +121,7 @@ class UiEvent : BaseEvent native ui
|
|||
native readonly bool IsAlt;
|
||||
}
|
||||
|
||||
class InputEvent : BaseEvent native play
|
||||
class InputEvent : BaseEvent native play version("2.4")
|
||||
{
|
||||
enum EGenericEvent
|
||||
{
|
||||
|
@ -270,7 +270,7 @@ class InputEvent : BaseEvent native play
|
|||
native readonly int MouseY;
|
||||
}
|
||||
|
||||
class ConsoleEvent : BaseEvent native
|
||||
class ConsoleEvent : BaseEvent native version("2.4")
|
||||
{
|
||||
// for net events, this will be the activator.
|
||||
// for UI events, this is always -1, and you need to check if level is loaded and use players[consoleplayer].
|
||||
|
@ -279,7 +279,7 @@ class ConsoleEvent : BaseEvent native
|
|||
native readonly int Args[3];
|
||||
}
|
||||
|
||||
class StaticEventHandler : Object native play
|
||||
class StaticEventHandler : Object native play version("2.4")
|
||||
{
|
||||
// static event handlers CAN register other static event handlers.
|
||||
// unlike EventHandler.Create that will not create them.
|
||||
|
@ -335,7 +335,7 @@ class StaticEventHandler : Object native play
|
|||
native bool RequireMouse;
|
||||
}
|
||||
|
||||
class EventHandler : StaticEventHandler native
|
||||
class EventHandler : StaticEventHandler native version("2.4")
|
||||
{
|
||||
clearscope static native StaticEventHandler Create(class<StaticEventHandler> type);
|
||||
clearscope static native StaticEventHandler CreateOnce(class<StaticEventHandler> type);
|
||||
|
|
|
@ -31,10 +31,10 @@ class CustomInventory : StateProvider
|
|||
//---------------------------------------------------------------------------
|
||||
|
||||
// This is only here, because these functions were originally exported on Inventory, despite only working for weapons, so this is here to satisfy some potential old mods having called it through CustomInventory.
|
||||
deprecated action void A_GunFlash(statelabel flash = null, int flags = 0) {}
|
||||
deprecated action void A_Lower() {}
|
||||
deprecated action void A_Raise() {}
|
||||
deprecated action void A_CheckReload() {}
|
||||
deprecated("2.3") action void A_GunFlash(statelabel flash = null, int flags = 0) {}
|
||||
deprecated("2.3") action void A_Lower() {}
|
||||
deprecated("2.3") action void A_Raise() {}
|
||||
deprecated("2.3") action void A_CheckReload() {}
|
||||
native bool CallStateChain (Actor actor, State state);
|
||||
|
||||
//===========================================================================
|
||||
|
|
|
@ -32,7 +32,7 @@
|
|||
**
|
||||
*/
|
||||
|
||||
struct StrifeDialogueNode native
|
||||
struct StrifeDialogueNode native version("2.4")
|
||||
{
|
||||
native Class<Actor> DropType;
|
||||
native int ThisNodeNum;
|
||||
|
@ -49,7 +49,7 @@ struct StrifeDialogueNode native
|
|||
}
|
||||
|
||||
// FStrifeDialogueReply holds responses the player can give to the NPC
|
||||
struct StrifeDialogueReply native
|
||||
struct StrifeDialogueReply native version("2.4")
|
||||
{
|
||||
native StrifeDialogueReply Next;
|
||||
native Class<Actor> GiveType;
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
|
||||
struct KeyBindings native
|
||||
struct KeyBindings native version("2.4")
|
||||
{
|
||||
native static String NameKeys(int k1, int k2);
|
||||
|
||||
|
@ -8,7 +8,7 @@ struct KeyBindings native
|
|||
native void UnbindACommand (String str);
|
||||
}
|
||||
|
||||
struct OptionValues native
|
||||
struct OptionValues native version("2.4")
|
||||
{
|
||||
native static int GetCount(Name group);
|
||||
native static String GetText(Name group, int index);
|
||||
|
@ -16,7 +16,7 @@ struct OptionValues native
|
|||
native static String GetTextValue(Name group, int index);
|
||||
}
|
||||
|
||||
struct JoystickConfig native
|
||||
struct JoystickConfig native version("2.4")
|
||||
{
|
||||
enum EJoyAxis
|
||||
{
|
||||
|
@ -48,7 +48,7 @@ struct JoystickConfig native
|
|||
|
||||
}
|
||||
|
||||
class Menu : Object native ui
|
||||
class Menu : Object native ui version("2.4")
|
||||
{
|
||||
enum EMenuKey
|
||||
{
|
||||
|
@ -287,7 +287,7 @@ class Menu : Object native ui
|
|||
|
||||
}
|
||||
|
||||
class MenuDescriptor : Object native ui
|
||||
class MenuDescriptor : Object native ui version("2.4")
|
||||
{
|
||||
native Name mMenuName;
|
||||
native String mNetgameMessage;
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
//
|
||||
//=============================================================================
|
||||
|
||||
class MenuItemBase : Object native ui
|
||||
class MenuItemBase : Object native ui version("2.4")
|
||||
{
|
||||
protected native double mXpos, mYpos;
|
||||
protected native Name mAction;
|
||||
|
|
|
@ -32,7 +32,7 @@
|
|||
**
|
||||
*/
|
||||
|
||||
struct FOptionMenuSettings
|
||||
struct FOptionMenuSettings version("2.4")
|
||||
{
|
||||
int mTitleColor;
|
||||
int mFontColor;
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
// INTERMISSION
|
||||
// Structure passed e.g. to WI_Start(wb)
|
||||
//
|
||||
struct WBPlayerStruct native
|
||||
struct WBPlayerStruct native version("2.4")
|
||||
{
|
||||
// Player stats, kills, collected items etc.
|
||||
native int skills;
|
||||
|
@ -14,7 +14,7 @@ struct WBPlayerStruct native
|
|||
native int fragcount; // [RH] Cumulative frags for this player
|
||||
}
|
||||
|
||||
struct WBStartStruct native
|
||||
struct WBStartStruct native version("2.4")
|
||||
{
|
||||
native int finished_ep;
|
||||
native int next_ep;
|
||||
|
|
Loading…
Reference in a new issue