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 e3e37432b8..849f6a477e 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 cc964ecc05..3b2711e246 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 2c66cb42d1..d4f86ecd5b 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 ffca8a8fad..1ded466a9e 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 6911c6688b..bb85a023c2 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 8ef594d1bf..fec5af63e0 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 a41a6def8d..bb21524a52 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 639c762ebd..b7365b2b21 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 46ecbd3faf..3c8d1173b9 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 4bf6f076eb..b7e20f7fc3 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 f004e469a3..d7547a198e 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 24beb7f9c6..750c384e0d 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 6f25318d61..4df76a73e7 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 cf2a757469..b36f93881c 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 3437c848cb..a4a2cc4533 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 074bb89681..3507e45a8b 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 0f0acc3077..3eb855007e 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 a63020bbf5..c899bbd373 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 07f993efe1..c3c641b4ea 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 0000000000..8f4756b5b6 --- /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 13fca8ad10..37e4149f92 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 0000000000..b1d5090738 --- /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 bebf285492..5989702fdf 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