- scriptified Chicken and Pig - not tested yet, because other things have priority.

This commit is contained in:
Christoph Oelckers 2016-11-22 12:21:55 +01:00
parent 3db712cd73
commit bbb0778fd4
16 changed files with 309 additions and 357 deletions

View file

@ -867,7 +867,6 @@ set( NOT_COMPILED_SOURCE_FILES
sc_man_scanner.h sc_man_scanner.h
sc_man_scanner.re sc_man_scanner.re
g_doom/a_painelemental.cpp g_doom/a_painelemental.cpp
g_heretic/a_chicken.cpp
g_heretic/a_dsparil.cpp g_heretic/a_dsparil.cpp
g_heretic/a_hereticartifacts.cpp g_heretic/a_hereticartifacts.cpp
g_heretic/a_hereticweaps.cpp g_heretic/a_hereticweaps.cpp
@ -892,7 +891,6 @@ set( NOT_COMPILED_SOURCE_FILES
g_hexen/a_magecone.cpp g_hexen/a_magecone.cpp
g_hexen/a_magelightning.cpp g_hexen/a_magelightning.cpp
g_hexen/a_magestaff.cpp g_hexen/a_magestaff.cpp
g_hexen/a_pig.cpp
g_hexen/a_serpent.cpp g_hexen/a_serpent.cpp
g_hexen/a_spike.cpp g_hexen/a_spike.cpp
g_hexen/a_summon.cpp g_hexen/a_summon.cpp

View file

@ -100,6 +100,7 @@ FString GetPrintableDisplayName(PClassPlayerPawn *cls);
class APlayerPawn : public AActor class APlayerPawn : public AActor
{ {
DECLARE_CLASS_WITH_META(APlayerPawn, AActor, PClassPlayerPawn) DECLARE_CLASS_WITH_META(APlayerPawn, AActor, PClassPlayerPawn)
HAS_FIELDS
HAS_OBJECT_POINTERS HAS_OBJECT_POINTERS
public: public:
@ -116,7 +117,7 @@ public:
virtual void PlayRunning (); virtual void PlayRunning ();
virtual void ThrowPoisonBag (); virtual void ThrowPoisonBag ();
virtual void TweakSpeeds (double &forwardmove, double &sidemove); virtual void TweakSpeeds (double &forwardmove, double &sidemove);
virtual void MorphPlayerThink (); void MorphPlayerThink ();
virtual void ActivateMorphWeapon (); virtual void ActivateMorphWeapon ();
AWeapon *PickNewWeapon (PClassAmmo *ammotype); AWeapon *PickNewWeapon (PClassAmmo *ammotype);
AWeapon *BestWeapon (PClassAmmo *ammotype); AWeapon *BestWeapon (PClassAmmo *ammotype);

View file

@ -1,233 +0,0 @@
/*
#include "actor.h"
#include "gi.h"
#include "m_random.h"
#include "s_sound.h"
#include "d_player.h"
#include "a_action.h"
#include "a_pickups.h"
#include "p_local.h"
#include "a_sharedglobal.h"
#include "p_enemy.h"
#include "d_event.h"
#include "gstrings.h"
#include "vm.h"
*/
void P_UpdateBeak (AActor *actor);
static FRandom pr_chickenplayerthink ("ChickenPlayerThink");
static FRandom pr_chicattack ("ChicAttack");
static FRandom pr_feathers ("Feathers");
static FRandom pr_beakatkpl1 ("BeakAtkPL1");
static FRandom pr_beakatkpl2 ("BeakAtkPL2");
class AChickenPlayer : public APlayerPawn
{
DECLARE_CLASS (AChickenPlayer, APlayerPawn)
public:
void MorphPlayerThink ();
};
IMPLEMENT_CLASS(AChickenPlayer, false, false, false, false)
void AChickenPlayer::MorphPlayerThink ()
{
if (health > 0)
{ // Handle beak movement
P_UpdateBeak (this);
}
if (player->morphTics & 15)
{
return;
}
if (Vel.X == 0 && Vel.Y == 0 && pr_chickenplayerthink () < 160)
{ // Twitch view angle
Angles.Yaw += pr_chickenplayerthink.Random2() * (360. / 256. / 32.);
}
if ((Z() <= floorz) && (pr_chickenplayerthink() < 32))
{ // Jump and noise
Vel.Z += JumpZ;
FState * painstate = FindState(NAME_Pain);
if (painstate != NULL) SetState (painstate);
}
if (pr_chickenplayerthink () < 48)
{ // Just noise
S_Sound (this, CHAN_VOICE, "chicken/active", 1, ATTN_NORM);
}
}
//----------------------------------------------------------------------------
//
// PROC A_ChicAttack
//
//----------------------------------------------------------------------------
DEFINE_ACTION_FUNCTION(AActor, A_ChicAttack)
{
PARAM_SELF_PROLOGUE(AActor);
if (!self->target)
{
return 0;
}
if (self->CheckMeleeRange())
{
int damage = 1 + (pr_chicattack() & 1);
int newdam = P_DamageMobj (self->target, self, self, damage, NAME_Melee);
P_TraceBleed (newdam > 0 ? newdam : damage, self->target, self);
}
return 0;
}
//----------------------------------------------------------------------------
//
// PROC A_Feathers
//
//----------------------------------------------------------------------------
DEFINE_ACTION_FUNCTION(AActor, A_Feathers)
{
PARAM_SELF_PROLOGUE(AActor);
int i;
int count;
AActor *mo;
if (self->health > 0)
{ // Pain
count = pr_feathers() < 32 ? 2 : 1;
}
else
{ // Death
count = 5 + (pr_feathers()&3);
}
for (i = 0; i < count; i++)
{
mo = Spawn("Feather", self->PosPlusZ(20.), NO_REPLACE);
mo->target = self;
mo->Vel.X = pr_feathers.Random2() / 256.;
mo->Vel.Y = pr_feathers.Random2() / 256.;
mo->Vel.Z = 1. + pr_feathers() / 128.;
mo->SetState (mo->SpawnState + (pr_feathers()&7));
}
return 0;
}
//---------------------------------------------------------------------------
//
// PROC P_UpdateBeak
//
//---------------------------------------------------------------------------
void P_UpdateBeak (AActor *self)
{
DPSprite *pspr;
if (self->player != nullptr && (pspr = self->player->FindPSprite(PSP_WEAPON)) != nullptr)
{
pspr->y = WEAPONTOP + self->player->chickenPeck / 2;
}
}
//---------------------------------------------------------------------------
//
// PROC A_BeakRaise
//
//---------------------------------------------------------------------------
DEFINE_ACTION_FUNCTION(AActor, A_BeakRaise)
{
PARAM_ACTION_PROLOGUE(AActor);
player_t *player;
if (nullptr == (player = self->player))
{
return 0;
}
player->GetPSprite(PSP_WEAPON)->y = WEAPONTOP;
P_SetPsprite(player, PSP_WEAPON, player->ReadyWeapon->GetReadyState());
return 0;
}
//----------------------------------------------------------------------------
//
// PROC P_PlayPeck
//
//----------------------------------------------------------------------------
void P_PlayPeck (AActor *chicken)
{
S_Sound (chicken, CHAN_VOICE, "chicken/peck", 1, ATTN_NORM);
}
//----------------------------------------------------------------------------
//
// PROC A_BeakAttackPL1
//
//----------------------------------------------------------------------------
DEFINE_ACTION_FUNCTION(AActor, A_BeakAttackPL1)
{
PARAM_ACTION_PROLOGUE(AActor);
DAngle angle;
int damage;
DAngle slope;
player_t *player;
FTranslatedLineTarget t;
if (NULL == (player = self->player))
{
return 0;
}
damage = 1 + (pr_beakatkpl1()&3);
angle = player->mo->Angles.Yaw;
slope = P_AimLineAttack (player->mo, angle, MELEERANGE);
P_LineAttack (player->mo, angle, MELEERANGE, slope, damage, NAME_Melee, "BeakPuff", true, &t);
if (t.linetarget)
{
player->mo->Angles.Yaw = t.angleFromSource;
}
P_PlayPeck (player->mo);
player->chickenPeck = 12;
player->GetPSprite(PSP_WEAPON)->Tics -= pr_beakatkpl1() & 7;
return 0;
}
//----------------------------------------------------------------------------
//
// PROC A_BeakAttackPL2
//
//----------------------------------------------------------------------------
DEFINE_ACTION_FUNCTION(AActor, A_BeakAttackPL2)
{
PARAM_ACTION_PROLOGUE(AActor);
DAngle angle;
int damage;
DAngle slope;
player_t *player;
FTranslatedLineTarget t;
if (NULL == (player = self->player))
{
return 0;
}
damage = pr_beakatkpl2.HitDice (4);
angle = player->mo->Angles.Yaw;
slope = P_AimLineAttack (player->mo, angle, MELEERANGE);
P_LineAttack (player->mo, angle, MELEERANGE, slope, damage, NAME_Melee, "BeakPuff", true, &t);
if (t.linetarget)
{
player->mo->Angles.Yaw = t.angleFromSource;
}
P_PlayPeck (player->mo);
player->chickenPeck = 12;
player->GetPSprite(PSP_WEAPON)->Tics -= pr_beakatkpl2()&3;
return 0;
}

View file

@ -19,7 +19,6 @@
#include "serializer.h" #include "serializer.h"
// Include all the other Heretic stuff here to reduce compile time // Include all the other Heretic stuff here to reduce compile time
#include "a_chicken.cpp"
#include "a_dsparil.cpp" #include "a_dsparil.cpp"
#include "a_hereticartifacts.cpp" #include "a_hereticartifacts.cpp"
#include "a_hereticweaps.cpp" #include "a_hereticweaps.cpp"

View file

@ -49,6 +49,13 @@ void AdjustPlayerAngle (AActor *pmo, FTranslatedLineTarget *t)
} }
} }
DEFINE_ACTION_FUNCTION(AActor, AdjustPlayerAngle)
{
PARAM_SELF_PROLOGUE(AActor);
PARAM_POINTER(t, FTranslatedLineTarget);
AdjustPlayerAngle(self, t);
return 0;
}
//============================================================================ //============================================================================
// //
// TryPunch // TryPunch

View file

@ -45,7 +45,6 @@
#include "a_magecone.cpp" #include "a_magecone.cpp"
#include "a_magelightning.cpp" #include "a_magelightning.cpp"
#include "a_magestaff.cpp" #include "a_magestaff.cpp"
#include "a_pig.cpp"
#include "a_serpent.cpp" #include "a_serpent.cpp"
#include "a_spike.cpp" #include "a_spike.cpp"
#include "a_summon.cpp" #include "a_summon.cpp"

View file

@ -1,107 +0,0 @@
/*
#include "actor.h"
#include "gi.h"
#include "m_random.h"
#include "s_sound.h"
#include "d_player.h"
#include "a_action.h"
#include "a_pickups.h"
#include "p_local.h"
#include "a_sharedglobal.h"
#include "p_enemy.h"
#include "d_event.h"
#include "gstrings.h"
#include "vm.h"
*/
static FRandom pr_snoutattack ("SnoutAttack");
static FRandom pr_pigattack ("PigAttack");
static FRandom pr_pigplayerthink ("PigPlayerThink");
// Pig player ---------------------------------------------------------------
class APigPlayer : public APlayerPawn
{
DECLARE_CLASS (APigPlayer, APlayerPawn)
public:
void MorphPlayerThink ();
};
IMPLEMENT_CLASS(APigPlayer, false, false, false, false)
void APigPlayer::MorphPlayerThink ()
{
if (player->morphTics & 15)
{
return;
}
if(Vel.X == 0 && Vel.Y == 0 && pr_pigplayerthink() < 64)
{ // Snout sniff
if (player->ReadyWeapon != nullptr)
{
P_SetPsprite(player, PSP_WEAPON, player->ReadyWeapon->FindState("Grunt"));
}
S_Sound (this, CHAN_VOICE, "PigActive1", 1, ATTN_NORM); // snort
return;
}
if (pr_pigplayerthink() < 48)
{
S_Sound (this, CHAN_VOICE, "PigActive", 1, ATTN_NORM);
}
}
//============================================================================
//
// A_SnoutAttack
//
//============================================================================
DEFINE_ACTION_FUNCTION(AActor, A_SnoutAttack)
{
PARAM_ACTION_PROLOGUE(AActor);
DAngle angle;
int damage;
DAngle slope;
player_t *player;
AActor *puff;
FTranslatedLineTarget t;
if (NULL == (player = self->player))
{
return 0;
}
damage = 3+(pr_snoutattack()&3);
angle = player->mo->Angles.Yaw;
slope = P_AimLineAttack(player->mo, angle, MELEERANGE);
puff = P_LineAttack(player->mo, angle, MELEERANGE, slope, damage, NAME_Melee, "SnoutPuff", true, &t);
S_Sound(player->mo, CHAN_VOICE, "PigActive", 1, ATTN_NORM);
if(t.linetarget)
{
AdjustPlayerAngle(player->mo, &t);
if(puff != NULL)
{ // Bit something
S_Sound(player->mo, CHAN_VOICE, "PigAttack", 1, ATTN_NORM);
}
}
return 0;
}
//============================================================================
//
// A_PigPain
//
//============================================================================
DEFINE_ACTION_FUNCTION(AActor, A_PigPain)
{
PARAM_SELF_PROLOGUE(AActor);
CALL_ACTION(A_Pain, self);
if (self->Z() <= self->floorz)
{
self->Vel.Z = 3.5;
}
return 0;
}

View file

@ -109,8 +109,29 @@ IMPLEMENT_POINTERS_END
void DPSprite::InitNativeFields() void DPSprite::InitNativeFields()
{ {
auto meta = RUNTIME_CLASS(DPSprite); auto meta = RUNTIME_CLASS(DPSprite);
PType *TypeActor = NewPointer(RUNTIME_CLASS(AActor));
PType *TypePSP = NewPointer(RUNTIME_CLASS(DPSprite));
PType *TypePlayer = NewPointer(NewNativeStruct("Player", nullptr));
meta->AddNativeField("State", TypeState, myoffsetof(DPSprite, State), VARF_ReadOnly); meta->AddNativeField("State", TypeState, myoffsetof(DPSprite, State), VARF_ReadOnly);
meta->AddNativeField("Caller", TypeActor, myoffsetof(DPSprite, Caller), VARF_ReadOnly);
meta->AddNativeField("Next", TypePSP, myoffsetof(DPSprite, Next), VARF_ReadOnly);
meta->AddNativeField("Owner", TypePlayer, myoffsetof(DPSprite, Owner), VARF_ReadOnly);
meta->AddNativeField("Sprite", TypeSpriteID, myoffsetof(DPSprite, Sprite));
meta->AddNativeField("Frame", TypeSInt32, myoffsetof(DPSprite, Frame));
meta->AddNativeField("ID", TypePlayer, myoffsetof(DPSprite, ID), VARF_ReadOnly);
meta->AddNativeField("processPending", TypeBool, myoffsetof(DPSprite, processPending));
meta->AddNativeField("x", TypeFloat64, myoffsetof(DPSprite, x));
meta->AddNativeField("y", TypeFloat64, myoffsetof(DPSprite, y));
meta->AddNativeField("oldx", TypeFloat64, myoffsetof(DPSprite, oldx));
meta->AddNativeField("oldy", TypeFloat64, myoffsetof(DPSprite, oldy));
meta->AddNativeField("firstTic", TypeBool, myoffsetof(DPSprite, firstTic));
meta->AddNativeField("Tics", TypeSInt32, myoffsetof(DPSprite, Tics));
meta->AddNativeField("bAddWeapon", TypeSInt32, myoffsetof(DPSprite, Flags), 0, PSPF_ADDWEAPON);
meta->AddNativeField("bAddBob", TypeSInt32, myoffsetof(DPSprite, Flags), 0, PSPF_ADDBOB);
meta->AddNativeField("bPowDouble", TypeSInt32, myoffsetof(DPSprite, Flags), 0, PSPF_POWDOUBLE);
meta->AddNativeField("bCVarFast", TypeSInt32, myoffsetof(DPSprite, Flags), 0, PSPF_CVARFAST);
meta->AddNativeField("bFlip", TypeSInt32, myoffsetof(DPSprite, Flags), 0, PSPF_FLIP);
} }
//------------------------------------------------------------------------ //------------------------------------------------------------------------
@ -179,6 +200,14 @@ DPSprite *player_t::FindPSprite(int layer)
return pspr; return pspr;
} }
DEFINE_ACTION_FUNCTION(_Player, FindPSprite) // the underscore is needed to get past the name mangler which removes the first clas name character to match the class representation (needs to be fixed in a later commit)
{
PARAM_SELF_STRUCT_PROLOGUE(player_t);
PARAM_INT(id);
ACTION_RETURN_OBJECT(self->FindPSprite((PSPLayers)id));
}
//------------------------------------------------------------------------ //------------------------------------------------------------------------
// //
// //

View file

@ -161,6 +161,12 @@ bool ValidatePlayerClass(PClassActor *ti, const char *name)
return true; return true;
} }
void APlayerPawn::InitNativeFields()
{
auto meta = RUNTIME_CLASS(APlayerPawn);
meta->AddNativeField("JumpZ", TypeFloat64, myoffsetof(APlayerPawn, JumpZ));
}
void SetupPlayerClasses () void SetupPlayerClasses ()
{ {
FPlayerClass newclass; FPlayerClass newclass;
@ -622,7 +628,7 @@ void player_t::SendPitchLimits() const
// //
//=========================================================================== //===========================================================================
IMPLEMENT_CLASS(APlayerPawn, false, true, false, true) IMPLEMENT_CLASS(APlayerPawn, false, true, true, true)
IMPLEMENT_POINTERS_START(APlayerPawn) IMPLEMENT_POINTERS_START(APlayerPawn)
IMPLEMENT_POINTER(InvFirst) IMPLEMENT_POINTER(InvFirst)
@ -1381,6 +1387,10 @@ void APlayerPawn::GiveDefaultInventory ()
void APlayerPawn::MorphPlayerThink () void APlayerPawn::MorphPlayerThink ()
{ {
VINDEX(APlayerPawn, MorphPlayerThink);
VMValue params[1] = { (DObject*)this };
VMFrameStack stack;
stack.Call(VFUNC, params, 1, nullptr, 0, nullptr);
} }
void APlayerPawn::ActivateMorphWeapon () void APlayerPawn::ActivateMorphWeapon ()

View file

@ -2543,7 +2543,15 @@ FxExpression *FxAddSub::Resolve(FCompileContext& ctx)
return nullptr; return nullptr;
} }
if (left->IsVector() && right->IsVector()) if (left->ValueType == TypeState && right->IsInteger() && Operator == '+' && !left->isConstant())
{
// This is the only special case of pointer addition that will be accepted - because it is used quite often in the existing game code.
ValueType = TypeState;
right = new FxMulDiv('*', right, new FxConstant((int)sizeof(FState), ScriptPosition)); // multiply by size here, so that constants can be better optimized.
right = right->Resolve(ctx);
ABORT(right);
}
else if (left->IsVector() && right->IsVector())
{ {
// a vector2 can be added to or subtracted from a vector 3 but it needs to be the right operand. // a vector2 can be added to or subtracted from a vector 3 but it needs to be the right operand.
if (left->ValueType == right->ValueType || (left->ValueType == TypeVector3 && right->ValueType == TypeVector2)) if (left->ValueType == right->ValueType || (left->ValueType == TypeVector3 && right->ValueType == TypeVector2))
@ -2616,6 +2624,16 @@ ExpEmit FxAddSub::Emit(VMFunctionBuilder *build)
ExpEmit op2 = right->Emit(build); ExpEmit op2 = right->Emit(build);
if (Operator == '+') if (Operator == '+')
{ {
if (op1.RegType == REGT_POINTER)
{
assert(!op1.Konst);
assert(op2.RegType == REGT_INT);
op1.Free(build);
op2.Free(build);
ExpEmit opout(build, REGT_POINTER);
build->Emit(op2.Konst? OP_ADDA_RK : OP_ADDA_RR, opout.RegNum, op1.RegNum, op2.RegNum);
return opout;
}
// Since addition is commutative, only the second operand may be a constant. // Since addition is commutative, only the second operand may be a constant.
if (op1.Konst) if (op1.Konst)
{ {

View file

@ -67,6 +67,7 @@ class Actor : Thinker native
virtual native void Activate(Actor activator); virtual native void Activate(Actor activator);
virtual native void Deactivate(Actor activator); virtual native void Deactivate(Actor activator);
native void AdjustPlayerAngle(FTranslatedLineTarget t);
native static readonly<Actor> GetDefaultByType(class<Actor> cls); native static readonly<Actor> GetDefaultByType(class<Actor> cls);
native static double deltaangle(double ang1, double ang2); native static double deltaangle(double ang1, double ang2);
native static double absangle(double ang1, double ang2); native static double absangle(double ang1, double ang2);
@ -467,7 +468,6 @@ class Actor : Thinker native
native void A_DeQueueCorpse(); native void A_DeQueueCorpse();
native void A_ClearLastHeard(); native void A_ClearLastHeard();
native bool A_SelectWeapon(class<Weapon> whichweapon, int flags = 0); native bool A_SelectWeapon(class<Weapon> whichweapon, int flags = 0);
native void A_Feathers();
native void A_ClassBossHealth(); native void A_ClassBossHealth();
native void A_ShootGun(); native void A_ShootGun();
native void A_RocketInFlight(); native void A_RocketInFlight();
@ -476,7 +476,6 @@ class Actor : Thinker native
native void A_GiveQuestItem(int itemno); native void A_GiveQuestItem(int itemno);
native void A_RemoveForcefield(); native void A_RemoveForcefield();
native void A_DropWeaponPieces(class<Actor> p1, class<Actor> p2, class<Actor> p3); native void A_DropWeaponPieces(class<Actor> p1, class<Actor> p2, class<Actor> p3);
native void A_PigPain ();
native void A_SetAngle(double angle = 0, int flags = 0, int ptr = AAPTR_DEFAULT); native void A_SetAngle(double angle = 0, int flags = 0, int ptr = AAPTR_DEFAULT);
native void A_SetPitch(double pitch, int flags = 0, int ptr = AAPTR_DEFAULT); native void A_SetPitch(double pitch, int flags = 0, int ptr = AAPTR_DEFAULT);
native void A_SetRoll(double roll, int flags = 0, int ptr = AAPTR_DEFAULT); native void A_SetRoll(double roll, int flags = 0, int ptr = AAPTR_DEFAULT);

View file

@ -948,3 +948,8 @@ enum EFSkillProperty // floating point properties
SKILLP_FriendlyHealth, SKILLP_FriendlyHealth,
}; };
enum EWeaponPos
{
WEAPONBOTTOM = 128,
WEAPONTOP = 32
}

View file

@ -26,8 +26,6 @@ class Beak : Weapon
Weapon.SisterWeapon "BeakPowered"; Weapon.SisterWeapon "BeakPowered";
} }
action native void A_BeakRaise ();
action native void A_BeakAttackPL1();
States States
{ {
@ -44,9 +42,56 @@ class Beak : Weapon
BEAK A 18 A_BeakAttackPL1; BEAK A 18 A_BeakAttackPL1;
Goto Ready; Goto Ready;
} }
//---------------------------------------------------------------------------
//
// PROC A_BeakRaise
//
//---------------------------------------------------------------------------
action void A_BeakRaise ()
{
if (player == null)
{
return;
}
player.GetPSprite(PSP_WEAPON).y = WEAPONTOP;
player.SetPsprite(PSP_WEAPON, player.ReadyWeapon.GetReadyState());
}
//----------------------------------------------------------------------------
//
// PROC A_BeakAttackPL1
//
//----------------------------------------------------------------------------
action void A_BeakAttackPL1()
{
FTranslatedLineTarget t;
if (player == null)
{
return;
}
int damage = random[BeakAtk](1,3);
double ang = angle;
double slope = AimLineAttack (ang, MELEERANGE);
LineAttack (ang, MELEERANGE, slope, damage, 'Melee', "BeakPuff", true, t);
if (t.linetarget)
{
angle = t.angleFromSource;
}
A_PlaySound ("chicken/peck", CHAN_VOICE);
player.chickenPeck = 12;
player.GetPSprite(PSP_WEAPON).Tics -= random[BeakAtk](0,7);
}
} }
// BeakPowered ---------------------------------------------------------------------
class BeakPowered : Beak class BeakPowered : Beak
{ {
Default Default
@ -55,7 +100,6 @@ class BeakPowered : Beak
Weapon.SisterWeapon "Beak"; Weapon.SisterWeapon "Beak";
} }
action native void A_BeakAttackPL2();
States States
{ {
@ -63,11 +107,40 @@ class BeakPowered : Beak
BEAK A 12 A_BeakAttackPL2; BEAK A 12 A_BeakAttackPL2;
Goto Ready; Goto Ready;
} }
//----------------------------------------------------------------------------
//
// PROC A_BeakAttackPL2
//
//----------------------------------------------------------------------------
action void A_BeakAttackPL2()
{
FTranslatedLineTarget t;
if (player == null)
{
return;
}
int damage = random[BeakAtk](1,8) * 4;
double ang = angle;
double slope = AimLineAttack (ang, MELEERANGE);
LineAttack (ang, MELEERANGE, slope, damage, 'Melee', "BeakPuff", true, t);
if (t.linetarget)
{
angle = t.angleFromSource;
}
A_PlaySound ("chicken/peck", CHAN_VOICE);
player.chickenPeck = 12;
player.GetPSprite(PSP_WEAPON).Tics -= random[BeakAtk](0,3);
}
} }
// Chicken player ----------------------------------------------------------- // Chicken player -----------------------------------------------------------
class ChickenPlayer : PlayerPawn native class ChickenPlayer : PlayerPawn
{ {
Default Default
{ {
@ -118,6 +191,44 @@ class ChickenPlayer : PlayerPawn native
CHKN L -1; CHKN L -1;
Stop; Stop;
} }
//---------------------------------------------------------------------------
//
// PROC P_UpdateBeak
//
//---------------------------------------------------------------------------
override void MorphPlayerThink ()
{
if (health > 0)
{ // Handle beak movement
PSprite pspr;
if (player != null && (pspr = player.FindPSprite(PSP_WEAPON)) != null)
{
pspr.y = WEAPONTOP + player.chickenPeck / 2;
}
}
if (player.morphTics & 15)
{
return;
}
if (Vel.X == 0 && Vel.Y == 0 && random[ChickenPlayerThink]() < 160)
{ // Twitch view ang
angle += Random2[ChickenPlayerThink]() * (360. / 256. / 32.);
}
if ((pos.z <= floorz) && (random[ChickenPlayerThink]() < 32))
{ // Jump and noise
Vel.Z += JumpZ;
State painstate = FindState('Pain');
if (painstate != null) SetState (painstate);
}
if (random[ChickenPlayerThink]() < 48)
{ // Just noise
A_PlaySound ("chicken/active", CHAN_VOICE);
}
}
} }
@ -199,3 +310,37 @@ class Feather : Actor
} }
} }
extend class Actor
{
//----------------------------------------------------------------------------
//
// PROC A_Feathers
// This is used by both the chicken player and monster and must be in the
// common base class to be accessible by both
//
//----------------------------------------------------------------------------
void A_Feathers()
{
int count;
if (health > 0)
{ // Pain
count = random[Feathers]() < 32 ? 2 : 1;
}
else
{ // Death
count = 5 + (random[Feathers]()&3);
}
for (int i = 0; i < count; i++)
{
Actor mo = Spawn("Feather", pos + (0, 0, 20), NO_REPLACE);
mo.target = self;
mo.Vel.X = Random2[Feathers]() / 256.;
mo.Vel.Y = Random2[Feathers]() / 256.;
mo.Vel.Z = 1. + random[Feathers]() / 128.;
mo.SetState (mo.SpawnState + (random[Feathers]()&7));
}
}
}

View file

@ -34,8 +34,6 @@ class Snout : Weapon
Weapon.YAdjust 10; Weapon.YAdjust 10;
} }
action native void A_SnoutAttack ();
States States
{ {
Ready: Ready:
@ -54,12 +52,43 @@ class Snout : Weapon
WPIG B 8; WPIG B 8;
Goto Ready; Goto Ready;
} }
//============================================================================
//
// A_SnoutAttack
//
//============================================================================
action void A_SnoutAttack ()
{
FTranslatedLineTarget t;
if (player == null)
{
return;
}
int damage = random[SnoutAttack](3, 6);
double ang = angle;
double slope = AimLineAttack(ang, MELEERANGE);
Actor puff = LineAttack(ang, MELEERANGE, slope, damage, 'Melee', "SnoutPuff", true, t);
A_PlaySound("PigActive", CHAN_VOICE);
if(t.linetarget)
{
AdjustPlayerAngle(t);
if(puff != null)
{ // Bit something
A_PlaySound("PigAttack", CHAN_VOICE);
}
}
}
} }
// Pig player --------------------------------------------------------------- // Pig player ---------------------------------------------------------------
class PigPlayer : PlayerPawn native class PigPlayer : PlayerPawn
{ {
Default Default
{ {
@ -112,6 +141,30 @@ class PigPlayer : PlayerPawn native
PIGY M 1 A_FreezeDeathChunks; PIGY M 1 A_FreezeDeathChunks;
Wait; Wait;
} }
override void MorphPlayerThink ()
{
if (player.morphTics & 15)
{
return;
}
if(Vel.X == 0 && Vel.Y == 0 && random[PigPlayerThink]() < 64)
{ // Snout sniff
if (player.ReadyWeapon != null)
{
player.SetPsprite(PSP_WEAPON, player.ReadyWeapon.FindState('Grunt'));
}
A_PlaySound ("PigActive1", CHAN_VOICE); // snort
return;
}
if (random[PigPlayerThink]() < 48)
{
A_PlaySound ("PigActive", CHAN_VOICE); // snort
}
}
} }
@ -168,3 +221,21 @@ class Pig : MorphedMonster
} }
} }
extend class Actor
{
//============================================================================
//
// A_PigPain
//
//============================================================================
void A_PigPain ()
{
A_Pain();
if (pos.z <= floorz)
{
Vel.Z = 3.5;
}
}
}

View file

@ -457,6 +457,12 @@ class Weapon : StateProvider native
native bool DepleteAmmo(bool altFire, bool checkEnough = true, int ammouse = -1); native bool DepleteAmmo(bool altFire, bool checkEnough = true, int ammouse = -1);
virtual State GetReadyState ()
{
return FindState('Ready');
}
native action void A_ZoomFactor(double scale = 1, int flags = 0); native action void A_ZoomFactor(double scale = 1, int flags = 0);
native action void A_SetCrosshair(int xhair); native action void A_SetCrosshair(int xhair);
const ZOOM_INSTANT = 1; const ZOOM_INSTANT = 1;

View file

@ -49,6 +49,10 @@ class PlayerPawn : Actor native
if (MeleeState != null) SetState (MeleeState); if (MeleeState != null) SetState (MeleeState);
} }
virtual void MorphPlayerThink()
{
}
} }
class PlayerChunk : PlayerPawn native class PlayerChunk : PlayerPawn native
@ -78,4 +82,5 @@ struct Player native
native void SetPsprite(int id, State stat, bool pending = false); native void SetPsprite(int id, State stat, bool pending = false);
native void SetSafeFlash(Weapon weap, State flashstate, int index); native void SetSafeFlash(Weapon weap, State flashstate, int index);
native PSprite GetPSprite(int id); native PSprite GetPSprite(int id);
native PSprite FindPSprite(int id);
} }