/* ****************************************************************************
            //#Format Of Command Builder:
//---G


    Each AI command uses a series of defines, and each define MUST be used.
        #define CMDNAME     COMMAND_NAME

        #define DESCRIPTION \
        / *
          *  Description  *See Documentation*
          * /

        #define PARAMS BYTE(FirstParam), BYTE(secondParam)

        _AI_CMD(
            {
                Code           //Put Code here (newlines supported within {})
            })

    Each command is responsible for undefining its own macros

    You may define up to 8 unsigned parameters.
    Parameter Types:    BYTE(NAME) Most parameters are single bytes
                        DBYTE(NAME) Most Floats are 2 bytes
                        TBYTE(NAME) Some times are 3 bytes
                        QBYTE(NAME) Most bit masks are 4 bytes

    Code:   inline comments / / are NOT allowed.


    _AI_CMD_POLYMORPH(
        Commands before base command, // (Do NOT separate by comma)
        POLYMORPH NAME,
        (CMD PARAM NAMES OR VALUES),
        (POLY PARAM NAMES))

    Parameter Types: N/A - Polymorphs are Non-Typed.

    CMD PARAMS: These MUST follow the same order as the base command.
                Params not included in Polymorph must have a value.

    POLY PARAMS: Parameters for Polymorph.

    Polymorph example:
                    _AI_CMD_POLYMORPH(,
                                        Poly,
                                        (CHR_SELF,3,LABEL),
                                        (LABEL)
                                        )

    Explanation:    Cmd takes CHR_ID, NUM, LABEL
                    Poly Takes LABEL
                    CHR_ID and NUM must have values

    //##HACKS:
        //###Extending Polymorph child commands:
            Extra Commands after CMDNAME can be added to the 3rd Parameter
            after (CMD PARAMS)
                e.g. ...LOOP, (GOTOLABEL) SLEEP() GOTO_FIRST(GOTOLABEL),...
                    Where (GOTOLABEL) is the param and
                    SLEEP and GOTO_FIRST are extra commands.
            See DO LOOP and RANDOM_GENERATE_LESS_THAN

    //##Documentation:
    Description Format: Opening and closing / * * / MUST have 2 asterisks
                        after/before slash.( / * * ... * * /)
                        Important Notes are indicated by Exception tags.

    Comments:           Ordinary comments NOT for output MUST NOT have
                        2 asterisks after/before slash.

    Inline Comments:    Inline comments that use / / ARE included in output
                        If you do NOT want the comment included in output
                        use multi-line comment tags.(/ * * /)
                        HACK: To include an inline comment within a parameter
                              of Command Builder or to otherwise break a
                              single line use a multi-line comment tag with
                              a new line inside, e.g. \ * \ n
                                                      * /

**************************************************************************** */
/*Visual Studio syntax highlighter Assistant,
(FIXED 20/02/2020) if problems occur break this asterisk! --> */
#ifndef __GNUC__
#include <CPPLib.h>
#endif
#if 0
#ifndef _AI_CMD
    #define _SYNHILITE
    #include <bondgame.h>
    #include "game/chrai.h"
    #include "game/chr2.h"
    void aiExecute2(void *Entityp, s32 EntityType)    {    u8 *sp7B0;    u8 *sp7AC;    u32 Offset;    ChrRecord *ChrEntityp = 0;    VehichleRecord *VehichleEntityp;    VehichleRecord *AircraftEntityp;    u8 *AiListp;   u8 *CurrentCommandp = AiListp + Offset;   switch (CurrentCommandp[0]){case 0:
    #define _AI_CMD(CODE)
    #define _AI_DEBUG(CODE)
    #define _AI_CMD_POLYMORPH(PRE, NAME, CMD_PARAMS_POST, POLY_PARAMS)
    #define BYTE()
    #define DBYTE()
    #define TBYTE()
    #define QBYTE()
#endif
#endif
/*
//#Useful Regex:
/(^\*.{78,78}?)(.*)(\*$)/  $1$3  regex for long comments trimming
(^_.*?\()(.*?,)(.*$)             Matches command name replace with $1CMDNAME,$3

//##Change log:
//!FIXED! 02/02/2020 Use of CMDNAME mandatory for Polymorph
//!FIXED! 05/11/2020 No longer need Number of Params, they are calculated.
//BUG!   11/11/2020 Attempt at more automated All-in-One command that did away
                  with defines worked except for polymorph description...
                  Syntax was as below:
                  (command, description, params, code,(polymorphs))
*/




//==============================================================================
//## GOTO NEXT
//==============================================================================
#if 0 /*//BUG All-in-One command failed on description inclusion during loop (newlines)*/
_AI_CMD(GONEXT,
    /*******************************************************************************
    * Skips a22ll commands between this command and LABEL then continues
    * executing after found LABEL
    ********************************************************************************
    * @param     LABEL: 8bit Integer/enum ID of label to Go To
    * @return    Nothing, Move Execution to Next Label with ID LABEL or End
    * @exception Does Not search from beginning
    *******************************************************************************/,
    (BYTE(LABEL)),
    {  /*Match*/
        AIRecord1 *ai = AiListp + Offset;
        Offset        = chraiGoToLabel(AiListp, Offset, ai->val);
        break;
    },
    (
        (BREAK(ID), (id, 2)),
        (BREAK1(ID), (id, 2)),
        (
            CON(ID), (ID,4),(, (SLEEP(), SLEEP3))),
        (ANO(ID), (ID,6), (SLEEP()))
    ))
#endif

/* #define CMDNAME GOTO_NEXT */
#define CMDNAME GotoNext

#define DESCRIPTION \
/*******************************************************************************
* Skips all commands between this command and LABEL then continues
* executing after found LABEL
********************************************************************************
* @param     LABEL: 8bit Integer/enum ID of label to Go To
* @return    Continue execution from LABEL or End
* @exception Does Not search from beginning
*******************************************************************************/

#define PARAMS BYTE(LABEL)

_AI_CMD({ /*Match*/
          AIRecord1 *ai = AiListp + Offset;
          Offset        = chraiGoToLabel(AiListp, Offset, ai->val);
          break;
})

//ALIAS FOR SYNTAX SUGER
_AI_CMD_POLYMORPH(,
                  BREAK,
                  (LABEL),
                  (LABEL))

#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## GOTO FIRST
//==============================================================================

/* #define CMDNAME GOTO_FIRST */
#define CMDNAME GotoFirst

#define DESCRIPTION \
/*******************************************************************************
* Starting from the beginning, Skips all commands until LABEL - continues
* executing after found LABEL
********************************************************************************
* @param     LABEL: 8bit Integer/enum ID of label to Go To
* @return    Continue execution from LABEL or End
* @exception None
*******************************************************************************/

#define PARAMS BYTE(LABEL)

_AI_CMD({ /*Match*/
          AIRecord1 *ai = AiListp + Offset;
          Offset        = chraiGoToLabel(AiListp, 0, ai->val);
          break;
})

//ALIAS FOR SYNTAX SUGER

#undef DESCRIPTION
#define DESCRIPTION \
/*******************************************************************************
* LOOP back to DO with ID
********************************************************************************
* @param     ID: 8bit Integer/enum ID of DO
* @return    Loop Back and Continue execution from LABEL
* @exception Must have valid DO ID
*******************************************************************************/

_AI_CMD_POLYMORPH( ,
                  LOOP,
                  (ID),
                  (ID))

#undef DESCRIPTION
#define DESCRIPTION \
/*******************************************************************************
* Exit LOOP early and go back to DO with ID
********************************************************************************
* @param     ID: 8bit Integer/enum ID of DO
* @return    Loop back and Continue execution from LABEL
* @exception Must have valid DO ID
*******************************************************************************/

_AI_CMD_POLYMORPH( ,
                  CONTINUE,
                  (ID),
                  (ID))

#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## LABEL
//==============================================================================

/* #define CMDNAME LABEL */
#define CMDNAME Label

#define DESCRIPTION \
/*******************************************************************************
* Used as a target for Loops and Conditionals
********************************************************************************
* @param     ID: 8bit Integer/enum ID
* @return    Nothing
* @exception None
*******************************************************************************/

#define PARAMS BYTE(ID)

_AI_CMD(
    {
        Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        break;
    })

//ALIAS FOR SYNTAX SUGER

#undef DESCRIPTION
#define DESCRIPTION \
/*******************************************************************************
* DO LOOP with ID
********************************************************************************
* @param     ID: 8bit Integer/enum ID of DO
* @return    Nothing
* @exception Do NOT re-use the ID again within this block
*******************************************************************************/

_AI_CMD_POLYMORPH(,
                  DO,
                  /*label*/(ID) /*HACK - The way Polymorphs work mean
                                       the CMDNAME is always first, afterwards
                                       we can stuff the Polymorph with as many
                                       commands as we want
                                       Here we want :
                                           Label(ID) Yield()
                                       so we place Yield() after (ID)*/
                  Yield(),
                  (ID))

#undef DESCRIPTION
#define DESCRIPTION \
/*******************************************************************************
* DO INFINITE LOOP with ID
********************************************************************************
* @param     ID: 8bit Integer/enum ID of LOOP
* @return    Nothing
* @exception Must have unique ID
*******************************************************************************/

_AI_CMD_POLYMORPH(,
                  YIELD_FOREVER,
                  (ID)
                  Yield()
                  GotoFirst(ID),
                  (ID))

#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## Yield
//==============================================================================

/* #define CMDNAME SLEEP */
#define CMDNAME Yield

#define DESCRIPTION\
/*******************************************************************************
* Halt the AI List - frees engine to start executing next AI List until all
* Lists have been executed for game tick.
********************************************************************************
* @return    Current AI List Position
* @exception Off-screen/idle guard's will take 14 game ticks instead of 1 tick
*******************************************************************************/

#define PARAMS /*NONE*/

_AI_CMD({ /*83% Match*/
          Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
          if (ChrEntityp)
          {
              ChrEntityp->ailist   = AiListp;
              ChrEntityp->aioffset = Offset;
          }
          else if (VehichleEntityp)
          {
              VehichleEntityp->ailist   = AiListp;
              VehichleEntityp->aioffset = Offset;
          }
          else if (AircraftEntityp)
          {
              AircraftEntityp->ailist   = AiListp;
              AircraftEntityp->aioffset = Offset;
          }
          return;
})

#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## END
//==============================================================================

/* #define CMDNAME ENDLIST */
#define CMDNAME EndList /*canonical name*/

#define DESCRIPTION\
/*******************************************************************************
* Used for AI List parser to check when List ends
********************************************************************************
* @exception: Not recommended to execute this command - to finish a List create
*             an infinite loop (YIELD_FOREVER) or jump to GAILIST_DEAD_AI
*             when List has finished tasks
*******************************************************************************/

#define PARAMS /*NONE*/

_AI_CMD({return;}/*Match*/)

#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## JUMP TO AI LIST
//==============================================================================

/* #define CMDNAME JUMP_TO_AI_LIST */
#define CMDNAME SetChrAiList

#define DESCRIPTION\
/******************************************************************************
* Set Chr's current AI List to beginning of AI_LIST_ID and execute at
* next opportunity.
*******************************************************************************
* @param      CHR_NUM: ID of Chr (0-255)
* @param      AI_LIST_ID: 16bit pointer to AI List within Chr/Global
* @exception: Not recommended to Go to a BG List (10XX)
******************************************************************************/

#define PARAMS BYTE(CHR_NUM), DBYTE(AI_LIST_ID)
_AI_CMD(
    {
        AIRecord * ai = AiListp + Offset;
        ChrRecord *chr;
        u16        AI_LIST_ID = CharArrayTo16(ai->val,1);
        u8         CHR_NUM    = ai->val[0];
        if (CHR_NUM == ((u8)CHR_SELF))
        {
            AiListp = ailistFindById(AI_LIST_ID);
            Offset  = 0;
        }
        else
        {
            chr = chrFindById(ChrEntityp, CHR_NUM);
            if (chr)
            {
                chr->ailist   = ailistFindById(AI_LIST_ID);
                chr->aioffset = 0;
                chr->sleep    = 0;
            }
            Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        }
        break;
    }
)

//POLYMORPHS

#undef DESCRIPTION
#define DESCRIPTION \
/******************************************************************************
* Jump my AI to AI_LIST_ID then return to Standard Guard
*******************************************************************************
* @return     None - Will continue as "Standard Guard"
* @param      AI_LIST_ID: AI List to execute
* @exception: AI_LIST_ID Must NOT be a BG List (10XX)
******************************************************************************/

_AI_CMD_POLYMORPH(SetReturnAiList(GAILIST_STANDARD_GUARD),
                  JUMPTO_THEN_GUARD,
                  (CHR_SELF, (!isBGAIListID(AI_LIST_ID) ? AI_LIST_ID : AI_ERR_SUB)),
                  (AI_LIST_ID))

#undef DESCRIPTION
#define DESCRIPTION \
/******************************************************************************
* Jump my AI to AI_LIST_ID and loop
*******************************************************************************
* @return     None - Will Loop the New AI List
* @param      AI_LIST_ID: AI List to execute
* @exception: AI_LIST_ID Must NOT be a BG List (10XX)
******************************************************************************/

_AI_CMD_POLYMORPH(SetReturnAiList(AI_LIST_ID),
                  JUMPTO_THEN_LOOP,
                  (CHR_SELF, (!isBGAIListID(AI_LIST_ID) ? AI_LIST_ID : AI_ERR_SUB)),
                  (AI_LIST_ID))

#undef DESCRIPTION
#define DESCRIPTION \
/******************************************************************************
* Jump To Another AI List
*******************************************************************************
* @return     None
* @param      AI_LIST_ID: AI List to execute
* @exception: AI_LIST_ID Must NOT be a BG List (10XX)
******************************************************************************/

_AI_CMD_POLYMORPH(,
                  JumpTo,
                  (CHR_SELF, (!isBGAIListID(AI_LIST_ID) ? AI_LIST_ID : AI_ERR_SUB)),
                  (AI_LIST_ID))

#undef DESCRIPTION
#define DESCRIPTION \
/******************************************************************************
* Set Bond's Cinema AI List
*******************************************************************************
* @param      AI_LIST_ID: AI List for Bond to execute during cinema
* @exception: AI_LIST_ID Must NOT be a BG List (10XX)
*             Only use when Bond has a 3rd person model (cutscenes)
******************************************************************************/

_AI_CMD_POLYMORPH(,
                  SetBondsAiList,
                  (CHR_BOND_CINEMA, (!isBGAIListID(AI_LIST_ID) ? AI_LIST_ID : AI_ERR_NOTCHR)),
                  (AI_LIST_ID))

#undef DESCRIPTION
#define DESCRIPTION \
/******************************************************************************
* Set my Clone's AI List
*******************************************************************************
*   @param      AI_LIST_ID: AI List for Bond to execute during cinema
*   @exception: AI_LIST_ID Must NOT be a BG List (10XX)
******************************************************************************/

_AI_CMD_POLYMORPH(,
                  SetMyClonesAiList,
                  (CHR_CLONE, (!isBGAIListID(AI_LIST_ID) ? AI_LIST_ID : AI_ERR_NOTCHR)),
                  (AI_LIST_ID))

#undef DESCRIPTION
#define DESCRIPTION \
/******************************************************************************
* Call AI Subroutine with AI_LIST_ID and return (If subroutine allows)
*******************************************************************************
* @return     Restart Calling AI list on Return
* @param      AI_LIST_ID: AI List to execute (Must be a subroutine/have return)
* @exception: AI_LIST_ID Must NOT be a BG List (10XX)
*             "THIS" must be defined for this function to be able to return
******************************************************************************/

_AI_CMD_POLYMORPH(\
                  IF_ELSE DEFER(LPAREN)() DEFINED DEFER(LPAREN)() THIS DEFER(RPAREN)() \
                   DEFER(RPAREN)() DEFER(LPAREN)() AI_ERR_NO_THIS   DEFER(RPAREN)() DEFER(LPAREN)() DEFER(LPAREN)() \
                  SetReturnAiList(THIS) ,\
                  CALL,\
                  (CHR_SELF, (!isBGAIListID(AI_LIST_ID) && isSubroutine(AI_LIST_ID) ? AI_LIST_ID : AI_ERR_NOTSUB)) DEFER2(RPAREN)() DEFER2(RPAREN)() DEFER2(COMMA)(),\
                  (AI_LIST_ID)\
)

#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## SET RETURN AI LIST
//==============================================================================

/* #define CMDNAME SET_RETURN_AI_LIST */
#define CMDNAME SetReturnAiList

#define DESCRIPTION \
/*******************************************************************************
* Set the Return AI List - used by aiReturn
********************************************************************************
* @param      AI_LIST_ID to be able to return to
* @exception: Not recommended to set stored List to a BG List (10XX)
*******************************************************************************/

#define PARAMS DBYTE(AI_LIST_ID)

_AI_CMD(
    {
        AIRecord *ai         = AiListp + Offset;
        u16       AI_LIST_ID = CharArrayTo16(ai->val,0);
        if (ChrEntityp)
        {
            ChrEntityp->aireturnlist = AI_LIST_ID;
        }
        else if (VehichleEntityp)
        {
            VehichleEntityp->aireturnlist = AI_LIST_ID;
        }
        else if (AircraftEntityp)
        {
            AircraftEntityp->aireturnlist = AI_LIST_ID;
        }
        Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        break;
    })



#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## JUMP TO RETURN AI LIST
//==============================================================================

/* #define CMDNAME JUMP_TO_RETURN_AI_LIST */
#define CMDNAME Return

#define DESCRIPTION \
/*******************************************************************************
* Jump the return AI List set in chr struct - pointer set by SetReturnAiList
* Used for subroutine Lists.
********************************************************************************
* @return     Continue execution from beginning of stored return list ID
* @exception: If stored return List pointer is not set, game will crash
*******************************************************************************/

#define PARAMS /*NONE*/

_AI_CMD(
    {
        if (ChrEntityp)
        {
            AiListp = ailistFindById(ChrEntityp->aireturnlist);
        }
        else if (VehichleEntityp)
        {
            AiListp = ailistFindById(VehichleEntityp->aireturnlist);
        }
        else if (AircraftEntityp)
        {
            AiListp = ailistFindById(AircraftEntityp->aireturnlist);
        }
        Offset = 0;
        break;
    })



#undef DESCRIPTION
#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## GUARD ANIMATION STOP
//==============================================================================

/* #define CMDNAME GUARD_ANIMATION_STOP */
#define CMDNAME Stop

#define DESCRIPTION \
/*******************************************************************************
* Immediately Reset me back to idle pose
*******************************************************************************/

#define PARAMS /*NONE*/

_AI_CMD(
    {
        chraiStopAnimation(ChrEntityp);
        Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        break;
    })



#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## GUARD KNEEL
//==============================================================================

/* #define CMDNAME GUARD_KNEEL */
#define CMDNAME Kneel

#define DESCRIPTION \
/*******************************************************************************
* Make me kneel on one knee
*******************************************************************************/

#define PARAMS /*NONE*/

_AI_CMD(
    {
        check_if_able_to_then_kneel(ChrEntityp);
        Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        break;
    })



#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## PLAY ANIMATION
//==============================================================================

/* #define CMDNAME GUARD_PLAY_ANIMATION */
#define CMDNAME PlayAnimation

#define DESCRIPTION \
/*******************************************************************************
* Play an animation
********************************************************************************
* @param      START/END: set to -1/-1 will playback the entire animation length.
* @param      INTERPOL_TIME: will set how long it will take to transition
*                            from the previous state.
* @param      ANIMATION_ID: Use ANIM_# enum
* @param      BITFIELD: Use ANIM_# Bit flags
* @exception: If interpolation time is too low it may crash! - use 16 if unsure.
*             Start/End Keyframe 30 tick units.
*             Interpolation use 60 tick units.
*******************************************************************************/

#define PARAMS DBYTE(ANIMATION_ID), DBYTE(START_TIME30),DBYTE(END_TIME30), BYTE(BITFIELD), BYTE(INTERPOL_TIME60)

_AI_CMD(
    {
        AIRecord *ai = AiListp + Offset;
        s32       startframe, anim_id, zero, endframe;
        anim_id    = CharArrayTo16(ai->val,0);
        startframe = CharArrayTo16(ai->val,2);
        endframe   = CharArrayTo16(ai->val,4);
        if (startframe == (u16)-1)
        {
            startframe = 0;
        }
        if (endframe == (u16)-1)
        {
            endframe = -1;
        }
        if (ChrEntityp)
        {
            check_if_able_to_then_perform_animation(ChrEntityp, anim_id, startframe, endframe, ai->val[6], ai->val[7]);
        }
        else if (AircraftEntityp)
        {
            zero = 0;
            modelSetAnimation(AircraftEntityp->model, animation_table_ptrs2[anim_id], zero, startframe, 0.5f, (s32)ai->val[7]);
            if (endframe >= 0)
            {
                modelSetAnimEndFrame(AircraftEntityp->model, endframe);
            }
        }
        Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        break;
    })

//POLYMORPHS

#undef DESCRIPTION
#define DESCRIPTION \
/*******************************************************************************
* Play an animation
*******************************************************************************/

_AI_CMD_POLYMORPH(,
                  PlayAnimationSimple,
                  (ANIMATION_ID, -1, -1,
                    ANIM_UNKNOWN | ANIM_LOOP_HOLD_LAST_FRAME,
                    ANIM_DEFAULT_INTERPOLATION),
                  (ANIMATION_ID))

#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## IF GUARD PLAYING ANIMATION
//==============================================================================

/* #define CMDNAME IF_GUARD_PLAYING_ANIMATION */
#define CMDNAME IFPlayingAnimation

#define DESCRIPTION \
/*******************************************************************************
* Am I in animation playback state? (ACT_ANIM)
********************************************************************************
* @return     Continue execution from LABEL if TRUE
*******************************************************************************/

#define PARAMS BYTE(GOTOLABEL)

_AI_CMD(
    {
        AIRecord1 *ai = (AiListp + Offset);
        if (ChrEntityp->actiontype == ACT_ANIM)
        {
            Offset = chraiGoToLabel(AiListp, Offset, ai->val);
        }
        else
        {
            Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        }
        break;
    })



#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## GUARD POINTS AT BOND
//==============================================================================

/* #define CMDNAME GUARD_POINTS_AT_BOND */
#define CMDNAME PointAtBond

#define DESCRIPTION \
/*******************************************************************************
* Point only if Bond is directly in front of me, else command is
* ignored
********************************************************************************
* @exception: Global AI List GAILIST_ATTACK_BOND skips this command
*             if bit field flag FLAGS2_DONT_POINT_AT_BOND is on
*******************************************************************************/

#define PARAMS /*NONE*/

_AI_CMD(
    {
        chrTrySurprisedOneHand(ChrEntityp);
        Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        break;
    })



#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## GUARD LOOKS AROUND SELF
//==============================================================================

/* #define CMDNAME GUARD_LOOKS_AROUND_SELF */
#define CMDNAME LookSurprised

#define DESCRIPTION \
/*******************************************************************************
* Make me look around when shots land near me
*******************************************************************************/

#define PARAMS /*NONE*/

_AI_CMD(
    {
        chrTrySurprisedLookAround(ChrEntityp);
        Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        break;
    })



#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## GUARD TRY SIDESTEPPING
//==============================================================================

/* #define CMDNAME GUARD_TRY_SIDESTEPPING */
#define CMDNAME TRYSidestepping

#define DESCRIPTION \
/*******************************************************************************
* Try getting me to sidestep
********************************************************************************
* @return     Continue execution from LABEL if successful
* @exception: Direction is random
*******************************************************************************/

#define PARAMS BYTE(GOTOLABEL)

_AI_CMD(
                {
    AIRecord1 *ai = AiListp + Offset;
    if (actor_steps_sideways(ChrEntityp))
    {
        Offset = chraiGoToLabel(AiListp, Offset, ai->val);
    }
    else
    {
        Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
    }
    break;
    })



#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## GUARD TRY HOPPING SIDEWAYS
//==============================================================================

/* #define CMDNAME GUARD_TRY_HOPPING_SIDEWAYS */
#define CMDNAME TRYSideHopping

#define DESCRIPTION \
/*******************************************************************************
* Try getting me to hop sideways
********************************************************************************
* @return     Continue execution from LABEL if successful
* @exception: Direction is random
*******************************************************************************/

#define PARAMS BYTE(GOTOLABEL)

_AI_CMD(
    {
        AIRecord1 *ai = AiListp + Offset;
        if (actor_hops_sideways(ChrEntityp))
        {
            Offset = chraiGoToLabel(AiListp, Offset, ai->val);
        }
        else
        {
            Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        }
        break;
    })



#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## GUARD TRY RUNNING TO SIDE
//==============================================================================

/* #define CMDNAME GUARD_TRY_RUNNING_TO_SIDE */
#define CMDNAME TRYSideRunning

#define DESCRIPTION \
/*******************************************************************************
* Try getting me to Run sideways of Bond
********************************************************************************
* @return     Continue execution from LABEL if successful
* @exception: Direction is random
*******************************************************************************/

#define PARAMS BYTE(GOTOLABEL)

_AI_CMD(
    {
        AIRecord1 *ai = AiListp + Offset;
        if (actor_Runs_sideways(ChrEntityp))
        {
            Offset = chraiGoToLabel(AiListp, Offset, ai->val);
        }
        else
        {
            Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        }
        break;
    })



#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## GUARD TRY FIRING WALK
//==============================================================================

/* #define CMDNAME GUARD_TRY_FIRING_WALK */
#define CMDNAME TRYFiringWalk

#define DESCRIPTION \
/*******************************************************************************
* Try getting me to walk and fire at Bond
********************************************************************************
* @return     Continue execution from LABEL if successful
* @exception: Bond needs to be at long distance away from guard to work
*******************************************************************************/

#define PARAMS BYTE(GOTOLABEL)

_AI_CMD(
    {
        AIRecord1 *ai = AiListp + Offset;
        if (actor_walks_and_fires(ChrEntityp))
        {
            Offset = chraiGoToLabel(AiListp, Offset, ai->val);
        }
        else
        {
            Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        }
        break;
    })



#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## GUARD TRY FIRING RUN
//==============================================================================

/* #define CMDNAME GUARD_TRY_FIRING_RUN */
#define CMDNAME TRYFiringRun

#define DESCRIPTION \
/*******************************************************************************
* Try getting me to Run and fire at Bond,
********************************************************************************
* @return     Continue execution from LABEL if successful
* @exception: Bond needs to be at long distance away from guard to work
*******************************************************************************/

#define PARAMS BYTE(GOTOLABEL)

_AI_CMD(
    {
        AIRecord1 *ai = AiListp + Offset;
        if (actor_runs_and_fires(ChrEntityp))
        {
            Offset = chraiGoToLabel(AiListp, Offset, ai->val);
        }
        else
        {
            Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        }
        break;
    })



#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## GUARD TRY FIRING ROLL
//==============================================================================

/* #define CMDNAME GUARD_TRY_FIRING_ROLL */
#define CMDNAME TRYFiringRoll

#define DESCRIPTION \
/*******************************************************************************
* Try getting me to roll on ground then fire at Bond
********************************************************************************
* @return     Continue execution from LABEL if successful
* @exception: Bond cannot be too close to guard or it will not work
*******************************************************************************/

#define PARAMS BYTE(GOTOLABEL)

_AI_CMD(
    {
        AIRecord1 *ai = AiListp + Offset;
        if (actor_rolls_fires_crouched(ChrEntityp))
        {
            Offset = chraiGoToLabel(AiListp, Offset, ai->val);
        }
        else
        {
            Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        }
        break;
    })



#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## GUARD TRY FIRE OR AIM AT TARGET
//==============================================================================

/* #define CMDNAME GUARD_TRY_FIRE_OR_AIM_AT_TARGET */
#define CMDNAME TRYFireOrAimAtTarget

#define DESCRIPTION \
/*******************************************************************************
* Try making me aim/fire their weapon at TARGET
********************************************************************************
* @return     Continue execution from LABEL if successful
* @param      TARGET: 16bit ID
* @param      BITFIELD: 16bit Target Type
* @exception: Use TARGET_# flags to set the target type (pad/Bond/chr) or use
*             Separated commands e.g. GUARD_TRYFIRE_AT_PAD
*******************************************************************************/

#define PARAMS DBYTE(BITFIELD), DBYTE(TARGET), BYTE(GOTOLABEL)

_AI_CMD(
    {
        AIRecord *ai         = AiListp + Offset;
        s32       targetid   = CharArrayTo16(ai->val,2);
        s32       targettype = CharArrayTo16(ai->val,0);
        if (actor_aim_at_actor(ChrEntityp, targettype, targetid))
        {
            Offset = chraiGoToLabel(AiListp, Offset, ai->val[4]);
        }
        else
        {
            Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        }
        break;
    })

//POLYMORPHS

#undef DESCRIPTION
#define DESCRIPTION \
/*******************************************************************************
* Try making me aim/fire their weapon at ...
********************************************************************************
* @return     Continue execution from LABEL if successful
* @param      TARGET: 16bit ID
*******************************************************************************/

_AI_CMD_POLYMORPH(,
                  TRYFireAtPad,
                  (TARGET_PAD, TARGET, GOTOLABEL),
                  (TARGET, GOTOLABEL))
_AI_CMD_POLYMORPH(,
                  TRYAimAtPad,
                  (TARGET_PAD | TARGET_AIM_ONLY, TARGET, GOTOLABEL),
                  (TARGET, GOTOLABEL))
_AI_CMD_POLYMORPH(,
                  TRYFireAtBond,
                  (TARGET_BOND, 0, GOTOLABEL),
                  (GOTOLABEL))
_AI_CMD_POLYMORPH(,
                  TRYAimAtBond,
                  (TARGET_BOND | TARGET_AIM_ONLY, 0, GOTOLABEL),
                  (GOTOLABEL))

#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## GUARD TRY FIRE OR AIM AT TARGET KNEEL
//==============================================================================

/* #define CMDNAME GUARD_TRY_FIRE_OR_AIM_AT_TARGET_KNEEL */
#define CMDNAME TRYFireOrAimAtTargetKneel

#define DESCRIPTION \
/*******************************************************************************
* Try making me kneel and aim/fire at TARGET
********************************************************************************
* @return     Continue execution from LABEL if successful
* @param      TARGET: 16bit ID
* @param      BITFIELD: 16bit Target Type
* @exception: Use TARGET_# flags to set the target type (pad/Bond/chr)
*******************************************************************************/

#define PARAMS DBYTE(BITFIELD), DBYTE(TARGET), BYTE(GOTOLABEL)

_AI_CMD(
    {
        AIRecord *ai         = AiListp + Offset;
        s32       targetid   = CharArrayTo16(ai->val,2);
        s32       targettype = CharArrayTo16(ai->val,0);
        if (actor_kneel_aim_at_actor(ChrEntityp, targettype, targetid))
        {
            Offset = chraiGoToLabel(AiListp, Offset, ai->val[4]);
        }
        else
        {
            Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        }
        break;
    })

//POLYMORPHS

#undef DESCRIPTION
#define DESCRIPTION \
/*******************************************************************************
* Make me kneel and aim/fire at ...
********************************************************************************
* @return     Continue execution from LABEL if successful
* @param      TARGET: 16bit ID
*******************************************************************************/

_AI_CMD_POLYMORPH(,
                  TRYFireAtPadKneeling,
                  (TARGET_PAD, TARGET, GOTOLABEL),
                  (TARGET, GOTOLABEL))
_AI_CMD_POLYMORPH(,
                  TRYAimAtPadKneeling,
                  (TARGET_PAD | TARGET_AIM_ONLY, TARGET, GOTOLABEL),
                  (TARGET, GOTOLABEL))
_AI_CMD_POLYMORPH(,
                  TRYFireAtBondKneeling,
                  (TARGET_BOND, 0, GOTOLABEL),
                  (GOTOLABEL))
_AI_CMD_POLYMORPH(,
                  TRYAimAtBondKneeling,
                  (TARGET_BOND | TARGET_AIM_ONLY, 0, GOTOLABEL),
                  (GOTOLABEL))

#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## GUARD TRY FIRE OR AIM AT TARGET UPDATE
//==============================================================================

/* #define CMDNAME GUARD_TRY_FIRE_OR_AIM_AT_TARGET_UPDATE */
#define CMDNAME TRYFireOrAimAtTargetUpdate

#define DESCRIPTION \
/*******************************************************************************
* Try updating my TARGET
********************************************************************************
* @return     Continue execution from LABEL if successful
* @param      BITFIELD: 16bit Target Type Use TARGET_# flags to set the
*                       target type (pad/Bond/chr)
* @exception: Only works if guard is currently aiming at a target.
*             Use TARGET_# flags to set the target type (pad/Bond/chr)
*******************************************************************************/

#define PARAMS DBYTE(BITFIELD), DBYTE(TARGET), BYTE(GOTOLABEL)

_AI_CMD(
    {
        AIRecord *ai         = AiListp + Offset;
        s32       targetid   = CharArrayTo16(ai->val,2);
        s32       targettype = CharArrayTo16(ai->val,0);
        if (actor_fire_or_aim_at_target_update(ChrEntityp, targettype, targetid))
        {
            Offset = chraiGoToLabel(AiListp, Offset, ai->val[4]);
        }
        else
        {
            Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        }
        break;
    })

//POLYMORPHS

#undef DESCRIPTION
#define DESCRIPTION \
/*******************************************************************************
* Try updating my TARGET
********************************************************************************
* @return     Continue execution from LABEL if successful
* @param      TARGET 16bit ID
*******************************************************************************/

_AI_CMD_POLYMORPH(,
                    TRYFireAtPadUpdate,
                    (TARGET_PAD, TARGET, GOTOLABEL),
                    (TARGET, GOTOLABEL))
_AI_CMD_POLYMORPH(,
                  TRYAimAtPadUpdate,
                    (TARGET_PAD | TARGET_AIM_ONLY, TARGET, GOTOLABEL),
                    (TARGET, GOTOLABEL))
_AI_CMD_POLYMORPH(,
                    TRYFireAtBondUpdate,
                    (TARGET_BOND, 0, GOTOLABEL),
                    (GOTOLABEL))
_AI_CMD_POLYMORPH(,
                    TRYAimAtBondUpdate,
                    (TARGET_BOND | TARGET_AIM_ONLY, 0, GOTOLABEL),
                    (GOTOLABEL))

#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## GUARD TRY FACING TARGET
//==============================================================================

/* #define CMDNAME GUARD_TRY_FACING_TARGET */
#define CMDNAME TRYFacingTarget

#define DESCRIPTION \
/*******************************************************************************
* Try making me continuously face TARGET
********************************************************************************
* @return     Continue execution from LABEL if successful
* @param      TARGET: 16bit ID
* @param      BITFIELD: 16bit Target Type
* @exception: If guard was shot while facing target, guard will snap out of facing
*             state.
*             Use TARGET_# flags to set the target type (pad/Bond/chr)
*             Can NOT use TARGET_AIM_ONLY flag
*******************************************************************************/

#define PARAMS DBYTE(BITFIELD),DBYTE(TARGET),BYTE(GOTOLABEL)

_AI_CMD(
    {
        AIRecord *ai         = AiListp + Offset;
        s32       targetid   = CharArrayTo16(ai->val,2);
        s32       targettype = CharArrayTo16(ai->val,0);
        if (check_set_actor_standing_still(ChrEntityp, targettype, targetid))
        {
            Offset = chraiGoToLabel(AiListp, Offset, ai->val[4]);
        }
        else
        {
            Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        }
        break;
    })

//POLYMORPHS

_AI_CMD_POLYMORPH(,
                    TRYFacingPad,
                    (TARGET_PAD, TARGET, GOTOLABEL),
                    (TARGET, GOTOLABEL))
_AI_CMD_POLYMORPH(,
                    TRYFacingBond,
                    (TARGET_BOND, 0, GOTOLABEL),
                    (GOTOLABEL))

#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## CHR HIT BODY PART WITH ITEM DAMAGE
//==============================================================================

/* #define CMDNAME CHR_HIT_BODY_PART_WITH_ITEM_DAMAGE */
#define CMDNAME HitChrWithItem

#define DESCRIPTION \
/*******************************************************************************
* Hit Chr body part with item damage, play reaction to hit location
********************************************************************************
* @exception: Does NOT trigger items firing sfx.
* @param ITEM_NUM: Item damage uses body part damage modifier.
* @param PART_NUM: Use HIT_# define for hit part number
* @param  CHR_NUM: ID of Chr (0-255)
*******************************************************************************/

#define PARAMS BYTE(CHR_NUM),BYTE(PART_NUM),BYTE(ITEM_NUM)

_AI_CMD(
    {
        AIRecord * ai  = AiListp + Offset;
        ChrRecord *chr = chrFindById(ChrEntityp, ai->val[0]);
        vec3d      vec = New_Vector();
        if (chr && chr->prop)
        {
            handles_shot_actors(chr, (s8)ai->val[1], &vec, ai->val[2], NULL);
        }
        Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        break;
    })

//POLYMORPHS

_AI_CMD_POLYMORPH(,
                    HitMeWithItem,
                    (CHR_SELF, PART_NUM, ITEM_NUM),
                    (PART_NUM, ITEM_NUM))

#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## CHR HIT CHR BODY PART WITH HELD ITEM
//==============================================================================

/* #define CMDNAME CHR_HIT_CHR_BODY_PART_WITH_HELD_ITEM */
#define CMDNAME ChrHitChr

#define DESCRIPTION \
/*******************************************************************************
* Chr hits Target chrs body part with held item, play reaction to hit location
********************************************************************************
* @exception: Does NOT trigger items firing sfx or Chr's firing animation
* @param ITEM_NUM: Item damage uses body part damage modifier.
* @param PART_NUM: Use HIT_# define for hit part number
* @param  CHR_NUM: ID of Chr (0-255)
*******************************************************************************/

#define PARAMS BYTE(CHR_NUM),BYTE(CHR_NUM_TARGET),BYTE(PART_NUM)

_AI_CMD(
    {
        AIRecord * ai   = AiListp + Offset;
        ChrRecord *chr1 = chrFindById(ChrEntityp, ai->val[0]);
        ChrRecord *chr2 = chrFindById(ChrEntityp, ai->val[1]);
        if (chr1 && chr2 && chr1->prop && chr2->prop)
        {
            PropRecord *     prop = chrGetEquippedWeaponPropWithCheck(chr1, GUNRIGHT);
            WeaponObjRecord *weapon;
            vec3d            vec = New_Vector();
            if (!prop)
            {
                prop = chrGetEquippedWeaponPropWithCheck(chr1, GUNLEFT);
            }
            if (prop)
            {
                vec.x = chr2->prop->pos.x - chr1->prop->pos.x;
                vec.y = chr2->prop->pos.y - chr1->prop->pos.y;
                vec.z = chr2->prop->pos.z - chr1->prop->pos.z;
                guNormalize(&vec.x, &vec.y, &vec.z);
                if (prop)
                {
                }
                weapon = prop->weapon;
                handles_shot_actors(chr2, (s8)ai->val[2], &vec, weapon->weaponnum, 0);
            }
        }
        Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        break;
    })

//POLYMORPHS

_AI_CMD_POLYMORPH(,
                    ChrHitMe,
                    (CHR_NUM, CHR_SELF, PART_NUM),
                    (CHR_NUM, PART_NUM))
_AI_CMD_POLYMORPH(,
                    IHitChr,
                    (CHR_SELF, CHR_NUM_TARGET, PART_NUM),
                    (CHR_NUM_TARGET, PART_NUM))

#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## GUARD TRY THROWING GRENADE
//==============================================================================

/* #define CMDNAME GUARD_TRY_THROWING_GRENADE */
#define CMDNAME TRYThrowingGrenade

#define DESCRIPTION \
/*******************************************************************************
* Try getting me to throw a grenade at Bond
********************************************************************************
* @return     Continue execution from LABEL if successful
* @exception: A random number is generated and compared against chr->grenadeprob,
*             if number is less than grenadeprob throw grenade and Go to LABEL,
*             else do nothing.
*             Chr->grenadeprob default is 0 - to change use setup
*             object 12 or GUARD_SET_GRENADE_PROBABILITY
*******************************************************************************/

#define PARAMS BYTE(GOTOLABEL)

_AI_CMD(
    {
        AIRecord1 *ai = AiListp + Offset;
        if (actor_draws_throws_grenade_at_player_if_possible(ChrEntityp))
        {
            Offset = chraiGoToLabel(AiListp, Offset, ai->val);
        }
        else
        {
            Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        }
        break;
    })



#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## GUARD TRY DROPPING ITEM
//==============================================================================

/* #define CMDNAME GUARD_TRY_DROPPING_ITEM */
#define CMDNAME TRYDroppingItem

#define DESCRIPTION \
/*******************************************************************************
* Try spawning and drop item with Prop model
********************************************************************************
* @return     Continue execution from LABEL if successful
* @exception: Dropped item uses item type (08) with model number -
*             They can be picked up.
*             Grenade/Mines will be dropped live - this is used for cradle
*             (ailist #0411)
*******************************************************************************/

#define PARAMS DBYTE(PROP_NUM),BYTE(ITEM_NUM),BYTE(GOTOLABEL)

_AI_CMD(
    {
        AIRecord *ai       = AiListp + Offset;
        u16       modelnum = CharArrayTo16(ai->val,0);
        if (chrDropItem(ChrEntityp, modelnum, ai->val[2]))
        {
            Offset = chraiGoToLabel(AiListp, Offset, ai->val[3]);
        }
        else
        {
            Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        }
        break;
    })



#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## GUARD RUNS TO PAD
//==============================================================================

/* #define CMDNAME GUARD_RUNS_TO_PAD */
#define CMDNAME RunToPad

#define DESCRIPTION \
/*******************************************************************************
* Makes me Run to pad
********************************************************************************
* @param 16bit PAD ID
*******************************************************************************/

#define PARAMS DBYTE(PAD)

_AI_CMD(
    {
        AIRecord *ai  = AiListp + Offset;
        u16       pad = CharArrayTo16(ai->val,0);
        chrGoToPad(ChrEntityp, pad, SPEED_RUN);
        Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        break;
    })



#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## GUARD RUNS TO PAD PRESET
//==============================================================================

/* #define CMDNAME GUARD_RUNS_TO_PAD_PRESET */
#define CMDNAME RunToPadPreset

#define DESCRIPTION \
/*******************************************************************************
* Makes me Run to my Preset Pad
*******************************************************************************/

#define PARAMS /*NONE*/

/* PD uses GoTo Pad (speed) which seems better
switch (AI->val[0])
{
    case SPEED_WALK:
        chrGoToPad(ChrEntityp, ChrEntityp->padpreset1, SPEED_WALK);
        break;
    case SPEED_RUN:
        etc...
*/
_AI_CMD(
    {
        chrGoToPad(ChrEntityp, ChrEntityp->padpreset1, SPEED_RUN);
        Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        break;
    })



#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## GUARD WALKS TO PAD
//==============================================================================

/* #define CMDNAME GUARD_WALKS_TO_PAD */
#define CMDNAME WalkToPad

#define DESCRIPTION \
/*******************************************************************************
* Makes me walk to pad
********************************************************************************
* @param 16bit PAD ID
*******************************************************************************/

#define PARAMS DBYTE(PAD)

_AI_CMD(
    {
        AIRecord *ai  = AiListp + Offset;
        u16       pad = CharArrayTo16(ai->val,0);
        chrGoToPad(ChrEntityp, pad, SPEED_WALK);
        Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        break;
    })



#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## GUARD SPRINTS TO PAD
//==============================================================================

/* #define CMDNAME GUARD_SPRINTS_TO_PAD */
#define CMDNAME SprintToPad

#define DESCRIPTION \
/*******************************************************************************
* Makes me Sprint to pad - don't ask me to do this too long
********************************************************************************
* @param 16bit PAD ID
*******************************************************************************/

#define PARAMS DBYTE(PAD)

_AI_CMD(
    {
        AIRecord *ai  = AiListp + Offset;
        u16       pad = CharArrayTo16(ai->val,0);
        chrGoToPad(ChrEntityp, pad, SPEED_SPRINT);
        Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        break;
    })



#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## GUARD START PATROL
//==============================================================================

/* #define CMDNAME GUARD_START_PATROL */
#define CMDNAME StartPatrol

#define DESCRIPTION \
/*******************************************************************************
* Makes me walk a predefined path within setup
********************************************************************************
* @exception: Usually paired with GAILIST_SIMPLE_GUARD_DEAF
*             or GAILIST_SIMPLE_GUARD
*******************************************************************************/

#define PARAMS BYTE(PATH_NUM)

_AI_CMD(
    {
        AIRecord *  ai   = AiListp + Offset;
        PathRecord *path = pathFindById(ai->val[0]);
        if_actor_able_set_on_path(ChrEntityp, path);
        Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        break;
    })



#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## GUARD SURRENDERS
//==============================================================================

/* #define CMDNAME GUARD_SURRENDERS */
#define CMDNAME Surrender

#define DESCRIPTION \
/*******************************************************************************
* Makes me surrender and drop all attached and held items
********************************************************************************
* @exception: Will NOT drop items embedded within guard
*******************************************************************************/

#define PARAMS /*NONE*/

_AI_CMD(
    {
        chrTrySurrender(ChrEntityp);
        Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        break;
    })



#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## GUARD REMOVE FADE
//==============================================================================

/* #define CMDNAME GUARD_REMOVE_FADE */
#define CMDNAME RemoveMe

#define DESCRIPTION \
/*******************************************************************************
* Makes me fade away - fade time is 90 ticks (1.5 seconds). When
* the fade finishes, automatically remove guard
********************************************************************************
* @exception: Guard collision is ignored during fade
*             Will NOT drop items
*******************************************************************************/

#define PARAMS /*NONE*/

_AI_CMD(
    {
        chrFadeOut(ChrEntityp);
        Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        break;
    })



#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## CHR REMOVE INSTANT
//==============================================================================

/* #define CMDNAME CHR_REMOVE_INSTANT */
#define CMDNAME ChrRemoveInstant

#define DESCRIPTION \
/*******************************************************************************
* Instantly remove CHR_NUM unlike RemoveMe
********************************************************************************
* @param      CHR_NUM: ID of Chr (0-255)
* @exception: Will NOT drop items
*******************************************************************************/

#define PARAMS BYTE(CHR_NUM)

_AI_CMD(
    {
        AIRecord * ai  = AiListp + Offset;
        ChrRecord *chr = chrFindById(ChrEntityp, ai->val[0]);
        if (chr && chr->prop)
        {
            chr->hidden |= CHRHIDDEN_REMOVE;
        }
        Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        break;
    })

//POLYMORPHS

#undef DESCRIPTION
#define DESCRIPTION \
/*******************************************************************************
* Instantly remove me unlike RemoveMe
********************************************************************************
* @exception: Will NOT drop items
*******************************************************************************/

_AI_CMD_POLYMORPH(,
                    RemoveMeInstantly,
                    (CHR_SELF),
                    ())

#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## GUARD TRY TRIGGERING ALARM AT PAD
//==============================================================================

/* #define CMDNAME GUARD_TRY_TRIGGERING_ALARM_AT_PAD */
#define CMDNAME TRYTriggeringAlarmAtPad

#define DESCRIPTION \
/*******************************************************************************
* Try to activate alarm assigned to pad
********************************************************************************
* @return     Continue execution from LABEL if successful
* @exception: Does NOT care what object type is at pad, as long as the object
*             is NOT destroyed.
*             Checks if guard is alive before activating alarm.
*             When triggering alarm, guard will be set to state ACT_STARTALARM
*             and play animation
*******************************************************************************/

#define PARAMS DBYTE(PAD),BYTE(GOTOLABEL)

_AI_CMD(
    {
        AIRecord *ai     = AiListp + Offset;
        u16       pad_id = CharArrayTo16(ai->val,0);
        if (chrTryStartAlarm(ChrEntityp, pad_id))
        {
            Offset = chraiGoToLabel(AiListp, Offset, ai->val[2]);
        }
        else
        {
            Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        }
        break;
    })



#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## ALARM ON
//==============================================================================

/* #define CMDNAME ALARM_ON */
#define CMDNAME AlarmOn

#define DESCRIPTION \
/*******************************************************************************
* Activates alarm
*******************************************************************************/

#define PARAMS /*NONE*/

_AI_CMD(
    {
        alarmActivate();
        Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        break;
    })

#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## ALARM OFF
//==============================================================================

/* #define CMDNAME ALARM_OFF */
#define CMDNAME AlarmOff

#define DESCRIPTION \
/*******************************************************************************
* Deactivates alarm
*******************************************************************************/

#define PARAMS /*NONE*/

_AI_CMD(
    {
        alarmDeactivate();
        Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        break;
    })

#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## REMOVED COMMAND27
//==============================================================================

/* #define CMDNAME REMOVED_COMMAND27 */
#define CMDNAME TRYRunFromBond

#define DESCRIPTION \
/*******************************************************************************
* Try running from Bond
********************************************************************************
* @return     Continue execution from LABEL if successful
*******************************************************************************/

#define PARAMS BYTE(GOTOLABEL)

_AI_CMD(
    {
    AIRecord1 *ai = AiListp + Offset;
    if (removed_animation_routine_27(ChrEntityp))
    {
        Offset = chraiGoToLabel(AiListp, Offset, ai->val);
    }
    else
    {
        Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
    }
    break;
})

#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## GUARD TRY RUNNING TO BOND POSITION
//==============================================================================

/* #define CMDNAME GUARD_TRY_RUNNING_TO_BOND_POSITION */
#define CMDNAME TRYRunToBond

#define DESCRIPTION \
/*******************************************************************************
* Try getting me to Run to Bond
********************************************************************************
* @return     Continue execution from LABEL if successful
* @exception: Guard has died or Bond is at an unreachable area (no navigation
*             pads in area)
*******************************************************************************/

#define PARAMS BYTE(GOTOLABEL)

_AI_CMD(
    {
        AIRecord1 *ai = AiListp + Offset;
        if (chrGoToBond(ChrEntityp, SPEED_RUN))
        {
            Offset = chraiGoToLabel(AiListp, Offset, ai->val);
        }
        else
        {
            Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        }
        break;
    })



#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## GUARD TRY WALKING TO BOND POSITION
//==============================================================================

/* #define CMDNAME GUARD_TRY_WALKING_TO_BOND_POSITION */
#define CMDNAME TRYWalkToBond

#define DESCRIPTION \
/*******************************************************************************
* Try getting me to walk to Bond
********************************************************************************
* @return     Continue execution from LABEL if successful
* @exception: Guard has died or Bond is at an unreachable area (no navigation
*             pads in area)
*******************************************************************************/

#define PARAMS BYTE(GOTOLABEL)

_AI_CMD(
    {
        AIRecord *ai = AiListp + Offset;
        if (chrGoToBond(ChrEntityp, SPEED_WALK))
        {
            Offset = chraiGoToLabel(AiListp, Offset, ai->val[0]);
        }
        else
        {
            Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        }
        break;
    })



#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## GUARD TRY SPRINTNING TO BOND POSITION
//==============================================================================

/* #define CMDNAME GUARD_TRY_SPRINTNING_TO_BOND_POSITION */
#define CMDNAME TRYSprintToBond

#define DESCRIPTION \
/*******************************************************************************
* Try getting me to Sprint to Bond
********************************************************************************
* @return     Continue execution from LABEL if successful
* @exception: Guard has died or Bond is at an unreachable area (no navigation
*             pads in area)
*******************************************************************************/

#define PARAMS BYTE(GOTOLABEL)

_AI_CMD(
    {
        AIRecord *ai = AiListp + Offset;
        if (chrGoToBond(ChrEntityp, SPEED_SPRINT))
        {
            Offset = chraiGoToLabel(AiListp, Offset, ai->val[0]);
        }
        else
        {
            Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        }
        break;
    })



#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## REMOVED COMMAND2B
//==============================================================================

/* #define CMDNAME REMOVED_COMMAND2B */
#define CMDNAME TRYFindCover

#define DESCRIPTION \
/*******************************************************************************
* Try finding cover
********************************************************************************
* @return     Continue execution from LABEL if successful
*******************************************************************************/

#define PARAMS BYTE(GOTOLABEL)

_AI_CMD(
    {
        AIRecord *ai = AiListp + Offset;
        if (removed_animation_routine_2B(ChrEntityp))
        {
            Offset = chraiGoToLabel(AiListp, Offset, ai->val[0]);
        }
        else
        {
            Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        }
        break;
    })



#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## GUARD TRY RUNNING TO CHR POSITION
//==============================================================================

/* #define CMDNAME GUARD_TRY_RUNNING_TO_CHR_POSITION */
#define CMDNAME TRYRunToChr

#define DESCRIPTION \
/*******************************************************************************
* Try getting me to Run to CHR_NUM
********************************************************************************
* @return     Continue execution from LABEL if successful
* @param      CHR_NUM: ID of Chr (0-255)
* @exception: Guard has died or CHR_NUM is at an unreachable area (no navigation
*             pads in area) or CHR_NUM is dead
*******************************************************************************/

#define PARAMS BYTE(CHR_NUM), BYTE(GOTOLABEL)

_AI_CMD(
    {
        AIRecord *ai = AiListp + Offset;
        if (chrGoToChr(ChrEntityp, ai->val[0], SPEED_RUN))
        {
            Offset = chraiGoToLabel(AiListp, Offset, ai->val[1]);
        }
        else
        {
            Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        }
        break;
    })

//POLYMORPHS

_AI_CMD_POLYMORPH(,
                    TRYRunToPresetChr,
                    (CHR_PRESET, GOTOLABEL),
                    (GOTOLABEL))

#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## GUARD TRY WALKING TO CHR POSITION
//==============================================================================

/* #define CMDNAME GUARD_TRY_WALKING_TO_CHR_POSITION */
#define CMDNAME TRYWalkToChr

#define DESCRIPTION \
/*******************************************************************************
* Try getting me to walk to CHR_NUM
********************************************************************************
* @return     Continue execution from LABEL if successful
* @param      CHR_NUM: ID of Chr (0-255)
* @exception: Guard has died or CHR_NUM is at an unreachable area (no navigation
*             pads in area) or CHR_NUM is dead
*******************************************************************************/

#define PARAMS BYTE(CHR_NUM), BYTE(GOTOLABEL)

_AI_CMD(
    {
        AIRecord *ai = AiListp + Offset;
        if (chrGoToChr(ChrEntityp, ai->val[0], SPEED_WALK))
        {
            Offset = chraiGoToLabel(AiListp, Offset, ai->val[1]);
        }
        else
        {
            Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        }
        break;
    })

//POLYMORPHS

_AI_CMD_POLYMORPH(,
                    TRYWalkToPresetChr,
                    (CHR_PRESET, GOTOLABEL),
                    (GOTOLABEL))

#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## GUARD TRY SPRINTNING TO CHR POSITION
//==============================================================================

/* #define CMDNAME GUARD_TRY_SPRINTNING_TO_CHR_POSITION */
#define CMDNAME TRYSprintToChr

#define DESCRIPTION \
/*******************************************************************************
* Try getting me to Sprint to CHR_NUM
********************************************************************************
* @return     Continue execution from LABEL if successful
* @param      CHR_NUM: ID of Chr (0-255)
* @exception: Guard has died or CHR_NUM is at an unreachable area (no navigation
*             pads in area) or CHR_NUM is dead
*******************************************************************************/

#define PARAMS BYTE(CHR_NUM), BYTE(GOTOLABEL)

_AI_CMD(
    {
        AIRecord *ai = AiListp + Offset;
        if (chrGoToChr(ChrEntityp, ai->val[0] & 0xff, SPEED_SPRINT))
        {
            Offset = chraiGoToLabel(AiListp, Offset, ai->val[1]);
        }
        else
        {
            Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        }
        break;
    })

//POLYMORPHS

_AI_CMD_POLYMORPH(,
                    TRYSprintToPresetChr,
                    (CHR_PRESET, GOTOLABEL),
                    (GOTOLABEL))


#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## IF GUARD HAS STOPPED MOVING
//==============================================================================

/* #define CMDNAME IF_GUARD_HAS_STOPPED_MOVING */
#define CMDNAME IFImOnPatrolOrStopped

#define DESCRIPTION \
/*******************************************************************************
* Have I stopped moving?
********************************************************************************
* @return     Continue execution from LABEL if TRUE
* @exception: Check if guard is NOT looking for Bond or if guard has finished
*             moving to destination
*******************************************************************************/

#define PARAMS BYTE(GOTOLABEL)

_AI_CMD(
    {
        AIRecord1 *ai = AiListp + Offset;
        if (chrHasStoppedOrPatroling(ChrEntityp))
        {
            Offset = chraiGoToLabel(AiListp, Offset, ai->val);
        }
        else
        {
            Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        }
        break;
    })



#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## IF CHR DYING OR DEAD
//==============================================================================

/* #define CMDNAME IF_CHR_DYING_OR_DEAD */
#define CMDNAME IFChrDyingOrDead

#define DESCRIPTION \
/*******************************************************************************
* Is CHR_NUM dying or dead?
********************************************************************************
* @param      CHR_NUM: ID of Chr (0-255)
* @return     Continue execution from LABEL if TRUE
*******************************************************************************/

#define PARAMS BYTE(CHR_NUM), BYTE(GOTOLABEL)

_AI_CMD(
    {
        AIRecord * ai  = AiListp + Offset;
        ChrRecord *chr = chrFindById(ChrEntityp, ai->val[0]);
        if (!chr || chrIsDead(chr))
        {
            Offset = chraiGoToLabel(AiListp, Offset, ai->val[1]);
        }
        else
        {
            Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        }
        break;
    })

//POLYMORPHS

_AI_CMD_POLYMORPH(,
                    IFImDyingOrDead,
                    (CHR_SELF, GOTOLABEL),
                    (GOTOLABEL))

#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## IF CHR DOES NOT EXIST
//==============================================================================

/* #define CMDNAME IF_CHR_DOES_NOT_EXIST */
#define CMDNAME IFChrDoesNotExist

#define DESCRIPTION \
/*******************************************************************************
* Does CHR_NUM NOT exist? (died and faded/not spawned)
********************************************************************************
* @param      CHR_NUM: ID of Chr (0-255)
* @return     Continue execution from LABEL if TRUE
* @exception: This command is used to check if chr has finished dying animation
*             and faded away, or chr num is free
*******************************************************************************/

#define PARAMS BYTE(CHR_NUM), BYTE(GOTOLABEL)

_AI_CMD(
    {
        AIRecord * ai  = AiListp + Offset;
        ChrRecord *chr = chrFindById(ChrEntityp, ai->val[0]);
        if (!chr || !chr->model)
        {
            Offset = chraiGoToLabel(AiListp, Offset, ai->val[1]);
        }
        else
        {
            Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        }
        break;
    })

//POLYMORPHS

_AI_CMD_POLYMORPH(,
                  IFIDoNotExist,
                  (CHR_SELF, GOTOLABEL),
                  (GOTOLABEL))

_AI_CMD_POLYMORPH(,
                  IFMyCloneDoesNotExist,
                  (CHR_CLONE, GOTOLABEL),
                  (GOTOLABEL))

#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## IF GUARD SEES BOND
//==============================================================================

/* #define CMDNAME IF_GUARD_SEES_BOND */
#define CMDNAME IFISeeBond

#define DESCRIPTION \
/*******************************************************************************
* Can I see Bond?
********************************************************************************
* @return     Continue execution from LABEL if TRUE
* @exception: Uses chr->visionrange while checking for Bond. Once Bond has been
*             spotted, check if Bond and guard are within line of sight
*             (ignores facing direction).
*             injured guards will also set spotted Bond state (won't work with
*             invincible/armoured guards).
*             If Bond breaks line of sight, do not Go to LABEL. If Bond has broken
*             line of sight for more than 10 seconds, reset spotted bond state.
*             When using with IF_GUARD_WAS_SHOT_OR_SEEN_WITHIN_LAST_10_SECS, make
*             sure IF_GUARD_SEES_BOND takes priority over
*             IF_GUARD_WAS_SHOT_OR_SEEN_WITHIN_LAST_10_SECS
*******************************************************************************/

#define PARAMS BYTE(GOTOLABEL)

_AI_CMD(
    {
        AIRecord1 *ai = AiListp + Offset;
        if (chrCheckTargetInSight(ChrEntityp))
        {
            Offset = chraiGoToLabel(AiListp, Offset, ai->val);
        }
        else
        {
            Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        }
        break;
    })



#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## RANDOM GENERATE SEED
//==============================================================================

/* #define CMDNAME RANDOM_GENERATE_SEED */
#define CMDNAME SetNewRandom

#define DESCRIPTION \
/*******************************************************************************
* Generate a random byte and store to My/BG Random
********************************************************************************
* @exception: Random byte range is 00-FF (unsigned)
*******************************************************************************/

#define PARAMS /*NONE*/

_AI_CMD(
    {
        ChrEntityp->random = randomGetNext();
        Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        break;
    })



#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## IF RANDOM SEED LESS THAN
//==============================================================================

/* #define CMDNAME IF_RANDOM_SEED_LESS_THAN */
#define CMDNAME IFRandomLessThan

#define DESCRIPTION \
/*******************************************************************************
* Is My/BG Random Less Than BYTE?
********************************************************************************
* @return     Continue execution from LABEL if TRUE
* @exception: Compare is unsigned
*******************************************************************************/

#define PARAMS BYTE(BYTE), BYTE(GOTOLABEL)

_AI_CMD(
    {
        AIRecord *ai = AiListp + Offset;
        if (ai->val[0] > ChrEntityp->random)
        {
            Offset = chraiGoToLabel(AiListp, Offset, ai->val[1]);
        }
        else
        {
            Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        }
        break;
    })

//POLYMORPHS

_AI_CMD_POLYMORPH(SetNewRandom(),
                  IFNewRandomLessThan,
                  (BYTE, GOTOLABEL),
                  (BYTE, GOTOLABEL))



#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## IF RANDOM SEED GREATER THAN
//==============================================================================

/* #define CMDNAME IF_RANDOM_SEED_GREATER_THAN */
#define CMDNAME IFRandomGreaterThan

#define DESCRIPTION \
/*******************************************************************************
* Is My/BG Random Greater Than BYTE?
********************************************************************************
* @return     Continue execution from LABEL if TRUE
* @exception: Compare is unsigned
*******************************************************************************/

#define PARAMS BYTE(BYTE), BYTE(GOTOLABEL)

_AI_CMD(
    {
        AIRecord *ai = AiListp + Offset;
        if (ai->val[0] < ChrEntityp->random)
        {
            Offset = chraiGoToLabel(AiListp, Offset, ai->val[1]);
        }
        else
        {
            Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        }
        break;
    })

//POLYMORPHS

_AI_CMD_POLYMORPH(SetNewRandom(),
                  IFNewRandomGreaterThan,
                  (BYTE, GOTOLABEL),
                  (BYTE, GOTOLABEL))




#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## IF ALARM IS ON UNUSED
//==============================================================================

/* #define CMDNAME IF_ALARM_IS_ON_UNUSED */
#define CMDNAME IFICanHearAlarm

#define DESCRIPTION \
/*******************************************************************************
* Is alarm is activated and in range?
********************************************************************************
* @return     Continue execution from LABEL if TRUE
* @exception: This command works but is unused in retail game, use IF_ALARM_IS_ON
*             instead.
*******************************************************************************/

#define PARAMS BYTE(GOTOLABEL)

_AI_CMD(
    {
        AIRecord *ai = AiListp + Offset;
        if (chrCanHearAlarm(ChrEntityp))
        {
            Offset = chraiGoToLabel(AiListp, Offset, ai->val[0]);
        }
        else
        {
            Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        }
        break;
    })



#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## IF ALARM IS ON
//==============================================================================

/* #define CMDNAME IF_ALARM_IS_ON */
#define CMDNAME IFAlarmIsOn

#define DESCRIPTION \
/*******************************************************************************
* Is alarm is activated?
********************************************************************************
* @return     Continue execution from LABEL if TRUE
*******************************************************************************/

#define PARAMS BYTE(GOTOLABEL)

_AI_CMD(
    {
        AIRecord *ai = AiListp + Offset;
        if (alarmIsActive())
        {
            Offset = chraiGoToLabel(AiListp, Offset, ai->val[0]);
        }
        else
        {
            Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        }
        break;
    })



#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## IF GAS IS LEAKING
//==============================================================================

/* #define CMDNAME IF_GAS_IS_LEAKING */
#define CMDNAME IFGasIsLeaking

#define DESCRIPTION \
/*******************************************************************************
* Is gas leaking?
********************************************************************************
* @return     Continue execution from LABEL if TRUE
* @exception: Once gas leak event has started, always Go to LABEL
*******************************************************************************/

#define PARAMS BYTE(GOTOLABEL)

_AI_CMD(
    {
        AIRecord *ai = AiListp + Offset;
        if (check_if_toxic_gas_activated())
        {
            Offset = chraiGoToLabel(AiListp, Offset, ai->val[0]);
        }
        else
        {
            Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        }
        break;
    })



#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## IF GUARD HEARD BOND
//==============================================================================

/* #define CMDNAME IF_GUARD_HEARD_BOND */
#define CMDNAME IFIHeardBond

#define DESCRIPTION \
/*******************************************************************************
* Did I hear Bond fire a weapon?
********************************************************************************
* @return     Continue execution from LABEL if TRUE
* @exception: Uses chr->hearingscale while listening for Bond. To check if Bond has
*             shot within the last 10 seconds, use
*             IFIHeardBondRecently
*******************************************************************************/

#define PARAMS BYTE(GOTOLABEL)

_AI_CMD(
    {
        AIRecord *ai = AiListp + Offset;
        if (chrIsHearingBond(ChrEntityp))
        {
            Offset = chraiGoToLabel(AiListp, Offset, ai->val[0]);
        }
        else
        {
            Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        }
        break;
    })



#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## IF GUARD SEE ANOTHER GUARD SHOT
//==============================================================================

/* #define CMDNAME IF_GUARD_SEE_ANOTHER_GUARD_SHOT */
#define CMDNAME IFISeeSomeoneShot

#define DESCRIPTION \
/*******************************************************************************
* Did I see another chr shot? (by anyone)
********************************************************************************
* @return     Continue execution from LABEL if TRUE
* @exception: Guard friendly fire (if flagged) will also trigger Go to LABEL.
*             Checks if chr->chrseeshot is set to valid chrnum (not -1).
*             Does NOT work with shot invincible/armoured guards
*******************************************************************************/

#define PARAMS BYTE(GOTOLABEL)

_AI_CMD(
    {
        AIRecord *ai = AiListp + Offset;
        if (chrSawInjury(ChrEntityp))
        {
            Offset = chraiGoToLabel(AiListp, Offset, ai->val[0]);
        }
        else
        {
            Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        }
        break;
    })



#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## IF GUARD SEE ANOTHER GUARD DIE
//==============================================================================

/* #define CMDNAME IF_GUARD_SEE_ANOTHER_GUARD_DIE */
#define CMDNAME IFISeeSomeoneDie

#define DESCRIPTION \
/*******************************************************************************
* Did I see another chr die? (for any reason)
********************************************************************************
* @return     Continue execution from LABEL if TRUE
* @exception: When a chr in sight switches to ACT_DIE/ACT_DEAD, Go to LABEL.
*             Checks if chr->chrseedie is set to valid chrnum (not -1)
*******************************************************************************/

#define PARAMS BYTE(GOTOLABEL)

_AI_CMD(
    {
        AIRecord *ai = AiListp + Offset;
        if (chrSawDeath(ChrEntityp))
        {
            Offset = chraiGoToLabel(AiListp, Offset, ai->val[0]);
        }
        else
        {
            Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        }
        break;
    })



#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## IF GUARD AND BOND WITHIN LINE OF SIGHT
//==============================================================================

/* #define CMDNAME IF_GUARD_AND_BOND_WITHIN_LINE_OF_SIGHT */
#define CMDNAME IFICouldSeeBond

#define DESCRIPTION \
/*******************************************************************************
* If I looked at Bond, could I walk in a straight line to him?
********************************************************************************
* @return     Continue execution from LABEL if TRUE
* @exception: Uses Stand Tiles & ignores facing direction of Bond/guard.
*             If Prop/guard is in the way do not Go to LABEL.
*             Does NOT use chr->visionrangefor line of sight check.
*             Use IFISeeBond to check using chr->visionrange and
*             IFImOnScreen to account for Bond's view
*******************************************************************************/

#define PARAMS BYTE(GOTOLABEL)

_AI_CMD(
    {
        AIRecord *ai = AiListp + Offset;
        if (chrCanSeeBond(ChrEntityp))
        {
            Offset = chraiGoToLabel(AiListp, Offset, ai->val[0]);
        }
        else
        {
            Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        }
        break;
    })



#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## IF GUARD AND BOND WITHIN PARTIAL LINE OF SIGHT
//==============================================================================

/* #define CMDNAME IF_GUARD_AND_BOND_WITHIN_PARTIAL_LINE_OF_SIGHT */
#define CMDNAME IFICouldSeeBondsStan

#define DESCRIPTION \
/*******************************************************************************
* If I looked at Bond, could I walk in a straight line to his Stan Tile?
********************************************************************************
* @return     Continue execution from LABEL if TRUE
* @exception: Unused command, functions like above but only Go to LABEL if Bond is
*             half occluded by Stand Tiles (Neither blocked or within full view)
*******************************************************************************/

#define PARAMS BYTE(GOTOLABEL)

_AI_CMD(
    {
        AIRecord *ai = AiListp + Offset;
        if (chrIsTargetNearlyInSight(ChrEntityp))
        {
            Offset = chraiGoToLabel(AiListp, Offset, ai->val[0]);
        }
        else
        {
            Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        }
        break;
    })



#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## IF GUARD WAS SHOT WITHIN LAST 10 SECS
//==============================================================================

/* #define CMDNAME IF_GUARD_WAS_SHOT_OR_SEEN_WITHIN_LAST_10_SECS */
#define CMDNAME IFIWasShotRecently

#define DESCRIPTION \
/*******************************************************************************
* Was I shot (by anyone) or seen Bond within the last 10 seconds?
********************************************************************************
* @return     Continue execution from LABEL if TRUE
* @exception: This will NOT count guard as shot if they are invincible/have armour.
*             When using with IFISeeBond, make sure IFISeeBond takes
*             priority over IFIWasShotRecently.
*             To check if guard was hit/damaged use
*             IFChrWasDamagedSinceLastCheck/IFIWasShotRecently
*             instead, or check if flags CHRFLAG_WAS_DAMAGED/CHRFLAG_WAS_HIT are set
*             using IFMyFlagsAreSet OR IFChrFlagsAreSet
*******************************************************************************/

#define PARAMS BYTE(GOTOLABEL)

_AI_CMD(
    {
        AIRecord *ai = AiListp + Offset;
        if (chrSawTargetRecently(ChrEntityp))
        {
            Offset = chraiGoToLabel(AiListp, Offset, ai->val[0]);
        }
        else
        {
            Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        }
        break;
    })



#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## IF GUARD HEARD BOND WITHIN LAST 10 SECS
//==============================================================================

/* #define CMDNAME IF_GUARD_HEARD_BOND_WITHIN_LAST_10_SECS */
#define CMDNAME IFIHeardBondRecently

#define DESCRIPTION \
/*******************************************************************************
* Did I hear Bond fire weapon within the last 10 seconds?
********************************************************************************
* @return     Continue execution from LABEL if TRUE
* @exception: Uses chr->hearingscale while listening for Bond. To check if Bond has
*             now fired weapon instead of within the last 10 seconds, use
*             IF_GUARD_HEARD_BOND
*******************************************************************************/

#define PARAMS BYTE(GOTOLABEL)

_AI_CMD(
    {
        AIRecord *ai = AiListp + Offset;
        if (chrHeardTargetRecently(ChrEntityp))
        {
            Offset = chraiGoToLabel(AiListp, Offset, ai->val[0]);
        }
        else
        {
            Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        }
        break;
    })



#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## IF GUARD IN ROOM WITH CHR
//==============================================================================

/* #define CMDNAME IF_GUARD_IN_ROOM_WITH_CHR */
#define CMDNAME IFImInRoomWithChr

#define DESCRIPTION \
/*******************************************************************************
* Am I in same room as chr?
********************************************************************************
* @return     Continue execution from LABEL if TRUE
* @param      CHR_NUM: ID of Chr (0-255)
*******************************************************************************/

#define PARAMS BYTE(CHR_NUM), BYTE(GOTOLABEL)

_AI_CMD(
    {
        AIRecord * ai  = AiListp + Offset;
        ChrRecord *chr = chrFindById(ChrEntityp, *ai->val);
        if (chr && chr->prop && check_if_position_in_same_room(ChrEntityp, &chr->prop->pos, chr->prop->stan))
        {
            Offset = chraiGoToLabel(AiListp, Offset, ai->val[1]);
        }
        else
        {
            Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        }
        break;
    })



#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## IF GUARD HAS NOT BEEN SEEN
//==============================================================================

/* #define CMDNAME IF_GUARD_HAS_NOT_BEEN_SEEN */
#define CMDNAME IFIveNotBeenSeen

#define DESCRIPTION \
/*******************************************************************************
* Have I been on-screen yet?
********************************************************************************
* @return     Continue execution from LABEL if TRUE
* @exception: When Bond has seen guard, it will add flag CHRFLAG_HAS_BEEN_ON_SCREEN
*             to chr->chrflags. The seen flag will be set for duration of level
*******************************************************************************/

#define PARAMS BYTE(GOTOLABEL)

_AI_CMD(
    {
        AIRecord *ai = AiListp + Offset;
        if (!(ChrEntityp->chrflags & CHRFLAG_HAS_BEEN_ON_SCREEN))
        {
            Offset = chraiGoToLabel(AiListp, Offset, ai->val[0]);
        }
        else
        {
            Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        }
        break;
    })



#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## IF GUARD IS ON SCREEN
//==============================================================================

/* #define CMDNAME IF_GUARD_IS_ON_SCREEN */
#define CMDNAME IFImOnScreen

#define DESCRIPTION \
/*******************************************************************************
* Am I currently being rendered on screen?
********************************************************************************
* @return     Continue execution from LABEL if TRUE
* @exception: Portals will affect this commands output.
*******************************************************************************/

#define PARAMS BYTE(GOTOLABEL)

_AI_CMD(
    {
        AIRecord *ai = AiListp + Offset;
        if ((ChrEntityp->prop->flags & PROPFLAG_ONSCREEN))
        {
            Offset = chraiGoToLabel(AiListp, Offset, ai->val[0]);
        }
        else
        {
            Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        }
        break;
    })



#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## IF GUARD ROOM CONTAINING SELF IS ON SCREEN
//==============================================================================

/* #define CMDNAME IF_GUARD_ROOM_CONTAINING_SELF_IS_ON_SCREEN */
#define CMDNAME IFMyRoomIsOnScreen

#define DESCRIPTION \
/*******************************************************************************
* Is my room being rendered on screen?
********************************************************************************
* @return     Continue execution from LABEL if TRUE
* @exception: Only checks if room is being rendered, not if Bond can see guard.
*             To check if guard is being rendered use IF_GUARD_IS_ON_SCREEN
*             instead.
*******************************************************************************/

#define PARAMS BYTE(GOTOLABEL)

_AI_CMD(
    {
        AIRecord *ai = AiListp + Offset;
        if (getROOMID_isRendered(getTileRoom(ChrEntityp->prop->stan)))
        {
            Offset = chraiGoToLabel(AiListp, Offset, ai->val[0]);
        }
        else
        {
            Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        }
        break;
    })



#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## IF ROOM CONTAINING PAD IS ON SCREEN
//==============================================================================

/* #define CMDNAME IF_ROOM_CONTAINING_PAD_IS_ON_SCREEN */
#define CMDNAME IFRoomWithPadIsOnScreen

#define DESCRIPTION \
/*******************************************************************************
* Is the room containing PAD being rendered on screen?
********************************************************************************
* @return     Continue execution from LABEL if TRUE
* @exception: Only checks if room is being rendered, not if Bond can see inside
*             room
*******************************************************************************/

#define PARAMS DBYTE(PAD), BYTE(GOTOLABEL)

_AI_CMD(
    {
        AIRecord *ai     = AiListp + Offset;
        u16       pad_id = CharArrayTo16(ai->val,0);
        if (check_if_room_for_preset_loaded(ChrEntityp, pad_id))
        {
            Offset = chraiGoToLabel(AiListp, Offset, ai->val[2]);
        }
        else
        {
            Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        }
        break;
    })



#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## IF GUARD IS TARGETED BY BOND
//==============================================================================

/* #define CMDNAME IF_GUARD_IS_TARGETED_BY_BOND */
#define CMDNAME IFImTargetedByBond

#define DESCRIPTION \
/*******************************************************************************
* Is Bond looking/aiming at me?
********************************************************************************
* @return     Continue execution from LABEL if TRUE
* @exception: Also checks if player's cross-hair is aiming at guard
*******************************************************************************/

#define PARAMS BYTE(GOTOLABEL)

_AI_CMD(
    {
        AIRecord *ai = AiListp + Offset;
        if (sub_GAME_7F0333F8(ChrEntityp))
        {
            Offset = chraiGoToLabel(AiListp, Offset, ai->val[0]);
        }
        else
        {
            Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        }
        break;
    })



#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## IF GUARD SHOT FROM BOND MISSED
//==============================================================================

/* #define CMDNAME IF_GUARD_SHOT_FROM_BOND_MISSED */
#define CMDNAME IFBondMissedMe

#define DESCRIPTION \
/*******************************************************************************
* Did Bond's shot miss/land near me?
********************************************************************************
* @return     Continue execution from LABEL if TRUE
* @exception: Command will sometimes Go to LABEL if guard was shot - use
*             IF_GUARD_WAS_SHOT_OR_SEEN_WITHIN_LAST_10_SECS instead to check if
*             guard was shot recently (more consistent)
*******************************************************************************/

#define PARAMS BYTE(GOTOLABEL)

_AI_CMD(
    {
        AIRecord *ai = AiListp + Offset;
        if (chrIfNearMiss(ChrEntityp))
        {
            Offset = chraiGoToLabel(AiListp, Offset, ai->val[0]);
        }
        else
        {
            Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        }
        break;
    })



#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## IF GUARD COUNTER CLOCKWISE DIRECTION TO BOND LESS THAN
//==============================================================================

/* #define CMDNAME IF_GUARD_COUNTER_CLOCKWISE_DIRECTION_TO_BOND_LESS_THAN */
#define CMDNAME IFMyAngleToBondLessThan

// Alternative Names?
// aiIfTargetInFovLeft or aiIfBondOutOfFov
#define DESCRIPTION \
/*******************************************************************************
* Is my counter-clockwise angle to Bond Less Than ANGLE?
********************************************************************************
* @return     Continue execution from LABEL if TRUE
* @param      ANGLE: 0 - 255 Unsigned Integer
* @exception: This is RAW byte command, use either Deg or Rad Variant
*******************************************************************************/

#define PARAMS BYTE(ANGLE), BYTE(GOTOLABEL)

_AI_CMD(
    {
        AIRecord *ai  = AiListp + Offset;
        float     rad = chrGetAngleToBond(ChrEntityp);
        if (ByteToRadian((ai->val[0])) > rad)
        {
            Offset = chraiGoToLabel(AiListp, Offset, ai->val[1]);
        }
        else
        {
            Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        }
        break;
    })

//POLYMORPHS

#undef DESCRIPTION
#define DESCRIPTION \
/*******************************************************************************
* Is my counter-clockwise angle to Bond Less Than DEG?
********************************************************************************
* @return     Continue execution from LABEL if TRUE
* @param DEG: Degrees
*              00: Never TRUE, angle is always above 0
*              90: Bond must be within my 9-to-12 o'clock
*             180: Bond must be on my left-side 6-to-12 o'clock
*             270: Bond must be within my 3-to-12 o'clock
*             360: Always TRUE - angle is always Less Than 360
*******************************************************************************/

_AI_CMD_POLYMORPH(,
                  IFMyAngleToBondLessThanDeg,
                  (DEG2BYTE(DEG), GOTOLABEL),
                  (DEG, GOTOLABEL))

#undef DESCRIPTION
#define DESCRIPTION \
/*******************************************************************************
* Is my counter-clockwise angle to Bond Less Than RAD?
********************************************************************************
* @return     Continue execution from LABEL if TRUE
* @param RAD: Radians
*              00: Never TRUE, angle is always above 0
*              PI: Bond must be on my left-side 6-to-12 o'clock
*             TAU: Always TRUE - angle is always Less Than TAU
*******************************************************************************/

_AI_CMD_POLYMORPH(,
                  IFMyAngleToBondLessThanRad,
                  (RAD2BYTE(RAD), GOTOLABEL),
                  (RAD, GOTOLABEL))

#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## IF GUARD COUNTER CLOCKWISE DIRECTION TO BOND GREATER THAN
//==============================================================================

/* #define CMDNAME IF_GUARD_COUNTER_CLOCKWISE_DIRECTION_TO_BOND_GREATER_THAN */
#define CMDNAME IFMyAngleToBondGreaterThan

#define DESCRIPTION \
/*******************************************************************************
* Is my counter-clockwise angle to Bond Greater Than ANGLE?
********************************************************************************
* @return     Continue execution from LABEL if TRUE
* @param      ANGLE: 0 - 255 Unsigned Integer
* @exception: This is RAW byte command, use either Deg or Rad Variant
*******************************************************************************/

#define PARAMS BYTE(ANGLE), BYTE(GOTOLABEL)

_AI_CMD(
    {
        AIRecord *ai  = AiListp + Offset;
        float     rad = chrGetAngleToBond(ChrEntityp);
        if (ByteToRadian((ai->val[0])) < rad)
        {
            Offset = chraiGoToLabel(AiListp, Offset, ai->val[1]);
        }
        else
        {
            Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        }
        break;
    })

//POLYMORPHS

#undef DESCRIPTION
#define DESCRIPTION \
/*******************************************************************************
* Is my counter-clockwise angle to Bond Greater Than DEG?
********************************************************************************
* @return     Continue execution from LABEL if TRUE
* @param DEG: Degrees
*              00: Always TRUE, angle is always above 0
*              90: Bond must be within my 12-to-9 o'clock
*             180: Bond must be on my right-side 12-to-6 o'clock
*             270: Bond must be within my 12-to-3 o'clock
*             360: Never TRUE - angle is always Less Than 360
******************************************************************************/

_AI_CMD_POLYMORPH(,
                  IFMyAngleToBondGreaterThanDeg,
                  (DEG2BYTE(DEG), GOTOLABEL),
                  (DEG, GOTOLABEL))

#undef DESCRIPTION
#define DESCRIPTION \
/*******************************************************************************
* Is my counter-clockwise angle to Bond Greater Than RAD?
********************************************************************************
* @return     Continue execution from LABEL if TRUE
* @param RAD: Radians
*              00: Always TRUE, angle is always above 0
*              PI: Bond must be on my right-side 6-to-12 o'clock
*             TAU: Never TRUE - angle is always Less Than TAU
*******************************************************************************/

_AI_CMD_POLYMORPH(,
                  IFMyAngleToBondGreaterThanRad,
                  (RAD2BYTE(RAD), GOTOLABEL),
                  (RAD, GOTOLABEL))
#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## IF GUARD COUNTER CLOCKWISE DIRECTION FROM BOND LESS THAN
//==============================================================================

/* #define CMDNAME IF_GUARD_COUNTER_CLOCKWISE_DIRECTION_FROM_BOND_LESS_THAN */
#define CMDNAME IFMyAngleFromBondLessThan

#define DESCRIPTION \
/*******************************************************************************
* Is my counter-clockwise angle from Bond Less Than ANGLE?
********************************************************************************
* @return     Continue execution from LABEL if TRUE
* @param      ANGLE: 0 - 255 Unsigned Integer
* @exception: This is RAW byte command, use either Deg or Rad Variant
*******************************************************************************/
#define PARAMS BYTE(ANGLE), BYTE(GOTOLABEL)

_AI_CMD(
    {
        AIRecord *ai  = AiListp + Offset;
        float     rad = chrGetAngleFromBond(ChrEntityp);
        if (ByteToRadian((ai->val[0])) > rad)
        {
            Offset = chraiGoToLabel(AiListp, Offset, ai->val[1]);
        }
        else
        {
            Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        }
        break;
    })

//POLYMORPHS

#undef DESCRIPTION
#define DESCRIPTION \
/*******************************************************************************
* Is my counter-clockwise angle from Bond Less Than DEG?
********************************************************************************
* @return     Continue execution from LABEL if TRUE
* @param DEG: Degrees
*              00: Never TRUE, angle is always above 0
*              90: I must be within Bond's 9-to-12 o'clock
*             180: I must be on Bond's left-side 6-to-12 o'clock
*             270: I must be within Bond's 3-to-12 o'clock
*             360: Always TRUE - angle is always Less Than 360
*******************************************************************************/

_AI_CMD_POLYMORPH(,
                  IFMyAngleFromBondLessThanDeg,
                  (DEG2BYTE(DEG), GOTOLABEL),
                  (DEG, GOTOLABEL))

#undef DESCRIPTION
#define DESCRIPTION \
/*******************************************************************************
* Is my counter-clockwise angle from Bond Less Than RAD?
********************************************************************************
* @return     Continue execution from LABEL if TRUE
* @param RAD: Radians
*              00: Never TRUE, angle is always above 0
*              PI: Bond must be on my left-side 6-to-12 o'clock
*             TAU: Always TRUE - angle is always Less Than TAU
*******************************************************************************/

_AI_CMD_POLYMORPH(,
                  IFMyAngleFromBondLessThanRad,
                  (RAD2BYTE(ANGLE), GOTOLABEL),
                  (ANGLE, GOTOLABEL))
#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## IF GUARD COUNTER CLOCKWISE ANGLE FROM BOND GREATER THAN
//==============================================================================

/* #define CMDNAME IF_GUARD_COUNTER_CLOCKWISE_DIRECTION_FROM_BOND_GREATER_THAN */
#define CMDNAME IFMyAngleFromBondGreaterThan

#define DESCRIPTION \
/*******************************************************************************
* Is my counter-clockwise angle from Bond Greater Than ANGLE?
********************************************************************************
* @return     Continue execution from LABEL if TRUE
* @param      ANGLE: 0 - 255 Unsigned Integer
* @exception: This is RAW byte command, use either Deg or Rad Variant
*******************************************************************************/
#define PARAMS BYTE(ANGLE), BYTE(GOTOLABEL)

_AI_CMD(
    {
        AIRecord *ai  = AiListp + Offset;
        float     rad = chrGetAngleFromBond(ChrEntityp);
        if (ByteToRadian((ai->val[0])) < rad)
        {
            Offset = chraiGoToLabel(AiListp, Offset, ai->val[1]);
        }
        else
        {
            Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        }
        break;
    })

//POLYMORPHS

#undef DESCRIPTION
#define DESCRIPTION \
/*******************************************************************************
* Is my counter-clockwise angle from Bond Greater Than DEG?
********************************************************************************
* @return     Continue execution from LABEL if TRUE
* @param DEG: Degrees
*              00: Always TRUE, angle is always above 0
*              90: I must be within Bond's 12-to-9 o'clock
*             180: I must be on Bond's right-side 12-to-6 o'clock
*             270: I must be within Bond's 12-to-3 o'clock
*             360: Never TRUE - angle is always Less Than 360
*******************************************************************************/

_AI_CMD_POLYMORPH(,
                  IFMyAngleFromBondGreaterThanDeg,
                  (DEG2BYTE(DEG), GOTOLABEL),
                  (DEG, GOTOLABEL))

#undef DESCRIPTION
#define DESCRIPTION \
/*******************************************************************************
* Is my counter-clockwise angle from Bond Greater Than RAD?
********************************************************************************
* @return     Continue execution from LABEL if TRUE
* @param RAD: Radians
*              00: Always TRUE, angle is always above 0
*              PI: I must be on Bond's right-side 12-to-6 o'clock
*             TAU: Never TRUE - angle is always Less Than TAU
*******************************************************************************/

_AI_CMD_POLYMORPH(,
                  IFMyAngleFromBondGreaterThanRad,
                  (RAD2BYTE(ANGLE), GOTOLABEL),
                  (ANGLE, GOTOLABEL))
#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## IF GUARD DISTANCE TO BOND LESS THAN
//==============================================================================

/* #define CMDNAME IF_GUARD_DISTANCE_TO_BOND_LESS_THAN */
#define CMDNAME IFMyDistanceToBondLessThanDecimeter

#define DESCRIPTION \
/*******************************************************************************
* Is my distance to Bond Less Than DISTANCE?
********************************************************************************
* @return     Continue execution from LABEL if TRUE
* @param      DISTANCE: Integer, scale is 10 units per meter (decimetre)
*******************************************************************************/

#define PARAMS DBYTE(DISTANCE), BYTE(GOTOLABEL)

_AI_CMD(
    {
        AIRecord *ai       = AiListp + Offset;
        f32       distance = CharArrayTo16(ai->val,0) * 10.0f;
        if (distance > chrGetDistanceToBond(ChrEntityp))
        {
            Offset = chraiGoToLabel(AiListp, Offset, ai->val[2]);
        }
        else
        {
            Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        }
        break;
    })

//POLYMORPHS

#undef DESCRIPTION
#define DESCRIPTION \
/*******************************************************************************
* Is my distance to Bond Less Than DISTANCE?
********************************************************************************
* @return     Continue execution from LABEL if TRUE
* @param      DISTANCE: Float, meters to bond
********************************************************************************/
_AI_CMD_POLYMORPH(,
                    IFMyDistanceToBondLessThanMeter,
                    ((u16)(DISTANCE * 10), GOTOLABEL),
                    (DISTANCE, GOTOLABEL))

#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## IF GUARD DISTANCE TO BOND GREATER THAN
//==============================================================================

/* #define CMDNAME IF_GUARD_DISTANCE_TO_BOND_GREATER_THAN */
#define CMDNAME IFMyDistanceToBondGreaterThanDecimeter

#define DESCRIPTION \
/*******************************************************************************
* Is my distance to Bond Greater Than DISTANCE?
********************************************************************************
* @return     Continue execution from LABEL if TRUE
* @param      DISTANCE: Integer, scale is 10 units per meter (decimetre)
*******************************************************************************/

#define PARAMS DBYTE(DISTANCE), BYTE(GOTOLABEL)

_AI_CMD(
    {
        AIRecord *ai       = AiListp + Offset;
        f32       distance = CharArrayTo16(ai->val,0) * 10.0f;
        if (distance < chrGetDistanceToBond(ChrEntityp))
        {
            Offset = chraiGoToLabel(AiListp, Offset, ai->val[2]);
        }
        else
        {
            Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        }
        break;
    })

//POLYMORPHS

#undef DESCRIPTION
#define DESCRIPTION \
/*******************************************************************************
* Is My distance to Bond Greater Than DISTANCE?
********************************************************************************
* @return     Continue execution from LABEL if TRUE
* @param      DISTANCE: Float, meters to Bond
********************************************************************************/
_AI_CMD_POLYMORPH(,
                    IFMyDistanceToBondGreaterThanMeter,
                    ((u16)(DISTANCE * 10), GOTOLABEL),
                    (DISTANCE, GOTOLABEL))

#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## IF CHR DISTANCE TO PAD LESS THAN
//==============================================================================

/* #define CMDNAME IF_CHR_DISTANCE_TO_PAD_LESS_THAN */
#define CMDNAME IFChrDistanceToPadLessThanDecimeter

#define DESCRIPTION \
/*******************************************************************************
* Is My/Chr distance to PAD Less Than DISTANCE?
********************************************************************************
* @return     Continue execution from LABEL if TRUE
* @param      DISTANCE: Integer, scale is 10 units per meter (decimetre)
* @param      CHR_NUM: ID of Chr (0-255)
*******************************************************************************/

#define PARAMS BYTE(CHR_NUM), DBYTE(DISTANCE), DBYTE(PAD), BYTE(GOTOLABEL)

_AI_CMD(
    {
        AIRecord * ai     = AiListp + Offset;
        ChrRecord *chr    = chrFindById(ChrEntityp, ai->val[0]);
        u16        padnum = CharArrayTo16(ai->val,3);
        f32        value  = CharArrayTo16(ai->val,1) * 10.0f;
        if (chr && (value > chrGetDistanceToPad(chr, padnum)))
        {
            Offset = chraiGoToLabel(AiListp, Offset, ai->val[5]);
        }
        else
        {
            Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        }
        break;
    })

//POLYMORPHS

_AI_CMD_POLYMORPH(,
                    IFMyDistanceToPadLessThanDecimeter,
                    (CHR_SELF, (DISTANCE), PAD, GOTOLABEL),
                    (DISTANCE, PAD, GOTOLABEL))

#undef DESCRIPTION
#define DESCRIPTION \
/*******************************************************************************
* Is My/Chr distance to PAD Less Than DISTANCE?
********************************************************************************
* @return     Continue execution from LABEL if TRUE
* @param      DISTANCE: Float, meters to pad
* @param      CHR_NUM: ID of Chr (0-255)
*******************************************************************************/

_AI_CMD_POLYMORPH(,
                    IFChrDistanceToPadLessThanMeter,
                    (CHR_NUM, (u16)(DISTANCE * 10), PAD, GOTOLABEL),
                    (CHR_NUM, DISTANCE, PAD, GOTOLABEL))
_AI_CMD_POLYMORPH(,
                    IFMyDistanceToPadLessThanMeter,
                    (CHR_SELF, (u16)(DISTANCE * 10), PAD, GOTOLABEL),
                    (DISTANCE, PAD, GOTOLABEL))

#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## IF CHR DISTANCE TO PAD GREATER THAN
//==============================================================================

/* #define CMDNAME IF_CHR_DISTANCE_TO_PAD_GREATER_THAN */
#define CMDNAME IFChrDistanceToPadGreaterThanDecimeter

#define DESCRIPTION \
/*******************************************************************************
* Is My/Chr distance to PAD Greater Than DISTANCE?
********************************************************************************
* @return     Continue execution from LABEL if TRUE
* @param      DISTANCE: Integer, scale is 10 units per meter (decimetre)
* @param      CHR_NUM: ID of Chr (0-255)
*******************************************************************************/

#define PARAMS BYTE(CHR_NUM), DBYTE(DISTANCE), DBYTE(PAD), BYTE(GOTOLABEL)

_AI_CMD(
    {
        AIRecord * ai     = AiListp + Offset;
        ChrRecord *chr    = chrFindById(ChrEntityp, ai->val[0]);
        u16        padnum = CharArrayTo16(ai->val,3);
        f32        value  = CharArrayTo16(ai->val,1) * 10.0f;
        if (chr && (value < chrGetDistanceToPad(chr, padnum)))
        {
            Offset = chraiGoToLabel(AiListp, Offset, ai->val[5]);
        }
        else
        {
            Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        }
        break;
    })

//POLYMORPHS

_AI_CMD_POLYMORPH(,
                    IFMyDistanceToPadGreaterThanDecimeter,
                    (CHR_SELF, (DISTANCE), PAD, GOTOLABEL),
                    (DISTANCE, PAD, GOTOLABEL))

#undef DESCRIPTION
#define DESCRIPTION \
/*******************************************************************************
* Is My/Chr distance to PAD Greater Than DISTANCE?
********************************************************************************
* @return     Continue execution from LABEL if TRUE
* @param      DISTANCE: Float, meters to pad
* @param      CHR_NUM: ID of Chr (0-255)
********************************************************************************/
_AI_CMD_POLYMORPH(,
                    IFChrDistanceToPadGreaterThanMeter,
                    (CHR_NUM, (u16)(DISTANCE * 10), PAD, GOTOLABEL),
                    (CHR_NUM, DISTANCE, PAD, GOTOLABEL))
_AI_CMD_POLYMORPH(,
                    IFMyDistanceToPadGreaterThanMeter,
                    (CHR_SELF, (u16)(DISTANCE * 10), PAD, GOTOLABEL),
                    (DISTANCE, PAD, GOTOLABEL))

#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## IF GUARD DISTANCE TO CHR LESS THAN
//==============================================================================

/* #define CMDNAME IF_GUARD_DISTANCE_TO_CHR_LESS_THAN */
#define CMDNAME IFMyDistanceToChrLessThanDecimeter

#define DESCRIPTION \
/*******************************************************************************
* Is my distance to CHR_NUM Less Than DISTANCE?
********************************************************************************
* @return     Continue execution from LABEL if TRUE
* @param      DISTANCE: Integer, scale is 10 units per meter (decimetre)
* @param      CHR_NUM: ID of Chr (0-255)
*******************************************************************************/

#define PARAMS DBYTE(DISTANCE), BYTE(CHR_NUM), BYTE(GOTOLABEL)

_AI_CMD(
    {
        AIRecord *ai     = AiListp + Offset;
        f32       cutoff = CharArrayTo16(ai->val,0) * 10.0f;
        if (cutoff > chrGetDistanceToChr(ChrEntityp, ai->val[2]))
        {
            Offset = chraiGoToLabel(AiListp, Offset, ai->val[3]);
        }
        else
        {
            Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        }
        break;
    })

//POLYMORPHS

#undef DESCRIPTION
#define DESCRIPTION \
/*******************************************************************************
* Is my distance to CHR_NUM Less Than DISTANCE?
********************************************************************************
* @return     Continue execution from LABEL if TRUE
* @param      DISTANCE: Float, meters to chr
* @param      CHR_NUM: ID of Chr (0-255)
********************************************************************************/
_AI_CMD_POLYMORPH(,
                    IFMyDistanceToChrLessThanMeter,
                    ((u16)(DISTANCE * 10), CHR_NUM, GOTOLABEL),
                    (DISTANCE, CHR_NUM, GOTOLABEL))

#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## IF GUARD DISTANCE TO CHR GREATER THAN
//==============================================================================

/* #define CMDNAME IF_GUARD_DISTANCE_TO_CHR_GREATER_THAN */
#define CMDNAME IFMyDistanceToChrGreaterThanDecimeter

#define DESCRIPTION \
/*******************************************************************************
* Is my distance to CHR_NUM Greater Than DISTANCE?
********************************************************************************
* @return     Continue execution from LABEL if TRUE
* @param      DISTANCE: Integer, scale is 10 units per meter (decimetre)
* @param      CHR_NUM: ID of Chr (0-255)
********************************************************************************/

#define PARAMS DBYTE(DISTANCE), BYTE(CHR_NUM), BYTE(GOTOLABEL)

_AI_CMD(
    {
        AIRecord *ai     = AiListp + Offset;
        f32       cutoff = CharArrayTo16(ai->val,0) * 10.0f;
        if (cutoff < chrGetDistanceToChr(ChrEntityp, ai->val[2]))
        {
            Offset = chraiGoToLabel(AiListp, Offset, ai->val[3]);
        }
        else
        {
            Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        }
        break;
    })

//POLYMORPHS

#undef DESCRIPTION
#define DESCRIPTION \
/*******************************************************************************
* Is my distance to CHR_NUM Greater Than DISTANCE?
********************************************************************************
* @return     Continue execution from LABEL if TRUE
* @param      DISTANCE: Float, meters to chr
* @param      CHR_NUM: ID of Chr (0-255)
*******************************************************************************/
_AI_CMD_POLYMORPH(,
                    IFMyDistanceToChrGreaterThanMeters,
                    ((u16)(DISTANCE * 10), CHR_NUM, GOTOLABEL),
                    (DISTANCE, CHR_NUM, GOTOLABEL))

#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## GUARD TRY SETTING CHR PRESET TO GUARD WITHIN DISTANCE
//==============================================================================

/* #define CMDNAME GUARD_TRY_SETTING_CHR_PRESET_TO_GUARD_WITHIN_DISTANCE */
#define CMDNAME TRYSettingMyPresetToChrWithinDistanceDecimeter

#define DESCRIPTION \
/*******************************************************************************
* Try setting my padpreset to the pad of the first Chr found within DISTANCE
********************************************************************************
* @return     Continue execution from LABEL if a Chr is found
* @param      DISTANCE: Integer, scale is 10 units per meter (decimeter)
* @exception: Does NOT pick the closest found chr, but whoever was found first
*             within the DISTANCE argument.
*******************************************************************************/

#define PARAMS DBYTE(DISTANCE), BYTE(GOTOLABEL)

_AI_CMD(
    {
        AIRecord *ai       = AiListp + Offset;
        f32       distance = CharArrayTo16(ai->val,0) * 10.0f;
        if (sub_GAME_7F033B38(ChrEntityp, distance))
        {
            Offset = chraiGoToLabel(AiListp, Offset, ai->val[2]);
        }
        else
        {
            Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        }
        break;
    })

//POLYMORPHS

#undef DESCRIPTION
#define DESCRIPTION \
/*******************************************************************************
* Try setting my padpreset to the pad of the first Chr found within DISTANCE
********************************************************************************
* @return     Continue execution from LABEL if a Chr is found
* @param      DISTANCE: Float, meters to test
* @exception: Does NOT pick the closest found chr, but whoever was found first
*             within the DISTANCE argument.
*******************************************************************************/
_AI_CMD_POLYMORPH(,
                    TRYSettingMyPresetToChrWithinDistanceMeters,
                    ((u16)(DISTANCE * 10), GOTOLABEL),
                    (DISTANCE,  GOTOLABEL))

#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## IF BOND DISTANCE TO PAD LESS THAN
//==============================================================================

/* #define CMDNAME IF_BOND_DISTANCE_TO_PAD_LESS_THAN */
#define CMDNAME IFBondDistanceToPadLessThanDecimeter

#define DESCRIPTION \
/*******************************************************************************
* Is Bond within DISTANCE to PAD?
********************************************************************************
* @return     Continue execution from LABEL if TRUE
* @param      DISTANCE: Integer, scale is 10 units per meter (decimeter)
********************************************************************************/

#define PARAMS DBYTE(DISTANCE), DBYTE(PAD), BYTE(GOTOLABEL)

_AI_CMD(
    {
        AIRecord *ai    = AiListp + Offset;
        u16       pad   = CharArrayTo16(ai->val,2);
        f32       value = CharArrayTo16(ai->val,0) * 10.0f;
        if (value > chrGetDistanceFromBondToPad(ChrEntityp, pad))
        {
            Offset = chraiGoToLabel(AiListp, Offset, ai->val[4]);
        }
        else
        {
            Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        }
        break;
    })

//POLYMORPHS

#undef DESCRIPTION
#define DESCRIPTION \
/*******************************************************************************
* Is Bond within DISTANCE to PAD?
********************************************************************************
* @return     Continue execution from LABEL if TRUE
* @param      DISTANCE: Float, meters to pad
********************************************************************************/
_AI_CMD_POLYMORPH(,
                    IFBondDistanceToPadLessThanMeter,
                    ((u16)(DISTANCE * 10), PAD, GOTOLABEL),
                    (DISTANCE, PAD, GOTOLABEL))

#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## IF BOND DISTANCE TO PAD GREATER THAN
//==============================================================================

/* #define CMDNAME IF_BOND_DISTANCE_TO_PAD_GREATER_THAN */
#define CMDNAME IFBondDistanceToPadGreaterThanDecimeter

#define DESCRIPTION \
/*******************************************************************************
* Is Bond further than DISTANCE to PAD?
********************************************************************************
* @return     Continue execution from LABEL if TRUE
* @param      DISTANCE: Integer, scale is 10 units per meter (decimeter)
********************************************************************************/

#define PARAMS DBYTE(DISTANCE), DBYTE(PAD), BYTE(GOTOLABEL)

_AI_CMD(
    {
        AIRecord *ai    = AiListp + Offset;
        u16       pad   = CharArrayTo16(ai->val,2);
        f32       value = CharArrayTo16(ai->val,0) * 10.0f;
        if (value < chrGetDistanceFromBondToPad(ChrEntityp, pad))
        {
            Offset = chraiGoToLabel(AiListp, Offset, ai->val[4]);
        }
        else
        {
            Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        }
        break;
    })

//POLYMORPHS

#undef DESCRIPTION
#define DESCRIPTION \
/*******************************************************************************
* Is Bond further than DISTANCE to PAD?
********************************************************************************
* @return     Continue execution from LABEL if TRUE
* @param      DISTANCE: Float, meters to pad
********************************************************************************/
_AI_CMD_POLYMORPH(,
                    IFBondDistanceToPadGreaterThanMeter,
                    ((u16)(DISTANCE * 10), PAD, GOTOLABEL),
                    (DISTANCE, PAD, GOTOLABEL))

#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## IF CHR IN ROOM WITH PAD
//==============================================================================

/* #define CMDNAME IF_CHR_IN_ROOM_WITH_PAD */
#define CMDNAME IFChrInRoomWithPad

#define DESCRIPTION \
/*******************************************************************************
* Is CHR_NUM in room with PAD?
********************************************************************************
* @return     Continue execution from LABEL if TRUE
* @param      CHR_NUM: ID of Chr (0-255)
*******************************************************************************/

#define PARAMS BYTE(CHR_NUM), DBYTE(PAD), BYTE(GOTOLABEL)

_AI_CMD(
    {
        AIRecord *ai     = AiListp + Offset;
        u16       pad_id = CharArrayTo16(ai->val,1);
        if (chrIfInPadRoom(ChrEntityp, ai->val[0], pad_id))
        {
            Offset = chraiGoToLabel(AiListp, Offset, ai->val[3]);
        }
        else
        {
            Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        }
        break;
    })

//POLYMORPHS

#undef DESCRIPTION
#define DESCRIPTION \
/*******************************************************************************
* Am I in room with PAD?
********************************************************************************
* @return     Continue execution from LABEL if TRUE
********************************************************************************/
_AI_CMD_POLYMORPH(,
                    IFImInRoomWithPad,
                    (CHR_SELF, PAD, GOTOLABEL),
                    (PAD, GOTOLABEL))

#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## IF BOND IN ROOM WITH PAD
//==============================================================================

/* #define CMDNAME IF_BOND_IN_ROOM_WITH_PAD */
#define CMDNAME IFBondInRoomWithPad

#define DESCRIPTION \
/*******************************************************************************
* Is Bond in room with PAD?
********************************************************************************
* @return     Continue execution from LABEL if TRUE
*******************************************************************************/

#define PARAMS DBYTE(PAD), BYTE(GOTOLABEL)

_AI_CMD(
    {
        AIRecord *ai     = AiListp + Offset;
        u16       pad_id = CharArrayTo16(ai->val,0);
        if (check_if_actor_is_at_preset(ChrEntityp, pad_id))
        {
            Offset = chraiGoToLabel(AiListp, Offset, ai->val[2]);
        }
        else
        {
            Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        }
        break;
    })



#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## IF BOND COLLECTED OBJECT
//==============================================================================

/* #define CMDNAME IF_BOND_COLLECTED_OBJECT */
#define CMDNAME IFBondCollectedObject

#define DESCRIPTION \
/*******************************************************************************
* Did Bond collect Tagged Object OBJECT_TAG?
********************************************************************************
* @return     Continue execution from LABEL if TRUE
*******************************************************************************/

#define PARAMS BYTE(OBJECT_TAG), BYTE(GOTOLABEL)

_AI_CMD(
    {
        AIRecord *    ai  = AiListp + Offset;
        ObjectRecord *obj = objFindByTagId(ai->val[0]);
        if (obj && obj->prop && bondinvHasPropInInv(obj->prop))
        {
            Offset = chraiGoToLabel(AiListp, Offset, ai->val[1]);
        }
        else
        {
            Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        }
        break;
    })



#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## IF KEY DROPPED
//==============================================================================

/* #define CMDNAME IF_KEY_DROPPED */
#define CMDNAME IFKeyDropped

#define DESCRIPTION \
/*******************************************************************************
* Is Key, if it exists, "Dropped"? (not moving/in mid-air)
********************************************************************************
* @return     Continue execution from LABEL if TRUE
* @exception: Used to check if a Key with ID has been "Dropped" (RUNTIMEBITFLAG_DEPOSIT)
*             Make sure IFItemIsAttachedToObject takes priority over
*             IFKeyDropped when using both commands
*******************************************************************************/

#define PARAMS BYTE(KEY_ID), BYTE(GOTOLABEL)

_AI_CMD(
    {
        AIRecord *ai = AiListp + Offset;
        if (weaponFindThrown(ai->val[0]))
        {
            Offset = chraiGoToLabel(AiListp, Offset, ai->val[1]);
        }
        else
        {
            Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        }
        break;
    })



#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## IF ITEM IS ATTACHED TO OBJECT
//==============================================================================

/* #define CMDNAME IF_ITEM_IS_ATTACHED_TO_OBJECT */
#define CMDNAME IFItemIsAttachedToObject

#define DESCRIPTION \
/*******************************************************************************
* Was ITEM_NUM thrown onto Tagged Object
********************************************************************************
* @return     Continue execution from LABEL if TRUE
* @exception: Used to check if Bond threw an item onto a tagged object.
*             If used with IFKeyDropped, make sure
*             IFItemIsAttachedToObject take priority over
*             IFKeyDropped
*******************************************************************************/

#define PARAMS BYTE(ITEM_NUM), BYTE(OBJECT_TAG), BYTE(GOTOLABEL)

_AI_CMD(
    {
        struct
        {
            u8 cmd;
            u8 val;
            u8 val1;
            u8 val2;
        } *ai              = AiListp + Offset;
        ObjectRecord *obj  = objFindByTagId(ai->val1);
        bool          pass = FALSE;
        if (obj && obj->prop)
        {
            PropRecord *prop = obj->prop->child;
            while (prop)
            {
                if (prop->type == PROP_TYPE_WEAPON)
                {
                    WeaponObjRecord *weapon = prop->weapon;
                    if (weapon->weaponnum == ai->val)
                    {
                        pass = TRUE;
                        break;
                    }
                }
                prop = prop->prev;
            }
        }
        if (pass)
        {
            Offset = chraiGoToLabel(AiListp, Offset, ai->val2);
        }
        else
        {
            Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        }
        break;
    })



#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## IF BOND HAS ITEM EQUIPPED
//==============================================================================

/* #define CMDNAME IF_BOND_HAS_ITEM_EQUIPPED */
#define CMDNAME IFBondHasItemEquipped

#define DESCRIPTION \
/*******************************************************************************
* If Bond has an item equipped (currently held)
********************************************************************************
* @return     Continue execution from LABEL if TRUE
*******************************************************************************/

#define PARAMS BYTE(ITEM_NUM), BYTE(GOTOLABEL)

_AI_CMD(
    {
        AIRecord *ai = AiListp + Offset;
        if (ai->val[0] == getCurrentPlayerWeaponId(GUNRIGHT) || ai->val[0] == getCurrentPlayerWeaponId(GUNLEFT))
        {
            Offset = chraiGoToLabel(AiListp, Offset, ai->val[1]);
        }
        else
        {
            Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        }
        break;
    })



#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## IF OBJECT EXISTS
//==============================================================================

/* #define CMDNAME IF_OBJECT_EXISTS */
#define CMDNAME IFObjectExists

#define DESCRIPTION \
/*******************************************************************************
* If tagged object exists in level
********************************************************************************
* @return     Continue execution from LABEL if TRUE
*******************************************************************************/

#define PARAMS BYTE(OBJECT_TAG), BYTE(GOTOLABEL)

_AI_CMD(
    {
        AIRecord *    ai  = AiListp + Offset;
        ObjectRecord *obj = objFindByTagId(ai->val[0]);
        if (obj && obj->prop)
        {
            Offset = chraiGoToLabel(AiListp, Offset, ai->val[1]);
        }
        else
        {
            Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        }
        break;
    })



#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## IF OBJECT NOT DESTROYED
//==============================================================================

/* #define CMDNAME IF_OBJECT_NOT_DESTROYED */
#define CMDNAME IFObjectNotDestroyed

#define DESCRIPTION \
/*******************************************************************************
* If tagged object is not destroyed
********************************************************************************
* @return     Continue execution from LABEL if TRUE
*******************************************************************************/

#define PARAMS BYTE(OBJECT_TAG), BYTE(GOTOLABEL)

_AI_CMD(
    {
        AIRecord *    ai  = AiListp + Offset;
        ObjectRecord *obj = objFindByTagId(ai->val[0]);
        if (obj && obj->prop && objIsHealthy(obj))
        {
            Offset = chraiGoToLabel(AiListp, Offset, ai->val[1]);
        }
        else
        {
            Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        }
        break;
    })



#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## IF OBJECT WAS ACTIVATED
//==============================================================================

/* #define CMDNAME IF_OBJECT_WAS_ACTIVATED */
#define CMDNAME IFObjectWasActivated

#define DESCRIPTION \
/*******************************************************************************
* If tagged object was activated since last check
********************************************************************************
* @return     Continue execution from LABEL if TRUE
* @exception: When executed, it will clear tagged objects activated flag.
*             Only Bond and OBJECT_ACTIVATE can activate tagged objects.
*             Bond can NOT activate destroyed objects
*******************************************************************************/

#define PARAMS BYTE(OBJECT_TAG), BYTE(GOTOLABEL)

_AI_CMD(
    {
        AIRecord *    ai  = AiListp + Offset;
        ObjectRecord *obj = objFindByTagId(ai->val[0]);
        if (obj && obj->prop && (obj->runtime_bitflags & RUNTIMEBITFLAG_ACTIVATED))
        {
            obj->runtime_bitflags &= ~RUNTIMEBITFLAG_ACTIVATED;
            Offset = chraiGoToLabel(AiListp, Offset, ai->val[1]);
        }
        else
        {
            Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        }
        break;
    })



#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## IF BOND USED GADGET ON OBJECT
//==============================================================================

/* #define CMDNAME IF_BOND_USED_GADGET_ON_OBJECT */
#define CMDNAME IFBondUsedGadgetOnObject

#define DESCRIPTION \
/*******************************************************************************
* If Bond used a gadget item on a tagged object since last check
********************************************************************************
* @return     Continue execution from LABEL if TRUE
* @exception: Gadgets are a pre-defined list of items set to gadget flag:
*               ITEM_BOMBDEFUSER
*               ITEM_DATATHIEF
*               ITEM_DOORDECODER
*               ITEM_EXPLOSIVEFLOPPY
*               ITEM_DATTAPE
*******************************************************************************/

#define PARAMS BYTE(OBJECT_TAG), BYTE(GOTOLABEL)

_AI_CMD(
    {
        AIRecord *    ai  = AiListp + Offset;
        ObjectRecord *obj = objFindByTagId(ai->val[0]);
        if (obj && obj->prop && (obj->openstate & PROPSTATE_ACTIVATED))
        {
            obj->openstate &= ~PROPSTATE_ACTIVATED;
            Offset = chraiGoToLabel(AiListp, Offset, ai->val[1]);
        }
        else
        {
            Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        }
        break;
    })



#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## OBJECT ACTIVATE
//==============================================================================

/* #define CMDNAME OBJECT_ACTIVATE */
#define CMDNAME ActivateObject

#define DESCRIPTION \
/*******************************************************************************
* Activate a tagged object
********************************************************************************
* @exception: Does NOT check if object has been destroyed
*******************************************************************************/

#define PARAMS BYTE(OBJECT_TAG)

_AI_CMD(
    {
        AIRecord *    ai  = AiListp + Offset;
        ObjectRecord *obj = objFindByTagId(ai->val[0]);
        if (obj && obj->prop)
        {
            if (obj->prop->type == PROP_TYPE_DOOR)
            {
                doorActivateWrapper(obj->prop);
            }
            else if (obj->prop->type == PROP_TYPE_OBJ || obj->prop->type == PROP_TYPE_WEAPON)
            {
                propobjInteract(obj->prop);
            }
        }
        Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        break;
    })



#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//##  DESTROY OBJECT
//==============================================================================
// Canonical Name : destroyobj
/* #define CMDNAME OBJECT_DESTROY */
#define CMDNAME DestroyObject

#define DESCRIPTION \
/*******************************************************************************
* Destroy/explode a tagged object
********************************************************************************
* @exception: Only works if object is not destroyed.
*             Can NOT destroy invincible objects
*******************************************************************************/

#define PARAMS BYTE(OBJECT_TAG)

_AI_CMD(
    {
        AIRecord *    ai  = AiListp + Offset;
        ObjectRecord *obj = objFindByTagId(ai->val[0]);
        if (obj && obj->prop)
        {
            if (!objGetDestroyedLevel(obj))
            {
                f32 damage = ((obj->damage - obj->maxdamage) + 1) / 250.0f;
                maybe_detonate_object(obj, damage, &obj->runtime_pos, 29, -1);
            }
        }
        Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        break;
    })

#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## OBJECT DROP FROM CHR
//==============================================================================

/* #define CMDNAME OBJECT_DROP_FROM_CHR */
#define CMDNAME DropObject

#define DESCRIPTION \
/*******************************************************************************
* Drop tagged object held by/attached to chr
********************************************************************************
* @exception: Item must be held by or attached to a chr.
*             Embedded objects will not drop, only works with attached objects.
*             Props can be damaged on drop
*******************************************************************************/

#define PARAMS BYTE(OBJECT_TAG)

_AI_CMD(
    {
        AIRecord *    ai  = AiListp + Offset;
        ObjectRecord *obj = objFindByTagId(ai->val[0]);
        if (obj && obj->prop && obj->prop->parent && obj->prop->parent->type == PROP_TYPE_CHR)
        {
            ChrRecord *chr = obj->prop->parent->chr;
            propobjSetDropped(obj->prop, 2);
            chr->hidden |= CHRHIDDEN_DROP_HELD_ITEMS;
        }
        Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        break;
    })

#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## CHR DROP ALL CONCEALED ITEMS
//==============================================================================

/* #define CMDNAME CHR_DROP_ALL_CONCEALED_ITEMS */
#define CMDNAME ChrDropAllConcealedItems

#define DESCRIPTION \
/*******************************************************************************
* Make chr/me drop all concealed attachments
********************************************************************************
* @param      CHR_NUM: ID of Chr (0-255)
* @exception: Item must be attached to chr, to drop held items use
*             ChrDropAllHeldItems.
*             Embedded objects will not drop, only works with attached objects.
*             Props can be damaged on drop
*******************************************************************************/

#define PARAMS BYTE(CHR_NUM)

_AI_CMD(
    {
        AIRecord * ai  = AiListp + Offset;
        ChrRecord *chr = chrFindById(ChrEntityp, ai->val[0]);
        if (chr && chr->prop)
        {
            chrDropItems(chr);
        }
        Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        break;
    })

// POLYMORPHS

_AI_CMD_POLYMORPH(,
                  DropAllConcealedItems,
                  (CHR_SELF),
                  ())

#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## CHR DROP ALL HELD ITEMS
//==============================================================================

/* #define CMDNAME CHR_DROP_ALL_HELD_ITEMS */
#define CMDNAME ChrDropAllHeldItems

#define DESCRIPTION \
/*******************************************************************************
* Make chr drop all held items
********************************************************************************
* @param      CHR_NUM: ID of Chr (0-255)
* @exception: Items must be held by chr, to drop concealed attachments use
*             ChrDropAllConcealedItems.
*             Embedded objects will not drop, only works with attached objects
*******************************************************************************/

#define PARAMS BYTE(CHR_NUM)

_AI_CMD(
    {
        AIRecord * ai  = AiListp + Offset;
        ChrRecord *chr = chrFindById(ChrEntityp, ai->val[0]);
        if (chr && chr->prop)
        {
            if (chr->weapons_held[GUNRIGHT])
            {
                propobjSetDropped(chr->weapons_held[GUNRIGHT], 1);
                chr->hidden |= CHRHIDDEN_DROP_HELD_ITEMS;
            }
            if (chr->weapons_held[GUNLEFT])
            {
                propobjSetDropped(chr->weapons_held[GUNLEFT], 1);
                chr->hidden |= CHRHIDDEN_DROP_HELD_ITEMS;
            }
        }
        Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        break;
    })

// POLYMORPHS

_AI_CMD_POLYMORPH(,
                  DropAllHeldItems,
                  (CHR_SELF),
                  ())

#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## BOND COLLECT OBJECT
//==============================================================================

/* #define CMDNAME BOND_COLLECT_OBJECT */
#define CMDNAME BondCollectObject

#define DESCRIPTION \
/*******************************************************************************
* Force Bond to instantly collect a tagged object
********************************************************************************
* @exception: Does not trigger bottom text telling player they collected an item
*******************************************************************************/

#define PARAMS BYTE(OBJECT_TAG)

_AI_CMD(
    {
        AIRecord *    ai  = AiListp + Offset;
        ObjectRecord *obj = objFindByTagId(ai->val[0]);
        if (obj && obj->prop)
        {
            bool a = collect_or_interact_object(obj->prop, 0);
            propExecuteTickOperation(obj->prop, a);
        }
        Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        break;
    })

#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## CHR EQUIP OBJECT
//==============================================================================

/* #define CMDNAME CHR_EQUIP_OBJECT */
#define CMDNAME ChrEquipObject

#define DESCRIPTION \
/*******************************************************************************
* Makes chr hold tagged object
********************************************************************************
* @param      CHR_NUM: ID of Chr (0-255)
* @exception: If chrs hands are occupied, object will be equipped as a concealed
*             attachment, but if tagged object's GUNHAND flag is free on guard
*             then guard will equip weapon.
*             Tagged objects Prop MUST have a holding position command within
*             the model file
*******************************************************************************/

#define PARAMS BYTE(OBJECT_TAG), BYTE(CHR_NUM)

_AI_CMD(
    {
        AIRecord *    ai  = AiListp + Offset;
        ObjectRecord *obj = objFindByTagId(ai->val[0]);
        ChrRecord *   chr = chrFindById(ChrEntityp, ai->val[1]);
        if (obj && obj->prop && chr)
        {
            if (obj->prop->parent)
            {
                objDetach(obj->prop);
            }
            else
            {
                chrpropDeregisterRooms(obj->prop);
                chrpropDelist(obj->prop);
                chrpropDisable(obj->prop);
            }
            if (obj->type != PROPDEF_COLLECTABLE || !chrEquipWeapon(obj, chr))
            {
                chrpropReparent(obj->prop, chr->prop);
            }
        }
        Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        break;
    })

#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## Move Object (Canonical Name)
//==============================================================================
//Canonical Name: MoveObj
/* #define CMDNAME OBJECT_MOVE_TO_PAD */
#define CMDNAME MoveObject

#define DESCRIPTION \
/*******************************************************************************
* Move object to PAD
********************************************************************************
* @exception: If object is assigned to padextra type, then object scale will be
*             lost after moving to target pad.
*             Object will inherit rotation from target pad
*******************************************************************************/

#define PARAMS BYTE(OBJECT_TAG), DBYTE(PAD)

_AI_CMD(
    {
        AIRecord *          ai  = AiListp + Offset;
        ObjectRecord *      obj = objFindByTagId(ai->val[0]);
        volatile PadRecord *pad;
        u16                 padnum = CharArrayTo16(ai->val,1);
        Mtxf                matrix;
        if (obj && obj->prop)
        {
            if (isNotBoundPad(padnum))
            {
                pad = &g_CurrentSetup.pads[padnum];
            }
            else
            {
                pad = (PadRecord *)&g_CurrentSetup.boundpads[getBoundPadNum(padnum)];
            }
            matrix_4x4_7F059908(&matrix, 0, 0, 0, -pad->target.x, -pad->target.y, -pad->target.z, pad->up.x, pad->up.y, pad->up.z);
            if (obj->model)
            {
                matrix_scalar_multiply(obj->model->scale, &matrix);
            }
            sub_GAME_7F04088C(obj, pad, &matrix, pad->stan, pad);
            setupUpdateObjectRoomPosition(obj);
        }
        Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        break;
    })

#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## DOOR OPEN
//==============================================================================

/* #define CMDNAME DOOR_OPEN */
#define CMDNAME DoorOpen

#define DESCRIPTION \
/*******************************************************************************
* Open tagged door even if locked
*******************************************************************************/

#define PARAMS BYTE(OBJECT_TAG)

_AI_CMD(
    {
        AIRecord *  ai  = AiListp + Offset;
        DoorRecord *obj = objFindByTagId(ai->val[0]);
        if (obj && obj->prop && obj->prop->type == PROP_TYPE_DOOR)
        {
            doorActivate(obj, DOORSTATE_OPENING);
        }
        Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        break;
    })

#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## DOOR CLOSE
//==============================================================================

/* #define CMDNAME DOOR_CLOSE */
#define CMDNAME DoorClose

#define DESCRIPTION \
/*******************************************************************************
* Close tagged door
*******************************************************************************/

#define PARAMS BYTE(OBJECT_TAG)

_AI_CMD(
    {
        AIRecord *  ai  = AiListp + Offset;
        DoorRecord *obj = objFindByTagId(ai->val[0]);
        if (obj && obj->prop && obj->prop->type == PROP_TYPE_DOOR)
        {
            doorActivate(obj, DOORSTATE_CLOSING);
        }
        Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        break;
    })

#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## IF DOOR STATE EQUAL
//==============================================================================

/* #define CMDNAME IF_DOOR_STATE_EQUAL */
#define CMDNAME IFDoorStateEqual

#define DESCRIPTION \
/*******************************************************************************
* If tagged door state matches any of bit field
********************************************************************************
* @return     Continue execution from LABEL if TRUE
* @exception: Use DOOR_STATE_# flags for door state argument.
*             Flags can be combined
*******************************************************************************/

#define PARAMS BYTE(OBJECT_TAG), BYTE(DOOR_STATE), BYTE(GOTOLABEL)

_AI_CMD(
    {
        AIRecord *    ai   = AiListp + Offset;
        ObjectRecord *obj  = objFindByTagId(ai->val[0]);
        bool          pass = FALSE;
        if (obj && obj->prop && obj->type == PROPDEF_DOOR)
        {
            DoorRecord *door = (DoorRecord *)obj;
            if (door->openstate == DOORSTATE_STATIONARY)
            {
                if (door->openPosition <= 0)
                {
                    pass = (ai->val[1] & DOOR_STATE_CLOSED) != 0;
                }
                else
                {
                    pass = (ai->val[1] & DOOR_STATE_OPEN) != 0;
                }
            }
            else if (door->openstate == DOORSTATE_OPENING || door->openstate == DOORSTATE_WAITING)
            {
                pass = (ai->val[1] & DOOR_STATE_OPENING) != 0;
            }
            else if (door->openstate == DOORSTATE_CLOSING)
            {
                pass = (ai->val[1] & DOOR_STATE_CLOSING) != 0;
            }
        }
        if (pass)
        {
            Offset = chraiGoToLabel(AiListp, Offset, ai->val[2]);
        }
        else
        {
            Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        }
        break;
    })

//POLYMORPHS

#undef DESCRIPTION
#define DESCRIPTION \
/*******************************************************************************
* If tagged door state is ...
********************************************************************************
* @return     Continue execution from LABEL if TRUE
*******************************************************************************/

_AI_CMD_POLYMORPH(,
                    IFDoorClosed,
                    (DOOR_STATE_CLOSED, GOTOLABEL),
                    (GOTOLABEL))
_AI_CMD_POLYMORPH(,
                    IFDoorOpen,
                    (DOOR_STATE_OPEN, GOTOLABEL),
                    (GOTOLABEL))
_AI_CMD_POLYMORPH(,
                    IFDoorClosing,
                    (DOOR_STATE_CLOSING, GOTOLABEL),
                    (GOTOLABEL))
_AI_CMD_POLYMORPH(,
                    IFDoorOpening,
                    (DOOR_STATE_OPENING, GOTOLABEL),
                    (GOTOLABEL))
_AI_CMD_POLYMORPH(,
                    IFDoorOpenOrClosed,
                    (IFDOOR_STATE_OPEN | DOOR_STATE_CLOSED, GOTOLABEL),
                    (GOTOLABEL))
_AI_CMD_POLYMORPH(,
                    IFDoorOpeningOrClosing,
                    (DOOR_STATE_CLOSING | DOOR_STATE_CLOSED, GOTOLABEL),
                    (GOTOLABEL))
_AI_CMD_POLYMORPH(,
                    IFDoorOpenOrClosing,
                    (DOOR_STATE_OPEN | DOOR_STATE_CLOSING, GOTOLABEL),
                    (GOTOLABEL))
_AI_CMD_POLYMORPH(,
                    IFDoorNotOpening,
                    (DOOR_STATE_OPEN | DOOR_STATE_CLOSING | DOOR_STATE_CLOSED, GOTOLABEL),
                    (GOTOLABEL))
_AI_CMD_POLYMORPH(,
                    IFDoorClosedOrOpening,
                    (DOOR_STATE_CLOSED | DOOR_STATE_OPENING, GOTOLABEL),
                    (GOTOLABEL))
_AI_CMD_POLYMORPH(,
                    IFDoorOpenOrOpening,
                    (DOOR_STATE_OPEN | DOOR_STATE_OPENING, GOTOLABEL),
                    (GOTOLABEL))
_AI_CMD_POLYMORPH(,
                    IFDoorNotClosing,
                    (DOOR_STATE_OPEN | DOOR_STATE_OPENING | DOOR_STATE_CLOSED, GOTOLABEL),
                    (GOTOLABEL))
_AI_CMD_POLYMORPH(,
                    IFDoorOpeningOrClosed,
                    (DOOR_STATE_OPENING | DOOR_STATE_CLOSED, GOTOLABEL),
                    (GOTOLABEL))
_AI_CMD_POLYMORPH(,
                    IFDoorNotOpen,
                    (DOOR_STATE_OPENING | DOOR_STATE_CLOSED | DOOR_STATE_CLOSING, GOTOLABEL),
                    (GOTOLABEL))
_AI_CMD_POLYMORPH(,
                    IFDoorNotClosed,
                    (DOOR_STATE_OPEN | DOOR_STATE_OPENING | DOOR_STATE_CLOSING, GOTOLABEL),
                    (GOTOLABEL))

#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## IF DOOR HAS BEEN OPENED BEFORE
//==============================================================================

/* #define CMDNAME IF_DOOR_HAS_BEEN_OPENED_BEFORE */
#define CMDNAME IFDoorHasBeenOpenedBefore

#define DESCRIPTION \
/*******************************************************************************
* If tagged door has been opened before
********************************************************************************
* @return     Continue execution from LABEL if TRUE
* @exception: If tagged door is open by default in setup, then it must be closed
*       before it will check if opened again
*******************************************************************************/

#define PARAMS BYTE(OBJECT_TAG), BYTE(GOTOLABEL)

_AI_CMD(
    {
        AIRecord *    ai  = AiListp + Offset;
        ObjectRecord *obj = objFindByTagId(ai->val[0]);
        if (obj && obj->prop && obj->type == PROPDEF_DOOR && (obj->runtime_bitflags & RUNTIMEBITFLAG_BEENOPENED))
        {
            Offset = chraiGoToLabel(AiListp, Offset, ai->val[1]);
        }
        else
        {
            Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        }
        break;
    })

#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## DOOR SET LOCK
//==============================================================================

/* #define CMDNAME DOOR_SET_LOCK */
#define CMDNAME DoorSetLock

#define DESCRIPTION \
/*******************************************************************************
* Set tagged doors lock with flags
********************************************************************************
* @exception: Use DOOR_LOCK_# flags for LOCK argument.
*       Lock flags are same as used within setup for doors and keys
*******************************************************************************/

#define PARAMS BYTE(OBJECT_TAG), BYTE(LOCK_FLAG)

_AI_CMD(
    {
        AIRecord *    ai  = AiListp + Offset;
        ObjectRecord *obj = objFindByTagId(ai->val[0]);
        if (obj && obj->prop && obj->prop->type == PROP_TYPE_DOOR)
        {
            DoorRecord *door = (DoorRecord *)obj;
            u8          bits = ai->val[1];
            door->keyflags   = door->keyflags | bits;
        }
        Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        break;
    })

#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## DOOR UNSET LOCK
//==============================================================================

/* #define CMDNAME DOOR_UNSET_LOCK */
#define CMDNAME DoorUnsetLock

#define DESCRIPTION \
/*******************************************************************************
* Unset tagged doors lock with flags
********************************************************************************
* @exception: Use DOOR_LOCK_# flags for LOCK argument.
*       Lock flags are same as used within setup for doors and keys
*******************************************************************************/

#define PARAMS BYTE(OBJECT_TAG), BYTE(LOCK_FLAG)

_AI_CMD(
    {
        AIRecord *    ai  = AiListp + Offset;
        ObjectRecord *obj = objFindByTagId(ai->val[0]);
        if (obj && obj->prop && obj->prop->type == PROP_TYPE_DOOR)
        {
            DoorRecord *door = (DoorRecord *)obj;
            u8          bits = ai->val[1];
            door->keyflags &= ~bits;
        }
        Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        break;
    })

#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## IF DOOR LOCK EQUAL
//==============================================================================

/* #define CMDNAME IF_DOOR_LOCK_EQUAL */
#define CMDNAME IFDoorLockEqual

#define DESCRIPTION \
/*******************************************************************************
* If tagged doors lock flags matches any lock flag argument
********************************************************************************
* @return     Continue execution from LABEL if TRUE
* @exception: Use DOOR_LOCK_# flags for LOCK argument.
*       Lock flags are same as used within setup for doors and keys
*******************************************************************************/

#define PARAMS BYTE(OBJECT_TAG), BYTE(LOCK_FLAG), BYTE(GOTOLABEL)

_AI_CMD(
    {
        AIRecord *    ai   = AiListp + Offset;
        ObjectRecord *obj  = objFindByTagId(ai->val[0]);
        bool          pass = FALSE;
        if (obj && obj->prop && obj->prop->type == PROP_TYPE_DOOR)
        {
            DoorRecord *door = (DoorRecord *)obj;
            s32         bits = ai->val[1];
            if ((door->keyflags & bits) == bits)
            {
                pass = TRUE;
            }
        }
        if (pass)
        {
            Offset = chraiGoToLabel(AiListp, Offset, ai->val[2]);
        }
        else
        {
            Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        }
        break;
    })

#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## IF OBJECTIVE NUM COMPLETE
//==============================================================================

/* #define CMDNAME IF_OBJECTIVE_NUM_COMPLETE */
#define CMDNAME IFObjectiveNumComplete

#define DESCRIPTION \
/*******************************************************************************
* If objective ID completed
********************************************************************************
* @return     Continue execution from LABEL if TRUE
* @exception: Ignores difficulty settings.
*             For example - if game on agent and player
*                completes an unlisted 00 agent objective, checking that
*                objective num will Go to LABEL
*******************************************************************************/

#define PARAMS BYTE(OBJ_NUM), BYTE(GOTOLABEL)

_AI_CMD(
    {
        struct
        {
            u8 cmd;
            u8 val;
            u8 label;
        } *ai = AiListp + Offset;
        if (objectiveGetCount() > ai->val && OBJECTIVESTATUS_COMPLETE == objectiveGetStatus(ai->val))
        {
            Offset = chraiGoToLabel(AiListp, Offset, ai->label);
        }
        else
        {
            Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        }
        break;
    })

#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## GUARD TRY UNKNOWN6E
//==============================================================================

/* #define CMDNAME GUARD_TRY_UNKNOWN6E */
#define CMDNAME TRYUnknown6e

#define DESCRIPTION \
/*******************************************************************************
* Unknown command, Go to LABEL
********************************************************************************
* @return     Continue execution from LABEL if Successful
* @exception: Sets chr->padpreset1
*             BITFIELD (hex):
*             0001: Sets to nearest pad to path to Bond
*             0004: ???
*             0008: ???
*             0010: ???
*             0020: ???
*******************************************************************************/

#define PARAMS BYTE(UNKNOWN_FLAG), BYTE(GOTOLABEL)

_AI_CMD(
    {
        AIRecord *ai = AiListp + Offset;
        if (check_2328_preset_set_with_method(ChrEntityp, ai->val[0]))
        {
            Offset = chraiGoToLabel(AiListp, Offset, ai->val[1]);
        }
        else
        {
            Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        }
        break;
    })

#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## GUARD TRY UNKNOWN6F
//==============================================================================

/* #define CMDNAME GUARD_TRY_UNKNOWN6F */
#define CMDNAME TRYUnknown6f

#define DESCRIPTION \
/*******************************************************************************
* Unknown command
********************************************************************************
* @return     Continue execution from LABEL if Successful
* @exception: Sets chr->padpreset1
*             BITFIELD (hex):
*             0001: Sets to nearest pad to path to Bond
*             0004: ???
*             0008: ???
*             0010: ???
*             0020: ???
*******************************************************************************/

#define PARAMS BYTE(UNKNOWN_FLAG), BYTE(GOTOLABEL)

_AI_CMD(
    {
        AIRecord *ai = AiListp + Offset;
        if (sub_GAME_7F033AAC(ChrEntityp, ai->val[0]))
        {
            Offset = chraiGoToLabel(AiListp, Offset, ai->val[1]);
        }
        else
        {
            Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        }
        break;
    })

#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## IF GAME DIFFICULTY LESS THAN
//==============================================================================

/* #define CMDNAME IF_GAME_DIFFICULTY_LESS_THAN */
#define CMDNAME IFGameDifficultyLessThan

#define DESCRIPTION \
/*******************************************************************************
* If current difficulty is less than DIFFICULTY_ID
********************************************************************************
* @return     Continue execution from LABEL if TRUE
* @param DIFICULTY_ID
*               01: agent only
*               02: agent/secret agent
*               03: agent/secret agent/00 agent
*******************************************************************************/

#define PARAMS BYTE(DIFICULTY_ID), BYTE(GOTOLABEL)

_AI_CMD(
    {
        AIRecord *ai = AiListp + Offset;
        if (ai->val[0] > lvlGetSelectedDifficulty())
        {
            Offset = chraiGoToLabel(AiListp, Offset, ai->val[1]);
        }
        else
        {
            Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        }
        break;
    })

#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## IF GAME DIFFICULTY GREATER THAN
//==============================================================================

/* #define CMDNAME IF_GAME_DIFFICULTY_GREATER_THAN */
#define CMDNAME IFGameDifficultyGreaterThan

#define DESCRIPTION \
/*******************************************************************************
* If current difficulty is greater than DIFFICULTY_ID
********************************************************************************
* @return     Continue execution from LABEL if TRUE
* @param DIFICULTY_ID
*               00: secret agent/00 agent/007
*               01: 00 agent/007
*               02: 007 only
*******************************************************************************/

#define PARAMS BYTE(DIFICULTY_ID), BYTE(GOTOLABEL)

_AI_CMD(
    {
        AIRecord *ai = AiListp + Offset;
        if (ai->val[0] < lvlGetSelectedDifficulty())
        {
            Offset = chraiGoToLabel(AiListp, Offset, ai->val[1]);
        }
        else
        {
            Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        }
        break;
    })

#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## IF MISSION TIME LESS THAN
//==============================================================================

/* #define CMDNAME IF_MISSION_TIME_LESS_THAN */
#define CMDNAME IFMissionTimeLessThan

#define DESCRIPTION \
/*******************************************************************************
* If current mission time (in seconds) < SECONDS
********************************************************************************
* @return         Continue execution from LABEL if TRUE
* @param SECONDS: Unsigned Integer seconds, compares against mission timer
*******************************************************************************/

#define PARAMS DBYTE(SECONDS), BYTE(GOTOLABEL)

_AI_CMD(
    {
        AIRecord *ai     = AiListp + Offset;
        f32       target = CharArrayTo16(ai->val,0);
        if (target > lvlGetCurrentMultiPlayerSec())
        {
            Offset = chraiGoToLabel(AiListp, Offset, ai->val[2]);
        }
        else
        {
            Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        }
        break;
    })

#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## IF MISSION TIME GREATER THAN
//==============================================================================

/* #define CMDNAME IF_MISSION_TIME_GREATER_THAN */
#define CMDNAME IFMissionTimeGreaterThan

#define DESCRIPTION \
/*******************************************************************************
* If current mission time (in seconds) > SECONDS
********************************************************************************
* @return         Continue execution from LABEL if TRUE
* @param SECONDS: Unsigned Integer seconds, compares against mission timer
*******************************************************************************/

#define PARAMS DBYTE(SECONDS), BYTE(GOTOLABEL)

_AI_CMD(
    {
        AIRecord *ai     = AiListp + Offset;
        f32       target = CharArrayTo16(ai->val,0);
        if (target < lvlGetCurrentMultiPlayerSec())
        {
            Offset = chraiGoToLabel(AiListp, Offset, ai->val[2]);
        }
        else
        {
            Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        }
        break;
    })

#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## IF SYSTEM POWER TIME LESS THAN
//==============================================================================

/* #define CMDNAME IF_SYSTEM_POWER_TIME_LESS_THAN */
#define CMDNAME IFSystemPowerTimeLessThan

#define DESCRIPTION \
/*******************************************************************************
* If system powered on time (in minutes) < MINUTES
********************************************************************************
* @return         Continue execution from LABEL if TRUE
* @param MINUTES: Unsigned Integer minutes, compares against mission time
r*******************************************************************************/

#define PARAMS DBYTE(MINUTES), BYTE(GOTOLABEL)

_AI_CMD(
    {
        AIRecord *ai     = AiListp + Offset;
        f32       target = CharArrayTo16(ai->val,0) * CHRLV_FRAMERATE_F;
        if (target > lvlGetCurrentMultiPlayerMin())
        {
            Offset = chraiGoToLabel(AiListp, Offset, ai->val[2]);
        }
        else
        {
            Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        }
        break;
    })

#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## IF SYSTEM POWER TIME GREATER THAN
//==============================================================================

/* #define CMDNAME IF_SYSTEM_POWER_TIME_GREATER_THAN */
#define CMDNAME IFSystemPowerTimeGreaterThan

#define DESCRIPTION \
/*******************************************************************************
* If system powered on time (in minutes) > MINUTES
********************************************************************************
* @return         Continue execution from LABEL if TRUE
* @param MINUTES: Unsigned Integer minutes, compares against mission time
*******************************************************************************/

#define PARAMS DBYTE(MINUTES), BYTE(GOTOLABEL)

_AI_CMD(
    {
        AIRecord *ai     = AiListp + Offset;
        f32       target = CharArrayTo16(ai->val,0) * CHRLV_FRAMERATE_F;
        if (target < lvlGetCurrentMultiPlayerMin())
        {
            Offset = chraiGoToLabel(AiListp, Offset, ai->val[2]);
        }
        else
        {
            Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        }
        break;
    })

#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## IF LEVEL ID LESS THAN
//==============================================================================

/* #define CMDNAME IF_LEVEL_ID_LESS_THAN */
#define CMDNAME IFLevelIdLessThan

#define DESCRIPTION \
/*******************************************************************************
* If current level id < LEVEL_ID
********************************************************************************
* @return          Continue execution from LABEL if TRUE
* @param LEVEL_ID: Uses LEVELID enum values, not briefing menu stage number
*******************************************************************************/

#define PARAMS BYTE(LEVEL_ID), BYTE(GOTOLABEL)

_AI_CMD(
    {
        AIRecord *ai = AiListp + Offset;
        if (ai->val[0] > bossGetStageNum())
        {
            Offset = chraiGoToLabel(AiListp, Offset, ai->val[1]);
        }
        else
        {
            Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        }
        break;
    })

#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## IF LEVEL ID GREATER THAN
//==============================================================================

/* #define CMDNAME IF_LEVEL_ID_GREATER_THAN */
#define CMDNAME IFLevelIdGreaterThan

#define DESCRIPTION \
/*******************************************************************************
* If current level id > LEVEL_ID
********************************************************************************
* @return          Continue execution from LABEL if TRUE
* @param LEVEL_ID: Uses LEVELID enum values, not briefing menu stage number
*******************************************************************************/

#define PARAMS BYTE(LEVEL_ID), BYTE(GOTOLABEL)

_AI_CMD(
    {
        AIRecord *ai = AiListp + Offset;
        if (ai->val[0] < bossGetStageNum())
        {
            Offset = chraiGoToLabel(AiListp, Offset, ai->val[1]);
        }
        else
        {
            Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        }
        break;
    })

#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## IF GUARD HITS LESS THAN
//==============================================================================

/* #define CMDNAME IF_GUARD_HITS_LESS_THAN */
#define CMDNAME IFMyNumArghsLessThan

#define DESCRIPTION \
/*******************************************************************************
* If I have been hit less than HIT_NUM
********************************************************************************
* @return     Continue execution from LABEL if TRUE
* @param      HIT_NUM: Unsigned Integer to test against numarghs
* @exception: Hits count even if guard is invincible
*******************************************************************************/

#define PARAMS BYTE(HIT_NUM), BYTE(GOTOLABEL)

_AI_CMD(
    {
        AIRecord *ai = AiListp + Offset;
        if (ai->val[0] > chrGetNumArghs(ChrEntityp))
        {
            Offset = chraiGoToLabel(AiListp, Offset, ai->val[1]);
        }
        else
        {
            Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        }
        break;
    })

#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## IF GUARD HITS GREATER THAN
//==============================================================================

/* #define CMDNAME IF_GUARD_HITS_GREATER_THAN */
#define CMDNAME IFMyNumArghsGreaterThan

#define DESCRIPTION \
/*******************************************************************************
* If I have been Hit Greater Than HIT_NUM
********************************************************************************
* @return     Continue execution from LABEL if TRUE
* @param      HIT_NUM: Unsigned Integer to test against numarghs
* @exception: Hits count even if guard is invincible
*******************************************************************************/

#define PARAMS BYTE(HIT_NUM), BYTE(GOTOLABEL)

_AI_CMD(
    {
        AIRecord *ai = AiListp + Offset;
        if (ai->val[0] < chrGetNumArghs(ChrEntityp))
        {
            Offset = chraiGoToLabel(AiListp, Offset, ai->val[1]);
        }
        else
        {
            Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        }
        break;
    })

#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## IF GUARD HITS MISSED LESS THAN
//==============================================================================

/* #define CMDNAME IF_GUARD_HITS_MISSED_LESS_THAN */
#define CMDNAME IFMyNumCloseArghsLessThan

#define DESCRIPTION \
/*******************************************************************************
* If Bond's shots missed/landed near me less than MISSED_NUM
********************************************************************************
* @return     Continue execution from LABEL if TRUE
* @param      MISSED_NUM: Unsigned Integer to test against numclosearghs
*******************************************************************************/

#define PARAMS BYTE(MISSED_NUM), BYTE(GOTOLABEL)

_AI_CMD(
    {
        AIRecord *ai = AiListp + Offset;
        if (ai->val[0] > chrGetNumCloseArghs(ChrEntityp))
        {
            Offset = chraiGoToLabel(AiListp, Offset, ai->val[1]);
        }
        else
        {
            Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        }
        break;
    })

#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## IF GUARD HITS MISSED GREATER THAN
//==============================================================================

/* #define CMDNAME IF_GUARD_HITS_MISSED_GREATER_THAN */
#define CMDNAME IFMyNumCloseArghsGreaterThan

#define DESCRIPTION \
/*******************************************************************************
* If Bond's shots missed/landed near me Greater Than MISSED_NUM
********************************************************************************
* @return     Continue execution from LABEL if TRUE
* @param      MISSED_NUM: Unsigned Integer to test against numclosearghs
*******************************************************************************/

#define PARAMS BYTE(MISSED_NUM), BYTE(GOTOLABEL)

_AI_CMD(
    {
        AIRecord *ai = AiListp + Offset;
        if (ai->val[0] < chrGetNumCloseArghs(ChrEntityp))
        {
            Offset = chraiGoToLabel(AiListp, Offset, ai->val[1]);
        }
        else
        {
            Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        }
        break;
    })

#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## IF CHR HEALTH LESS THAN
//==============================================================================

/* #define CMDNAME IF_CHR_HEALTH_LESS_THAN */
#define CMDNAME IFChrHealthLessThan

#define DESCRIPTION \
/*******************************************************************************
* If My/chrs health < HEALTH
********************************************************************************
* @return     Continue execution from LABEL if TRUE
* @param      CHR_NUM: ID of Chr (0-255)
* @param      HEALTH (Default = 40 (4.0f))
* @exception: HEALTH is unsigned, converted to float and compares difference
*             between chr->maxdamage - chr->damage.
*             Armour is tested
*******************************************************************************/

#define PARAMS BYTE(CHR_NUM), BYTE(HEALTH), BYTE(GOTOLABEL)

_AI_CMD(
    {
        AIRecord * ai    = AiListp + Offset;
        f32        value = (ai->val[1]) * 0.1f;
        ChrRecord *chr   = chrFindById(ChrEntityp, ai->val[0]);
        if (chr && ((chr->maxdamage - chr->damage) < value))
        {
            Offset = chraiGoToLabel(AiListp, Offset, ai->val[2]);
        }
        else
        {
            Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        }
        break;
    })

//POLYMORPHS

_AI_CMD_POLYMORPH(,
                    IFChrHealthLessThanF,
                    (CHR_NUM,(u8)(HEALTH*10), GOTOLABEL),
                    (CHR_NUM,HEALTH,GOTOLABEL))
_AI_CMD_POLYMORPH(,
                    IFMyHealthLessThan,
                    (CHR_SELF,HEALTH, GOTOLABEL),
                    (HEALTH, GOTOLABEL))
_AI_CMD_POLYMORPH(,
                    IFMyHealthLessThanF,
                    (CHR_SELF, (u8)(HEALTH*10), GOTOLABEL),
                    (HEALTH,GOTOLABEL))
#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## IF CHR HEALTH GREATER THAN
//==============================================================================

/* #define CMDNAME IF_CHR_HEALTH_GREATER_THAN */
#define CMDNAME IFChrHealthGreaterThan

#define DESCRIPTION \
/*******************************************************************************
* If My/chrs health > HEALTH
********************************************************************************
* @return     Continue execution from LABEL if TRUE
* @param      CHR_NUM: ID of Chr (0-255)
* @param      HEALTH (Default = 40 (4.0f))
* @exception: HEALTH is unsigned, converted to float and compares difference
*             between chr->maxdamage - chr->damage.
*             Armour is tested
*******************************************************************************/

#define PARAMS BYTE(CHR_NUM), BYTE(HEALTH), BYTE(GOTOLABEL)

_AI_CMD(
    {
        AIRecord * ai    = AiListp + Offset;
        f32        value = (ai->val[1]) * 0.1f;
        ChrRecord *chr   = chrFindById(ChrEntityp, ai->val[0]);
        if (chr && ((chr->maxdamage - chr->damage) > value))
        {
            Offset = chraiGoToLabel(AiListp, Offset, ai->val[2]);
        }
        else
        {
            Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        }
        break;
    })

//POLYMORPHS

_AI_CMD_POLYMORPH(,
                    IFChrHealthGreaterThanF,
                    (CHR_NUM,(u8)(HEALTH*10), GOTOLABEL),
                    (CHR_NUM,HEALTH,GOTOLABEL))
_AI_CMD_POLYMORPH(,
                    IFMyHealthGreaterThan,
                    (CHR_SELF,HEALTH, GOTOLABEL),
                    (HEALTH, GOTOLABEL))
_AI_CMD_POLYMORPH(,
                    IFMyHealthGreaterThanF,
                    (CHR_SELF, (u8)(HEALTH*10), GOTOLABEL),
                    (HEALTH,GOTOLABEL))

#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## IF CHR WAS DAMAGED SINCE LAST CHECK
//==============================================================================

/* #define CMDNAME IF_CHR_WAS_DAMAGED_SINCE_LAST_CHECK */
#define CMDNAME IFChrWasDamagedSinceLastCheck

#define DESCRIPTION \
/*******************************************************************************
* If chr has taken damage since last check
********************************************************************************
* @return     Continue execution from LABEL if TRUE
* @param      CHR_NUM: ID of Chr (0-255)
* @exception: Checks chr->chrflags if CHRFLAG_WAS_DAMAGED is set.
*             If true, unset flag and Go to LABEL.
*             CHRFLAG_WAS_DAMAGED is set if guard took damage (not invincible)
*******************************************************************************/

#define PARAMS BYTE(CHR_NUM), BYTE(GOTOLABEL)

_AI_CMD(
    {
        AIRecord * ai  = AiListp + Offset;
        ChrRecord *chr = chrFindById(ChrEntityp, ai->val[0]);
        if (chr && (chr->chrflags & CHRFLAG_WAS_DAMAGED))
        {
            chr->chrflags &= ~CHRFLAG_WAS_DAMAGED;
            Offset = chraiGoToLabel(AiListp, Offset, ai->val[1]);
        }
        else
        {
            Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        }
        break;
    })

//POLYMORPHS

_AI_CMD_POLYMORPH(,
                    IFIWasDamagedSinceLastCheck,
                    (CHR_SELF, GOTOLABEL),
                    (GOTOLABEL))

#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## IF BOND HEALTH LESS THAN
//==============================================================================

/* #define CMDNAME IF_BOND_HEALTH_LESS_THAN */
#define CMDNAME IFBondHealthLessThan

#define DESCRIPTION \
/*******************************************************************************
* If Bond's health < HEALTH
********************************************************************************
* @return     Continue execution from LABEL if TRUE
* @param      HEALTH u8 where 255 equal to 100% health
* @exception: Does NOT check armour.
*******************************************************************************/

#define PARAMS BYTE(HEALTH), BYTE(GOTOLABEL)

_AI_CMD(
    {
        AIRecord *ai  = AiListp + Offset;
        float     val = (ai->val[0]) / 255.0f;
        if (val > bondviewGetCurrentPlayerHealth())
        {
            Offset = chraiGoToLabel(AiListp, Offset, ai->val[1]);
        }
        else
        {
            Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        }
        break;
    })

#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## IF BOND HEALTH GREATER THAN
//==============================================================================

/* #define CMDNAME IF_BOND_HEALTH_GREATER_THAN */
#define CMDNAME IFBondHealthGreaterThan

#define DESCRIPTION \
/*******************************************************************************
* If Bond's health > HEALTH
********************************************************************************
* @return     Continue execution from LABEL if TRUE
* @param      HEALTH u8 where 255 equal to 100% health
* @exception: Does NOT check armour.
*******************************************************************************/

#define PARAMS BYTE(HEALTH), BYTE(GOTOLABEL)

_AI_CMD(
    {
        AIRecord *ai  = AiListp + Offset;
        float     val = (ai->val[0]) / 255.0f;
        if (val < bondviewGetCurrentPlayerHealth())
        {
            Offset = chraiGoToLabel(AiListp, Offset, ai->val[1]);
        }
        else
        {
            Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        }
        break;
    })

#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## LOCAL BYTE 1 SET
//==============================================================================

/* #define CMDNAME LOCAL_BYTE_1_SET */
#define CMDNAME SetMyMorale

#define DESCRIPTION \
/*******************************************************************************
* Set My Morale
********************************************************************************
* @param      CHRBYTE is a value 0 - 255
*******************************************************************************/

#define PARAMS BYTE(CHRBYTE)

_AI_CMD(
    {
        AIRecord1 *ai      = AiListp + Offset;
        ChrEntityp->morale = ai->val;
        Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        break;
    })

#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## LOCAL BYTE 1 ADD
//==============================================================================

/* #define CMDNAME LOCAL_BYTE_1_ADD */
#define CMDNAME AddToMyMorale

#define DESCRIPTION \
/*******************************************************************************
* Add To My Morale - Make me Happier
********************************************************************************
* @param      CHRBYTE is a value 0 - 255
* @exception Anything above 255 is clamped to 255
*******************************************************************************/

#define PARAMS BYTE(CHRBYTE)

_AI_CMD(
    {
        AIRecord1 *ai = AiListp + Offset;
        if (255 - ai->val < ChrEntityp->morale)
        {
            ChrEntityp->morale = 255;
        }
        else
        {
            ChrEntityp->morale += ai->val;
        }
        Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        break;
    })

#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## LOCAL BYTE 1 SUBTRACT
//==============================================================================

/* #define CMDNAME LOCAL_BYTE_1_SUBTRACT */
#define CMDNAME SubtractFromMyMorale

#define DESCRIPTION \
/*******************************************************************************
* Subtract from My Morale - Make me Sad
********************************************************************************
* @param      CHRBYTE is a value 0 - 255
* @exception Anything below 0 is clamped to 0
*******************************************************************************/

#define PARAMS BYTE(CHRBYTE)

_AI_CMD(
    {
        AIRecord1 *ai = AiListp + Offset;
        if (ai->val > ChrEntityp->morale)
        {
            ChrEntityp->morale = 0;
        }
        else
        {
            ChrEntityp->morale -= ai->val;
        }
        Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        break;
    })

#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## IF LOCAL BYTE 1 LESS THAN
//==============================================================================

/* #define CMDNAME IF_LOCAL_BYTE_1_LESS_THAN */
#define CMDNAME IFMyMoraleLessThan

#define DESCRIPTION \
/*******************************************************************************
* If My Morale Less Than
********************************************************************************
* @return     Continue execution from LABEL if TRUE
*******************************************************************************/

#define PARAMS BYTE(CHRBYTE), BYTE(GOTOLABEL)

_AI_CMD(
    {
        AIRecord *ai = AiListp + Offset;
        if (ai->val[0] > ChrEntityp->morale)
        {
            Offset = chraiGoToLabel(AiListp, Offset, ai->val[1]);
        }
        else
        {
            Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        }
        break;
    })

#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## IF LOCAL BYTE 1 LESS THAN RANDOM SEED
//==============================================================================

/* #define CMDNAME IF_LOCAL_BYTE_1_LESS_THAN_RANDOM_SEED */
#define CMDNAME IFMyMoraleLessThanRandom

#define DESCRIPTION \
/*******************************************************************************
* If My Morale is Less Than My Random (Use SetRandom)
********************************************************************************
* @return     Continue execution from LABEL if TRUE
*******************************************************************************/

#define PARAMS BYTE(GOTOLABEL)

_AI_CMD(
    {
        AIRecord *ai = AiListp + Offset;
        if (ChrEntityp->morale < ChrEntityp->random)
        {
            Offset = chraiGoToLabel(AiListp, Offset, ai->val[0]);
        }
        else
        {
            Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        }
        break;
    })

#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## LOCAL BYTE 2 SET
//==============================================================================

/* #define CMDNAME LOCAL_BYTE_2_SET */
#define CMDNAME SetMyAlertness

#define DESCRIPTION \
/*******************************************************************************
* Set My Morale
********************************************************************************
* @param      CHRBYTE is a value 0 - 255
*******************************************************************************/

#define PARAMS BYTE(CHRBYTE)

_AI_CMD(
    {
        AIRecord1 *ai         = AiListp + Offset;
        ChrEntityp->alertness = ai->val;
        Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        break;
    })

#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## LOCAL BYTE 2 ADD
//==============================================================================

/* #define CMDNAME LOCAL_BYTE_2_ADD */
#define CMDNAME AddToMyAlertness

#define DESCRIPTION \
/*******************************************************************************
* Add To My Alertness - Make me See Things
********************************************************************************
* @param      CHRBYTE is a value 0 - 255
* @exception Anything above 255 is clamped to 255
*******************************************************************************/

#define PARAMS BYTE(CHRBYTE)

_AI_CMD(
    {
        AIRecord1 *ai = AiListp + Offset;
        if (255 - ai->val < ChrEntityp->alertness)
        {
            ChrEntityp->alertness = 255;
        }
        else
        {
            ChrEntityp->alertness += ai->val;
        }
        Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        break;
    })

#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## LOCAL BYTE 2 SUBTRACT
//==============================================================================

/* #define CMDNAME LOCAL_BYTE_2_SUBTRACT */
#define CMDNAME SubtractFromMyAlertness

#define DESCRIPTION \
/*******************************************************************************
* Subtract from My Morale - Make me Blind
********************************************************************************
* @param      CHRBYTE is a value 0 - 255
* @exception Anything below 0 is clamped to 0
*******************************************************************************/

#define PARAMS BYTE(CHRBYTE)

_AI_CMD(
    {
        AIRecord1 *ai = AiListp + Offset;
        if (ai->val > ChrEntityp->alertness)
        {
            ChrEntityp->alertness = 0;
        }
        else
        {
            ChrEntityp->alertness -= ai->val;
        }
        Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        break;
    })

#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## IF LOCAL BYTE 2 LESS THAN
//==============================================================================

/* #define CMDNAME IF_LOCAL_BYTE_2_LESS_THAN */
#define CMDNAME IFMyAlertnessLessThan

#define DESCRIPTION \
/*******************************************************************************
* If My Alertness is Less Than
********************************************************************************
* @return     Continue execution from LABEL if TRUE
*******************************************************************************/

#define PARAMS BYTE(CHRBYTE), BYTE(GOTOLABEL)

_AI_CMD(
    {
        AIRecord *ai = AiListp + Offset;
        if (ai->val[0] > ChrEntityp->alertness)
        {
            Offset = chraiGoToLabel(AiListp, Offset, ai->val[1]);
        }
        else
        {
            Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        }
        break;
    })

#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## IF LOCAL BYTE 2 LESS THAN RANDOM SEED
//==============================================================================

/* #define CMDNAME IF_LOCAL_BYTE_2_LESS_THAN_RANDOM_SEED */
#define CMDNAME IFMyAlertnessLessThanRandom

#define DESCRIPTION \
/*******************************************************************************
* If My Morale is Less Than My Random (Use SetRandom)
********************************************************************************
* @return     Continue execution from LABEL if TRUE
*******************************************************************************/

#define PARAMS BYTE(GOTOLABEL)

_AI_CMD(
    {
        AIRecord *ai = AiListp + Offset;
        if (ChrEntityp->alertness < ChrEntityp->random)
        {
            Offset = chraiGoToLabel(AiListp, Offset, ai->val[0]);
        }
        else
        {
            Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        }
        break;
    })

#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## GUARD SET HEARING SCALE
//==============================================================================

/* #define CMDNAME GUARD_SET_HEARING_SCALE */
#define CMDNAME SetMyHearingScale

#define DESCRIPTION \
/*******************************************************************************
* Set My hearing scale - the higher the value, the further away I
* can hear Bond's gunfire
********************************************************************************
* @param      HEARING_SCALE 0.0 - 32.0 (Default = 1.0f)
* @exception: HEARING_SCALE is converted to float and divided by 1000 before setting
*             to hearingscale
*******************************************************************************/

#define PARAMS DBYTE(HEARING_SCALE)

_AI_CMD(
    {
        AIRecord *ai             = AiListp + Offset;
        f32       distance       = CharArrayTo16(ai->val,0) / 1000.0f;
        ChrEntityp->hearingscale = distance;
        Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        break;
    })

//POLYMORPHS

_AI_CMD_POLYMORPH(,
                    SetMyHearingScaleF,
                    ((u16)(HEARING_SCALE * 1000)),
                    (HEARING_SCALE))

#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## GUARD SET VISION RANGE
//==============================================================================

/* #define CMDNAME GUARD_SET_VISION_RANGE */
#define CMDNAME SetMyVisionRange

#define DESCRIPTION \
/*******************************************************************************
* Set My vision range - the smaller the value, the longer I
* take to detect Bond with IFISeeBond.
********************************************************************************
* @param      VISION_RANGE 0-255 (Default = 100)
* @exception: Sets to chr->visionrange.
*             Does NOT affect firing distance
*******************************************************************************/

#define PARAMS BYTE(VISION_RANGE)

_AI_CMD(
    {
        AIRecord *ai            = AiListp + Offset;
        ChrEntityp->visionrange = (ai->val[0]);
        Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        break;
    })

#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## GUARD SET GRENADE PROBABILITY
//==============================================================================

/* #define CMDNAME GUARD_SET_GRENADE_PROBABILITY */
#define CMDNAME SetMyGrenadeProbability

#define DESCRIPTION \
/*******************************************************************************
* Set My probability of lobbing a grenade  -  the higher the value, the
* more likely I will throw one when asked (TRYThrowingGrenade).
********************************************************************************
* @exception: Sets to chr->grenadeprob - (Default = 0)
*             The only way to make guards throw grenades is by using this
*             command or assigning setup object 0x12 to chr
*******************************************************************************/

#define PARAMS BYTE(GRENADE_PROB)

_AI_CMD(
    {
        AIRecord *ai            = AiListp + Offset;
        ChrEntityp->grenadeprob = ai->val[0];
        Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        break;
    })

#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## GUARD SET CHR NUM
//==============================================================================

/* #define CMDNAME GUARD_SET_CHR_NUM */
#define CMDNAME SetMyChrNum

#define DESCRIPTION \
/*******************************************************************************
* Set My Chr Number, or ID, for calling me later. e.g. respawn or clone.
********************************************************************************
* @param CHR_NUM: Should be a unique Integer 0-240
*******************************************************************************/

#define PARAMS BYTE(CHR_NUM)

_AI_CMD(
    {
        AIRecord *ai       = AiListp + Offset;
        ChrEntityp->chrnum = ai->val[0];
        Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        break;
    })

#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## GUARD SET HEALTH TOTAL
//==============================================================================

/* #define CMDNAME GUARD_SET_HEALTH_TOTAL */
#define CMDNAME SetMyHealthTotal

#define DESCRIPTION \
/*******************************************************************************
* Set My total HEALTH - the higher the value, the more shots needed
* to kill Me.
********************************************************************************
* @param      HEALTH: unsigned float (Default = 4.0) -1 will be set to 6553.5f
* @exception: Sets to chr->maxdamage.
*             HEALTH is converted to float and divided by 10 before setting to
*             maxdamage.
*             Will use 007 health modifier If difficulty mode 007 is active
*******************************************************************************/

#define PARAMS DBYTE(HEALTH)

_AI_CMD(
    {
        AIRecord *ai     = AiListp + Offset;
        f32       amount = CharArrayTo16(ai->val,0) * 0.1f;
        chrSetMaxDamage(ChrEntityp, amount);
        Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        break;
    })

//POLYMORPHS

_AI_CMD_POLYMORPH(,
                    SetMyHealthTotalF,
                    ((u16)(HEALTH*10)),
                    (HEALTH))
#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## GUARD SET ARMOUR
//==============================================================================

/* #define CMDNAME GUARD_SET_ARMOUR */
#define CMDNAME SetMyArmour

#define DESCRIPTION \
/*******************************************************************************
* Set My armour value - the higher the value, the higher the armour.
* Armoured guards will not show hit reactions. They also do NOT instantly die
* from explosions, instead taking damaged based on how close they are to
* explosions like Bond. To any setup designers reading this, please use armour
* sparingly!
********************************************************************************
* @param      ARMOUR: Unsigned (Default = 0)
* @exception: Subtracts from chr->damage - negative damage means guard has armour.
*             Instead of storing armour as a separate chr variable, we reuse the
*             current damage and read negative damage as armour.
*             Will use 007 health modifier if difficulty mode 007 is active.
*             -1 will be set to 6553.5f armour, or -6553.5f damage (Invincible)
*******************************************************************************/

#define PARAMS DBYTE(AMOUNT)

_AI_CMD(
    {
        AIRecord *ai     = AiListp + Offset;
        f32       amount = CharArrayTo16(ai->val,0) * 0.1f; /*if (cheatIsActive(CHEAT_ENEMYSHIELDS) { amount = amount < 8 ? 8 : amount; }*/
        chrAddHealth(ChrEntityp, amount);
        Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        break;
    })

//POLYMORPHS

_AI_CMD_POLYMORPH(,
                    SetMyArmourF,
                    ((u16)(AMOUNT*10)),
                    (AMOUNT))

#undef DESCRIPTION
#define DESCRIPTION \
/*******************************************************************************
* Remove All damage from me
********************************************************************************
* @return     Continue execution from DONELABEL
* @param      LOOPLABEL: Private ID for internal loop
*******************************************************************************/
_AI_CMD_POLYMORPH( \
    DO(LOOPLABEL) \
        IFMyHealthGreaterThanF(3.5,DONELABEL),
        RemoveMyDamage, (5) \
    LOOP(LOOPLABEL), /* Loop till health > 3 (damage < 1) */
                (LOOPLABEL, DONELABEL)/*PolyParams*/
                )
#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## GUARD SET SPEED RATING
//==============================================================================

/* #define CMDNAME GUARD_SET_SPEED_RATING */
#define CMDNAME SetMySpeedRating

#define DESCRIPTION \
/*******************************************************************************
* Set My speed rating - controls how quickly I animate.
********************************************************************************
* @param      SPEED_RATING: Signed* 0 - 100 (Default = 0)
* @exception: Sets to chr->speedrating.
*             Negative values will make guard animate slower - this affects
*             firing animations.
*             Does NOT use 007 reaction speed modifier.
*             Do NOT use values above 100 or below 0 or it may crash
*******************************************************************************/

#define PARAMS BYTE(SPEED_RATING)

_AI_CMD(
    {
        struct
        {
            u8 cmd;
            s8 val;
        } *ai                   = AiListp + Offset;
        ChrEntityp->speedrating = ai->val;
        Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        break;
    })

#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## GUARD SET ARGH RATING
//==============================================================================

/* #define CMDNAME GUARD_SET_ARGH_RATING */
#define CMDNAME SetMyArghRating

#define DESCRIPTION \
/*******************************************************************************
* Set My argh rating - controls how quickly I recover from being shot.
********************************************************************************
* @param      ARGH_RATING Signed* 0-100 (Default = 0) (100 is almost no hit
*             reaction)
* @exception: Sets to chr->arghrating.
*             Negative values will make guard animate slower - this affects
*             firing animations.
*             Does NOT use 007 reaction speed modifier
*             Do NOT use values above 100 or below 0 or it may crash
*******************************************************************************/

#define PARAMS BYTE(ARGH_RATING)

_AI_CMD(
    {
        struct
        {
            u8 cmd;
            s8 val;
        } *ai = AiListp + Offset;
        ChrEntityp->arghrating = ai->val;
        Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        break;
    })

#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## GUARD SET ACCURACY RATING
//==============================================================================

/* #define CMDNAME GUARD_SET_ACCURACY_RATING */
#define CMDNAME SetMyAccuracyRating

#define DESCRIPTION \
/*******************************************************************************
* Set My Accuracy Rating - controls how accurately I fire my weapon
********************************************************************************
* @param      ACCURACY_RATING: Signed* 0 - 100 (Default = 0)
* @exception: Sets to chr->accuracyrating.
*             Does NOT use 007 accuracy modifier
*******************************************************************************/

#define PARAMS BYTE(ACCURACY_RATING)

_AI_CMD(
    {
        struct
        {
            u8 cmd;
            s8 val;
        } *ai                      = AiListp + Offset;
        ChrEntityp->accuracyrating = ai->val;
        Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        break;
    })

#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## GUARD FLAGS2 SET ON
//==============================================================================

/* #define CMDNAME GUARD_BITFIELD_SET_ON */
#define CMDNAME SetMyFlags2

#define DESCRIPTION \
/*******************************************************************************
* Set My/BG flags2
********************************************************************************
* @param BITS: Can be used to store a custom flag per chr, useful for missions.
*              Global Lists use flag 01, which is defined as
*              FLAGS2_DONT_POINT_AT_BOND.
*              Other bits are free to use for setups AI Lists.
*              BG Lists are free to utilize the entire spectrum of flags
*******************************************************************************/

#define PARAMS BYTE(BITS)

_AI_CMD(
    {
        AIRecord *ai = AiListp + Offset;
        chrSetFlags(ChrEntityp, ai->val[0]);
        Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        break;
    })

//POLYMORPHS

_AI_CMD_POLYMORPH(,
                  SetBGFlags2,
                  (BITS),
                  (BITS))

#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE






//==============================================================================
//## GUARD FLAGS2 SET OFF
//==============================================================================

/* #define CMDNAME GUARD_BITFIELD_SET_OFF */
#define CMDNAME UnsetMyFlags2

#define DESCRIPTION \
/*******************************************************************************
* Unset flags2
********************************************************************************
* @param BITS: Can be used to store a custom flag per chr, useful for missions.
*              Global Lists use flag 01, which is defined as
*              FLAGS2_DONT_POINT_AT_BOND.
*              Other bits are free to use for setups AI Lists.
*              BG Lists are free to utilize the entire spectrum of flags
*******************************************************************************/

#define PARAMS BYTE(BITS)

_AI_CMD(
    {
        AIRecord *ai = AiListp + Offset;
        chrUnsetFlags(ChrEntityp, ai->val[0]);
        Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        break;
    })

//POLYMORPHS

_AI_CMD_POLYMORPH(,
                  UnsetBGFlags2,
                  (BITS),
                  (BITS))

#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## IF GUARD FLAGS2 IS SET ON
//==============================================================================

/* #define CMDNAME IF_GUARD_BITFIELD_IS_SET_ON */
#define CMDNAME IFMyFlags2Has

#define DESCRIPTION \
/*******************************************************************************
* If flags2 has BITS enabled
********************************************************************************
* @return     Continue execution from LABEL if TRUE
* @param BITS: Can be used to store a custom flag per chr, useful for missions.
*              Global Lists use flag 01, which is defined as
*              FLAGS2_DONT_POINT_AT_BOND.
*              Other bits are free to use for setups AI Lists.
*              BG Lists are free to utilize the entire spectrum of flags
*******************************************************************************/

#define PARAMS BYTE(BITS), BYTE(GOTOLABEL)

_AI_CMD(
    {
        AIRecord *ai = AiListp + Offset;
        if (chrHasFlag(ChrEntityp, ai->val[0]))
        {
            Offset = chraiGoToLabel(AiListp, Offset, ai->val[1]);
        }
        else
        {
            Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        }
        break;
    })

//POLYMORPHS

_AI_CMD_POLYMORPH(,
                  IFBGFlags2Has,
                  (BITS),
                  (BITS))

#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## CHR FLAGS2 SET ON
//==============================================================================

/* #define CMDNAME CHR_BITFIELD_SET_ON */
#define CMDNAME SetChrBitfield

#define DESCRIPTION \
/*******************************************************************************
* Set flags2 for CHR_NUM
********************************************************************************
* @param CHR_NUM: ID of Chr (0-240)
* @param BITS: Can be used to store a custom flag per chr, useful for missions.
*              Global Lists use flag 01, which is defined as
*              FLAGS2_DONT_POINT_AT_BOND.
*              Other bits are free to use for setups AI Lists.
*              BG Lists are free to utilize the entire spectrum of flags
*******************************************************************************/

#define PARAMS BYTE(CHR_NUM), BYTE(BITS)

_AI_CMD(
    {
        AIRecord *ai = AiListp + Offset;
        chrSetFlagsById(ChrEntityp, ai->val[0], ai->val[1]);
        Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        break;
    })

#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## CHR FLAGS2 SET OFF
//==============================================================================

/* #define CMDNAME CHR_BITFIELD_SET_OFF */
#define CMDNAME UnsetChrBitfield

#define DESCRIPTION \
/*******************************************************************************
* Unset flags2 for CHR_NUM
********************************************************************************
* @param CHR_NUM: ID of Chr (0-240)
* @param BITS: Can be used to store a custom flag per chr, useful for missions.
*              Global Lists use flag 01, which is defined as
*              FLAGS2_DONT_POINT_AT_BOND.
*              Other bits are free to use for setups AI Lists.
*              BG Lists are free to utilize the entire spectrum of flags
*******************************************************************************/

#define PARAMS BYTE(CHR_NUM), BYTE(BITS)

_AI_CMD(
    {
        AIRecord *ai = AiListp + Offset;
        chrUnsetFlagsById(ChrEntityp, ai->val[0], ai->val[1]);
        Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        break;
    })

#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## IF CHR FLAGS2 IS SET ON
//==============================================================================

/* #define CMDNAME IF_CHR_BITFIELD_IS_SET_ON */
#define CMDNAME IFChrBitfieldHas

#define DESCRIPTION \
/*******************************************************************************
* If flags2 has BITS enabled
********************************************************************************
* @return         Continue execution from LABEL if TRUE
* @param CHR_NUM: ID of Chr (0-240)
* @param BITS: Can be used to store a custom flag per chr, useful for missions.
*              Global Lists use flag 01, which is defined as
*              FLAGS2_DONT_POINT_AT_BOND.
*              Other bits are free to use for setups AI Lists.
*              BG Lists are free to utilize the entire spectrum of flags
*******************************************************************************/

#define PARAMS BYTE(CHR_NUM), BYTE(BITS), BYTE(GOTOLABEL)

_AI_CMD(
    {
        AIRecord *ai = AiListp + Offset;
        if (chrHasFlagById(ChrEntityp, ai->val[0], ai->val[1]))
        {
            Offset = chraiGoToLabel(AiListp, Offset, ai->val[2]);
        }
        else
        {
            Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        }
        break;
    })

#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## OBJECTIVE BITFIELD SET ON
//==============================================================================

/* #define CMDNAME OBJECTIVE_BITFIELD_SET_ON */
#define CMDNAME SetObjectiveBitfield

#define DESCRIPTION \
/*******************************************************************************
* Set bits in objective bit field
********************************************************************************
* @param      BITFIELD: 32 bits
* @exception: Can be used to store a mission unique objective flag, which can be
*             linked to mission objectives.
*             It can also be used to store miscellaneous flags used by other
*             AI Lists
*             If a mission objective is changed while in third person, it will
*             not be updated on the briefing page - all mission objectives
*             status are locked while in third person
*******************************************************************************/

#define PARAMS QBYTE(BITFIELD)

_AI_CMD(
    {
        AIRecord *ai    = AiListp + Offset;
        s32       flags = CharArrayTo32(ai->val,0);
        chrSetStageFlags(ChrEntityp, flags);
        Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        break;
    })

#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## OBJECTIVE BITFIELD SET OFF
//==============================================================================

/* #define CMDNAME OBJECTIVE_BITFIELD_SET_OFF */
#define CMDNAME UnsetObjectiveBitfield

#define DESCRIPTION \
/*******************************************************************************
* Unset bits in objective bit field
********************************************************************************
* @param      BITFIELD: 32 bits
* @exception: Can be used to store a mission unique objective flag, which can be
*             linked to mission objectives.
*             It can also be used to store miscellaneous flags used by other
*             AI Lists
*             If a mission objective is changed while in third person, it will
*             not be updated on the briefing page - all mission objectives
*             status are locked while in third person
*******************************************************************************/

#define PARAMS QBYTE(BITFIELD)

_AI_CMD(
    {
        AIRecord *ai    = AiListp + Offset;
        s32       flags = CharArrayTo32(ai->val,0);
        chrUnsetStageFlags(ChrEntityp, flags);
        Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        break;
    })

#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## IF OBJECTIVE BITFIELD IS SET ON
//==============================================================================

/* #define CMDNAME IF_OBJECTIVE_BITFIELD_IS_SET_ON */
#define CMDNAME IFObjectiveBitfieldHas

#define DESCRIPTION \
/*******************************************************************************
* If ObjectiveBitfield has BITS enabled
********************************************************************************
* @return     Continue execution from LABEL if TRUE
*******************************************************************************/

#define PARAMS QBYTE(BITS), BYTE(GOTOLABEL)

_AI_CMD(
    {
        AIRecord *ai    = AiListp + Offset;
        s32       flags = CharArrayTo32(ai->val,0);
        if (chrHasStageFlag(ChrEntityp, flags)) /* PD && ai->val[4] == 1) || (!chrHasStageFlag(ChrEntityp, flags) && ai->val[4] == 0* */
        {
            Offset = chraiGoToLabel(AiListp, Offset, ai->val[4]);
        }
        else
        {
            Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        }
        break;
    })

#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## GUARD FLAGS SET ON
//==============================================================================

/* #define CMDNAME GUARD_FLAGS_SET_ON */
#define CMDNAME SetMychrflags

#define DESCRIPTION \
/*******************************************************************************
* Set My/BG chrflags
********************************************************************************
* @param CHRFLAGS: uses CHRFLAG_# defines.
* @exception: Chr->chrflags are not AI List or setup exclusive, they are
*             controlled by many parts of the engine.
*             Can be used by BG AI Lists, BG AI Lists are free to utilize the
*             entire spectrum of flags
*******************************************************************************/

#define PARAMS QBYTE(CHRFLAGS)

_AI_CMD(
    {
        AIRecord *ai    = AiListp + Offset;
        CHRFLAG   flags = CharArrayTo32(ai->val,0);
        ChrEntityp->chrflags |= flags;
        Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        break;
    })

//POLYMORPHS

_AI_CMD_POLYMORPH(,
                    SetBGchrflags,
                    (CHRFLAGS),
                    (CHRFLAGS))
#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## GUARD FLAGS SET OFF
//==============================================================================

/* #define CMDNAME GUARD_FLAGS_SET_OFF */
#define CMDNAME UnsetMychrflags

#define DESCRIPTION \
/*******************************************************************************
* Set My/BG chrflags
********************************************************************************
* @param CHRFLAGS: uses CHRFLAG_# defines.
* @exception: Chr->chrflags are not AI List or setup exclusive, they are
*             controlled by many parts of the engine.
*             Can be used by BG AI Lists, BG AI Lists are free to utilize the
*             entire spectrum of flags
*******************************************************************************/

#define PARAMS QBYTE(CHRFLAGS)

_AI_CMD(
    {
        AIRecord *ai    = AiListp + Offset;
        CHRFLAG   flags = CharArrayTo32(ai->val,0);
        ChrEntityp->chrflags &= ~flags;
        Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        break;
    })

//POLYMORPHS

_AI_CMD_POLYMORPH(,
                    UnsetBGchrflags,
                    (CHRFLAGS),
                    (CHRFLAGS))

#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## IF GUARD FLAGS IS SET ON
//==============================================================================

/* #define CMDNAME IF_GUARD_FLAGS_IS_SET_ON */
#define CMDNAME IFMychrflagsHas

#define DESCRIPTION \
/*******************************************************************************
* If My/BG chrflags has CHRFLAGS set
********************************************************************************
* @param CHRFLAGS: uses CHRFLAG_# defines.
* @return     Continue execution from LABEL if TRUE
* @exception: Chr->chrflags are not AI List or setup exclusive, they are
*             controlled by many parts of the engine.
*             Can be used by BG AI Lists, BG AI Lists are free to utilize the
*             entire spectrum of flags
*******************************************************************************/

#define PARAMS QBYTE(CHRFLAGS), BYTE(GOTOLABEL)

_AI_CMD(
    {
        AIRecord *ai    = AiListp + Offset;
        CHRFLAG   flags = CharArrayTo32(ai->val,0);
        if ((ChrEntityp->chrflags & flags) == flags)
        {
            Offset = chraiGoToLabel(AiListp, Offset, ai->val[4]);
        }
        else
        {
            Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        }
        break;
    })

//POLYMORPHS

_AI_CMD_POLYMORPH(,
                    IFBGchrflagsHas,
                    (CHRFLAGS),
                    (CHRFLAGS))

#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## CHR FLAGS SET ON
//==============================================================================

/* #define CMDNAME CHR_FLAGS_SET_ON */
#define CMDNAME SetChrchrflags

#define DESCRIPTION \
/*******************************************************************************
* Set chr chrflags
********************************************************************************
* @param CHRFLAGS: uses CHRFLAG_# defines.
* @exception: Chr->chrflags are not AI List or setup exclusive, they are
*             controlled by many parts of the engine.
*******************************************************************************/

#define PARAMS BYTE(CHR_NUM), QBYTE(CHRFLAGS)

_AI_CMD(
    {
        AIRecord * ai    = AiListp + Offset;
        CHRFLAG    flags = CharArrayTo32(ai->val,1);
        ChrRecord *chr   = chrFindById(ChrEntityp, ai->val[0]);
        if (chr)
        {
            chr->chrflags |= flags;
        }
        Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        break;
    })

#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## CHR FLAGS SET OFF
//==============================================================================

/* #define CMDNAME CHR_FLAGS_SET_OFF */
#define CMDNAME UnsetChrchrflags

#define DESCRIPTION \
/*******************************************************************************
* Unset chr chrflags
********************************************************************************
* @param CHRFLAGS: uses CHRFLAG_# defines.
* @exception: Chr->chrflags are not AI List or setup exclusive, they are
*             controlled by many parts of the engine.
*******************************************************************************/

#define PARAMS BYTE(CHR_NUM), QBYTE(CHRFLAGS)

_AI_CMD(
    {
        AIRecord * ai    = AiListp + Offset;
        CHRFLAG    flags = CharArrayTo32(ai->val,1);
        ChrRecord *chr   = chrFindById(ChrEntityp, ai->val[0]);
        if (chr)
        {
            chr->chrflags &= ~flags;
        }
        Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        break;
    })

#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## IF CHR FLAGS IS SET ON
//==============================================================================

/* #define CMDNAME IF_CHR_FLAGS_IS_SET_ON */
#define CMDNAME IFChrchrflagsHas

#define DESCRIPTION \
/*******************************************************************************
* If Chr chrflags has CHRFLAGS set
********************************************************************************
* @param CHRFLAGS: uses CHRFLAG_# defines.
* @return     Continue execution from LABEL if TRUE
* @exception: Chr->chrflags are not AI List or setup exclusive, they are
*             controlled by many parts of the engine.
*******************************************************************************/

#define PARAMS BYTE(CHR_NUM), QBYTE(CHRFLAGS), BYTE(GOTOLABEL)

_AI_CMD(
    {
        AIRecord * ai    = AiListp + Offset;
        CHRFLAG    flags = CharArrayTo32(ai->val,1);
        ChrRecord *chr   = chrFindById(ChrEntityp, ai->val[0]);
        if (chr && (chr->chrflags & flags) == flags)
        {
            Offset = chraiGoToLabel(AiListp, Offset, ai->val[5]);
        }
        else
        {
            Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        }
        break;
    })

#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## OBJECT FLAGS 1 SET ON
//==============================================================================

/* #define CMDNAME OBJECT_FLAGS_1_SET_ON */
#define CMDNAME SetObjectFlags

#define DESCRIPTION \
/*******************************************************************************
* Set object flags
********************************************************************************
* @param BITFIELD uses PROPFLAG_# defines
*******************************************************************************/

#define PARAMS BYTE(OBJECT_TAG), QBYTE(BITFIELD)

_AI_CMD(
    {
        AIRecord *    ai    = AiListp + Offset;
        s32           flags = CharArrayTo32(ai->val,1);
        ObjectRecord *obj   = objFindByTagId(ai->val[0]);
        if (obj && obj->prop)
        {
            obj->flags |= flags;
        }
        Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        break;
    })

#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## OBJECT FLAGS 1 SET OFF
//==============================================================================

/* #define CMDNAME OBJECT_FLAGS_1_SET_OFF */
#define CMDNAME UnsetObjectFlags

#define DESCRIPTION \
/*******************************************************************************
* Unset object flags
********************************************************************************
* @param BITFIELD uses PROPFLAG_# defines
*******************************************************************************/

#define PARAMS BYTE(OBJECT_TAG), QBYTE(BITFIELD)

_AI_CMD(
    {
        AIRecord *    ai    = AiListp + Offset;
        s32           flags = CharArrayTo32(ai->val,1);
        ObjectRecord *obj   = objFindByTagId(ai->val[0]);
        if (obj && obj->prop)
        {
            obj->flags &= ~flags;
        }
        Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        break;
    })

#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## IF OBJECT FLAGS 1 IS SET ON
//==============================================================================

/* #define CMDNAME IF_OBJECT_FLAGS_1_IS_SET_ON */
#define CMDNAME IFObjectFlagsHas

#define DESCRIPTION \
/*******************************************************************************
* If object flags has BITS
********************************************************************************
* @param BITS: uses PROPFLAG_# defines
* @return     Continue execution from LABEL if TRUE
*******************************************************************************/

#define PARAMS BYTE(OBJECT_TAG), QBYTE(BITS), BYTE(GOTOLABEL)

_AI_CMD(
    {
        AIRecord *    ai    = AiListp + Offset;
        s32           flags = CharArrayTo32(ai->val,1);
        ObjectRecord *obj   = objFindByTagId(ai->val[0]);
        if (obj && obj->prop && (obj->flags & flags) == flags)
        {
            Offset = chraiGoToLabel(AiListp, Offset, ai->val[5]);
        }
        else
        {
            Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        }
        break;
    })

#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## OBJECT FLAGS 2 SET ON
//==============================================================================

/* #define CMDNAME OBJECT_FLAGS_2_SET_ON */
#define CMDNAME SetObjectFlags2

#define DESCRIPTION \
/*******************************************************************************
* Set object flags2
********************************************************************************
* @param BITS: uses PROPFLAG2_# defines
*******************************************************************************/

#define PARAMS BYTE(OBJECT_TAG), QBYTE(BITS)

_AI_CMD(
    {
        AIRecord *    ai    = AiListp + Offset;
        s32           flags = CharArrayTo32(ai->val,1);
        ObjectRecord *obj   = objFindByTagId(ai->val[0]);
        if (obj && obj->prop)
        {
            obj->flags2 |= flags;
        }
        Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        break;
    })

#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## OBJECT FLAGS 2 SET OFF
//==============================================================================

/* #define CMDNAME OBJECT_FLAGS_2_SET_OFF */
#define CMDNAME UnsetObjectFlags2

#define DESCRIPTION \
/*******************************************************************************
* Unset object flags2
********************************************************************************
* @param BITS: uses PROPFLAG2_# defines
*******************************************************************************/

#define PARAMS BYTE(OBJECT_TAG), QBYTE(BITS)

_AI_CMD(
    {
        AIRecord *    ai    = AiListp + Offset;
        s32           flags = CharArrayTo32(ai->val,1);
        ObjectRecord *obj   = objFindByTagId(ai->val[0]);
        if (obj && obj->prop)
        {
            obj->flags2 &= ~flags;
        }
        Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        break;
    })

#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## IF OBJECT FLAGS 2 IS SET ON
//==============================================================================

/* #define CMDNAME IF_OBJECT_FLAGS_2_IS_SET_ON */
#define CMDNAME IFObjectFlags2Has

#define DESCRIPTION \
/*******************************************************************************
* If object flags2 has BITS
********************************************************************************
* @param BITS: uses PROPFLAG2_# defines
* @return     Continue execution from LABEL if TRUE
*******************************************************************************/

#define PARAMS BYTE(OBJECT_TAG), QBYTE(BITFIELD), BYTE(GOTOLABEL)

_AI_CMD(
    {
        AIRecord *    ai    = AiListp + Offset;
        s32           flags = CharArrayTo32(ai->val,1);
        ObjectRecord *obj   = objFindByTagId(ai->val[0]);
        if (obj && obj->prop && ((obj->flags2 & flags) == flags))
        {
            Offset = chraiGoToLabel(AiListp, Offset, ai->val[5]);
        }
        else
        {
            Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        }
        break;
    })

#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## GUARD SET CHR PRESET
//==============================================================================

/* #define CMDNAME GUARD_SET_CHR_PRESET */
#define CMDNAME SetMyChrPreset

#define DESCRIPTION \
/*******************************************************************************
* Set My/BG Preset Chr to CHR_PRESET
*******************************************************************************/

#define PARAMS BYTE(CHR_PRESET)

_AI_CMD(
    {
        AIRecord *ai = AiListp + Offset;
        chrSetChrPreset(ChrEntityp, ai->val[0]);
        Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        break;
    })

//POLYMORPHS

_AI_CMD_POLYMORPH(,
                    SetBGChrPreset,
                    (CHRFLAGS),
                    (CHRFLAGS))

#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## CHR SET CHR PRESET
//==============================================================================

/* #define CMDNAME CHR_SET_CHR_PRESET */
#define CMDNAME SetChrChrPreset

#define DESCRIPTION \
/*******************************************************************************
* Set Chr Preset Chr to CHR_PRESET
*******************************************************************************/

#define PARAMS BYTE(CHR_NUM), BYTE(CHR_PRESET)

_AI_CMD(
    {
        AIRecord *ai = AiListp + Offset;
        chrSetChrPreset2(ChrEntityp, ai->val[0], ai->val[1]);
        Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        break;
    })

#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## GUARD SET PAD PRESET
//==============================================================================

/* #define CMDNAME GUARD_SET_PAD_PRESET */
#define CMDNAME SetMyPadPreset

#define DESCRIPTION \
/*******************************************************************************
* Set My/BG Preset Pad to PAD_PRESET
*******************************************************************************/

#define PARAMS DBYTE(PAD_PRESET)

_AI_CMD(
    {
        AIRecord *ai     = AiListp + Offset;
        u16       pad_id = CharArrayTo16(ai->val,0);
        if (ChrEntityp)
        {
            chrSetPadPreset(ChrEntityp, pad_id);
        }
        else if (AircraftEntityp)
        {
            AircraftEntityp->pad = pad_id;
        }
        Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        break;
    })

//POLYMORPHS

_AI_CMD_POLYMORPH(,
                    SetBGPadPreset,
                    (CHRFLAGS),
                    (CHRFLAGS))

#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## CHR SET PAD PRESET
//==============================================================================

/* #define CMDNAME CHR_SET_PAD_PRESET */
#define CMDNAME SetChrPadPreset

#define DESCRIPTION \
/*******************************************************************************
* Set Chr Preset Pad to PAD_PRESET
*******************************************************************************/

#define PARAMS BYTE(CHR_NUM), DBYTE(PAD_PRESET)

_AI_CMD(
    {
        AIRecord *ai     = AiListp + Offset;
        u16       pad_id = CharArrayTo16(ai->val,1);
        chrSetPadPresetByChrnum(ChrEntityp, ai->val[0], pad_id);
        Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        break;
    })

#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## Print (Canonical name)
//==============================================================================
/*
pre-process comments into byte strings
sed -e ':loop' -e 's/PRINT\("(..*?)(\\n|\s|\S)"/PRINT("$1",\'$2\'/g' -e 't loop'
sed -e 's/PRINT\("(.)"(.*)\)/PRINT,\'$1\'$2,\'\0\',/g'
*/
#define CMDNAME PRINT

#define DESCRIPTION \
/*******************************************************************************
* Debug Comment
********************************************************************************
* @return: May have originally printed to stderr on host sgi devkit.
*******************************************************************************/

_AI_DEBUG(
    {
        Offset += chraiitemsize(AiListp, Offset);
        break;
    })

#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## LOCAL TIMER RESET START
//==============================================================================

/* #define CMDNAME LOCAL_TIMER_RESET_START */
#define CMDNAME MyTimerStart

#define DESCRIPTION \
/*******************************************************************************
* Start My/BG timer from 0. Counts up 60 times a second.
********************************************************************************
* @exception: Local timer is unique for each chr, while hud countdown is global
*             for the entire mission.
*******************************************************************************/

#define PARAMS /*NONE*/

_AI_CMD(
    {
        chrRestartTimer(ChrEntityp);
        Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        break;
    })

//POLYMORPHS

_AI_CMD_POLYMORPH(,
                    BGTimerResetStart,
                    (),
                    ())

#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## LOCAL TIMER RESET
//==============================================================================

/* #define CMDNAME LOCAL_TIMER_RESET */
#define CMDNAME MyTimerReset

#define DESCRIPTION \
/*******************************************************************************
* Reset My/BG timer to 0. Does Not start or stop timer.
********************************************************************************
* @exception: Local timer is unique for each chr, while hud countdown is global
*             for the entire mission.
*******************************************************************************/

#define PARAMS /*NONE*/

_AI_CMD(
    {
        ChrEntityp->timer60 = 0;
        Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        break;
    })

//POLYMORPHS

_AI_CMD_POLYMORPH(,
                    BGTimerReset,
                    (),
                    ())

#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## LOCAL TIMER STOP
//==============================================================================

/* #define CMDNAME LOCAL_TIMER_STOP */
#define CMDNAME MyTimerPause

#define DESCRIPTION \
/*******************************************************************************
* Pause My/BG timer.
********************************************************************************
* @exception: Local timer is unique for each chr, while hud countdown is global
*             for the entire mission.
*******************************************************************************/

#define PARAMS /*NONE*/

_AI_CMD(
    {
        ChrEntityp->hidden &= ~CHRHIDDEN_TIMER_ACTIVE;
        Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        break;
    })

//POLYMORPHS

_AI_CMD_POLYMORPH(,
                    BGTimerPause,
                    (),
                    ())

#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## LOCAL TIMER START
//==============================================================================

/* #define CMDNAME LOCAL_TIMER_START */
#define CMDNAME MyTimerResume

#define DESCRIPTION \
/*******************************************************************************
* Resume My/BG timer. If you want to Start from 0, use MyTimerStart()
********************************************************************************
* @exception: Local timer is unique for each chr, while hud countdown is global
*             for the entire mission.
*******************************************************************************/

#define PARAMS /*NONE*/

_AI_CMD(
    {
        ChrEntityp->hidden |= CHRHIDDEN_TIMER_ACTIVE;
        Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        break;
    })

//POLYMORPHS

_AI_CMD_POLYMORPH(,
                    BGTimerResume,
                    (),
                    ())

#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## IF LOCAL TIMER HAS STOPPED
//==============================================================================

/* #define CMDNAME IF_LOCAL_TIMER_HAS_STOPPED */
#define CMDNAME IFMyTimerIsNotRunning

#define DESCRIPTION \
/*******************************************************************************
* If My/BG timer is NOT running (paused)
********************************************************************************
* @return     Continue execution from LABEL if TRUE
* @exception: By default, timer is inactive
*******************************************************************************/

#define PARAMS BYTE(GOTOLABEL)

_AI_CMD(
    {
        AIRecord *ai = AiListp + Offset;
        if (((ChrEntityp->hidden & CHRHIDDEN_TIMER_ACTIVE) == 0))
        {
            Offset = chraiGoToLabel(AiListp, Offset, ai->val[0]);
        }
        else
        {
            Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        }
        break;
    })

//POLYMORPHS

_AI_CMD_POLYMORPH(,
                    IFBGTimerIsNotRunning,
                    (GOTOLABEL),
                    (GOTOLABEL))

#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## IF LOCAL TIMER LESS THAN
//==============================================================================

/* #define CMDNAME IF_LOCAL_TIMER_LESS_THAN */
#define CMDNAME IFMyTimerLessThanTicks

#define DESCRIPTION \
/*******************************************************************************
* If My/BG Timer is Less Than TICKS
********************************************************************************
* @return     Continue execution from LABEL if TRUE
* @param      TICKS: Unsigned integer 1/60 or 1/50 Second
* @exception: TICKS is converted to float and compared.
*******************************************************************************/

#define PARAMS TBYTE(TICKS), BYTE(GOTOLABEL)

_AI_CMD(
    {
        AIRecord * ai = AiListp + Offset;
        f32      valf = ((unsigned) CharArrayTo24(ai->val,0)) / CHRLV_FRAMERATE_F;
        if (chrGetTimer(ChrEntityp) < valf)
        {
            Offset = chraiGoToLabel(AiListp, Offset, ai->val[3]);
        }
        else
        {
            Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        }
        break;
    })

//POLYMORPHS

_AI_CMD_POLYMORPH(,
                    IFBGTimerLessThanTicks,
                    (TICKS,GOTOLABEL),
                    (TICKS,GOTOLABEL))
#undef DESCRIPTION
#define DESCRIPTION \
/*******************************************************************************
* If My/BG Timer is Less Than SECONDS
********************************************************************************
* @return     Continue execution from LABEL if TRUE
* @param      SECONDS: Unsigned integer
*******************************************************************************/

_AI_CMD_POLYMORPH(,
                    IFMyTimerLessThanSeconds,
                    ((SECS_TO_TIMER60(SECONDS)), GOTOLABEL),
                    (SECONDS, GOTOLABEL))
_AI_CMD_POLYMORPH(,
                    IFBGTimerLessThanSeconds,
                    ((SECS_TO_TIMER60(SECONDS)), GOTOLABEL),
                    (SECONDS, GOTOLABEL))


#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## IF LOCAL TIMER GREATER THAN
//==============================================================================

/* #define CMDNAME IF_LOCAL_TIMER_GREATER_THAN */
#define CMDNAME IFMyTimerGreaterThanTicks

#define DESCRIPTION \
/*******************************************************************************
* If My/BG Timer is Greater Than TICKS
********************************************************************************
* @return     Continue execution from LABEL if TRUE
* @param      TICKS: Unsigned integer 1/60 or 1/50 Second
* @exception: TICKS is converted to float and compared.
*******************************************************************************/

#define PARAMS TBYTE(TICKS), BYTE(GOTOLABEL)

_AI_CMD(
    {
        AIRecord * ai = AiListp + Offset;
        f32       valf = ((unsigned) CharArrayTo24(ai->val,0)) / CHRLV_FRAMERATE_F;
        if (chrGetTimer(ChrEntityp) > valf)
        {
            Offset = chraiGoToLabel(AiListp, Offset, ai->val[3]);
        }
        else
        {
            Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        }
        break;
    })

//POLYMORPHS

_AI_CMD_POLYMORPH(,
                    IFBGTimerGreaterThanTicks,
                    (TICKS,GOTOLABEL),
                    (TICKS,GOTOLABEL))
#undef DESCRIPTION
#define DESCRIPTION \
/*******************************************************************************
* If My/BG Timer is Less Than SECONDS
********************************************************************************
* @return     Continue execution from LABEL if TRUE
* @param      SECONDS: Unsigned integer
*******************************************************************************/

_AI_CMD_POLYMORPH(,
                    IFMyTimerGreaterThanSeconds,
                    ((SECS_TO_TIMER60(SECONDS)), GOTOLABEL),
                    (SECONDS, GOTOLABEL))
_AI_CMD_POLYMORPH(,
                    IFBGTimerGreaterThanSeconds,
                    ((SECS_TO_TIMER60(SECONDS)), GOTOLABEL),
                    (SECONDS, GOTOLABEL))
#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## HUD COUNTDOWN SHOW
//==============================================================================

/* #define CMDNAME HUD_COUNTDOWN_SHOW */
#define CMDNAME HudCountdownShow

#define DESCRIPTION \
/*******************************************************************************
* Shows the hud countdown
*******************************************************************************/

#define PARAMS /*NONE*/

_AI_CMD(
    {
        countdownTimerSetVisible(1, TRUE);
        Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        break;
    })

#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## HUD COUNTDOWN HIDE
//==============================================================================

/* #define CMDNAME HUD_COUNTDOWN_HIDE */
#define CMDNAME HudCountdownHide

#define DESCRIPTION \
/*******************************************************************************
* Hides the hud countdown
********************************************************************************
* @exception: Can be used as a hidden global timer for objective logic
*******************************************************************************/

#define PARAMS /*NONE*/

_AI_CMD(
    {
        countdownTimerSetVisible(1, FALSE);
        Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        break;
    })

#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## HUD COUNTDOWN SET
//==============================================================================

/* #define CMDNAME HUD_COUNTDOWN_SET */
#define CMDNAME HudCountdownSet

#define DESCRIPTION \
/*******************************************************************************
* Set the hud countdown
********************************************************************************
* @exception: To make the timer count up, set to 0 and start timer
*******************************************************************************/

#define PARAMS DBYTE(SECONDS)

_AI_CMD(
    {
        AIRecord * ai = AiListp + Offset;
        f32       seconds = CharArrayTo16(ai->val,0);
        countdownTimerSetValue(seconds * CHRLV_FRAMERATE_F);
        Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        break;
    })

#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## HUD COUNTDOWN STOP
//==============================================================================

/* #define CMDNAME HUD_COUNTDOWN_STOP */
#define CMDNAME HudCountdownStop

#define DESCRIPTION \
/*******************************************************************************
* Stops the hud countdown
*******************************************************************************/

#define PARAMS /*NONE*/

_AI_CMD(
    {
        countdownTimerSetRunning(FALSE);
        Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        break;
    })

#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## HUD COUNTDOWN START
//==============================================================================

/* #define CMDNAME HUD_COUNTDOWN_START */
#define CMDNAME HudCountdownStart

#define DESCRIPTION \
/*******************************************************************************
* Start the hud countdown
*******************************************************************************/

#define PARAMS /*NONE*/

_AI_CMD(
    {
        countdownTimerSetRunning(FALSE);
        Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        break;
    })

#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## IF HUD COUNTDOWN HAS STOPPED
//==============================================================================

/* #define CMDNAME IF_HUD_COUNTDOWN_HAS_STOPPED */
#define CMDNAME IFHudCountdownIsNotRunning

#define DESCRIPTION \
/*******************************************************************************
* If Hud Countdown is NOT running (paused)
********************************************************************************
* @return     Continue execution from LABEL if TRUE
* @exception: By default, timer is inactive
*******************************************************************************/

#define PARAMS BYTE(GOTOLABEL)

_AI_CMD(
    {
        AIRecord * ai = AiListp + Offset;
        if (!countdownTimerIsRunning())
        {
            Offset = chraiGoToLabel(AiListp, Offset, ai->val[0]);
        }
        else
        {
            Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        }
        break;
    })

#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## IF HUD COUNTDOWN LESS THAN
//==============================================================================

/* #define CMDNAME IF_HUD_COUNTDOWN_LESS_THAN */
#define CMDNAME IFHudCountdownLessThan

#define DESCRIPTION \
/*******************************************************************************
* if hud countdown is Less Than SECONDS
********************************************************************************
* @return     Continue execution from LABEL if TRUE
* @param      SECONDS: Unsigned Integer, 0 means TRUE on count-up only.
* @exception: SECONDS can NOT test negative values (counting up)
*******************************************************************************/

#define PARAMS DBYTE(SECONDS), BYTE(GOTOLABEL)

_AI_CMD(
    {
        AIRecord * ai = AiListp + Offset;
        f32       value = CharArrayTo16(ai->val,0);
        if (countdownTimerGetValue() < value * CHRLV_FRAMERATE_F)
        {
            Offset = chraiGoToLabel(AiListp, Offset, ai->val[2]);
        }
        else
        {
            Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        }
        break;
    })

#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## IF HUD COUNTDOWN GREATER THAN
//==============================================================================

/* #define CMDNAME IF_HUD_COUNTDOWN_GREATER_THAN */
#define CMDNAME IFHudCountdownGreaterThan

#define DESCRIPTION \
/*******************************************************************************
* if hud countdown is Greater Than SECONDS
********************************************************************************
* @return     Continue execution from LABEL if TRUE
* @param      SECONDS: Unsigned Integer, 0 means TRUE on count-down only.
* @exception: SECONDS can NOT test negative values (counting up)
*******************************************************************************/

#define PARAMS DBYTE(SECONDS), BYTE(GOTOLABEL)

_AI_CMD(
    {
        AIRecord * ai = AiListp + Offset;
        f32       value = CharArrayTo16(ai->val,0);
        if (countdownTimerGetValue() > value * CHRLV_FRAMERATE_F)
        {
            Offset = chraiGoToLabel(AiListp, Offset, ai->val[2]);
        }
        else
        {
            Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        }
        break;
    })

#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## CHR TRY SPAWNING AT PAD
//==============================================================================

/* #define CMDNAME CHR_TRY_SPAWNING_AT_PAD */
#define CMDNAME TRYSpawningChrAtPad

#define DESCRIPTION \
/*******************************************************************************
* Spawn new chr at pad
********************************************************************************
* @return     Continue execution from LABEL if Successful
* @param      BITFIELD: uses SPAWN_# defines
* @param      AI_LIST_ID: Assign this 16bit AI_LIST ID to Chr
* @exception: If out of memory chr can NOT spawn.
*             If pad is blocked, attempt to spawn chr around pad.
*             By default, guard will NOT spawn if target pad can be seen by Bond.
*             However this is ignored if passed SPAWN_IGNORE_PAD_SIGHT_CHECK flag.
*******************************************************************************/

#define PARAMS BYTE(BODY_NUM), BYTE(HEAD_NUM), DBYTE(PAD), DBYTE(AI_LIST_ID), QBYTE(BITFIELD), BYTE(GOTOLABEL)

_AI_CMD(
    {
        AIRecord * ai = AiListp + Offset;
        u16       pad = CharArrayTo16(ai->val,2);
        CHRFLAG   flags = CharArrayTo32(ai->val,6);
        u16       ailistid = CharArrayTo16(ai->val,4);
        AIRecord *ailist = ailistFindById(ailistid);
        if (chrSpawnAtPad(ChrEntityp, ai->val[0], (s8) ai->val[1], pad, ailist, flags))
        {
            Offset = chraiGoToLabel(AiListp, Offset, ai->val[10]);
        }
        else
        {
            Offset += CAT(CAT(AI_, CMDNAME), _LENGTH)2;
        }
        break;
    })

#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## CHR TRY SPAWNING NEXT TO UNSEEN CHR
//==============================================================================

/* #define CMDNAME CHR_TRY_SPAWNING_NEXT_TO_UNSEEN_CHR */
#define CMDNAME TRYSpawningChrNextToChr

#define DESCRIPTION \
/*******************************************************************************
* Spawn a chr next to another chr
*******************************************************************************
* @return     Continue execution from LABEL if Successful
* @param      BITFIELD: uses SPAWN_# defines
* @param      AI_LIST_ID: Assign this 16bit AI_LIST ID to Chr
* @exception: If out of memory or chr/pad is in view of Bond - chr can NOT spawn.
*             Target chr must still exist in level or else command will crash.
*             Will not spawn chr if target chr has been seen before
*             (CHRFLAG_HAS_BEEN_ON_SCREEN)
*******************************************************************************/

#define PARAMS BYTE(BODY_NUM), BYTE(HEAD_NUM), BYTE(CHR_NUM_TARGET), DBYTE(AI_LIST_ID), QBYTE(BITFIELD), BYTE(GOTOLABEL)

_AI_CMD(
    {
        AIRecord * ai = AiListp + Offset;
        CHRFLAG   flags = CharArrayTo32(ai->val,5);
        u16       ailistid = CharArrayTo16(ai->val,3);
        AIRecord *ailist = ailistFindById(ailistid);
        if (chrSpawnAtChr(ChrEntityp, ai->val[0], (s8) ai->val[1], ai->val[2], ailist, flags))
        {
            Offset = chraiGoToLabel(AiListp, Offset, ai->val[9]);
        }
        else
        {
            Offset += CAT(CAT(AI_, CMDNAME), _LENGTH)1;
        }
        break;
    })

#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## GUARD TRY SPAWNING ITEM
//==============================================================================

/* #define CMDNAME GUARD_TRY_SPAWNING_ITEM */
#define CMDNAME TRYGiveMeItem

#define DESCRIPTION \
/*******************************************************************************
* Spawn Item/Weapon for Me
********************************************************************************
* @return     Continue execution from LABEL if Successful
* @param      PROPFLAG uses PROPFLAG defines - NOT PROPFLAG2!
* @param      PROP_NUM: use PROP_xxx e.g. PROP_CHRWPPK for visible model
* @param      ITEM_NUM: use ITEM_xxx e.g. ITEM_WPPK for item type
* @exception: If out of memory or hands occupied - Item can NOT spawn.
*             Spawned Prop must have a holding position command within the
*             model file, else use conceal flag so guard does not attempt to
*             hold Prop
*******************************************************************************/

#define PARAMS DBYTE(PROP_NUM), BYTE(ITEM_NUM), QBYTE(PROPFLAG), BYTE(GOTOLABEL)

_AI_CMD(
    {
        AIRecord * ai = AiListp + Offset;
        s32         flags = CharArrayTo32(ai->val,3);
        s32         model = CharArrayTo16(ai->val,0);
        PropRecord *prop = NULL;
        if (ChrEntityp && ChrEntityp->prop && ChrEntityp->model)
        {
            /* more nice PD code that might be useful in future
            if (cheatIsActive(CHEAT_MARQUIS))
            {
                flags &= ~0x10000000;
                flags |= 0x20000000;
                prop = chrGiveWeapon(ChrEntityp, model, ai->val[2], flags);
            }
            else
            */
            if (cheatIsActive(28))
            {
                switch (ai->val[2])
                {
                    case ITEM_KNIFE:
                    case ITEM_THROWKNIFE:
                    case ITEM_WPPK:
                    case ITEM_WPPKSIL:
                    case ITEM_TT33:
                    case ITEM_SKORPION:
                    case ITEM_AK47:
                    case ITEM_UZI:
                    case ITEM_MP5K:
                    case ITEM_MP5KSIL:
                    case ITEM_SPECTRE:
                    case ITEM_M16:
                    case ITEM_FNP90:
                    case ITEM_SHOTGUN:
                    case ITEM_AUTOSHOT:
                    case ITEM_SNIPERRIFLE:
                    case ITEM_RUGER:
                    case ITEM_GOLDENGUN:
                    case ITEM_SILVERWPPK:
                    case ITEM_GOLDWPPK:
                    case ITEM_LASER:
                    case ITEM_WATCHLASER:
                    case ITEM_TIMEDMINE:
                    case ITEM_PROXIMITYMINE:
                    case ITEM_REMOTEMINE:
                    case ITEM_TRIGGER:
                    case ITEM_TASER:
                        prop = chrGiveWeapon(ChrEntityp, PROP_CHRROCKETLAUNCH, ITEM_ROCKETLAUNCH, flags);
                        break;
                    default:
                        prop = chrGiveWeapon(ChrEntityp, model, ai->val[2], flags);
                        break;
                }
            }
            else
            {
                prop = chrGiveWeapon(ChrEntityp, model, ai->val[2], flags);
            }
        }
        if (prop)
        {
            Offset = chraiGoToLabel(AiListp, Offset, ai->val[7]);
        }
        else
        {
            Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        }
        break;
    })

#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## GUARD TRY SPAWNING HAT
//==============================================================================

/* #define CMDNAME GUARD_TRY_SPAWNING_HAT */
#define CMDNAME TRYGiveMeHat

#define DESCRIPTION \
/*******************************************************************************
* Spawn hat on Me
********************************************************************************
* @return     Continue execution from LABEL if Successful
* @param      PROPFLAG uses PROPFLAG defines - NOT PROPFLAG2!
* @param      PROP_NUM: use PROP_xxx e.g. PROP_CHRWPPK for visible model
* @exception: If out of memory or already have hat - Item can NOT spawn.
*             Spawned Prop must have a holding position command within the
*             model file.
*******************************************************************************/

#define PARAMS DBYTE(PROP_NUM), QBYTE(PROP_BITFIELD), BYTE(GOTOLABEL)

_AI_CMD(
    {
        AIRecord * ai = AiListp + Offset;
        s32       flags = CharArrayTo32(ai->val,2);
        s32       modelnum = CharArrayTo16(ai->val,0);
        bool      ok = FALSE;
        if (ChrEntityp && ChrEntityp->prop && ChrEntityp->model)
        {
            ok = hatCreateForChr(ChrEntityp, modelnum, flags);
        }
        if (ok)
        {
            Offset = chraiGoToLabel(AiListp, Offset, ai->val[6]);
        }
        else
        {
            Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        }
        break;
    })

#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## CHR TRY SPAWNING CLONE
//==============================================================================

/* #define CMDNAME CHR_TRY_SPAWNING_CLONE */
#define CMDNAME TRYCloningChr

#define DESCRIPTION \
/*******************************************************************************
* If CHR_NUM has clone flag on, spawn a clone
********************************************************************************
* @return     Continue execution from LABEL if Successful
* @param      AI_LIST_ID Assign this 16bit AI_LIST ID to Clone
* @exception: Clone flag is stored in chr->chrflags which is assigned at setup
*             Newly spawned clone is placed in front of original chr
*******************************************************************************/

#define PARAMS BYTE(CHR_NUM), DBYTE(AI_LIST_ID), BYTE(GOTOLABEL)

_AI_CMD(
    {
        AIRecord * ai = AiListp + Offset;
        u16              ailistid = CharArrayTo16(ai->val,1);
        u8 *ailist = ailistFindById((u16) ailistid);
        ChrRecord *chr = chrFindById(ChrEntityp, ai->val[0]);
        bool             pass = FALSE;
        int              chrnum;
        PropRecord *srcweaponLprop = NULL;
        PropRecord *srcweaponRprop = NULL;
        PropRecord *cloneweaponRprop = NULL;
        PropRecord *cloneweaponLprop = NULL;
        PropRecord *cloneprop = NULL;
        ChrRecord *clone = NULL;
        WeaponObjRecord *srcweaponL = NULL;
        WeaponObjRecord *cloneweaponL = NULL;
        WeaponObjRecord *cloneweaponR = NULL;
        WeaponObjRecord *srcweaponR = NULL;
        PropRecord *hatprop;
        ObjectRecord *hatobj;
        if (chr && (chr->chrflags & CHRFLAG_CLONE))
        {
            cloneprop = chrSpawnAtChr(ChrEntityp, chr->bodynum, -1, chr->chrnum, ailist, 0);
            if (cloneprop)
            {
                clone = cloneprop->chr;
                chrnum = chr->chrnum + 10000;
                if (!chrFindById(ChrEntityp, chrnum))
                {
                    clone->chrnum = chrnum;
                }
                srcweaponRprop = chrGetEquippedWeaponProp(chr, GUNRIGHT);
                if (srcweaponRprop)
                {
                    srcweaponR = srcweaponRprop->weapon;
                    cloneweaponRprop = chrGiveWeapon(clone, srcweaponR->obj, srcweaponR->weaponnum, 0);
                    if (cloneweaponRprop)
                    {
                        cloneweaponR = cloneweaponRprop->weapon;
                    }
                }
                srcweaponLprop = chrGetEquippedWeaponProp(chr, GUNLEFT);
                if (srcweaponLprop)
                {
                    srcweaponL = srcweaponLprop->weapon;
                    cloneweaponLprop = chrGiveWeapon(clone, srcweaponL->obj, srcweaponL->weaponnum, 0x10000000);
                    if (cloneweaponLprop)
                    {
                        cloneweaponL = cloneweaponLprop->weapon;
                    }
                }
                if (srcweaponL && srcweaponR && cloneweaponL && cloneweaponR && srcweaponR == srcweaponL->dualweapon && srcweaponL == srcweaponR->dualweapon)
                {
                    propweaponSetDual(cloneweaponL, cloneweaponR);
                }
                {
                    hatprop = chr->handle_positiondata_hat;
                    if (hatprop)
                    {
                        hatobj = hatprop->obj;
                        hatCreateForChr(clone, hatobj->obj, 0);
                    }
                }
                /*PD extras
                clone->morale     = chr->morale;
                clone->alertness  = chr->alertness;
                clone->padpreset1 = chr->padpreset1;*
                */
                pass = TRUE;
            }
        }
        if (pass)
        {
            Offset = chraiGoToLabel(AiListp, Offset, ai->val[3]);
        }
        else
        {
            Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        }
        break;
    })

#undef DESCRIPTION
#define DESCRIPTION \
/*******************************************************************************
* If I have clone flag on, spawn a clone
********************************************************************************
* @return     Continue execution from LABEL if Successful
* @param      AI_LIST_ID Assign this 16bit AI_LIST ID to Clone
* @exception: Clone flag is stored in chr->chrflags which is assigned at setup
*             Newly spawned clone is placed in front of original chr
*******************************************************************************/

_AI_CMD_POLYMORPH(,
                    TRYCloningMe,
                    (CHR_SELF, AI_LIST_ID, GOTOLABEL),
                    (AI_LIST_ID, GOTOLABEL))

#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## TEXT PRINT BOTTOM
//==============================================================================

/* #define CMDNAME TEXT_PRINT_BOTTOM */
#define CMDNAME TextPrintBottom

#define DESCRIPTION \
/*******************************************************************************
* Print text slot to bottom left part of screen (where pick-up text is located)
********************************************************************************
* @exception: If text slot is not currently allocated in memory, game will soft-lock.
*       Expects string to end with '\n' (LF) character
*******************************************************************************/

#define PARAMS DBYTE(TEXT_SLOT)

_AI_CMD(
    {
        AIRecord * ai = AiListp + Offset;
        char *text = langGet(CharArrayTo16(ai->val,0));
        hudmsgBottomShow(text);
        Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        break;
    })

#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## TEXT PRINT TOP
//==============================================================================

/* #define CMDNAME TEXT_PRINT_TOP */
#define CMDNAME TextPrintTop

#define DESCRIPTION \
/*******************************************************************************
* Print text slot to top part of screen
********************************************************************************
* @exception: If text slot is not currently allocated in memory, game will soft-lock.
*       Expects string to end with '\n' (LF) character
*******************************************************************************/

#define PARAMS DBYTE(TEXT_SLOT)

_AI_CMD(
    {
        AIRecord * ai = AiListp + Offset;
        char *text = langGet(CharArrayTo16(ai->val,0));
        hudmsgTopShow(text);
        Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        break;
    })

#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//# SFX COMMANDS
//==============================================================================




//==============================================================================
//## SFX PLAY
//==============================================================================

/* #define CMDNAME SFX_PLAY */
#define CMDNAME SfxPlay

#define DESCRIPTION \
/*******************************************************************************
* Play a sound effect
********************************************************************************
* @exception: Channel argument range is 0-7.
*       Use a channel if you plan on modifying sfx volume with SFX Commands;
*           SFX_EMIT_FROM_OBJECT
*           SFX_EMIT_FROM_PAD
*           SFX_SET_CHANNEL_VOLUME
*           SFX_FADE_CHANNEL_VOLUME
*           SFX_STOP_CHANNEL
*           IF_SFX_CHANNEL_VOLUME_LESS_THAN
*       If you do NOT plan on doing this, use an invalid channel such as -1.
*       This will play the sfx but not bother initializing channel
*       data for SFX Commands.
*       If a sfx is already occupying channel, retriggering sfx will overwrite
*       old sfx slot data and no longer can be used by SFX Commands
*******************************************************************************/

#define PARAMS DBYTE(SOUND_NUM), BYTE(CHANNEL_NUM)

_AI_CMD(
    {
        AIRecord * ai = AiListp + Offset;
        s16       audio_id = CharArrayTo16(ai->val,0);
        audioPlayFromProp((s8) ai->val[2], audio_id);
        Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        break;
    })

//POLYMORPHS

_AI_CMD_POLYMORPH(,
                  SFX_PLAY_SIMPLE,
                  (SOUND_NUM, -1),
                  (SOUND_NUM))

#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## SFX EMIT FROM OBJECT
//==============================================================================

/* #define CMDNAME SFX_EMIT_FROM_OBJECT */
#define CMDNAME SfxEmitFromObject

#define DESCRIPTION \
/*******************************************************************************
* Sets an occupied sfx channel to emit from a tagged object
********************************************************************************
* @param      VOL_DECAY_TIME60: is number of ticks to fully transition from
*             max volume to target volume
* @exception: Panning is not calculated (mono), only affects volume.
*******************************************************************************/

#define PARAMS BYTE(CHANNEL_NUM), BYTE(OBJECT_TAG), DBYTE(VOL_DECAY_TIME60)

_AI_CMD(
    {
        struct
        {
            u8 cmd;
            s8 slotID;
            u8 val[];
        } *ai = AiListp + Offset;
        ObjectRecord *obj = objFindByTagId(ai->val[0]);
        u16           sfxID = CharArrayTo16(ai->val,1);
        if (ai->slotID >= 0 && ai->slotID < 8 && obj)
        {
            sfx_related[ai->slotID].sfxID = sfxID;
            sfx_related[ai->slotID].pos = NULL;
            sfx_related[ai->slotID].Obj = obj;
            if (sfxID == 0)
            {
                audioPlayFromProp2(ai->slotID);
            }
        }
        Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        break;
    })

#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## SFX EMIT FROM PAD
//==============================================================================

/* #define CMDNAME SFX_EMIT_FROM_PAD */
#define CMDNAME SfxEmitFromPad

#define DESCRIPTION \
/*******************************************************************************
* Sets an occupied sfx channel to emit from a pad
********************************************************************************
* @param VOL_DECAY_TIME60 is number of ticks to fully transition from
*               max volume to target volume
* @exception: Panning is not calculated (mono), only affects volume.
*******************************************************************************/

#define PARAMS BYTE(CHANNEL_NUM), DBYTE(PAD), DBYTE(VOL_DECAY_TIME60)

_AI_CMD(
    {
        struct
        {
            u8 cmd;
            s8 SlotID;
            u8 val[];
        } *ai = AiListp + Offset;
        u16        padnum = CharArrayTo16(ai->val,0);
        PadRecord *pad;
        u16        sfxID = CharArrayTo16(ai->val,2);
        if (isNotBoundPad(padnum))
        {
            pad = &g_CurrentSetup.pads[padnum];
        }
        else
        {
            pad = (PadRecord *) &g_CurrentSetup.boundpads[getBoundPadNum(padnum)];
        }
        if (ai->SlotID >= 0 && ai->SlotID < 8 && pad)
        {
            sfx_related[ai->SlotID].sfxID = sfxID;
            sfx_related[ai->SlotID].pos = pad;
            sfx_related[ai->SlotID].Obj = NULL;
            if (sfxID == 0)
            {
                audioPlayFromProp2(ai->SlotID);
            }
        }
        Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        break;
    })

#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## SFX SET CHANNEL VOLUME
//==============================================================================

/* #define CMDNAME SFX_SET_CHANNEL_VOLUME */
#define CMDNAME SfxSetChannelVolume

#define DESCRIPTION \
/*******************************************************************************
* Sets an occupied sfx channel's volume
********************************************************************************
* @param      TRANSITION_TIME60: is number of ticks to fade between current
*             volume to TARGET_VOLUME
*@param       TARGET_VOLUME: signed range 0 - 100% (0x0000-0x7FFF)
* @exception: TARGET_VOLUME can NOT be negative
*******************************************************************************/

#define PARAMS BYTE(CHANNEL_NUM), DBYTE(TARGET_VOLUME), DBYTE(TRANSITION_TIME60)

_AI_CMD(
    {
        struct
        {
            u8 cmd;
            s8 slotID;
            u8 val[];
        } *ai = AiListp + Offset;
        s16 vol = CharArrayTo16(ai->val,0);
        u16 sfxID = CharArrayTo16(ai->val,2);
        if (ai->slotID >= 0 && ai->slotID < 8)
        {
            sfx_related[ai->slotID].sfxID = sfxID;
            sfx_related[ai->slotID].Volume = vol;
            sfx_related[ai->slotID].pos = NULL;
            sfx_related[ai->slotID].Obj = NULL;
            if (sfxID == 0)
            {
                audioPlayFromProp2(ai->slotID);
            }
        }
        Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        break;
    })

#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## SFX FADE CHANNEL VOLUME
//==============================================================================

/* #define CMDNAME SFX_FADE_CHANNEL_VOLUME */
#define CMDNAME SfxFadeChannelVolume

#define DESCRIPTION \
/*******************************************************************************
* Fade out an occupied sfx channel's volume by volume percent
********************************************************************************
* @param TRANSITION_TIME60: is number of ticks to fade between current
*               volume to TARGET_VOLUME
* @param TARGET_VOLUME: signed range 0 - 100% (0x0000-0x7FFF)
* @exception: TARGET_VOLUME can NOT be negative
*******************************************************************************/

#define PARAMS BYTE(CHANNEL_NUM), DBYTE(TARGET_VOLUME), DBYTE(TRANSITION_TIME60)

_AI_CMD(
    {
        struct
        {
            u8 cmd;
            s8 slotID;
            u8 val[];
        } *ai = AiListp + Offset;
        f32 vol = CharArrayTo16(ai->val,0);
        u16 sfxID = CharArrayTo16(ai->val,2);
        if (ai->slotID >= 0 && ai->slotID < 8)
        {
            sfx_related[ai->slotID].sfxID = sfxID;
            sfx_related[ai->slotID].Volume = sub_GAME_7F0539B8(vol);
            sfx_related[ai->slotID].pos = NULL;
            sfx_related[ai->slotID].Obj = NULL;
            if (sfxID == 0)
            {
                audioPlayFromProp2(ai->slotID);
            }
        }
        Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        break;
    })

#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## SFX STOP CHANNEL
//==============================================================================

/* #define CMDNAME SFX_STOP_CHANNEL */
#define CMDNAME SfxStopChannel

#define DESCRIPTION \
/*******************************************************************************
* Stop playing sfx in occupied sfx channel
*******************************************************************************/

#define PARAMS BYTE(CHANNEL_NUM)

_AI_CMD(
    {
        AIRecord1s * ai = AiListp + Offset;
        sub_GAME_7F0349BC(ai->val);
        Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        break;
    })

#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## IF SFX CHANNEL VOLUME LESS THAN
//==============================================================================

/* #define CMDNAME IF_SFX_CHANNEL_VOLUME_LESS_THAN */
#define CMDNAME IFSfxChannelVolumeLessThan

#define DESCRIPTION \
/*******************************************************************************
* If a sfx channel's volume is < volume argument
********************************************************************************
* @return     Continue execution from LABEL if TRUE
* @param VOLUME signed range 0 - 100% (0x0000-0x7FFF)
* @exception: If sfx channel is free (no audio playing), Go to LABEL.
*       VOLUME can NOT be negative
*******************************************************************************/

#define PARAMS BYTE(CHANNEL_NUM), DBYTE(VOLUME), BYTE(GOTOLABEL)

_AI_CMD(
    {
        struct
        {
            u8 cmd;
            s8 slotID;
            u8 val[];
        } *ai = AiListp + Offset;
        s16 vol = CharArrayTo16(ai->val,0);
        if ((ai->slotID >= 0) && (ai->slotID < 8) && (sfx_related[ai->slotID].Volume2 < vol))
        {
            Offset = chraiGoToLabel(AiListp, Offset, ai->val[2]);
        }
        else
        {
            Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        }
        break;
    })

#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE

//==============================================================================
//# SFX COMMANDS END
//==============================================================================




//==============================================================================
//# VEHICHLE COMMANDS
//==============================================================================

//==============================================================================
//## VEHICLE START PATH
//==============================================================================

/* #define CMDNAME VEHICLE_START_PATH */
#define CMDNAME VehicleStartPath

#define DESCRIPTION \
/*******************************************************************************
* Makes vehicle follow a predefined path within setup
*******************************************************************************/

#define PARAMS BYTE(PATH_NUM)

_AI_CMD(
    {
        AIRecord * ai = AiListp + Offset;
        PathRecord *path = pathFindById(ai->val[0]);
        if (VehichleEntityp)
        {
            VehichleEntityp->path = path;
            VehichleEntityp->nextstep = 0;
        }
        Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        break;
    })

#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## VEHICLE SPEED
//==============================================================================

/* #define CMDNAME VEHICLE_SPEED */
#define CMDNAME VehicleSpeed

#define DESCRIPTION \
/*******************************************************************************
* Sets vehicle speed, usually paired with VEHICLE_START_PATH
********************************************************************************
* @param TOP_SPEED Unsigned (1000 = 1 meter per second)
*             ACCELERATION_TIME60 is number of game ticks to reach top speed
*             (lower = faster)
*******************************************************************************/

#define PARAMS DBYTE(TOP_SPEED), DBYTE(ACCELERATION_TIME60)

_AI_CMD(
    {
        AIRecord * ai = AiListp + Offset;
        f32       speedtime = CharArrayTo16(ai->val,2);
        f32       speedaim = CharArrayTo16(ai->val,0) * 100.0f / 15360.0f;
        if (VehichleEntityp)
        {
            VehichleEntityp->speedaim = speedaim;
            VehichleEntityp->speedtime60 = speedtime;
        }
        Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        break;
    })

//POLYMORPHS

_AI_CMD_POLYMORPH(,
                    VEHICLE_SPEED_MPH,
                    ((TOP_SPEED * 447), TOP_SPEED*(60/MPH_PER_SECOND)),
                    (TOP_SPEED, MPH_PER_SECOND))
_AI_CMD_POLYMORPH(,
                    VEHICLE_SPEED_KPH,
                    ((TOP_SPEED * 278), TOP_SPEED*(60/KPH_PER_SECOND)),
                    (TOP_SPEED, KPH_PER_SECOND))
_AI_CMD_POLYMORPH(,
                    VEHICLE_SPEED_MS,
                    ((TOP_SPEED * 1000), TOP_SPEED*(60/MSS)),
                    (TOP_SPEED, MSS))

#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## AIRCRAFT ROTOR SPEED
//==============================================================================

/* #define CMDNAME AIRCRAFT_ROTOR_SPEED */
#define CMDNAME AircraftRotorSpeed

#define DESCRIPTION \
/*******************************************************************************
* Sets aircraft's rotor speed
********************************************************************************
* @param ROTOR_SPEED Unsigned Revolutions Per Minute (RPM)
*             ACCELERATION_TIME60 is number of game ticks to reach top speed
*             (lower = faster)
*******************************************************************************/

#define PARAMS DBYTE(ROTOR_SPEED), DBYTE(ACCELERATION_TIME60)

_AI_CMD(
    {
        AIRecord * ai = AiListp + Offset;
        f32       speedtime = CharArrayTo16(ai->val,2);
        f32       speedaim = CharArrayTo16(ai->val,0) * M_TAU_F / 3600.0f;
        if (AircraftEntityp)
        {
            AircraftEntityp->rotaryspeedaim = speedaim;
            AircraftEntityp->rotaryspeedtime = speedtime;
        }
        Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        break;
    })

//POLYMORPHS

_AI_CMD_POLYMORPH(,
                    AIRCRAFT_ROTOR_SPEED_RPMS,
                    (ROTOR_SPEED, ROTOR_SPEED*(60/RPMS)),
                    (ROTOR_SPEED, RPMS))

#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE

//==============================================================================
//# VEHICHLE COMMANDS END
//==============================================================================




//==============================================================================
//## IF CAMERA IS IN INTRO
//==============================================================================

/* #define CMDNAME IF_CAMERA_IS_IN_INTRO */
#define CMDNAME IFCameraIsInIntro

#define DESCRIPTION \
/*******************************************************************************
* If camera mode equal to INTRO_CAM/FADESWIRL_CAM (viewing mission intro)
********************************************************************************
* @return     Continue execution from LABEL if TRUE
* @exception: If setup lacks intro camera structs, intro will be skipped
*******************************************************************************/

#define PARAMS BYTE(GOTOLABEL)

_AI_CMD(
    {
        AIRecord * ai = AiListp + Offset;
        if ((get_camera_mode() == 1) || (get_camera_mode() == 2))
        {
            Offset = chraiGoToLabel(AiListp, Offset, ai->val[0]);
        }
        else
        {
            Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        }
        break;
    })

#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## IF CAMERA IS IN BOND SWIRL
//==============================================================================

/* #define CMDNAME IF_CAMERA_IS_IN_BOND_SWIRL */
#define CMDNAME IFCameraIsInBondSwirl

#define DESCRIPTION \
/*******************************************************************************
* If camera mode equal to SWIRL_CAM (moving to back of Bond's head)
********************************************************************************
* @return     Continue execution from LABEL if TRUE
* @exception: If setup lacks swirl points, intro swirl will be skipped
*******************************************************************************/

#define PARAMS BYTE(GOTOLABEL)

_AI_CMD(
    {
        AIRecord * ai = AiListp + Offset;
        if (get_camera_mode() == 3)
        {
            Offset = chraiGoToLabel(AiListp, Offset, ai->val[0]);
        }
        else
        {
            Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        }
        break;
    })

#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## TV CHANGE SCREEN BANK
//==============================================================================

/* #define CMDNAME TV_CHANGE_SCREEN_BANK */
#define CMDNAME TvChangeScreenBank

#define DESCRIPTION \
/*******************************************************************************
* Change the screen bank of a tagged TV monitor
********************************************************************************
* @exception: If tagged object has multiple screens, use SCREEN_INDEX to set.
*       If tagged object has one screen, SCREEN_INDEX is ignored
*******************************************************************************/

#define PARAMS BYTE(OBJECT_TAG), BYTE(SCREEN_INDEX), BYTE(SCREEN_BANK)

_AI_CMD(
    {
        AIRecord * ai = AiListp + Offset;
        ObjectRecord *obj = objFindByTagId(ai->val[0]);
        if (obj && obj->prop)
        {
            if (obj->type == PROPDEF_MONITOR)
            {
                MonitorObjRecord *sm = (MonitorObjRecord *) obj;
                monitorSetImageByNum(&sm->Monitor.cmdlist, ai->val[2]);
            }
            else if (obj->type == PROPDEF_MULTI_MONITOR)
            {
                u8 slot = ai->val[1];
                if (slot < 4)
                {
                    MultiMonitorObjRecord *mm = (MultiMonitorObjRecord *) obj;
                    monitorSetImageByNum(&mm->Monitor[slot].cmdlist, ai->val[2]);
                }
            }
        }
        Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        break;
    })

#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## IF BOND IN TANK
//==============================================================================

/* #define CMDNAME IF_BOND_IN_TANK */
#define CMDNAME IFBondInTank /*canonical name*/

#define DESCRIPTION \
/*******************************************************************************
* If Bond is controlling tank
********************************************************************************
* @return     Continue execution from LABEL if TRUE
*******************************************************************************/

#define PARAMS BYTE(GOTOLABEL)

_AI_CMD(
    {
        AIRecord * ai = AiListp + Offset;
        if (isBondInTank() == TRUE)
        {
            Offset = chraiGoToLabel(AiListp, Offset, ai->val[0]);
        }
        else
        {
            Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        }
        break;
    })

#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## END LEVEL
//==============================================================================

/* #define CMDNAME EXIT_LEVEL */
#define CMDNAME EndLevel /* canonical name */

#define DESCRIPTION \
/*******************************************************************************
* Exits the level
********************************************************************************
* @exception: Recommend not to use this command, instead use GAILIST_END_LEVEL for
*       exit cutscene ailist.
*       Retail game has a glitch with hires mode that needs to execute this
*       command in a loop, check Cuba's 1000 List
*******************************************************************************/

#define PARAMS /*NONE*/

_AI_CMD(
    {
        if (cameraBufferToggle)
        {
            if (cameraFrameCounter2 == FALSE)
            {
                cameraFrameCounter2 = TRUE;
            }
        }
        else
        {
            bossReturnTitleStage();
        }
        Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        break;
    })

#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE




//==============================================================================
//# CINEMATICS
//==============================================================================


/*******************************************************************************
* Prepare to Transition to bond from Camera
*******************************************************************************/
DEFINE(CameraTransitionToBond()
       Yield()
       Yield()
       Yield())

/*******************************************************************************
* Prepare to Transition from bond to Camera
*******************************************************************************/
DEFINE(CameraTransitionFromBond()
       BondHideWeapons()
       Yield()
       Yield()
       Yield())





//==============================================================================
//## CAMERA RETURN TO BOND
//==============================================================================

/* #define CMDNAME CAMERA_RETURN_TO_BOND */
#define CMDNAME CameraReturnToBond

#define DESCRIPTION \
/*******************************************************************************
* Switch back to first person view
********************************************************************************
* @exception: Unused command, never used in retail game.
*       Tagged items within inventory will become invalid after command - only
*       weapons are safe.
*       Must have 3 Yield commands before executing this command or else
*       engine will crash on console (use CameraTransitionToBond).
*       Mission time is resumed on return to first person view
*******************************************************************************/

#define PARAMS /*NONE*/

_AI_CMD(
    {
        set_camera_mode(CAMERAMODE_FP_NOINPUT);
        Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        break;
    })

//POLYMORPHS

#undef DESCRIPTION
#undef PARAMS
#undef CMDNAME
#undef CODE





//==============================================================================
//## CAMERA LOOK AT BOND FROM PAD
//==============================================================================

/* #define CMDNAME CAMERA_LOOK_AT_BOND_FROM_PAD */
#define CMDNAME CameraLookAtBondFromPad

#define DESCRIPTION \
/*******************************************************************************
* Change view to PAD and look at Bond
********************************************************************************
* @exception: Must have a BondHideWeapons and 3 Yield commands before executing
*       or else engine will crash on console (use CameraTransitionFromBond).
*       If camera mode is already in third person then you do NOT need to
*       do the above.
*       Mission time is paused while in third person
*******************************************************************************/

#define PARAMS DBYTE(PAD)

_AI_CMD(
    {
        AIRecord * ai = AiListp + Offset;
        u16       padnum = CharArrayTo16(ai->val,0);
        if (isNotBoundPad(padnum))
        {
            dword_CODE_bss_800799F8 = &g_CurrentSetup.pads[padnum];
        }
        else
        {
            dword_CODE_bss_800799F8 = (PadRecord *) &g_CurrentSetup.boundpads[getBoundPadNum(padnum)];
        }
        set_camera_mode(CAMERAMODE_POSEND);
        Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        break;
    })

#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## CAMERA SWITCH
//==============================================================================

/* #define CMDNAME CAMERA_SWITCH */
#define CMDNAME CameraSwitch

#define DESCRIPTION \
/*******************************************************************************
* Change view to tagged cameras position and rotation
********************************************************************************
* @exception: Must have a BondHideWeapons and 3 Yield commands before executing
*       or else engine will crash on console (use CameraTransitionFromBond).
*       If camera mode is already in third person then you do NOT need to
*       do the above
*       Only look at Bond if flag is set.
*       Unused flag may have separated look at Bond as x/y flags instead of
*       a single flag - for retail unused flag does nothing
*       Mission time is paused while in third person
*******************************************************************************/

#define PARAMS BYTE(OBJECT_TAG), DBYTE(LOOK_AT_BOND_FLAG), DBYTE(UNUSED_FLAG)

_AI_CMD(
    {
        AIRecord * ai = AiListp + Offset;
        TagObjectRecord *tag = sub_GAME_7F057080(ai->val[0]);
        if (tag)
        {
            int TagIndex = tagGetCommandIndex(tag);
            if (TagIndex >= 0)
            {
                CutsceneRecord *cdef = setupGetPtrToCommandByIndex(tag->OffsetToObj + TagIndex);
                dword_CODE_bss_800799F8 = NULL;
                gBondViewCutscene = cdef;
                dword_CODE_bss_80079A18 = CharArrayTo16(ai->val,1);
                dword_CODE_bss_80079A1C = CharArrayTo16(ai->val,3);
                set_camera_mode(CAMERAMODE_POSEND);
            }
        }
        Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        break;
    })

#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## IF BOND Y POS LESS THAN
//==============================================================================

/* #define CMDNAME IF_BOND_Y_POS_LESS_THAN */
#define CMDNAME IFBondYPosLessThan

#define DESCRIPTION \
/*******************************************************************************
* If Bond's y axis position < Y_POS
********************************************************************************
* @return     Continue execution from LABEL if TRYE
* @param Y_POS signed Integer
* @exception: Checks if Bond's y axis is below Y_POS.
*       Uses world units. (scale is 1:1 to in-game position.)
*       Bond's point of view is accounted for by command (like debug's manpos)
*******************************************************************************/

#define PARAMS DBYTE(Y_POS), BYTE(GOTOLABEL)

_AI_CMD(
    {
        AIRecord * ai = AiListp + Offset;
        f32       bondpos = (s16) CharArrayTo16(ai->val,0);
        if (get_curplayer_positiondata()->pos.y < bondpos)
        {
            Offset = chraiGoToLabel(AiListp, Offset, ai->val[2]);
        }
        else
        {
            Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        }
        break;
    })

#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## HUD HIDE AND LOCK CONTROLS AND PAUSE MISSION TIME
//==============================================================================

/* #define CMDNAME HUD_HIDE_AND_LOCK_CONTROLS_AND_PAUSE_MISSION_TIME */
#define CMDNAME BondDisableControl

#define DESCRIPTION \
/*******************************************************************************
* hide hud elements, lock player controls and pause mission time
********************************************************************************
* @param BITFIELD use HUD_# flags to hide individual Hud Elements
* @exception: Will not hide element on command execution.
*       This is needed for dialogue/HUD countdown while in cinema mode.
*       Flags can be combined together to show multiple elements.
*       Sequential executions of HUD_HIDE can be used to hide more elements,
*       but once an element has been hidden it cannot be shown again until
*       HUD_SHOW_ALL is executed.
*       Bond can take damage while in locked state.
*       Mission time is paused while in locked state
*******************************************************************************/

#define PARAMS BYTE(BITFIELD)

_AI_CMD(
    {
        AIRecord1 * ai = AiListp + Offset;
        gunSetSightVisible(GUNSIGHTREASON_NOCONTROL, FALSE);
        gunSetGunAmmoVisible(GUNAMMOREASON_NOCONTROL, FALSE);
        if (!(PLAYERFLAG_NOCONTROL & ai->val))
        {
            hudmsgsSetOff(PLAYERFLAG_NOCONTROL);
        }
        if (!(ai->val & PLAYERFLAG_LOCKCONTROLS))
        {
            bondviewSetUpperTextDisplayFlag(PLAYERFLAG_NOCONTROL);
        }
        if (!(ai->val & PLAYERFLAG_NOTIMER))
        {
            countdownTimerSetVisible(16, FALSE);
        }
        is_timer_active = FALSE;
        Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        break;
    })

#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## HUD SHOW ALL AND UNLOCK CONTROLS AND RESUME MISSION TIME
//==============================================================================

/* #define CMDNAME HUD_SHOW_ALL_AND_UNLOCK_CONTROLS_AND_RESUME_MISSION_TIME */
#define CMDNAME BondEnableControl

#define DESCRIPTION \
/*******************************************************************************
* Show all hud elements, unlock player control and resume mission time
********************************************************************************
* @exception: Should only be executed after HUD_HIDE_AND_LOCK_CONTROLS
*       Mission time is resumed
*******************************************************************************/

#define PARAMS /*NONE*/

_AI_CMD(
    {
        gunSetSightVisible(GUNSIGHTREASON_NOCONTROL, TRUE);
        gunSetGunAmmoVisible(GUNAMMOREASON_NOCONTROL, TRUE);
        hudmsgsSetOn(PLAYERFLAG_NOCONTROL);
        bondviewClearUpperTextDisplayFlag(2);
        countdownTimerSetVisible(16, TRUE);
        is_timer_active = TRUE;
        Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        break;
    })

#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## CHR TRY TELEPORTING TO PAD
//==============================================================================

/* #define CMDNAME CHR_TRY_TELEPORTING_TO_PAD */
#define CMDNAME TRYTeleportingChrToPad

#define DESCRIPTION \
/*******************************************************************************
* Teleport chr to PAD
********************************************************************************
* @return     Continue execution from LABEL if Successful
*******************************************************************************/

#define PARAMS BYTE(CHR_NUM), DBYTE(PAD), BYTE(GOTOLABEL)

_AI_CMD(
    {
        AIRecord * ai = AiListp + Offset;
        s32        padnum = CharArrayTo16(ai->val,1);
        ChrRecord *chr = chrFindById(ChrEntityp, ai->val[0]);
        bool       pass = FALSE;
        PadRecord *pad;
        f32        FacingDirection;
        coord3d    pos;
        StandTile *stan;
        if (chr)
        {
            padnum = chrResolvePadId(ChrEntityp, padnum);
            if (isNotBoundPad(padnum))
            {
                pad = &g_CurrentSetup.pads[padnum];
            }
            else
            {
                pad = (PadRecord *) &g_CurrentSetup.boundpads[getBoundPadNum(padnum)];
            }
            FacingDirection = atan2f(pad->target.x, pad->target.z);
            pos.x = pad->pos.x;
            pos.y = pad->pos.y;
            pos.z = pad->pos.z;
            stan = pad->stan;
            sub_GAME_7F03D058(chr->prop, FALSE);
            if (chrAdjustPosForSpawn(&pos, &stan, FacingDirection, TRUE))
            {
                {
                    chr->prop->pos.x = pos.x;
                    chr->prop->pos.y = pos.y;
                    chr->prop->pos.z = pos.z;
                }
                chr->prop->stan = stan;
                chr->chrflags = chr->chrflags | CHRFLAG_INIT;
                setsubroty(chr->model, FacingDirection);
                setsuboffset(chr->model, &pos);
                sub_GAME_7F020D94(chr);
                if (chr->prop == pPlayer->prop)
                {
                    pPlayer->pos4.x = pos.x;
                    pPlayer->pos4.y = pos.y;
                    pPlayer->pos4.z = pos.z;
                    pPlayer->current_tile_ptr = stan;
                }
                pass = TRUE;
            }
            sub_GAME_7F03D058(chr->prop, TRUE);
        }
        if (pass)
        {
            Offset = chraiGoToLabel(AiListp, Offset, ai->val[3]);
        }
        else
        {
            Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        }
        break;
    })

//POLYMORPHS

_AI_CMD_POLYMORPH(,
                    TRYTeleportingMeToPad,
                    (CHR_SELF, PAD, GOTOLABEL),
                    (PAD, GOTOLABEL))

#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## SCREEN FADE TO BLACK
//==============================================================================

/* #define CMDNAME SCREEN_FADE_TO_BLACK */
#define CMDNAME ScreenFadeToBlack

#define DESCRIPTION \
/*******************************************************************************
* Fades the screen out to black
********************************************************************************
* @exception: Fade duration is 1 second
*******************************************************************************/

#define PARAMS /*NONE*/

_AI_CMD(
    {
    if (stop_time_flag != 2)
    {
        currentPlayerSetFadeColour(0, 0, 0, 0);
        currentPlayerSetFadeFrac(CHRLV_FRAMERATE_F, 1);
    }
    Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
    break;
    })

#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## SCREEN FADE FROM BLACK
//==============================================================================

/* #define CMDNAME SCREEN_FADE_FROM_BLACK */
#define CMDNAME ScreenFadeFromBlack

#define DESCRIPTION \
/*******************************************************************************
* Fades the screen from black
********************************************************************************
* @exception: Fade duration is 1 second
*******************************************************************************/

#define PARAMS /*NONE*/

_AI_CMD(
    {
        if (stop_time_flag != 2)
        {
            currentPlayerSetFadeColour(0, 0, 0, 1);
            currentPlayerSetFadeFrac(CHRLV_FRAMERATE_F, 0);
        }
        Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        break;
    })

#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## IF SCREEN FADE COMPLETED
//==============================================================================

/* #define CMDNAME IF_SCREEN_FADE_COMPLETED */
#define CMDNAME IFScreenFadeCompleted

#define DESCRIPTION \
/*******************************************************************************
* When screen fade has completed (from/to black)
********************************************************************************
* @return     Continue execution from LABEL if TRUE
* @exception: Fade duration is 1 second
*******************************************************************************/

#define PARAMS BYTE(GOTOLABEL)

_AI_CMD(
    {
        AIRecord * ai = AiListp + Offset;
        if (pPlayer->colourfadetimemax60 < 0)
        {
            Offset = chraiGoToLabel(AiListp, Offset, ai->val[0]);
        }
        else
        {
            Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        }
        break;
    })

#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## CHR HIDE ALL
//==============================================================================

/* #define CMDNAME CHR_HIDE_ALL */
#define CMDNAME HideAllChrs

#define DESCRIPTION \
/*******************************************************************************
* Hide all characters in level - including Bond's third person model.
********************************************************************************
* @exception: Hidden characters will halt their AI List execution until unhidden
*       Execute this before switching to exit camera or Bond will disappear
*******************************************************************************/

#define PARAMS /*NONE*/

_AI_CMD(
    {
        s32 num;
        for (num = get_numguards() - 1; num >= 0; num--)
        {
            if (g_ChrSlots[num].model != NULL)
            {
                g_ChrSlots[num].chrflags |= CHRFLAG_HIDDEN;
            }
        }
        Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        break;
    })

#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## CHR SHOW ALL
//==============================================================================

/* #define CMDNAME CHR_SHOW_ALL */
#define CMDNAME ShowAllChrs

#define DESCRIPTION \
/*******************************************************************************
* Show all characters previously hidden by HideAllChrs
*******************************************************************************/

#define PARAMS /*NONE*/

_AI_CMD(
    {
        s32 num;
        for (num = get_numguards() - 1; num >= 0; num--)
        {
            g_ChrSlots[num].chrflags &= ~CHRFLAG_HIDDEN;
        }
        Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        break;
    })

#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE


//==============================================================================
//# CINEMATICS END
//==============================================================================





//==============================================================================
//## DOOR OPEN INSTANT
//==============================================================================

/* #define CMDNAME DOOR_OPEN_INSTANT */
#define CMDNAME DoorOpenInstant

#define DESCRIPTION \
/*******************************************************************************
* Instantly open tagged door
********************************************************************************
* @exception: Mostly used for cutscenes, does NOT trigger door opening sfx.
*       Open tagged door even if locked
*******************************************************************************/

#define PARAMS BYTE(OBJECT_TAG)

_AI_CMD(
    {
        AIRecord * ai = AiListp + Offset;
        DoorRecord *door = objFindByTagId(ai->val[0]);
        if (door && door->prop)
        {
            door->speed = 0;
            door->openPosition = door->maxFrac;
            door->openedTime = g_GlobalTimer;
            door->openstate = DOORSTATE_STATIONARY;
            sub_GAME_7F052B00(door);
            doorActivatePortal(door);
            sub_GAME_7F053B10(door);
        }
        Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        break;
    })

#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## CHR REMOVE ITEM IN HAND
//==============================================================================

/* #define CMDNAME CHR_REMOVE_ITEM_IN_HAND */
#define CMDNAME ChrRemoveItemInHand

#define DESCRIPTION \
/*******************************************************************************
* Remove the item held by HAND_INDEX
********************************************************************************
* @exception: Does NOT drop item, instead clears holding item flag for HAND_INDEX
*******************************************************************************/

#define PARAMS BYTE(CHR_NUM), BYTE(HAND_INDEX)

_AI_CMD(
    {
        AIRecord * ai = AiListp + Offset;
        ChrRecord *chr = chrFindById(ChrEntityp, ai->val[0]);
        if (chr)
        {
            chrSetWeaponFlag4(chr, ai->val[1]);
        }
        Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        break;
    })

#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## IF NUMBER OF ACTIVE PLAYERS LESS THAN
//==============================================================================

/* #define CMDNAME IF_NUMBER_OF_ACTIVE_PLAYERS_LESS_THAN */
#define CMDNAME IfNumberOfActivePlayersLessThan

#define DESCRIPTION \
/*******************************************************************************
* If the number of active players < NUMBER
********************************************************************************
* @return     Continue execution from LABEL if TRUE
* @exception: Single player always has a total of active players set to 1
*******************************************************************************/

#define PARAMS BYTE(NUMBER), BYTE(GOTOLABEL)

_AI_CMD(
    {
        struct
        {
            u8 cmd;
            s8 val;
            u8 label;
        } *ai = AiListp + Offset;
        if (getPlayerCount() < ai->val)
        {
            Offset = chraiGoToLabel(AiListp, Offset, ai->label);
        }
        else
        {
            Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        }
        break;
    })

//POLYMORPHS

#undef DESCRIPTION
#define DESCRIPTION \
/*******************************************************************************
* If Single Player
********************************************************************************
* @return     Continue execution from LABEL if TRUE
*******************************************************************************/

_AI_CMD_POLYMORPH(,
                    IFSinglePlayer,
                    (2, GOTOLABEL),
                    (GOTOLABEL))

#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## IF BOND ITEM TOTAL AMMO LESS THAN
//==============================================================================

/* #define CMDNAME IF_BOND_ITEM_TOTAL_AMMO_LESS_THAN */
#define CMDNAME IFBondItemTotalAmmoLessThan

#define DESCRIPTION \
/*******************************************************************************
* If Bond's total ammo for item < AMMO_TOTAL
********************************************************************************
* @return     Continue execution from LABEL if TRUE
* @param AMMO_TOTAL singed char includes loaded gun + reserve
*******************************************************************************/

#define PARAMS BYTE(ITEM_NUM), BYTE(AMMO_TOTAL), BYTE(GOTOLABEL)

_AI_CMD(
    {
        AIRecord * ai = AiListp + Offset;
        if (currentPlayerGetAmmoCount((s8) ai->val[0]) < (s8) ai->val[1])
        {
            Offset = chraiGoToLabel(AiListp, Offset, ai->val[2]);
        }
        else
        {
            Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        }
        break;
    })

#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## BOND EQUIP ITEM
//==============================================================================

/* #define CMDNAME BOND_EQUIP_ITEM */
#define CMDNAME BondEquipItem

#define DESCRIPTION \
/*******************************************************************************
* Forces Bond to equip an item - only works in first person
********************************************************************************
* @exception: Can be used for any item, even if Bond does NOT have it in inventory
*******************************************************************************/

#define PARAMS BYTE(ITEM_NUM)

_AI_CMD(
    {
        AIRecord * ai = AiListp + Offset;
        currentPlayerEquipWeaponWrapper(GUNRIGHT, (s8) ai->val[0]);
        currentPlayerEquipWeaponWrapper(GUNLEFT, 0);
        Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        break;
    })

#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## BOND EQUIP ITEM CINEMA
//==============================================================================

/* #define CMDNAME BOND_EQUIP_ITEM_CINEMA */
#define CMDNAME BondEquipItemCinema

#define DESCRIPTION \
/*******************************************************************************
* Forces Bond to equip an item - only works in third person (cinema)
********************************************************************************
* @exception: Can be used for any item, even if Bond does NOT have it in inventory
*******************************************************************************/

#define PARAMS BYTE(ITEM_NUM)

_AI_CMD(
    {
        AIRecord * ai = AiListp + Offset;
        currentPlayerUnEquipWeaponWrapper(GUNRIGHT, (s8) ai->val[0]);
        currentPlayerUnEquipWeaponWrapper(GUNLEFT, 0);
        Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        break;
    })

#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## BOND SET LOCKED VELOCITY
//==============================================================================

/* #define CMDNAME BOND_SET_LOCKED_VELOCITY */
#define CMDNAME BondSetLockedVelocity

#define DESCRIPTION \
/*******************************************************************************
* Forces Bond to move in X/Z direction
********************************************************************************
* @param X/Z_SPEED60 is number of world units per tick (1 = 0.6m/s)
* @exception: Only works when Bond has been locked by HUD_HIDE_AND_LOCK_CONTROLS.
*       Used for dam jump.
*       Uses world units. (scale is 1:1 to in-game position.)
*******************************************************************************/

#define PARAMS BYTE(X_SPEED60), BYTE(Z_SPEED60)

_AI_CMD(
    {
        AIRecord * ai = AiListp + Offset;
        g_ForceBondMoveOffset.x = (s8) ai->val[0];
        g_ForceBondMoveOffset.y = 0;
        g_ForceBondMoveOffset.z = (s8) ai->val[1];
        Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        break;
    })

#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## IF OBJECT IN ROOM WITH PAD
//==============================================================================

/* #define CMDNAME IF_OBJECT_IN_ROOM_WITH_PAD */
#define CMDNAME IFObjectInRoomWithPad

#define DESCRIPTION \
/*******************************************************************************
* If tagged object in the same room with PAD
********************************************************************************
* @return     Continue execution from LABEL if TRUE
*******************************************************************************/

#define PARAMS BYTE(OBJECT_TAG), DBYTE(PAD), BYTE(GOTOLABEL)

_AI_CMD(
    {
        AIRecord * ai = AiListp + Offset;
        u16           padnum = CharArrayTo16(ai->val,1);
        PadRecord *pad;
        ObjectRecord *obj = objFindByTagId(ai->val[0]);
        if (isNotBoundPad(padnum))
        {
            pad = &g_CurrentSetup.pads[padnum * 1];
        }
        else
        {
            pad = (PadRecord *) &g_CurrentSetup.boundpads[getBoundPadNum(padnum)];
        }
        if (pad->stan && obj && obj->prop && (pad->stan->RoomID == obj->prop->stan->RoomID))
        {
            Offset = chraiGoToLabel(AiListp, Offset, ai->val[3]);
        }
        else
        {
            Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        }
        break;
    })

#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## IF GUARD IS FIRING AND LOCKED FORWARD
//==============================================================================

/* #define CMDNAME IF_GUARD_IS_FIRING_AND_TARGET_180_RANGE */
#define CMDNAME IFImFiringAndLockedForward

#define DESCRIPTION \
/*******************************************************************************
* If guard is in firing state (ACT_ATTACK) and TARGET_180_RANGE is set
********************************************************************************
* @return     Continue execution from LABEL if TRUE
*******************************************************************************/

#define PARAMS BYTE(GOTOLABEL)

_AI_CMD(
    {
        AIRecord1 * ai = AiListp + Offset;
        if (ChrEntityp->actiontype == ACT_ATTACK &&
            !ChrEntityp->act_attack.type_of_motion &&
             ChrEntityp->act_attack.attacktype & ATTACKTYPE_DONTTURN)
        {
            Offset = chraiGoToLabel(AiListp, Offset, ai->val);
        }
        else
        {
            Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        }
        break;
    })

#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## IF GUARD IS FIRING
//==============================================================================

/* #define CMDNAME IF_GUARD_IS_FIRING */
#define CMDNAME IFImFiring

#define DESCRIPTION \
/*******************************************************************************
* If guard is in firing state (ACT_ATTACK)
********************************************************************************
* @return     Continue execution from LABEL if TRUE
*******************************************************************************/

#define PARAMS BYTE(GOTOLABEL)

_AI_CMD(
    {
        AIRecord1 * ai = AiListp + Offset;
        if (ChrEntityp->actiontype == ACT_ATTACK)
        {
            Offset = chraiGoToLabel(AiListp, Offset, ai->val);
        }
        else
        {
            Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        }
        break;
    })

#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## SWITCH ENVIRONMENT
//==============================================================================

/* #define CMDNAME SWITCH_SKY */
#define CMDNAME SwitchSky

#define DESCRIPTION \
/*******************************************************************************
* Instantly switch Sky to the Alternative Sky
********************************************************************************
* @exception: Can NOT be stopped after executing.
*       Level must have a Sky assigned or will crash!
*******************************************************************************/

#define PARAMS /*NONE*/

_AI_CMD(
    {
        switch_to_solosky2(1.0);
        Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        break;
    })

#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## TRIGGER FADE AND EXIT LEVEL ON BUTTON PRESS
//==============================================================================

/* #define CMDNAME TRIGGER_FADE_AND_EXIT_LEVEL_ON_BUTTON_PRESS */
#define CMDNAME TriggerFadeAndExitLevelOnButtonPress

#define DESCRIPTION \
/*******************************************************************************
* If player pressed any button, fade to black and exit level
********************************************************************************
* @exception: This command activates a state where game will fade to black when
*       button input is detected from controller 1.
*       Does NOT pause mission time
*******************************************************************************/

#define PARAMS /*NONE*/

_AI_CMD(
    {
        if (stop_time_flag == FALSE)
        {
            stop_time_flag = TRUE;
        }
        Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        break;
    })

#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## IF BOND IS DEAD
//==============================================================================

/* #define CMDNAME IF_BOND_IS_DEAD */
#define CMDNAME IFBondIsDead

#define DESCRIPTION \
/*******************************************************************************
* If Bond has died/been killed
********************************************************************************
* @return     Continue execution from LABEL if TRUE
*******************************************************************************/

#define PARAMS BYTE(GOTOLABEL)

_AI_CMD(
    {
        AIRecord * ai = AiListp + Offset;
        if (pPlayer->bonddead)
        {
            Offset = chraiGoToLabel(AiListp, Offset, ai->val[0]);
        }
        else
        {
            Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        }
        break;
    })

#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## BOND DISABLE DAMAGE AND PICKUPS
//==============================================================================

/* #define CMDNAME BOND_DISABLE_DAMAGE_AND_PICKUPS */
#define CMDNAME BondDisableDamageAndPickups

#define DESCRIPTION \
/*******************************************************************************
* Disables Bond damage and ability to pick up items
********************************************************************************
* @exception: Commonly used for level exit AI Lists - prevents Bond dying after
*       triggering exit cutscene.
*       Use IF_BOND_DAMAGE_AND_PICKUPS_DISABLED to check if flag is set on
*******************************************************************************/

#define PARAMS /*NONE*/

_AI_CMD(
    {
        g_PlayerInvincible = TRUE;
        Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        break;
    })

#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## BOND HIDE WEAPONS
//==============================================================================

/* #define CMDNAME BOND_HIDE_WEAPONS */
#define CMDNAME BondHideWeapons

#define DESCRIPTION \
/*******************************************************************************
* Set Bond's left/right weapons to be invisible
*******************************************************************************/

#define PARAMS /*NONE*/

_AI_CMD(
    {
        remove_item_in_hand(GUNRIGHT);
        remove_item_in_hand(GUNLEFT);
        Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        break;
    })

#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## CAMERA ORBIT PAD
//==============================================================================

/* #define CMDNAME CAMERA_ORBIT_PAD */
#define CMDNAME CameraOrbitPad

#define DESCRIPTION \
/*******************************************************************************
* Change view to orbit a pad with set speed
********************************************************************************
* @param LAT_DISTANCE: Unsigned camera distance from pad in centimeters
* @param VERT_DISTANCE: Signed camera distance from pad in centimeters
* @param ORBIT_SPEED: Normalised angle -0.5 - 0 - 0.5 (8000 - 7FFF) to rotate
*                     per frame. Generally stick to a low range as it is used
*                     for delta timing (FF00-0100) (-0.02 - 0 - 0.02)
* @param PAD: pad for camera to target and orbit around
* @param Y_POS_OFFSET: Signed offset the relative y position for pad
*                      (boom/jib)
* @param INITIAL_ROTATION: Counter-clockwise normalised angle 0 - 1.0 (65535)
* @exception: Must have a BondHideWeapons and 3 Yield commands before executing
*       or else engine will crash on console (use CameraTransitionFromBond).
*       If camera mode is already in third person then you do NOT need to do
*       the above.
*       Mission time is paused while in third person
*******************************************************************************/

#define PARAMS DBYTE(LAT_DISTANCE), DBYTE(VERT_DISTANCE), DBYTE(ORBIT_SPEED60), DBYTE(PAD), DBYTE(Y_POS_OFFSET), DBYTE(INITIAL_ROTATION)

_AI_CMD(
    {
        AIRecord * ai = AiListp + Offset;
        s32       padnum;
        s32       speed60;
        s32       camDististance;
        s32       targetHeight;
        s32       camHeight;
        s32       start;
        camDististance          = CharArrayTo16(ai->val, 0);
        camHeight               = (s16)CharArrayTo16(ai->val, 2);
        speed60                 = (s16)CharArrayTo16(ai->val, 4);
        padnum                  = CharArrayTo16(ai->val, 6);
        targetHeight            = (s16)CharArrayTo16(ai->val, 8);
        start                   = CharArrayTo16(ai->val, 10);
        dword_CODE_bss_800799F8 = NULL;
        gBondViewCutscene       = NULL;
        flt_CODE_bss_80079A00   = (start * M_TAU_F) / M_U16_MAX_VALUE_F; /*unit direction 0 - 1 (increments are 0.000001)*/
        flt_CODE_bss_80079A04   = (speed60 * M_TAU_F) / M_U16_MAX_VALUE_F; /*how many increments per frame*/
        flt_CODE_bss_80079A08   = camDististance;
        flt_CODE_bss_80079A0C   = camHeight;
        flt_CODE_bss_80079A10   = targetHeight;
        dword_CODE_bss_80079A14 = padnum;
        set_camera_mode(CAMERAMODE_POSEND);
        Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        break;
    })

#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## CREDITS ROLL
//==============================================================================

/* #define CMDNAME CREDITS_ROLL */
#define CMDNAME CreditsRoll

#define DESCRIPTION \
/*******************************************************************************
* Trigger credits crawl
********************************************************************************
* @exception: Credits text and positions are stored in setup intro struct
*******************************************************************************/

#define PARAMS /*NONE*/

_AI_CMD(
    {
        credits_state = TRUE;
        Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        break;
    })

#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## IF CREDITS HAS COMPLETED
//==============================================================================

/* #define CMDNAME IF_CREDITS_HAS_COMPLETED */
#define CMDNAME IFCreditsHasCompleted

#define DESCRIPTION \
/*******************************************************************************
* If credits crawl has finished
********************************************************************************
* @return     Continue execution from LABEL if TRUE
*******************************************************************************/

#define PARAMS BYTE(GOTOLABEL)

_AI_CMD(
    {
        AIRecord *ai = AiListp + Offset;
        if (credits_state == 2)
        {
            Offset = chraiGoToLabel(AiListp, Offset, ai->val[0]);
        }
        else
        {
            Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        }
        break;
    })

#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## IF OBJECTIVE ALL COMPLETED
//==============================================================================

/* #define CMDNAME IF_OBJECTIVE_ALL_COMPLETED */
#define CMDNAME IFObjectiveAllCompleted

#define DESCRIPTION \
/*******************************************************************************
* If all objectives for current difficulty has been completed
********************************************************************************
* @return     Continue execution from LABEL if TRUE
* @exception: Uses objective difficulty settings within setup, briefing file
*       settings are not referenced.
*       Ensure both setup and briefing files are consistent
*******************************************************************************/

#define PARAMS BYTE(GOTOLABEL)

_AI_CMD(
    {
        AIRecord *ai = AiListp + Offset;
        if (objectiveIsAllComplete())
        {
            Offset = chraiGoToLabel(AiListp, Offset, ai->val[0]);
        }
        else
        {
            Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        }
        break;
    })

#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## IF FOLDER ACTOR IS EQUAL
//==============================================================================

/* #define CMDNAME IF_FOLDER_ACTOR_IS_EQUAL */
#define CMDNAME IFFolderActorIsEqual

#define DESCRIPTION \
/*******************************************************************************
* If current Bond equal to folder actor index
********************************************************************************
* @return     Continue execution from LABEL if TRUE
* @exception: In retail release only index 0 works.
*       Originally this would have checked which Bond
*       (Brosnan/Connery/Moore/Dalton) is currently used, with each briefing
*       folder using a different Bond likeness in-game. However Rare didn't
*       have the license to use the other actor's faces so this feature was
*       removed. Command is only used for Cuba (credits)
*******************************************************************************/

#define PARAMS BYTE(BOND_ACTOR_INDEX), BYTE(GOTOLABEL)

_AI_CMD(
    {
        AIRecord *ai = AiListp + Offset;
        if (fileGetBondForCurrentFolder() == (s8)ai->val[0])
        {
            Offset = chraiGoToLabel(AiListp, Offset, ai->val[1]);
        }
        else
        {
            Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        }
        break;
    })

#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## IF BOND DAMAGE AND PICKUPS DISABLED
//==============================================================================

/* #define CMDNAME IF_BOND_DAMAGE_AND_PICKUPS_DISABLED */
#define CMDNAME IFBondDamageAndPickupsDisabled

#define DESCRIPTION \
/*******************************************************************************
* If Bond damage and ability to pick up items disabled
********************************************************************************
* @return     Continue execution from LABEL if TRUE
* @exception: Used to check when Bond has exited level, usually to stop guards from
*       spawning during mission cinema.
*       Use BOND_DISABLE_DAMAGE_AND_PICKUPS to set state on
*******************************************************************************/

#define PARAMS BYTE(GOTOLABEL)

_AI_CMD(
    {
        AIRecord *ai = AiListp + Offset;
        if (g_PlayerInvincible)
        {
            Offset = chraiGoToLabel(AiListp, Offset, ai->val[0]);
        }
        else
        {
            Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        }
        break;
    })

#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## MUSIC XTRACK PLAY
//==============================================================================

/* #define CMDNAME MUSIC_XTRACK_PLAY */
#define CMDNAME MusicPlaySlot

#define DESCRIPTION \
/*******************************************************************************
* Play levels x track for duration
********************************************************************************
* @param SECONDS unsigned
* @param MUSIC_SLOT available music slots range is 0-3
* @exception: SECONDS_STOPPED_DURATION is used by MUSIC_XTRACK_STOP
*       When a slot is stopped, the xtrack will continue to play until
*       this or SECONDS_TOTAL_DURATION reaches 0.
*       If you do not want this to happen, set SECONDS_STOPPED_DURATION to 0
*******************************************************************************/

#define PARAMS BYTE(MUSIC_SLOT), BYTE(SECONDS_STOPPED_DURATION), BYTE(SECONDS_TOTAL_DURATION)

_AI_CMD(
    {
        AIRecord *ai = AiListp + Offset;
        Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        musicPlaySlot((s8)ai->val[0], ai->val[1], ai->val[2]);
        break;
    })

#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## MUSIC XTRACK STOP
//==============================================================================

/* #define CMDNAME MUSIC_XTRACK_STOP */
#define CMDNAME MusicStopSlot

#define DESCRIPTION \
/*******************************************************************************
* Stop currently playing x track in slot
********************************************************************************
* @param MUSIC_SLOT available music slots range is 0-3
* @exception: Use slot -1 to stop all xtrack slots instantly.
*       When stopping a music slot, it will let the track continue to play
*       until the seconds stopped duration time or total time
*       (set by MUSIC_XTRACK_PLAY) reaches zero.
*       This is ignored when using music slot -1
*******************************************************************************/

#define PARAMS BYTE(MUSIC_SLOT)

_AI_CMD(
    {
        AIRecord *ai = AiListp + Offset;
        Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        musicStopSlot((s8)ai->val[0]);
        break;
    })

#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## TRIGGER EXPLOSIONS AROUND BOND
//==============================================================================

/* #define CMDNAME TRIGGER_EXPLOSIONS_AROUND_BOND */
#define CMDNAME TriggerExplosionsAroundBond

#define DESCRIPTION \
/*******************************************************************************
* Triggers explosions around Bond, will continue forever
********************************************************************************
* @exception: Does NOT trigger level exit or killed in action state
*******************************************************************************/

#define PARAMS /*NONE*/

_AI_CMD(
    {
        SurroundWithExplosions(0);
        Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        break;
    })

#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## IF KILLED CIVILIANS GREATER THAN
//==============================================================================

/* #define CMDNAME IF_KILLED_CIVILIANS_GREATER_THAN */
#define CMDNAME IFKilledCiviliansGreaterThan

#define DESCRIPTION \
/*******************************************************************************
* If total civilians killed > CIVILIANS_KILLED
********************************************************************************
* @return     Continue execution from LABEL if TRUE
* @exception: Guards flagged with CHRFLAG_COUNT_DEATH_AS_CIVILIAN will count towards
*       total when killed.
*       Usually set for scientists/civilians/innocent NPCs
*******************************************************************************/

#define PARAMS BYTE(CIVILIANS_KILLED), BYTE(GOTOLABEL)

_AI_CMD(
    {
        AIRecord *ai = AiListp + Offset;
        if (ai->val[0] < get_civilian_casualties())
        {
            Offset = chraiGoToLabel(AiListp, Offset, ai->val[1]);
        }
        else
        {
            Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        }
        break;
    })

#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## IF CHR WAS SHOT SINCE LAST CHECK
//==============================================================================

/* #define CMDNAME IF_CHR_WAS_SHOT_SINCE_LAST_CHECK */
#define CMDNAME IFChrWasShotSinceLastCheck

#define DESCRIPTION \
/*******************************************************************************
* If chr was shot since last check
********************************************************************************
* @return     Continue execution from LABEL if TRUE
* @exception: Checks chr->chrflags if CHRFLAG_WAS_HIT is set.
*       If true, unset flag and Go to LABEL.
*       CHRFLAG_WAS_HIT is set even if guard is invincible
*******************************************************************************/

#define PARAMS BYTE(CHR_NUM), BYTE(GOTOLABEL)

_AI_CMD(
    {
        AIRecord * ai  = AiListp + Offset;
        ChrRecord *chr = chrFindById(ChrEntityp, ai->val[0]);
        if (chr && chr->chrflags & CHRFLAG_WAS_HIT)
        {
            chr->chrflags &= ~CHRFLAG_WAS_HIT;
            Offset = chraiGoToLabel(AiListp, Offset, ai->val[1]);
        }
        else
        {
            Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        }
        break;
    })

#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## BOND KILLED IN ACTION
//==============================================================================

/* #define CMDNAME BOND_KILLED_IN_ACTION */
#define CMDNAME BondKilledInAction

#define DESCRIPTION \
/*******************************************************************************
* Sets briefing status to killed in action, automatic mission failure
********************************************************************************
* @exception: Does NOT kill Bond, only changes the mission status
*******************************************************************************/

#define PARAMS /*NONE*/

_AI_CMD(
    {
        g_isBondKIA = TRUE;
        Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        break;
    })

#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## GUARD RAISES ARMS
//==============================================================================

/* #define CMDNAME GUARD_RAISES_ARMS */
#define CMDNAME RaiseArms

#define DESCRIPTION \
/*******************************************************************************
* Makes me raise my arms for half a second
*******************************************************************************/

#define PARAMS /*NONE*/

_AI_CMD(
    {
        chrTrySurprisedSurrender(ChrEntityp);
        Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        break;
    })

#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## GAS LEAK AND FADE FOG
//==============================================================================

/* #define CMDNAME GAS_LEAK_AND_FADE_FOG */
#define CMDNAME GasLeakAndFadeFog

#define DESCRIPTION \
/*******************************************************************************
* Trigger gas leak event and slowly transition fog to the next fogs slot
********************************************************************************
* @exception: This command triggers a gas leak.
*       For the level egypt, this command will not trigger a gas leak, but
*       instead will only transition the fog.
*       Can NOT be stopped after executing.
*       Level must have a fog assigned or will crash!
*******************************************************************************/

#define PARAMS /*NONE*/

_AI_CMD(
    {
        void *p[3] = ptr_80030A88_3words;
        init_trigger_toxic_gas_effect(p);
        Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        break;
    })

//POLYMORPHS

#undef DESCRIPTION
#define DESCRIPTION \
/*******************************************************************************
* Trigger gas leak event and slowly transition fog to the next fogs slot
********************************************************************************
* @exception: This command triggers a gas leak.
*       For the level Egypt, this command will not trigger a gas leak, but
*       instead will only transition the fog.
*       Can NOT be stopped after executing.
*       Level must have a fog assigned or will crash!
*       ONLY WORKS FOR LEVEL ID AZTEC
*******************************************************************************/
_AI_CMD_POLYMORPH( , FADE_FOG, (), ())

#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE





//==============================================================================
//## OBJECT ROCKET LAUNCH
//==============================================================================

/* #define CMDNAME OBJECT_ROCKET_LAUNCH */
#define CMDNAME ObjectRocketLaunch

#define DESCRIPTION \
/*******************************************************************************
* Launch a tagged object like a rocket
********************************************************************************
* @exception: If tagged object can NOT be turned upright, object will be destroyed
*       instead.
*       Can be used to drop attached props
*******************************************************************************/

#define PARAMS BYTE(OBJECT_TAG)

_AI_CMD(
    {
        AIRecord1 *   ai  = AiListp + Offset;
        ObjectRecord *obj = objFindByTagId(ai->val);
        if (obj && obj->prop)
        {
            sub_GAME_7F03FDA8(obj->prop);
            if (obj->runtime_bitflags & RUNTIMEBITFLAG_DEPOSIT)
            {
                obj->projectile->flags |= 0x601;
                projectileSetSticky(obj->prop);
                matrix_4x4_set_identity(&obj->projectile->mtx);
                obj->projectile->speed.x = 0;
                obj->projectile->speed.y = 1.0f / CHRLV_FRAMERATE_F;
                obj->projectile->speed.z = 0;
                obj->projectile->unk10.x = 0;
                obj->projectile->unk10.y = 0.29166666f;
                obj->projectile->unk10.z = 0;
            }
        }
        Offset += CAT(CAT(AI_, CMDNAME), _LENGTH);
        break;
    })

//POLYMORPHS

_AI_CMD_POLYMORPH( , PROP_DROP_FROM_ATTACHMENT, (OBJECT_TAG),
                  (OBJECT_TAG),DESCRIPTION)

#undef PARAMS
#undef CMDNAME
#undef DESCRIPTION
#undef CODE

/* this allows syntax highlighter to work even on compile */
 #ifdef _SYNHILITE
;   }    }
#endif
//UNDEF CMDS

#undef _AI_CMD
#undef _AI_DEBUG
#undef _AI_CMD_POLYMORPH
#undef DESCRIPTION
#undef CMDNAME
#undef CODE
#undef PARAMS
#undef _SYNHILITE
