704
%{
#include "StdH.h"
#include "Entities/Common/PathFinding.h"

#define MAX_TARGETS 6
%}

uses "Entities/Marker";

%{
// info structure
static EntityInfo eiMarker = {
  EIBT_ROCK, 10.0f,
  0.0f, 1.0f, 0.0f,     // source (eyes)
  0.0f, 1.0f, 0.0f,     // target (body)
};

%}

class export CNavigationMarker : CEntity {
name      "NavigationMarker";
thumbnail "Thumbnails\\NavigationMarker.tbn";
features  "HasName", "IsTargetable";

properties:
  1 CTString m_strName          "Name" 'N' = "Marker",
  2 RANGE m_fMarkerRange        "Marker Range" 'M' = 1.0f,  // range around marker (marker doesn't have to be hit directly)

  100 CEntityPointer m_penTarget0  "Target 0" 'T' COLOR(C_dBLUE|0xFF),
  101 CEntityPointer m_penTarget1  "Target 1"     COLOR(C_dBLUE|0xFF),
  102 CEntityPointer m_penTarget2  "Target 2"     COLOR(C_dBLUE|0xFF),
  103 CEntityPointer m_penTarget3  "Target 3"     COLOR(C_dBLUE|0xFF),
  104 CEntityPointer m_penTarget4  "Target 4"     COLOR(C_dBLUE|0xFF),
  105 CEntityPointer m_penTarget5  "Target 5"     COLOR(C_dBLUE|0xFF),

  {
    CPathNode *m_ppnNode;  // for pathfinding algorithm
  }

components:
  1 model   MODEL_MARKER     "Models\\Editor\\NavigationMarker.mdl",
  2 texture TEXTURE_MARKER   "Models\\Editor\\NavigationMarker.tex"

functions:
  void CNavigationMarker(void)
  {
    m_ppnNode = NULL;
  }
  void ~CNavigationMarker(void)
  {
    ASSERT(m_ppnNode == NULL);
  }

  /* Read from stream. */
  void Read_t( CTStream *istr) // throw char *
  {
    CEntity::Read_t(istr);
    m_ppnNode = NULL;
  }
  
  CEntity *GetTarget(void) const { return m_penTarget0; };

  /* Entity info */
  void *GetEntityInfo(void) {
    return &eiMarker;
  };

  // get node used for pathfinding algorithm
  CPathNode *GetPathNode(void)
  {
    if (m_ppnNode==NULL) {
      m_ppnNode = new CPathNode(this);
    }

    return m_ppnNode;
  }

  CEntityPointer &TargetPointer(INDEX i)
  {
    ASSERT(i>=0 && i<MAX_TARGETS);
    return (&m_penTarget0)[i];
  }
  CNavigationMarker &Target(INDEX i)
  {
    return (CNavigationMarker &)*TargetPointer(i);
  }

  // get link with given index or null if no more (for iteration along the graph)
  CNavigationMarker *GetLink(INDEX i)
  {
    for(INDEX iTarget=0; iTarget<MAX_TARGETS; iTarget++) {
      CNavigationMarker *penLink = &Target(iTarget);
      if (penLink==NULL) {
        continue;
      }
      if (iTarget==i) {
        return penLink;
      }
    }
    return NULL;
  }

  const CTString &GetDescription(void) const {
    return m_strName;
  }

  /* Check if entity is moved on a route set up by its targets. */
  BOOL MovesByTargetedRoute(CTString &strTargetProperty) const {
    strTargetProperty = "Target 0";
    return TRUE;
  };
  /* Check if entity can drop marker for making linked route. */
  BOOL DropsMarker(CTFileName &fnmMarkerClass, CTString &strTargetProperty) const {
    fnmMarkerClass = CTFILENAME("Classes\\NavigationMarker.ecl");
    strTargetProperty = "Target 0";
    return TRUE;
  }
  // this is MARKER
  virtual BOOL IsMarker(void) const {
    return TRUE;
  }

procedures:
  Main() {
    InitAsEditorModel();
    SetPhysicsFlags(EPF_MODEL_IMMATERIAL);
    SetCollisionFlags(ECF_IMMATERIAL);

    // set appearance
    const FLOAT fSize = 0.25f;
    GetModelObject()->StretchModel(FLOAT3D(fSize, fSize, fSize));
    SetModel(MODEL_MARKER);
    ModelChangeNotify();
    SetModelMainTexture(TEXTURE_MARKER);

    // for each non-empty target
    for (INDEX iTarget=0; iTarget<MAX_TARGETS; iTarget++) {
      CEntityPointer &penTarget = TargetPointer(iTarget);
      if (penTarget==NULL) {
        continue;
      }

      // if not valid class
      if (!IsOfClass(penTarget, "NavigationMarker")) {
        // clear it
        penTarget = NULL;
        continue;
      }

      CNavigationMarker &nmOther = (CNavigationMarker &)*penTarget;

      // check if it has back pointer
      BOOL bFound = FALSE;
      for (INDEX iTarget2=0; iTarget2<MAX_TARGETS; iTarget2++) {
        CEntityPointer &penTarget2 = nmOther.TargetPointer(iTarget2);
        if (penTarget2==this) {
          bFound = TRUE;
          break;
        }
      }
      // if none found
      if (!bFound) {
        // auto-set an empty one if possible
        for (INDEX iTarget2=0; iTarget2<MAX_TARGETS; iTarget2++) {
          CEntityPointer &penTarget2 = nmOther.TargetPointer(iTarget2);
          if (penTarget2==NULL) {
            penTarget2 = this;
            break;
          }
        }
      }
    }

    return;
  }
};

