/*
===========================================================================
Copyright (C) 2024 the OpenMoHAA team

This file is part of OpenMoHAA source code.

OpenMoHAA source code is free software; you can redistribute it
and/or modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.

OpenMoHAA source code is distributed in the hope that it will be
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with OpenMoHAA source code; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
===========================================================================
*/

// player_combat.cpp: Player combat system and combat utility functions
//

#include "player.h"
#include "weapturret.h"
#include "weaputils.h"

//Forward
//Back
//TurnRight
//TurnLeft
//Moveleft (strafe)
//Moveright (strafe)
//Moveup (Jump)
//Movedown (Duck)
//Action (Use)
//Sneak (Toggle or Momentary)
//Speed/Walk (Toggle or Momentary)
//Fire Left hand
//Fire Right hand

#define SLOPE_45_MIN         0.7071f
#define SLOPE_45_MAX         0.831f
#define SLOPE_22_MIN         SLOPE_45_MAX
#define SLOPE_22_MAX         0.95f

#define MIN_Z                -999999
#define PUSH_OBJECT_DISTANCE 16.0f

#define ARMS_NAME            "Bip01 Spine2"

static Vector min_box_8x8(-4, -4, -4);
static Vector max_box_8x8(4, 4, 4);
static Vector min4x4(-4, -4, 0);
static Vector max4x4x0(4, 4, 0);
static Vector max4x4x8(4, 4, 8);

qboolean Player::CondTrue(Conditional& condition)
{
    return true;
}

qboolean Player::CondChance(Conditional& condition)
{
    float percent_chance;

    percent_chance = atof(condition.getParm(1));

    return (G_Random() < percent_chance);
}

qboolean Player::CondHealth(Conditional& condition)
{
    return health < atoi(condition.getParm(1));
}

qboolean Player::CondBlocked(Conditional& condition)
{
    int test_moveresult;

    test_moveresult = moveresult;

    if (flags & FL_IMMOBILE) {
        test_moveresult = MOVERESULT_BLOCKED;
    }

    if (condition.numParms()) {
        return test_moveresult >= atoi(condition.getParm(1));
    }

    return test_moveresult >= MOVERESULT_BLOCKED;
}

qboolean Player::CondPain(Conditional& condition)
{
    return pain != 0 && level.time > nextpaintime;
}

qboolean Player::CondOnGround(Conditional& condition)
{
    if (groundentity || client->ps.walking) {
        falling = 0;
        return qtrue;
    } else {
        return qfalse;
    }
}

qboolean Player::CondHasWeapon(Conditional& condition)
{
    return WeaponsOut();
}

qboolean Player::CondNewWeapon(Conditional& condition)
{
    Weapon *weapon;

    weapon = GetNewActiveWeapon();

    if (weapon) {
        return true;
    } else {
        return false;
    }
}

qboolean Player::CondImmediateSwitch(Conditional& condition)
{
    static cvar_t *g_immediateswitch = NULL;

    if (!g_immediateswitch) {
        g_immediateswitch = gi.Cvar_Get("g_immediateswitch", "0", 0);
    }

    return (g_gametype->integer && g_immediateswitch->integer);
}

// Check to see if a weapon has been raised
qboolean Player::CondUseWeapon(Conditional& condition)
{
    const char *weaponName;
    const char *parm;

    weaponhand_t hand;
    Weapon      *weap;

    weap = GetNewActiveWeapon();
    parm = condition.getParm(1);

    if (!str::icmp(parm, "ERROR")) {
        if (weap) {
            warning("Player::CondUseweapon", "%s does not have a valid RAISE_WEAPON state\n", weap->item_name.c_str());
        } else {
            warning("Player::CondUseweapon", "New Active weapon does not exist\n");
        }

        ClearNewActiveWeapon();
        return qtrue;
    }

    hand = WeaponHandNameToNum(parm);

    if (hand == WEAPON_ERROR) {
        return false;
    }

    weaponName = condition.getParm(2);

    if ((weap != NULL) && (GetNewActiveWeaponHand() == hand) && (!Q_stricmp(weap->item_name, weaponName))) {
        return true;
    } else {
        return false;
    }
}

qboolean Player::CondUseWeaponClass(Conditional& condition)
{
    const char *weaponClass;
    const char *parm;

    weaponhand_t hand;
    Weapon      *weap;

    weap = GetNewActiveWeapon();
    parm = condition.getParm(1);

    if (!str::icmp(parm, "ERROR")) {
        if (weap) {
            warning(
                "Player::CondUseweaponclass", "%s does not have a valid RAISE_WEAPON state\n", weap->getName().c_str()
            );
        } else {
            warning("Player::CondUseweaponclass", "New Active weapon does not exist\n");
        }

        ClearNewActiveWeapon();
        return qtrue;
    }

    hand = WeaponHandNameToNum(parm);

    if (hand == WEAPON_ERROR) {
        return false;
    }

    weaponClass = condition.getParm(2);

    if ((weap != NULL) && (weap->isSubclassOf(Weapon)) && (GetNewActiveWeaponHand() == hand)
        && (weap->GetWeaponClass() & G_WeaponClassNameToNum(weaponClass))) {
        return true;
    } else {
        return false;
    }
}

// Checks to see if weapon is active
qboolean Player::CondWeaponActive(Conditional& condition)
{
    const char  *weaponName;
    weaponhand_t hand;

    weaponName = condition.getParm(2);
    hand       = WeaponHandNameToNum(condition.getParm(1));

    if (hand == WEAPON_ERROR) {
        return false;
    }

    Weapon *weapon = GetActiveWeapon(hand);

    return (weapon && !Q_stricmp(weaponName, weapon->item_name));
}

qboolean Player::CondWeaponClassActive(Conditional& condition)
{
    const char  *weaponClass;
    weaponhand_t hand;

    weaponClass = condition.getParm(2);
    hand        = WeaponHandNameToNum(condition.getParm(1));

    if (hand == WEAPON_ERROR) {
        return false;
    }

    Weapon *weapon = GetActiveWeapon(hand);

    return (weapon && G_WeaponClassNameToNum(weaponClass) & weapon->GetWeaponClass());
}

qboolean Player::CondWeaponCurrentFireAnim(Conditional& condition)
{
    weaponhand_t hand      = WeaponHandNameToNum(condition.getParm(1));
    int          iFireAnim = atoi(condition.getParm(2));
    Weapon      *weapon;

    if (hand == WEAPON_ERROR) {
        return false;
    }

    weapon = GetActiveWeapon(hand);

    return weapon && weapon->m_iCurrentFireAnim == iFireAnim;
}

// Checks to see if weapon is active and ready to fire
qboolean Player::CondWeaponReadyToFire(Conditional& condition)
{
    firemode_t   mode       = FIRE_PRIMARY;
    str          weaponName = "None";
    weaponhand_t hand;
    qboolean     ready;

    if (level.playerfrozen || m_bFrozen || (flags & FL_IMMOBILE)) {
        return false;
    }

    hand = WeaponHandNameToNum(condition.getParm(1));

    if (condition.numParms() > 1) {
        weaponName = condition.getParm(2);
    }

    if (hand == WEAPON_ERROR) {
        return false;
    }

    Weapon *weapon = GetActiveWeapon(hand);

    // Weapon there check
    if (!weapon) {
        return false;
    }

    // Name check
    if (condition.numParms() > 1) {
        if (strcmp(weaponName, weapon->item_name)) {
            return false;
        }
    }

    // Ammo check
    ready = weapon->ReadyToFire(mode);
    return (ready);
}

// Checks to see if weapon is active and ready to fire
qboolean Player::CondWeaponClassReadyToFire(Conditional& condition)
{
    firemode_t   mode        = FIRE_PRIMARY;
    str          weaponClass = "None";
    weaponhand_t hand;
    qboolean     ready;
    Weapon      *weapon;

    if (level.playerfrozen || m_bFrozen || (flags & FL_IMMOBILE)) {
        return false;
    }

    hand = WeaponHandNameToNum(condition.getParm(1));

    if (condition.numParms() > 1) {
        weaponClass = condition.getParm(2);
    }

    if (hand == WEAPON_ERROR) {
        return false;
    }

    weapon = GetActiveWeapon(hand);

    // Weapon there check
    if (!weapon) {
        return qfalse;
    }

    // Name check
    if (condition.numParms() > 1) {
        if (!(G_WeaponClassNameToNum(weaponClass) & weapon->GetWeaponClass())) {
            return qfalse;
        }

        if (condition.numParms() > 2) {
            mode = WeaponModeNameToNum(condition.getParm(3));
        }
    }

    // Ammo check
    ready = weapon->ReadyToFire(mode);
    return (ready);
}

qboolean Player::CondUsingVehicle(Conditional& condition)
{
    return (m_pVehicle != NULL);
}

qboolean Player::CondVehicleType(Conditional& condition)
{
    str sType = condition.getParm(1);
    if (m_pVehicle && m_pVehicle->IsSubclassOfVehicle()) {
        return !str::cmp(sType, m_pVehicle->getName());
    } else {
        return !str::cmp(sType, "none");
    }
}

qboolean Player::CondIsPassenger(Conditional& condition)
{
    return m_pVehicle && m_pVehicle->IsSubclassOfVehicle() && m_pVehicle->FindPassengerSlotByEntity(this);
}

qboolean Player::CondIsDriver(Conditional& condition)
{
    return m_pVehicle && m_pVehicle->IsSubclassOfVehicle() && m_pVehicle->FindDriverSlotByEntity(this);
}

qboolean Player::CondUsingTurret(Conditional& condition)
{
    return (m_pTurret != NULL);
}

qboolean Player::CondIsEscaping(Conditional& condition)
{
    return m_jailstate == JAILSTATE_ESCAPE;
}

qboolean Player::CondAbleToDefuse(Conditional& condition)
{
    Weapon *weapon = GetActiveWeapon(WEAPON_MAIN);
    float   maxrange;

    if (!weapon) {
        weapon = GetActiveWeapon(WEAPON_OFFHAND);
    }

    Vector vForward, vRight, vUp;
    AngleVectors(m_vViewAng, vForward, vRight, vUp);

    maxrange = weapon->GetMaxRange();

    return FindDefusableObject(vForward, this, maxrange) ? true : false;
}

qboolean Player::CondCanPlaceLandmine(Conditional& condition)
{
    Weapon *weapon = GetActiveWeapon(WEAPON_MAIN);
    if (!weapon) {
        weapon = GetActiveWeapon(WEAPON_OFFHAND);
    }

    Vector vPos, vForward, vRight, vUp, vBarrel;
    weapon->GetMuzzlePosition(vPos, vBarrel, vForward, vRight, vUp);

    return CanPlaceLandmine(vPos, this);
}

qboolean Player::CondOnLandmine(Conditional& condition)
{
    MeasureLandmineDistances();

    return m_fMineDist <= 1.f;
}

qboolean Player::CondNearLandmine(Conditional& condition)
{
    MeasureLandmineDistances();

    return m_fMineDist < 3.f && m_fMineDist > 1.f;
}

void Player::MeasureLandmineDistances()
{
    Weapon* weapon;
    float previousMineDist;
    float maxrange;
    int i;

    if (m_fMineCheckTime == level.time) {
        return;
    }
    m_fMineCheckTime = level.time;
    previousMineDist = m_fMineDist;
    m_fMineDist = 1000;

    weapon = GetActiveWeapon(WEAPON_MAIN);
    if (!weapon) {
        weapon = GetActiveWeapon(WEAPON_OFFHAND);
    }

    maxrange = 40;
    if (weapon) {
        maxrange = weapon->GetMaxRange();
    }

    for (i = 0; i < globals.max_entities; i++) {
        TriggerLandmine* landmine;
        Entity* ent;
        vec3_t forward;
        Vector delta;
        float radius;

        ent = G_GetEntity(i);
        if (!ent) {
            continue;
        }

        if (!ent->isSubclassOf(TriggerLandmine)) {
            continue;
        }

        landmine = static_cast<TriggerLandmine*>(ent);
        // This could be from an allied player
        if (landmine->IsImmune(this)) {
            continue;
        }

        AngleVectorsLeft(angles, forward, NULL, NULL);
        delta = landmine->origin - origin;
        radius = delta.length();

        if (radius < maxrange) {
            ent->PostEvent(EV_Show, level.frametime);
        }

        if (radius > m_fMineDist) {
            continue;
        }

        if (radius >= 40) {
            float dot;

            delta.normalize();

            dot = DotProduct(delta, forward);
            if (dot > 0 && radius / Square(dot) < m_fMineDist) {
                m_fMineDist = radius / Square(dot);
            }
        } else if (radius < m_fMineDist) {
            m_fMineDist = radius;
        }
    }

    m_fMineDist /= maxrange;

    if (m_fMineDist > 3) {
        StopLoopSound();
        return;
    }

    if (floorf(previousMineDist * 20) != floorf(m_fMineDist * 20)) {
        float pitch;

        pitch = 2.f - log(m_fMineDist + 1.0);

        LoopSound("minedetector_on", -1, -1, -1, pitch);
    }
}

qboolean Player::CondIsAssistingEscape(Conditional& condition)
{
    return m_jailstate == JAILSTATE_ASSIST_ESCAPE;
}

qboolean Player::CondTurretType(Conditional& condition)
{
    str name = condition.getParm(1);

    if (m_pTurret) {
        return m_pTurret->getName() == name;
    } else {
        return name == "none";
    }
}

qboolean Player::CondWeaponReadyToFireNoSound(Conditional& condition)
{
    firemode_t   mode       = FIRE_PRIMARY;
    str          weaponName = "None";
    weaponhand_t hand;
    qboolean     ready;

    if (level.playerfrozen || m_bFrozen || (flags & FL_IMMOBILE)) {
        return false;
    }

    hand = WeaponHandNameToNum(condition.getParm(1));

    if (condition.numParms() > 1) {
        weaponName = condition.getParm(2);
    }

    if (hand == WEAPON_ERROR) {
        return false;
    }

    Weapon *weapon = GetActiveWeapon(hand);

    // Weapon there check
    if (!weapon) {
        return qfalse;
    }

    // Name check
    if (condition.numParms() > 1) {
        if (strcmp(weaponName, weapon->item_name)) {
            return qfalse;
        }
    }

    // Ammo check
    ready = weapon->ReadyToFire(mode, qfalse);
    return (ready);
}

qboolean Player::CondPutAwayMain(Conditional& condition)
{
    Weapon *weapon = GetActiveWeapon(WEAPON_MAIN);

    return weapon && weapon->GetPutaway();
}

// Check to see if any of the active weapons need to be put away
qboolean Player::CondPutAwayOffHand(Conditional& condition)
{
    Weapon *weapon = GetActiveWeapon(WEAPON_OFFHAND);

    return weapon && weapon->GetPutaway();
}

// Checks to see if any weapon is active in the specified hand
qboolean Player::CondAnyWeaponActive(Conditional& condition)
{
    weaponhand_t hand;
    Weapon      *weap;

    hand = WeaponHandNameToNum(condition.getParm(1));

    if (hand == WEAPON_ERROR) {
        return false;
    }

    weap = GetActiveWeapon(hand);
    return (weap != NULL);
}

qboolean Player::CondAttackBlocked(Conditional& condition)
{
    if (attack_blocked) {
        attack_blocked = qfalse;
        return true;
    } else {
        return false;
    }
}

qboolean Player::CondSemiAuto(Conditional& condition)
{
    firemode_t   mode = FIRE_PRIMARY;
    str          handname;
    weaponhand_t hand;

    handname = condition.getParm(1);

    hand = WeaponHandNameToNum(handname);

    if (hand != WEAPON_ERROR) {
        return GetActiveWeapon(hand)->m_bSemiAuto;
    } else {
        return qfalse;
    }
}

qboolean Player::CondMinChargeTime(Conditional& condition)
{
    const char  *handname;
    weaponhand_t hand;
    Weapon      *weap;

    handname = condition.getParm(1);
    hand     = WeaponHandNameToNum(handname);

    if (hand != WEAPON_ERROR) {
        weap = GetActiveWeapon(hand);
        // Fixed in 2.0.
        //  Make sure the active weapon is valid
        if (weap) {
            float charge_time = weap->GetMinChargeTime(FIRE_PRIMARY);
            if (charge_time) {
                if (charge_start_time) {
                    return level.time - charge_start_time >= charge_time;
                } else {
                    return qfalse;
                }
            } else {
                return qtrue;
            }
        }
    }

    return qfalse;
}

qboolean Player::CondMaxChargeTime(Conditional& condition)
{
    const char  *handname;
    weaponhand_t hand;
    Weapon      *weap;

    handname = condition.getParm(1);
    hand     = WeaponHandNameToNum(handname);

    if (hand != WEAPON_ERROR) {
        weap = GetActiveWeapon(hand);
        if (weap) {
            float charge_time = weap->GetMaxChargeTime(FIRE_PRIMARY);
            if (charge_time) {
                if (charge_start_time) {
                    return level.time - charge_start_time >= charge_time;
                } else {
                    return qfalse;
                }
            } else {
                return qtrue;
            }
        }
    }

    return qfalse;
}

qboolean Player::CondBlockDelay(Conditional& condition)
{
    float t = atof(condition.getParm(1));
    return (level.time > (attack_blocked_time + t));
}

qboolean Player::CondMuzzleClear(Conditional& condition)
{
    weaponhand_t hand;

    hand = WeaponHandNameToNum(condition.getParm(1));

    if (hand == WEAPON_ERROR) {
        return false;
    }

    Weapon *weapon = GetActiveWeapon(hand);
    return (weapon && weapon->MuzzleClear());
}

// Checks to see if any weapon is active in the specified hand
qboolean Player::CondWeaponHasAmmo(Conditional& condition)
{
    weaponhand_t hand;
    Weapon      *weap;
    firemode_t   mode = FIRE_PRIMARY;

    hand = WeaponHandNameToNum(condition.getParm(1));

    if (condition.numParms() > 1) {
        mode = WeaponModeNameToNum(condition.getParm(2));
    }

    if (hand == WEAPON_ERROR) {
        return false;
    }

    weap = GetActiveWeapon(hand);

    if (!weap) {
        return false;
    } else {
        return (weap->HasAmmo(mode));
    }
}

qboolean Player::CondWeaponHasAmmoInClip(Conditional& condition)
{
    weaponhand_t hand;
    Weapon      *weap;
    firemode_t   mode = FIRE_PRIMARY;

    hand = WeaponHandNameToNum(condition.getParm(1));

    if (condition.numParms() > 1) {
        mode = WeaponModeNameToNum(condition.getParm(2));
    }

    if (hand == WEAPON_ERROR) {
        return false;
    }

    weap = GetActiveWeapon(hand);

    if (!weap) {
        return false;
    } else {
        return (weap->HasAmmoInClip(mode));
    }
}

qboolean Player::CondReload(Conditional& condition)
{
    Weapon      *weapon;
    weaponhand_t hand = WEAPON_MAIN;

    if (condition.numParms() > 0) {
        hand = WeaponHandNameToNum(condition.getParm(1));
        if (hand == WEAPON_ERROR) {
            return qfalse;
        }
    }

    weapon = GetActiveWeapon(WEAPON_MAIN);

    if (!weapon) {
        return qfalse;
    }

    if (weapon->ShouldReload() && weapon->HasAmmo(FIRE_PRIMARY)) {
        return qtrue;
    }

    return qfalse;
}

qboolean Player::CondWeaponsHolstered(Conditional& condition)
{
    if (holsteredWeapon) {
        return qtrue;
    } else {
        return qfalse;
    }
}

qboolean Player::CondWeaponIsItem(Conditional& condition)
{
    weaponhand_t hand = WeaponHandNameToNum(condition.getParm(1));
    Weapon      *weapon;

    if (hand == WEAPON_ERROR) {
        return false;
    }

    weapon = GetActiveWeapon(hand);

    return weapon && weapon->IsSubclassOfInventoryItem();
}

qboolean Player::CondNewWeaponIsItem(Conditional& condition)
{
    Weapon *weapon = GetNewActiveWeapon();
    return weapon && weapon->IsSubclassOfInventoryItem();
}

qboolean Player::CondTurnLeft(Conditional& condition)
{
    float yaw;

    yaw = SHORT2ANGLE(last_ucmd.angles[YAW] + client->ps.delta_angles[YAW]);

    return (angledist(old_v_angle[YAW] - yaw) < -8.0f);
}

qboolean Player::CondTurnRight(Conditional& condition)
{
    float yaw;

    yaw = SHORT2ANGLE(last_ucmd.angles[YAW] + client->ps.delta_angles[YAW]);

    return (angledist(old_v_angle[YAW] - yaw) > 8.0f);
}

qboolean Player::CondForward(Conditional& condition)
{
    return last_ucmd.forwardmove > 0;
}

qboolean Player::CondBackward(Conditional& condition)
{
    return last_ucmd.forwardmove < 0;
}

qboolean Player::CondStrafeLeft(Conditional& condition)
{
    return last_ucmd.rightmove < 0;
}

qboolean Player::CondStrafeRight(Conditional& condition)
{
    return last_ucmd.rightmove > 0;
}

qboolean Player::CondJump(Conditional& condition)
{
    if (client->ps.pm_flags & PMF_NO_MOVE) {
        return false;
    }

    return last_ucmd.upmove > 0;
}

qboolean Player::CondCrouch(Conditional& condition)
{
    // Added in 2.0
    //  Don't crouch if the player is not moving
    if (client->ps.pm_flags & PMF_NO_MOVE
        && (g_target_game > target_game_e::TG_MOH || g_gametype->integer != GT_SINGLE_PLAYER)) {
        // Added in 2.30
        //  Allow ducking if specified
        if (!m_pGlueMaster || !m_bGlueDuckable) {
            return viewheight != DEFAULT_VIEWHEIGHT;
        }
    }

    return (last_ucmd.upmove) < 0;
}

qboolean Player::CondJumpFlip(Conditional& condition)
{
    return velocity.z < (sv_gravity->value * 0.5f);
}

qboolean Player::CondAnimDoneLegs(Conditional& condition)
{
    return animdone_Legs;
}

qboolean Player::CondAnimDoneTorso(Conditional& condition)
{
    return animdone_Torso;
}

qboolean Player::CondAttackPrimary(Conditional& condition)
{
    Weapon *weapon;

    if (level.playerfrozen || m_bFrozen || (flags & FL_IMMOBILE)) {
        return false;
    }

    if (g_gametype->integer != GT_SINGLE_PLAYER && !m_bAllowFighting) {
        return false;
    }

    if (!(last_ucmd.buttons & BUTTON_ATTACKLEFT)) {
        return false;
    }

    last_attack_button = BUTTON_ATTACKLEFT;

    weapon = GetActiveWeapon(WEAPON_MAIN);
    if (weapon) {
        return true;
    }

    // No ammo
    return false;
}

qboolean Player::CondAttackButtonPrimary(Conditional& condition)
{
    if (level.playerfrozen || m_bFrozen || (flags & FL_IMMOBILE)) {
        return false;
    }

    if (g_gametype->integer != GT_SINGLE_PLAYER && !m_bAllowFighting) {
        return false;
    }

    return (last_ucmd.buttons & BUTTON_ATTACKLEFT);
}

qboolean Player::CondAttackSecondary(Conditional& condition)
{
    Weapon *weapon;

    if (level.playerfrozen || m_bFrozen || (flags & FL_IMMOBILE)) {
        return false;
    }

    if (g_gametype->integer != GT_SINGLE_PLAYER && !m_bAllowFighting) {
        return false;
    }

    if (!(last_ucmd.buttons & BUTTON_ATTACKRIGHT)) {
        return false;
    }

    last_attack_button = BUTTON_ATTACKRIGHT;

    weapon = GetActiveWeapon(WEAPON_MAIN);
    if (weapon) {
        return true;
    }

    // No ammo
    return false;
}

qboolean Player::CondAttackButtonSecondary(Conditional& condition)
{
    if (level.playerfrozen || m_bFrozen || (flags & FL_IMMOBILE)) {
        return false;
    }

    if (g_gametype->integer != GT_SINGLE_PLAYER && !m_bAllowFighting) {
        return false;
    }

    return (last_ucmd.buttons & BUTTON_ATTACKRIGHT);
}

qboolean Player::CondPositionType(Conditional& condition)
{
    int flags = 0;
    str s;

    s = condition.getParm(1);

    if (!s.icmp("crouching")) {
        flags = MPF_POSITION_CROUCHING;
    } else if (!s.icmp("prone")) {
        flags = MPF_POSITION_PRONE;
    } else if (!s.icmp("offground")) {
        flags = MPF_POSITION_OFFGROUND;
    } else {
        flags = MPF_POSITION_STANDING;
    }

    return (m_iMovePosFlags & flags);
}

qboolean Player::CondMovementType(Conditional& condition)
{
    int flags = 0;
    str s;

    s = condition.getParm(1);

    if (!s.icmp("walking")) {
        flags = MPF_MOVEMENT_WALKING;
    } else if (!s.icmp("running")) {
        flags = MPF_MOVEMENT_RUNNING;
    } else if (!s.icmp("falling")) {
        flags = MPF_MOVEMENT_FALLING;
    }

    return (m_iMovePosFlags & flags);
}

qboolean Player::CondRun(Conditional& condition)
{
    return (last_ucmd.buttons & BUTTON_RUN) != 0;
}

qboolean Player::CondUse(Conditional& condition)
{
    return (last_ucmd.buttons & BUTTON_USE) != 0;
}

qboolean Player::CondCanTurn(Conditional& condition)
{
    float    yaw;
    Vector   oldang(v_angle);
    qboolean result;

    yaw = atof(condition.getParm(1));

    v_angle[YAW] = (int)(anglemod(v_angle[YAW] + yaw) / 22.5f) * 22.5f;
    SetViewAngles(v_angle);

    result = CheckMove(vec_zero);

    SetViewAngles(oldang);

    return result;
}

qboolean Player::CondLeftVelocity(Conditional& condition)
{
    if (condition.numParms()) {
        return move_left_vel >= atof(condition.getParm(1));
    } else {
        return move_left_vel > 4.0f;
    }

    return qfalse;
}

qboolean Player::CondRightVelocity(Conditional& condition)
{
    if (condition.numParms()) {
        return move_right_vel >= atof(condition.getParm(1));
    } else {
        return move_right_vel > 4.0f;
    }

    return qfalse;
}

qboolean Player::CondBackwardVelocity(Conditional& condition)
{
    if (condition.numParms()) {
        return move_backward_vel >= atof(condition.getParm(1));
    } else {
        return move_backward_vel > 4.0f;
    }

    return qfalse;
}

qboolean Player::CondForwardVelocity(Conditional& condition)
{
    if (condition.numParms()) {
        return move_forward_vel >= atof(condition.getParm(1));
    } else {
        return move_forward_vel > 4.0f;
    }

    return qfalse;
}

qboolean Player::CondUpVelocity(Conditional& condition)
{
    if (condition.numParms()) {
        return move_up_vel >= atof(condition.getParm(1));
    } else {
        return move_up_vel > 4.0f;
    }

    return qfalse;
}

qboolean Player::CondDownVelocity(Conditional& condition)
{
    if (condition.numParms()) {
        return move_down_vel >= atof(condition.getParm(1));
    } else {
        return move_down_vel > 4.0f;
    }

    return qfalse;
}

qboolean Player::CondHasVelocity(Conditional& condition)
{
    float fSpeed;

    if (condition.numParms()) {
        fSpeed = atof(condition.getParm(1));
    } else {
        fSpeed = 4.0f;
    }

    return (
        (move_forward_vel > fSpeed) || (move_backward_vel > fSpeed) || (move_right_vel > fSpeed)
        || (move_left_vel > fSpeed)
    );
}

qboolean Player::Cond22DegreeSlope(Conditional& condition)
{
    if (client->ps.walking && client->ps.groundPlane && (client->ps.groundTrace.plane.normal[2] < SLOPE_22_MAX)
        && (client->ps.groundTrace.plane.normal[2] >= SLOPE_22_MIN)) {
        return qtrue;
    }

    return qfalse;
}

qboolean Player::Cond45DegreeSlope(Conditional& condition)
{
    if (client->ps.walking && client->ps.groundPlane && (client->ps.groundTrace.plane.normal[2] < SLOPE_45_MAX)
        && (client->ps.groundTrace.plane.normal[2] >= SLOPE_45_MIN)) {
        return qtrue;
    }

    return qfalse;
}

qboolean Player::CondRightLegHigh(Conditional& condition)
{
    float groundyaw;
    float yawdelta;
    int   which;

    groundyaw = (int)vectoyaw(client->ps.groundTrace.plane.normal);
    yawdelta  = anglemod(v_angle.y - groundyaw);
    which     = ((int)yawdelta + 45) / 90;

    return (which == 3);
}

qboolean Player::CondLeftLegHigh(Conditional& condition)
{
    float groundyaw;
    float yawdelta;
    int   which;

    groundyaw = (int)vectoyaw(client->ps.groundTrace.plane.normal);
    yawdelta  = anglemod(v_angle.y - groundyaw);
    which     = ((int)yawdelta + 45) / 90;

    return (which == 1);
}

qboolean Player::CondFacingUpSlope(Conditional& condition)
{
    float groundyaw;
    float yawdelta;
    int   which;

    groundyaw = (int)vectoyaw(client->ps.groundTrace.plane.normal);
    yawdelta  = anglemod(v_angle.y - groundyaw);
    which     = ((int)yawdelta + 45) / 90;

    return (which == 2);
}

qboolean Player::CondFacingDownSlope(Conditional& condition)
{
    float groundyaw;
    float yawdelta;
    int   which;

    groundyaw = (int)vectoyaw(client->ps.groundTrace.plane.normal);
    yawdelta  = anglemod(v_angle.y - groundyaw);
    which     = ((int)yawdelta + 45) / 90;

    return ((which == 0) || (which == 4));
}

qboolean Player::CondFalling(Conditional& condition)
{
    return falling;
}

qboolean Player::CondGroundEntity(Conditional& condition)
{
    return (groundentity != NULL);
}

qboolean Player::CondMediumImpact(Conditional& condition)
{
    return mediumimpact;
}

qboolean Player::CondHardImpact(Conditional& condition)
{
    return hardimpact;
}

qboolean Player::CondCanFall(Conditional& condition)
{
    return canfall;
}

qboolean Player::CondAtDoor(Conditional& condition)
{
    // Check if the player is at a door
    return (atobject && atobject->isSubclassOf(Door));
}

qboolean Player::CondAtUseAnim(Conditional& condition)
{
    // Check if the player is at a useanim
    if (atobject && atobject->isSubclassOf(UseAnim)) {
        return ((UseAnim *)(Entity *)atobject)->canBeUsed(this);
    }

    return false;
}

qboolean Player::CondTouchUseAnim(Conditional& condition)
{
    if (toucheduseanim) {
        return ((UseAnim *)(Entity *)toucheduseanim)->canBeUsed(this);
    }

    return qfalse;
}

qboolean Player::CondUseAnimFinished(Conditional& condition)
{
    return (useanim_numloops <= 0);
}

qboolean Player::CondAtUseObject(Conditional& condition)
{
    // Check if the player is at a useanim
    if (atobject && atobject->isSubclassOf(UseObject)) {
        return ((UseObject *)(Entity *)atobject)->canBeUsed(origin, yaw_forward);
    }

    return false;
}

qboolean Player::CondLoopUseObject(Conditional& condition)
{
    // Check if the player is at a useanim
    if (useitem_in_use && useitem_in_use->isSubclassOf(UseObject)) {
        return ((UseObject *)(Entity *)useitem_in_use)->Loop();
    }

    return false;
}

qboolean Player::CondDead(Conditional& condition)
{
    return (deadflag);
}

qboolean Player::CondKnockDown(Conditional& condition)
{
    if (knockdown) {
        knockdown = false;
        return true;
    } else {
        return false;
    }
}

qboolean Player::CondPainType(Conditional& condition)
{
    if (pain_type == MOD_string_to_int(condition.getParm(1))) {
        return qtrue;
    } else {
        return qfalse;
    }
}

qboolean Player::CondPainDirection(Conditional& condition)
{
    if (pain_dir == Pain_string_to_int(condition.getParm(1))) {
        return qtrue;
    } else {
        return qfalse;
    }
}

qboolean Player::CondPainLocation(Conditional& condition)
{
    str sLocationName;
    int iLocationNum;

    sLocationName = condition.getParm(1);

    if (!sLocationName.icmp("miss")) {
        iLocationNum = HITLOC_MISS;
    } else if (!sLocationName.icmp("general")) {
        iLocationNum = HITLOC_GENERAL;
    } else if (!sLocationName.icmp("head")) {
        iLocationNum = HITLOC_HEAD;
    } else if (!sLocationName.icmp("helmet")) {
        iLocationNum = HITLOC_HELMET;
    } else if (!sLocationName.icmp("neck")) {
        iLocationNum = HITLOC_NECK;
    } else if (!sLocationName.icmp("torso_upper")) {
        iLocationNum = HITLOC_TORSO_UPPER;
    } else if (!sLocationName.icmp("torso_mid")) {
        iLocationNum = HITLOC_TORSO_MID;
    } else if (!sLocationName.icmp("torso_lower")) {
        iLocationNum = HITLOC_TORSO_LOWER;
    } else if (!sLocationName.icmp("pelvis")) {
        iLocationNum = HITLOC_PELVIS;
    } else if (!sLocationName.icmp("r_arm_upper")) {
        iLocationNum = HITLOC_R_ARM_UPPER;
    } else if (!sLocationName.icmp("l_arm_upper")) {
        iLocationNum = HITLOC_L_ARM_UPPER;
    } else if (!sLocationName.icmp("r_leg_upper")) {
        iLocationNum = HITLOC_R_LEG_UPPER;
    } else if (!sLocationName.icmp("l_leg_upper")) {
        iLocationNum = HITLOC_L_LEG_UPPER;
    } else if (!sLocationName.icmp("r_arm_lower")) {
        iLocationNum = HITLOC_R_ARM_LOWER;
    } else if (!sLocationName.icmp("l_arm_lower")) {
        iLocationNum = HITLOC_L_ARM_LOWER;
    } else if (!sLocationName.icmp("r_leg_lower")) {
        iLocationNum = HITLOC_R_LEG_LOWER;
    } else if (!sLocationName.icmp("l_leg_lower")) {
        iLocationNum = HITLOC_L_LEG_LOWER;
    } else if (!sLocationName.icmp("r_hand")) {
        iLocationNum = HITLOC_R_HAND;
    } else if (!sLocationName.icmp("l_hand")) {
        iLocationNum = HITLOC_L_HAND;
    } else if (!sLocationName.icmp("r_foot")) {
        iLocationNum = HITLOC_R_FOOT;
    } else if (!sLocationName.icmp("l_foot")) {
        iLocationNum = HITLOC_L_FOOT;
    } else {
        Com_Printf("CondPainLocation: Unknown player hit location %s\n", sLocationName.c_str());
    }

    return (pain_location == iLocationNum);
}

qboolean Player::CondPainThreshold(Conditional& condition)
{
    float threshold = atof(condition.getParm(1));

    if ((pain >= threshold) && (level.time > nextpaintime)) {
        pain = 0; // zero out accumulation since we are going into a pain anim right now
        return true;
    } else {
        return false;
    }
}

qboolean Player::CondLegsState(Conditional& condition)
{
    if (currentState_Legs) {
        str current = currentState_Legs->getName();
        str compare = condition.getParm(1);

        if (current == compare) {
            return true;
        }
    }

    return false;
}

qboolean Player::CondTorsoState(Conditional& condition)
{
    if (currentState_Torso) {
        str current = currentState_Torso->getName();
        str compare = condition.getParm(1);

        if (current == compare) {
            return true;
        }
    }

    return false;
}

qboolean Player::CondStateName(Conditional& condition)
{
    str part      = condition.getParm(1);
    str statename = condition.getParm(2);

    if (currentState_Legs && !part.icmp("legs")) {
        return (!statename.icmpn(currentState_Legs->getName(), statename.length()));
    } else if (!part.icmp("torso")) {
        return (!statename.icmpn(currentState_Torso->getName(), statename.length()));
    }

    return false;
}

qboolean Player::CondPush(Conditional& condition)
{
    // Check if the player is at a pushobject
    if (atobject && atobject->isSubclassOf(PushObject) && (atobject_dist < (PUSH_OBJECT_DISTANCE + 15.0f))) {
        Vector dir;

        dir = atobject_dir * 8.0f;
        return ((PushObject *)(Entity *)atobject)->canPush(dir);
    }

    return qfalse;
}

qboolean Player::CondPull(Conditional& condition)
{
    // Check if the player is at a pushobject
    if (atobject && atobject->isSubclassOf(PushObject) && (atobject_dist < (PUSH_OBJECT_DISTANCE + 15.0f))) {
        Vector dir;

        dir = atobject_dir * -64.0f;
        return ((PushObject *)(Entity *)atobject)->canPush(dir);
    }

    return qfalse;
}

qboolean Player::CondLadder(Conditional& condition)
{
    trace_t trace;
    Vector  forward;
    Vector  start, end;

    AngleVectors(m_vViewAng, forward, NULL, NULL);

    start = (m_vViewPos - forward * 12.0f);
    end   = (m_vViewPos + forward * 128.0f);

    trace = G_Trace(start, vec_zero, vec_zero, end, this, MASK_LADDER, qfalse, "checkladder");
    if (trace.fraction == 1.0f || !trace.ent || !trace.ent->entity || !trace.ent->entity->isSubclassOf(FuncLadder)) {
        return qfalse;
    }

    return ((FuncLadder *)trace.ent->entity)->CanUseLadder(this);
}

qboolean Player::CondTopOfLadder(Conditional& condition)
{
    if (!m_pLadder) {
        return false;
    }

    if (maxs[2] + origin[2] > m_pLadder->absmax[2]) {
        return true;
    }

    return false;
}

qboolean Player::CondOnLadder(Conditional& condition)
{
    return m_pLadder != NULL;
}

qboolean Player::CondCanClimbUpLadder(Conditional& condition)
{
    trace_t trace;
    Vector  fwd;
    Vector  vec;
    Vector  start, end;

    AngleVectorsLeft(angles, fwd, NULL, NULL);

    start = origin - fwd * 12.0f;
    start[2] += maxs[2] - 8.0f;

    end = start + fwd * 40.0f;

    // check the normal bounding box first and trace to that position
    trace = G_Trace(start, vec_zero, vec_zero, end, this, MASK_LADDER, qtrue, "Player::CondCanClimbUpLadder");
    if ((trace.fraction == 1.0f) || (!trace.ent) || (!trace.ent->entity)
        || (!trace.ent->entity->isSubclassOf(FuncLadder))) {
        return qfalse;
    }

    Vector vEnd = (origin + Vector(0, 0, 16));

    return G_SightTrace(origin, mins, maxs, vEnd, this, NULL, MASK_BEAM, qtrue, "Player::CondCanClimbUpLadder");
}

qboolean Player::CondCanClimbDownLadder(Conditional& condition)
{
    Vector vEnd = origin - Vector(0, 0, 16);

    return G_SightTrace(origin, mins, maxs, vEnd, this, NULL, MASK_BEAM, qtrue, "Player::CondCanClimbDownLadder");
}

qboolean Player::CondCanGetOffLadderTop(Conditional& condition)
{
    Vector  vForward, vStart, vEnd;
    trace_t trace;

    angles.AngleVectorsLeft(&vForward);

    vStart = origin - vForward * 12.0f * 1.005f;
    vStart[2] += maxs[2] - 8.0f;

    vEnd = vStart + vForward * 40.0f;

    trace = G_Trace(vStart, vec_zero, vec_zero, vEnd, this, MASK_LADDER, qtrue, "Player::CondCanGetOffLadderTop 1");

    if (trace.fraction >= 1.0f) {
        vStart = origin;

        vEnd = origin;
        vEnd[2] += 98.0f;

        if (G_SightTrace(vStart, mins, maxs, vEnd, this, NULL, MASK_BEAM, true, "Player::CondCanGetOffLadderTop 2")) {
            vStart = vEnd;
            vEnd   = vStart + yaw_forward * 16.0f;

            return G_SightTrace(
                vStart, mins, maxs, vEnd, this, NULL, MASK_BEAM, true, "Player::CondCanGetOffLadderTop 3"
            );
        }
    }

    return false;
}

qboolean Player::CondCanGetOffLadderBottom(Conditional& condition)
{
    Vector  vStart, vEnd;
    trace_t trace;

    vStart = origin;

    vEnd = origin;
    vEnd[2] -= 40.0f;

    trace = G_Trace(vStart, mins, maxs, vEnd, edict, MASK_BEAM, true, "Player::CondCangetoffladerbottom");

    if (trace.fraction != 1.0f) {
        return (trace.entityNum == ENTITYNUM_WORLD);
    }

    return false;
}

qboolean Player::CondLookingUp(Conditional& condition)
{
    float angle = 0 - atof(condition.getParm(1));

    return angle > m_vViewAng[0];
}

qboolean Player::CondCanStand(Conditional& condition)
{
    Vector  newmins(mins);
    Vector  newmaxs(maxs);
    trace_t trace;

    newmins[2] = MINS_Z;
    newmaxs[2] = MAXS_Z;

    trace = G_Trace(origin, newmins, newmaxs, origin, this, MASK_PLAYERSOLID, true, "checkcanstand");
    if (trace.startsolid) {
        return qfalse;
    }

    return qtrue;
}

qboolean Player::CondSolidForward(Conditional& condition)
{
    // Trace out forward to see if there is a solid ahead
    float  dist = atof(condition.getParm(1));
    Vector end(centroid + yaw_forward * dist);
    Vector vMins(mins.x, mins.y, -8);
    Vector vMaxs(maxs.x, maxs.y, 8);

    trace_t trace = G_Trace(centroid, vMins, vMaxs, end, this, MASK_SOLID, true, "Player::CondSolidforward");

    return (trace.fraction < 0.7f);
}

qboolean Player::CondCheckHeight(Conditional& condition)
{
    str     sHeight = condition.getParm(1);
    float   fHeight;
    Vector  newmaxs;
    trace_t trace;

    if (!sHeight.icmp("stand")) {
        fHeight = 94.0f;
    } else if (!sHeight.icmp("duckrun")) {
        fHeight = 60.0f;
    } else if (!sHeight.icmp("duck")) {
        fHeight = 54.0f;
    } else if (!sHeight.icmp("prone")) {
        fHeight = 20.0f;
    } else {
        fHeight = atoi(sHeight.c_str());
    }

    if (fHeight < 16.0f) {
        fHeight = 16.0f;
    }

    if (maxs[2] >= fHeight) {
        return true;
    } else {
        newmaxs    = maxs;
        newmaxs[2] = fHeight;

        trace = G_Trace(origin, mins, newmaxs, origin, edict, MASK_PLAYERSOLID, true, "Player::CondCheckHeight");

        if (trace.startsolid) {
            return false;
        } else {
            return true;
        }
    }
}

qboolean Player::CondViewInWater(Conditional& condition)
{
    return (gi.pointcontents(m_vViewPos, 0) & MASK_WATER) != 0;
}

qboolean Player::CondDuckedViewInWater(Conditional& condition)
{
    Vector vPos = origin;
    vPos[2] += 48.0f;

    return (gi.pointcontents(vPos, 0) & MASK_WATER) != 0;
}

qboolean Player::CondCheckMovementSpeed(Conditional& condition)
{
    weaponhand_t hand;
    Weapon      *weapon;

    hand = WeaponHandNameToNum(condition.getParm(1));
    if (hand == WEAPON_ERROR) {
        return false;
    }

    weapon = GetActiveWeapon(hand);
    if (!weapon) {
        return false;
    }

    if (weapon->m_fMaxFireMovement == 1.f) {
        return true;
    }

    return (velocity.length() / sv_runspeed->value) <= (weapon->m_fMaxFireMovement * weapon->m_fMovementSpeed + 0.1f);
}

qboolean Player::CondActionAnimDone(Conditional& condition)
{
    // was removed in mohaas 2.0
    return false;
}

qboolean Player::CondAnimDoneVM(Conditional& condition)
{
    return animDoneVM;
}

qboolean Player::CondClientCommand(Conditional& condition)
{
    str command = condition.getParm(1);

    if (!command.icmp(m_lastcommand)) {
        return qtrue;
    } else {
        return qfalse;
    }
}

qboolean Player::CondVMAnim(Conditional& condition)
{
    return condition.getParm(1) == m_sVMcurrent;
}

qboolean Player::CondVariable(Conditional& condition)
{
    // parameters
    str     var_name;
    str     value_operator;
    Player *player = (Player *)this;

    // variables
    int                 cmp_int = 0, var_int = 0;
    float               cmp_float = 0.0f, var_float = 0.0f;
    char               *cmp_str      = NULL;
    char               *var_str      = NULL;
    ScriptVariableList *variableList = NULL;
    ScriptVariable     *variable     = NULL;
    char                _operator[2];
    size_t              i, nLength;
    size_t              indexval = -1;
    int                 founds   = 0;
    qboolean            isString = qfalse, isFloat = qfalse, isInteger = qfalse;

    var_name       = condition.getParm(1);
    value_operator = condition.getParm(2);

    if (!var_name) {
        gi.Printf("Var_CompareValue : the variable was not specified !\n", condition.getName());
        return qfalse;
    } else if (!value_operator) {
        gi.Printf("Var_CompareValue : the value was not specified !\n", condition.getName());
        return qfalse;
    }

    nLength = value_operator.length();

    // Lookup for the operator, until we found one
    for (i = 0; i < nLength; i++) {
        if ((value_operator[i] == '<' && value_operator[i + 1] == '=')
            || (value_operator[i] == '>' && value_operator[i + 1] == '=')
            || (value_operator[i] == '=' && value_operator[i + 1] == '=')
            || (value_operator[i] == '!' && value_operator[i + 1] == '=') || value_operator[i] == '<'
            || value_operator[i] == '>' || value_operator[i] == '&') {
            if (indexval == -1) {
                indexval = i;
            }

            founds++;
        }
    }

    // Fail if we didn't found/found multiples operators
    if (!founds) {
        gi.Printf(
            "Var_CompareValue : unknown/no comparison/relational operator was specified (var_name=\"%s\"|value=\"%s\") "
            "!\n",
            var_name.c_str(),
            value_operator.c_str()
        );
        return qfalse;
    } else if (founds > 1) {
        gi.Printf(
            "Var_CompareValue : more than one operator was specified (var_name='%s'|value='%s') !\n",
            var_name.c_str(),
            value_operator.c_str()
        );
        return qfalse;
    }

    _operator[0] = value_operator[indexval];
    _operator[1] = value_operator[indexval + 1];

    // If this is not a greater/less than operator, then the loop
    // shouldn't encounter a part of the operator
    if ((_operator[0] == '<' && _operator[1] != '=') || (_operator[0] == '>' && _operator[1] != '=')) {
        i = indexval;
    } else {
        i = indexval + 2;
    }

    while ((value_operator[i] == ' ' || value_operator[i] == '\0') && i < nLength) {
        i++;
    }

    indexval = -1;
    founds   = 0;

    // Loop until we find a character after the operator
    for (; i < nLength; i++) {
        if (value_operator[i] != '\0' && value_operator[i] != ' ' && value_operator[i] != _operator[0]
            && value_operator[i] != _operator[1]) {
            if (indexval == -1) {
                indexval = i;
            }

            founds++;
        }
    }

    if (!founds) {
        gi.Printf(
            "Var_CompareValue : no value was specified after the operator ! (var_name=\"%s\") !\n", var_name.c_str()
        );
        return qfalse;
    }

    // Get the variable list from the player

    variableList = this->Vars();

    // Get the variable from the variable list
    variable = variableList->GetVariable(var_name);

    if (variable != NULL) {
        isFloat   = variable->GetType() == VARIABLE_FLOAT;
        isInteger = variable->GetType() == VARIABLE_INTEGER;
        isString  = variable->GetType() == VARIABLE_STRING || variable->GetType() == VARIABLE_CONSTSTRING;

        if (!isFloat && !isString && !isInteger) {
            gi.Printf(
                "Var_CompareValue : invalid type \"%s\" (%d) for variable \"%s\"\n",
                typenames[variable->GetType()],
                variable->GetType(),
                var_name.c_str()
            );
            return qfalse;
        }

        // Retrieve the values from the variable
        if (isFloat) {
            var_float = variable->floatValue();
        } else {
            var_int = variable->intValue();
        }
    }

    cmp_str = (char *)value_operator.c_str() + indexval;

    if (!isString) {
        cmp_int   = atoi(cmp_str);
        cmp_float = (float)atof(cmp_str);
    }

    // If this is a string, compare between the two strings
    if (isString) {
        if (_operator[0] == '=' && _operator[1] == '=') {
            // == (EQUAL TO) operator

            return strcmp(cmp_str, var_str) == 0;
        } else if (_operator[0] == '!' && _operator[1] == '=') {
            // != (NOT EQUAL TO) operator

            return strcmp(cmp_str, var_str) != 0;
        }
    }

    // Now compare between the two values with the right operator and return
    if (_operator[0] == '<') {
        // < (LESS THAN) operator

        if (isFloat) {
            return var_float < cmp_float;
        }

        return var_int < cmp_int;
    } else if (_operator[0] == '>') {
        // > (GREATER THAN) operator

        if (isFloat) {
            return var_float > cmp_float;
        }

        return var_int > cmp_int;
    } else if (_operator[0] == '<' && _operator[1] == '=') {
        // <= (LESS THAN OR EQUAL TO) operator

        if (isFloat) {
            return var_float <= cmp_float;
        }

        return var_int <= cmp_int;
    } else if (_operator[0] == '>' && _operator[1] == '=') {
        // >= (GREATER THAN OR EQUAL TO) operator

        if (isFloat) {
            return var_float >= cmp_float;
        }

        return var_int >= cmp_int;
    } else if (_operator[0] == '!' && _operator[1] == '=') {
        // != (NOT EQUAL TO) operator

        if (isFloat) {
            return var_float != cmp_float;
        }

        return var_int != cmp_int;
    } else if (_operator[0] == '=' && _operator[1] == '=') {
        // == (EQUAL TO) operator

        if (isFloat) {
            return var_float == cmp_float;
        }

        return var_int == cmp_int;
    } else if (_operator[0] == '&') {
        // & (BITWISE AND) operator

        return var_int & cmp_int;
    }

    return qtrue;
}

CLASS_DECLARATION(Class, Conditional, NULL) {
    {NULL, NULL}
};

Condition<Player> Player::m_conditions[] = {
    {"default",                         &Player::CondTrue                    },
    {"CHANCE",                          &Player::CondChance                  },
    {"HEALTH",                          &Player::CondHealth                  },
    {"BLOCKED",                         &Player::CondBlocked                 },
    {"PAIN",                            &Player::CondPain                    },
    {"ONGROUND",                        &Player::CondOnGround                }, // Checks to see if the right attack button is pressed
    {"HAS_WEAPON",                      &Player::CondHasWeapon               },
    {"NEW_WEAPON",                      &Player::CondNewWeapon               },
    {"IMMEDIATE_SWITCH",                &Player::CondImmediateSwitch         },
    {"IS_NEW_WEAPON",                   &Player::CondUseWeapon               },
    {"IS_WEAPON_ACTIVE",                &Player::CondWeaponActive            },
    {"WEAPON_CURRENT_FIRE_ANIM",        &Player::CondWeaponCurrentFireAnim   },
    {"IS_WEAPON_READY_TO_FIRE",         &Player::CondWeaponReadyToFire       },
    {"IS_WEAPON_READY_TO_FIRE_NOSOUND", &Player::CondWeaponReadyToFireNoSound},
    {"PUTAWAYMAIN",                     &Player::CondPutAwayMain             },
    {"PUTAWAYLEFT",                     &Player::CondPutAwayOffHand          },
    {"ANY_WEAPON_ACTIVE",               &Player::CondAnyWeaponActive         },
    {"ATTACK_BLOCKED",                  &Player::CondAttackBlocked           },
    {"IS_WEAPON_SEMIAUTO",              &Player::CondSemiAuto                },
    {"MIN_CHARGE_TIME_MET",             &Player::CondMinChargeTime           },
    {"MAX_CHARGE_TIME_MET",             &Player::CondMaxChargeTime           },
    {"IS_NEW_WEAPONCLASS",              &Player::CondUseWeaponClass          },
    {"IS_WEAPONCLASS_ACTIVE",           &Player::CondWeaponClassActive       },
    {"IS_WEAPONCLASS_READY_TO_FIRE",    &Player::CondWeaponClassReadyToFire  },
    {"IS_USING_VEHICLE",                &Player::CondUsingVehicle            },
    {"VEHICLE_TYPE",                    &Player::CondVehicleType             },
    {"IS_PASSENGER",                    &Player::CondIsPassenger             },
    {"IS_DRIVER",                       &Player::CondIsDriver                },
    {"IS_USING_TURRET",                 &Player::CondUsingTurret             },
    {"TURRET_TYPE",                     &Player::CondTurretType              },
    {"BLOCK_DELAY",                     &Player::CondBlockDelay              },
    {"MUZZLE_CLEAR",                    &Player::CondMuzzleClear             },
    {"HAS_AMMO",                        &Player::CondWeaponHasAmmo           },
    {"HAS_AMMO_IN_CLIP",                &Player::CondWeaponHasAmmoInClip     },
    {"RELOAD",                          &Player::CondReload                  },
    {"WEAPONS_HOLSTERED",               &Player::CondWeaponsHolstered        },
    {"IS_WEAPON_AN_ITEM",               &Player::CondWeaponIsItem            },
    {"NEW_WEAPON_AN_ITEM",              &Player::CondNewWeaponIsItem         },
    {"POSITION_TYPE",                   &Player::CondPositionType            },
    {"MOVEMENT_TYPE",                   &Player::CondMovementType            },
    {"RUN",                             &Player::CondRun                     },
    {"USE",                             &Player::CondUse                     },
    {"LEFT",                            &Player::CondTurnLeft                },
    {"RIGHT",                           &Player::CondTurnRight               },
    {"FORWARD",                         &Player::CondForward                 },
    {"BACKWARD",                        &Player::CondBackward                },
    {"STRAFE_LEFT",                     &Player::CondStrafeLeft              },
    {"STRAFE_RIGHT",                    &Player::CondStrafeRight             },
    {"JUMP",                            &Player::CondJump                    },
    {"CROUCH",                          &Player::CondCrouch                  },
    {"DO_JUMP_FLIP",                    &Player::CondJumpFlip                },
    {"ANIMDONE_LEGS",                   &Player::CondAnimDoneLegs            },
    {"ANIMDONE_TORSO",                  &Player::CondAnimDoneTorso           },
    {"CAN_TURN",                        &Player::CondCanTurn                 },
    {"LEFT_VELOCITY",                   &Player::CondLeftVelocity            },
    {"RIGHT_VELOCITY",                  &Player::CondRightVelocity           },
    {"BACKWARD_VELOCITY",               &Player::CondBackwardVelocity        },
    {"FORWARD_VELOCITY",                &Player::CondForwardVelocity         },
    {"UP_VELOCITY",                     &Player::CondUpVelocity              },
    {"DOWN_VELOCITY",                   &Player::CondDownVelocity            },
    {"HAS_VELOCITY",                    &Player::CondHasVelocity             },
    {"SLOPE_22",                        &Player::Cond22DegreeSlope           },
    {"SLOPE_45",                        &Player::Cond45DegreeSlope           },
    {"LOOKING_UP",                      &Player::CondLookingUp               },
    {"RIGHT_LEG_HIGH",                  &Player::CondRightLegHigh            },
    {"LEFT_LEG_HIGH",                   &Player::CondLeftLegHigh             },
    {"CAN_FALL",                        &Player::CondCanFall                 },
    {"AT_DOOR",                         &Player::CondAtDoor                  },
    {"FALLING",                         &Player::CondFalling                 },
    {"MEDIUM_IMPACT",                   &Player::CondMediumImpact            },
    {"HARD_IMPACT",                     &Player::CondHardImpact              },
    {"KILLED",                          &Player::CondDead                    },
    {"PAIN_TYPE",                       &Player::CondPainType                },
    {"PAIN_DIRECTION",                  &Player::CondPainDirection           },
    {"PAIN_LOCATION",                   &Player::CondPainLocation            },
    {"PAIN_THRESHOLD",                  &Player::CondPainThreshold           },
    {"KNOCKDOWN",                       &Player::CondKnockDown               },
    {"LEGS",                            &Player::CondLegsState               },
    {"TORSO",                           &Player::CondTorsoState              },
    {"AT_USEANIM",                      &Player::CondAtUseAnim               },
    {"TOUCHEDUSEANIM",                  &Player::CondTouchUseAnim            },
    {"FINISHEDUSEANIM",                 &Player::CondUseAnimFinished         },
    {"AT_USEOBJECT",                    &Player::CondAtUseObject             },
    {"LOOP_USEOBJECT",                  &Player::CondLoopUseObject           },
    {"CAN_PUSH",                        &Player::CondPush                    },
    {"CAN_PULL",                        &Player::CondPull                    },
    {"AT_LADDER",                       &Player::CondLadder                  },
    {"AT_TOP_OF_LADDER",                &Player::CondTopOfLadder             },
    {"ON_LADDER",                       &Player::CondOnLadder                },
    {"CAN_CLIMB_UP_LADDER",             &Player::CondCanClimbUpLadder        },
    {"CAN_CLIMB_DOWN_LADDER",           &Player::CondCanClimbDownLadder      },
    {"CAN_GET_OFF_LADDER_TOP",          &Player::CondCanGetOffLadderTop      },
    {"CAN_GET_OFF_LADDER_BOTTOM",       &Player::CondCanGetOffLadderBottom   },
    {"CAN_STAND",                       &Player::CondCanStand                },
    {"FACING_UP_SLOPE",                 &Player::CondFacingUpSlope           },
    {"FACING_DOWN_SLOPE",               &Player::CondFacingDownSlope         },
    {"SOLID_FORWARD",                   &Player::CondSolidForward            },
    {"STATE_ACTIVE",                    &Player::CondStateName               },
    {"GROUNDENTITY",                    &Player::CondGroundEntity            },
    {"CHECK_HEIGHT",                    &Player::CondCheckHeight             },
    {"VIEW_IN_WATER",                   &Player::CondViewInWater             },
    {"DUCKED_VIEW_IN_WATER",            &Player::CondDuckedViewInWater       },
    {"IS_ESCAPING",                     &Player::CondIsEscaping              },
    {"IS_ASSISTING_ESCAPE",             &Player::CondIsAssistingEscape       },
    {"NEAR_LANDMINE",                   &Player::CondNearLandmine            },
    {"ON_LANDMINE",                     &Player::CondOnLandmine              },
    {"ABLE_TO_DEFUSE",                  &Player::CondAbleToDefuse            },
    {"CAN_PLACE_LANDMINE",              &Player::CondCanPlaceLandmine        },
    {"IS_USING_TURRET",                 &Player::CondUsingTurret             },
    {"ATTACK_PRIMARY",                  &Player::CondAttackPrimary
    }, // Checks to see if there is an active weapon as well as the button being pressed
    {"ATTACK_SECONDARY",                &Player::CondAttackSecondary
    }, // Checks to see if there is an active weapon as well as the button being pressed
    {"ATTACK_PRIMARY_BUTTON",           &Player::CondAttackButtonPrimary     }, // Checks to see if the left attack button is pressed
    {"ATTACK_SECONDARY_BUTTON",         &Player::CondAttackButtonSecondary   },
    {"CHECK_MOVEMENT_SPEED",            &Player::CondCheckMovementSpeed      },

    // Weapon conditions
    {"ANIMDONE_VM",                     &Player::CondAnimDoneVM              },
    {"CLIENT_COMMAND",                  &Player::CondClientCommand           },
    {"IS_VM_ANIM",                      &Player::CondVMAnim                  },
    {"VAR_OPERATOR",                    &Player::CondVariable                },
    {NULL,                              NULL                                 },
};
