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

uses "Entities/ModelHolder2";

enum SwitchType {
  0 SWT_ONCE    "Once",
  1 SWT_ONOFF   "On/Off",
};

class CSwitch: CModelHolder2 {
name      "Switch";
thumbnail "Thumbnails\\Switch.tbn";
features  "HasName", "HasTarget", "IsTargetable";

properties:
  4 ANIMATION m_iModelONAnimation     "Model ON animation" 'D' = 0,
  5 ANIMATION m_iTextureONAnimation   "Texture ON animation" = 0,
  6 ANIMATION m_iModelOFFAnimation    "Model OFF animation" 'G' = 0,
  7 ANIMATION m_iTextureOFFAnimation  "Texture OFF animation" = 0,

 10 CEntityPointer m_penTarget      "ON-OFF Target" 'T' COLOR(C_dBLUE|0xFF),                      // send event to entity
 11 enum EventEType m_eetEvent      "ON  Event type" 'U' = EET_START,  // type of event to send
 12 enum EventEType m_eetOffEvent   "OFF Event type" 'I' = EET_IGNORE, // type of event to send

 18 enum SwitchType m_swtType   "Type" 'Y' = SWT_ONOFF,
 19 CTString m_strMessage       "Message" 'M' = "",

 // internal -> do not use
 20 BOOL m_bSwitchON = FALSE,
 21 CEntityPointer m_penCaused,   // who triggered it last time
 22 BOOL m_bUseable = FALSE,      // set while the switch can be triggered
 23 BOOL m_bInvisible "Invisible" = FALSE,    // make it editor model

components:

functions:                                        
  /* Get anim data for given animation property - return NULL for none. */
  CAnimData *GetAnimData(SLONG slPropertyOffset) 
  {
    if (slPropertyOffset==offsetof(CSwitch, m_iModelONAnimation) ||
        slPropertyOffset==offsetof(CSwitch, m_iModelOFFAnimation)) {
      return GetModelObject()->GetData();
    } else if (slPropertyOffset==offsetof(CSwitch, m_iTextureONAnimation) ||
               slPropertyOffset==offsetof(CSwitch, m_iTextureOFFAnimation)) {
      return GetModelObject()->mo_toTexture.GetData();
    } else {
      return CModelHolder2::GetAnimData(slPropertyOffset);
    }
  }

  // test if this door reacts on this entity
  BOOL CanReactOnEntity(CEntity *pen)
  {
    if (pen==NULL) {
      return FALSE;
    }
    // never react on non-live or dead entities
    if (!(pen->GetFlags()&ENF_ALIVE)) {
      return FALSE;
    }

    return TRUE;
  }

procedures:
  // turn the switch on
  SwitchON() {
    // if already on
    if (m_bSwitchON) {
      // do nothing
      return;
    }
    // switch ON
    GetModelObject()->PlayAnim(m_iModelONAnimation, 0);
    GetModelObject()->mo_toTexture.PlayAnim(m_iTextureONAnimation, 0);
    m_bSwitchON = TRUE;
    // send event to target
    SendToTarget(m_penTarget, m_eetEvent, m_penCaused);
    // wait for anim end
    wait(GetModelObject()->GetAnimLength(m_iModelONAnimation)) {
      on (EBegin) : { resume; } on (ETimer) : { stop; } otherwise(): { resume; }
    }

    return EReturn();  // to notify that can be usable
  };

  // turn the switch off
  SwitchOFF() {
    // if already off
    if (!m_bSwitchON) {
      // do nothing
      return;
    }
    // switch off
    GetModelObject()->PlayAnim(m_iModelOFFAnimation, 0);
    GetModelObject()->mo_toTexture.PlayAnim(m_iTextureOFFAnimation, 0);
    m_bSwitchON = FALSE;
    // send off event to target
    SendToTarget(m_penTarget, m_eetOffEvent, m_penCaused);
    // wait for anim end
    wait(GetModelObject()->GetAnimLength(m_iModelOFFAnimation)) {
      on (EBegin) : { resume; } on (ETimer) : { stop; } otherwise(): { resume; }
    }

    return EReturn();  // to notify that can be usable
  };

  MainLoop_Once() {
    m_bUseable = TRUE;

    //main loop
    wait() {
      // trigger event -> change switch
      on (ETrigger eTrigger) : {
        if (CanReactOnEntity(eTrigger.penCaused) && m_bUseable) {
          m_bUseable = FALSE;
          m_penCaused = eTrigger.penCaused;
          call SwitchON();
        }
      }
      // start -> switch ON
      on (EStart) : {
        m_bUseable = FALSE;
        call SwitchON();
      }
      // stop -> switch OFF
      on (EStop) : {
        m_bUseable = FALSE;
        call SwitchOFF();
      }
      on (EReturn) : {
        m_bUseable = !m_bSwitchON;
        resume;
      }
    }
  };

  MainLoop_OnOff() {
    m_bUseable = TRUE;

    //main loop
    wait() {
      // trigger event -> change switch
      on (ETrigger eTrigger) : {
        if (CanReactOnEntity(eTrigger.penCaused) && m_bUseable) {
          m_bUseable = FALSE;
          m_penCaused = eTrigger.penCaused;
          // if switch is ON make it OFF
          if (m_bSwitchON) {
            call SwitchOFF();
          // else if switch is OFF make it ON
          } else {
            call SwitchON();
          }
        }
      }
      // start -> switch ON
      on (EStart) : {
        m_bUseable = FALSE;
        call SwitchON();
      }
      // stop -> switch OFF
      on (EStop) : {
        m_bUseable = FALSE;
        call SwitchOFF();
      }
      on (EReturn) : {
        m_bUseable = TRUE;
        resume;
      }
    }
  };

  Main() {
    // init as model
    CModelHolder2::InitModelHolder();

    if (m_bInvisible) {
      SwitchToEditorModel();
    }

    if (m_swtType==SWT_ONCE) {
      jump MainLoop_Once();
    } else {
      jump MainLoop_OnOff();
    }

    return;
  };
};
