mirror of
https://github.com/ptitSeb/Serious-Engine
synced 2024-11-25 03:40:26 +01:00
482 lines
15 KiB
Erlang
482 lines
15 KiB
Erlang
|
/* Copyright (c) 2002-2012 Croteam Ltd. All rights reserved. */
|
||
|
|
||
|
311
|
||
|
%{
|
||
|
#include "StdH.h"
|
||
|
%}
|
||
|
|
||
|
uses "EntitiesMP/EnemyBase";
|
||
|
uses "EntitiesMP/Debris";
|
||
|
uses "EntitiesMP/EnemyMarker";
|
||
|
|
||
|
|
||
|
enum EnemyFlyType {
|
||
|
0 EFT_GROUND_ONLY "Ground only", // can't fly
|
||
|
1 EFT_FLY_ONLY "Fly only", // always fly can't land
|
||
|
2 EFT_FLY_GROUND_GROUND "Fly(ground) - ground attack", // start attack on ground - ground
|
||
|
3 EFT_FLY_GROUND_AIR "Fly(ground) - air attack", // start attack in air - ground
|
||
|
4 EFT_FLY_AIR_GROUND "Fly(air) - ground attack", // start attack on ground - air
|
||
|
5 EFT_FLY_AIR_AIR "Fly(air) - air attack", // start attack in air - air
|
||
|
};
|
||
|
|
||
|
|
||
|
class export CEnemyFly : CEnemyBase {
|
||
|
name "Enemy Fly";
|
||
|
thumbnail "";
|
||
|
|
||
|
properties:
|
||
|
1 enum EnemyFlyType m_EeftType "Type" 'T' = EFT_FLY_GROUND_AIR, // fly type
|
||
|
2 BOOL m_bInAir = FALSE, // entity is in air
|
||
|
3 BOOL m_bAirAttack = FALSE, // start air attack
|
||
|
4 BOOL m_bStartInAir = FALSE, // initially in air
|
||
|
|
||
|
// moving/attack properties - CAN BE SET
|
||
|
16 FLOAT m_fGroundToAirSpeed = 2.0f, // ground to air speed
|
||
|
17 FLOAT m_fAirToGroundSpeed = 4.0f, // air to ground speed
|
||
|
18 FLOAT m_fAirToGroundMin = 1.0f, // how long to fly up before attacking
|
||
|
19 FLOAT m_fAirToGroundMax = 2.0f,
|
||
|
27 FLOAT m_fFlyHeight = 2.0f, // fly height above player handle
|
||
|
|
||
|
// these following must be ordered exactly like this for GetProp() to function
|
||
|
10 FLOAT m_fFlyWalkSpeed = 1.0f, // fly walk speed
|
||
|
11 ANGLE m_aFlyWalkRotateSpeed = AngleDeg(10.0f), // fly walk rotate speed
|
||
|
12 FLOAT m_fFlyAttackRunSpeed = 1.0f, // fly attack run speed
|
||
|
13 ANGLE m_aFlyAttackRotateSpeed = AngleDeg(10.0f), // fly attack rotate speed
|
||
|
14 FLOAT m_fFlyCloseRunSpeed = 1.0f, // fly close run speed
|
||
|
15 ANGLE m_aFlyCloseRotateSpeed = AngleDeg(10.0f), // fly close rotate speed
|
||
|
20 FLOAT m_fFlyAttackDistance = 50.0f, // fly attack distance mode
|
||
|
21 FLOAT m_fFlyCloseDistance = 10.0f, // fly close distance mode
|
||
|
22 FLOAT m_fFlyAttackFireTime = 2.0f, // fly attack distance fire time
|
||
|
23 FLOAT m_fFlyCloseFireTime = 1.0f, // fly close distance fire time
|
||
|
24 FLOAT m_fFlyStopDistance = 0.0f, // fly stop moving toward enemy if closer than stop distance
|
||
|
25 FLOAT m_fFlyIgnoreRange = 200.0f, // fly cease attack if enemy farther
|
||
|
26 FLOAT m_fFlyLockOnEnemyTime = 0.0f, // fly time needed to fire
|
||
|
|
||
|
// marker variables
|
||
|
100 BOOL m_bFlyToMarker = FALSE,
|
||
|
|
||
|
components:
|
||
|
1 class CLASS_BASE "Classes\\EnemyBase.ecl",
|
||
|
|
||
|
functions:
|
||
|
// overridable function for access to different properties of derived classes (flying/diving)
|
||
|
virtual FLOAT &GetProp(FLOAT &m_fBase)
|
||
|
{
|
||
|
if (m_bInAir) {
|
||
|
return *((&m_fBase)+(&m_fFlyWalkSpeed-&m_fWalkSpeed));
|
||
|
} else {
|
||
|
return m_fBase;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// get position you would like to go to when following player
|
||
|
virtual FLOAT3D PlayerDestinationPos(void)
|
||
|
{
|
||
|
// if not in air
|
||
|
if (!m_bInAir) {
|
||
|
// use base class
|
||
|
return CEnemyBase::PlayerDestinationPos();
|
||
|
}
|
||
|
|
||
|
// get distance to player
|
||
|
FLOAT fDist = CalcDist(m_penEnemy);
|
||
|
// determine height above from the distance
|
||
|
FLOAT fHeight;
|
||
|
// if in close attack range
|
||
|
if (fDist<=m_fFlyCloseDistance) {
|
||
|
// go to fixed height above player
|
||
|
fHeight = m_fFlyHeight;
|
||
|
// if in further
|
||
|
} else {
|
||
|
// fly more above if further
|
||
|
fHeight = m_fFlyHeight + fDist/5.0f;
|
||
|
}
|
||
|
|
||
|
// calculate the position from the height
|
||
|
return
|
||
|
m_penEnemy->GetPlacement().pl_PositionVector
|
||
|
+ FLOAT3D(m_penEnemy->en_mRotation(1, 2), m_penEnemy->en_mRotation(2, 2), m_penEnemy->en_mRotation(3, 2))
|
||
|
* fHeight;
|
||
|
}
|
||
|
|
||
|
// flying enemies never use pathfinding
|
||
|
void StartPathFinding(void)
|
||
|
{
|
||
|
if (m_bInAir) {
|
||
|
m_dtDestination=DT_PLAYERSPOTTED;
|
||
|
m_vPlayerSpotted = PlayerDestinationPos();
|
||
|
} else {
|
||
|
CEnemyBase::StartPathFinding();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
virtual void AdjustDifficulty(void)
|
||
|
{
|
||
|
FLOAT fMoveSpeed = GetSP()->sp_fEnemyMovementSpeed;
|
||
|
FLOAT fAttackSpeed = GetSP()->sp_fEnemyMovementSpeed;
|
||
|
|
||
|
m_fFlyAttackFireTime *= 1/fAttackSpeed;
|
||
|
m_fFlyCloseFireTime *= 1/fAttackSpeed;
|
||
|
m_fFlyLockOnEnemyTime *= 1/fAttackSpeed;
|
||
|
// m_fFlyWalkSpeed *= fMoveSpeed;
|
||
|
// m_aFlyWalkRotateSpeed *= fMoveSpeed;
|
||
|
m_fFlyAttackRunSpeed *= fMoveSpeed;
|
||
|
m_aFlyAttackRotateSpeed *= fMoveSpeed;
|
||
|
m_fFlyCloseRunSpeed *= fMoveSpeed;
|
||
|
m_aFlyCloseRotateSpeed *= fMoveSpeed;
|
||
|
m_fGroundToAirSpeed *= fMoveSpeed;
|
||
|
m_fAirToGroundSpeed *= fMoveSpeed;
|
||
|
|
||
|
CEnemyBase::AdjustDifficulty();
|
||
|
}
|
||
|
|
||
|
// close attack if possible
|
||
|
virtual BOOL CanHitEnemy(CEntity *penTarget, FLOAT fCosAngle) {
|
||
|
if (IsInPlaneFrustum(penTarget, fCosAngle)) {
|
||
|
return IsVisibleCheckAll(penTarget);
|
||
|
}
|
||
|
return FALSE;
|
||
|
};
|
||
|
/************************************************************
|
||
|
* MOVING FUNCTIONS *
|
||
|
************************************************************/
|
||
|
|
||
|
// set desired rotation and translation to go/orient towards desired position
|
||
|
// and get the resulting movement type
|
||
|
virtual ULONG SetDesiredMovement(void)
|
||
|
{
|
||
|
// if not in air
|
||
|
if (!m_bInAir) {
|
||
|
// use base class
|
||
|
return CEnemyBase::SetDesiredMovement();
|
||
|
}
|
||
|
|
||
|
// get base rotation from base class
|
||
|
ULONG ulFlags = CEnemyBase::SetDesiredMovement();
|
||
|
|
||
|
// if we may move
|
||
|
if (m_fMoveSpeed>0.0f) {
|
||
|
// fix translation for 3d movement
|
||
|
FLOAT3D vTranslation = (m_vDesiredPosition - GetPlacement().pl_PositionVector) * !en_mRotation;
|
||
|
vTranslation(1) = 0.0f;
|
||
|
if (vTranslation(3)>0) {
|
||
|
vTranslation(3) = 0.0f;
|
||
|
}
|
||
|
vTranslation.Normalize();
|
||
|
vTranslation *= m_fMoveSpeed;
|
||
|
SetDesiredTranslation(vTranslation);
|
||
|
}
|
||
|
|
||
|
return ulFlags;
|
||
|
}
|
||
|
|
||
|
/************************************************************
|
||
|
* CLASS SUPPORT FUNCTIONS *
|
||
|
************************************************************/
|
||
|
// set entity position
|
||
|
void SetEntityPosition() {
|
||
|
switch (m_EeftType) {
|
||
|
case EFT_GROUND_ONLY: // ground only enemy
|
||
|
case EFT_FLY_GROUND_GROUND: // fly, on ground, start attack on ground
|
||
|
m_bAirAttack = FALSE;
|
||
|
m_bStartInAir = m_bInAir = FALSE;
|
||
|
m_bFlyToMarker = FALSE;
|
||
|
SetPhysicsFlags((GetPhysicsFlags() & ~EPF_MODEL_FLYING) | EPF_MODEL_WALKING);
|
||
|
ChangeCollisionToGround();
|
||
|
break;
|
||
|
|
||
|
case EFT_FLY_GROUND_AIR: // fly, on ground, start attack in air
|
||
|
m_bAirAttack = TRUE;
|
||
|
m_bStartInAir = m_bInAir = FALSE;
|
||
|
m_bFlyToMarker = FALSE;
|
||
|
SetPhysicsFlags((GetPhysicsFlags() & ~EPF_MODEL_FLYING) | EPF_MODEL_WALKING);
|
||
|
ChangeCollisionToGround();
|
||
|
break;
|
||
|
|
||
|
case EFT_FLY_AIR_GROUND: // fly, in air, start attack on ground
|
||
|
m_bAirAttack = FALSE;
|
||
|
m_bStartInAir = m_bInAir = TRUE;
|
||
|
m_bFlyToMarker = TRUE;
|
||
|
SetPhysicsFlags((GetPhysicsFlags() & ~EPF_MODEL_WALKING) | EPF_MODEL_FLYING);
|
||
|
ChangeCollisionToAir();
|
||
|
break;
|
||
|
|
||
|
case EFT_FLY_ONLY: // air only enemy
|
||
|
case EFT_FLY_AIR_AIR: // fly, in air, start attack in air
|
||
|
m_bAirAttack = TRUE;
|
||
|
m_bStartInAir = m_bInAir = TRUE;
|
||
|
m_bFlyToMarker = TRUE;
|
||
|
SetPhysicsFlags((GetPhysicsFlags() & ~EPF_MODEL_WALKING) | EPF_MODEL_FLYING);
|
||
|
ChangeCollisionToAir();
|
||
|
break;
|
||
|
}
|
||
|
StandingAnim();
|
||
|
};
|
||
|
|
||
|
|
||
|
/************************************************************
|
||
|
* VIRTUAL FUNCTIONS THAT NEED OVERRIDE *
|
||
|
************************************************************/
|
||
|
virtual FLOAT AirToGroundAnim(void) { return _pTimer->TickQuantum; }
|
||
|
virtual FLOAT GroundToAirAnim(void) { return _pTimer->TickQuantum; }
|
||
|
virtual void ChangeCollisionToAir(void) {}
|
||
|
virtual void ChangeCollisionToGround(void) {}
|
||
|
|
||
|
|
||
|
|
||
|
procedures:
|
||
|
/************************************************************
|
||
|
* PROCEDURES WHEN NO ANY SPECIAL ACTION *
|
||
|
************************************************************/
|
||
|
/*
|
||
|
#### !!!!
|
||
|
MoveToDestinationFlying(EVoid) {
|
||
|
m_fMoveFrequency = 0.25f;
|
||
|
m_fMoveTime = _pTimer->CurrentTick() + 45.0f;
|
||
|
while ((m_vDesiredPosition-GetPlacement().pl_PositionVector).Length()>m_fMoveSpeed*m_fMoveFrequency*2.0f &&
|
||
|
m_fMoveTime>_pTimer->CurrentTick()) {
|
||
|
wait (m_fMoveFrequency) {
|
||
|
on (EBegin) : { FlyToPosition(); }
|
||
|
on (ETimer) : { stop; }
|
||
|
}
|
||
|
}
|
||
|
return EReturn();
|
||
|
}
|
||
|
// Move to destination
|
||
|
MoveToDestination(EVoid) : CEnemyBase::MoveToDestination {
|
||
|
if (m_bFlyToMarker && !m_bInAir) {
|
||
|
autocall GroundToAir() EReturn;
|
||
|
} else if (!m_bFlyToMarker && m_bInAir) {
|
||
|
autocall AirToGround() EReturn;
|
||
|
}
|
||
|
|
||
|
// animation
|
||
|
if (m_bRunToMarker) {
|
||
|
RunningAnim();
|
||
|
} else {
|
||
|
WalkingAnim();
|
||
|
}
|
||
|
// fly to position
|
||
|
if (m_bInAir) {
|
||
|
jump MoveToDestinationFlying();
|
||
|
// move to position
|
||
|
} else {
|
||
|
jump CEnemyBase::MoveToDestination();
|
||
|
}
|
||
|
};*/
|
||
|
|
||
|
// return to start position
|
||
|
ReturnToStartPosition(EVoid) : CEnemyBase::ReturnToStartPosition
|
||
|
{
|
||
|
jump CEnemyBase::BeIdle();
|
||
|
/* // if on ground, but can fly
|
||
|
if (!m_bInAir && m_EeftType!=EFT_GROUND_ONLY) {
|
||
|
// fly up
|
||
|
autocall GroundToAir() EReturn;
|
||
|
}
|
||
|
|
||
|
GetWatcher()->StartPlayers(); // start watching
|
||
|
m_vDesiredPosition = m_vStartPosition-en_vGravityDir*2.0f;
|
||
|
m_vStartDirection = (GetPlacement().pl_PositionVector-m_vStartPosition).Normalize();
|
||
|
m_fMoveSpeed = m_fAttackRunSpeed;
|
||
|
m_aRotateSpeed = m_aAttackRotateSpeed;
|
||
|
RunningAnim();
|
||
|
autocall MoveToDestinationFlying() EReturn;
|
||
|
|
||
|
WalkingAnim();
|
||
|
m_vDesiredAngle = m_vStartDirection;
|
||
|
StopTranslating();
|
||
|
|
||
|
autocall CEnemyBase::RotateToStartDirection() EReturn;
|
||
|
|
||
|
// if should be on ground
|
||
|
if (m_bInAir && !m_bStartInAir) {
|
||
|
// fly down
|
||
|
autocall AirToGround() EReturn;
|
||
|
}
|
||
|
StopMoving();
|
||
|
StandingAnim();
|
||
|
|
||
|
jump CEnemyBase::BeIdle();*/
|
||
|
};
|
||
|
|
||
|
/************************************************************
|
||
|
* PROCEDURES WHEN HARMED *
|
||
|
************************************************************/
|
||
|
// Play wound animation and falling body part
|
||
|
BeWounded(EDamage eDamage) : CEnemyBase::BeWounded {
|
||
|
// land on ground
|
||
|
if (!(m_EeftType!=EFT_FLY_ONLY && m_bInAir && ((IRnd()&3)==0))) {
|
||
|
jump CEnemyBase::BeWounded(eDamage);
|
||
|
} else if (TRUE) {
|
||
|
m_bAirAttack = FALSE;
|
||
|
autocall AirToGround() EReturn;
|
||
|
}
|
||
|
return EReturn();
|
||
|
};
|
||
|
|
||
|
|
||
|
|
||
|
/************************************************************
|
||
|
* AIR - GROUND PROCEDURES *
|
||
|
************************************************************/
|
||
|
// air to ground
|
||
|
AirToGround(EVoid)
|
||
|
{
|
||
|
// land on brush
|
||
|
SetDesiredTranslation(FLOAT3D(0, -m_fAirToGroundSpeed, 0));
|
||
|
SetDesiredRotation(ANGLE3D(0, 0, 0));
|
||
|
WalkingAnim();
|
||
|
wait() {
|
||
|
on (EBegin) : { resume; }
|
||
|
// on brush stop
|
||
|
on (ETouch etouch) : {
|
||
|
if (etouch.penOther->GetRenderType() & RT_BRUSH) {
|
||
|
SetDesiredTranslation(FLOAT3D(0, 0, 0));
|
||
|
stop;
|
||
|
}
|
||
|
resume;
|
||
|
}
|
||
|
on (EDeath) : { pass; }
|
||
|
otherwise() : { resume; }
|
||
|
}
|
||
|
// on ground
|
||
|
SetPhysicsFlags((GetPhysicsFlags() & ~EPF_MODEL_FLYING) | EPF_MODEL_WALKING);
|
||
|
m_bInAir = FALSE;
|
||
|
ChangeCollisionToGround();
|
||
|
// animation
|
||
|
wait(AirToGroundAnim()) {
|
||
|
on (EBegin) : { resume; }
|
||
|
on (ETimer) : { stop; }
|
||
|
on (EDeath) : { pass; }
|
||
|
otherwise() : { resume; }
|
||
|
}
|
||
|
return EReturn();
|
||
|
};
|
||
|
|
||
|
// ground to air
|
||
|
GroundToAir(EVoid)
|
||
|
{
|
||
|
// fly in air
|
||
|
SetPhysicsFlags((GetPhysicsFlags() & ~EPF_MODEL_WALKING) | EPF_MODEL_FLYING);
|
||
|
m_bInAir = TRUE;
|
||
|
SetDesiredTranslation(FLOAT3D(0, m_fGroundToAirSpeed, 0));
|
||
|
SetDesiredRotation(ANGLE3D(0, 0, 0));
|
||
|
ChangeCollisionToAir();
|
||
|
// animation
|
||
|
wait(GroundToAirAnim()) {
|
||
|
on (EBegin) : { resume; }
|
||
|
on (EDeath) : { pass; }
|
||
|
on (ETimer) : { stop; }
|
||
|
otherwise() : { resume; }
|
||
|
}
|
||
|
// move in air further
|
||
|
WalkingAnim();
|
||
|
wait(Lerp(m_fAirToGroundMin, m_fAirToGroundMax, FRnd())) {
|
||
|
on (EBegin) : { resume; }
|
||
|
on (EDeath) : { pass; }
|
||
|
on (ETimer) : { stop; }
|
||
|
otherwise() : { resume; }
|
||
|
}
|
||
|
SetDesiredTranslation(FLOAT3D(0, 0, 0));
|
||
|
return EReturn();
|
||
|
};
|
||
|
|
||
|
|
||
|
|
||
|
/************************************************************
|
||
|
* ATTACK ENEMY PROCEDURES *
|
||
|
************************************************************/
|
||
|
// initialize attack is overridden to switch fly/walk modes if needed
|
||
|
AttackEnemy() : CEnemyBase::AttackEnemy
|
||
|
{
|
||
|
// air attack
|
||
|
if (m_bAirAttack) {
|
||
|
// ground to air
|
||
|
if (!m_bInAir) {
|
||
|
autocall GroundToAir() EReturn;
|
||
|
}
|
||
|
// ground attack
|
||
|
} else if (TRUE) {
|
||
|
// air to ground
|
||
|
if (m_bInAir) {
|
||
|
autocall AirToGround() EReturn;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
jump CEnemyBase::AttackEnemy();
|
||
|
}
|
||
|
|
||
|
// this is called to hit the player when near
|
||
|
Hit(EVoid) : CEnemyBase::Hit
|
||
|
{
|
||
|
if (m_bInAir) {
|
||
|
jump FlyHit();
|
||
|
} else {
|
||
|
jump GroundHit();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// this is called to shoot at player when far away or otherwise unreachable
|
||
|
Fire(EVoid) : CEnemyBase::Fire
|
||
|
{
|
||
|
if (m_bInAir) {
|
||
|
jump FlyFire();
|
||
|
} else {
|
||
|
jump GroundFire();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/************************************************************
|
||
|
* D E A T H *
|
||
|
************************************************************/
|
||
|
Death(EVoid) : CEnemyBase::Death {
|
||
|
// clear fly flag
|
||
|
SetPhysicsFlags((GetPhysicsFlags() & ~EPF_MODEL_FLYING) | EPF_MODEL_WALKING);
|
||
|
ChangeCollisionToGround();
|
||
|
jump CEnemyBase::Death();
|
||
|
};
|
||
|
|
||
|
|
||
|
|
||
|
/************************************************************
|
||
|
* M A I N L O O P *
|
||
|
************************************************************/
|
||
|
// main loop
|
||
|
MainLoop(EVoid) : CEnemyBase::MainLoop {
|
||
|
SetEntityPosition();
|
||
|
jump CEnemyBase::MainLoop();
|
||
|
};
|
||
|
|
||
|
// dummy main
|
||
|
Main(EVoid) {
|
||
|
return;
|
||
|
};
|
||
|
|
||
|
/************************************************************
|
||
|
* VIRTUAL PROCEDURES THAT NEED OVERRIDE *
|
||
|
************************************************************/
|
||
|
// this is called to hit the player when near and you are on ground
|
||
|
GroundHit(EVoid)
|
||
|
{
|
||
|
return EReturn();
|
||
|
}
|
||
|
// this is called to shoot at player when far away or otherwise unreachable and you are on ground
|
||
|
GroundFire(EVoid)
|
||
|
{
|
||
|
return EReturn();
|
||
|
}
|
||
|
|
||
|
// this is called to hit the player when near and you are in air
|
||
|
FlyHit(EVoid)
|
||
|
{
|
||
|
return EReturn();
|
||
|
}
|
||
|
|
||
|
// this is called to shoot at player when far away or otherwise unreachable and you are in air
|
||
|
FlyFire(EVoid)
|
||
|
{
|
||
|
return EReturn();
|
||
|
}
|
||
|
};
|