- scriptified A_Mushroom to test something a bit more complex.

- fixed: FxMinusSign trashed local variables that were used with negation.
- fixed: FxConditional only handled ints and floats, but not pointers and strings.
- fixed: A 'no states in non-actors' error was triggered, even for classes without any states.
This commit is contained in:
Christoph Oelckers 2016-10-30 14:00:11 +01:00
parent 9eeb56212b
commit f8ccda2dc8
15 changed files with 290 additions and 222 deletions

View file

@ -838,7 +838,6 @@ set( NOT_COMPILED_SOURCE_FILES
g_doom/a_archvile.cpp
g_doom/a_bossbrain.cpp
g_doom/a_doomweaps.cpp
g_doom/a_fatso.cpp
g_doom/a_keen.cpp
g_doom/a_lostsoul.cpp
g_doom/a_painelemental.cpp

View file

@ -1488,6 +1488,14 @@ static int PatchFrame (int frameNum)
return result;
}
// there is exactly one place where this is needed and we do not want to expose the state internals to ZSCRIPT.
DEFINE_ACTION_FUNCTION(AActor, isDEHState)
{
PARAM_PROLOGUE;
PARAM_STATE(state);
ACTION_RETURN_BOOL(state != nullptr && (state->DefineFlags & SDF_DEHACKED));
}
static int PatchSprite (int sprNum)
{
int result;

View file

@ -349,6 +349,13 @@ void DObject::Destroy ()
ObjectFlags = (ObjectFlags & ~OF_Fixed) | OF_EuthanizeMe;
}
DEFINE_ACTION_FUNCTION(DObject, Destroy)
{
PARAM_SELF_PROLOGUE(DObject);
self->Destroy();
return 0;
}
//==========================================================================
//
//

View file

@ -25,7 +25,6 @@
#include "a_archvile.cpp"
#include "a_bossbrain.cpp"
#include "a_doomweaps.cpp"
#include "a_fatso.cpp"
#include "a_keen.cpp"
#include "a_lostsoul.cpp"
#include "a_painelemental.cpp"

View file

@ -1,84 +0,0 @@
/*
#include "actor.h"
#include "info.h"
#include "m_random.h"
#include "s_sound.h"
#include "p_local.h"
#include "p_enemy.h"
#include "gstrings.h"
#include "a_action.h"
#include "vm.h"
*/
//
// Mancubus attack,
// firing three missiles in three different directions?
// Doesn't look like it.
//
//
// killough 9/98: a mushroom explosion effect, sorta :)
// Original idea: Linguica
//
enum
{
MSF_Standard = 0,
MSF_Classic = 1,
MSF_DontHurt = 2,
};
DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_Mushroom)
{
PARAM_SELF_PROLOGUE(AActor);
PARAM_CLASS_DEF (spawntype, AActor)
PARAM_INT_DEF (n)
PARAM_INT_DEF (flags)
PARAM_FLOAT_DEF (vrange)
PARAM_FLOAT_DEF (hrange)
int i, j;
if (n == 0)
{
n = self->GetMissileDamage(0, 1);
}
if (spawntype == NULL)
{
spawntype = PClass::FindActor("FatShot");
}
P_RadiusAttack (self, self->target, 128, 128, self->DamageType, (flags & MSF_DontHurt) ? 0 : RADF_HURTSOURCE);
P_CheckSplash(self, 128.);
// Now launch mushroom cloud
AActor *target = Spawn("Mapspot", self->Pos(), NO_REPLACE); // We need something to aim at.
AActor *master = (flags & MSF_DontHurt) ? (AActor*)(self->target) : self;
target->Height = self->Height;
for (i = -n; i <= n; i += 8)
{
for (j = -n; j <= n; j += 8)
{
AActor *mo;
target->SetXYZ(
self->X() + i, // Aim in many directions from source
self->Y() + j,
self->Z() + (P_AproxDistance(i,j) * vrange)); // Aim up fairly high
if ((flags & MSF_Classic) || // Flag explicitely set, or no flags and compat options
(flags == 0 && (self->state->DefineFlags & SDF_DEHACKED) && (i_compatflags & COMPATF_MUSHROOM)))
{ // Use old function for MBF compatibility
mo = P_OldSpawnMissile (self, master, target, spawntype);
}
else // Use normal function
{
mo = P_SpawnMissile(self, target, spawntype, master);
}
if (mo != NULL)
{ // Slow it down a bit
mo->Vel *= hrange;
mo->flags &= ~MF_NOGRAVITY; // Make debris fall under gravity
}
}
}
target->Destroy();
return 0;
}

View file

@ -67,7 +67,7 @@ enum EStateDefineFlags
SDF_DEHACKED = 8, // Identify a state as having been modified by a dehacked lump
};
enum EStateType
enum EStateType : int // this must ensure proper alignment.
{
STATE_Actor,
STATE_Psprite,

View file

@ -4258,6 +4258,17 @@ AActor *AActor::StaticSpawn (PClassActor *type, const DVector3 &pos, replace_t a
return actor;
}
DEFINE_ACTION_FUNCTION(AActor, Spawn)
{
PARAM_PROLOGUE;
PARAM_CLASS(type, AActor);
PARAM_FLOAT_DEF(x);
PARAM_FLOAT_DEF(y);
PARAM_FLOAT_DEF(z);
PARAM_INT_DEF(flags);
ACTION_RETURN_OBJECT(AActor::StaticSpawn(type, DVector3(x, y, z), replace_t(flags)));
}
PClassActor *ClassForSpawn(FName classname)
{
PClass *cls = PClass::FindClass(classname);
@ -5951,6 +5962,16 @@ AActor *P_OldSpawnMissile(AActor *source, AActor *owner, AActor *dest, PClassAct
return th;
}
DEFINE_ACTION_FUNCTION(AActor, OldSpawnMissile)
{
PARAM_SELF_PROLOGUE(AActor);
PARAM_OBJECT(dest, AActor);
PARAM_CLASS(type, AActor);
PARAM_OBJECT_DEF(owner, AActor);
ACTION_RETURN_OBJECT(P_OldSpawnMissile(self, owner, dest, type));
}
//---------------------------------------------------------------------------
//
// FUNC P_SpawnMissileAngle
@ -6616,6 +6637,16 @@ DEFINE_ACTION_FUNCTION(AActor, VelFromAngle)
return 0;
}
DEFINE_ACTION_FUNCTION(AActor, SetXYZ)
{
PARAM_SELF_PROLOGUE(AActor);
PARAM_FLOAT(x);
PARAM_FLOAT(y);
PARAM_FLOAT(z);
self->SetXYZ(x, y, z);
return 0;
}
//----------------------------------------------------------------------------
//
// DropItem handling

View file

@ -156,7 +156,7 @@ void FCompileContext::CheckReturn(PPrototype *proto, FScriptPosition &pos)
if (fail)
{
pos.Message(MSG_ERROR, "All return expressions must deduce to the same type");
pos.Message(MSG_ERROR, "Return type mismatch");
}
}
@ -214,7 +214,7 @@ static PSymbolTable Builtins;
static PSymbol *FindBuiltinFunction(FName funcname, VMNativeFunction::NativeCallType func)
{
PSymbol *sym = Builtins.FindSymbol(funcname, false);
if (sym == NULL)
if (sym == nullptr)
{
PSymbolVMFunction *symfunc = new PSymbolVMFunction(funcname);
VMNativeFunction *calldec = new VMNativeFunction(func, funcname);
@ -282,7 +282,7 @@ bool FxExpression::isConstant() const
VMFunction *FxExpression::GetDirectFunction()
{
return NULL;
return nullptr;
}
//==========================================================================
@ -387,7 +387,7 @@ FxExpression *FxConstant::MakeConstant(PSymbol *sym, const FScriptPosition &pos)
{
FxExpression *x;
PSymbolConstNumeric *csym = dyn_cast<PSymbolConstNumeric>(sym);
if (csym != NULL)
if (csym != nullptr)
{
if (csym->ValueType->IsA(RUNTIME_CLASS(PInt)))
{
@ -400,13 +400,13 @@ FxExpression *FxConstant::MakeConstant(PSymbol *sym, const FScriptPosition &pos)
else
{
pos.Message(MSG_ERROR, "Invalid constant '%s'\n", csym->SymbolName.GetChars());
return NULL;
return nullptr;
}
}
else
{
pos.Message(MSG_ERROR, "'%s' is not a constant\n", sym->SymbolName.GetChars());
x = NULL;
x = nullptr;
}
return x;
}
@ -794,7 +794,7 @@ FxExpression *FxIntCast::Resolve(FCompileContext &ctx)
{
FxExpression *x = basex;
x->ValueType = ValueType;
basex = NULL;
basex = nullptr;
delete this;
return x;
}
@ -833,7 +833,7 @@ FxExpression *FxIntCast::Resolve(FCompileContext &ctx)
}
ScriptPosition.Message(MSG_ERROR, "Numeric type expected");
delete this;
return NULL;
return nullptr;
}
//==========================================================================
@ -891,7 +891,7 @@ FxExpression *FxFloatCast::Resolve(FCompileContext &ctx)
if (basex->IsFloat())
{
FxExpression *x = basex;
basex = NULL;
basex = nullptr;
delete this;
return x;
}
@ -923,7 +923,7 @@ FxExpression *FxFloatCast::Resolve(FCompileContext &ctx)
{
ScriptPosition.Message(MSG_ERROR, "Numeric type expected");
delete this;
return NULL;
return nullptr;
}
}
@ -982,7 +982,7 @@ FxExpression *FxNameCast::Resolve(FCompileContext &ctx)
if (basex->ValueType == TypeName)
{
FxExpression *x = basex;
basex = NULL;
basex = nullptr;
delete this;
return x;
}
@ -1001,7 +1001,7 @@ FxExpression *FxNameCast::Resolve(FCompileContext &ctx)
{
ScriptPosition.Message(MSG_ERROR, "Cannot convert to name");
delete this;
return NULL;
return nullptr;
}
}
@ -1060,7 +1060,7 @@ FxExpression *FxStringCast::Resolve(FCompileContext &ctx)
if (basex->ValueType == TypeString)
{
FxExpression *x = basex;
basex = NULL;
basex = nullptr;
delete this;
return x;
}
@ -1091,7 +1091,7 @@ FxExpression *FxStringCast::Resolve(FCompileContext &ctx)
{
ScriptPosition.Message(MSG_ERROR, "Cannot convert to string");
delete this;
return NULL;
return nullptr;
}
}
@ -1158,7 +1158,7 @@ FxExpression *FxColorCast::Resolve(FCompileContext &ctx)
{
FxExpression *x = basex;
x->ValueType = TypeColor;
basex = NULL;
basex = nullptr;
delete this;
return x;
}
@ -1177,7 +1177,7 @@ FxExpression *FxColorCast::Resolve(FCompileContext &ctx)
{
ScriptPosition.Message(MSG_ERROR, "Cannot convert to color");
delete this;
return NULL;
return nullptr;
}
}
@ -1237,7 +1237,7 @@ FxExpression *FxSoundCast::Resolve(FCompileContext &ctx)
{
FxExpression *x = basex;
x->ValueType = TypeSound;
basex = NULL;
basex = nullptr;
delete this;
return x;
}
@ -1256,7 +1256,7 @@ FxExpression *FxSoundCast::Resolve(FCompileContext &ctx)
{
ScriptPosition.Message(MSG_ERROR, "Cannot convert to sound");
delete this;
return NULL;
return nullptr;
}
}
@ -1318,6 +1318,7 @@ FxExpression *FxTypeCast::Resolve(FCompileContext &ctx)
// first deal with the simple types
if (ValueType == TypeError || basex->ValueType == TypeError)
{
ScriptPosition.Message(MSG_ERROR, "Trying to cast to invalid type. This error message means that somewhere in the script compiler an error check is missing.");
delete this;
return nullptr;
}
@ -1502,7 +1503,7 @@ FxExpression *FxPlusSign::Resolve(FCompileContext& ctx)
if (Operand->IsNumeric() || Operand->IsVector())
{
FxExpression *e = Operand;
Operand = NULL;
Operand = nullptr;
delete this;
return e;
}
@ -1510,7 +1511,7 @@ FxExpression *FxPlusSign::Resolve(FCompileContext& ctx)
{
ScriptPosition.Message(MSG_ERROR, "Numeric type expected");
delete this;
return NULL;
return nullptr;
}
}
@ -1571,7 +1572,7 @@ FxExpression *FxMinusSign::Resolve(FCompileContext& ctx)
{
ScriptPosition.Message(MSG_ERROR, "Numeric type expected");
delete this;
return NULL;
return nullptr;
}
}
@ -1587,7 +1588,14 @@ ExpEmit FxMinusSign::Emit(VMFunctionBuilder *build)
ExpEmit from = Operand->Emit(build);
assert(from.Konst == 0);
assert(ValueType->GetRegCount() == from.RegCount);
// Do it in-place.
// Do it in-place, unless a local variable
if (from.Fixed)
{
ExpEmit to = ExpEmit(build, from.RegType, from.RegCount);
build->Emit(Operand->ValueType->GetMoveOp(), to.RegNum, from.RegNum);
from = to;
}
if (ValueType->GetRegType() == REGT_INT)
{
build->Emit(OP_NEG, from.RegNum, from.RegNum, 0);
@ -1653,10 +1661,10 @@ FxExpression *FxUnaryNotBitwise::Resolve(FCompileContext& ctx)
// DECORATE allows floats here so cast them to int.
Operand = new FxIntCast(Operand, true);
Operand = Operand->Resolve(ctx);
if (Operand == NULL)
if (Operand == nullptr)
{
delete this;
return NULL;
return nullptr;
}
}
@ -1665,7 +1673,7 @@ FxExpression *FxUnaryNotBitwise::Resolve(FCompileContext& ctx)
{
ScriptPosition.Message(MSG_ERROR, "Integer type expected");
delete this;
return NULL;
return nullptr;
}
if (Operand->isConstant())
@ -1804,13 +1812,13 @@ FxExpression *FxSizeAlign::Resolve(FCompileContext& ctx)
{
ScriptPosition.Message(MSG_ERROR, "cannot determine %s of a constant", Which == TK_AlignOf? "alignment" : "size");
delete this;
return NULL;
return nullptr;
}
else if (!Operand->RequestAddress(nullptr))
{
ScriptPosition.Message(MSG_ERROR, "Operand must be addressable to determine %s", Which == TK_AlignOf ? "alignment" : "size");
delete this;
return NULL;
return nullptr;
}
else
{
@ -2513,7 +2521,8 @@ FxAddSub::FxAddSub(int o, FxExpression *l, FxExpression *r)
FxExpression *FxAddSub::Resolve(FCompileContext& ctx)
{
CHECKRESOLVED();
if (!ResolveLR(ctx, true)) return NULL;
if (!ResolveLR(ctx, true))
return nullptr;
if (!IsNumeric() && !IsVector())
{
@ -2652,13 +2661,14 @@ FxExpression *FxMulDiv::Resolve(FCompileContext& ctx)
{
CHECKRESOLVED();
if (!ResolveLR(ctx, true)) return NULL;
if (!ResolveLR(ctx, true))
return nullptr;
if (!IsNumeric() && !IsVector())
{
ScriptPosition.Message(MSG_ERROR, "Numeric type expected");
delete this;
return NULL;
return nullptr;
}
else if (left->isConstant() && right->isConstant())
{
@ -2672,7 +2682,7 @@ FxExpression *FxMulDiv::Resolve(FCompileContext& ctx)
{
ScriptPosition.Message(MSG_ERROR, "Division by 0");
delete this;
return NULL;
return nullptr;
}
v = Operator == '*'? v1 * v2 :
@ -2693,7 +2703,7 @@ FxExpression *FxMulDiv::Resolve(FCompileContext& ctx)
{
ScriptPosition.Message(MSG_ERROR, "Division by 0");
delete this;
return NULL;
return nullptr;
}
v = Operator == '*'? v1 * v2 :
@ -2820,7 +2830,8 @@ FxExpression *FxPow::Resolve(FCompileContext& ctx)
{
CHECKRESOLVED();
if (!ResolveLR(ctx, true)) return nullptr;
if (!ResolveLR(ctx, true))
return nullptr;
if (!IsNumeric())
{
@ -2879,13 +2890,14 @@ FxCompareRel::FxCompareRel(int o, FxExpression *l, FxExpression *r)
FxExpression *FxCompareRel::Resolve(FCompileContext& ctx)
{
CHECKRESOLVED();
if (!ResolveLR(ctx, true)) return NULL;
if (!ResolveLR(ctx, true))
return nullptr;
if (!IsNumeric())
{
ScriptPosition.Message(MSG_ERROR, "Numeric type expected");
delete this;
return NULL;
return nullptr;
}
else if (left->isConstant() && right->isConstant())
{
@ -3038,19 +3050,20 @@ FxExpression *FxCompareEq::Resolve(FCompileContext& ctx)
{
CHECKRESOLVED();
if (!ResolveLR(ctx, true)) return NULL;
if (!ResolveLR(ctx, true))
return nullptr;
if (!left || !right)
{
delete this;
return NULL;
return nullptr;
}
if (!IsNumeric() && !IsPointer() && !IsVector() && ValueType != TypeName)
{
ScriptPosition.Message(MSG_ERROR, "Numeric type expected");
delete this;
return NULL;
return nullptr;
}
if (Operator == TK_ApproxEq && left->ValueType->GetRegType() != REGT_FLOAT && left->ValueType->GetRegType() != REGT_STRING)
@ -3174,7 +3187,8 @@ FxBinaryInt::FxBinaryInt(int o, FxExpression *l, FxExpression *r)
FxExpression *FxBinaryInt::Resolve(FCompileContext& ctx)
{
CHECKRESOLVED();
if (!ResolveLR(ctx, false)) return NULL;
if (!ResolveLR(ctx, false))
return nullptr;
if (IsFloat() && ctx.FromDecorate)
{
@ -3189,10 +3203,10 @@ FxExpression *FxBinaryInt::Resolve(FCompileContext& ctx)
right = new FxIntCast(right, ctx.FromDecorate);
right = right->Resolve(ctx);
}
if (left == NULL || right == NULL)
if (left == nullptr || right == nullptr)
{
delete this;
return NULL;
return nullptr;
}
ValueType = TypeSInt32;
}
@ -3201,7 +3215,7 @@ FxExpression *FxBinaryInt::Resolve(FCompileContext& ctx)
{
ScriptPosition.Message(MSG_ERROR, "Integer type expected");
delete this;
return NULL;
return nullptr;
}
else if (left->isConstant() && right->isConstant())
{
@ -3322,7 +3336,8 @@ FxLtGtEq::FxLtGtEq(FxExpression *l, FxExpression *r)
FxExpression *FxLtGtEq::Resolve(FCompileContext& ctx)
{
CHECKRESOLVED();
if (!ResolveLR(ctx, true)) return NULL;
if (!ResolveLR(ctx, true))
return nullptr;
if (!left->IsNumeric() || !right->IsNumeric())
{
@ -3467,14 +3482,14 @@ FxExpression *FxBinaryLogical::Resolve(FCompileContext& ctx)
else if (b_left==1)
{
FxExpression *x = right;
right=NULL;
right=nullptr;
delete this;
return x;
}
else if (b_right==1)
{
FxExpression *x = left;
left=NULL;
left=nullptr;
delete this;
return x;
}
@ -3496,14 +3511,14 @@ FxExpression *FxBinaryLogical::Resolve(FCompileContext& ctx)
else if (b_left==0)
{
FxExpression *x = right;
right=NULL;
right=nullptr;
delete this;
return x;
}
else if (b_right==0)
{
FxExpression *x = left;
left=NULL;
left=nullptr;
delete this;
return x;
}
@ -3723,7 +3738,7 @@ ExpEmit FxTypeCheck::Emit(VMFunctionBuilder *build)
PSymbol *sym = FindBuiltinFunction(NAME_BuiltinTypeCheck, BuiltinTypeCheck);
assert(sym->IsKindOf(RUNTIME_CLASS(PSymbolVMFunction)));
assert(((PSymbolVMFunction *)sym)->Function != NULL);
assert(((PSymbolVMFunction *)sym)->Function != nullptr);
auto callfunc = ((PSymbolVMFunction *)sym)->Function;
int opcode = (EmitTail ? OP_TAIL_K : OP_CALL_K);
@ -3813,7 +3828,7 @@ FxExpression *FxConditional::Resolve(FCompileContext& ctx)
FxExpression *e = result? truex:falsex;
delete (result? falsex:truex);
falsex = truex = NULL;
falsex = truex = nullptr;
delete this;
return e;
}
@ -3867,9 +3882,22 @@ ExpEmit FxConditional::Emit(VMFunctionBuilder *build)
ExpEmit trueop = truex->Emit(build);
if (trueop.Konst)
{
assert(trueop.RegType == REGT_FLOAT);
out = ExpEmit(build, REGT_FLOAT);
build->Emit(OP_LKF, out.RegNum, trueop.RegNum);
if (trueop.RegType == REGT_FLOAT)
{
out = ExpEmit(build, REGT_FLOAT);
build->Emit(OP_LKF, out.RegNum, trueop.RegNum);
}
else if (trueop.RegType == REGT_POINTER)
{
out = ExpEmit(build, REGT_POINTER);
build->Emit(OP_LKP, out.RegNum, trueop.RegNum);
}
else
{
assert(trueop.RegType == REGT_STRING);
out = ExpEmit(build, REGT_STRING);
build->Emit(OP_LKS, out.RegNum, trueop.RegNum);
}
}
else
{
@ -3892,8 +3920,22 @@ ExpEmit FxConditional::Emit(VMFunctionBuilder *build)
ExpEmit falseop = falsex->Emit(build);
if (falseop.Konst)
{
assert(falseop.RegType == REGT_FLOAT);
build->Emit(OP_LKF, out.RegNum, falseop.RegNum);
if (falseop.RegType == REGT_FLOAT)
{
out = ExpEmit(build, REGT_FLOAT);
build->Emit(OP_LKF, out.RegNum, falseop.RegNum);
}
else if (falseop.RegType == REGT_POINTER)
{
out = ExpEmit(build, REGT_POINTER);
build->Emit(OP_LKP, out.RegNum, falseop.RegNum);
}
else
{
assert(falseop.RegType == REGT_STRING);
out = ExpEmit(build, REGT_STRING);
build->Emit(OP_LKS, out.RegNum, falseop.RegNum);
}
}
else
{
@ -3901,17 +3943,7 @@ ExpEmit FxConditional::Emit(VMFunctionBuilder *build)
// returned by "true" so that only one register is returned by
// this tree.
falseop.Free(build);
if (falseop.RegType == REGT_INT)
{
build->Emit(OP_MOVE, out.RegNum, falseop.RegNum, 0);
}
else
{
assert(falseop.RegType == REGT_FLOAT);
assert(falseop.RegCount == out.RegCount);
static int flops[] = { OP_MOVEF, OP_MOVEV2, OP_MOVEV3 };
build->Emit(flops[falseop.RegNum], out.RegNum, falseop.RegNum, 0);
}
build->Emit(falsex->ValueType->GetMoveOp(), out.RegNum, falseop.RegNum, 0);
}
}
build->BackpatchToHere(truejump);
@ -3958,7 +3990,7 @@ FxExpression *FxAbs::Resolve(FCompileContext &ctx)
{
ScriptPosition.Message(MSG_ERROR, "Numeric type expected");
delete this;
return NULL;
return nullptr;
}
else if (val->isConstant())
{
@ -3976,7 +4008,7 @@ FxExpression *FxAbs::Resolve(FCompileContext &ctx)
default:
// shouldn't happen
delete this;
return NULL;
return nullptr;
}
FxExpression *x = new FxConstant(value, ScriptPosition);
delete this;
@ -4047,7 +4079,7 @@ FxExpression *FxATan2::Resolve(FCompileContext &ctx)
{
ScriptPosition.Message(MSG_ERROR, "numeric value expected for parameter");
delete this;
return NULL;
return nullptr;
}
if (yval->isConstant() && xval->isConstant())
{
@ -4151,7 +4183,7 @@ FxExpression *FxMinMax::Resolve(FCompileContext &ctx)
{
ScriptPosition.Message(MSG_ERROR, "Arguments must be of type int or float");
delete this;
return NULL;
return nullptr;
}
}
if (floatcount != 0)
@ -4230,7 +4262,7 @@ FxExpression *FxMinMax::Resolve(FCompileContext &ctx)
}
}
delete choices[j];
choices[j] = NULL;
choices[j] = nullptr;
choices.Delete(j);
}
}
@ -4329,12 +4361,12 @@ FxRandom::FxRandom(FRandom * r, FxExpression *mi, FxExpression *ma, const FScrip
: FxExpression(EFX_Random, pos)
{
EmitTail = false;
if (mi != NULL && ma != NULL)
if (mi != nullptr && ma != nullptr)
{
min = new FxIntCast(mi, nowarn);
max = new FxIntCast(ma, nowarn);
}
else min = max = NULL;
else min = max = nullptr;
rng = r;
ValueType = TypeSInt32;
}
@ -4424,13 +4456,13 @@ ExpEmit FxRandom::Emit(VMFunctionBuilder *build)
PSymbol *sym = FindBuiltinFunction(NAME_BuiltinRandom, BuiltinRandom);
assert(sym->IsKindOf(RUNTIME_CLASS(PSymbolVMFunction)));
assert(((PSymbolVMFunction *)sym)->Function != NULL);
assert(((PSymbolVMFunction *)sym)->Function != nullptr);
callfunc = ((PSymbolVMFunction *)sym)->Function;
int opcode = (EmitTail ? OP_TAIL_K : OP_CALL_K);
build->Emit(OP_PARAM, 0, REGT_POINTER | REGT_KONST, build->GetConstantAddress(rng, ATAG_RNG));
if (min != NULL && max != NULL)
if (min != nullptr && max != nullptr)
{
EmitParameter(build, min, ScriptPosition);
EmitParameter(build, max, ScriptPosition);
@ -4545,7 +4577,7 @@ ExpEmit FxRandomPick::Emit(VMFunctionBuilder *build)
PSymbol *sym = FindBuiltinFunction(NAME_BuiltinRandom, BuiltinRandom);
assert(sym->IsKindOf(RUNTIME_CLASS(PSymbolVMFunction)));
assert(((PSymbolVMFunction *)sym)->Function != NULL);
assert(((PSymbolVMFunction *)sym)->Function != nullptr);
callfunc = ((PSymbolVMFunction *)sym)->Function;
build->Emit(OP_PARAM, 0, REGT_POINTER | REGT_KONST, build->GetConstantAddress(rng, ATAG_RNG));
@ -4623,9 +4655,9 @@ ExpEmit FxRandomPick::Emit(VMFunctionBuilder *build)
//
//==========================================================================
FxFRandom::FxFRandom(FRandom *r, FxExpression *mi, FxExpression *ma, const FScriptPosition &pos)
: FxRandom(r, NULL, NULL, pos, true)
: FxRandom(r, nullptr, nullptr, pos, true)
{
if (mi != NULL && ma != NULL)
if (mi != nullptr && ma != nullptr)
{
min = new FxFloatCast(mi);
max = new FxFloatCast(ma);
@ -4670,13 +4702,13 @@ ExpEmit FxFRandom::Emit(VMFunctionBuilder *build)
PSymbol *sym = FindBuiltinFunction(NAME_BuiltinFRandom, BuiltinFRandom);
assert(sym->IsKindOf(RUNTIME_CLASS(PSymbolVMFunction)));
assert(((PSymbolVMFunction *)sym)->Function != NULL);
assert(((PSymbolVMFunction *)sym)->Function != nullptr);
callfunc = ((PSymbolVMFunction *)sym)->Function;
int opcode = (EmitTail ? OP_TAIL_K : OP_CALL_K);
build->Emit(OP_PARAM, 0, REGT_POINTER | REGT_KONST, build->GetConstantAddress(rng, ATAG_RNG));
if (min != NULL && max != NULL)
if (min != nullptr && max != nullptr)
{
EmitParameter(build, min, ScriptPosition);
EmitParameter(build, max, ScriptPosition);
@ -4764,7 +4796,7 @@ ExpEmit FxRandom2::Emit(VMFunctionBuilder *build)
PSymbol *sym = FindBuiltinFunction(NAME_BuiltinRandom, BuiltinRandom);
assert(sym->IsKindOf(RUNTIME_CLASS(PSymbolVMFunction)));
assert(((PSymbolVMFunction *)sym)->Function != NULL);
assert(((PSymbolVMFunction *)sym)->Function != nullptr);
callfunc = ((PSymbolVMFunction *)sym)->Function;
int opcode = (EmitTail ? OP_TAIL_K : OP_CALL_K);
@ -4896,7 +4928,7 @@ FxExpression *FxIdentifier::Resolve(FCompileContext& ctx)
}
// now check the global identifiers.
else if ((sym = ctx.FindGlobal(Identifier)) != NULL)
else if ((sym = ctx.FindGlobal(Identifier)) != nullptr)
{
if (sym->IsKindOf(RUNTIME_CLASS(PSymbolConst)))
{
@ -4910,7 +4942,7 @@ FxExpression *FxIdentifier::Resolve(FCompileContext& ctx)
}
// and line specials
else if ((num = P_FindLineSpecial(Identifier, NULL, NULL)))
else if ((num = P_FindLineSpecial(Identifier, nullptr, nullptr)))
{
ScriptPosition.Message(MSG_DEBUGLOG, "Resolving name '%s' as line special %d\n", Identifier.GetChars(), num);
newex = new FxConstant(num, ScriptPosition);
@ -5355,25 +5387,25 @@ FxExpression *FxArrayElement::Resolve(FCompileContext &ctx)
// DECORATE allows floats here so cast them to int.
index = new FxIntCast(index, ctx.FromDecorate);
index = index->Resolve(ctx);
if (index == NULL)
if (index == nullptr)
{
delete this;
return NULL;
return nullptr;
}
}
if (index->ValueType->GetRegType() != REGT_INT && index->ValueType != TypeName)
{
ScriptPosition.Message(MSG_ERROR, "Array index must be integer");
delete this;
return NULL;
return nullptr;
}
PArray *arraytype = dyn_cast<PArray>(Array->ValueType);
if (arraytype == NULL)
if (arraytype == nullptr)
{
ScriptPosition.Message(MSG_ERROR, "'[]' can only be used with arrays.");
delete this;
return NULL;
return nullptr;
}
if (index->isConstant())
{
@ -5382,7 +5414,7 @@ FxExpression *FxArrayElement::Resolve(FCompileContext &ctx)
{
ScriptPosition.Message(MSG_ERROR, "Array index out of bounds");
delete this;
return NULL;
return nullptr;
}
}
@ -5392,13 +5424,13 @@ FxExpression *FxArrayElement::Resolve(FCompileContext &ctx)
// int arrays only for now
ScriptPosition.Message(MSG_ERROR, "Only numeric arrays are supported.");
delete this;
return NULL;
return nullptr;
}
if (!Array->RequestAddress(&AddressWritable))
{
ScriptPosition.Message(MSG_ERROR, "Unable to dereference array.");
delete this;
return NULL;
return nullptr;
}
return this;
}
@ -5901,12 +5933,12 @@ FxExpression *FxActionSpecialCall::Resolve(FCompileContext& ctx)
bool failed = false;
SAFE_RESOLVE_OPT(Self, ctx);
if (ArgList != NULL)
if (ArgList != nullptr)
{
for (unsigned i = 0; i < ArgList->Size(); i++)
{
(*ArgList)[i] = (*ArgList)[i]->Resolve(ctx);
if ((*ArgList)[i] == NULL)
if ((*ArgList)[i] == nullptr)
{
failed = true;
}
@ -5934,7 +5966,7 @@ FxExpression *FxActionSpecialCall::Resolve(FCompileContext& ctx)
if (failed)
{
delete this;
return NULL;
return nullptr;
}
}
ValueType = TypeSInt32;
@ -5959,7 +5991,7 @@ int BuiltinCallLineSpecial(VMFrameStack *stack, VMValue *param, TArray<VMValue>
{
v[i - 2] = param[i].i;
}
ACTION_RETURN_INT(P_ExecuteSpecial(param[0].i, NULL, reinterpret_cast<AActor*>(param[1].a), false, v[0], v[1], v[2], v[3], v[4]));
ACTION_RETURN_INT(P_ExecuteSpecial(param[0].i, nullptr, reinterpret_cast<AActor*>(param[1].a), false, v[0], v[1], v[2], v[3], v[4]));
}
ExpEmit FxActionSpecialCall::Emit(VMFunctionBuilder *build)
@ -5970,7 +6002,7 @@ ExpEmit FxActionSpecialCall::Emit(VMFunctionBuilder *build)
// fixme: This really should use the Self pointer that got passed to this class instead of just using the first argument from the function.
// Once static functions are possible, or specials can be called through a member access operator this won't work anymore.
build->Emit(OP_PARAM, 0, REGT_POINTER, 0); // pass self
if (ArgList != NULL)
if (ArgList != nullptr)
{
for (; i < ArgList->Size(); ++i)
{
@ -6002,7 +6034,7 @@ ExpEmit FxActionSpecialCall::Emit(VMFunctionBuilder *build)
PSymbol *sym = FindBuiltinFunction(NAME_BuiltinCallLineSpecial, BuiltinCallLineSpecial);
assert(sym->IsKindOf(RUNTIME_CLASS(PSymbolVMFunction)));
assert(((PSymbolVMFunction *)sym)->Function != NULL);
assert(((PSymbolVMFunction *)sym)->Function != nullptr);
callfunc = ((PSymbolVMFunction *)sym)->Function;
if (EmitTail)
@ -6144,7 +6176,7 @@ FxExpression *FxVMFunctionCall::Resolve(FCompileContext& ctx)
if (failed)
{
delete this;
return NULL;
return nullptr;
}
TArray<PType *> &rets = proto->ReturnTypes;
if (rets.Size() > 0)
@ -6207,7 +6239,7 @@ ExpEmit FxVMFunctionCall::Emit(VMFunctionBuilder *build)
}
}
// Emit code to pass explicit parameters
if (ArgList != NULL)
if (ArgList != nullptr)
{
for (unsigned i = 0; i < ArgList->Size(); ++i)
{
@ -6311,25 +6343,25 @@ FxExpression *FxFlopFunctionCall::Resolve(FCompileContext& ctx)
{
CHECKRESOLVED();
if (ArgList == NULL || ArgList->Size() != 1)
if (ArgList == nullptr || ArgList->Size() != 1)
{
ScriptPosition.Message(MSG_ERROR, "%s only has one parameter", FName(FxFlops[Index].Name).GetChars());
delete this;
return NULL;
return nullptr;
}
(*ArgList)[0] = (*ArgList)[0]->Resolve(ctx);
if ((*ArgList)[0] == NULL)
if ((*ArgList)[0] == nullptr)
{
delete this;
return NULL;
return nullptr;
}
if (!(*ArgList)[0]->IsNumeric())
{
ScriptPosition.Message(MSG_ERROR, "numeric value expected for parameter");
delete this;
return NULL;
return nullptr;
}
if ((*ArgList)[0]->isConstant())
{
@ -6417,7 +6449,7 @@ FxExpression *FxSequence::Resolve(FCompileContext &ctx)
CHECKRESOLVED();
for (unsigned i = 0; i < Expressions.Size(); ++i)
{
if (NULL == (Expressions[i] = Expressions[i]->Resolve(ctx)))
if (nullptr == (Expressions[i] = Expressions[i]->Resolve(ctx)))
{
delete this;
return nullptr;
@ -6455,7 +6487,7 @@ VMFunction *FxSequence::GetDirectFunction()
{
return Expressions[0]->GetDirectFunction();
}
return NULL;
return nullptr;
}
//==========================================================================
@ -6792,7 +6824,7 @@ FxIfStatement::FxIfStatement(FxExpression *cond, FxExpression *true_part,
WhenFalse = false_part;
if (WhenTrue != nullptr) WhenTrue->NeedResult = false;
if (WhenFalse != nullptr) WhenFalse->NeedResult = false;
assert(cond != NULL);
assert(cond != nullptr);
}
FxIfStatement::~FxIfStatement()
@ -6840,8 +6872,8 @@ FxExpression *FxIfStatement::Resolve(FCompileContext &ctx)
FxExpression *e = result ? WhenTrue : WhenFalse;
delete (result ? WhenFalse : WhenTrue);
WhenTrue = WhenFalse = NULL;
if (e == NULL) e = new FxNop(ScriptPosition); // create a dummy if this statement gets completely removed by optimizing out the constant parts.
WhenTrue = WhenFalse = nullptr;
if (e == nullptr) e = new FxNop(ScriptPosition); // create a dummy if this statement gets completely removed by optimizing out the constant parts.
delete this;
return e;
}
@ -6861,7 +6893,7 @@ ExpEmit FxIfStatement::Emit(VMFunctionBuilder *build)
ExpEmit cond = Condition->Emit(build);
assert(cond.RegType != REGT_STRING && !cond.Konst);
if (WhenTrue != NULL)
if (WhenTrue != nullptr)
{
path1 = WhenTrue;
path2 = WhenFalse;
@ -6871,9 +6903,9 @@ ExpEmit FxIfStatement::Emit(VMFunctionBuilder *build)
{
// When there is only a false path, reverse the condition so we can
// treat it as a true path.
assert(WhenFalse != NULL);
assert(WhenFalse != nullptr);
path1 = WhenFalse;
path2 = NULL;
path2 = nullptr;
condcheck = 0;
}
@ -6900,7 +6932,7 @@ ExpEmit FxIfStatement::Emit(VMFunctionBuilder *build)
// Evaluate first path
v = path1->Emit(build);
v.Free(build);
if (path2 != NULL)
if (path2 != nullptr)
{
size_t path1jump = build->Emit(OP_JMP, 0);
// Evaluate second path
@ -7412,18 +7444,18 @@ FxExpression *FxClassTypeCast::Resolve(FCompileContext &ctx)
{
ScriptPosition.Message(MSG_ERROR, "Cannot convert %s to class type", basex->ValueType->DescriptiveName());
delete this;
return NULL;
return nullptr;
}
if (basex->isConstant())
{
FName clsname = static_cast<FxConstant *>(basex)->GetValue().GetName();
PClass *cls = NULL;
PClass *cls = nullptr;
if (clsname != NAME_None)
{
cls = PClass::FindClass(clsname);
if (cls == NULL)
if (cls == nullptr)
{
/* lax */
// Since this happens in released WADs it must pass without a terminal error... :(
@ -7437,7 +7469,7 @@ FxExpression *FxClassTypeCast::Resolve(FCompileContext &ctx)
{
ScriptPosition.Message(MSG_ERROR, "class '%s' is not compatible with '%s'", clsname.GetChars(), desttype->TypeName.GetChars());
delete this;
return NULL;
return nullptr;
}
ScriptPosition.Message(MSG_DEBUG, "resolving '%s' as class name", clsname.GetChars());
}
@ -7474,7 +7506,7 @@ int BuiltinNameToClass(VMFrameStack *stack, VMValue *param, TArray<VMValue> &def
if (!cls->IsDescendantOf(desttype))
{
Printf("class '%s' is not compatible with '%s'", clsname.GetChars(), desttype->TypeName.GetChars());
cls = NULL;
cls = nullptr;
}
ret->SetPointer(const_cast<PClass *>(cls), ATAG_OBJECT);
return 1;
@ -7484,7 +7516,7 @@ ExpEmit FxClassTypeCast::Emit(VMFunctionBuilder *build)
{
if (basex->ValueType != TypeName)
{
return ExpEmit(build->GetConstantAddress(NULL, ATAG_OBJECT), REGT_POINTER, true);
return ExpEmit(build->GetConstantAddress(nullptr, ATAG_OBJECT), REGT_POINTER, true);
}
ExpEmit clsname = basex->Emit(build);
assert(!clsname.Konst);
@ -7497,7 +7529,7 @@ ExpEmit FxClassTypeCast::Emit(VMFunctionBuilder *build)
PSymbol *sym = FindBuiltinFunction(NAME_BuiltinNameToClass, BuiltinNameToClass);
assert(sym->IsKindOf(RUNTIME_CLASS(PSymbolVMFunction)));
assert(((PSymbolVMFunction *)sym)->Function != NULL);
assert(((PSymbolVMFunction *)sym)->Function != nullptr);
callfunc = ((PSymbolVMFunction *)sym)->Function;
build->Emit(OP_CALL_K, build->GetConstantAddress(callfunc, ATAG_OBJECT), 2, 1);
@ -7526,7 +7558,7 @@ FxExpression *FxStateByIndex::Resolve(FCompileContext &ctx)
ScriptPosition.Message(MSG_ERROR, "%s: Attempt to jump to non existing state index %d",
ctx.Class->TypeName.GetChars(), index);
delete this;
return NULL;
return nullptr;
}
FxExpression *x = new FxConstant(aclass->OwnedStates + index, ScriptPosition);
delete this;
@ -7677,7 +7709,7 @@ FxMultiNameState::FxMultiNameState(const char *_statestring, const FScriptPositi
}
names = MakeStateNameList(statestring);
names.Insert(0, scopename);
scope = NULL;
scope = nullptr;
}
//==========================================================================
@ -7693,7 +7725,7 @@ FxExpression *FxMultiNameState::Resolve(FCompileContext &ctx)
if (names[0] == NAME_None)
{
scope = NULL;
scope = nullptr;
}
else if (names[0] == NAME_Super)
{
@ -7702,27 +7734,27 @@ FxExpression *FxMultiNameState::Resolve(FCompileContext &ctx)
else
{
scope = PClass::FindActor(names[0]);
if (scope == NULL)
if (scope == nullptr)
{
ScriptPosition.Message(MSG_ERROR, "Unknown class '%s' in state label", names[0].GetChars());
delete this;
return NULL;
return nullptr;
}
else if (!scope->IsAncestorOf(ctx.Class))
{
ScriptPosition.Message(MSG_ERROR, "'%s' is not an ancestor of '%s'", names[0].GetChars(), ctx.Class->TypeName.GetChars());
delete this;
return NULL;
return nullptr;
}
}
if (scope != NULL)
if (scope != nullptr)
{
FState *destination = NULL;
FState *destination = nullptr;
// If the label is class specific we can resolve it right here
if (names[1] != NAME_None)
{
destination = scope->FindState(names.Size()-1, &names[1], false);
if (destination == NULL)
if (destination == nullptr)
{
ScriptPosition.Message(MSG_OPTERROR, "Unknown state jump destination");
/* lax */
@ -7749,7 +7781,7 @@ static int DoFindState(VMFrameStack *stack, VMValue *param, int numparam, VMRetu
{
PARAM_OBJECT_AT(0, self, AActor);
FState *state = self->GetClass()->FindState(numparam - 1, names);
if (state == NULL)
if (state == nullptr)
{
const char *dot = "";
Printf("Jump target '");
@ -7822,7 +7854,7 @@ ExpEmit FxMultiNameState::Emit(VMFunctionBuilder *build)
}
assert(sym->IsKindOf(RUNTIME_CLASS(PSymbolVMFunction)));
assert(((PSymbolVMFunction *)sym)->Function != NULL);
assert(((PSymbolVMFunction *)sym)->Function != nullptr);
callfunc = ((PSymbolVMFunction *)sym)->Function;
build->Emit(OP_CALL_K, build->GetConstantAddress(callfunc, ATAG_OBJECT), names.Size() + 1, 1);

View file

@ -671,7 +671,7 @@ void InitThingdef()
symt.AddSymbol(new PField(NAME_Score, TypeSInt32, VARF_Native, myoffsetof(AActor, Score)));
symt.AddSymbol(new PField(NAME_Accuracy, TypeSInt32, VARF_Native, myoffsetof(AActor, accuracy)));
symt.AddSymbol(new PField(NAME_Stamina, TypeSInt32, VARF_Native, myoffsetof(AActor, stamina)));
symt.AddSymbol(new PField(NAME_Height, TypeFloat64, VARF_Native|VARF_ReadOnly, myoffsetof(AActor, Height)));
symt.AddSymbol(new PField(NAME_Height, TypeFloat64, VARF_Native, myoffsetof(AActor, Height)));
symt.AddSymbol(new PField(NAME_Radius, TypeFloat64, VARF_Native|VARF_ReadOnly, myoffsetof(AActor, radius)));
symt.AddSymbol(new PField(NAME_ReactionTime, TypeSInt32, VARF_Native, myoffsetof(AActor, reactiontime)));
symt.AddSymbol(new PField(NAME_MeleeRange, TypeFloat64, VARF_Native, myoffsetof(AActor, meleerange)));
@ -698,6 +698,7 @@ void InitThingdef()
symt.AddSymbol(new PField("Pos", TypeVector3, VARF_Native|VARF_ReadOnly, myoffsetof(AActor, __Pos)));
symt.AddSymbol(new PField("Vel", TypeVector3, VARF_Native, myoffsetof(AActor, Vel)));
symt.AddSymbol(new PField("Scale", TypeVector2, VARF_Native, myoffsetof(AActor, Scale)));
symt.AddSymbol(new PField("CurState", TypeState, VARF_Native|VARF_ReadOnly, myoffsetof(AActor, state))); // has to be renamed on the script side because it clashes with the same named type.
symt.AddSymbol(new PField("SeeState", TypeState, VARF_Native|VARF_ReadOnly, myoffsetof(AActor, SeeState)));
symt.AddSymbol(new PField(NAME_Target, TypeActor, VARF_Native, myoffsetof(AActor, target)));
symt.AddSymbol(new PField(NAME_Master, TypeActor, VARF_Native, myoffsetof(AActor, master)));

View file

@ -1490,6 +1490,7 @@ PType *ZCCCompiler::ResolveUserType(ZCC_BasicType *type, PSymbolTable *symt)
}
return type;
}
Error(type, "Unable to resolve %s as type.", FName(type->UserType->Id).GetChars());
return TypeError;
}
@ -2186,8 +2187,8 @@ static bool CheckRandom(ZCC_Expression *duration)
FxExpression *ZCCCompiler::SetupActionFunction(PClass *cls, ZCC_TreeNode *af)
{
// We have 3 cases to consider here:
// 1. An action function without parameters. This can be called directly
// 2. An action functon with parameters or a non-action function. This needs to be wrapped into a helper function to set everything up.
// 1. A function without parameters. This can be called directly
// 2. A functon with parameters. This needs to be wrapped into a helper function to set everything up.
// 3. An anonymous function.
// 1. and 2. are exposed through AST_ExprFunctionCall
@ -2236,9 +2237,10 @@ void ZCCCompiler::CompileStates()
{
for (auto c : Classes)
{
if (!c->Type()->IsDescendantOf(RUNTIME_CLASS(AActor)))
{
Error(c->cls, "%s: States can only be defined for actors.", c->Type()->TypeName.GetChars());
if (c->States.Size()) Error(c->cls, "%s: States can only be defined for actors.", c->Type()->TypeName.GetChars());
continue;
}
FString statename; // The state builder wants the label as one complete string, not separated into tokens.

View file

@ -1,3 +1,4 @@
zscript/base.txt
zscript/constants.txt
zscript/actor.txt

View file

@ -53,9 +53,13 @@ class Actor : Thinker native
return GetPointer(ptr_select1) == GetPointer(ptr_select2);
}
native static bool isDehState(state st);
native void SetOrigin(vector3 newpos, bool moving);
native void SetXYZ(vector3 newpos);
native Actor GetPointer(int aaptr);
native static Actor Spawn(class<Actor> type, vector3 pos = (0,0,0), int replace = NO_REPLACE);
native Actor SpawnMissile(Actor dest, class<Actor> type, Actor owner = null);
native Actor OldSpawnMissile(Actor dest, class<Actor> type, Actor owner = null);
native void TraceBleed(int damage, Actor missile);
native bool CheckMeleeRange();
native int DamageMobj(Actor inflictor, Actor source, int damage, Name mod, int flags = 0, double angle = 0);
@ -228,7 +232,6 @@ class Actor : Thinker native
native void A_SpawnFly(class<Actor> spawntype = null); // needs special treatment for default
native void A_BrainExplode();
native void A_Detonate();
native void A_Mushroom(class<Actor> spawntype = "FatShot", int numspawns = 0, int flags = 0, float vrange = 4.0, float hrange = 0.5);
native bool A_CallSpecial(int special, int arg1=0, int arg2=0, int arg3=0, int arg4=0, int arg5=0);
native void A_SetFloorClip();

View file

@ -0,0 +1,8 @@
class Object native
{
/*virtual*/ native void Destroy();
}
class Thinker : Object native
{
}

View file

@ -876,3 +876,9 @@ enum EDmgFlags
DMG_NO_PROTECT = 256,
DMG_USEANGLE = 512,
}
enum EReplace
{
NO_REPLACE = 0,
ALLOW_REPLACE = 1
}

View file

@ -110,6 +110,12 @@ extend class Actor
A_PlaySound("fatso/raiseguns", CHAN_WEAPON);
}
//
// Mancubus attack,
// firing three missiles in three different directions?
// Doesn't look like it.
//
void A_FatAttack1(class<Actor> spawntype = "FatShot")
{
if (target)
@ -163,5 +169,54 @@ extend class Actor
}
}
}
//
// killough 9/98: a mushroom explosion effect, sorta :)
// Original idea: Linguica
//
void A_Mushroom(class<Actor> spawntype = "FatShot", int numspawns = 0, int flags = 0, float vrange = 4.0, float hrange = 0.5)
{
int i, j;
if (numspawns == 0)
{
numspawns = GetMissileDamage(0, 1);
}
A_Explode(128, 128, (flags & MSF_DontHurt) ? 0 : XF_HURTSOURCE);
// Now launch mushroom cloud
Actor aimtarget = Spawn("Mapspot", pos, NO_REPLACE); // We need something to aim at.
Actor owner = (flags & MSF_DontHurt) ? target : self;
aimtarget.Height = Height;
bool shootmode = ((flags & MSF_Classic) || // Flag explicitely set, or no flags and compat options
(flags == 0 && isDehState(CurState) && GetCVar("compat_mushroom")));
for (i = -numspawns; i <= numspawns; i += 8)
{
for (j = -numspawns; j <= numspawns; j += 8)
{
Actor mo;
aimtarget.SetXYZ(pos + (i, j, (i, j).Length() * vrange)); // Aim up fairly high
if (shootmode)
{ // Use old function for MBF compatibility
mo = OldSpawnMissile(aimtarget, spawntype, owner);
}
else // Use normal function
{
mo = SpawnMissile(aimtarget, spawntype, owner);
}
if (mo)
{ // Slow it down a bit
mo.Vel *= hrange;
mo.bNoGravity = false; // Make debris fall under gravity
}
}
}
aimtarget.Destroy();
}
}