702
%{
#include "StdH.h"
%}

class CWatchPlayers: CRationalEntity {
name      "Watch Players";
thumbnail "Thumbnails\\WatchPlayers.tbn";
features "HasName", "IsTargetable";

properties:
  1 CEntityPointer m_penOwner "Owner/Target" 'O' COLOR(C_dBROWN|0xFF),             // entity which owns it / target
  2 FLOAT m_fWaitTime         "Wait time" 'W' = 0.1f,         // watch time
  3 RANGE m_fDistance         "Watch distance" 'D' = 100.0f,  // distance when player is seen
  4 BOOL m_bRangeWatcher      "Range watcher" 'R' = TRUE,    // range watcher
  5 enum EventEType m_eetEventClose  "Close Event type" 'T' = EET_TRIGGER,   // type of event to send
  6 enum EventEType m_eetEventFar    "Far Event type" 'Y' = EET_ENVIRONMENTSTOP,      // type of event to send
  7 CEntityPointer m_penCurrentWatch,
  8 BOOL m_bActive  "Active" 'A' = TRUE,
  9 CTString m_strName "Name" 'N' = "",

components:
  1 model   MODEL_WATCHPLAYERS      "Models\\Editor\\WatchPlayers.mdl",
  2 texture TEXTURE_WATCHPLAYERS    "Models\\Editor\\WatchPlayers.tex"

functions:
/************************************************************
 *                      USER FUNCTIONS                      *
 ************************************************************/
  // check if any player is close
  BOOL IsAnyPlayerClose(void) {
    // far enough to not move at all
    FLOAT fClosest = 100000.0f;
    FLOAT fDistance;

    m_penCurrentWatch = NULL;
    // for all players
    for (INDEX iPlayer=0; iPlayer<GetMaxPlayers(); iPlayer++) {
      CEntity *penPlayer = GetPlayerEntity(iPlayer);
      // if player is alive and visible
      if (penPlayer!=NULL && penPlayer->GetFlags()&ENF_ALIVE && !(penPlayer->GetFlags()&ENF_INVISIBLE)) {
        fDistance = 100000.0f;
        if (m_bRangeWatcher) {
          // calculate distance to player from wathcer
          fDistance = (penPlayer->GetPlacement().pl_PositionVector-
                       GetPlacement().pl_PositionVector).Length();
        } else {
          if (m_penOwner!=NULL) {
            // calculate distance to player from owner
            fDistance = (penPlayer->GetPlacement().pl_PositionVector-
                         m_penOwner->GetPlacement().pl_PositionVector).Length();
          }
        }
        if (fDistance<fClosest) {
          fClosest = fDistance;
          m_penCurrentWatch = penPlayer;
        }
      }
    }
    // if close enough start moving
    return (fClosest < m_fDistance);
  };

  // send close event
  void SendCloseEvent(void) {
    // send range event
    if (m_bRangeWatcher && m_penOwner==NULL) {
//      SendInRange(this, m_eetEventClose, FLOATaabbox3D(GetPlacement().pl_PositionVector, m_fDistance));
    // send to owner
    } else {
      SendToTarget(m_penOwner, m_eetEventClose, m_penCurrentWatch);
    }
  };

  // send far event
  void SendFarEvent(void) {
    // send range event
    if (m_bRangeWatcher && m_penOwner==NULL) {
//      SendInRange(this, m_eetEventFar, FLOATaabbox3D(GetPlacement().pl_PositionVector, m_fDistance));
    // send to owner
    } else {
      SendToTarget(m_penOwner, m_eetEventFar);
    }
  };

procedures:
  // main (initialization)
  Main(EVoid) {
    // init as nothing
    InitAsEditorModel();
    SetPhysicsFlags(EPF_MODEL_IMMATERIAL);
    SetCollisionFlags(ECF_IMMATERIAL);

    // set appearance
    SetModel(MODEL_WATCHPLAYERS);
    SetModelMainTexture(TEXTURE_WATCHPLAYERS);

    if (m_fWaitTime<0.1f) {
      m_fWaitTime=0.1f;
    }

    if (m_bActive) {
      jump Active();
    } else {
      jump Inactive();
    }
  };

  Active()
  {
    autocall FarWatch() EDeactivate;
    jump Inactive();
  }

  Inactive()
  {
    wait() {
      on (EActivate) : {
        stop;
      };
      otherwise() : {
        resume;
      };
    }
    jump Active();
  }

  // player is close
  CloseWatch(EVoid) {
    while (TRUE) {
      wait(m_fWaitTime) {
        on (EBegin) : {
          if (!IsAnyPlayerClose()) {
            // notify for player off range
            SendFarEvent();
            jump FarWatch();
          }
          resume;
        }
        on (ETimer) : { stop; }
      }
    }
  };
  
  // player is far
  FarWatch(EVoid) {
    while (TRUE) {
      wait(m_fWaitTime) {
        on (EBegin) : {
          if (IsAnyPlayerClose()) {
            // notify for player in range
            SendCloseEvent();
            jump CloseWatch();
          }
          resume;
        }
        on (ETimer) : { stop; }
      }
    }
  };
};