// =============================================================
// This squirrel file contains the code to handle special capabilities
//



// **********************************************
// Superior toughness
function prereqSuperTough( creature ) {
  // print( "In prereqSuperTough. Creature=" + creature.getName() + ".\n" );

  // FIXME: this is inefficient. Instead, expose Character class
  // bindings so we can say something like: 
  // creature.getClass() == scourgeGame.characterClass
  return( creature.getLevel() >= 3 &&
          ( creature.isOfRootClass( "Man-at-arms" ) ||
						creature.isOfRootClass( "Cutpurse" ) ) );
}

function actionSuperTough( creature ) {
  causeDefense( 5, _( "Superior toughness" ) );
}




// **********************************************
// Missile defense
function prereqMissileDefense( creature ) {
  return( creature.getLevel() >= 3 &&
          creature.getSkillByName( "RANGED_WEAPON" ) >= 20 );
}

function actionMissileDefense( creature ) {
  //print( "In actionMissileDefense. Creature=" + creature.getName() + ".\n" );

  // FIXME: only if missile damage
  //print( "armor=" + armor + " type=" + typeof( armor ) + "\n" );
  
  // the weapon this player is being attacked with
  weapon <- scourgeGame.getMission().getCurrentWeapon();

  // is it a ranged weapon?
  if( weapon != null ) {
  	//print( "*** weapon=" + weapon.getName() + " ranged=" + weapon.isRanged().tostring() + "\n" );
    if( weapon.isRanged() ) {
      causeDefense( 25, _( "Dodging missiles" ) );
    }
  }
}


// **********************************************
// War-rage
function prereqWarrage( creature ) {
  return( creature.getSkillByName( "MELEE_WEAPON" ) >= 30 ||
  		  creature.getSkillByName( "LARGE_WEAPON" ) >= 30 ||
          creature.getSkillByName( "HAND_TO_HAND_COMBAT" ) >= 30 );
}

function actionWarrage( creature ) {
  // the weapon this player is using
  weapon <- scourgeGame.getMission().getCurrentWeapon();

  // check that the weapon's skill is more than 90%
  skillCheck <- false;
  if( weapon != null ) {
    //print( "*** weapon=" + weapon.getName() + "\n" );
    skillCheck = ( !( weapon.isRanged() ) && 
                   creature.getSkill( weapon.getDamageSkill() ) >= 30 );
  } else {
    //print( "*** hand-to-hand combat.\n" );
    skillCheck = ( creature.getSkillByName( "HAND_TO_HAND_COMBAT" ) >= 30 );
  }

  //print( "*** skillCheck=" + skillCheck.tostring() + "\n" );

  if( skillCheck ) {
    causeDamage( 15, _( "War rage" ) );
  }
}


// **********************************************
// Killer Blow
function prereqKillerblow( creature ) {
	return( creature.isOfRootClass( "Barbarian" ) );
}

function actionKillerblow( creature ) {
	causeDamage( 10, _( "The terrible blow" ) );
}


// **********************************************
// Natural healing
function prereqNaturalHealing( creature ) {
  // print( "In prereqNaturalHealing. Creature=" + creature.getName() + ".\n" );

  // at least 25pts of nature magic
  return( creature.getSkillByName( "NATURE_MAGIC" ) >= 25 );
}

function actionNaturalHealing( creature ) {
  if( !incrementDailyCount( creature, "NaturalHealing.lastDateUsed", 2 ) ) {
    scourgeGame.printMessage( _( "...but already used this capability twice today." ) );
    return;
  }

  // Heal some points
  amount <- ( rand() * 
              ( 5.0 * creature.getLevel().tofloat() ) / 
              RAND_MAX ).tointeger();
  if( creature.getHp() + amount > creature.getMaxHp() ) 
    amount = creature.getMaxHp() - creature.getHp();
  
  if( amount == 1 ) {
    scourgeGame.printMessage( _( "...and heals 1 hit point." ) );
  } else {
    scourgeGame.printMessage( format( _( "...and heals %d hit points." ), amount ) );
  }
  creature.setHp( creature.getHp() + amount );
}


// **********************************************
// Healing Aura
function prereqHealingAura( creature ) {
  return( ( creature.isOfRootClass( "Warrior" ) && creature.getLevel() >= 20 ) ||
					( creature.isOfRootClass( "Scholar" ) && creature.getLevel() >= 20 ) ||
					( creature.isOfRootClass( "Healer" ) && creature.getLevel() >= 10 ) );
}

function actionHealingAura( creature ) {
	n <- creature.getLevel() / 7 + 1;
	i <- 0;	
	hp <- 0;
	healed <- false;
	for( i = 0; i < scourgeGame.getPartySize(); i++ ) {
		hp = scourgeGame.getPartyMember( i ).getHp() + n;
		if( hp > scourgeGame.getPartyMember( i ).getMaxHp() ) 
			hp = scourgeGame.getPartyMember( i ).getMaxHp();
		if( hp != scourgeGame.getPartyMember( i ).getHp() ) healed = true;
		scourgeGame.getPartyMember( i ).setHp( hp );
  }
	if( healed ) 
		scourgeGame.printMessage( format( _( "%s's holy innocence sooths your pains." ), creature.getName() ) );
}


// **********************************************
// Speedy Casting
function prereqSpeedyCast( creature ) {
	return( creature.isCharacter() &&
					creature.getSkillByName( "LIFE_AND_DEATH_MAGIC" ) >= 75 ||
					creature.getSkillByName( "DECEIT_MAGIC" ) >= 75 ||
					creature.getSkillByName( "NATURE_MAGIC" ) >= 75 ||
					creature.getSkillByName( "AWARENESS_MAGIC" ) >= 75 ||
					creature.getSkillByName( "HISTORY_MAGIC" ) >= 75 ||
          creature.getSkillByName( "CONFRONTATION_MAGIC" ) >= 75 );
}

function actionSpeedyCast( creature ) {
	// the action is handled via the waitHandlerSpell function in map.nut.
}


// **********************************************
// Vitality Transfer
function prereqVitality( creature ) {
  return( creature.getSkillByName( "AWARENESS_MAGIC" ) >= 25 &&
          creature.getLevel() >= 5 );
}

function actionVitality( creature ) {

  if( !incrementDailyCount( creature, "VitalityTransfer.lastDateUsed", 3 ) ) {
    scourgeGame.printMessage( _( "...but already used this capability three times today." ) );
    return;
  }

  i <- 0;
  aveHp <- 0.0;
  for( i = 0; i < scourgeGame.getPartySize(); i++ ) {
    aveHp += scourgeGame.getPartyMember( i ).getHp().tofloat();
  }
  aveHp /= scourgeGame.getPartySize().tofloat();
  for( i = 0; i < scourgeGame.getPartySize(); i++ ) {
    scourgeGame.getPartyMember( i ).setHp( aveHp.tointeger() );
  }

  scourgeGame.printMessage( _( "...the divine healing affects all party members." ) );
}


// **********************************************
// Terminal Energy
function prereqTerminal( creature ) {
  return( creature.isOfRootClass( "Magician" ) ||
          creature.isOfRootClass( "Scholar" ) ||
          creature.isOfRootClass( "Healer" ) );
}

function actionTerminal( creature ) {
  if( creature.getHp().tofloat() / creature.getMaxHp().tofloat() < 0.1 ) {
    if( creature.getMp() < creature.getMaxMp() ) {
      n <- ( creature.getMaxMp().tofloat() / 20.0 ).tointeger();
      creature.setMp( creature.getMp() + n );
      scourgeGame.printMessage( format( _( "...nearing death, %s gains %d MP (Terminal Energy)." ), creature.getName(), n ) );
    }
  }
}



// **********************************************
// Mystic Defense
function prereqMystic( creature ) {
  return( creature.getSkillByName( "HISTORY_MAGIC" ) >= 25 );
}

function actionMystic( creature ) {
  causeDefense( 15, _( "Chanelling mystic energies" ) );
}


// **********************************************
// Small Arms Mastery
function prereqSmallArmMastery( creature ) {
  return( creature.isOfRootClass( "Cutpurse" ) );
}

function actionSmallArmMastery( creature ) {
  weapon <- scourgeGame.getMission().getCurrentWeapon();
  
  // is it a ranged weapon?
  if( weapon != null ) {

    isSmall <- weapon.hasTag( "SMALL" );

//    print( "*** weapon=" + weapon.getName() + " is small=" + isSmall.tostring() + "\n" );
    
    if( isSmall ) {
      causeDamage( 10, _( "Small Arms Mastery" ) );
    }
  }
}



// **********************************************
// Neutralize Poison
function prereqNeutPoison( creature ) {
  return( creature.isOfRootClass( "Cutpurse" ) &&
          creature.getLevel() >= 10 );
}

function actionNeutPoison( creature ) {
  if( creature.getStateMod( 6 ) ) {
    scourgeGame.printMessage( _( "This character is not currently poisoned." ) );
    return;
  }

  if( !incrementDailyCount( creature, "NeutPoison.lastDateUsed", 3 ) ) {
    scourgeGame.printMessage( _( "You already used this capability thrice today." ) );
    return;
  }

  creature.setStateMod( 6, false );
}



// **********************************************
// Arcane Stance
function prereqArcaneStance( creature ) {
    return( creature.isOfRootClass( "Scholar" ) );
}

function actionArcaneStance( creature ) {
  causeDefense( 5, format( _( "%s uses an Arcane Stance which" ), creature.getName() ) );
}


// **********************************************
// Bow Mastery
function prereqBowMastery( creature ) {
  return( creature.isOfRootClass( "Scholar" ) &&
          creature.getLevel() >= 10 );
}

function actionBowMastery( creature ) {
  weapon <- scourgeGame.getMission().getCurrentWeapon();

  // is it a ranged weapon?
  if( weapon != null ) {
//    print( "*** weapon=" + weapon.getName() + " is ranged=" + 
//           weapon.isRanged().tostring() + "\n" );  
    if( weapon.isRanged() ) {
      causeDamage( 10, _( "masterful bow handling" ) );
    }
  }
}





// =============================================================
// Utility functions
//

function causeDamage( percent, message ) {
  delta <- ( damage / 100.0 ) * percent;
  damage += delta;
  if( delta.tointeger() > 0 ) {
    scourgeGame.printMessage( 
			format( _( "...%s causes %d bonus damage!" ), message, delta.tointeger() ) );
  }
}

function causeDefense( percent, message ) {
  delta <- ( armor / 100.0 ) * percent;
  armor += delta;
  if( delta.tointeger() > 0 ) {
    scourgeGame.printMessage( 
			format( _( "...%s causes %d armor bonus!" ), message, delta.tointeger() ) );
  }
}

/**
 * Returns true if the daily count for 'key' can be incremented.
 * Returns false if the value of 'key' is already at least 'maxCount'.
 */
function incrementDailyCount( creature, key, maxCount ) {
    /*
    If the code below looks complicated, it's because I want to avoid 
    storing an infinite number of values. So, the scheme looks like this:
        
    creature_name."NaturalHealing.lastDateUsed" -> lastDateUsed
    lastDateUsed -> count
    
    where:
    lastDateUsed is a Date::getShortString() formatted date
    count is an integer in string format
  */

  // How many times have we used this capability today?
  mainKey <- encodeKeyForCreature( creature, key );
  lastDateUsed <- scourgeGame.getValue( mainKey );
  today <- scourgeGame.getDateString();
  todayKey <- encodeKeyForCreature( creature, today );
  if( lastDateUsed == null ) {
//    print( "First use of this capability ever.\n" );

    // and save today's count of 1
    scourgeGame.setValue( mainKey, today )
    scourgeGame.setValue( todayKey, "1" );
  } else {
//    print( "lastDateUsed=" + lastDateUsed + "\n" );
    lastDateUsedKey <- encodeKeyForCreature( creature, lastDateUsed );
    
    // is now a day later?
    if( scourgeGame.isADayLater( lastDateUsed ) ) {
//      print( "Last use was more than a day ago.\n" );
      // if so, erase the count
      scourgeGame.eraseValue( lastDateUsedKey );
      
      // and save today's count of 1
      scourgeGame.setValue( mainKey, today )
      scourgeGame.setValue( todayKey, "1" );
    } else {
//      print( "Last use was less than a day ago.\n" );
      // otherwise get the count
      count <- scourgeGame.getValue( lastDateUsedKey );
      if( count == null ) count = 0;
      else count = count.tointeger();
//      print( "count=" + count + "\n" );
  
      if( count.tointeger() >= maxCount ) {
        // Used it too many times.
//        print( "Reached max count: count=" + count + " max=" + maxCount + ".\n" );
        return false;
      } else {
        // increment the count
        count = count + 1;

        // and save it
        scourgeGame.setValue( lastDateUsedKey, count.tostring() );
      }
    }
  }
  return true;
}

/**
 * Return the "key" encoded for "creature".
 * These methods ensure that keys are unique per creature.
 */
function encodeKeyForCreature( creature, key ) {
  return creature.getName() + "." + key;
}

/**
 * Returns only the key portion of an encoded key as generated by
 * encodeKeyForCreature.
 * These methods ensure that keys are unique per creature.
 */
function decodeKeyForCreature( creature, key ) {
  delim <- key.find( "." );
  if( delim == null ) return key;
  return key.slice( delim );
}

