
static const int COBSCALE = 65536;
static const int COBSCALEHALF = COBSCALE / 2;
static const float CORDDIV   = 1.0f / COBSCALE;
static const float RAD2TAANG = COBSCALEHALF / PI;
static const float TAANG2RAD = PI / COBSCALEHALF; 


const int COBFN_Create = 0;
const int COBFN_StartMoving = 1;
const int COBFN_StopMoving = 2;
const int COBFN_Activate = 3;
const int COBFN_Killed = 4;
const int COBFN_Deactivate = 5;
const int COBFN_SetDirection = 6;
const int COBFN_SetSpeed = 7;
const int COBFN_RockUnit = 8;
const int COBFN_HitByWeapon = 9;
const int COBFN_MoveRate0 = 10;
const int COBFN_MoveRate1 = 11;
const int COBFN_MoveRate2 = 12;
const int COBFN_MoveRate3 = 13;
const int COBFN_SetSFXOccupy = 14;
const int COBFN_HitByWeaponId = 15;
const int COBFN_Last = 16;      
    //Make sure to update this, so the array will be sized properly 
// These are special (they need space for MaxWeapons of each)
const int COB_MaxWeapons = 32;
const int COBFN_QueryPrimary   = COBFN_Last;
const int COBFN_AimPrimary     = COBFN_QueryPrimary   + COB_MaxWeapons;
const int COBFN_AimFromPrimary = COBFN_AimPrimary     + COB_MaxWeapons;
const int COBFN_FirePrimary    = COBFN_AimFromPrimary + COB_MaxWeapons;
const int COBFN_EndBurst       = COBFN_FirePrimary    + COB_MaxWeapons;
const int COBFN_Shot           = COBFN_EndBurst       + COB_MaxWeapons;
const int COBFN_BlockShot      = COBFN_Shot           + COB_MaxWeapons;
const int COBFN_TargetWeight   = COBFN_BlockShot      + COB_MaxWeapons;
const int COBFN_Weapon_Funcs   = 8; 




//Command documentation from http://visualta.tauniverse.com/Downloads/cob-commands.txt
//And some information from basm0.8 source (basm ops.txt)

//Model interaction
const int MOVE       = 0x10001000;
const int TURN       = 0x10002000;
const int SPIN       = 0x10003000;
const int STOP_SPIN  = 0x10004000;
const int SHOW       = 0x10005000;
const int HIDE       = 0x10006000;
const int CACHE      = 0x10007000;
const int DONT_CACHE = 0x10008000;
const int MOVE_NOW   = 0x1000B000;
const int TURN_NOW   = 0x1000C000;
const int SHADE      = 0x1000D000;
const int DONT_SHADE = 0x1000E000;
const int EMIT_SFX   = 0x1000F000;

//Blocking operations
const int WAIT_TURN  = 0x10011000;
const int WAIT_MOVE  = 0x10012000;
const int SLEEP      = 0x10013000;

//Stack manipulation
const int PUSH_CONSTANT    = 0x10021001;
const int PUSH_LOCAL_VAR   = 0x10021002;
const int PUSH_STATIC      = 0x10021004;
const int CREATE_LOCAL_VAR = 0x10022000;
const int POP_LOCAL_VAR    = 0x10023002;
const int POP_STATIC       = 0x10023004;
const int POP_STACK        = 0x10024000;        // Not sure what this is supposed to do

//Arithmetic operations
const int ADD         = 0x10031000;
const int SUB         = 0x10032000;
const int MUL         = 0x10033000;
const int DIV         = 0x10034000;
const int MOD         = 0x10034001; // spring specific
const int BITWISE_AND = 0x10035000;
const int BITWISE_OR  = 0x10036000;
const int BITWISE_XOR = 0x10037000;
const int BITWISE_NOT = 0x10038000;

//Native function calls
const int RAND           = 0x10041000;
const int GET_UNIT_VALUE = 0x10042000;
const int GET            = 0x10043000;

//Comparison
const int SET_LESS             = 0x10051000;
const int SET_LESS_OR_EQUAL    = 0x10052000;
const int SET_GREATER          = 0x10053000;
const int SET_GREATER_OR_EQUAL = 0x10054000;
const int SET_EQUAL            = 0x10055000;
const int SET_NOT_EQUAL        = 0x10056000;
const int LOGICAL_AND          = 0x10057000;
const int LOGICAL_OR           = 0x10058000;
const int LOGICAL_XOR          = 0x10059000;
const int LOGICAL_NOT          = 0x1005A000;

//Flow control
const int START           = 0x10061000;
const int CALL            = 0x10062000; // converted when executed
const int REAL_CALL       = 0x10062001; // spring custom
const int LUA_CALL        = 0x10062002; // spring custom
const int JUMP            = 0x10064000;
const int RETURN          = 0x10065000;
const int JUMP_NOT_EQUAL  = 0x10066000;
const int SIGNAL          = 0x10067000;
const int SET_SIGNAL_MASK = 0x10068000;

//Piece destruction
const int EXPLODE    = 0x10071000;
const int PLAY_SOUND = 0x10072000;

//Special functions
const int SET    = 0x10082000;
const int ATTACH = 0x10083000;
const int DROP   = 0x10084000;

// Indices for SET, GET, and GET_UNIT_VALUE for LUA return values
#define LUA0 110 // (LUA0 returns the lua call status, 0 or 1)
#define LUA1 111
#define LUA2 112
#define LUA3 113
#define LUA4 114
#define LUA5 115
#define LUA6 116
#define LUA7 117
#define LUA8 118
#define LUA9 119




// Indices for set/get value
#define ACTIVATION           1  // set or get
#define STANDINGMOVEORDERS   2  // set or get
#define STANDINGFIREORDERS   3  // set or get
#define HEALTH               4  // get (0-100%)
#define INBUILDSTANCE        5  // set or get
#define BUSY                 6  // set or get (used by misc. special case missions like transport ships)
#define PIECE_XZ             7  // get
#define PIECE_Y              8  // get
#define UNIT_XZ              9  // get
#define UNIT_Y              10  // get
#define UNIT_HEIGHT         11  // get
#define XZ_ATAN             12  // get atan of packed x,z coords
#define XZ_HYPOT            13  // get hypot of packed x,z coords
#define ATAN                14  // get ordinary two-parameter atan
#define HYPOT               15  // get ordinary two-parameter hypot
#define GROUND_HEIGHT       16  // get land height, 0 if below water
#define BUILD_PERCENT_LEFT  17  // get 0 = unit is built and ready, 1-100 = How much is left to build
#define YARD_OPEN           18  // set or get (change which plots we occupy when building opens and closes)
#define BUGGER_OFF          19  // set or get (ask other units to clear the area)
#define ARMORED             20  // set or get

/*#define WEAPON_AIM_ABORTED  21
#define WEAPON_READY        22
#define WEAPON_LAUNCH_NOW   23
#define FINISHED_DYING      26
#define ORIENTATION         27*/
#define IN_WATER            28
#define CURRENT_SPEED       29
//#define MAGIC_DEATH         31
#define VETERAN_LEVEL       32
#define ON_ROAD             34

#define MAX_ID                    70
#define MY_ID                     71
#define UNIT_TEAM                 72
#define UNIT_BUILD_PERCENT_LEFT   73
#define UNIT_ALLIED               74
#define MAX_SPEED                 75
#define CLOAKED                   76
#define WANT_CLOAK                77
#define GROUND_WATER_HEIGHT       78 // get land height, negative if below water
#define UPRIGHT                   79 // set or get
#define POW                       80 // get
#define PRINT                     81 // get, so multiple args can be passed
#define HEADING                   82 // get
#define TARGET_ID                 83 // get
#define LAST_ATTACKER_ID          84 // get
#define LOS_RADIUS                85 // set or get
#define AIR_LOS_RADIUS            86 // set or get
#define RADAR_RADIUS              87 // set or get
#define JAMMER_RADIUS             88 // set or get
#define SONAR_RADIUS              89 // set or get
#define SONAR_JAM_RADIUS          90 // set or get
#define SEISMIC_RADIUS            91 // set or get
#define DO_SEISMIC_PING           92 // get
#define CURRENT_FUEL              93 // set or get
#define TRANSPORT_ID              94 // get
#define SHIELD_POWER              95 // set or get
#define STEALTH                   96 // set or get
#define CRASHING                  97 // set or get, returns whether aircraft isCrashing state
#define CHANGE_TARGET             98 // set, the value it's set to determines the affected weapon
#define CEG_DAMAGE                99 // set
#define COB_ID                   100 // get
#define PLAY_SOUND               101 // get, so multiple args can be passed
#define KILL_UNIT                102 // get KILL_UNIT(unitId, SelfDestruct=true, Reclaimed=false)
#define ALPHA_THRESHOLD          103 // set or get
#define SET_WEAPON_UNIT_TARGET   106 // get (fake set)
#define SET_WEAPON_GROUND_TARGET 107 // get (fake set)
#define SONAR_STEALTH            108 // set or get

// NOTE: [LUA0 - LUA9] are defined in CobThread.cpp as [110 - 119]

#define FLANK_B_MODE             120 // set or get
#define FLANK_B_DIR              121 // set or get, set is through get for multiple args
#define FLANK_B_MOBILITY_ADD     122 // set or get
#define FLANK_B_MAX_DAMAGE       123 // set or get
#define FLANK_B_MIN_DAMAGE       124 // set or get
#define WEAPON_RELOADSTATE       125 // get (with fake set)
#define WEAPON_RELOADTIME        126 // get (with fake set)
#define WEAPON_ACCURACY          127 // get (with fake set)
#define WEAPON_SPRAY             128 // get (with fake set)
#define WEAPON_RANGE             129 // get (with fake set)
#define WEAPON_PROJECTILE_SPEED  130 // get (with fake set)
#define MIN                      131 // get
#define MAX                      132 // get
#define ABS                      133 // get
#define GAME_FRAME               134 // get

// NOTE: shared variables use codes [1024 - 5119]





                break;
            case CREATE_LOCAL_VAR:
                if (paramCount == 0) {
                    stack.push_back(0);
                }
                else {
                    paramCount--;
                }
                break;
            case GET_UNIT_VALUE:
                r1 = POP();
                if ((r1 >= LUA0) && (r1 <= LUA9)) {
                    stack.push_back(luaArgs[r1 - LUA0]);
                    break;
                }
                ForceCommitAllAnims();          // getunitval could possibly read piece locations
                r1 = owner->GetUnitVal(r1, 0, 0, 0, 0);
                stack.push_back(r1);
                break;
            case JUMP_NOT_EQUAL:
                r1 = GET_LONG_PC();
                r2 = POP();
                if (r2 == 0) {
                    PC = r1;
                }
                break;
            case JUMP:
                r1 = GET_LONG_PC();
                //this seem to be an error in the docs..
                //r2 = script.scriptOffsets[callStack.back().functionId] + r1;
                PC = r1;
                break;
            case POP_LOCAL_VAR:
                r1 = GET_LONG_PC();
                r2 = POP();
                stack[callStack.back().stackTop + r1] = r2;
                break;
            case PUSH_LOCAL_VAR:
                r1 = GET_LONG_PC();
                r2 = stack[callStack.back().stackTop + r1];
                stack.push_back(r2);
                break;
            case SET_LESS_OR_EQUAL:
                r2 = POP();
                r1 = POP();
                if (r1 <= r2)
                    stack.push_back(1);
                else
                    stack.push_back(0);
                break;
            case BITWISE_AND:
                r1 = POP();
                r2 = POP();
                stack.push_back(r1 & r2);
                break;
            case BITWISE_OR:    //seems to want stack contents or'd, result places on stack
                r1 = POP();
                r2 = POP();
                stack.push_back(r1 | r2);
                break;
            case BITWISE_XOR:
                r1 = POP();
                r2 = POP();
                stack.push_back(r1 ^ r2);
                break;
            case BITWISE_NOT:
                r1 = POP();
                stack.push_back(~r1);
                break;
            case EXPLODE:
                r1 = GET_LONG_PC();
                r2 = POP();
                owner->Explode(r1, r2);
                break;
            case PLAY_SOUND:
                r1 = GET_LONG_PC();
                r2 = POP();
                owner->PlayUnitSound(r1, r2);
                break;
            case PUSH_STATIC:
                r1 = GET_LONG_PC();
                stack.push_back(owner->staticVars[r1]);
                //logOutput.Print("Push static %d val %d", r1, owner->staticVars[r1]);
                break;
            case SET_NOT_EQUAL:
                r1 = POP();
                r2 = POP();
                if (r1 != r2)
                    stack.push_back(1);
                else
                    stack.push_back(0);
                break;
            case SET_EQUAL:
                r1 = POP();
                r2 = POP();
                if (r1 == r2)
                    stack.push_back(1);
                else
                    stack.push_back(0);
                break;
            case SET_LESS:
                r2 = POP();
                r1 = POP();
                if (r1 < r2)
                    stack.push_back(1);
                else
                    stack.push_back(0);
                break;
            case SET_GREATER:
                r2 = POP();
                r1 = POP();
                if (r1 > r2)
                    stack.push_back(1);
                else
                    stack.push_back(0);
                break;
            case SET_GREATER_OR_EQUAL:
                r2 = POP();
                r1 = POP();
                if (r1 >= r2)
                    stack.push_back(1);
                else
                    stack.push_back(0);
                break;
            case RAND:
                r2 = POP();
                r1 = POP();
                r3 = gs->randInt() % (r2 - r1 + 1) + r1;
                stack.push_back(r3);
                break;
            case EMIT_SFX:
                r1 = POP();
                r2 = GET_LONG_PC();
                owner->EmitSfx(r1, r2);
                break;
            case MUL:
                r1 = POP();
                r2 = POP();
                stack.push_back(r1 * r2);
                break;
            case SIGNAL:
                r1 = POP();
                owner->Signal(r1);
                break;
            case SET_SIGNAL_MASK:
                r1 = POP();
                signalMask = r1;
                break;
            case TURN:
                r2 = POP();
                r1 = POP();
                r3 = GET_LONG_PC();
                r4 = GET_LONG_PC();
                //logOutput.Print("Turning piece %s axis %d to %d speed %d", script.pieceNames[r3].c_str(), r4, r2, r1);
                ForceCommitAnim(1, r3, r4);
                owner->Turn(r3, r4, r1, r2);
                break;
            case GET:
                r5 = POP();
                r4 = POP();
                r3 = POP();
                r2 = POP();
                r1 = POP();
                if ((r1 >= LUA0) && (r1 <= LUA9)) {
                    stack.push_back(luaArgs[r1 - LUA0]);
                    break;
                }
                ForceCommitAllAnims();
                r6 = owner->GetUnitVal(r1, r2, r3, r4, r5);
                stack.push_back(r6);
                break;
            case ADD:
                r2 = POP();
                r1 = POP();
                stack.push_back(r1 + r2);
                break;
            case SUB:
                r2 = POP();
                r1 = POP();
                r3 = r1 - r2;
                stack.push_back(r3);
                break;
            case DIV:
                r2 = POP();
                r1 = POP();
                if (r2 != 0)
                    r3 = r1 / r2;
                else {
                    r3 = 1000;  //infinity!
                    logOutput.Print("CobError: division by zero");
                }
                stack.push_back(r3);
                break;
            case MOD:
                r2 = POP();
                r1 = POP();
                if (r2 != 0)
                    stack.push_back(r1 % r2);
                else {
                    stack.push_back(0);
                    logOutput.Print("CobError: modulo division by zero");
                }
                break;
            case MOVE:
                r1 = GET_LONG_PC();
                r2 = GET_LONG_PC();
                r4 = POP();
                r3 = POP();
                ForceCommitAnim(2, r1, r2);
                owner->Move(r1, r2, r3, r4);
                break;
            case MOVE_NOW:{
                r1 = GET_LONG_PC();
                r2 = GET_LONG_PC();
                r3 = POP();

                if (owner->smoothAnim) {
                    DelayedAnim a;
                    a.type = 2;
                    a.piece = r1;
                    a.axis = r2;
                    a.dest = r3;
                    delayedAnims.push_back(a);

                    //logOutput.Print("Delayed move %s %d %d", owner->pieces[r1].name.c_str(), r2, r3);
                }
                else {
                    owner->MoveNow(r1, r2, r3);
                }

                break;}
            case TURN_NOW:{
                r1 = GET_LONG_PC();
                r2 = GET_LONG_PC();
                r3 = POP();

                if (owner->smoothAnim) {
                    DelayedAnim a;
                    a.type = 1;
                    a.piece = r1;
                    a.axis = r2;
                    a.dest = r3;
                    delayedAnims.push_back(a);
                }
                else {
                    owner->TurnNow(r1, r2, r3);
                }

                break;}
            case WAIT_TURN:
                r1 = GET_LONG_PC();
                r2 = GET_LONG_PC();
                //logOutput.Print("Waiting for turn on piece %s around axis %d", script.pieceNames[r1].c_str(), r2);
                if (owner->AddTurnListener(r1, r2, this)) {
                    state = WaitTurn;
                    GCobEngine.SetCurThread(NULL);
                    return 0;
                }
                else
                    break;
            case WAIT_MOVE:
                r1 = GET_LONG_PC();
                r2 = GET_LONG_PC();
                //logOutput.Print("Waiting for move on piece %s on axis %d", script.pieceNames[r1].c_str(), r2);
                if (owner->AddMoveListener(r1, r2, this)) {
                    state = WaitMove;
                    GCobEngine.SetCurThread(NULL);
                    return 0;
                }
                break;
            case SET:
                r2 = POP();
                r1 = POP();
                //logOutput.Print("Setting unit value %d to %d", r1, r2);
                if ((r1 >= LUA0) && (r1 <= LUA9)) {
                    luaArgs[r1 - LUA0] = r2;
                    break;
                }
                owner->SetUnitVal(r1, r2);
                break;
            case ATTACH:
                r3 = POP();
                r2 = POP();
                r1 = POP();
                owner->AttachUnit(r2, r1);
                break;
            case DROP:
                r1 = POP();
                owner->DropUnit(r1);
                break;
            case LOGICAL_NOT:       //Like bitwise, but only on values 1 and 0.
                r1 = POP();
                if (r1 == 0)
                    stack.push_back(1);
                else
                    stack.push_back(0);
                break;
            case LOGICAL_AND:
                r1 = POP();
                r2 = POP();
                if (r1 && r2)
                    stack.push_back(1);
                else
                    stack.push_back(0);
                break;
            case LOGICAL_OR:
                r1 = POP();
                r2 = POP();
                if (r1 || r2)
                    stack.push_back(1);
                else
                    stack.push_back(0);
                break;
            case LOGICAL_XOR:
                r1 = POP();
                r2 = POP();
                if (!!r1 ^ !!r2)
                    stack.push_back(1);
                else
                    stack.push_back(0);
                break;
            case HIDE:
                r1 = GET_LONG_PC();
                owner->SetVisibility(r1, false);
                //logOutput.Print("Hiding %d", r1);
                break;
            case SHOW:{
                r1 = GET_LONG_PC();
                int i;
                for (i = 0; i < COB_MaxWeapons; ++i)
                    if (callStack.back().functionId == script.scriptIndex[COBFN_FirePrimary + i])
                        break;

                // If true, we are in a Fire-script and should show a special flare effect
                if (i < COB_MaxWeapons) {
                    owner->ShowFlare(r1);
                }
                else {
                    owner->SetVisibility(r1, true);
                }
                //logOutput.Print("Showing %d", r1);
                break;}
            default:
                logOutput.Print("CobError: Unknown opcode %x (in %s:%s at %x)", opcode, script.name.c_str(), script.scriptNames[callStack.back().functionId].c_str(), PC - 1);
                logOutput.Print("Exec trace:");
                ei = execTrace.begin();
                while (ei != execTrace.end()) {
                    logOutput.Print("PC: %3x  opcode: %s", *ei, GetOpcodeName(script.code[*ei]).c_str());
                    ei++;
                }
                state = Dead;
                GCobEngine.SetCurThread(NULL);
                return -1;
                break;
        }

    }

    GCobEngine.SetCurThread(NULL);
    return 0;
}







int CCobInstance::GetUnitVal(int val, int p1, int p2, int p3, int p4)
{
#ifndef _CONSOLE
    switch(val)
    {
    case ACTIVATION:
        if (unit->activated)
            return 1;
        else
            return 0;
        break;
    case STANDINGMOVEORDERS:
        return unit->moveState;
        break;
    case STANDINGFIREORDERS:
        return unit->fireState;
        break;
    case HEALTH:{
        if (p1 <= 0)
            return (int) ((unit->health/unit->maxHealth)*100.0f);
        CUnit *u = (p1 < MAX_UNITS) ? uh->units[p1] : NULL;
        if (u == NULL)
            return 0;
        else
            return (int) ((u->health/u->maxHealth)*100.0f);
        }
    case INBUILDSTANCE:
        if (unit->inBuildStance)
            return 1;
        else
            return 0;
    case BUSY:
        if (busy)
            return 1;
        else
            return 0;
        break;
    case PIECE_XZ:{
        if (!unit->localmodel->PieceExists(p1))
            GCobEngine.ShowScriptError("Invalid piecenumber for get piece_xz");
        float3 relPos = unit->localmodel->GetPiecePos(p1);
        float3 pos = unit->pos + unit->frontdir * relPos.z + unit->updir * relPos.y + unit->rightdir * relPos.x;
        return PACKXZ(pos.x, pos.z);}
    case PIECE_Y:{
        if (!unit->localmodel->PieceExists(p1))
            GCobEngine.ShowScriptError("Invalid piecenumber for get piece_y");
        float3 relPos = unit->localmodel->GetPiecePos(p1);
        float3 pos = unit->pos + unit->frontdir * relPos.z + unit->updir * relPos.y + unit->rightdir * relPos.x;
        return (int)(pos.y * COBSCALE);}
    case UNIT_XZ: {
        if (p1 <= 0)
            return PACKXZ(unit->pos.x, unit->pos.z);
        CUnit *u = (p1 < MAX_UNITS) ? uh->units[p1] : NULL;
        if (u == NULL)
            return PACKXZ(0,0);
        else
            return PACKXZ(u->pos.x, u->pos.z);}
    case UNIT_Y: {
        //logOutput.Print("Unit-y %d", p1);
        if (p1 <= 0)
            return (int)(unit->pos.y * COBSCALE);
        CUnit *u = (p1 < MAX_UNITS) ? uh->units[p1] : NULL;
        if (u == NULL)
            return 0;
        else
            return (int)(u->pos.y * COBSCALE);}
    case UNIT_HEIGHT:{
        if (p1 <= 0)
            return (int)(unit->radius * COBSCALE);
        CUnit *u = (p1 < MAX_UNITS) ? uh->units[p1] : NULL;
        if (u == NULL)
            return 0;
        else
            return (int)(u->radius * COBSCALE);}
    case XZ_ATAN:
        return (int)(TAANG2RAD*atan2((float)UNPACKX(p1), (float)UNPACKZ(p1)) + 32768 - unit->heading);
    case XZ_HYPOT:
        return (int)(hypot((float)UNPACKX(p1), (float)UNPACKZ(p1)) * COBSCALE);
    case ATAN:
        return (int)(TAANG2RAD*atan2((float)p1, (float)p2));
    case HYPOT:
        return (int)hypot((float)p1, (float)p2);
    case GROUND_HEIGHT:
        return (int)(ground->GetHeight(UNPACKX(p1), UNPACKZ(p1)) * COBSCALE);
    case GROUND_WATER_HEIGHT:
        return (int)(ground->GetHeight2(UNPACKX(p1), UNPACKZ(p1)) * COBSCALE);
    case BUILD_PERCENT_LEFT:
        return (int)((1 - unit->buildProgress) * 100);
    case YARD_OPEN:
        if (yardOpen)
            return 1;
        else
            return 0;
    case BUGGER_OFF:
        break;
    case ARMORED:
        if (unit->armoredState)
            return 1;
        else
            return 0;
    case VETERAN_LEVEL:
        return (int)(100*unit->experience);
    case CURRENT_SPEED:
        if (unit->moveType)
            return (int)(unit->speed.Length()*COBSCALE);
        return 0;
    case ON_ROAD:
        return 0;
    case IN_WATER:
        return (unit->pos.y < 0.0f) ? 1 : 0;
    case MAX_ID:
        return MAX_UNITS-1;
    case MY_ID:
        return unit->id;
    case UNIT_TEAM:{
        CUnit *u = (p1 >= 0 && p1 < MAX_UNITS) ? uh->units[p1] : NULL;
        return u ? unit->team : 0; }
    case UNIT_ALLIED:{
        CUnit *u = (p1 >= 0 && p1 < MAX_UNITS) ? uh->units[p1] : NULL;
        if (u) return gs->Ally (unit->allyteam, u->allyteam) ? 1 : 0;
        return 0;}
    case UNIT_BUILD_PERCENT_LEFT:{
        CUnit *u = (p1 >= 0 && p1 < MAX_UNITS) ? uh->units[p1] : NULL;
        if (u) return (int)((1 - u->buildProgress) * 100);
        return 0;}
    case MAX_SPEED:
        if(unit->moveType){
            return int(unit->moveType->maxSpeed*COBSCALE);
        }
        break;
    case CLOAKED:
        return !!unit->isCloaked;
    case WANT_CLOAK:
        return !!unit->wantCloak;
    case UPRIGHT:
        return !!unit->upright;
    case POW:
        return int(pow(((float)p1)/COBSCALE,((float)p2)/COBSCALE)*COBSCALE);
    case PRINT:
        logOutput.Print("Value 1: %d, 2: %d, 3: %d, 4: %d", p1, p2, p3, p4);
        break;
    case HEADING: {
        if (p1 <= 0)
            return unit->heading;
        CUnit *u = (p1 < MAX_UNITS) ? uh->units[p1] : NULL;
        if (u == NULL)
            return -1;
        else
            return u->heading;
    }
    case TARGET_ID:
        if (unit->weapons[p1-1]) {
            CWeapon* weapon = unit->weapons[p1-1];
            TargetType tType = weapon->targetType;
            if (tType == Target_Unit)
                return unit->weapons[p1 - 1]->targetUnit->id;
            else if (tType == Target_None)
                return -1;
            else if (tType == Target_Pos)
                return -2;
            else // Target_Intercept
                return -3;
        }
        return -4; // weapon does not exist
    case LAST_ATTACKER_ID:
        return unit->lastAttacker?unit->lastAttacker->id:-1;
    case LOS_RADIUS:
        return unit->realLosRadius;
    case AIR_LOS_RADIUS:
        return unit->realAirLosRadius;
    case RADAR_RADIUS:
        return unit->radarRadius;
    case JAMMER_RADIUS:
        return unit->jammerRadius;
    case SONAR_RADIUS:
        return unit->sonarRadius;
    case SONAR_JAM_RADIUS:
        return unit->sonarJamRadius;
    case SEISMIC_RADIUS:
        return unit->seismicRadius;
    case DO_SEISMIC_PING:
        float pingSize;
        if (p1 == 0) {
            pingSize = unit->seismicSignature;
        } else {
            pingSize = p1;
        }
        unit->DoSeismicPing(pingSize);
        break;
    case CURRENT_FUEL:
        return int(unit->currentFuel * float(COBSCALE));
    case TRANSPORT_ID:
        return unit->transporter?unit->transporter->id:-1;
    case SHIELD_POWER: {
        if (unit->shieldWeapon == NULL) {
            return -1;
        }
        const CPlasmaRepulser* shield = (CPlasmaRepulser*)unit->shieldWeapon;
        return int(shield->curPower * float(COBSCALE));
    }
    case STEALTH: {
        return unit->stealth ? 1 : 0;
    }
    case SONAR_STEALTH: {
        return unit->sonarStealth ? 1 : 0;
    }
    case CRASHING:
        return !!unit->crashing;
    case ALPHA_THRESHOLD: {
        return int(unit->alphaThreshold * 255);
    }
    case COB_ID: {
        if (p1 <= 0) {
            return unit->unitDef->cobID;
        } else {
            const CUnit *u = (p1 < MAX_UNITS) ? uh->units[p1] : NULL;
            return (u == NULL) ? -1 : u->unitDef->cobID;
        }
    }
    case PLAY_SOUND: {
        if ((p1 < 0) || (p1 >= script.sounds.size())) {
            return 1;
        }
        switch (p3) {   //who hears the sound
            case 0:      //ALOS
                if (!loshandler->InAirLos(unit->pos,gu->myAllyTeam)) { return 0; }
                break;
            case 1:     //LOS
                if (!(unit->losStatus[gu->myAllyTeam] & LOS_INLOS)) { return 0; }
                break;
            case 2:     //ALOS or radar
                if (!(loshandler->InAirLos(unit->pos,gu->myAllyTeam) || unit->losStatus[gu->myAllyTeam] & (LOS_INRADAR))) { return 0; }
                break;
            case 3:     //LOS or radar
                if (!(unit->losStatus[gu->myAllyTeam] & (LOS_INLOS | LOS_INRADAR))) { return 0; }
                break;
            case 4:     //everyone
                break;
            case 5:     //allies
                if (unit->allyteam != gu->myAllyTeam) { return 0; }
                break;
            case 6:     //team
                if (unit->team != gu->myTeam) { return 0; }
                break;
            case 7:     //enemies
                if (unit->allyteam == gu->myAllyTeam) { return 0; }
                break;
        }
        if (p4 == 0) {
            sound->PlaySample(script.sounds[p1], unit->pos, float(p2) / COBSCALE);
        } else {
            sound->PlaySample(script.sounds[p1], float(p2) / COBSCALE);
        }
        return 0;
    }
    case SET_WEAPON_UNIT_TARGET: {
        const int weaponID = p1 - 1;
        const int targetID = p2;
        const bool userTarget = !!p3;
        if ((weaponID < 0) || (weaponID >= unit->weapons.size())) {
            return 0;
        }
        CWeapon* weapon = unit->weapons[weaponID];
        if (weapon == NULL) { return 0; }
        if ((targetID < 0) || (targetID >= MAX_UNITS)) { return 0; }
        CUnit* target = (targetID == 0) ? NULL : uh->units[targetID];
        return weapon->AttackUnit(target, userTarget) ? 1 : 0;
    }
    case SET_WEAPON_GROUND_TARGET: {
        const int weaponID = p1 - 1;
        const float3 pos = float3(float(UNPACKX(p2)),
                                  float(p3) / float(COBSCALE),
                                  float(UNPACKZ(p2)));
        const bool userTarget = !!p4;
        if ((weaponID < 0) || (weaponID >= unit->weapons.size())) {
            return 0;
        }
        CWeapon* weapon = unit->weapons[weaponID];
        if (weapon == NULL) { return 0; }

        return weapon->AttackGround(pos, userTarget) ? 1 : 0;
    }
    case MIN:
        return std::min(p1, p2);
    case MAX:
        return std::max(p1, p2);
    case ABS:
        return abs(p1);
    case FLANK_B_MODE:
        return unit->flankingBonusMode;
    case FLANK_B_DIR:
        switch(p1){
            case 1: return int(unit->flankingBonusDir.x * COBSCALE);
            case 2: return int(unit->flankingBonusDir.y * COBSCALE);
            case 3: return int(unit->flankingBonusDir.z * COBSCALE);
            case 4: unit->flankingBonusDir.x = (p2/(float)COBSCALE); return 0;
            case 5: unit->flankingBonusDir.y = (p2/(float)COBSCALE); return 0;
            case 6: unit->flankingBonusDir.z = (p2/(float)COBSCALE); return 0;
            case 7: unit->flankingBonusDir = float3(p2/(float)COBSCALE, p3/(float)COBSCALE, p4/(float)COBSCALE).Normalize(); return 0;
            default: return(-1);
        }
    case FLANK_B_MOBILITY_ADD:
        return int(unit->flankingBonusMobilityAdd * COBSCALE);
    case FLANK_B_MAX_DAMAGE:
        return int((unit->flankingBonusAvgDamage + unit->flankingBonusDifDamage) * COBSCALE);
    case FLANK_B_MIN_DAMAGE:
        return int((unit->flankingBonusAvgDamage - unit->flankingBonusDifDamage) * COBSCALE);
    case KILL_UNIT: {
        if (p1 >= 0 && p1 < MAX_UNITS) {
            CUnit *u = p1 ? uh->units[p1] : unit;
            if (!u) {
                return 0;
            }
            if (u->beingBuilt) u->KillUnit(false, true, NULL); // no explosions and no corpse for units under construction
            else u->KillUnit(p2!=0, p3!=0, NULL);
            return 1;
        }
        return 0;
    }
    case WEAPON_RELOADSTATE: {
        if (p1 > 0 && p1 <= unit->weapons.size()) {
            return unit->weapons[p1-1]->reloadStatus;
        }
        else if (p1 < 0 && p1 >= 0 - unit->weapons.size()) {
            int old = unit->weapons[-p1-1]->reloadStatus;
            unit->weapons[-p1-1]->reloadStatus = p2;
            return old;
        }
        else {
            return -1;
        }
    }
    case WEAPON_RELOADTIME: {
        if (p1 > 0 && p1 <= unit->weapons.size()) {
            return unit->weapons[p1-1]->reloadTime;
        }
        else if (p1 < 0 && p1 >= 0 - unit->weapons.size()) {
            int old = unit->weapons[-p1-1]->reloadTime;
            unit->weapons[-p1-1]->reloadTime = p2;
            return old;
        }
        else {
            return -1;
        }
    }
    case WEAPON_ACCURACY: {
        if (p1 > 0 && p1 <= unit->weapons.size()) {
            return int(unit->weapons[p1-1]->accuracy * COBSCALE);
        }
        else if (p1 < 0 && p1 >= 0 - unit->weapons.size()) {
            int old = int(unit->weapons[-p1-1]->accuracy * COBSCALE);
            unit->weapons[-p1-1]->accuracy = float(p2) / COBSCALE;
            return old;
        }
        else {
            return -1;
        }
    }
    case WEAPON_SPRAY: {
        if (p1 > 0 && p1 <= unit->weapons.size()) {
            return int(unit->weapons[p1-1]->sprayAngle * COBSCALE);
        }
        else if (p1 < 0 && p1 >= 0 - unit->weapons.size()) {
            int old = int(unit->weapons[-p1-1]->sprayAngle * COBSCALE);
            unit->weapons[-p1-1]->sprayAngle = float(p2) / COBSCALE;
            return old;
        }
        else {
            return -1;
        }
    }
    case WEAPON_RANGE: {
        if (p1 > 0 && p1 <= unit->weapons.size()) {
            return int(unit->weapons[p1-1]->range * COBSCALE);
        }
        else if (p1 < 0 && p1 >= 0 - unit->weapons.size()) {
            int old = int(unit->weapons[-p1-1]->range * COBSCALE);
            unit->weapons[-p1-1]->range = float(p2) / COBSCALE;
            return old;
        }
        else {
            return -1;
        }
    }
    case WEAPON_PROJECTILE_SPEED: {
        if (p1 > 0 && p1 <= unit->weapons.size()) {
            return int(unit->weapons[p1-1]->projectileSpeed * COBSCALE);
        }
        else if (p1 < 0 && p1 >= 0 - unit->weapons.size()) {
            int old = int(unit->weapons[-p1-1]->projectileSpeed * COBSCALE);
            unit->weapons[-p1-1]->projectileSpeed = float(p2) / COBSCALE;
            return old;
        }
        else {
            return -1;
        }
    }
    case GAME_FRAME: {
        return gs->frameNum;                            
    }
    default:
        if ((val >= GLOBAL_VAR_START) && (val <= GLOBAL_VAR_END)) {
            return globalVars[val - GLOBAL_VAR_START];
        }
        else if ((val >= TEAM_VAR_START) && (val <= TEAM_VAR_END)) {
            return teamVars[unit->team][val - TEAM_VAR_START];
        }
        else if ((val >= ALLY_VAR_START) && (val <= ALLY_VAR_END)) {
            return allyVars[unit->allyteam][val - ALLY_VAR_START];
        }
        else if ((val >= UNIT_VAR_START) && (val <= UNIT_VAR_END)) {
            const int varID = val - UNIT_VAR_START;
            if (p1 == 0) {
                return unitVars[varID];
            }
            else if (p1 > 0) {
                // get the unit var for another unit
                const CUnit *u = (p1 < MAX_UNITS) ? uh->units[p1] : NULL;
                return (u == NULL) ? 0 : u->cob->unitVars[varID];
            }
            else {
                // set the unit var for another unit
                p1 = -p1;
                const CUnit *u = (p1 < MAX_UNITS) ? uh->units[p1] : NULL;
                if (u != NULL) {
                    u->cob->unitVars[varID] = p2;
                    return 1;
                }
                return 0;
            }
        }
        else {
            logOutput.Print("CobError: Unknown get constant %d  (params = %d %d %d %d)",
                            val, p1, p2, p3, p4);
        }
    }
#endif

    return 0;
}

void CCobInstance::SetUnitVal(int val, int param)
{
#ifndef _CONSOLE
    switch(val) {
        case ACTIVATION: {
            if(unit->unitDef->onoffable) {
                Command c;
                c.id = CMD_ONOFF;
                c.params.push_back(param == 0 ? 0 : 1);
                unit->commandAI->GiveCommand(c);
            }
            else {
                if(param == 0) {
                    unit->Deactivate();
                }
                else {
                    unit->Activate();
                }
            }
            break;
        }
        case STANDINGMOVEORDERS: {
            if (param >= 0 && param <= 2) {
                Command c;
                c.id = CMD_MOVE_STATE;
                c.params.push_back(param);
                unit->commandAI->GiveCommand(c);
            }
            break;
        }
        case STANDINGFIREORDERS: {
            if (param >= 0 && param <= 2) {
                Command c;
                c.id = CMD_FIRE_STATE;
                c.params.push_back(param);
                unit->commandAI->GiveCommand(c);
            }
            break;
        }
        case HEALTH: {
            break;
        }
        case INBUILDSTANCE: {
            //logOutput.Print("buildstance %d", param);
            unit->inBuildStance = (param != 0);
            break;
        }
        case BUSY: {
            busy = (param != 0);
            break;
        }
        case PIECE_XZ: {
            break;
        }
        case PIECE_Y: {
            break;
        }
        case UNIT_XZ: {
            break;
        }
        case UNIT_Y: {
            break;
        }
        case UNIT_HEIGHT: {
            break;
        }
        case XZ_ATAN: {
            break;
        }
        case XZ_HYPOT: {
            break;
        }
        case ATAN: {
            break;
        }
        case HYPOT: {
            break;
        }
        case GROUND_HEIGHT: {
            break;
        }
        case GROUND_WATER_HEIGHT: {
            break;
        }
        case BUILD_PERCENT_LEFT: {
            break;
        }
        case YARD_OPEN: {
            if (unit->yardMap != 0x0) {
                // note: if this unit is a factory, engine-controlled
                // OpenYard() and CloseYard() calls can interfere with
                // the yardOpen state (they probably should be removed
                // at some point)
                if (param == 0) {
                    if (groundBlockingObjectMap->CanCloseYard(unit)) {
                        groundBlockingObjectMap->CloseBlockingYard(unit, unit->yardMap);
                        yardOpen = false;
                    }
                } else {
                    groundBlockingObjectMap->OpenBlockingYard(unit, unit->yardMap);
                    yardOpen = true;
                }
            }
            break;
        }
        case BUGGER_OFF: {
            if (param != 0) {
                helper->BuggerOff(unit->pos + unit->frontdir * unit->radius, unit->radius * 1.5f);
            }
            break;
        }
        case ARMORED: {
            if (param) {
                unit->curArmorMultiple = unit->armoredMultiple;
            } else {
                unit->curArmorMultiple = 1;
            }
            unit->armoredState = (param != 0);
            break;
        }
        case VETERAN_LEVEL: {
            unit->experience = param * 0.01f;
            break;
        }
        case MAX_SPEED: {
            if (unit->moveType && param > 0) {
                // find the first CMD_SET_WANTED_MAX_SPEED and modify it if need be
                for (CCommandQueue::iterator it = unit->commandAI->commandQue.begin();
                        it != unit->commandAI->commandQue.end(); ++it) {
                    Command &c = *it;
                    if (c.id == CMD_SET_WANTED_MAX_SPEED && c.params[0] == unit->maxSpeed) {
                        c.params[0] = param/(float)COBSCALE;
                        break;
                    }
                }
                unit->moveType->SetMaxSpeed(param/(float)COBSCALE);
                unit->maxSpeed = param/(float)COBSCALE;
            }
            break;
        }
        case CLOAKED: {
            unit->wantCloak = !!param;
            break;
        }
        case WANT_CLOAK: {
            unit->wantCloak = !!param;
            break;
        }
        case UPRIGHT: {
            unit->upright = !!param;
            break;
        }
        case HEADING: {
            unit->heading = param % COBSCALE;
            unit->SetDirectionFromHeading();
            break;
        }
        case LOS_RADIUS: {
            unit->ChangeLos(param, unit->realAirLosRadius);
            unit->realLosRadius = param;
            break;
        }
        case AIR_LOS_RADIUS: {
            unit->ChangeLos(unit->realLosRadius, param);
            unit->realAirLosRadius = param;
            break;
        }
        case RADAR_RADIUS: {
            unit->ChangeSensorRadius(&unit->radarRadius, param);
            break;
        }
        case JAMMER_RADIUS: {
            unit->ChangeSensorRadius(&unit->jammerRadius, param);
            break;
        }
        case SONAR_RADIUS: {
            unit->ChangeSensorRadius(&unit->sonarRadius, param);
            break;
        }
        case SONAR_JAM_RADIUS: {
            unit->ChangeSensorRadius(&unit->sonarJamRadius, param);
            break;
        }
        case SEISMIC_RADIUS: {
            unit->ChangeSensorRadius(&unit->seismicRadius, param);
            break;
        }
        case CURRENT_FUEL: {
            unit->currentFuel = param / (float) COBSCALE;
            break;
        }
        case SHIELD_POWER: {
            if (unit->shieldWeapon != NULL) {
                CPlasmaRepulser* shield = (CPlasmaRepulser*)unit->shieldWeapon;
                shield->curPower = std::max(0.0f, float(param) / float(COBSCALE));
            }
            break;
        }
        case STEALTH: {
            unit->stealth = !!param;
            break;
        }
        case SONAR_STEALTH: {
            unit->sonarStealth = !!param;
            break;
        }
        case CRASHING: {
            if(dynamic_cast<CAirMoveType*>(unit->moveType)){
                if(!!param){
                    ((CAirMoveType*)unit->moveType)->SetState(AAirMoveType::AIRCRAFT_CRASHING);
                } else {
                    unit->crashing=false;
                    ((CAirMoveType*)unit->moveType)->aircraftState=AAirMoveType::AIRCRAFT_TAKEOFF;
                    ((CAirMoveType*)unit->moveType)->SetState(AAirMoveType::AIRCRAFT_FLYING);
                }
            }
            break;
        }
        case CHANGE_TARGET: {
            unit->weapons[param - 1]->avoidTarget = true;
            break;
        }
        case ALPHA_THRESHOLD: {
            unit->alphaThreshold = float(param) / 255.0f;
            break;
        }
        case CEG_DAMAGE: {
            unit->cegDamage = param;
            break;
        }
        case FLANK_B_MODE:
            unit->flankingBonusMode = param;
            break;
        case FLANK_B_MOBILITY_ADD:
            unit->flankingBonusMobilityAdd = (param / (float)COBSCALE);
            break;
        case FLANK_B_MAX_DAMAGE: {
            float mindamage = unit->flankingBonusAvgDamage - unit->flankingBonusDifDamage;
            unit->flankingBonusAvgDamage = (param / (float)COBSCALE + mindamage)*0.5f;
            unit->flankingBonusDifDamage = (param / (float)COBSCALE - mindamage)*0.5f;
            break;
         }
        case FLANK_B_MIN_DAMAGE: {
            float maxdamage = unit->flankingBonusAvgDamage + unit->flankingBonusDifDamage;
            unit->flankingBonusAvgDamage = (maxdamage + param / (float)COBSCALE)*0.5f;
            unit->flankingBonusDifDamage = (maxdamage - param / (float)COBSCALE)*0.5f;
            break;
        }
        default: {
            if ((val >= GLOBAL_VAR_START) && (val <= GLOBAL_VAR_END)) {
                globalVars[val - GLOBAL_VAR_START] = param;
            }
            else if ((val >= TEAM_VAR_START) && (val <= TEAM_VAR_END)) {
                teamVars[unit->team][val - TEAM_VAR_START] = param;
            }
            else if ((val >= ALLY_VAR_START) && (val <= ALLY_VAR_END)) {
                allyVars[unit->allyteam][val - ALLY_VAR_START] = param;
            }
            else if ((val >= UNIT_VAR_START) && (val <= UNIT_VAR_END)) {
                unitVars[val - UNIT_VAR_START] = param;
            }
            else {
                logOutput.Print("CobError: Unknown set constant %d", val);
            }
        }
    }
#endif
}