diff --git a/src/g_inventory/a_weapons.cpp b/src/g_inventory/a_weapons.cpp index 86819f75e..94e65a33c 100644 --- a/src/g_inventory/a_weapons.cpp +++ b/src/g_inventory/a_weapons.cpp @@ -55,7 +55,6 @@ IMPLEMENT_POINTERS_START(AWeapon) IMPLEMENT_POINTER(SisterWeapon) IMPLEMENT_POINTERS_END -/* DEFINE_FIELD(AWeapon, AmmoType1) DEFINE_FIELD(AWeapon, AmmoType2) DEFINE_FIELD(AWeapon, AmmoGive1) @@ -86,7 +85,6 @@ DEFINE_FIELD(AWeapon, GivenAsMorphWeapon) DEFINE_FIELD(AWeapon, bAltFire) DEFINE_FIELD(AWeapon, WeaponFlags) DEFINE_FIELD(AWeapon, bDehAmmo) -*/ //=========================================================================== // diff --git a/src/p_mobj.cpp b/src/p_mobj.cpp index 5e555e1db..1fd612acf 100644 --- a/src/p_mobj.cpp +++ b/src/p_mobj.cpp @@ -7284,7 +7284,7 @@ AActor *P_SpawnPlayerMissile (AActor *source, double x, double y, double z, DAngle vrange = nofreeaim ? 35. : 0.; if (!pLineTarget) pLineTarget = &scratch; - if (!(flags & ALF_NOWEAPONCHECK) && source->player && source->player->ReadyWeapon && ((source->player->ReadyWeapon->IntVar(NAME_WeaponFlags) & WIF_NOAUTOAIM) || noautoaim)) + if (!(aimflags & ALF_NOWEAPONCHECK) && source->player && source->player->ReadyWeapon && ((source->player->ReadyWeapon->IntVar(NAME_WeaponFlags) & WIF_NOAUTOAIM) || noautoaim)) { // Keep exactly the same angle and pitch as the player's own aim an = angle; diff --git a/src/sc_man_scanner.re b/src/sc_man_scanner.re index 3ef66b760..c4fcf6399 100644 --- a/src/sc_man_scanner.re +++ b/src/sc_man_scanner.re @@ -179,6 +179,7 @@ std2: 'none' { RET(TK_None); } 'auto' { RET(TK_Auto); } 'property' { RET(TK_Property); } + 'flagdef' { RET(ParseVersion >= MakeVersion(3, 7, 0)? TK_FlagDef : TK_Identifier); } 'native' { RET(TK_Native); } 'var' { RET(TK_Var); } 'out' { RET(ParseVersion >= MakeVersion(1, 0, 0)? TK_Out : TK_Identifier); } diff --git a/src/sc_man_tokens.h b/src/sc_man_tokens.h index dfe49518a..82dac84d8 100644 --- a/src/sc_man_tokens.h +++ b/src/sc_man_tokens.h @@ -67,6 +67,7 @@ xx(TK_Long, "'long'") xx(TK_ULong, "'ulong'") xx(TK_Void, "'void'") xx(TK_Struct, "'struct'") +xx(TK_FlagDef, "'flagdef'") xx(TK_Property, "'property'") xx(TK_Class, "'class'") xx(TK_Enum, "'enum'") diff --git a/src/scripting/symbols.cpp b/src/scripting/symbols.cpp index 335edd552..fbb06e900 100644 --- a/src/scripting/symbols.cpp +++ b/src/scripting/symbols.cpp @@ -196,6 +196,29 @@ PProperty::PProperty(FName name, TArray &fields) Variables = std::move(fields); } +/* PProperty *****************************************************************/ + +IMPLEMENT_CLASS(PPropFlag, false, false) + +//========================================================================== +// +// PField - Default Constructor +// +//========================================================================== + +PPropFlag::PPropFlag() + : PSymbol(NAME_None) +{ +} + +PPropFlag::PPropFlag(FName name, PField * field, int bitValue, bool forDecorate) + : PSymbol(name) +{ + Offset = field; + bitval = bitValue; + decorateOnly = forDecorate; +} + //========================================================================== // // diff --git a/src/scripting/symbols.h b/src/scripting/symbols.h index bc6de46b6..954edc85a 100644 --- a/src/scripting/symbols.h +++ b/src/scripting/symbols.h @@ -109,10 +109,11 @@ class PPropFlag : public PSymbol { DECLARE_CLASS(PPropFlag, PSymbol); public: - PPropFlag(FName name, PField *offset, int bitval); + PPropFlag(FName name, PField *offset, int bitval, bool decorateonly); PField *Offset; int bitval; + bool decorateOnly; protected: PPropFlag(); diff --git a/src/scripting/thingdef_data.cpp b/src/scripting/thingdef_data.cpp index c88262298..575a689c3 100644 --- a/src/scripting/thingdef_data.cpp +++ b/src/scripting/thingdef_data.cpp @@ -445,30 +445,10 @@ static FFlagDef InventoryFlagDefs[] = static FFlagDef WeaponFlagDefs[] = { // Weapon flags - DEFINE_FLAG(WIF, NOAUTOFIRE, AWeapon, WeaponFlags), - DEFINE_FLAG(WIF, READYSNDHALF, AWeapon, WeaponFlags), - DEFINE_FLAG(WIF, DONTBOB, AWeapon, WeaponFlags), - DEFINE_FLAG(WIF, AXEBLOOD, AWeapon, WeaponFlags), - DEFINE_FLAG(WIF, NOALERT, AWeapon, WeaponFlags), - DEFINE_FLAG(WIF, AMMO_OPTIONAL, AWeapon, WeaponFlags), - DEFINE_FLAG(WIF, ALT_AMMO_OPTIONAL, AWeapon, WeaponFlags), - DEFINE_FLAG(WIF, PRIMARY_USES_BOTH, AWeapon, WeaponFlags), - DEFINE_FLAG(WIF, WIMPY_WEAPON, AWeapon, WeaponFlags), - DEFINE_FLAG(WIF, POWERED_UP, AWeapon, WeaponFlags), - DEFINE_FLAG(WIF, STAFF2_KICKBACK, AWeapon, WeaponFlags), - DEFINE_FLAG(WIF, MELEEWEAPON, AWeapon, WeaponFlags), - DEFINE_FLAG(WIF, CHEATNOTWEAPON, AWeapon, WeaponFlags), - DEFINE_FLAG(WIF, NO_AUTO_SWITCH, AWeapon, WeaponFlags), - DEFINE_FLAG(WIF, AMMO_CHECKBOTH, AWeapon, WeaponFlags), - DEFINE_FLAG(WIF, NOAUTOAIM, AWeapon, WeaponFlags), - DEFINE_FLAG(WIF, NODEATHDESELECT, AWeapon, WeaponFlags), - DEFINE_FLAG(WIF, NODEATHINPUT, AWeapon, WeaponFlags), - DEFINE_FLAG(WIF, ALT_USES_BOTH, AWeapon, WeaponFlags), - DEFINE_DUMMY_FLAG(NOLMS, false), DEFINE_DUMMY_FLAG(ALLOW_WITH_RESPAWN_INVUL, false), - DEFINE_DUMMY_FLAG(BFG, true), - DEFINE_DUMMY_FLAG(EXPLOSIVE, true), + DEFINE_DUMMY_FLAG(BFG, false), + DEFINE_DUMMY_FLAG(EXPLOSIVE, false), }; @@ -511,6 +491,8 @@ static const struct FFlagList { const PClass * const *Type; FFlagDef *Defs; int }; #define NUM_FLAG_LISTS (countof(FlagLists)) +static FFlagDef forInternalFlags; + //========================================================================== // // Find a flag by name using a binary search @@ -548,6 +530,47 @@ static FFlagDef *FindFlag (FFlagDef *flags, int numflags, const char *flag) FFlagDef *FindFlag (const PClass *type, const char *part1, const char *part2, bool strict) { + + if (part2 == nullptr) + { + FStringf internalname("@flagdef@%s", part1); + FName name(internalname, true); + if (name != NAME_None) + { + auto field = dyn_cast(type->FindSymbol(name, true)); + if (field != nullptr && (!strict || !field->decorateOnly)) + { + forInternalFlags.fieldsize = 4; + forInternalFlags.name = ""; + forInternalFlags.flagbit = 1 << field->bitval; + forInternalFlags.structoffset = field->Offset->Offset; + forInternalFlags.varflags = 0; + return &forInternalFlags; + } + } + } + else + { + FStringf internalname("@flagdef@%s.%s", part1, part2); + FName name(internalname, true); + if (name != NAME_None) + { + auto field = dyn_cast(type->FindSymbol(name, true)); + if (field != nullptr) + { + forInternalFlags.fieldsize = 4; + forInternalFlags.name = ""; + forInternalFlags.flagbit = 1 << field->bitval; + forInternalFlags.structoffset = field->Offset->Offset; + forInternalFlags.varflags = 0; + return &forInternalFlags; + } + } + } + + // Not found. Try the internal flag definitions. + + FFlagDef *def; if (part2 == NULL) diff --git a/src/scripting/zscript/ast.cpp b/src/scripting/zscript/ast.cpp index 773173503..99453901d 100644 --- a/src/scripting/zscript/ast.cpp +++ b/src/scripting/zscript/ast.cpp @@ -348,6 +348,17 @@ static void PrintProperty(FLispString &out, ZCC_TreeNode *node) out.Close(); } +static void PrintFlagDef(FLispString &out, ZCC_TreeNode *node) +{ + ZCC_FlagDef *snode = (ZCC_FlagDef *)node; + out.Break(); + out.Open("flagdef"); + out.AddName(snode->NodeName); + out.AddName(snode->RefName); + out.AddInt(snode->BitValue); + out.Close(); +} + static void PrintStaticArrayState(FLispString &out, ZCC_TreeNode *node) { auto *snode = (ZCC_StaticArrayStatement *)node; @@ -959,6 +970,7 @@ void (* const TreeNodePrinter[NUM_AST_NODE_TYPES])(FLispString &, ZCC_TreeNode * PrintExprClassCast, PrintStaticArrayState, PrintProperty, + PrintFlagDef, }; FString ZCC_PrintAST(ZCC_TreeNode *root) diff --git a/src/scripting/zscript/zcc-parse.lemon b/src/scripting/zscript/zcc-parse.lemon index 97b63198a..83da14100 100644 --- a/src/scripting/zscript/zcc-parse.lemon +++ b/src/scripting/zscript/zcc-parse.lemon @@ -313,6 +313,7 @@ class_innards(X) ::= . { X = NULL; } class_innards(X) ::= class_innards(X) class_member(B). { SAFE_APPEND(X,B); } %type property_def{ZCC_Property *} +%type flag_def{ZCC_FlagDef *} %type struct_def{ZCC_Struct *} %type enum_def {ZCC_Enum *} %type states_def {ZCC_States *} @@ -325,6 +326,7 @@ class_member(X) ::= states_def(A). { X = A; /*X-overwrites-A*/ } class_member(X) ::= default_def(A). { X = A; /*X-overwrites-A*/ } class_member(X) ::= const_def(A). { X = A; /*X-overwrites-A*/ } class_member(X) ::= property_def(A). { X = A; /*X-overwrites-A*/ } +class_member(X) ::= flag_def(A). { X = A; /*X-overwrites-A*/ } class_member(X) ::= staticarray_statement(A). { X = A; /*X-overwrites-A*/ } @@ -344,6 +346,16 @@ property_def(X) ::= PROPERTY(T) IDENTIFIER(A) COLON identifier_list(B) SEMICOLON X = def; } +flag_def(X) ::= FLAGDEF(T) IDENTIFIER(A) COLON IDENTIFIER(B) COMMA INTCONST(C) SEMICOLON. +{ + NEW_AST_NODE(FlagDef,def,T); + def->NodeName = A.Name(); + def->RefName = B.Name(); + def->BitValue = C.Int; + X = def; +} + + identifier_list(X) ::= IDENTIFIER(A). { NEW_AST_NODE(Identifier,id,A); diff --git a/src/scripting/zscript/zcc_compile.cpp b/src/scripting/zscript/zcc_compile.cpp index ab31ee05f..ae36d3589 100644 --- a/src/scripting/zscript/zcc_compile.cpp +++ b/src/scripting/zscript/zcc_compile.cpp @@ -175,6 +175,10 @@ void ZCCCompiler::ProcessClass(ZCC_Class *cnode, PSymbolTreeNode *treenode) cls->Properties.Push(static_cast(node)); break; + case AST_FlagDef: + cls->FlagDefs.Push(static_cast(node)); + break; + case AST_VarDeclarator: cls->Fields.Push(static_cast(node)); break; @@ -1375,6 +1379,10 @@ void ZCCCompiler::CompileAllProperties() { if (c->Properties.Size() > 0) CompileProperties(c->ClassType(), c->Properties, c->Type()->TypeName); + + if (c->FlagDefs.Size() > 0) + CompileFlagDefs(c->ClassType(), c->FlagDefs, c->Type()->TypeName); + } } @@ -1415,20 +1423,83 @@ bool ZCCCompiler::CompileProperties(PClass *type, TArray &Proper fields.Push(f); id = (ZCC_Identifier*)id->SiblingNext; } while (id != p->Body); + + FString qualifiedname; + // Store the full qualified name and prepend some 'garbage' to the name so that no conflicts with other symbol types can happen. + // All these will be removed from the symbol table after the compiler finishes to free up the allocated space. + FName name = FName(p->NodeName); + if (prefix == NAME_None) qualifiedname.Format("@property@%s", name.GetChars()); + else qualifiedname.Format("@property@%s.%s", prefix.GetChars(), name.GetChars()); + + fields.ShrinkToFit(); + if (!type->VMType->Symbols.AddSymbol(Create(qualifiedname, fields))) + { + Error(id, "Unable to add property %s to class %s", FName(p->NodeName).GetChars(), type->TypeName.GetChars()); + } } + } + return true; +} - FString qualifiedname; - // Store the full qualified name and prepend some 'garbage' to the name so that no conflicts with other symbol types can happen. - // All these will be removed from the symbol table after the compiler finishes to free up the allocated space. - FName name = FName(p->NodeName); - if (prefix == NAME_None) qualifiedname.Format("@property@%s", name.GetChars()); - else qualifiedname.Format("@property@%s.%s", prefix.GetChars(), name.GetChars()); +//========================================================================== +// +// ZCCCompiler :: CompileProperties +// +// builds the internal structure of a single class or struct +// +//========================================================================== - fields.ShrinkToFit(); - if (!type->VMType->Symbols.AddSymbol(Create(qualifiedname, fields))) +bool ZCCCompiler::CompileFlagDefs(PClass *type, TArray &Properties, FName prefix) +{ + if (!type->IsDescendantOf(RUNTIME_CLASS(AActor))) + { + Error(Properties[0], "Flags can only be defined for actors"); + return false; + } + for (auto p : Properties) + { + PField *field; + FName referenced = FName(p->RefName); + + if (FName(p->NodeName) == FName("prefix") && Wads.GetLumpFile(Lump) == 0) { - Error(id, "Unable to add property %s to class %s", FName(p->NodeName).GetChars(), type->TypeName.GetChars()); + // only for internal definitions: Allow setting a prefix. This is only for compatiblity with the old DECORATE property parser, but not for general use. + prefix = referenced; } + else + { + field = dyn_cast(type->FindSymbol(referenced, true)); + if (field == nullptr) + { + Error(p, "Variable %s not found in %s", referenced.GetChars(), type->TypeName.GetChars()); + } + if (!field->Type->isInt() || field->Type->Size != 4) + { + Error(p, "Variable %s in %s must have a size of 4 bytes for use as flag storage", referenced.GetChars(), type->TypeName.GetChars()); + } + + + FString qualifiedname; + // Store the full qualified name and prepend some 'garbage' to the name so that no conflicts with other symbol types can happen. + // All these will be removed from the symbol table after the compiler finishes to free up the allocated space. + FName name = FName(p->NodeName); + for (int i = 0; i < 2; i++) + { + if (i == 0) qualifiedname.Format("@flagdef@%s", name.GetChars()); + else + { + if (prefix == NAME_None) continue; + qualifiedname.Format("@flagdef@%s.%s", prefix.GetChars(), name.GetChars()); + } + + if (!type->VMType->Symbols.AddSymbol(Create(qualifiedname, field, p->BitValue, i == 0 && prefix != NAME_None))) + { + Error(p, "Unable to add flag definition %s to class %s", FName(p->NodeName).GetChars(), type->TypeName.GetChars()); + } + } + + type->VMType->AddNativeField(FStringf("b%s", name.GetChars()), TypeSInt32, field->Offset, 0, 1 << p->BitValue); + } } return true; } diff --git a/src/scripting/zscript/zcc_compile.h b/src/scripting/zscript/zcc_compile.h index 9a4a0af8b..238b5431c 100644 --- a/src/scripting/zscript/zcc_compile.h +++ b/src/scripting/zscript/zcc_compile.h @@ -54,6 +54,7 @@ struct ZCC_ClassWork : public ZCC_StructWork TArray Defaults; TArray States; TArray Properties; + TArray FlagDefs; ZCC_ClassWork(ZCC_Class * s, PSymbolTreeNode *n) { @@ -110,6 +111,7 @@ private: bool CompileFields(PContainerType *type, TArray &Fields, PClass *Outer, PSymbolTable *TreeNodes, bool forstruct, bool hasnativechildren = false); void CompileAllProperties(); bool CompileProperties(PClass *type, TArray &Properties, FName prefix); + bool CompileFlagDefs(PClass *type, TArray &FlagDefs, FName prefix); FString FlagsToString(uint32_t flags); PType *DetermineType(PType *outertype, ZCC_TreeNode *field, FName name, ZCC_Type *ztype, bool allowarraytypes, bool formember); PType *ResolveArraySize(PType *baseType, ZCC_Expression *arraysize, PContainerType *cls); diff --git a/src/scripting/zscript/zcc_parser.cpp b/src/scripting/zscript/zcc_parser.cpp index 9093b7b51..fde8e1f3f 100644 --- a/src/scripting/zscript/zcc_parser.cpp +++ b/src/scripting/zscript/zcc_parser.cpp @@ -150,6 +150,7 @@ static void InitTokenMap() TOKENDEF ('}', ZCC_RBRACE); TOKENDEF (TK_Struct, ZCC_STRUCT); TOKENDEF (TK_Property, ZCC_PROPERTY); + TOKENDEF (TK_FlagDef, ZCC_FLAGDEF); TOKENDEF (TK_Transient, ZCC_TRANSIENT); TOKENDEF (TK_Enum, ZCC_ENUM); TOKENDEF2(TK_SByte, ZCC_SBYTE, NAME_sByte); diff --git a/src/scripting/zscript/zcc_parser.h b/src/scripting/zscript/zcc_parser.h index 1089b2ed8..272d7e141 100644 --- a/src/scripting/zscript/zcc_parser.h +++ b/src/scripting/zscript/zcc_parser.h @@ -134,6 +134,7 @@ enum EZCCTreeNodeType AST_ClassCast, AST_StaticArrayStatement, AST_Property, + AST_FlagDef, NUM_AST_NODE_TYPES }; @@ -226,6 +227,12 @@ struct ZCC_Property : ZCC_NamedNode ZCC_TreeNode *Body; }; +struct ZCC_FlagDef : ZCC_NamedNode +{ + ENamedName RefName; + int BitValue; +}; + struct ZCC_Class : ZCC_Struct { ZCC_Identifier *ParentName; diff --git a/wadsrc/static/zscript/inventory/weapons.txt b/wadsrc/static/zscript/inventory/weapons.txt index e98dba8e8..58b2a071d 100644 --- a/wadsrc/static/zscript/inventory/weapons.txt +++ b/wadsrc/static/zscript/inventory/weapons.txt @@ -59,6 +59,26 @@ class Weapon : StateProvider native property SlotNumber: SlotNumber; property SlotPriority: SlotPriority; + flagdef NoAutoFire: WeaponFlags, 0; // weapon does not autofire + flagdef ReadySndHalf: WeaponFlags, 1; // ready sound is played ~1/2 the time + flagdef DontBob: WeaponFlags, 2; // don't bob the weapon + flagdef AxeBlood: WeaponFlags, 3; // weapon makes axe blood on impact + flagdef NoAlert: WeaponFlags, 4; // weapon does not alert monsters + flagdef Ammo_Optional: WeaponFlags, 5; // weapon can use ammo but does not require it + flagdef Alt_Ammo_Optional: WeaponFlags, 6; // alternate fire can use ammo but does not require it + flagdef Primary_Uses_Both: WeaponFlags, 7; // primary fire uses both ammo + flagdef Alt_Uses_Both: WeaponFlags, 8; // alternate fire uses both ammo + flagdef Wimpy_Weapon:WeaponFlags, 9; // change away when ammo for another weapon is replenished + flagdef Powered_Up: WeaponFlags, 10; // this is a tome-of-power'ed version of its sister + flagdef Ammo_CheckBoth: WeaponFlags, 11; // check for both primary and secondary fire before switching it off + flagdef No_Auto_Switch: WeaponFlags, 12; // never switch to this weapon when it's picked up + flagdef Staff2_Kickback: WeaponFlags, 13; // the powered-up Heretic staff has special kickback + flagdef NoAutoaim: WeaponFlags, 14; // this weapon never uses autoaim (useful for ballistic projectiles) + flagdef MeleeWeapon: WeaponFlags, 15; // melee weapon. Used by monster AI with AVOIDMELEE. + flagdef NoDeathDeselect: WeaponFlags, 16; // Don't jump to the Deselect state when the player dies + flagdef NoDeathInput: WeaponFlags, 17; // The weapon cannot be fired/reloaded/whatever when the player is dead + flagdef CheatNotWeapon: WeaponFlags, 18; // Give cheat considers this not a weapon (used by Sigil) + Default { Inventory.PickupSound "misc/w_pkup";