package BonusHandler
// By muzzel - Version 1.3 - Credits: Crigges, Earth-Fury
import BonusHandlerConfig
import AbilityObjEditing
import ObjectIds

int array powersOf2
int array rawShifts
int rawShiftNeg
BonusType array bonusTypes

public enum Bonus
	LIFE
	MANA
	DAMAGE
	ARMOR
	SIGHTRANGE
	LIFEREGEN
	MANAREGEN
	ATTACKSPEED
	STRENGTH
	AGILITY
	INTELLIGENCE

/** Sets the bonus of specified type for the unit to the given amount. */
public function setUnitBonus(unit u, Bonus bonusType, int amount)
	bonusTypes[bonusType castTo int].setBonus(u, amount)

/** Sets the bonus of specified type for the unit to zero. */
public function clearUnitBonus(unit u, Bonus bonusType)
	bonusTypes[bonusType castTo int].clearBonus(u)

/** Gets the highest applicable value for the specified bonus type. */
public function getMaxBonus(Bonus bonusType) returns int
	return powersOf2[bonusTypes[bonusType castTo int].maxBonus - 1]

/** Gets the smallest applicable value for the specified bonus type. */
public function getMinBonus(Bonus bonusType) returns int
	return -powersOf2[bonusTypes[bonusType castTo int].maxBonus]
	
/** Removes all bonuses from the specified unit. */
public function clearAllUnitBonuses(unit u)
	bonusTypes[Bonus.LIFE castTo int].clearBonus(u)
	bonusTypes[Bonus.MANA castTo int].clearBonus(u)
	bonusTypes[Bonus.DAMAGE castTo int].clearBonus(u)
	bonusTypes[Bonus.ARMOR castTo int].clearBonus(u)
	bonusTypes[Bonus.SIGHTRANGE castTo int].clearBonus(u)
	bonusTypes[Bonus.LIFEREGEN castTo int].clearBonus(u)
	bonusTypes[Bonus.MANAREGEN castTo int].clearBonus(u)
	bonusTypes[Bonus.ATTACKSPEED castTo int].clearBonus(u)
	bonusTypes[Bonus.STRENGTH castTo int].clearBonus(u)
	bonusTypes[Bonus.AGILITY castTo int].clearBonus(u)
	bonusTypes[Bonus.INTELLIGENCE castTo int].clearBonus(u)
	

/** Enumeration of the bonustypes. */
class BonusType
	int baseRaw
	int maxBonus
	
	construct(int baseRaw, int maxBonus)
		this.baseRaw = baseRaw
		this.maxBonus = maxBonus
	
	function setBonus(unit u, int bonus)
		if bonus >= powersOf2[maxBonus]
			printWarning("Bonus exceeds maximum")
			return
		if bonus < -powersOf2[maxBonus]
			printWarning("Bonus exceeds minimum")
			return
		
		int val
		var raw = baseRaw + rawShiftNeg
		if bonus < 0
			u.addAbility(raw)
			UnitMakeAbilityPermanent(u, true, raw)
			val = bonus + powersOf2[maxBonus]
		else
			val = bonus
			UnitMakeAbilityPermanent(u, false, raw)
			u.removeAbility(raw)
			
		for n = maxBonus-1 downto 0
			raw = baseRaw + rawShifts[n]
			if val >= powersOf2[n]
				u.addAbility(raw)
				UnitMakeAbilityPermanent(u, true, raw)
				val -= powersOf2[n]
			else
				UnitMakeAbilityPermanent(u, false, raw)
				u.removeAbility(raw)
				
	function clearBonus(unit u)
		var raw = baseRaw + rawShiftNeg
		UnitMakeAbilityPermanent(u, false, raw)
		u.removeAbility(raw)
		for n = 0 to maxBonus-1
			raw = baseRaw + rawShifts[n]
			UnitMakeAbilityPermanent(u, false, raw)
			u.removeAbility(raw)
		
	
/** Example: 'XY00' + shiftRaw(15) -> 'XY15' */
function shiftRaw(int n) returns int
	if n < 10
		return n
	else
		return (n div 10) * 256 + n.moduloInt(10)
	

/** Example: pow2(x) -> 2^x */
function pow2(int e) returns int
	if e == 0
		return 1
	return 2 * pow2(e-1)

function setProperties(AbilityDefinition a, string suffix, string bonusName)
	a.setItemAbility(false)
	a.setEditorSuffix("(" + suffix + ")")
	a.setIconNormal("ReplaceableTextures\\CommandButtons\\BTNPenguin.blp")
	a.setName("BonusHandler - " + bonusName)


init
	for n = 0 to 30 // if u need bonuses higher than 1 billion u should rethink your map design...
		powersOf2[n] = Pow(2, n.toReal()).toInt()
	for n = 0 to 30
		rawShifts[n] = shiftRaw(n)
	rawShiftNeg = '00--' - '0000'
	
	bonusTypes[Bonus.LIFE castTo int] = new BonusType('$L00', MAX_BONUS_LIFE)
	bonusTypes[Bonus.MANA castTo int] = new BonusType('$M00', MAX_BONUS_MANA)
	bonusTypes[Bonus.DAMAGE castTo int] = new BonusType('$D00', MAX_BONUS_DAMAGE)
	bonusTypes[Bonus.ARMOR castTo int] = new BonusType('$R00', MAX_BONUS_ARMOR)
	bonusTypes[Bonus.SIGHTRANGE castTo int] = new BonusType('$V00', MAX_BONUS_SIGHTRANGE)
	bonusTypes[Bonus.LIFEREGEN castTo int] = new BonusType('$l00', MAX_BONUS_LIFEREGEN)
	bonusTypes[Bonus.MANAREGEN castTo int] = new BonusType('$m00', MAX_BONUS_MANAREGEN)
	bonusTypes[Bonus.ATTACKSPEED castTo int] = new BonusType('$P00', MAX_BONUS_ATTACKSPEED)
	bonusTypes[Bonus.STRENGTH castTo int] = new BonusType('$S00', MAX_BONUS_STRENGTH)
	bonusTypes[Bonus.AGILITY castTo int] = new BonusType('$A00', MAX_BONUS_AGILITY)
	bonusTypes[Bonus.INTELLIGENCE castTo int] = new BonusType('$I00', MAX_BONUS_INTELLIGENCE)
	

@compiletime function genAbilities()
	string rawCode
	// Life:
	rawCode = "$L"
	for n = 0 to MAX_BONUS_LIFE
		string raw
		int sign = 1
		if n < MAX_BONUS_LIFE
			raw = rawCode
			if n < 10
				raw = raw + "0"
			raw = raw + I2S(n)
		else
			raw = rawCode + "--"
			sign = -1
		let a = new AbilityDefinitionMaxLifeBonusLeast(idString2IdInteger(raw))
		a.setMaxLifeGained(1, sign * pow2(n))
		setProperties(a, I2S(sign * pow2(n)), "Life")
		
	// Mana:
	rawCode = "$M"
	for n = 0 to MAX_BONUS_MANA
		string raw
		int sign = 1
		if n < MAX_BONUS_MANA
			raw = rawCode
			if n < 10
				raw = raw + "0"
			raw = raw + I2S(n)
		else
			raw = rawCode + "--"
			sign = -1
		let a = new AbilityDefinitionMaxManaBonusMost(idString2IdInteger(raw))
		a.setMaxManaGained(1, sign * pow2(n))
		setProperties(a, I2S(sign * pow2(n)), "Mana")
	// Damage:
	rawCode = "$D"
	for n = 0 to MAX_BONUS_DAMAGE
		string raw
		int sign = 1
		if n < MAX_BONUS_DAMAGE
			raw = rawCode
			if n < 10
				raw = raw + "0"
			raw = raw + I2S(n)
		else
			raw = rawCode + "--"
			sign = -1
		let a = new AbilityDefinitionAttackBonus(idString2IdInteger(raw))
		a.setAttackBonus(1, sign * pow2(n))
		setProperties(a, I2S(sign * pow2(n)), "Damage")
		
	// Armor:
	rawCode = "$R"
	for n = 0 to MAX_BONUS_ARMOR
		string raw
		int sign = 1
		if n < MAX_BONUS_ARMOR
			raw = rawCode
			if n < 10
				raw = raw + "0"
			raw = raw + I2S(n)
		else
			raw = rawCode + "--"
			sign = -1
		let a = new AbilityDefinitionDefenseBonusPlus1(idString2IdInteger(raw))
		a.setDefenseBonus(1, sign * pow2(n))
		setProperties(a, I2S(sign * pow2(n)), "Armor")

	// Sightrange:
	rawCode = "$V"
	for n = 0 to MAX_BONUS_SIGHTRANGE
		string raw
		int sign = 1
		if n < MAX_BONUS_SIGHTRANGE
			raw = rawCode
			if n < 10
				raw = raw + "0"
			raw = raw + I2S(n)
		else
			raw = rawCode + "--"
			sign = -1
		let a = new AbilityDefinitionSightBonus(idString2IdInteger(raw))
		a.setSightRangeBonus(1, sign * pow2(n))
		setProperties(a, I2S(sign * pow2(n)), "Sightrange")

	// Liferegen:
	rawCode = "$l"
	for n = 0 to MAX_BONUS_LIFEREGEN
		string raw
		int sign = 1
		if n < MAX_BONUS_LIFEREGEN
			raw = rawCode
			if n < 10
				raw = raw + "0"
			raw = raw + I2S(n)
		else
			raw = rawCode + "--"
			sign = -1
		let a = new AbilityDefinitionRegenLife(idString2IdInteger(raw))
		a.setHitPointsRegeneratedPerSecond(1, sign * pow2(n))
		setProperties(a, I2S(sign * pow2(n)), "Liferegen")

	// Manaregen:
	rawCode = "$m"
	for n = 0 to MAX_BONUS_MANAREGEN
		string raw
		int sign = 1
		if n < MAX_BONUS_MANAREGEN
			raw = rawCode
			if n < 10
				raw = raw + "0"
			raw = raw + I2S(n)
		else
			raw = rawCode + "--"
			sign = -1
		if IS_MANAREGEN_PERCENTAL
			let a = new AbilityDefinitionItemRegenMana(idString2IdInteger(raw))
			a.setManaRegenerationBonusasfractionofnormal(1, sign * pow2(n) / 100.)
			setProperties(a, I2S(sign * pow2(n)) + "%", "Manaregen")
		else
			let a = new AbilityDefinitionNeutralRegenmanaonly(idString2IdInteger(raw))
			a.setPercentage(1, false)
			a.setAreaofEffect(1, 1)
			a.setTargetsAllowed(1, "self")
			a.setAmountRegenerated(1, sign * pow2(n) / 200.)
			a.setRace(Race.Other)
			setProperties(a, I2S(sign * pow2(n)), "Manaregen")

	// Attackspeed:
	rawCode = "$P"
	for n = 0 to MAX_BONUS_ATTACKSPEED
		string raw
		int sign = 1
		if n < MAX_BONUS_ATTACKSPEED
			raw = rawCode
			if n < 10
				raw = raw + "0"
			raw = raw + I2S(n)
		else
			raw = rawCode + "--"
			sign = -1
		let a = new AbilityDefinitionAttackSpeedIncrease(idString2IdInteger(raw))
		a.setAttackSpeedIncrease(1, sign * pow2(n) / 100.)
		setProperties(a, I2S(sign * pow2(n)) + "%", "Attackspeed")

	// Strength:
	rawCode = "$S"
	for n = 0 to MAX_BONUS_STRENGTH
		string raw
		int sign = 1
		if n < MAX_BONUS_STRENGTH
			raw = rawCode
			if n < 10
				raw = raw + "0"
			raw = raw + I2S(n)
		else
			raw = rawCode + "--"
			sign = -1
		let a = new AbilityDefinitionStrengthBonusPlus1(idString2IdInteger(raw))
		a.setStrengthBonus(1, sign * pow2(n))
		setProperties(a, I2S(sign * pow2(n)), "Strength")

	// Agility:
	rawCode = "$A"
	for n = 0 to MAX_BONUS_AGILITY
		string raw
		int sign = 1
		if n < MAX_BONUS_AGILITY
			raw = rawCode
			if n < 10
				raw = raw + "0"
			raw = raw + I2S(n)
		else
			raw = rawCode + "--"
			sign = -1
		let a = new AbilityDefinitionAgilityBonusPlus1(idString2IdInteger(raw))
		a.setAgilityBonus(1, sign * pow2(n))
		setProperties(a, I2S(sign * pow2(n)), "Agility")

	// Intelligence:
	rawCode = "$I"
	for n = 0 to MAX_BONUS_INTELLIGENCE
		string raw
		int sign = 1
		if n < MAX_BONUS_INTELLIGENCE
			raw = rawCode
			if n < 10
				raw = raw + "0"
			raw = raw + I2S(n)
		else
			raw = rawCode + "--"
			sign = -1
		let a = new AbilityDefinitionIntelligenceBonusPlus1(idString2IdInteger(raw))
		a.setIntelligenceBonus(1, sign * pow2(n))
		setProperties(a, I2S(sign * pow2(n)), "Intelligence")