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<ARipper> (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<VMValue> &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<PPointer*>(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<PPointer *>(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<PStruct *>(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<PClass*>(static_cast<PPointer*>(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<PPointer*>(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<ZCC_VarDeclarator *> &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<ZCC_VarDeclarator *> &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<Object> 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<Actor> 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