diff --git a/src/actor.h b/src/actor.h index 28e68b64a..193741995 100644 --- a/src/actor.h +++ b/src/actor.h @@ -111,6 +111,8 @@ struct FPortalGroupArray; // Any questions? // + + // --- mobj.flags --- enum ActorFlag { @@ -1014,7 +1016,9 @@ public: SDWORD tics; // state tic counter FState *state; - VMFunction *Damage; // For missiles and monster railgun + //VMFunction *Damage; // For missiles and monster railgun + int DamageVal; + VMFunction *DamageFunc; int projectileKickback; ActorFlags flags; ActorFlags2 flags2; // Heretic flags @@ -1201,6 +1205,23 @@ public: FState *GetRaiseState(); void Revive(); + void SetDamage(int dmg) + { + DamageVal = dmg; + DamageFunc = nullptr; + } + + bool IsZeroDamage() const + { + return DamageVal == 0 && DamageFunc == nullptr; + } + + void RestoreDamage() + { + DamageVal = GetDefault()->DamageVal; + DamageFunc = GetDefault()->DamageFunc; + } + FState *FindState (FName label) const { return GetClass()->FindState(1, &label); diff --git a/src/d_dehacked.cpp b/src/d_dehacked.cpp index 4beb5c25a..dfa4f56f0 100644 --- a/src/d_dehacked.cpp +++ b/src/d_dehacked.cpp @@ -915,7 +915,7 @@ static int PatchThing (int thingy) } else if (linelen == 14 && stricmp (Line1, "Missile damage") == 0) { - info->Damage = CreateDamageFunction(val); + info->SetDamage(val); } else if (linelen == 5) { diff --git a/src/g_doom/a_bossbrain.cpp b/src/g_doom/a_bossbrain.cpp index 5d10e0138..e6fd5f16d 100644 --- a/src/g_doom/a_bossbrain.cpp +++ b/src/g_doom/a_bossbrain.cpp @@ -47,7 +47,7 @@ static void BrainishExplosion (const DVector3 &pos) boom->SetState (state); } boom->effects = 0; - boom->Damage = NULL; // disables collision detection which is not wanted here + boom->SetDamage(0); // disables collision detection which is not wanted here boom->tics -= pr_brainscream() & 7; if (boom->tics < 1) boom->tics = 1; diff --git a/src/g_heretic/a_ironlich.cpp b/src/g_heretic/a_ironlich.cpp index 7f33fb57a..a88748407 100644 --- a/src/g_heretic/a_ironlich.cpp +++ b/src/g_heretic/a_ironlich.cpp @@ -115,7 +115,7 @@ DEFINE_ACTION_FUNCTION(AActor, A_LichAttack) fire->target = baseFire->target; fire->Angles.Yaw = baseFire->Angles.Yaw; fire->Vel = baseFire->Vel; - fire->Damage = NULL; + fire->SetDamage(0); fire->health = (i+1) * 2; P_CheckMissileSpawn (fire, self->radius); } @@ -205,7 +205,7 @@ DEFINE_ACTION_FUNCTION(AActor, A_LichFireGrow) self->AddZ(9.); if (self->health == 0) { - self->Damage = self->GetDefault()->Damage; + self->RestoreDamage(); self->SetState (self->FindState("NoGrow")); } return 0; diff --git a/src/g_hexen/a_magelightning.cpp b/src/g_hexen/a_magelightning.cpp index b5718f4d7..896384f4e 100644 --- a/src/g_hexen/a_magelightning.cpp +++ b/src/g_hexen/a_magelightning.cpp @@ -340,7 +340,7 @@ DEFINE_ACTION_FUNCTION(AActor, A_LastZap) { mo->SetState (mo->FindState (NAME_Death)); mo->Vel.Z = 40; - mo->Damage = NULL; + mo->SetDamage(0); } return 0; } diff --git a/src/gl/scene/gl_drawinfo.cpp b/src/gl/scene/gl_drawinfo.cpp index 0d612cf64..d1dd60c4f 100644 --- a/src/gl/scene/gl_drawinfo.cpp +++ b/src/gl/scene/gl_drawinfo.cpp @@ -497,6 +497,10 @@ void GLDrawList::SortWallIntoWall(SortNode * head,SortNode * sort) // // //========================================================================== +EXTERN_CVAR(Int, gl_billboard_mode) +EXTERN_CVAR(Bool, gl_billboard_faces_camera) +EXTERN_CVAR(Bool, gl_billboard_particles) + void GLDrawList::SortSpriteIntoWall(SortNode * head,SortNode * sort) { GLWall * wh=&walls[drawitems[head->itemindex].index]; @@ -527,6 +531,27 @@ void GLDrawList::SortSpriteIntoWall(SortNode * head,SortNode * sort) } else { + const bool drawWithXYBillboard = ((ss->particle && gl_billboard_particles) || (!(ss->actor && ss->actor->renderflags & RF_FORCEYBILLBOARD) + && (gl_billboard_mode == 1 || (ss->actor && ss->actor->renderflags & RF_FORCEXYBILLBOARD)))); + + const bool drawBillboardFacingCamera = gl_billboard_faces_camera; + // [Nash] has +ROLLSPRITE + const bool rotated = (ss->actor != nullptr && ss->actor->renderflags & RF_ROLLSPRITE | RF_WALLSPRITE | RF_FLATSPRITE); + + // cannot sort them at the moment. This requires more complex splitting. + if (drawWithXYBillboard || drawBillboardFacingCamera || rotated) + { + float v1 = wh->PointOnSide(ss->x, ss->y); + if (v1 < 0) + { + head->AddToLeft(sort); + } + else + { + head->AddToRight(sort); + } + return; + } double r=ss->CalcIntersectionVertex(wh); float ix=(float)(ss->x1 + r * (ss->x2-ss->x1)); diff --git a/src/gl/scene/gl_sprite.cpp b/src/gl/scene/gl_sprite.cpp index af76de826..720df3b2b 100644 --- a/src/gl/scene/gl_sprite.cpp +++ b/src/gl/scene/gl_sprite.cpp @@ -923,7 +923,7 @@ void GLSprite::Process(AActor* thing, sector_t * sector, int thruportal) // This is a non-translucent sprite (i.e. STYLE_Normal or equivalent) trans=1.f; - if (!gl_sprite_blend || modelframe || (thing->renderflags & RF_SPRITETYPEMASK) == RF_WALLSPRITE) + if (!gl_sprite_blend || modelframe || (thing->renderflags & (RF_FLATSPRITE|RF_WALLSPRITE)) || gl_billboard_faces_camera) { RenderStyle.SrcAlpha = STYLEALPHA_One; RenderStyle.DestAlpha = STYLEALPHA_Zero; @@ -934,8 +934,6 @@ void GLSprite::Process(AActor* thing, sector_t * sector, int thruportal) RenderStyle.SrcAlpha = STYLEALPHA_Src; RenderStyle.DestAlpha = STYLEALPHA_InvSrc; } - - } if ((gltexture && gltexture->GetTransparent()) || (RenderStyle.Flags & STYLEF_RedIsAlpha)) { diff --git a/src/info.cpp b/src/info.cpp index 7198a5de4..6eeca1186 100644 --- a/src/info.cpp +++ b/src/info.cpp @@ -344,7 +344,7 @@ size_t PClassActor::PropagateMark() // Mark damage function if (Defaults != NULL) { - GC::Mark(((AActor *)Defaults)->Damage); + GC::Mark(((AActor *)Defaults)->DamageFunc); } // marked += ActorInfo->NumOwnedStates * sizeof(FState); diff --git a/src/p_acs.cpp b/src/p_acs.cpp index 67ae37771..ffac7a78f 100644 --- a/src/p_acs.cpp +++ b/src/p_acs.cpp @@ -3791,7 +3791,7 @@ void DLevelScript::DoSetActorProperty (AActor *actor, int property, int value) break; case APROP_Damage: - actor->Damage = CreateDamageFunction(value); + actor->SetDamage(value); break; case APROP_Alpha: diff --git a/src/p_map.cpp b/src/p_map.cpp index 7316e1c96..5cc9bdf72 100644 --- a/src/p_map.cpp +++ b/src/p_map.cpp @@ -1320,7 +1320,7 @@ bool PIT_CheckThing(FMultiBlockThingsIterator &it, FMultiBlockThingsIterator::Ch // [RH] What is the point of this check, again? In Hexen, it is unconditional, // but here we only do it if the missile's damage is 0. // MBF bouncer might have a non-0 damage value, but they must not deal damage on impact either. - if ((tm.thing->BounceFlags & BOUNCE_Actors) && (tm.thing->Damage == 0 || !(tm.thing->flags & MF_MISSILE))) + if ((tm.thing->BounceFlags & BOUNCE_Actors) && (tm.thing->IsZeroDamage() || !(tm.thing->flags & MF_MISSILE))) { return (tm.thing->target == thing || !(thing->flags & MF_SOLID)); } diff --git a/src/p_mobj.cpp b/src/p_mobj.cpp index 6d8ce4418..d38901f1e 100644 --- a/src/p_mobj.cpp +++ b/src/p_mobj.cpp @@ -139,7 +139,8 @@ IMPLEMENT_POINTY_CLASS (AActor) DECLARE_POINTER (LastHeard) DECLARE_POINTER (master) DECLARE_POINTER (Poisoner) - DECLARE_POINTER (Damage) + DECLARE_POINTER (DamageFunc) + DECLARE_POINTER (alternative) END_POINTERS AActor::~AActor () @@ -148,73 +149,6 @@ AActor::~AActor () // Use Destroy() instead. } -//========================================================================== -// -// CalcDamageValue -// -// Given a script function, returns an integer to represent it in a -// savegame. This encoding is compatible with previous incarnations -// where damage was an integer. -// -// 0 : use null function -// 0x40000000 : use default function -// anything else : use function that returns this number -// -//========================================================================== - -static int CalcDamageValue(VMFunction *func) -{ - if (func == NULL) - { - return 0; - } - VMScriptFunction *sfunc = dyn_cast(func); - if (sfunc == NULL) - { - return 0x40000000; - } - VMOP *op = sfunc->Code; - // If the function was created by CreateDamageFunction(), extract - // the value used to create it and return that. Otherwise, return - // indicating to use the default function. - if (op->op == OP_RETI && op->a == 0) - { - return op->i16; - } - if (op->op == OP_RET && op->a == 0 && op->b == (REGT_INT | REGT_KONST)) - { - return sfunc->KonstD[op->c]; - } - return 0x40000000; -} - -//========================================================================== -// -// UncalcDamageValue -// -// Given a damage integer, returns a script function for it. -// -//========================================================================== - -static VMFunction *UncalcDamageValue(int dmg, VMFunction *def) -{ - if (dmg == 0) - { - return NULL; - } - if ((dmg & 0xC0000000) == 0x40000000) - { - return def; - } - // Does the default version return this? If so, use it. Otherwise, - // create a new function. - if (CalcDamageValue(def) == dmg) - { - return def; - } - return CreateDamageFunction(dmg); -} - //========================================================================== // // AActor :: Serialize @@ -262,18 +196,16 @@ void AActor::Serialize(FArchive &arc) << projectilepassheight << Vel << tics - << state; - if (arc.IsStoring()) + << state + << DamageVal; + if (DamageVal == 0x40000000 || DamageVal == -1) { - int dmg; - dmg = CalcDamageValue(Damage); - arc << dmg; + DamageVal = -1; + DamageFunc = GetDefault()->DamageFunc; } else { - int dmg; - arc << dmg; - Damage = UncalcDamageValue(dmg, GetDefault()->Damage); + DamageFunc = nullptr; } P_SerializeTerrain(arc, floorterrain); arc << projectileKickback @@ -2974,8 +2906,21 @@ CCMD(utid) int AActor::GetMissileDamage (int mask, int add) { - if (Damage == NULL) + if (DamageVal >= 0) { + if (mask == 0) + { + return add * DamageVal; + } + else + { + return ((pr_missiledamage() & mask) + add) * DamageVal; + } + } + if (DamageFunc == nullptr) + { + // This should never happen + assert(false && "No damage function found"); return 0; } VMFrameStack stack; @@ -2987,22 +2932,11 @@ int AActor::GetMissileDamage (int mask, int add) results[0].IntAt(&amount); results[1].IntAt(&calculated); - if (stack.Call(Damage, ¶m, 1, results, 2) < 1) + if (stack.Call(DamageFunc, ¶m, 1, results, 2) < 1) { // No results return 0; } - if (calculated) - { - return amount; - } - else if (mask == 0) - { - return add * amount; - } - else - { - return ((pr_missiledamage() & mask) + add) * amount; - } + return amount; } void AActor::Howl () @@ -3694,7 +3628,7 @@ void AActor::Tick () // still have missiles that go straight up and down through actors without // damaging anything. // (for backwards compatibility this must check for lack of damage function, not for zero damage!) - if ((flags & MF_MISSILE) && Vel.X == 0 && Vel.Y == 0 && Damage != NULL) + if ((flags & MF_MISSILE) && Vel.X == 0 && Vel.Y == 0 && !IsZeroDamage()) { Vel.X = MinVel; } diff --git a/src/thingdef/olddecorations.cpp b/src/thingdef/olddecorations.cpp index 5fa994262..3ea0c4788 100644 --- a/src/thingdef/olddecorations.cpp +++ b/src/thingdef/olddecorations.cpp @@ -496,8 +496,7 @@ static void ParseInsideDecoration (Baggage &bag, AActor *defaults, else if (def == DEF_Projectile && sc.Compare ("Damage")) { sc.MustGetNumber (); - FxDamageValue *x = new FxDamageValue(new FxConstant(sc.Number, sc), false); - defaults->Damage = (VMFunction *)(uintptr_t)(ActorDamageFuncs.Push(x) + 1); + defaults->SetDamage(sc.Number); } else if (def == DEF_Projectile && sc.Compare ("DamageType")) { diff --git a/src/thingdef/thingdef.cpp b/src/thingdef/thingdef.cpp index dfc960627..635637149 100644 --- a/src/thingdef/thingdef.cpp +++ b/src/thingdef/thingdef.cpp @@ -348,12 +348,12 @@ static void FinishThingdef() continue; } - if (def->Damage != NULL) + if (def->DamageFunc != nullptr) { - FxDamageValue *dmg = (FxDamageValue *)ActorDamageFuncs[(uintptr_t)def->Damage - 1]; + FxDamageValue *dmg = (FxDamageValue *)ActorDamageFuncs[(uintptr_t)def->DamageFunc - 1]; VMScriptFunction *sfunc; sfunc = dmg->GetFunction(); - if (sfunc == NULL) + if (sfunc == nullptr) { FCompileContext ctx(ti); dmg = static_cast(dmg->Resolve(ctx)); @@ -365,15 +365,15 @@ static void FinishThingdef() dmg->Emit(&buildit); sfunc = buildit.MakeFunction(); sfunc->NumArgs = 1; - sfunc->Proto = NULL; ///FIXME: Need a proper prototype here + sfunc->Proto = nullptr; ///FIXME: Need a proper prototype here // Save this function in case this damage value was reused // (which happens quite easily with inheritance). dmg->SetFunction(sfunc); } } - def->Damage = sfunc; + def->DamageFunc = sfunc; - if (dump != NULL && sfunc != NULL) + if (dump != nullptr && sfunc != nullptr) { char label[64]; int labellen = mysnprintf(label, countof(label), "Function %s.Damage", diff --git a/src/thingdef/thingdef_codeptr.cpp b/src/thingdef/thingdef_codeptr.cpp index 642f132af..21551364b 100644 --- a/src/thingdef/thingdef_codeptr.cpp +++ b/src/thingdef/thingdef_codeptr.cpp @@ -231,6 +231,38 @@ DEFINE_ACTION_FUNCTION(AActor, CheckClass) return 0; } +//========================================================================== +// +// CheckClass +// +// NON-ACTION function to calculate missile damage for the given actor +// +//========================================================================== + +DEFINE_ACTION_FUNCTION(AActor, GetMissileDamage) +{ + if (numret > 0) + { + assert(ret != NULL); + PARAM_SELF_PROLOGUE(AActor); + PARAM_INT(mask); + PARAM_INT(add) + PARAM_INT_OPT(pick_pointer) { pick_pointer = AAPTR_DEFAULT; } + + self = COPY_AAPTR(self, pick_pointer); + if (self == NULL) + { + ret->SetInt(0); + } + else + { + ret->SetInt(self->GetMissileDamage(mask, add)); + } + return 1; + } + return 0; +} + //========================================================================== // // IsPointerEqual diff --git a/src/thingdef/thingdef_data.cpp b/src/thingdef/thingdef_data.cpp index 756c56a1a..8248634a3 100644 --- a/src/thingdef/thingdef_data.cpp +++ b/src/thingdef/thingdef_data.cpp @@ -666,4 +666,5 @@ void InitThingdef() symt.AddSymbol(new PField(NAME_Speed, TypeFloat64, VARF_Native, myoffsetof(AActor, Speed))); symt.AddSymbol(new PField(NAME_Threshold, TypeSInt32, VARF_Native|VARF_ReadOnly, myoffsetof(AActor, threshold))); symt.AddSymbol(new PField(NAME_DefThreshold, TypeSInt32, VARF_Native|VARF_ReadOnly, myoffsetof(AActor, DefThreshold))); + symt.AddSymbol(new PField(NAME_Damage, TypeSInt32, VARF_Native|VARF_ReadOnly, myoffsetof(AActor, DamageVal))); } diff --git a/src/thingdef/thingdef_exp.h b/src/thingdef/thingdef_exp.h index 2f06e085e..5fe8f252f 100644 --- a/src/thingdef/thingdef_exp.h +++ b/src/thingdef/thingdef_exp.h @@ -854,20 +854,6 @@ public: ExpEmit Emit(VMFunctionBuilder *build); }; -//========================================================================== -// -// FxDamage -// -//========================================================================== - -class FxDamage : public FxExpression -{ -public: - FxDamage(const FScriptPosition&); - FxExpression *Resolve(FCompileContext&); - ExpEmit Emit(VMFunctionBuilder *build); -}; - //========================================================================== // // FxArrayElement @@ -1188,12 +1174,11 @@ public: class FxDamageValue : public FxExpression { FxExpression *val; - bool Calculated; VMScriptFunction *MyFunction; public: - FxDamageValue(FxExpression *v, bool calc); + FxDamageValue(FxExpression *v); ~FxDamageValue(); FxExpression *Resolve(FCompileContext&); diff --git a/src/thingdef/thingdef_expression.cpp b/src/thingdef/thingdef_expression.cpp index 034c304c2..ce603f261 100644 --- a/src/thingdef/thingdef_expression.cpp +++ b/src/thingdef/thingdef_expression.cpp @@ -3218,11 +3218,6 @@ FxExpression *FxIdentifier::Resolve(FCompileContext& ctx) ScriptPosition.Message(MSG_ERROR, "Invalid member identifier '%s'\n", Identifier.GetChars()); } } - // the damage property needs special handling - else if (Identifier == NAME_Damage) - { - newex = new FxDamage(ScriptPosition); - } // now check the global identifiers. else if ((sym = ctx.FindGlobal(Identifier)) != NULL) { @@ -3316,65 +3311,6 @@ ExpEmit FxSelf::Emit(VMFunctionBuilder *build) } -//========================================================================== -// -// -// -//========================================================================== - -FxDamage::FxDamage(const FScriptPosition &pos) -: FxExpression(pos) -{ -} - -//========================================================================== -// -// FxDamage :: Resolve -// -//========================================================================== - -FxExpression *FxDamage::Resolve(FCompileContext& ctx) -{ - CHECKRESOLVED(); - ValueType = TypeSInt32; - return this; -} - -//========================================================================== -// -// FxDamage :: Emit -// -// Call this actor's damage function, if it has one -// -//========================================================================== - -ExpEmit FxDamage::Emit(VMFunctionBuilder *build) -{ - ExpEmit dmgval(build, REGT_INT); - - // Get damage function - ExpEmit dmgfunc(build, REGT_POINTER); - build->Emit(OP_LO, dmgfunc.RegNum, 0/*self*/, build->GetConstantInt(myoffsetof(AActor, Damage))); - - // If it's non-null... - build->Emit(OP_EQA_K, 1, dmgfunc.RegNum, build->GetConstantAddress(nullptr, ATAG_GENERIC)); - size_t nulljump = build->Emit(OP_JMP, 0); - - // ...call it - build->Emit(OP_PARAM, 0, REGT_POINTER, 0/*self*/); - build->Emit(OP_CALL, dmgfunc.RegNum, 1, 1); - build->Emit(OP_RESULT, 0, REGT_INT, dmgval.RegNum); - size_t notnulljump = build->Emit(OP_JMP, 0); - - // Otherwise, use 0 - build->BackpatchToHere(nulljump); - build->EmitLoadInt(dmgval.RegNum, 0); - build->BackpatchToHere(notnulljump); - - return dmgval; -} - - //========================================================================== // // @@ -5225,18 +5161,12 @@ ExpEmit FxMultiNameState::Emit(VMFunctionBuilder *build) // //========================================================================== -FxDamageValue::FxDamageValue(FxExpression *v, bool calc) +FxDamageValue::FxDamageValue(FxExpression *v) : FxExpression(v->ScriptPosition) { val = v; ValueType = TypeVoid; - Calculated = calc; MyFunction = NULL; - - if (!calc) - { - assert(v->isConstant() && "Non-calculated damage must be constant"); - } } FxDamageValue::~FxDamageValue() @@ -5272,7 +5202,7 @@ ExpEmit FxDamageValue::Emit(VMFunctionBuilder *build) assert(emitval.RegType == REGT_INT); build->Emit(OP_RET, 0, REGT_INT | (emitval.Konst ? REGT_KONST : 0), emitval.RegNum); } - build->Emit(OP_RETI, 1 | RET_FINAL, Calculated); + build->Emit(OP_RETI, 1 | RET_FINAL, true); return ExpEmit(); } diff --git a/src/thingdef/thingdef_parse.cpp b/src/thingdef/thingdef_parse.cpp index b99f1a384..963165ab1 100644 --- a/src/thingdef/thingdef_parse.cpp +++ b/src/thingdef/thingdef_parse.cpp @@ -865,19 +865,21 @@ static bool ParsePropertyParams(FScanner &sc, FPropertyInfo *prop, AActor *defau if (sc.CheckString ("(")) { - x = new FxDamageValue(new FxIntCast(ParseExpression(sc, bag.Info)), true); + conv.i = -1; + params.Push(conv); + x = new FxDamageValue(new FxIntCast(ParseExpression(sc, bag.Info))); sc.MustGetStringName(")"); + conv.exp = x; + params.Push(conv); + } else { sc.MustGetNumber(); - if (sc.Number != 0) - { - x = new FxDamageValue(new FxConstant(sc.Number, bag.ScriptPosition), false); - } + conv.i = sc.Number; + params.Push(conv); + conv.exp = nullptr; } - conv.exp = x; - params.Push(conv); } break; diff --git a/src/thingdef/thingdef_properties.cpp b/src/thingdef/thingdef_properties.cpp index f6ad511af..52194435b 100644 --- a/src/thingdef/thingdef_properties.cpp +++ b/src/thingdef/thingdef_properties.cpp @@ -636,7 +636,8 @@ DEFINE_PROPERTY(threshold, I, Actor) //========================================================================== DEFINE_PROPERTY(damage, X, Actor) { - PROP_EXP_PARM(id, 0); + PROP_INT_PARM(dmgval, 0); + PROP_EXP_PARM(id, 1); // Damage can either be a single number, in which case it is subject // to the original damage calculation rules. Or, it can be an expression @@ -646,13 +647,15 @@ DEFINE_PROPERTY(damage, X, Actor) // Store this expression here for now. It will be converted to a function // later once all actors have been processed. - if (id == NULL) + defaults->DamageVal = dmgval; + + if (id == nullptr) { - defaults->Damage = NULL; + defaults->DamageFunc = nullptr; } else { - defaults->Damage = (VMFunction *)(uintptr_t)(ActorDamageFuncs.Push(id) + 1); + defaults->DamageFunc = (VMFunction *)(uintptr_t)(ActorDamageFuncs.Push(id) + 1); } } diff --git a/wadsrc/static/actors/actor.txt b/wadsrc/static/actors/actor.txt index b8bbcab14..b8b805219 100644 --- a/wadsrc/static/actors/actor.txt +++ b/wadsrc/static/actors/actor.txt @@ -53,6 +53,7 @@ ACTOR Actor native //: Thinker native int CountProximity(class classname, float distance, int flags = 0, int ptr = AAPTR_DEFAULT); native float GetSpriteAngle(int ptr = AAPTR_DEFAULT); native float GetSpriteRotation(int ptr = AAPTR_DEFAULT); + native int GetMissileDamage(int mask, int add, int ptr = AAPTR_DEFAULT); // Action functions // Meh, MBF redundant functions. Only for DeHackEd support.