From 9ae272d753009808265fc66a15ebce3ed92de737 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Thu, 24 Nov 2016 20:02:44 +0100 Subject: [PATCH] - scriptified Heretic's blaster. - scriptified all Effect functions of Fastprojectile's children - implemented access to class meta data. - added a VM instruction to retrieve the class metadata, to eliminate the overhead of the function call that would otherwise be needed. - made GetClass() a builtin so that it can use the new instruction Important note about this commit: Scriptifying CFlameMissile::Effect revealed a problem with the virtual function interface: In order to work, this needs to be explicitly enabled for each single native class that may be used as a base for a scripted class. Needless to say, this will end up way too much work, as there are over 100 native classes, excluding those which will be scriptified. But in order to fix the problem this partially broken state needs to be committed first. --- src/dobject.cpp | 6 - src/g_heretic/a_hereticweaps.cpp | 127 --------- src/g_hexen/a_clericflame.cpp | 34 +-- src/g_shared/a_fastprojectile.cpp | 41 +-- src/g_shared/a_sharedglobal.h | 1 - src/info.cpp | 2 - src/info.h | 1 - src/namedef.h | 1 + src/p_mobj.cpp | 22 ++ src/scripting/codegeneration/codegen.cpp | 119 +++++--- src/scripting/codegeneration/codegen.h | 20 ++ src/scripting/vm/vmexec.cpp | 1 + src/scripting/vm/vmexec.h | 6 + src/scripting/vm/vmops.h | 1 + src/scripting/zscript/zcc_compile.cpp | 11 +- wadsrc/static/zscript.txt | 2 + wadsrc/static/zscript/actor.txt | 23 ++ wadsrc/static/zscript/base.txt | 1 - .../static/zscript/heretic/hereticweaps.txt | 173 ------------ .../static/zscript/heretic/weaponblaster.txt | 259 ++++++++++++++++++ wadsrc/static/zscript/hexen/clericflame.txt | 26 +- .../static/zscript/shared/fastprojectile.txt | 41 +++ wadsrc/static/zscript/shared/sharedmisc.txt | 11 - 23 files changed, 499 insertions(+), 430 deletions(-) create mode 100644 wadsrc/static/zscript/heretic/weaponblaster.txt create mode 100644 wadsrc/static/zscript/shared/fastprojectile.txt diff --git a/src/dobject.cpp b/src/dobject.cpp index e3e37432b..849f6a477 100644 --- a/src/dobject.cpp +++ b/src/dobject.cpp @@ -363,12 +363,6 @@ DEFINE_ACTION_FUNCTION(DObject, Destroy) return 0; } -DEFINE_ACTION_FUNCTION(DObject, GetClass) -{ - PARAM_SELF_PROLOGUE(DObject); - ACTION_RETURN_OBJECT(self->GetClass()); -} - //========================================================================== // // diff --git a/src/g_heretic/a_hereticweaps.cpp b/src/g_heretic/a_hereticweaps.cpp index cc964ecc0..3b2711e24 100644 --- a/src/g_heretic/a_hereticweaps.cpp +++ b/src/g_heretic/a_hereticweaps.cpp @@ -46,133 +46,6 @@ void P_DSparilTeleport (AActor *actor); extern bool P_AutoUseChaosDevice (player_t *player); -// Blaster FX 1 ------------------------------------------------------------- - -//---------------------------------------------------------------------------- -// -// Thinker for the ultra-fast blaster PL2 ripper-spawning missile. -// -//---------------------------------------------------------------------------- - -class ABlasterFX1 : public AFastProjectile -{ - DECLARE_CLASS(ABlasterFX1, AFastProjectile) -public: - void Effect (); - int DoSpecialDamage (AActor *target, int damage, FName damagetype); -}; - -int ABlasterFX1::DoSpecialDamage (AActor *target, int damage, FName damagetype) -{ - if (target->IsKindOf (PClass::FindClass ("Ironlich"))) - { // Less damage to Ironlich bosses - damage = pr_bfx1() & 1; - if (!damage) - { - return -1; - } - } - return damage; -} - -void ABlasterFX1::Effect () -{ - if (pr_bfx1t() < 64) - { - Spawn("BlasterSmoke", PosAtZ(MAX(Z() - 8., floorz)), ALLOW_REPLACE); - } -} - -IMPLEMENT_CLASS(ABlasterFX1, false, false, false, false) - -// Ripper ------------------------------------------------------------------- - - -class ARipper : public AActor -{ - DECLARE_CLASS (ARipper, AActor) -public: - int DoSpecialDamage (AActor *target, int damage, FName damagetype); -}; - -IMPLEMENT_CLASS(ARipper, false, false, false, false) - -int ARipper::DoSpecialDamage (AActor *target, int damage, FName damagetype) -{ - if (target->IsKindOf (PClass::FindClass ("Ironlich"))) - { // Less damage to Ironlich bosses - damage = pr_ripd() & 1; - if (!damage) - { - return -1; - } - } - return damage; -} - -//---------------------------------------------------------------------------- -// -// PROC A_FireBlasterPL1 -// -//---------------------------------------------------------------------------- - -DEFINE_ACTION_FUNCTION(AActor, A_FireBlasterPL1) -{ - PARAM_ACTION_PROLOGUE(AActor); - - DAngle ang; - int damage; - player_t *player; - - if (NULL == (player = self->player)) - { - return 0; - } - - AWeapon *weapon = self->player->ReadyWeapon; - if (weapon != NULL) - { - if (!weapon->DepleteAmmo (weapon->bAltFire)) - return 0; - } - DAngle pitch = P_BulletSlope(self); - damage = pr_fb1.HitDice (4); - ang = self->Angles.Yaw; - if (player->refire) - { - ang += pr_fb1.Random2() * (5.625 / 256); - } - P_LineAttack (self, ang, PLAYERMISSILERANGE, pitch, damage, NAME_Hitscan, "BlasterPuff"); - S_Sound (self, CHAN_WEAPON, "weapons/blastershoot", 1, ATTN_NORM); - return 0; -} - -//---------------------------------------------------------------------------- -// -// PROC A_SpawnRippers -// -//---------------------------------------------------------------------------- - -DEFINE_ACTION_FUNCTION(AActor, A_SpawnRippers) -{ - PARAM_SELF_PROLOGUE(AActor); - - unsigned int i; - DAngle ang; - AActor *ripper; - - for(i = 0; i < 8; i++) - { - ripper = Spawn (self->Pos(), ALLOW_REPLACE); - ang = i*45.; - ripper->target = self->target; - ripper->Angles.Yaw = ang; - ripper->VelFromAngle(); - P_CheckMissileSpawn (ripper, self->radius); - } - return 0; -} - // --- Skull rod ------------------------------------------------------------ diff --git a/src/g_hexen/a_clericflame.cpp b/src/g_hexen/a_clericflame.cpp index 2c66cb42d..d4f86ecd5 100644 --- a/src/g_hexen/a_clericflame.cpp +++ b/src/g_hexen/a_clericflame.cpp @@ -25,38 +25,6 @@ void A_CFlameMissile (AActor *); // Flame Missile ------------------------------------------------------------ -class ACFlameMissile : public AFastProjectile -{ - DECLARE_CLASS (ACFlameMissile, AFastProjectile) -public: - void BeginPlay (); - void Effect (); -}; - -IMPLEMENT_CLASS(ACFlameMissile, false, false, false, false) - -void ACFlameMissile::BeginPlay () -{ - special1 = 2; -} - -void ACFlameMissile::Effect () -{ - if (!--special1) - { - special1 = 4; - double newz = Z() - 12; - if (newz < floorz) - { - newz = floorz; - } - AActor *mo = Spawn ("CFlameFloor", PosAtZ(newz), ALLOW_REPLACE); - if (mo) - { - mo->Angles.Yaw = Angles.Yaw; - } - } -} //============================================================================ // @@ -80,7 +48,7 @@ DEFINE_ACTION_FUNCTION(AActor, A_CFlameAttack) if (!weapon->DepleteAmmo (weapon->bAltFire)) return 0; } - P_SpawnPlayerMissile (self, RUNTIME_CLASS(ACFlameMissile)); + P_SpawnPlayerMissile (self, PClass::FindActor("CFlameMissile")); S_Sound (self, CHAN_WEAPON, "ClericFlameFire", 1, ATTN_NORM); return 0; } diff --git a/src/g_shared/a_fastprojectile.cpp b/src/g_shared/a_fastprojectile.cpp index ffca8a8fa..1ded466a9 100644 --- a/src/g_shared/a_fastprojectile.cpp +++ b/src/g_shared/a_fastprojectile.cpp @@ -6,6 +6,7 @@ #include "p_lnspec.h" #include "b_bot.h" #include "p_checkposition.h" +#include "virtual.h" IMPLEMENT_CLASS(AFastProjectile, false, false, false, false) @@ -132,7 +133,13 @@ void AFastProjectile::Tick () if (!frac.isZero() && ripcount <= 0) { ripcount = count >> 3; - Effect(); + + // call the scripted 'Effect' method. + VINDEX(AFastProjectile, Effect); + // Without the type cast this picks the 'void *' assignment... + VMValue params[1] = { (DObject*)this }; + VMFrameStack stack; + stack.Call(VFUNC, params, 1, nullptr, 0, nullptr); } } } @@ -153,35 +160,3 @@ void AFastProjectile::Tick () } -void AFastProjectile::Effect() -{ - FName name = GetClass()->MissileName; - if (name != NAME_None) - { - double hitz = Z()-8; - - if (hitz < floorz) - { - hitz = floorz; - } - // Do not clip this offset to the floor. - hitz += GetClass()->MissileHeight; - - PClassActor *trail = PClass::FindActor(name); - if (trail != NULL) - { - AActor *act = Spawn (trail, PosAtZ(hitz), ALLOW_REPLACE); - if (act != nullptr) - { - if ((flags5 & MF5_GETOWNER) && (target != nullptr)) - act->target = target; - else - act->target = this; - - act->Angles.Pitch = Angles.Pitch; - act->Angles.Yaw = Angles.Yaw; - } - } - } -} - diff --git a/src/g_shared/a_sharedglobal.h b/src/g_shared/a_sharedglobal.h index 6911c6688..bb85a023c 100644 --- a/src/g_shared/a_sharedglobal.h +++ b/src/g_shared/a_sharedglobal.h @@ -228,7 +228,6 @@ class AFastProjectile : public AActor DECLARE_CLASS(AFastProjectile, AActor) public: void Tick (); - virtual void Effect(); }; diff --git a/src/info.cpp b/src/info.cpp index 8ef594d1b..fec5af63e 100644 --- a/src/info.cpp +++ b/src/info.cpp @@ -230,7 +230,6 @@ PClassActor::PClassActor() BurnHeight = -1; GibHealth = INT_MIN; WoundHealth = 6; - PoisonDamage = 0; FastSpeed = -1.; RDFactor = 1.; CameraHeight = INT_MIN; @@ -291,7 +290,6 @@ void PClassActor::DeriveData(PClass *newclass) newa->BloodColor = BloodColor; newa->GibHealth = GibHealth; newa->WoundHealth = WoundHealth; - newa->PoisonDamage = PoisonDamage; newa->FastSpeed = FastSpeed; newa->RDFactor = RDFactor; newa->CameraHeight = CameraHeight; diff --git a/src/info.h b/src/info.h index a41a6def8..bb21524a5 100644 --- a/src/info.h +++ b/src/info.h @@ -296,7 +296,6 @@ public: PalEntry BloodColor; // Colorized blood int GibHealth; // Negative health below which this monster dies an extreme death int WoundHealth; // Health needed to enter wound state - int PoisonDamage; // Amount of poison damage double FastSpeed; // speed in fast mode double RDFactor; // Radius damage factor double CameraHeight; // Height of camera when used as such diff --git a/src/namedef.h b/src/namedef.h index 639c762eb..b7365b2b2 100644 --- a/src/namedef.h +++ b/src/namedef.h @@ -285,6 +285,7 @@ xx(FRandom) xx(Random2) xx(RandomPick) xx(FRandomPick) +xx(GetClass) xx(Exp) xx(Log10) xx(Ceil) diff --git a/src/p_mobj.cpp b/src/p_mobj.cpp index 46ecbd3fa..3c8d1173b 100644 --- a/src/p_mobj.cpp +++ b/src/p_mobj.cpp @@ -310,6 +310,28 @@ DEFINE_FIELD(AActor, ConversationRoot) DEFINE_FIELD(AActor, Conversation) DEFINE_FIELD(AActor, DecalGenerator) +DEFINE_FIELD(PClassActor, Obituary) +DEFINE_FIELD(PClassActor, HitObituary) +DEFINE_FIELD(PClassActor, DeathHeight) +DEFINE_FIELD(PClassActor, BurnHeight) +DEFINE_FIELD(PClassActor, BloodColor) +DEFINE_FIELD(PClassActor, GibHealth) +DEFINE_FIELD(PClassActor, WoundHealth) +DEFINE_FIELD(PClassActor, FastSpeed) +DEFINE_FIELD(PClassActor, RDFactor) +DEFINE_FIELD(PClassActor, CameraHeight) +DEFINE_FIELD(PClassActor, HowlSound) +DEFINE_FIELD(PClassActor, BloodType) +DEFINE_FIELD(PClassActor, BloodType2) +DEFINE_FIELD(PClassActor, BloodType3) +DEFINE_FIELD(PClassActor, DontHurtShooter) +DEFINE_FIELD(PClassActor, ExplosionRadius) +DEFINE_FIELD(PClassActor, ExplosionDamage) +DEFINE_FIELD(PClassActor, MeleeDamage) +DEFINE_FIELD(PClassActor, MeleeSound) +DEFINE_FIELD(PClassActor, MissileName) +DEFINE_FIELD(PClassActor, MissileHeight) + //========================================================================== // // AActor :: Serialize diff --git a/src/scripting/codegeneration/codegen.cpp b/src/scripting/codegeneration/codegen.cpp index 4bf6f076e..b7e20f7fc 100644 --- a/src/scripting/codegeneration/codegen.cpp +++ b/src/scripting/codegeneration/codegen.cpp @@ -5637,12 +5637,6 @@ FxExpression *FxIdentifier::ResolveMember(FCompileContext &ctx, PStruct *classct return nullptr; } - if (vsym->Flags & VARF_Static) - { - // todo. For now these cannot be defined so let's just exit. - ScriptPosition.Message(MSG_ERROR, "Static members not implemented yet."); - return nullptr; - } auto x = isclass ? new FxClassMember(object, vsym, ScriptPosition) : new FxStructMember(object, vsym, ScriptPosition); object = nullptr; return x->Resolve(ctx); @@ -5918,40 +5912,14 @@ FxExpression *FxClassDefaults::Resolve(FCompileContext& ctx) // //========================================================================== -int BuiltinGetDefault(VMFrameStack *stack, VMValue *param, TArray &defaultparam, int numparam, VMReturn *ret, int numret) -{ - assert(numparam == 1); - PARAM_POINTER_AT(0, obj, DObject); - ACTION_RETURN_OBJECT(obj->GetClass()->Defaults); -} - -//========================================================================== -// -// -// -//========================================================================== - ExpEmit FxClassDefaults::Emit(VMFunctionBuilder *build) { - EmitParameter(build, obj, ScriptPosition); - PSymbol *sym = FindBuiltinFunction(NAME_BuiltinGetDefault, BuiltinGetDefault); - - assert(sym->IsKindOf(RUNTIME_CLASS(PSymbolVMFunction))); - assert(((PSymbolVMFunction *)sym)->Function != nullptr); - auto callfunc = ((PSymbolVMFunction *)sym)->Function; - int opcode = (EmitTail ? OP_TAIL_K : OP_CALL_K); - build->Emit(opcode, build->GetConstantAddress(callfunc, ATAG_OBJECT), 1, 1); - - if (EmitTail) - { - ExpEmit call; - call.Final = true; - return call; - } - - ExpEmit out(build, REGT_POINTER); - build->Emit(OP_RESULT, 0, REGT_POINTER, out.RegNum); - return out; + ExpEmit ob = obj->Emit(build); + ob.Free(build); + ExpEmit meta(build, REGT_POINTER); + build->Emit(OP_META, meta.RegNum, ob.RegNum); + build->Emit(OP_LO, meta.RegNum, meta.RegNum, build->GetConstantInt(myoffsetof(PClass, Defaults))); + return meta; } @@ -6272,6 +6240,11 @@ FxStructMember::~FxStructMember() bool FxStructMember::RequestAddress(FCompileContext &ctx, bool *writable) { + // Cannot take the address of metadata variables. + if (membervar->Flags & VARF_Static) + { + return false; + } AddressRequested = true; if (writable != nullptr) *writable = (AddressWritable && !ctx.CheckReadOnly(membervar->Flags) && (!classx->ValueType->IsKindOf(RUNTIME_CLASS(PPointer)) || !static_cast(classx->ValueType)->IsConst)); @@ -6411,6 +6384,14 @@ ExpEmit FxStructMember::Emit(VMFunctionBuilder *build) obj = newobj; } + if (membervar->Flags & VARF_Static) + { + obj.Free(build); + ExpEmit meta(build, REGT_POINTER); + build->Emit(OP_META, meta.RegNum, obj.RegNum); + obj = meta; + } + if (AddressRequested) { if (membervar->Offset == 0) @@ -6953,6 +6934,13 @@ FxExpression *FxFunctionCall::Resolve(FCompileContext& ctx) } break; + case NAME_GetClass: + if (CheckArgSize(NAME_GetClass, ArgList, 0, 0, ScriptPosition)) + { + func = new FxGetClass(new FxSelf(ScriptPosition)); + } + break; + case NAME_Random: // allow calling Random without arguments to default to (0, 255) if (ArgList.Size() == 0) @@ -7132,6 +7120,12 @@ FxExpression *FxMemberFunctionCall::Resolve(FCompileContext& ctx) // handle builtins: Vectors got 2: Length and Unit. if (MethodName == NAME_Length || MethodName == NAME_Unit) { + if (ArgList.Size() > 0) + { + ScriptPosition.Message(MSG_ERROR, "too many parameters in call to %s", MethodName.GetChars()); + delete this; + return nullptr; + } auto x = new FxVectorBuiltin(Self, MethodName); Self = nullptr; delete this; @@ -7144,6 +7138,17 @@ FxExpression *FxMemberFunctionCall::Resolve(FCompileContext& ctx) auto ptype = static_cast(Self->ValueType)->PointedType; if (ptype->IsKindOf(RUNTIME_CLASS(PStruct))) { + if (ptype->IsKindOf(RUNTIME_CLASS(PClass)) && MethodName == NAME_GetClass) + { + if (ArgList.Size() > 0) + { + ScriptPosition.Message(MSG_ERROR, "too many parameters in call to %s", MethodName.GetChars()); + delete this; + return nullptr; + } + auto x = new FxGetClass(Self); + return x->Resolve(ctx); + } cls = static_cast(ptype); } else @@ -7976,6 +7981,44 @@ ExpEmit FxVectorBuiltin::Emit(VMFunctionBuilder *build) return to; } +//========================================================================== +// +// +//========================================================================== + +FxGetClass::FxGetClass(FxExpression *self) + :FxExpression(EFX_GetClass, self->ScriptPosition) +{ + Self = self; +} + +FxGetClass::~FxGetClass() +{ + SAFE_DELETE(Self); +} + +FxExpression *FxGetClass::Resolve(FCompileContext &ctx) +{ + SAFE_RESOLVE(Self, ctx); + if (!Self->IsObject()) + { + ScriptPosition.Message(MSG_ERROR, "GetClass() requires an object"); + delete this; + return nullptr; + } + ValueType = NewClassPointer(static_cast(static_cast(Self->ValueType)->PointedType)); + return this; +} + +ExpEmit FxGetClass::Emit(VMFunctionBuilder *build) +{ + ExpEmit op = Self->Emit(build); + op.Free(build); + ExpEmit to(build, REGT_POINTER); + build->Emit(OP_META, to.RegNum, op.RegNum); + return to; +} + //========================================================================== // // FxSequence :: Resolve diff --git a/src/scripting/codegeneration/codegen.h b/src/scripting/codegeneration/codegen.h index f004e469a..d7547a198 100644 --- a/src/scripting/codegeneration/codegen.h +++ b/src/scripting/codegeneration/codegen.h @@ -286,6 +286,7 @@ enum EFxType EFX_StaticArrayVariable, EFX_CVar, EFX_NamedNode, + EFX_GetClass, EFX_COUNT }; @@ -320,6 +321,7 @@ public: bool IsPointer() const { return ValueType->GetRegType() == REGT_POINTER; } bool IsVector() const { return ValueType == TypeVector2 || ValueType == TypeVector3; }; bool IsBoolCompat() const { return ValueType->GetRegCount() == 1 && (ValueType->GetRegType() == REGT_INT || ValueType->GetRegType() == REGT_FLOAT || ValueType->GetRegType() == REGT_POINTER); } + bool IsObject() const { return ValueType->IsKindOf(RUNTIME_CLASS(PPointer)) && !ValueType->IsKindOf(RUNTIME_CLASS(PClassPointer)) && ValueType != TypeNullPtr && static_cast(ValueType)->PointedType->IsKindOf(RUNTIME_CLASS(PClass)); } virtual ExpEmit Emit(VMFunctionBuilder *build); @@ -1520,6 +1522,24 @@ public: ExpEmit Emit(VMFunctionBuilder *build); }; +//========================================================================== +// +// FxFlopFunctionCall +// +//========================================================================== + +class FxGetClass : public FxExpression +{ + FxExpression *Self; + +public: + + FxGetClass(FxExpression *self); + ~FxGetClass(); + FxExpression *Resolve(FCompileContext&); + ExpEmit Emit(VMFunctionBuilder *build); +}; + //========================================================================== // // FxVMFunctionCall diff --git a/src/scripting/vm/vmexec.cpp b/src/scripting/vm/vmexec.cpp index 24beb7f9c..750c384e0 100644 --- a/src/scripting/vm/vmexec.cpp +++ b/src/scripting/vm/vmexec.cpp @@ -75,6 +75,7 @@ #define ASSERTF(x) assert((unsigned)(x) < f->NumRegF) #define ASSERTA(x) assert((unsigned)(x) < f->NumRegA) #define ASSERTS(x) assert((unsigned)(x) < f->NumRegS) +#define ASSERTO(x) assert((unsigned)(x) < f->NumRegA && reg.atag[x] == ATAG_OBJECT) #define ASSERTKD(x) assert(sfunc != NULL && (unsigned)(x) < sfunc->NumKonstD) #define ASSERTKF(x) assert(sfunc != NULL && (unsigned)(x) < sfunc->NumKonstF) diff --git a/src/scripting/vm/vmexec.h b/src/scripting/vm/vmexec.h index 6f25318d6..4df76a73e 100644 --- a/src/scripting/vm/vmexec.h +++ b/src/scripting/vm/vmexec.h @@ -110,6 +110,12 @@ begin: reg.atag[a] = ATAG_GENERIC; // using ATAG_FRAMEPOINTER will cause endless asserts. NEXTOP; + OP(META): + ASSERTA(a); ASSERTO(B); + reg.a[a] = ((DObject*)reg.a[B])->GetClass(); // I wish this could be done without a special opcode but there's really no good way to guarantee initialization of the Class pointer... + reg.atag[a] = ATAG_OBJECT; + NEXTOP; + OP(LB): ASSERTD(a); ASSERTA(B); ASSERTKD(C); GETADDR(PB,KC,X_READ_NIL); diff --git a/src/scripting/vm/vmops.h b/src/scripting/vm/vmops.h index cf2a75746..b36f93881 100644 --- a/src/scripting/vm/vmops.h +++ b/src/scripting/vm/vmops.h @@ -23,6 +23,7 @@ xx(LKF_R, lk, RFRII8, NOP, 0, 0), // load float constant indexed xx(LKS_R, lk, RSRII8, NOP, 0, 0), // load string constant indexed xx(LKP_R, lk, RPRII8, NOP, 0, 0), // load pointer constant indexed xx(LFP, lf, LFP, NOP, 0, 0), // load frame pointer +xx(META, meta, RPRP, NOP, 0, 0), // load a class's meta class address // Load from memory. rA = *(rB + rkC) xx(LB, lb, RIRPKI, LB_R, 4, REGT_INT), // load byte diff --git a/src/scripting/zscript/zcc_compile.cpp b/src/scripting/zscript/zcc_compile.cpp index 3437c848c..a4a2cc453 100644 --- a/src/scripting/zscript/zcc_compile.cpp +++ b/src/scripting/zscript/zcc_compile.cpp @@ -1298,8 +1298,12 @@ bool ZCCCompiler::CompileFields(PStruct *type, TArray &Fiel if (field->Flags & ZCC_Meta) { - varflags |= VARF_ReadOnly; // metadata implies readonly - // todo: this needs to go into the metaclass and needs some handling + varflags |= VARF_Static|VARF_ReadOnly; // metadata implies readonly + if (!(field->Flags & ZCC_Native)) + { + // Non-native meta data is not implemented yet and requires some groundwork in the class copy code. + Error(field, "Metadata member %s must be native", FName(field->Names->Name).GetChars()); + } } if (field->Type->ArraySize != nullptr) @@ -1320,7 +1324,8 @@ bool ZCCCompiler::CompileFields(PStruct *type, TArray &Fiel if (varflags & VARF_Native) { - fd = FindField(type, FName(name->Name).GetChars()); + auto querytype = (varflags & VARF_Static) ? type->GetClass() : type; + fd = FindField(querytype, FName(name->Name).GetChars()); if (fd == nullptr) { Error(field, "The member variable '%s.%s' has not been exported from the executable.", type->TypeName.GetChars(), FName(name->Name).GetChars()); diff --git a/wadsrc/static/zscript.txt b/wadsrc/static/zscript.txt index 074bb8968..3507e45a8 100644 --- a/wadsrc/static/zscript.txt +++ b/wadsrc/static/zscript.txt @@ -31,6 +31,7 @@ zscript/shared/setcolor.txt zscript/shared/sectoraction.txt zscript/shared/ice.txt zscript/shared/dog.txt +zscript/shared/fastprojectile.txt zscript/doom/doomplayer.txt zscript/doom/possessed.txt @@ -100,6 +101,7 @@ zscript/heretic/weaponwand.txt zscript/heretic/weaponcrossbow.txt zscript/heretic/weapongauntlets.txt zscript/heretic/weaponmace.txt +zscript/heretic/weaponblaster.txt zscript/hexen/baseweapons.txt zscript/hexen/korax.txt diff --git a/wadsrc/static/zscript/actor.txt b/wadsrc/static/zscript/actor.txt index 0f0acc307..3eb855007 100644 --- a/wadsrc/static/zscript/actor.txt +++ b/wadsrc/static/zscript/actor.txt @@ -145,6 +145,29 @@ class Actor : Thinker native native readonly State SeeState; native State MeleeState; native State MissileState; + + native meta String Obituary; // Player was killed by this actor + native meta String HitObituary; // Player was killed by this actor in melee + native meta double DeathHeight; // Height on normal death + native meta double BurnHeight; // Height on burning death + native meta color BloodColor; // Colorized blood + native meta int GibHealth; // Negative health below which this monster dies an extreme death + native meta int WoundHealth; // Health needed to enter wound state + native meta double FastSpeed; // speed in fast mode + native meta double RDFactor; // Radius damage factor + native meta double CameraHeight; // Height of camera when used as such + native meta Sound HowlSound; // Sound being played when electrocuted or poisoned + native meta Name BloodType; // Blood replacement type + native meta Name BloodType2; // Bloopsplatter replacement type + native meta Name BloodType3; // AxeBlood replacement type + native meta bool DontHurtShooter; + native meta int ExplosionRadius; + native meta int ExplosionDamage; + native meta int MeleeDamage; + native meta Sound MeleeSound; + native meta Name MissileName; + native meta double MissileHeight; + // need some definition work first //FRenderStyle RenderStyle; diff --git a/wadsrc/static/zscript/base.txt b/wadsrc/static/zscript/base.txt index a63020bbf..c899bbd37 100644 --- a/wadsrc/static/zscript/base.txt +++ b/wadsrc/static/zscript/base.txt @@ -7,7 +7,6 @@ class Object native native static double G_SkillPropertyFloat(int p); virtual native void Destroy(); - native class GetClass(); } class Thinker : Object native diff --git a/wadsrc/static/zscript/heretic/hereticweaps.txt b/wadsrc/static/zscript/heretic/hereticweaps.txt index 07f993efe..c3c641b4e 100644 --- a/wadsrc/static/zscript/heretic/hereticweaps.txt +++ b/wadsrc/static/zscript/heretic/hereticweaps.txt @@ -7,179 +7,6 @@ class HereticWeapon : Weapon } } -// Blaster ------------------------------------------------------------------ - -class Blaster : HereticWeapon -{ - Default - { - +BLOODSPLATTER - Weapon.SelectionOrder 500; - Weapon.AmmoUse 1; - Weapon.AmmoGive 30; - Weapon.YAdjust 15; - Weapon.AmmoType "BlasterAmmo"; - Weapon.SisterWeapon "BlasterPowered"; - Inventory.PickupMessage "$TXT_WPNBLASTER"; - Tag "$TAG_BLASTER"; - Obituary "$OB_MPBLASTER"; - } - - action native void A_FireBlasterPL1(); - - States - { - Spawn: - WBLS A -1; - Stop; - Ready: - BLSR A 1 A_WeaponReady; - Loop; - Deselect: - BLSR A 1 A_Lower; - Loop; - Select: - BLSR A 1 A_Raise; - Loop; - Fire: - BLSR BC 3; - Hold: - BLSR D 2 A_FireBlasterPL1; - BLSR CB 2; - BLSR A 0 A_ReFire; - Goto Ready; - } -} - -class BlasterPowered : Blaster -{ - Default - { - +WEAPON.POWERED_UP - Weapon.AmmoUse 5; - Weapon.AmmoGive 0; - Weapon.SisterWeapon "Blaster"; - Tag "$TAG_BLASTERP"; - } - - States - { - Fire: - BLSR BC 0; - Hold: - BLSR D 3 A_FireCustomMissile("BlasterFX1"); - BLSR CB 4; - BLSR A 0 A_ReFire; - Goto Ready; - } -} - -// Blaster FX 1 ------------------------------------------------------------- - -class BlasterFX1 : FastProjectile native -{ - Default - { - Radius 12; - Height 8; - Speed 184; - Damage 2; - SeeSound "weapons/blastershoot"; - DeathSound "weapons/blasterhit"; - +SPAWNSOUNDSOURCE - Obituary "$OB_MPPBLASTER"; - } - - native void A_SpawnRippers(); - - States - { - Spawn: - ACLO E 200; - Loop; - Death: - FX18 A 3 BRIGHT A_SpawnRippers; - FX18 B 3 BRIGHT; - FX18 CDEFG 4 BRIGHT; - Stop; - } -} - -// Blaster smoke ------------------------------------------------------------ - -class BlasterSmoke : Actor -{ - Default - { - +NOBLOCKMAP - +NOGRAVITY - +NOTELEPORT - +CANNOTPUSH - RenderStyle "Translucent"; - Alpha 0.4; - } - - States - { - Spawn: - FX18 HIJKL 4; - Stop; - } -} - -// Ripper ------------------------------------------------------------------- - -class Ripper : Actor native -{ - Default - { - Radius 8; - Height 6; - Speed 14; - Damage 1; - Projectile; - +RIPPER - DeathSound "weapons/blasterpowhit"; - Obituary "$OB_MPPBLASTER"; - } - - States - { - Spawn: - FX18 M 4; - FX18 N 5; - Loop; - Death: - FX18 OPQRS 4 BRIGHT; - Stop; - } -} - -// Blaster Puff ------------------------------------------------------------- - -class BlasterPuff : Actor -{ - Default - { - +NOBLOCKMAP - +NOGRAVITY - +PUFFONACTORS - RenderStyle "Add"; - SeeSound "weapons/blasterhit"; - } - - States - { - Crash: - FX17 ABCDE 4 BRIGHT; - Stop; - Spawn: - FX17 FG 3 BRIGHT; - FX17 HIJKL 4 BRIGHT; - Stop; - } -} - // Skull (Horn) Rod --------------------------------------------------------- diff --git a/wadsrc/static/zscript/heretic/weaponblaster.txt b/wadsrc/static/zscript/heretic/weaponblaster.txt new file mode 100644 index 000000000..8f4756b5b --- /dev/null +++ b/wadsrc/static/zscript/heretic/weaponblaster.txt @@ -0,0 +1,259 @@ +// Blaster ------------------------------------------------------------------ + +class Blaster : HereticWeapon +{ + Default + { + +BLOODSPLATTER + Weapon.SelectionOrder 500; + Weapon.AmmoUse 1; + Weapon.AmmoGive 30; + Weapon.YAdjust 15; + Weapon.AmmoType "BlasterAmmo"; + Weapon.SisterWeapon "BlasterPowered"; + Inventory.PickupMessage "$TXT_WPNBLASTER"; + Tag "$TAG_BLASTER"; + Obituary "$OB_MPBLASTER"; + } + + States + { + Spawn: + WBLS A -1; + Stop; + Ready: + BLSR A 1 A_WeaponReady; + Loop; + Deselect: + BLSR A 1 A_Lower; + Loop; + Select: + BLSR A 1 A_Raise; + Loop; + Fire: + BLSR BC 3; + Hold: + BLSR D 2 A_FireBlasterPL1; + BLSR CB 2; + BLSR A 0 A_ReFire; + Goto Ready; + } + + //---------------------------------------------------------------------------- + // + // PROC A_FireBlasterPL1 + // + //---------------------------------------------------------------------------- + + action void A_FireBlasterPL1() + { + if (player == null) + { + return; + } + + Weapon weapon = player.ReadyWeapon; + if (weapon != null) + { + if (!weapon.DepleteAmmo (weapon.bAltFire)) + return; + } + + double pitch = BulletSlope(); + int damage = random[FireBlaster](1, 8) * 4; + double ang = angle; + if (player.refire) + { + ang += Random2[FireBlaster]() * (5.625 / 256); + } + LineAttack (ang, PLAYERMISSILERANGE, pitch, damage, 'Hitscan', "BlasterPuff"); + A_PlaySound ("weapons/blastershoot", CHAN_WEAPON); + } +} + +class BlasterPowered : Blaster +{ + Default + { + +WEAPON.POWERED_UP + Weapon.AmmoUse 5; + Weapon.AmmoGive 0; + Weapon.SisterWeapon "Blaster"; + Tag "$TAG_BLASTERP"; + } + + States + { + Fire: + BLSR BC 0; + Hold: + BLSR D 3 A_FireCustomMissile("BlasterFX1"); + BLSR CB 4; + BLSR A 0 A_ReFire; + Goto Ready; + } +} + +// Blaster FX 1 ------------------------------------------------------------- + +class BlasterFX1 : FastProjectile +{ + Default + { + Radius 12; + Height 8; + Speed 184; + Damage 2; + SeeSound "weapons/blastershoot"; + DeathSound "weapons/blasterhit"; + +SPAWNSOUNDSOURCE + Obituary "$OB_MPPBLASTER"; + } + + States + { + Spawn: + ACLO E 200; + Loop; + Death: + FX18 A 3 BRIGHT A_SpawnRippers; + FX18 B 3 BRIGHT; + FX18 CDEFG 4 BRIGHT; + Stop; + } + + //---------------------------------------------------------------------------- + // + // + // + //---------------------------------------------------------------------------- + + override int DoSpecialDamage (Actor target, int damage, Name damagetype) + { + if (target is "Ironlich") + { // Less damage to Ironlich bosses + damage = random[BlasterFX]() & 1; + if (!damage) + { + return -1; + } + } + return damage; + } + + override void Effect () + { + if (random[BlasterFX]() < 64) + { + Spawn("BlasterSmoke", (pos.xy, max(pos.z - 8, floorz)), ALLOW_REPLACE); + } + } + + //---------------------------------------------------------------------------- + // + // PROC A_SpawnRippers + // + //---------------------------------------------------------------------------- + + void A_SpawnRippers() + { + for(int i = 0; i < 8; i++) + { + Actor ripper = Spawn("Ripper", pos, ALLOW_REPLACE); + ripper.target = target; + ripper.angle = i*45; + ripper.VelFromAngle(); + ripper.CheckMissileSpawn (radius); + } + } +} + +// Blaster smoke ------------------------------------------------------------ + +class BlasterSmoke : Actor +{ + Default + { + +NOBLOCKMAP + +NOGRAVITY + +NOTELEPORT + +CANNOTPUSH + RenderStyle "Translucent"; + Alpha 0.4; + } + + States + { + Spawn: + FX18 HIJKL 4; + Stop; + } +} + +// Ripper ------------------------------------------------------------------- + +class Ripper : Actor +{ + Default + { + Radius 8; + Height 6; + Speed 14; + Damage 1; + Projectile; + +RIPPER + DeathSound "weapons/blasterpowhit"; + Obituary "$OB_MPPBLASTER"; + } + + States + { + Spawn: + FX18 M 4; + FX18 N 5; + Loop; + Death: + FX18 OPQRS 4 BRIGHT; + Stop; + } + + int DoSpecialDamage (Actor target, int damage, Name damagetype) + { + if (target is "Ironlich") + { // Less damage to Ironlich bosses + damage = random[Ripper]() & 1; + if (!damage) + { + return -1; + } + } + return damage; + } + +} + +// Blaster Puff ------------------------------------------------------------- + +class BlasterPuff : Actor +{ + Default + { + +NOBLOCKMAP + +NOGRAVITY + +PUFFONACTORS + RenderStyle "Add"; + SeeSound "weapons/blasterhit"; + } + + States + { + Crash: + FX17 ABCDE 4 BRIGHT; + Stop; + Spawn: + FX17 FG 3 BRIGHT; + FX17 HIJKL 4 BRIGHT; + Stop; + } +} + diff --git a/wadsrc/static/zscript/hexen/clericflame.txt b/wadsrc/static/zscript/hexen/clericflame.txt index 13fca8ad1..37e4149f9 100644 --- a/wadsrc/static/zscript/hexen/clericflame.txt +++ b/wadsrc/static/zscript/hexen/clericflame.txt @@ -170,7 +170,7 @@ class CircleFlame : Actor // Flame Missile ------------------------------------------------------------ -class CFlameMissile : FastProjectile native +class CFlameMissile : FastProjectile { Default { @@ -208,4 +208,28 @@ class CFlameMissile : FastProjectile native CFFX M 3 Bright; Stop; } + + override void BeginPlay () + { + special1 = 2; + } + + override void Effect () + { + if (!--special1) + { + special1 = 4; + double newz = pos.z - 12; + if (newz < floorz) + { + newz = floorz; + } + Actor mo = Spawn ("CFlameFloor", (pos.xy, newz), ALLOW_REPLACE); + if (mo) + { + mo.angle = angle; + } + } + } + } diff --git a/wadsrc/static/zscript/shared/fastprojectile.txt b/wadsrc/static/zscript/shared/fastprojectile.txt new file mode 100644 index 000000000..b1d509073 --- /dev/null +++ b/wadsrc/static/zscript/shared/fastprojectile.txt @@ -0,0 +1,41 @@ +// Fast projectiles -------------------------------------------------------- + +class FastProjectile : Actor native +{ + Default + { + Projectile; + MissileHeight 0; + } + + + virtual void Effect() + { + class trail = MissileName; + if (trail != null) + { + double hitz = pos.z - 8; + + if (hitz < floorz) + { + hitz = floorz; + } + // Do not clip this offset to the floor. + hitz += MissileHeight; + + Actor act = Spawn (trail, (pos.xy, hitz), ALLOW_REPLACE); + if (act != null) + { + if (bGetOwner && target != null) + act.target = target; + else + act.target = self; + + act.angle = angle; + act.pitch = pitch; + } + } + } + +} + diff --git a/wadsrc/static/zscript/shared/sharedmisc.txt b/wadsrc/static/zscript/shared/sharedmisc.txt index bebf28549..5989702fd 100644 --- a/wadsrc/static/zscript/shared/sharedmisc.txt +++ b/wadsrc/static/zscript/shared/sharedmisc.txt @@ -179,17 +179,6 @@ class RandomSpawner : Actor native } } -// Fast projectiles -------------------------------------------------------- - -class FastProjectile : Actor native -{ - Default - { - Projectile; - MissileHeight 0; - } -} - // Sector flag setter ------------------------------------------------------ class SectorFlagSetter : Actor native