609
%{
#include "StdH.h"
#include "Models/CutSequences/SpaceShip/SpaceShip.h"
#include "Models/CutSequences/SpaceShip/Door.h"
#include "Models/CutSequences/SpaceShip/LightBeam.h"
#include "Entities/Effector.h"
#include "Entities/Light.h"
%}

uses "Entities/PyramidSpaceShipMarker";

enum PSSState {
  0 PSSS_IDLE                  "Idle",                      // idle
  1 PSSS_MOVING                "Moving",                    // process of moving trough markers
  2 PSSS_REACHED_DESTINATION   "Reached destination",       // process of turning on
  3 PSSS_KILLING_BEAM_FIREING  "Killing beam fireing",      // killing beam fireing
  4 PSSS_BEAM_DEACTIVATED      "Killing beam deactivated",  // killing beam gas been deactivated, wait to pick up Sam
  5 PSSS_DOORS_CLOSED          "Doors closed",              // doors closed
};

event EForcePathMarker {
  CEntityPointer penForcedPathMarker,
};

%{
#define STRETCH_X (200.0f*m_fStretch)
#define STRETCH_Y (100.0f*m_fStretch)
#define STRETCH_Z (200.0f*m_fStretch)
#define PSS_STRETCH (FLOAT3D(STRETCH_X, STRETCH_Y, STRETCH_Z)*m_fStretch)
#define SND_FALLOFF 1000.0f
#define SND_HOTSPOT 250.0f
#define SND_VOLUME 2.0f

#define BIG_LIGHT_BEAM_LIFE_TIME (8.0f)
#define SMALL_FLARE_WAIT 2.0f
#define SMALL_FLARES_LIFE_TIME (BIG_LIGHT_BEAM_LIFE_TIME+SMALL_FLARE_WAIT)
#define SMALL_LIGHTNING_WAIT 1.5f
#define SMALL_LIGHTININGS_LIFE_TIME (SMALL_FLARES_LIFE_TIME+SMALL_LIGHTNING_WAIT)
#define BIG_FLARE_WAIT 1.0f
#define BIG_FLARE_LIFE_TIME (SMALL_LIGHTININGS_LIFE_TIME+BIG_FLARE_WAIT)

#define BM_DX (0.414657f*STRETCH_X)
#define BM_DY (-1.72731f*STRETCH_Y)
#define BM_DZ (0.414657f*STRETCH_Z)
#define BM_FLARE_DY (-0.25f*STRETCH_Y)

#define BM_MASTER_Y (-1.76648f*STRETCH_Y)
%}

class CPyramidSpaceShip: CMovableModelEntity {
name      "PyramidSpaceShip";
thumbnail "Thumbnails\\PyramidSpaceShip.tbn";
features  "HasName", "IsTargetable", "IsImportant";
properties:
  1 CTString m_strName            "Name" 'N' = "Pyramid Space Ship",  // name
  3 FLOAT m_fMovingSpeed = 0.0f,                                      // current speed
  5 CEntityPointer m_penBeamHit "Beam hit target marker" 'T',         // target point for light beam
  6 CEntityPointer m_penLightBeam "Beam model holder" 'B',            // light beam model holder
  7 FLOAT m_tmBeamTime =-1.0f,                                        // light beam time var
  8 CEntityPointer m_penHitPlaceFlare "Hit place flare" 'H',          // hit place model holder
  9 FLOAT m_tmHitFlareTime =-1.0f,                                    // light beam hit flare time var
 10 FLOAT m_iRingCounter = 0,                                         // ring counter
 11 FLOAT m_fRatio =0.0f,                                             // misc ratio
 12 CTString m_strDescription = "",                                   // description
 13 enum PSSState m_epssState = PSSS_IDLE,                            // current state
 14 FLOAT m_fStretch "Stretch" 'S' = 1.0f,                            // stretch factor
 // path properties
 20 BOOL m_bStopMoving = FALSE,                                       // stop moving on next marker
 21 CEntityPointer m_penTarget "Target" 'T' COLOR(C_lBLUE|0xFF),      // next path target
 29 CEntityPointer m_penFlyAwayTarget "Fly away path marker" COLOR(C_lBLUE|0xFF), // fly away path marker
 22 CEntityPointer m_penLast,                                         // previous marker
 23 BOOL m_bMoving = FALSE,                                           // set while moving
 24 FLOAT m_fRot = 0.0f,                                              // current rotation
 25 FLOAT m_fLastRotSpeed = 0.0f,                                     // last speed rotation
 26 FLOAT m_fRotSpeed = 0.0f,                                         // current speed rotation
 27 BOOL m_bApplyDamageToHitted = TRUE,                               // if damage should be applied
 28 FLOAT m_tmTemp = 0.0f,                                            // temporary time var

 30 FLOAT m_tmAtMarker = 0.0f,                                        // time when current marker was reached
 31 FLOAT m_tmDelta = 0.0f,                                           // time to reach next marker
 32 FLOAT3D m_vPNp0 = FLOAT3D(0,0,0),
 33 FLOAT3D m_vPNp1 = FLOAT3D(0,0,0),
 34 FLOAT3D m_vTNp0 = FLOAT3D(0,0,0),
 35 FLOAT3D m_vTNp1 = FLOAT3D(0,0,0),
 36 FLOATquat3D m_qPNp0 = FLOATquat3D(0,0,0,0),
 37 FLOATquat3D m_qPNp1 = FLOATquat3D(0,0,0,0),
 38 FLOATquat3D m_qANp0 = FLOATquat3D(0,0,0,0),
 39 FLOATquat3D m_qANp1 = FLOATquat3D(0,0,0,0),
 40 FLOAT m_fRotSpeedp0 = 0.0f,
 41 FLOAT m_fRotSpeedp1 = 0.0f,
 42 FLOAT m_fTRotSpeedp0 = 0.0f,
 43 FLOAT m_fTRotSpeedp1 = 0.0f,

 50 CSoundObject m_soPlates,
 51 CSoundObject m_soBeamMachine,
 52 CSoundObject m_soBeam,
 53 CSoundObject m_soFlaresFX,
 54 BOOL m_bFireingDeactivatedBeam=FALSE,

components:
  1 model   MODEL_SPACESHIP     "Models\\CutSequences\\SpaceShip\\SpaceShip.mdl",
  2 model   MODEL_BODY          "Models\\CutSequences\\SpaceShip\\Body.mdl",
  3 texture TEXTURE_BODY        "Models\\CutSequences\\SpaceShip\\Body.tex",
  4 model   MODEL_DOOR          "Models\\CutSequences\\SpaceShip\\Door.mdl",
  5 texture TEXTURE_DOOR        "Models\\CutSequences\\SpaceShip\\Door.tex",
  6 model   MODEL_BEAMMACHINE   "Models\\CutSequences\\SpaceShip\\BeamMachine.mdl",
  7 texture TEXTURE_BEAMMACHINE "Models\\CutSequences\\SpaceShip\\BeamMachine.tex",
  8 model   MODEL_BEAMRIM       "Models\\CutSequences\\SpaceShip\\BeamMachineRim.mdl",
  9 texture TEXTURE_BEAMRIM     "Models\\CutSequences\\SpaceShip\\BeamMachineRim.tex",
 10 class   CLASS_EFFECTOR      "Classes\\Effector.ecl",
 11 model   MODEL_SHIP_INSIDE   "Models\\CutSequences\\SpaceShip\\Fillin.mdl",
 20 sound   SOUND_PLATES        "Sounds\\CutSequences\\SpaceShip\\SSPlates.wav",
 21 sound   SOUND_BEAMMACHINE   "Sounds\\CutSequences\\SpaceShip\\SSProbe.wav",
 22 sound   SOUND_BEAM          "Sounds\\CutSequences\\SpaceShip\\LaserBeam.wav",
 23 sound   SOUND_WARMUP        "Sounds\\CutSequences\\SpaceShip\\Warmup.wav",

functions:
  void Precache(void) {
    PrecacheModel   (MODEL_SPACESHIP     );
    PrecacheModel   (MODEL_BODY          );
    PrecacheTexture (TEXTURE_BODY        );
    PrecacheModel   (MODEL_DOOR          );
    PrecacheTexture (TEXTURE_DOOR        );
    PrecacheModel   (MODEL_BEAMMACHINE   );
    PrecacheTexture (TEXTURE_BEAMMACHINE );
    PrecacheModel   (MODEL_BEAMRIM       );
    PrecacheModel   (MODEL_SHIP_INSIDE   );
    PrecacheTexture (TEXTURE_BEAMRIM     );
    PrecacheClass   (CLASS_EFFECTOR, ET_SIZING_RING_FLARE);
    PrecacheClass   (CLASS_EFFECTOR, ET_SIZING_BIG_BLUE_FLARE);
    PrecacheClass   (CLASS_EFFECTOR, ET_LIGHTNING);
    PrecacheClass   (CLASS_EFFECTOR, ET_MOVING_RING);
    PrecacheSound   (SOUND_PLATES        );
    PrecacheSound   (SOUND_BEAMMACHINE   );
    PrecacheSound   (SOUND_BEAM          );
    PrecacheSound   (SOUND_WARMUP        );
  }

  BOOL IsTargetValid(SLONG slPropertyOffset, CEntity *penTarget)
  {
    if(penTarget==NULL)
    {
      return FALSE;
    }
    if( slPropertyOffset==offsetof(CPyramidSpaceShip, m_penTarget) ||
        slPropertyOffset==offsetof(CPyramidSpaceShip, m_penFlyAwayTarget))
    {
      return( IsDerivedFromClass(penTarget, "Pyramid Space Ship Marker"));
    }
    return TRUE;
  }

  // Check if entity is moved on a route set up by its targets
  BOOL MovesByTargetedRoute( CTString &strTargetProperty) const
  {
    strTargetProperty = "Target";
    return TRUE;
  }

  /* Check if entity can drop marker for making linked route. */
  BOOL DropsMarker( CTFileName &fnmMarkerClass, CTString &strTargetProperty) const
  {
    fnmMarkerClass = CTFILENAME("Classes\\PyramidSpaceShipMarker.ecl");
    strTargetProperty = "Target";
    return TRUE;
  }

  // returns description
  const CTString &GetDescription(void) const
  {
    if (m_penTarget!=NULL) {
      ((CTString&)m_strDescription).PrintF("->%s", m_penTarget->GetName());
    } else {
      ((CTString&)m_strDescription).PrintF("-><none>");
    }
    return m_strDescription;
  }


  CPlacement3D GetLerpedPlacement(void) const
  {
    return CMovableEntity::GetLerpedPlacement();
  }

  void PreMoving()
  {
    // remember old placement for lerping
    en_plLastPlacement = en_plPlacement;  
  }

  void HideBeamMachine(void)
  {
    if(GetModelObject()->GetAttachmentModel( SPACESHIP_ATTACHMENT_BEAM_RIM) != NULL)
    {
      RemoveAttachment(SPACESHIP_ATTACHMENT_BEAM_RIM);
    }
    if(GetModelObject()->GetAttachmentModel( SPACESHIP_ATTACHMENT_BEAM) != NULL)
    {
      RemoveAttachment(SPACESHIP_ATTACHMENT_BEAM);
    }
  }

  void ShowBeamMachine(void)
  {
    AddAttachment(SPACESHIP_ATTACHMENT_BEAM_RIM, MODEL_BEAMRIM, TEXTURE_BEAMRIM);
    AddAttachment(SPACESHIP_ATTACHMENT_BEAM, MODEL_BEAMMACHINE, TEXTURE_BEAMMACHINE);
    GetModelObject()->GetAttachmentModel( SPACESHIP_ATTACHMENT_BEAM_RIM)->amo_moModelObject.StretchModel(PSS_STRETCH);
    GetModelObject()->GetAttachmentModel( SPACESHIP_ATTACHMENT_BEAM)->amo_moModelObject.StretchModel(PSS_STRETCH);
  }

  void InitializePathMoving( CPyramidSpaceShipMarker *penStartMarker)
  {
    // set as current
    m_penTarget = penStartMarker;

    m_epssState = PSSS_MOVING;
    // check all markers for correct type and numbers
    INDEX ctMarkers=1;
    CPyramidSpaceShipMarker *pcm0 = (CPyramidSpaceShipMarker*)&*m_penTarget;
    if( pcm0 == NULL)
    {
      return;
    }
    CPyramidSpaceShipMarker *pcm  = (CPyramidSpaceShipMarker*)&*pcm0->m_penTarget;
    // loop thru markers
    while( pcm!=NULL && pcm->m_penTarget!=pcm0)
    {
      pcm = (CPyramidSpaceShipMarker*)&*pcm->m_penTarget;
      if (pcm==NULL) {
        WarningMessage( "Space ship path - broken link!");
        return;
      }
      ctMarkers++;
      if (ctMarkers>500) {
        WarningMessage( "Space ship path - invalid marker loop!");
        return;
      }
    }
    // check if we have enough markers to do smooth interpolation
    if( ctMarkers<2) {
      WarningMessage( "Space ship path requires at least 2 markers in order to work!");
      return;
    }

    // prepare internal variables
    FLOAT tmCurrent = _pTimer->CurrentTick();
    m_tmAtMarker = tmCurrent;
    m_tmDelta = 0.0f;
    m_bStopMoving = FALSE;
    m_penLast = pcm; // keep last marker
    ASSERT( pcm->m_penTarget == m_penTarget);
    m_bMoving = TRUE;
    AddToMovers();
  }

  void DoMoving()  
  {
    if (!m_bMoving) {
      return;
    }
    // read current tick
    FLOAT tmCurrent = _pTimer->CurrentTick();
    // lerping is initially enabled
    BOOL bLerping = TRUE;

    // if we hit a marker
    if( tmCurrent > (m_tmAtMarker+m_tmDelta - _pTimer->TickQuantum*3/2)) 
    {
      // get markers
      CPyramidSpaceShipMarker *pcmNm1 = &(CPyramidSpaceShipMarker&)*m_penLast;
      CPyramidSpaceShipMarker *pcmNp0 = &(CPyramidSpaceShipMarker&)*m_penTarget;
      CPyramidSpaceShipMarker *pcmNp1 = &(CPyramidSpaceShipMarker&)*pcmNp0->m_penTarget;
      CPyramidSpaceShipMarker *pcmNp2 = &(CPyramidSpaceShipMarker&)*pcmNp1->m_penTarget;

      // if there is a trigger at the hit marker
      if (pcmNp0->m_penTrigger!=NULL) {
        // trigger it
        SendToTarget(pcmNp0->m_penTrigger, EET_TRIGGER, NULL);
      }

      // update markers for next interval
      m_penTarget = pcmNp1;
      m_penLast   = pcmNp0;

      // get markers
      CPyramidSpaceShipMarker &cmNm1 = *pcmNm1;
      CPyramidSpaceShipMarker &cmNp0 = *pcmNp0;
      CPyramidSpaceShipMarker &cmNp1 = *pcmNp1;
      CPyramidSpaceShipMarker &cmNp2 = *pcmNp2;

      // get positions from four markers
      const FLOAT3D &vPNm1 = cmNm1.GetPlacement().pl_PositionVector;
      const FLOAT3D &vPNp0 = cmNp0.GetPlacement().pl_PositionVector;
      const FLOAT3D &vPNp1 = cmNp1.GetPlacement().pl_PositionVector;
      const FLOAT3D &vPNp2 = cmNp2.GetPlacement().pl_PositionVector;
      ANGLE3D aPNm1 = cmNm1.GetPlacement().pl_OrientationAngle;
      ANGLE3D aPNp0 = cmNp0.GetPlacement().pl_OrientationAngle;
      ANGLE3D aPNp1 = cmNp1.GetPlacement().pl_OrientationAngle;
      ANGLE3D aPNp2 = cmNp2.GetPlacement().pl_OrientationAngle;
      FLOAT fRotSpeedm1 = cmNm1.m_fRotSpeed;
      FLOAT fRotSpeedp0 = cmNp0.m_fRotSpeed;
      FLOAT fRotSpeedp1 = cmNp1.m_fRotSpeed;
      FLOAT fRotSpeedp2 = cmNp2.m_fRotSpeed;

      // find quaternions for rotations
      FLOATquat3D qPNm1; qPNm1.FromEuler(aPNm1);
      FLOATquat3D qPNp0; qPNp0.FromEuler(aPNp0);
      FLOATquat3D qPNp1; qPNp1.FromEuler(aPNp1);
      FLOATquat3D qPNp2; qPNp2.FromEuler(aPNp2);

      // make all angles between quaternion pairs acute
      if( qPNm1%qPNp0<0 ) {
        qPNp0 = -qPNp0;
      }
      if( qPNp0%qPNp1<0 ) {
        qPNp1 = -qPNp1;
      }
      if( qPNp1%qPNp2<0 ) {
        qPNp2 = -qPNp2;
      }

      // update time and position
      m_tmAtMarker = m_tmAtMarker+m_tmDelta;
      m_tmDelta    = cmNp0.m_fDeltaTime;
      m_vPNp0 = vPNp0;
      m_vPNp1 = vPNp1;
      m_fRotSpeedp0 = fRotSpeedp0;
      m_fRotSpeedp1 = fRotSpeedp1;
      m_qPNp0 = qPNp0;
      m_qPNp1 = qPNp1;

      // determine delta time multipliers
      FLOAT tmDNm1 = cmNm1.m_fDeltaTime;
      FLOAT tmDNp0 = cmNp0.m_fDeltaTime;
      FLOAT tmDNp1 = cmNp1.m_fDeltaTime;
      FLOAT fD0 = 2*tmDNp0 / (tmDNm1+tmDNp0);
      FLOAT fD1 = 2*tmDNp0 / (tmDNp0+tmDNp1);

      // determine biases, tensions and continuities
      FLOAT fBNp0 = cmNp0.m_fBias;
      FLOAT fTNp0 = cmNp0.m_fTension;
      FLOAT fCNp0 = cmNp0.m_fContinuity;
      FLOAT fBNp1 = cmNp1.m_fBias;
      FLOAT fTNp1 = cmNp1.m_fTension;
      FLOAT fCNp1 = cmNp1.m_fContinuity;

      FLOAT fF00 = (1-fTNp0)*(1-fCNp0)*(1-fBNp0) / 2;
      FLOAT fF01 = (1-fTNp0)*(1+fCNp0)*(1+fBNp0) / 2;
      FLOAT fF10 = (1-fTNp1)*(1+fCNp1)*(1-fBNp1) / 2;
      FLOAT fF11 = (1-fTNp1)*(1-fCNp1)*(1+fBNp1) / 2;

      // find tangents for translation
      m_vTNp0 = ( (vPNp1-vPNp0) * fF00 + (vPNp0-vPNm1) * fF01) * fD0;
      m_vTNp1 = ( (vPNp2-vPNp1) * fF10 + (vPNp1-vPNp0) * fF11) * fD1;

      // find tangents for moving speed
      m_fTRotSpeedp0 = ( (fRotSpeedp1-fRotSpeedp0) * fF00 + (fRotSpeedp0-fRotSpeedm1) * fF01) * fD0;
      m_fTRotSpeedp1 = ( (fRotSpeedp2-fRotSpeedp1) * fF10 + (fRotSpeedp1-fRotSpeedp0) * fF11) * fD1;

      // find tangents for rotation
      FLOATquat3D qTNp0, qTNp1;
      qTNp0 = ( Log(qPNp0.Inv()*qPNp1) * fF00 + Log(qPNm1.Inv()*qPNp0) * fF01) * fD0;
      qTNp1 = ( Log(qPNp1.Inv()*qPNp2) * fF10 + Log(qPNp0.Inv()*qPNp1) * fF11) * fD1;

      // find squad parameters
      m_qANp0 = qPNp0*Exp( (qTNp0 - Log(qPNp0.Inv()*qPNp1))/2 );
      m_qANp1 = qPNp1*Exp( (Log(qPNp0.Inv()*qPNp1) - qTNp1)/2 );

      // check for stop moving
      if( cmNp0.m_bStopMoving && m_fRotSpeed==0.0f) {
        m_bStopMoving = TRUE;
      }
    }

    // calculate the parameter value and hermit basis
    FLOAT fT  = (tmCurrent - m_tmAtMarker) / m_tmDelta;
    FLOAT fH0 =  2*fT*fT*fT - 3*fT*fT + 1;
    FLOAT fH1 = -2*fT*fT*fT + 3*fT*fT;
    FLOAT fH2 =    fT*fT*fT - 2*fT*fT + fT;
    FLOAT fH3 =    fT*fT*fT -   fT*fT;

    // interpolate position, rotation and fov
    FLOAT3D vPos = m_vPNp0*fH0 + m_vPNp1*fH1 + m_vTNp0*fH2 + m_vTNp1*fH3;
    FLOAT fRotSpeed = m_fRotSpeedp0*fH0 + m_fRotSpeedp1*fH1 + m_fTRotSpeedp0*fH2 + m_fTRotSpeedp1*fH3;
    FLOATquat3D qRot = Squad(fT, m_qPNp0, m_qPNp1, m_qANp0, m_qANp1);
    FLOATmatrix3D mRotLocal;
    MakeRotationMatrixFast(mRotLocal, ANGLE3D(m_fRot,0,0));
    FLOATmatrix3D mRot;
    qRot.ToMatrix(mRot);
    mRot = mRotLocal*mRot;

    // just cache near polygons for various engine needs
    en_vNextPosition = vPos;
    en_mNextRotation = mRot;
    CacheNearPolygons();

    // set new placement
    CPlacement3D plNew;
    plNew.pl_PositionVector = vPos;
    DecomposeRotationMatrixNoSnap(plNew.pl_OrientationAngle, mRot);
    SetPlacement_internal(plNew, mRot, TRUE);
    // if lerping is disabled
    if (!bLerping) {
      // make last placement same as this one
      en_plLastPlacement = en_plPlacement;  
    }
    // set new speed
    m_fLastRotSpeed = m_fRotSpeed;
    m_fRotSpeed = fRotSpeed;
    m_fRot += m_fRotSpeed;
  }


  void PostMoving()  
  {
    if (!m_bMoving) {
      return;
    }
    //
    if( m_bStopMoving) {
      m_bMoving = FALSE;
      // mark for removing from list of movers
      en_ulFlags |= ENF_INRENDERING;
      m_epssState = PSSS_REACHED_DESTINATION;
      // remember old placement for lerping
      en_plLastPlacement = en_plPlacement;  
    }
  }

  void SpawnBeamMachineFlares(void)
  {
    // spawn small beam machine flares
    CPlacement3D plSpaceShip = GetPlacement();
    CPlacement3D plFlare1 = CPlacement3D( FLOAT3D(  BM_DX, BM_DY+BM_FLARE_DY,      0), ANGLE3D(0,0,0));
    CPlacement3D plFlare2 = CPlacement3D( FLOAT3D(      0, BM_DY+BM_FLARE_DY, -BM_DZ), ANGLE3D(0,0,0));
    CPlacement3D plFlare3 = CPlacement3D( FLOAT3D( -BM_DX, BM_DY+BM_FLARE_DY,      0), ANGLE3D(0,0,0));
    CPlacement3D plFlare4 = CPlacement3D( FLOAT3D(      0, BM_DY+BM_FLARE_DY,  BM_DZ), ANGLE3D(0,0,0));
    
    plFlare1.RelativeToAbsolute(plSpaceShip);
    plFlare2.RelativeToAbsolute(plSpaceShip);
    plFlare3.RelativeToAbsolute(plSpaceShip);
    plFlare4.RelativeToAbsolute(plSpaceShip);

    CEntity *penFlare1 = CreateEntity( plFlare1, CLASS_EFFECTOR);
    CEntity *penFlare2 = CreateEntity( plFlare2, CLASS_EFFECTOR);
    CEntity *penFlare3 = CreateEntity( plFlare3, CLASS_EFFECTOR);
    CEntity *penFlare4 = CreateEntity( plFlare4, CLASS_EFFECTOR);

    ESpawnEffector eSpawnFlare;
    eSpawnFlare.tmLifeTime = SMALL_FLARES_LIFE_TIME;
    eSpawnFlare.tmLifeTime = 20.0f;
    eSpawnFlare.eetType = ET_SIZING_RING_FLARE;

    penFlare1->Initialize( eSpawnFlare);
    penFlare2->Initialize( eSpawnFlare);
    penFlare3->Initialize( eSpawnFlare);
    penFlare4->Initialize( eSpawnFlare);
  }

  void SpawnBeamMachineMainFlare(void)
  {
    // spawn main flare
    CPlacement3D plSpaceShip = GetPlacement();
    CPlacement3D plFlare = CPlacement3D( FLOAT3D(0, BM_MASTER_Y+BM_FLARE_DY, 0), ANGLE3D(0,0,0));
    plFlare.RelativeToAbsolute(GetPlacement());
    CEntity *penFlare = CreateEntity( plFlare, CLASS_EFFECTOR);
    ESpawnEffector eSpawnFlare;
    eSpawnFlare.tmLifeTime = 20.0f;
    eSpawnFlare.fSize = 1.0f;
    eSpawnFlare.eetType = ET_SIZING_BIG_BLUE_FLARE;
    penFlare->Initialize( eSpawnFlare);
  }

  void ShowBeamMachineHitFlare(void)
  {
    if( m_penHitPlaceFlare!=NULL && IsOfClass(m_penHitPlaceFlare, "ModelHolder2") )
    {
      CModelObject *pmo = m_penHitPlaceFlare->GetModelObject();
      if( pmo != NULL) 
      {
        m_penHitPlaceFlare->SwitchToModel();
      }
    }
  }

  void HideBeamMachineHitFlare(void)
  {
    m_tmHitFlareTime = -1;
    if( m_penHitPlaceFlare!=NULL && IsOfClass(m_penHitPlaceFlare, "ModelHolder2") )
    {
      CModelObject *pmo = m_penHitPlaceFlare->GetModelObject();
      if( pmo != NULL) 
      {
        m_penHitPlaceFlare->SwitchToEditorModel();
        pmo->mo_colBlendColor = C_WHITE|CT_OPAQUE;
      }
    }
  }

  void SpawnBeamMachineLightnings(void)
  {
    // spawn beam lightnings
    CPlacement3D plLightning1 = CPlacement3D( FLOAT3D(BM_DX, BM_DY, 0), ANGLE3D(0,0,0));
    CPlacement3D plLightning2 = CPlacement3D( FLOAT3D(0, BM_DY, -BM_DZ), ANGLE3D(0,0,0));
    CPlacement3D plLightning3 = CPlacement3D( FLOAT3D(-BM_DX, BM_DY, 0), ANGLE3D(0,0,0));
    CPlacement3D plLightning4 = CPlacement3D( FLOAT3D(0, BM_DY, BM_DZ), ANGLE3D(0,0,0));
    
    CPlacement3D plLightningDest = CPlacement3D( FLOAT3D(0, BM_MASTER_Y, 0), ANGLE3D(0,0,0));
    CPlacement3D plSpaceShip = GetPlacement();
    plLightningDest.RelativeToAbsolute(plSpaceShip);
    
    plLightning1.RelativeToAbsolute(plSpaceShip);
    plLightning2.RelativeToAbsolute(plSpaceShip);
    plLightning3.RelativeToAbsolute(plSpaceShip);
    plLightning4.RelativeToAbsolute(plSpaceShip);

    CEntity *penLightning1 = CreateEntity( plLightning1, CLASS_EFFECTOR);
    CEntity *penLightning2 = CreateEntity( plLightning2, CLASS_EFFECTOR);
    CEntity *penLightning3 = CreateEntity( plLightning3, CLASS_EFFECTOR);
    CEntity *penLightning4 = CreateEntity( plLightning4, CLASS_EFFECTOR);

    ESpawnEffector eSpawnLightning;
    eSpawnLightning.eetType = ET_LIGHTNING;
    eSpawnLightning.tmLifeTime = SMALL_LIGHTININGS_LIFE_TIME;
    eSpawnLightning.vDestination = plLightningDest.pl_PositionVector; 
    eSpawnLightning.fSize = 16.0f;
    eSpawnLightning.ctCount = 16;

    penLightning1->Initialize( eSpawnLightning);
    penLightning2->Initialize( eSpawnLightning);
    penLightning3->Initialize( eSpawnLightning);
    penLightning4->Initialize( eSpawnLightning);
  }

  void SpawnBeamMachineMainLightning(void)
  {
    // spawn main lightning
    FLOAT3D vDestination = GetPlacement().pl_PositionVector + FLOAT3D( 0, BM_MASTER_Y, 0);
    CPlacement3D plSource = CPlacement3D( vDestination, ANGLE3D(0,0,0));
    if( m_penBeamHit != NULL)
    {
      plSource.pl_PositionVector = m_penBeamHit->GetPlacement().pl_PositionVector;
      CEntity *penEffector = CreateEntity( plSource, CLASS_EFFECTOR);
      ESpawnEffector eSpawnEffector;
      eSpawnEffector.eetType = ET_LIGHTNING;
      eSpawnEffector.tmLifeTime = BIG_LIGHT_BEAM_LIFE_TIME;
      eSpawnEffector.vDestination = vDestination;
      eSpawnEffector.fSize = 32.0f;
      eSpawnEffector.ctCount = 32;
      penEffector->Initialize( eSpawnEffector);
    }
  }

  void SpawnMovingRing(void)
  {
    if( m_penBeamHit != NULL)
    {
      FLOAT3D vStart = GetPlacement().pl_PositionVector + FLOAT3D( 0, BM_MASTER_Y, 0);
      CPlacement3D plSource = CPlacement3D( vStart, ANGLE3D(0,0,0));
      FLOAT3D vHitPlace = m_penBeamHit->GetPlacement().pl_PositionVector;
      CEntity *penEffector = CreateEntity( plSource, CLASS_EFFECTOR);
      ESpawnEffector eSpawnEffector;
      eSpawnEffector.eetType = ET_MOVING_RING;
      eSpawnEffector.tmLifeTime = BIG_LIGHT_BEAM_LIFE_TIME;
      eSpawnEffector.vDestination = vHitPlace+FLOAT3D(0.0f, 0.0f, 0.0f);
      eSpawnEffector.fSize = 16.0f;
      eSpawnEffector.ctCount = 2;
      penEffector->Initialize( eSpawnEffector);
    }
  }

  void TurnOnLightBeam(void)
  {
    if( m_penLightBeam!=NULL && IsOfClass(m_penLightBeam, "ModelHolder2") )
    {
      CModelObject *pmo = m_penLightBeam->GetModelObject();
      m_penLightBeam->SwitchToModel();
      pmo->mo_colBlendColor = C_WHITE|CT_OPAQUE;
    }
  }

  void TurnOffLightBeam(void)
  {
    m_tmBeamTime=-1.0f;

    if( m_penLightBeam!=NULL && IsOfClass(m_penLightBeam, "ModelHolder2") )
    {
      m_penLightBeam->SwitchToEditorModel();
      CModelObject *pmo = m_penLightBeam->GetModelObject();
    }
  }

  BOOL AdjustShadingParameters(FLOAT3D &vLightDirection, COLOR &colLight, COLOR &colAmbient)
  {
    FLOAT fBeamRatio = 1.0f;
    
    // light beam
    if( m_penLightBeam!=NULL && IsOfClass(m_penLightBeam, "ModelHolder2") )
    {
      CModelObject *pmo = m_penLightBeam->GetModelObject();
      if( pmo != NULL) 
      {
        if( m_tmBeamTime>-1.0f)
        {
          FLOAT fT = _pTimer->CurrentTick()-m_tmBeamTime;
          fBeamRatio = 1.0f-ClampUp(fT/2.0f, 1.0f);
          UBYTE ub = UBYTE (255.0f*fBeamRatio);
          COLOR col = RGBAToColor(ub,ub,ub,ub);
          pmo->mo_colBlendColor = col;
        }
      }
    }

    // hit flare
    if( m_penHitPlaceFlare!=NULL && IsOfClass(m_penHitPlaceFlare, "ModelHolder2") )
    {
      CModelObject *pmo = m_penHitPlaceFlare->GetModelObject();
      if( pmo != NULL) 
      {
        if( m_tmHitFlareTime>-1.0f)
        {
          FLOAT fT = _pTimer->CurrentTick()-m_tmHitFlareTime;
          FLOAT fRatio = (Sin(fT*2000)*0.5f+0.5f)*(Sin(fT*1333)*0.5f+0.5f);
          /*if(fRatio>0.5f)
          {
            fRatio=0.0f;
          }
          else
          {
            fRatio=1.0f;
          }*/

          UBYTE ub = UBYTE((200+55*fRatio)*fBeamRatio);
          //ub = 255;
          COLOR col = RGBAToColor(ub,ub,ub,ub);
          pmo->mo_colBlendColor = col;
        }
      }
    }
    return FALSE;
  };

procedures:

  OpenDoors()
  {
    // if ship inside not yet added
    if( GetModelObject()->GetAttachmentModel( SPACESHIP_ATTACHMENT_SHIPINSIDE) == NULL)
    {
      // add it
      AddAttachment( SPACESHIP_ATTACHMENT_SHIPINSIDE, MODEL_SHIP_INSIDE, TEXTURE_BODY);
      GetModelObject()->StretchModel(PSS_STRETCH);
    }
    ShowBeamMachine();

    PlaySound( m_soPlates, SOUND_PLATES, SOF_3D);
    GetModelObject()->GetAttachmentModel( SPACESHIP_ATTACHMENT_DOOR1)->amo_moModelObject.PlayAnim(DOOR_ANIM_OPENING, 0); 
    GetModelObject()->GetAttachmentModel( SPACESHIP_ATTACHMENT_DOOR2)->amo_moModelObject.PlayAnim(DOOR_ANIM_OPENING, 0); 
    GetModelObject()->GetAttachmentModel( SPACESHIP_ATTACHMENT_DOOR3)->amo_moModelObject.PlayAnim(DOOR_ANIM_OPENING, 0); 
    GetModelObject()->GetAttachmentModel( SPACESHIP_ATTACHMENT_DOOR4)->amo_moModelObject.PlayAnim(DOOR_ANIM_OPENING, 0); 
    GetModelObject()->GetAttachmentModel( SPACESHIP_ATTACHMENT_DOOR5)->amo_moModelObject.PlayAnim(DOOR_ANIM_OPENING, 0); 
    GetModelObject()->GetAttachmentModel( SPACESHIP_ATTACHMENT_DOOR6)->amo_moModelObject.PlayAnim(DOOR_ANIM_OPENING, 0); 
    GetModelObject()->GetAttachmentModel( SPACESHIP_ATTACHMENT_DOOR7)->amo_moModelObject.PlayAnim(DOOR_ANIM_OPENING, 0); 
    GetModelObject()->GetAttachmentModel( SPACESHIP_ATTACHMENT_DOOR8)->amo_moModelObject.PlayAnim(DOOR_ANIM_OPENING, 0); 

    // all children lights named pulsating should pulsate
    FOREACHINLIST( CEntity, en_lnInParent, en_lhChildren, iten)
    {
      if( IsOfClass(iten, "Light"))
      {
        if( iten->GetName() == "Pulsating")
        {
          CLight *penLight = (CLight *) &*iten;
          EChangeAnim eChange;
          eChange.iLightAnim=3;
          eChange.bLightLoop=TRUE;
          penLight->SendEvent(eChange);
        }
        else if( iten->GetName() == "Motors")
        {
          CLight *penLight = (CLight *) &*iten;
          EChangeAnim eChange;
          eChange.iLightAnim=4;
          eChange.bLightLoop=TRUE;
          penLight->SendEvent(eChange);
        }
      }
    }

    m_epssState = PSSS_KILLING_BEAM_FIREING;
    return EReturn();
  }

  CloseDoors()
  {
    m_epssState=PSSS_DOORS_CLOSED;
    // if ship inside attachment added
    if( GetModelObject()->GetAttachmentModel( SPACESHIP_ATTACHMENT_SHIPINSIDE) != NULL)
    {
      PlaySound( m_soPlates, SOUND_PLATES, SOF_3D);
      GetModelObject()->GetAttachmentModel( SPACESHIP_ATTACHMENT_DOOR1)->amo_moModelObject.PlayAnim(DOOR_ANIM_CLOSING, 0); 
      GetModelObject()->GetAttachmentModel( SPACESHIP_ATTACHMENT_DOOR2)->amo_moModelObject.PlayAnim(DOOR_ANIM_CLOSING, 0); 
      GetModelObject()->GetAttachmentModel( SPACESHIP_ATTACHMENT_DOOR3)->amo_moModelObject.PlayAnim(DOOR_ANIM_CLOSING, 0); 
      GetModelObject()->GetAttachmentModel( SPACESHIP_ATTACHMENT_DOOR4)->amo_moModelObject.PlayAnim(DOOR_ANIM_CLOSING, 0); 
      GetModelObject()->GetAttachmentModel( SPACESHIP_ATTACHMENT_DOOR5)->amo_moModelObject.PlayAnim(DOOR_ANIM_CLOSING, 0); 
      GetModelObject()->GetAttachmentModel( SPACESHIP_ATTACHMENT_DOOR6)->amo_moModelObject.PlayAnim(DOOR_ANIM_CLOSING, 0); 
      GetModelObject()->GetAttachmentModel( SPACESHIP_ATTACHMENT_DOOR7)->amo_moModelObject.PlayAnim(DOOR_ANIM_CLOSING, 0); 
      GetModelObject()->GetAttachmentModel( SPACESHIP_ATTACHMENT_DOOR8)->amo_moModelObject.PlayAnim(DOOR_ANIM_CLOSING, 0); 
    
      autowait( GetModelObject()->GetAttachmentModel( SPACESHIP_ATTACHMENT_DOOR1)->amo_moModelObject.GetAnimLength(DOOR_ANIM_CLOSING));
      // remove ship inside attachment
      RemoveAttachment( SPACESHIP_ATTACHMENT_SHIPINSIDE);
    }

    HideBeamMachine();
    InitializePathMoving( (CPyramidSpaceShipMarker*)&*m_penFlyAwayTarget);
    return EReturn();
  }

  FireLightBeam()
  {
    if(m_epssState==PSSS_DOORS_CLOSED)
    {
      return;
    }

    if(m_epssState==PSSS_BEAM_DEACTIVATED)
    {
      m_bFireingDeactivatedBeam=TRUE;
    }

    PlaySound( m_soBeamMachine, SOUND_BEAMMACHINE, SOF_3D);
    GetModelObject()->PlayAnim(SPACESHIP_ANIM_OPENING, 0); 
    autowait( GetModelObject()->GetAnimLength(SPACESHIP_ANIM_OPENING));

    PlaySound( m_soBeamMachine, SOUND_WARMUP, SOF_3D);
    SpawnBeamMachineFlares();
    autowait( SMALL_FLARE_WAIT);

    SpawnBeamMachineLightnings();
    autowait( SMALL_LIGHTNING_WAIT);

    SpawnBeamMachineMainFlare();
    autowait( BIG_FLARE_WAIT);

    // turn on light beam
    TurnOnLightBeam();
    if(!m_bFireingDeactivatedBeam)
    {
      SpawnBeamMachineMainLightning();
    }

    m_soBeam.Set3DParameters(SND_FALLOFF, SND_HOTSPOT, SND_VOLUME, 1.0f);
    PlaySound( m_soBeam, SOUND_BEAM, SOF_3D|SOF_LOOP);
    ShowBeamMachineHitFlare();
    m_tmHitFlareTime = _pTimer->CurrentTick();
    
    m_iRingCounter = 0;
    while(_pTimer->CurrentTick()<m_tmHitFlareTime+BIG_LIGHT_BEAM_LIFE_TIME)
    {
      // spawn one moving ring
      if( m_iRingCounter < 16)
      {
        SpawnMovingRing();
        m_iRingCounter++;
      }

      // apply beam damage
      m_tmTemp = _pTimer->CurrentTick();
      while( _pTimer->CurrentTick() < m_tmTemp+0.49f)
      {
        autowait(_pTimer->TickQuantum);
        // cast ray for possible damage
        if( m_penBeamHit != NULL && !m_bFireingDeactivatedBeam)
        {
          // cast ray
          FLOAT3D vSource = GetPlacement().pl_PositionVector + FLOAT3D( 0, BM_MASTER_Y, 0);
          FLOAT3D vDestination = m_penBeamHit->GetPlacement().pl_PositionVector;
          CCastRay crRay( this, vSource, vDestination);
          crRay.cr_bHitTranslucentPortals = FALSE;
          crRay.cr_bPhysical = FALSE;
          crRay.cr_ttHitModels = CCastRay::TT_COLLISIONBOX;
          crRay.cr_fTestR = 16.0f;
          GetWorld()->CastRay(crRay);
        
          // if entity is hit
          if( crRay.cr_penHit != NULL)
          {
            InflictDirectDamage( crRay.cr_penHit, this, DMT_BULLET, 
              10000.0f/GetGameDamageMultiplier()*_pTimer->TickQuantum/0.5f/16.0f,
              FLOAT3D(0, 0, 0), (vSource-vDestination).Normalize());
            crRay.cr_penHit->SendEvent( EHitBySpaceShipBeam());
          }
        }
      }
    }

    m_tmBeamTime = _pTimer->CurrentTick();
    while(_pTimer->CurrentTick()<m_tmBeamTime+2.0f)
    {
      autowait(_pTimer->TickQuantum);
      FLOAT tmNow = _pTimer->CurrentTick();
      FLOAT fRatio = CalculateRatio(tmNow, m_tmBeamTime, m_tmBeamTime+2.0f, 0, 1.0f);
      m_soBeam.Set3DParameters(SND_FALLOFF, SND_HOTSPOT, fRatio*SND_VOLUME, 1.0f);
    }
    
    // turn off light beam
    TurnOffLightBeam();
    HideBeamMachineHitFlare();
    
    // little pause
    autowait( 2.0f);

    GetModelObject()->PlayAnim(SPACESHIP_ANIM_CLOSING, 0); 
    PlaySound( m_soBeamMachine, SOUND_BEAMMACHINE, SOF_3D);
    autowait( GetModelObject()->GetAnimLength(SPACESHIP_ANIM_CLOSING));

    m_tmHitFlareTime = -1.0f;
    m_tmBeamTime = -1.0f;

    if(m_bFireingDeactivatedBeam)
    {
      jump CloseDoors();
    }
    return EReturn();
  }

  Main() {
    // declare yourself as a model
    InitAsEditorModel();
    //InitAsEditorModel();
    SetPhysicsFlags(EPF_MODEL_IMMATERIAL|EPF_MOVABLE);
    SetCollisionFlags(ECF_MODEL_HOLDER);

    en_fAcceleration = 1e6f;
    en_fDeceleration = 1e6f;

    m_soBeam.Set3DParameters(SND_FALLOFF, SND_HOTSPOT, SND_VOLUME, 1.0f);
    m_soBeamMachine.Set3DParameters(SND_FALLOFF, SND_HOTSPOT, SND_VOLUME/2.0f, 1.0f);
    m_soPlates.Set3DParameters(SND_FALLOFF, SND_HOTSPOT, SND_VOLUME/2.0f, 1.0f);
    m_soFlaresFX.Set3DParameters(SND_FALLOFF, SND_HOTSPOT, SND_VOLUME, 1.0f);

    // set appearance
    SetModel(MODEL_SPACESHIP);
    SetModelMainTexture(TEXTURE_BODY);
    AddAttachment(SPACESHIP_ATTACHMENT_BODY, MODEL_BODY, TEXTURE_BODY);
    AddAttachment(SPACESHIP_ATTACHMENT_DOOR1, MODEL_DOOR, TEXTURE_DOOR);
    AddAttachment(SPACESHIP_ATTACHMENT_DOOR2, MODEL_DOOR, TEXTURE_DOOR);
    AddAttachment(SPACESHIP_ATTACHMENT_DOOR3, MODEL_DOOR, TEXTURE_DOOR);
    AddAttachment(SPACESHIP_ATTACHMENT_DOOR4, MODEL_DOOR, TEXTURE_DOOR);
    AddAttachment(SPACESHIP_ATTACHMENT_DOOR5, MODEL_DOOR, TEXTURE_DOOR);
    AddAttachment(SPACESHIP_ATTACHMENT_DOOR6, MODEL_DOOR, TEXTURE_DOOR);
    AddAttachment(SPACESHIP_ATTACHMENT_DOOR7, MODEL_DOOR, TEXTURE_DOOR);
    AddAttachment(SPACESHIP_ATTACHMENT_DOOR8, MODEL_DOOR, TEXTURE_DOOR);

    GetModelObject()->StretchModel(PSS_STRETCH);
    ModelChangeNotify();
    m_bMoving = FALSE;
    m_epssState = PSSS_IDLE;
    m_bFireingDeactivatedBeam=FALSE;
    
    autowait( 0.25f);

    // turn off light beam
    TurnOffLightBeam();
    // turn off light beam hit flare
    HideBeamMachineHitFlare();

    // start moving
    wait() {
      on( EActivate):
      {
        SwitchToModel();
        InitializePathMoving((CPyramidSpaceShipMarker*)&*m_penTarget);
        resume;
      }
      on( ETrigger):
      {
        if(m_epssState == PSSS_IDLE)
        {
          // ignore all triggs
        }
        else if( m_epssState==PSSS_KILLING_BEAM_FIREING)
        {
          call FireLightBeam();
        }
        else if(m_epssState==PSSS_BEAM_DEACTIVATED)
        {
          call FireLightBeam();
        }
        else if(m_epssState == PSSS_REACHED_DESTINATION)
        {
          call OpenDoors();
        }
        resume;
      }
      on (EForcePathMarker eForcePathMarker):
      {
        if(m_epssState != PSSS_IDLE)
        {
          m_penTarget = eForcePathMarker.penForcedPathMarker;
          InitializePathMoving((CPyramidSpaceShipMarker*)&*m_penTarget);
        }
        resume;
      }
      on( EDeactivate):
      {
        m_epssState = PSSS_BEAM_DEACTIVATED;
        resume;
      }
      on( EReturn):
      {
        resume;
      }
    }

    Destroy();
    return;
  }
};
