// Constants
string REFEREE = "referee"; // Tag of Referee Object
string DARTS_BOARD = "DartsBoard"; // Tag of darts board
string WAGER = "wager";   // Amount bet on game
string WAITING_FOR_CONTESTANT =  "waitingForContestant";  // Waiting for second contestant
string CONTESTANT1 = "contestant1";  // Contestant 1 - initiation of the game. Player tag
string CONTESTANT2 = "contestant2";  // Contestant 2 - joins second
string GAME_IN_PROGRESS = "gameInProgress";  // Is game in progress
string PLAYER_TURN = "playerTurn"; // Which player's turn it is
string DART_NUMBER = "dartNumber"; // What dart of 3 is to be thrown
string DART_WEAPON_TAG = "SmallDart"; // Small dart tag
string CONTESTANT1_TARGET_REGION = "CONTESTANT1_TARGET_REGION"; // Aimed region for contestant1
string CONTESTANT2_TARGET_REGION = "CONTESTANT2_TARGET_REGION"; // Aimed region for contestant1
string TARGET_REGION_CENTRE = "TARGET_REGION_CENTRE";  // Aiming for centre
string TARGET_REGION_NUMBER ="TARGET_REGION_NUMBER";   // Aiming for a number
string TARGET_REGION_DOUBLE ="TARGET_REGION_DOUBLE";   // Aiming for a double
string TARGET_REGION_TRIPLE ="TARGET_REGION_TRIPLE";   // Aiming for a triple
string CONTESTANT1_TARGET_NUMBER = "CONTESTANT1_TARGET_NUMBER"; // Aimed number for contestant1 (25=outer bullseye, 50=bullseye)
string CONTESTANT2_TARGET_NUMBER = "CONTESTANT2_TARGET_NUMBER";// Aimed number for contestant1 (25=outer bullseye, 50=bullseye)
int BULLSEYE = 50; // Bullseye number
int RING_25 = 25;  // 25 ring number
string CONTESTANT1_SCORE = "CONTESTANT1_SCORE"; // Score for contestant 1 not including this turn
string CONTESTANT1_SCORE_THIS = "CONTESTANT1_SCORE_THIS"; // Score for contestant 1 including this turn
string CONTESTANT2_SCORE = "CONTESTANT2_SCORE"; // Score for contestant 1 not including this turn
string CONTESTANT2_SCORE_THIS = "CONTESTANT2_SCORE_THIS"; // Score for contestant 1 including this turn
string THROW_WAYPOINT = "NW_DART_THROW"; // Point where NPCs throw darts from
event NPC_THROW_DART_EVENT = EventUserDefined(5001);
event NPC_START_TURN = EventUserDefined(5002);
event NPC_END_TURN = EventUserDefined(5003);
event NPC_WIN = EventUserDefined(5004);
event NPC_LOSE = EventUserDefined(5005);
string DART_ITEM = "wthdt002";

// Get dart board instance
object getDB() {
    WriteTimestampedLogEntry("Tag: "+GetTag(OBJECT_SELF));

    if (FindSubString(GetTag(OBJECT_SELF), DARTS_BOARD) != -1) {
        return OBJECT_SELF;
    }
    else {
        return GetNearestObjectByTag(DARTS_BOARD);
    }
}

// Reset darts variables
void resetVars() {
    WriteTimestampedLogEntry("resetVars was called");
    object db = getDB();
    SetLocalInt(db, WAITING_FOR_CONTESTANT, FALSE);
    SetLocalInt(db, GAME_IN_PROGRESS, FALSE);
    SetLocalInt(db, WAGER, 0);
    SetLocalInt(db, PLAYER_TURN, 1);
    SetLocalInt(db, DART_NUMBER, 1);
    SetLocalString(db, CONTESTANT1_TARGET_REGION, TARGET_REGION_CENTRE);
    SetLocalString(db, CONTESTANT2_TARGET_REGION, TARGET_REGION_CENTRE);
    SetLocalInt(db, CONTESTANT1_TARGET_NUMBER, BULLSEYE);
    SetLocalInt(db, CONTESTANT2_TARGET_NUMBER, BULLSEYE);
    SetLocalInt(db, CONTESTANT1_SCORE, 501);
    SetLocalInt(db, CONTESTANT1_SCORE_THIS, 501);
    SetLocalInt(db, CONTESTANT2_SCORE, 501);
    SetLocalInt(db, CONTESTANT2_SCORE_THIS, 501);
    SetLocalObject(db, CONTESTANT1, db);
    SetLocalObject(db, CONTESTANT2, db);

    WriteTimestampedLogEntry("CONTESTANT1_TARGET_REGION set to "+GetLocalString(db, CONTESTANT1_TARGET_REGION));
}

// Start up a game
void initGame(int wager) {
    WriteTimestampedLogEntry("initGame called");
    resetVars();
    object db = getDB();
    SetLocalInt(db, WAGER, wager);
    WriteTimestampedLogEntry("wager:"+IntToString(wager));
    SetLocalInt(db, WAITING_FOR_CONTESTANT, TRUE);
    SetLocalObject(db, CONTESTANT1, GetPCSpeaker());
    WriteTimestampedLogEntry("CONTESTANT1:"+GetName(GetPCSpeaker()));
}

// Start when player 2 joins
void startGame() {
    WriteTimestampedLogEntry("startGame called");
    object db = getDB();
    SetLocalInt(db, WAITING_FOR_CONTESTANT, FALSE);
    SetLocalInt(db, GAME_IN_PROGRESS, TRUE);
    SetLocalObject(db, CONTESTANT2, GetPCSpeaker());
}

// Start when an NPC joins
void startGameNPC() {
    WriteTimestampedLogEntry("startGameNPC called");
    object db = getDB();
    SetLocalInt(db, WAITING_FOR_CONTESTANT, FALSE);
    SetLocalInt(db, GAME_IN_PROGRESS, TRUE);
    SetLocalObject(db, CONTESTANT2, OBJECT_SELF);
}


// Check to make sure it is this player's turn
int checkPlayerTurn(object player) {
    object db = getDB();
    WriteTimestampedLogEntry("checkPlayerTurn called");
    WriteTimestampedLogEntry(ObjectToString(db));
    WriteTimestampedLogEntry(IntToString(GetLocalInt(db, PLAYER_TURN)));
    WriteTimestampedLogEntry(GetLocalString(db, CONTESTANT1));
    WriteTimestampedLogEntry(IntToString(GetLocalInt(db, WAGER)));
    if (GetLocalInt(db, PLAYER_TURN) == 1) {
        if (GetLocalObject(db,CONTESTANT1) == player) {
            return TRUE;
        }
    }
    if (GetLocalInt(db, PLAYER_TURN) == 2) {
        if (GetLocalObject(db,CONTESTANT2) == player) {
            return TRUE;
        }
    }

    return FALSE;
}

// Set the target that the player is aiming for
void setPlayerTargetRegion(object player, string region) {
    object db = getDB();
    if (player == GetLocalObject(db,CONTESTANT1)) {
        SetLocalString(db, CONTESTANT1_TARGET_REGION, region);
    }
    else if (player == GetLocalObject(db,CONTESTANT2)) {
        SetLocalString(db, CONTESTANT2_TARGET_REGION, region);
    }
}

// Set the target that the player is aiming for
void setPlayerTargetNumber(object player, int number) {
    object db = GetNearestObjectByTag(DARTS_BOARD);
    if (player == GetLocalObject(db,CONTESTANT1)) {
        SetLocalInt(db, CONTESTANT1_TARGET_NUMBER, number);
    }
    else if (player == GetLocalObject(db,CONTESTANT2)) {
        SetLocalInt(db, CONTESTANT2_TARGET_NUMBER, number);
    }
}

// Get the description of the target that the current player is aiming at
string getTargetDescription() {
    WriteTimestampedLogEntry("getTargetDescription called");
    object db = getDB();
    string region = "";
    int number = 0;
    if (GetLocalInt(db, PLAYER_TURN) == 1) {
        region = GetLocalString(db, CONTESTANT1_TARGET_REGION);
        number = GetLocalInt(db, CONTESTANT1_TARGET_NUMBER);
    }
    else {
        region = GetLocalString(db, CONTESTANT2_TARGET_REGION);
        number = GetLocalInt(db, CONTESTANT2_TARGET_NUMBER);
    }

    WriteTimestampedLogEntry("Region" + region);
    WriteTimestampedLogEntry("Number" + IntToString(number));

    string target = "";
    if (number == BULLSEYE) {
        target = "Bullseye";
    }
    else if (number == RING_25) {
        target = "25 Ring";
    }
    else if (region == TARGET_REGION_NUMBER) {
        target = "Number "+IntToString(number);
    }
    else if (region == TARGET_REGION_DOUBLE) {
        target = "Double "+IntToString(number);
    }
    else if (region == TARGET_REGION_TRIPLE) {
        target = "Triple "+IntToString(number);
    }

    return target;
}

// Determine the attack bonus of the given creature
int calcAttackBonus(object player) {
    int dex_bonus = GetAbilityModifier(ABILITY_DEXTERITY, player);

    // determine attack bonus
    float attack_bonus = 0.0;

/*     attack_bonus = attack_bonus + GetLevelByClass(CLASS_TYPE_BARBARIAN, player);
    attack_bonus = attack_bonus + 0.75*GetLevelByClass(CLASS_TYPE_DRUID, player);
    attack_bonus = attack_bonus + GetLevelByClass(CLASS_TYPE_FIGHTER, player);
    attack_bonus = attack_bonus + 0.75*GetLevelByClass(CLASS_TYPE_MONK, player);
    attack_bonus = attack_bonus + GetLevelByClass(CLASS_TYPE_PALADIN, player);
    attack_bonus = attack_bonus + GetLevelByClass(CLASS_TYPE_RANGER, player);
    attack_bonus = attack_bonus + 0.75*GetLevelByClass(CLASS_TYPE_ROGUE, player);
    attack_bonus = attack_bonus + 0.75*GetLevelByClass(CLASS_TYPE_CLERIC, player);
    attack_bonus = attack_bonus + 0.5*GetLevelByClass(CLASS_TYPE_SORCERER, player);
    attack_bonus = attack_bonus + 0.5*GetLevelByClass(CLASS_TYPE_WIZARD, player);
    attack_bonus = attack_bonus + 0.75*GetLevelByClass(CLASS_TYPE_BARD, player); */

    // Halve bonus to scale game to be reasonable
   //int bonus = FloatToInt((IntToFloat(dex_bonus) + attack_bonus)/2);
   
   int bonus = GetBaseAttackBonus(player);

   return bonus;
}

// Determine the best target for an NPC to aim at
// NPCs are always player 2
void setBestTarget() {

    object db = getDB();
    int curr_score = GetLocalInt(db,CONTESTANT2_SCORE_THIS);

    if (curr_score > 60) {
        SetLocalString(db,CONTESTANT2_TARGET_REGION, TARGET_REGION_CENTRE);
        SetLocalInt(db,CONTESTANT2_TARGET_NUMBER, BULLSEYE);
    }
    else if (curr_score == 60) {
        SetLocalString(db,CONTESTANT2_TARGET_REGION, TARGET_REGION_TRIPLE);
        SetLocalInt(db,CONTESTANT2_TARGET_NUMBER, 20);
    }
    else if (curr_score == 57) {
        SetLocalString(db,CONTESTANT2_TARGET_REGION, TARGET_REGION_TRIPLE);
        SetLocalInt(db,CONTESTANT2_TARGET_NUMBER, 19);
    }
    else if (curr_score == 54) {
        SetLocalString(db,CONTESTANT2_TARGET_REGION, TARGET_REGION_TRIPLE);
        SetLocalInt(db,CONTESTANT2_TARGET_NUMBER, 18);
    }
    else if (curr_score == 51) {
        SetLocalString(db,CONTESTANT2_TARGET_REGION, TARGET_REGION_TRIPLE);
        SetLocalInt(db,CONTESTANT2_TARGET_NUMBER, 17);
    }
    if (curr_score >= 50) {
        SetLocalString(db,CONTESTANT2_TARGET_REGION, TARGET_REGION_CENTRE);
        SetLocalInt(db,CONTESTANT2_TARGET_NUMBER, BULLSEYE);
    }
    else if (curr_score == 48) {
        SetLocalString(db,CONTESTANT2_TARGET_REGION, TARGET_REGION_TRIPLE);
        SetLocalInt(db,CONTESTANT2_TARGET_NUMBER, 16);
    }
    else if (curr_score == 45) {
        SetLocalString(db,CONTESTANT2_TARGET_REGION, TARGET_REGION_TRIPLE);
        SetLocalInt(db,CONTESTANT2_TARGET_NUMBER, 15);
    }
    else if (curr_score == 42) {
        SetLocalString(db,CONTESTANT2_TARGET_REGION, TARGET_REGION_TRIPLE);
        SetLocalInt(db,CONTESTANT2_TARGET_NUMBER, 14);
    }
    else if (curr_score >= 40) {
        SetLocalString(db,CONTESTANT2_TARGET_REGION, TARGET_REGION_DOUBLE);
        SetLocalInt(db,CONTESTANT2_TARGET_NUMBER, 20);
    }
    else if (curr_score == 39) {
        SetLocalString(db,CONTESTANT2_TARGET_REGION, TARGET_REGION_TRIPLE);
        SetLocalInt(db,CONTESTANT2_TARGET_NUMBER, 13);
    }
    else if (curr_score == 38) {
        SetLocalString(db,CONTESTANT2_TARGET_REGION, TARGET_REGION_DOUBLE);
        SetLocalInt(db,CONTESTANT2_TARGET_NUMBER, 19);
    }
    else if (curr_score == 36) {
        SetLocalString(db,CONTESTANT2_TARGET_REGION, TARGET_REGION_DOUBLE);
        SetLocalInt(db,CONTESTANT2_TARGET_NUMBER, 18);
    }
    else if (curr_score == 34) {
        SetLocalString(db,CONTESTANT2_TARGET_REGION, TARGET_REGION_DOUBLE);
        SetLocalInt(db,CONTESTANT2_TARGET_NUMBER, 17);
    }
    else if (curr_score == 33) {
        SetLocalString(db,CONTESTANT2_TARGET_REGION, TARGET_REGION_TRIPLE);
        SetLocalInt(db,CONTESTANT2_TARGET_NUMBER, 11);
    }
    else if (curr_score == 32) {
        SetLocalString(db,CONTESTANT2_TARGET_REGION, TARGET_REGION_DOUBLE);
        SetLocalInt(db,CONTESTANT2_TARGET_NUMBER, 16);
    }
    else if (curr_score == 30) {
        SetLocalString(db,CONTESTANT2_TARGET_REGION, TARGET_REGION_DOUBLE);
        SetLocalInt(db,CONTESTANT2_TARGET_NUMBER, 15);
    }
    else if (curr_score == 28) {
        SetLocalString(db,CONTESTANT2_TARGET_REGION, TARGET_REGION_DOUBLE);
        SetLocalInt(db,CONTESTANT2_TARGET_NUMBER, 14);
    }
    else if (curr_score == 27) {
        SetLocalString(db,CONTESTANT2_TARGET_REGION, TARGET_REGION_TRIPLE);
        SetLocalInt(db,CONTESTANT2_TARGET_NUMBER, 9);
    }
    else if (curr_score == 26) {
        SetLocalString(db,CONTESTANT2_TARGET_REGION, TARGET_REGION_DOUBLE);
        SetLocalInt(db,CONTESTANT2_TARGET_NUMBER, 13);
    }
    else if (curr_score == 24) {
        SetLocalString(db,CONTESTANT2_TARGET_REGION, TARGET_REGION_DOUBLE);
        SetLocalInt(db,CONTESTANT2_TARGET_NUMBER, 12);
    }
    else if (curr_score == 22) {
        SetLocalString(db,CONTESTANT2_TARGET_REGION, TARGET_REGION_DOUBLE);
        SetLocalInt(db,CONTESTANT2_TARGET_NUMBER, 11);
    }
    else if (curr_score == 21) {
        SetLocalString(db,CONTESTANT2_TARGET_REGION, TARGET_REGION_TRIPLE);
        SetLocalInt(db,CONTESTANT2_TARGET_NUMBER, 7);
    }
    else if (curr_score > 20) {
        SetLocalString(db,CONTESTANT2_TARGET_REGION, TARGET_REGION_NUMBER);
        SetLocalInt(db,CONTESTANT2_TARGET_NUMBER, 20);
    }
    else if (curr_score <= 20) {
        SetLocalString(db,CONTESTANT2_TARGET_REGION, TARGET_REGION_NUMBER);
        SetLocalInt(db,CONTESTANT2_TARGET_NUMBER, curr_score);
    }
}

// PC wishes to concede the game
void concede(object oPC) {
    object db = getDB();
    object oPlayer1 = GetLocalObject(db,CONTESTANT1);
    object oPlayer2 = GetLocalObject(db,CONTESTANT2);
    string name = "";

    if (!GetIsObjectValid(oPlayer2)) {
        FloatingTextStringOnCreature("I concede", oPC, FALSE);
        resetVars();
    }
    else if (oPC == oPlayer1) {
        if (!GetIsPC(oPlayer2)) {
            SignalEvent(oPlayer2, NPC_WIN);
        }
        else {
            GiveGoldToCreature(oPlayer2, GetLocalInt(db, WAGER) * 2);
        }
        name = GetName(oPlayer1);
        FloatingTextStringOnCreature(name+" has conceded", db, FALSE);
        FloatingTextStringOnCreature("I concede", oPC, FALSE);
        resetVars();
    }
    else if (oPC == oPlayer2) {
        GiveGoldToCreature(oPlayer1, GetLocalInt(db, WAGER) * 2);
        FloatingTextStringOnCreature(name+" has conceded", db, FALSE);
        FloatingTextStringOnCreature("I concede", oPC, FALSE);
        resetVars();
    }
}

//void main () {}