mirror of
https://github.com/ZDoom/qzdoom-gpl.git
synced 2024-11-16 01:02:03 +00:00
- converted half of ClericHoly. (Making a commit before starting on the more complex stuff.)
- added a 'constructor' for color values.
This commit is contained in:
parent
bc1e4eff72
commit
177aa6ec42
17 changed files with 455 additions and 368 deletions
|
@ -386,6 +386,7 @@ enum ActorFlag7
|
|||
MF7_NOKILLSCRIPTS = 0x01000000, // [JM] No "KILL" Script on death whatsoever, even if forced by GameInfo.
|
||||
MF7_SPRITEANGLE = 0x02000000, // [MC] Utilize the SpriteAngle property and lock the rotation to the degrees specified.
|
||||
MF7_SMASHABLE = 0x04000000, // dies if hitting the floor.
|
||||
MF7_NOSHIELDREFLECT = 0x08000000, // will not be reflected by shields.
|
||||
};
|
||||
|
||||
// --- mobj.renderflags ---
|
||||
|
|
|
@ -24,332 +24,8 @@ static FRandom pr_wraithvergedrop ("WraithvergeDrop");
|
|||
|
||||
void SpawnSpiritTail (AActor *spirit);
|
||||
|
||||
//==========================================================================
|
||||
// Cleric's Wraithverge (Holy Symbol?) --------------------------------------
|
||||
|
||||
class ACWeapWraithverge : public AClericWeapon
|
||||
{
|
||||
DECLARE_CLASS (ACWeapWraithverge, AClericWeapon)
|
||||
public:
|
||||
|
||||
void Serialize(FSerializer &arc)
|
||||
{
|
||||
Super::Serialize (arc);
|
||||
arc("cholycount", CHolyCount);
|
||||
}
|
||||
PalEntry GetBlend ()
|
||||
{
|
||||
if (paletteflash & PF_HEXENWEAPONS)
|
||||
{
|
||||
if (CHolyCount == 3)
|
||||
return PalEntry(128, 70, 70, 70);
|
||||
else if (CHolyCount == 2)
|
||||
return PalEntry(128, 100, 100, 100);
|
||||
else if (CHolyCount == 1)
|
||||
return PalEntry(128, 130, 130, 130);
|
||||
else
|
||||
return PalEntry(0, 0, 0, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
return PalEntry (CHolyCount * 128 / 3, 131, 131, 131);
|
||||
}
|
||||
}
|
||||
BYTE CHolyCount;
|
||||
};
|
||||
|
||||
IMPLEMENT_CLASS(ACWeapWraithverge, false, false)
|
||||
|
||||
// Holy Spirit --------------------------------------------------------------
|
||||
|
||||
IMPLEMENT_CLASS(AHolySpirit, false, false)
|
||||
|
||||
bool AHolySpirit::Slam(AActor *thing)
|
||||
{
|
||||
if (thing->flags&MF_SHOOTABLE && thing != target)
|
||||
{
|
||||
if (multiplayer && !deathmatch && thing->player && target->player)
|
||||
{ // don't attack other co-op players
|
||||
return true;
|
||||
}
|
||||
if (thing->flags2&MF2_REFLECTIVE
|
||||
&& (thing->player || thing->flags2&MF2_BOSS))
|
||||
{
|
||||
tracer = target;
|
||||
target = thing;
|
||||
return true;
|
||||
}
|
||||
if (thing->flags3&MF3_ISMONSTER || thing->player)
|
||||
{
|
||||
tracer = thing;
|
||||
}
|
||||
if (pr_spiritslam() < 96)
|
||||
{
|
||||
int dam = 12;
|
||||
if (thing->player || thing->flags2&MF2_BOSS)
|
||||
{
|
||||
dam = 3;
|
||||
// ghost burns out faster when attacking players/bosses
|
||||
health -= 6;
|
||||
}
|
||||
P_DamageMobj(thing, this, target, dam, NAME_Melee);
|
||||
if (pr_spiritslam() < 128)
|
||||
{
|
||||
Spawn("HolyPuff", Pos(), ALLOW_REPLACE);
|
||||
S_Sound(this, CHAN_WEAPON, "SpiritAttack", 1, ATTN_NORM);
|
||||
if (thing->flags3&MF3_ISMONSTER && pr_spiritslam() < 128)
|
||||
{
|
||||
thing->Howl();
|
||||
}
|
||||
}
|
||||
}
|
||||
if (thing->health <= 0)
|
||||
{
|
||||
tracer = NULL;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool AHolySpirit::SpecialBlastHandling (AActor *source, double strength)
|
||||
{
|
||||
if (tracer == source)
|
||||
{
|
||||
tracer = target;
|
||||
target = source;
|
||||
GC::WriteBarrier(this, source);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
//============================================================================
|
||||
//
|
||||
// A_CHolyAttack2
|
||||
//
|
||||
// Spawns the spirits
|
||||
//============================================================================
|
||||
|
||||
DEFINE_ACTION_FUNCTION(AActor, A_CHolyAttack2)
|
||||
{
|
||||
PARAM_SELF_PROLOGUE(AActor);
|
||||
|
||||
int j;
|
||||
AActor *mo;
|
||||
|
||||
for (j = 0; j < 4; j++)
|
||||
{
|
||||
mo = Spawn<AHolySpirit> (self->Pos(), ALLOW_REPLACE);
|
||||
if (!mo)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
switch (j)
|
||||
{ // float bob index
|
||||
|
||||
case 0:
|
||||
mo->WeaveIndexZ = pr_holyatk2() & 7; // upper-left
|
||||
break;
|
||||
case 1:
|
||||
mo->WeaveIndexZ = 32 + (pr_holyatk2() & 7); // upper-right
|
||||
break;
|
||||
case 2:
|
||||
mo->WeaveIndexXY = 32 + (pr_holyatk2() & 7); // lower-left
|
||||
break;
|
||||
case 3:
|
||||
mo->WeaveIndexXY = 32 + (pr_holyatk2() & 7);
|
||||
mo->WeaveIndexZ = 32 + (pr_holyatk2() & 7);
|
||||
break;
|
||||
}
|
||||
mo->SetZ(self->Z());
|
||||
mo->Angles.Yaw = self->Angles.Yaw + 67.5 - 45.*j;
|
||||
mo->Thrust();
|
||||
mo->target = self->target;
|
||||
mo->args[0] = 10; // initial turn value
|
||||
mo->args[1] = 0; // initial look angle
|
||||
if (deathmatch)
|
||||
{ // Ghosts last slightly less longer in DeathMatch
|
||||
mo->health = 85;
|
||||
}
|
||||
if (self->tracer)
|
||||
{
|
||||
mo->tracer = self->tracer;
|
||||
mo->flags |= MF_NOCLIP|MF_SKULLFLY;
|
||||
mo->flags &= ~MF_MISSILE;
|
||||
}
|
||||
SpawnSpiritTail (mo);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
//============================================================================
|
||||
//
|
||||
// SpawnSpiritTail
|
||||
//
|
||||
//============================================================================
|
||||
|
||||
void SpawnSpiritTail (AActor *spirit)
|
||||
{
|
||||
AActor *tail, *next;
|
||||
int i;
|
||||
|
||||
tail = Spawn ("HolyTail", spirit->Pos(), ALLOW_REPLACE);
|
||||
tail->target = spirit; // parent
|
||||
for (i = 1; i < 3; i++)
|
||||
{
|
||||
next = Spawn ("HolyTailTrail", spirit->Pos(), ALLOW_REPLACE);
|
||||
tail->tracer = next;
|
||||
tail = next;
|
||||
}
|
||||
tail->tracer = NULL; // last tail bit
|
||||
}
|
||||
|
||||
//============================================================================
|
||||
//
|
||||
// A_CHolyAttack
|
||||
//
|
||||
//============================================================================
|
||||
|
||||
DEFINE_ACTION_FUNCTION(AActor, A_CHolyAttack)
|
||||
{
|
||||
PARAM_ACTION_PROLOGUE(AActor);
|
||||
|
||||
player_t *player;
|
||||
FTranslatedLineTarget t;
|
||||
|
||||
if (NULL == (player = self->player))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
ACWeapWraithverge *weapon = static_cast<ACWeapWraithverge *> (self->player->ReadyWeapon);
|
||||
if (weapon != NULL)
|
||||
{
|
||||
if (!weapon->DepleteAmmo (weapon->bAltFire))
|
||||
return 0;
|
||||
}
|
||||
AActor *missile = P_SpawnPlayerMissile (self, 0,0,0, PClass::FindActor("HolyMissile"), self->Angles.Yaw, &t);
|
||||
if (missile != NULL && !t.unlinked)
|
||||
{
|
||||
missile->tracer = t.linetarget;
|
||||
}
|
||||
|
||||
weapon->CHolyCount = 3;
|
||||
S_Sound (self, CHAN_WEAPON, "HolySymbolFire", 1, ATTN_NORM);
|
||||
return 0;
|
||||
}
|
||||
|
||||
//============================================================================
|
||||
//
|
||||
// A_CHolyPalette
|
||||
//
|
||||
//============================================================================
|
||||
|
||||
DEFINE_ACTION_FUNCTION(AActor, A_CHolyPalette)
|
||||
{
|
||||
PARAM_ACTION_PROLOGUE(AActor);
|
||||
|
||||
if (self->player != NULL)
|
||||
{
|
||||
ACWeapWraithverge *weapon = static_cast<ACWeapWraithverge *> (self->player->ReadyWeapon);
|
||||
if (weapon != NULL && weapon->CHolyCount != 0)
|
||||
{
|
||||
weapon->CHolyCount--;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
//============================================================================
|
||||
//
|
||||
// CHolyTailFollow
|
||||
//
|
||||
//============================================================================
|
||||
|
||||
static void CHolyTailFollow(AActor *actor, double dist)
|
||||
{
|
||||
AActor *child;
|
||||
DAngle an;
|
||||
double oldDistance, newDistance;
|
||||
|
||||
while (actor)
|
||||
{
|
||||
child = actor->tracer;
|
||||
if (child)
|
||||
{
|
||||
an = actor->AngleTo(child);
|
||||
oldDistance = child->Distance2D(actor);
|
||||
if (P_TryMove(child, actor->Pos().XY() + an.ToVector(dist), true))
|
||||
{
|
||||
newDistance = child->Distance2D(actor) - 1;
|
||||
if (oldDistance < 1)
|
||||
{
|
||||
if (child->Z() < actor->Z())
|
||||
{
|
||||
child->SetZ(actor->Z() - dist);
|
||||
}
|
||||
else
|
||||
{
|
||||
child->SetZ(actor->Z() + dist);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
child->SetZ(actor->Z() + (newDistance * (child->Z() - actor->Z()) / oldDistance));
|
||||
}
|
||||
}
|
||||
}
|
||||
actor = child;
|
||||
dist -= 1;
|
||||
}
|
||||
}
|
||||
|
||||
//============================================================================
|
||||
//
|
||||
// CHolyTailRemove
|
||||
//
|
||||
//============================================================================
|
||||
|
||||
static void CHolyTailRemove (AActor *actor)
|
||||
{
|
||||
AActor *next;
|
||||
|
||||
while (actor)
|
||||
{
|
||||
next = actor->tracer;
|
||||
actor->Destroy ();
|
||||
actor = next;
|
||||
}
|
||||
}
|
||||
|
||||
//============================================================================
|
||||
//
|
||||
// A_CHolyTail
|
||||
//
|
||||
//============================================================================
|
||||
|
||||
DEFINE_ACTION_FUNCTION(AActor, A_CHolyTail)
|
||||
{
|
||||
PARAM_SELF_PROLOGUE(AActor);
|
||||
|
||||
AActor *parent;
|
||||
|
||||
parent = self->target;
|
||||
|
||||
if (parent == NULL || parent->health <= 0) // better check for health than current state - it's safer!
|
||||
{ // Ghost removed, so remove all tail parts
|
||||
CHolyTailRemove (self);
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (P_TryMove(self, parent->Vec2Angle(14., parent->Angles.Yaw, true), true))
|
||||
{
|
||||
self->SetZ(parent->Z() - 5.);
|
||||
}
|
||||
CHolyTailFollow(self, 10);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
//============================================================================
|
||||
//
|
||||
// CHolyFindTarget
|
||||
|
@ -495,22 +171,3 @@ DEFINE_ACTION_FUNCTION(AActor, A_CHolyCheckScream)
|
|||
return 0;
|
||||
}
|
||||
|
||||
//============================================================================
|
||||
//
|
||||
// A_ClericAttack
|
||||
// (for the ClericBoss)
|
||||
//
|
||||
//============================================================================
|
||||
|
||||
DEFINE_ACTION_FUNCTION(AActor, A_ClericAttack)
|
||||
{
|
||||
PARAM_SELF_PROLOGUE(AActor);
|
||||
|
||||
if (!self->target) return 0;
|
||||
|
||||
AActor * missile = P_SpawnMissileZ (self, self->Z() + 40., self->target, PClass::FindActor ("HolyMissile"));
|
||||
if (missile != NULL) missile->tracer = NULL; // No initial target
|
||||
S_Sound (self, CHAN_WEAPON, "HolySymbolFire", 1, ATTN_NORM);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -5,14 +5,6 @@
|
|||
|
||||
void AdjustPlayerAngle(AActor *pmo, FTranslatedLineTarget *t);
|
||||
|
||||
class AHolySpirit : public AActor
|
||||
{
|
||||
DECLARE_CLASS (AHolySpirit, AActor)
|
||||
public:
|
||||
bool Slam (AActor *thing);
|
||||
bool SpecialBlastHandling (AActor *source, double strength);
|
||||
};
|
||||
|
||||
class AFighterWeapon : public AWeapon
|
||||
{
|
||||
DECLARE_CLASS (AFighterWeapon, AWeapon);
|
||||
|
|
|
@ -189,8 +189,11 @@ void KSpiritInit (AActor *spirit, AActor *korax)
|
|||
spirit->args[0] = 10; // initial turn value
|
||||
spirit->args[1] = 0; // initial look angle
|
||||
|
||||
#if 0 // Temporarily deactivated.
|
||||
// Spawn a tail for spirit
|
||||
SpawnSpiritTail (spirit);
|
||||
HolyTail.SpawnSpiritTail (spirit);
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
//============================================================================
|
||||
|
|
|
@ -1324,6 +1324,27 @@ PalEntry AInventory::GetBlend ()
|
|||
return 0;
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION(AInventory, GetBlend)
|
||||
{
|
||||
PARAM_SELF_PROLOGUE(AInventory);
|
||||
ACTION_RETURN_INT(self->GetBlend());
|
||||
}
|
||||
|
||||
PalEntry AInventory::CallGetBlend()
|
||||
{
|
||||
IFVIRTUAL(AInventory, GetBlend)
|
||||
{
|
||||
VMValue params[1] = { (DObject*)this };
|
||||
VMReturn ret;
|
||||
VMFrameStack stack;
|
||||
int retval;
|
||||
ret.IntAt(&retval);
|
||||
stack.Call(func, params, 1, &ret, 1, nullptr);
|
||||
return retval;
|
||||
}
|
||||
else return GetBlend();
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
// AInventory :: PrevItem
|
||||
|
|
|
@ -215,6 +215,7 @@ public:
|
|||
virtual int AlterWeaponSprite (visstyle_t *vis);
|
||||
|
||||
virtual PalEntry GetBlend ();
|
||||
PalEntry CallGetBlend();
|
||||
|
||||
protected:
|
||||
virtual bool TryPickup (AActor *&toucher);
|
||||
|
|
|
@ -3284,6 +3284,13 @@ void AActor::Howl ()
|
|||
}
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION(AActor, Howl)
|
||||
{
|
||||
PARAM_SELF_PROLOGUE(AActor);
|
||||
self->Howl();
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool AActor::Slam (AActor *thing)
|
||||
{
|
||||
flags &= ~MF_SKULLFLY;
|
||||
|
@ -3367,8 +3374,7 @@ bool AActor::AdjustReflectionAngle (AActor *thing, DAngle &angle)
|
|||
if (absangle(angle, thing->Angles.Yaw) > 45)
|
||||
return true; // Let missile explode
|
||||
|
||||
if (thing->IsKindOf (RUNTIME_CLASS(AHolySpirit))) // shouldn't this be handled by another flag???
|
||||
return true;
|
||||
if (thing->flags7 & MF7_NOSHIELDREFLECT) return true;
|
||||
|
||||
if (pr_reflect () < 128)
|
||||
angle += 45;
|
||||
|
@ -7349,6 +7355,16 @@ DEFINE_ACTION_FUNCTION(AActor, SetXYZ)
|
|||
return 0;
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION(AActor, Vec2Angle)
|
||||
{
|
||||
PARAM_SELF_PROLOGUE(AActor);
|
||||
PARAM_FLOAT(length);
|
||||
PARAM_ANGLE(angle);
|
||||
PARAM_BOOL_DEF(absolute);
|
||||
ACTION_RETURN_VEC2(self->Vec2Angle(length, angle, absolute));
|
||||
}
|
||||
|
||||
|
||||
DEFINE_ACTION_FUNCTION(AActor, Vec3Angle)
|
||||
{
|
||||
PARAM_SELF_PROLOGUE(AActor);
|
||||
|
|
|
@ -6969,13 +6969,19 @@ FxExpression *FxFunctionCall::Resolve(FCompileContext& ctx)
|
|||
|
||||
switch (MethodName)
|
||||
{
|
||||
case NAME_Color:
|
||||
if (ArgList.Size() == 3 || ArgList.Size() == 4)
|
||||
{
|
||||
func = new FxColorLiteral(ArgList, ScriptPosition);
|
||||
break;
|
||||
}
|
||||
// fall through
|
||||
case NAME_Bool:
|
||||
case NAME_Int:
|
||||
case NAME_uInt:
|
||||
case NAME_Float:
|
||||
case NAME_Double:
|
||||
case NAME_Name:
|
||||
case NAME_Color:
|
||||
case NAME_Sound:
|
||||
case NAME_State:
|
||||
case NAME_SpriteID:
|
||||
|
@ -8090,6 +8096,76 @@ ExpEmit FxGetClass::Emit(VMFunctionBuilder *build)
|
|||
return to;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
FxColorLiteral::FxColorLiteral(FArgumentList &args, FScriptPosition &sc)
|
||||
:FxExpression(EFX_ColorLiteral, sc)
|
||||
{
|
||||
ArgList = std::move(args);
|
||||
}
|
||||
|
||||
FxExpression *FxColorLiteral::Resolve(FCompileContext &ctx)
|
||||
{
|
||||
CHECKRESOLVED();
|
||||
unsigned constelements = 0;
|
||||
assert(ArgList.Size() == 3 || ArgList.Size() == 4);
|
||||
if (ArgList.Size() == 3) ArgList.Insert(0, nullptr);
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
if (ArgList[i] != nullptr)
|
||||
{
|
||||
SAFE_RESOLVE(ArgList[i], ctx);
|
||||
if (!ArgList[i]->IsInteger())
|
||||
{
|
||||
ScriptPosition.Message(MSG_ERROR, "Integer expected for color component");
|
||||
delete this;
|
||||
return nullptr;
|
||||
}
|
||||
if (ArgList[i]->isConstant())
|
||||
{
|
||||
constval += clamp(static_cast<FxConstant *>(ArgList[i])->GetValue().GetInt(), 0, 255) << (24 - i * 8);
|
||||
delete ArgList[i];
|
||||
ArgList[i] = nullptr;
|
||||
constelements++;
|
||||
}
|
||||
}
|
||||
else constelements++;
|
||||
}
|
||||
if (constelements == 4)
|
||||
{
|
||||
auto x = new FxConstant(constval, ScriptPosition);
|
||||
x->ValueType = TypeColor;
|
||||
delete this;
|
||||
return x;
|
||||
}
|
||||
ValueType = TypeColor;
|
||||
return this;
|
||||
}
|
||||
|
||||
ExpEmit FxColorLiteral::Emit(VMFunctionBuilder *build)
|
||||
{
|
||||
ExpEmit out(build, REGT_INT);
|
||||
build->Emit(OP_LK, out.RegNum, build->GetConstantInt(constval));
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
if (ArgList[i] != nullptr)
|
||||
{
|
||||
assert(!ArgList[i]->isConstant());
|
||||
ExpEmit in = ArgList[i]->Emit(build);
|
||||
in.Free(build);
|
||||
ExpEmit work(build, REGT_INT);
|
||||
build->Emit(OP_MAX_RK, work.RegNum, in.RegNum, build->GetConstantInt(0));
|
||||
build->Emit(OP_MIN_RK, work.RegNum, work.RegNum, build->GetConstantInt(255));
|
||||
if (i != 3) build->Emit(OP_SLL_RI, work.RegNum, work.RegNum, 24 - (i * 8));
|
||||
build->Emit(OP_OR_RR, out.RegNum, out.RegNum, work.RegNum);
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// FxSequence :: Resolve
|
||||
|
|
|
@ -287,6 +287,7 @@ enum EFxType
|
|||
EFX_CVar,
|
||||
EFX_NamedNode,
|
||||
EFX_GetClass,
|
||||
EFX_ColorLiteral,
|
||||
EFX_COUNT
|
||||
};
|
||||
|
||||
|
@ -1540,6 +1541,24 @@ public:
|
|||
ExpEmit Emit(VMFunctionBuilder *build);
|
||||
};
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// FxColorLiteral
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
class FxColorLiteral : public FxExpression
|
||||
{
|
||||
FArgumentList ArgList;
|
||||
int constval = 0;
|
||||
|
||||
public:
|
||||
|
||||
FxColorLiteral(FArgumentList &args, FScriptPosition &sc);
|
||||
FxExpression *Resolve(FCompileContext&);
|
||||
ExpEmit Emit(VMFunctionBuilder *build);
|
||||
};
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// FxVMFunctionCall
|
||||
|
|
|
@ -296,6 +296,7 @@ static FFlagDef ActorFlagDefs[]=
|
|||
DEFINE_FLAG(MF7, NOKILLSCRIPTS, AActor, flags7),
|
||||
DEFINE_FLAG(MF7, SPRITEANGLE, AActor, flags7),
|
||||
DEFINE_FLAG(MF7, SMASHABLE, AActor, flags7),
|
||||
DEFINE_FLAG(MF7, NOSHIELDREFLECT, AActor, flags7),
|
||||
|
||||
// Effect flags
|
||||
DEFINE_FLAG(FX, VISIBILITYPULSE, AActor, effects),
|
||||
|
|
|
@ -740,7 +740,7 @@ type_name(X) ::= DOT dottable_id(A).
|
|||
/* Type names can also be used as identifiers in contexts where type names
|
||||
* are not normally allowed. */
|
||||
%fallback IDENTIFIER
|
||||
SBYTE BYTE SHORT USHORT INT UINT BOOL FLOAT DOUBLE STRING VECTOR2 VECTOR3 NAME MAP ARRAY VOID STATE.
|
||||
SBYTE BYTE SHORT USHORT INT UINT BOOL FLOAT DOUBLE STRING VECTOR2 VECTOR3 NAME MAP ARRAY VOID STATE COLOR UINT8 INT8 UINT16 INT16.
|
||||
|
||||
/* Aggregate types */
|
||||
%type aggregate_type {ZCC_Type *}
|
||||
|
|
|
@ -104,7 +104,7 @@ void V_AddPlayerBlend (player_t *CPlayer, float blend[4], float maxinvalpha, int
|
|||
// [RH] All powerups can affect the screen blending now
|
||||
for (AInventory *item = CPlayer->mo->Inventory; item != NULL; item = item->Inventory)
|
||||
{
|
||||
PalEntry color = item->GetBlend ();
|
||||
PalEntry color = item->CallGetBlend ();
|
||||
if (color.a != 0)
|
||||
{
|
||||
V_AddBlend (color.r/255.f, color.g/255.f, color.b/255.f, color.a/255.f, blend);
|
||||
|
|
|
@ -339,6 +339,7 @@ class Actor : Thinker native
|
|||
native void SetZ(double z);
|
||||
native vector3 Vec3Offset(double x, double y, double z, bool absolute = false);
|
||||
native vector3 Vec3Angle(double length, double angle, double z = 0, bool absolute = false);
|
||||
native vector2 Vec2Angle(double length, double angle, bool absolute = false);
|
||||
native vector3 Vec2OffsetZ(double x, double y, double atz, bool absolute = false);
|
||||
native void VelFromAngle(double speed = 0, double angle = 0);
|
||||
native void Thrust(double speed = 0, double angle = 0);
|
||||
|
@ -351,6 +352,7 @@ class Actor : Thinker native
|
|||
native double DistanceBySpeed(Actor other, double speed);
|
||||
native name GetSpecies();
|
||||
native void PlayActiveSound();
|
||||
native void Howl();
|
||||
|
||||
// DECORATE compatible functions
|
||||
native bool CheckClass(class<Actor> checkclass, int ptr_select = AAPTR_DEFAULT, bool match_superclass = false);
|
||||
|
|
|
@ -1007,3 +1007,11 @@ enum EGameType
|
|||
GAME_DoomChex = GAME_Doom|GAME_Chex,
|
||||
GAME_DoomStrifeChex = GAME_Doom|GAME_Strife|GAME_Chex
|
||||
}
|
||||
|
||||
enum PaletteFlashFlags
|
||||
{
|
||||
PF_HEXENWEAPONS = 1,
|
||||
PF_POISON = 2,
|
||||
PF_ICE = 4,
|
||||
PF_HAZARD = 8,
|
||||
};
|
||||
|
|
|
@ -18,8 +18,6 @@ class ClericBoss : Actor
|
|||
Obituary "$OBCBOSS";
|
||||
}
|
||||
|
||||
native void A_ClericAttack();
|
||||
|
||||
States
|
||||
{
|
||||
Spawn:
|
||||
|
@ -79,4 +77,19 @@ class ClericBoss : Actor
|
|||
FDTH V 4 Bright ;
|
||||
Stop;
|
||||
}
|
||||
|
||||
//============================================================================
|
||||
//
|
||||
// A_ClericAttack
|
||||
//
|
||||
//============================================================================
|
||||
|
||||
void A_ClericAttack()
|
||||
{
|
||||
if (!target) return;
|
||||
|
||||
Actor missile = SpawnMissileZ (pos.z + 40., target, "HolyMissile");
|
||||
if (missile != null) missile.tracer = null; // No initial target
|
||||
A_PlaySound ("HolySymbolFire", CHAN_WEAPON);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -76,8 +76,10 @@ class WraithvergeDrop : Actor
|
|||
|
||||
// Cleric's Wraithverge (Holy Symbol?) --------------------------------------
|
||||
|
||||
class CWeapWraithverge : ClericWeapon native
|
||||
class CWeapWraithverge : ClericWeapon
|
||||
{
|
||||
int CHolyCount;
|
||||
|
||||
Default
|
||||
{
|
||||
Health 3;
|
||||
|
@ -96,8 +98,6 @@ class CWeapWraithverge : ClericWeapon native
|
|||
Inventory.PickupSound "WeaponBuild";
|
||||
}
|
||||
|
||||
action native void A_CHolyAttack();
|
||||
action native void A_CHolyPalette();
|
||||
|
||||
States
|
||||
{
|
||||
|
@ -122,6 +122,66 @@ class CWeapWraithverge : ClericWeapon native
|
|||
CHLY G 2 Offset (0, 36) A_CHolyPalette;
|
||||
Goto Ready;
|
||||
}
|
||||
|
||||
override color GetBlend ()
|
||||
{
|
||||
if (paletteflash & PF_HEXENWEAPONS)
|
||||
{
|
||||
if (CHolyCount == 3)
|
||||
return Color(128, 70, 70, 70);
|
||||
else if (CHolyCount == 2)
|
||||
return Color(128, 100, 100, 100);
|
||||
else if (CHolyCount == 1)
|
||||
return Color(128, 130, 130, 130);
|
||||
else
|
||||
return Color(0, 0, 0, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
return Color(CHolyCount * 128 / 3, 131, 131, 131);
|
||||
}
|
||||
}
|
||||
|
||||
//============================================================================
|
||||
//
|
||||
// A_CHolyAttack
|
||||
//
|
||||
//============================================================================
|
||||
|
||||
action void A_CHolyAttack()
|
||||
{
|
||||
FTranslatedLineTarget t;
|
||||
|
||||
if (player == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
Weapon weapon = player.ReadyWeapon;
|
||||
if (weapon != null)
|
||||
{
|
||||
if (!weapon.DepleteAmmo (weapon.bAltFire))
|
||||
return;
|
||||
}
|
||||
Actor missile = SpawnPlayerMissile ("HolyMissile", angle, pLineTarget:t);
|
||||
if (missile != null && !t.unlinked)
|
||||
{
|
||||
missile.tracer = t.linetarget;
|
||||
}
|
||||
|
||||
invoker.CHolyCount = 3;
|
||||
A_PlaySound ("HolySymbolFire", CHAN_WEAPON);
|
||||
}
|
||||
|
||||
//============================================================================
|
||||
//
|
||||
// A_CHolyPalette
|
||||
//
|
||||
//============================================================================
|
||||
|
||||
action void A_CHolyPalette()
|
||||
{
|
||||
if (invoker.CHolyCount > 0) invoker.CHolyCount--;
|
||||
}
|
||||
}
|
||||
|
||||
// Holy Missile -------------------------------------------------------------
|
||||
|
@ -139,8 +199,6 @@ class HolyMissile : Actor
|
|||
+EXTREMEDEATH
|
||||
}
|
||||
|
||||
native void A_CHolyAttack2();
|
||||
|
||||
States
|
||||
{
|
||||
Spawn:
|
||||
|
@ -149,6 +207,60 @@ class HolyMissile : Actor
|
|||
SPIR P 1 Bright A_CHolyAttack2;
|
||||
Stop;
|
||||
}
|
||||
|
||||
//============================================================================
|
||||
//
|
||||
// A_CHolyAttack2
|
||||
//
|
||||
// Spawns the spirits
|
||||
//============================================================================
|
||||
|
||||
void A_CHolyAttack2()
|
||||
{
|
||||
for (int j = 0; j < 4; j++)
|
||||
{
|
||||
Actor mo = Spawn("HolySpirit", Pos, ALLOW_REPLACE);
|
||||
if (!mo)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
switch (j)
|
||||
{ // float bob index
|
||||
|
||||
case 0:
|
||||
mo.WeaveIndexZ = random[HolyAtk2]() & 7; // upper-left
|
||||
break;
|
||||
case 1:
|
||||
mo.WeaveIndexZ = 32 + (random[HolyAtk2]() & 7); // upper-right
|
||||
break;
|
||||
case 2:
|
||||
mo.WeaveIndexXY = 32 + (random[HolyAtk2]() & 7); // lower-left
|
||||
break;
|
||||
case 3:
|
||||
mo.WeaveIndexXY = 32 + (random[HolyAtk2]() & 7);
|
||||
mo.WeaveIndexZ = 32 + (random[HolyAtk2]() & 7);
|
||||
break;
|
||||
}
|
||||
mo.SetZ(pos.z);
|
||||
mo.angle = angle + 67.5 - 45.*j;
|
||||
mo.Thrust();
|
||||
mo.target = target;
|
||||
mo.args[0] = 10; // initial turn value
|
||||
mo.args[1] = 0; // initial look angle
|
||||
if (deathmatch)
|
||||
{ // Ghosts last slightly less longer in DeathMatch
|
||||
mo.health = 85;
|
||||
}
|
||||
if (tracer)
|
||||
{
|
||||
mo.tracer = tracer;
|
||||
mo.bNoClip = true;
|
||||
mo.bSkullFly = true;
|
||||
mo.bMissile = false;
|
||||
}
|
||||
HolyTail.SpawnSpiritTail (mo);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Holy Missile Puff --------------------------------------------------------
|
||||
|
@ -192,7 +304,7 @@ class HolyPuff : Actor
|
|||
|
||||
// Holy Spirit --------------------------------------------------------------
|
||||
|
||||
class HolySpirit : Actor native
|
||||
class HolySpirit : Actor
|
||||
{
|
||||
Default
|
||||
{
|
||||
|
@ -204,7 +316,7 @@ class HolySpirit : Actor native
|
|||
Projectile;
|
||||
+RIPPER +SEEKERMISSILE
|
||||
+FOILINVUL +SKYEXPLODE +NOEXPLODEFLOOR +CANBLAST
|
||||
+EXTREMEDEATH
|
||||
+EXTREMEDEATH +NOSHIELDREFLECT
|
||||
RenderStyle "Translucent";
|
||||
Alpha 0.4;
|
||||
DeathSound "SpiritDie";
|
||||
|
@ -226,6 +338,70 @@ class HolySpirit : Actor native
|
|||
SPIR FGHI 4;
|
||||
Stop;
|
||||
}
|
||||
|
||||
//============================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//============================================================================
|
||||
|
||||
override bool Slam(Actor thing)
|
||||
{
|
||||
if (thing.bShootable && thing != target)
|
||||
{
|
||||
if (multiplayer && !deathmatch && thing.player && target.player)
|
||||
{ // don't attack other co-op players
|
||||
return true;
|
||||
}
|
||||
if (thing.bReflective && (thing.player || thing.bBoss))
|
||||
{
|
||||
tracer = target;
|
||||
target = thing;
|
||||
return true;
|
||||
}
|
||||
if (thing.bIsMonster || thing.player)
|
||||
{
|
||||
tracer = thing;
|
||||
}
|
||||
if (random[SpiritSlam]() < 96)
|
||||
{
|
||||
int dam = 12;
|
||||
if (thing.player || thing.bBoss)
|
||||
{
|
||||
dam = 3;
|
||||
// ghost burns out faster when attacking players/bosses
|
||||
health -= 6;
|
||||
}
|
||||
thing.DamageMobj(self, target, dam, 'Melee');
|
||||
if (random[SpiritSlam]() < 128)
|
||||
{
|
||||
Spawn("HolyPuff", Pos, ALLOW_REPLACE);
|
||||
A_PlaySound("SpiritAttack", CHAN_WEAPON);
|
||||
if (thing.bIsMonster && random[SpiritSlam]() < 128)
|
||||
{
|
||||
thing.Howl();
|
||||
}
|
||||
}
|
||||
}
|
||||
if (thing.health <= 0)
|
||||
{
|
||||
tracer = null;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
override bool SpecialBlastHandling (Actor source, double strength)
|
||||
{
|
||||
if (tracer == source)
|
||||
{
|
||||
tracer = target;
|
||||
target = source;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
// Holy Tail ----------------------------------------------------------------
|
||||
|
@ -242,8 +418,6 @@ class HolyTail : Actor
|
|||
Alpha 0.6;
|
||||
}
|
||||
|
||||
native void A_CHolyTail();
|
||||
|
||||
States
|
||||
{
|
||||
Spawn:
|
||||
|
@ -253,6 +427,108 @@ class HolyTail : Actor
|
|||
SPIR D -1;
|
||||
Stop;
|
||||
}
|
||||
|
||||
//============================================================================
|
||||
//
|
||||
// SpawnSpiritTail
|
||||
//
|
||||
//============================================================================
|
||||
|
||||
static void SpawnSpiritTail (Actor spirit)
|
||||
{
|
||||
Actor tail = Spawn ("HolyTail", spirit.Pos, ALLOW_REPLACE);
|
||||
tail.target = spirit; // parent
|
||||
for (int i = 1; i < 3; i++)
|
||||
{
|
||||
Actor next = Spawn ("HolyTailTrail", spirit.Pos, ALLOW_REPLACE);
|
||||
tail.tracer = next;
|
||||
tail = next;
|
||||
}
|
||||
tail.tracer = null; // last tail bit
|
||||
}
|
||||
|
||||
//============================================================================
|
||||
//
|
||||
// CHolyTailFollow
|
||||
//
|
||||
//============================================================================
|
||||
|
||||
private void CHolyTailFollow(double dist)
|
||||
{
|
||||
Actor mo = self;
|
||||
while (mo)
|
||||
{
|
||||
Actor child = mo.tracer;
|
||||
if (child)
|
||||
{
|
||||
double an = mo.AngleTo(child);
|
||||
double oldDistance = child.Distance2D(mo);
|
||||
if (child.TryMove(mo.Pos.XY + AngleToVector(an, dist), true))
|
||||
{
|
||||
double newDistance = child.Distance2D(mo) - 1;
|
||||
if (oldDistance < 1)
|
||||
{
|
||||
if (child.pos.z < mo.pos.z)
|
||||
{
|
||||
child.SetZ(mo.pos.z - dist);
|
||||
}
|
||||
else
|
||||
{
|
||||
child.SetZ(mo.pos.z + dist);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
child.SetZ(mo.pos.z + (newDistance * (child.pos.z - mo.pos.z) / oldDistance));
|
||||
}
|
||||
}
|
||||
}
|
||||
mo = child;
|
||||
dist -= 1;
|
||||
}
|
||||
}
|
||||
|
||||
//============================================================================
|
||||
//
|
||||
// CHolyTailRemove
|
||||
//
|
||||
//============================================================================
|
||||
|
||||
private void CHolyTailRemove ()
|
||||
{
|
||||
Actor mo = self;
|
||||
while (mo)
|
||||
{
|
||||
Actor next = mo.tracer;
|
||||
mo.Destroy ();
|
||||
mo = next;
|
||||
}
|
||||
}
|
||||
|
||||
//============================================================================
|
||||
//
|
||||
// A_CHolyTail
|
||||
//
|
||||
//============================================================================
|
||||
|
||||
void A_CHolyTail()
|
||||
{
|
||||
Actor parent = self.target;
|
||||
|
||||
if (parent == null || parent.health <= 0) // better check for health than current state - it's safer!
|
||||
{ // Ghost removed, so remove all tail parts
|
||||
CHolyTailRemove ();
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (TryMove(parent.Vec2Angle(14., parent.Angle, true), true))
|
||||
{
|
||||
self.SetZ(parent.pos.z - 5.);
|
||||
}
|
||||
CHolyTailFollow(10);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Holy Tail Trail ---------------------------------------------------------
|
||||
|
|
|
@ -26,6 +26,7 @@ class Inventory : Actor native
|
|||
}
|
||||
|
||||
virtual native bool Use (bool pickup);
|
||||
virtual native color GetBlend ();
|
||||
|
||||
|
||||
// These are regular functions for the item itself.
|
||||
|
|
Loading…
Reference in a new issue