package{ import flash.display.Sprite import flash.text.*; import flash.display.MovieClip; import fl.transitions.*; import fl.transitions.easing.*; import flash.system.System; //import flash.text.TextFormat; //import flash.text.TextFieldAutoSize; import myGlobal; public class Enemy extends Sprite{ public var totalHealth:int; public var currentHealth:int; public var totalMana:int; public var currentMana:int; public var vitality:int; public var intelligence:int; public var strength:int; public var agility:int; public var speed:int; public var currentlyDefending; public var xTarget:int; public var yTarget:int; public var enemyAction:String="Wait";//Variable to determine what frames enemy.swf should be at public var moveRanks:Array; function Enemy(constructorvitality:int, constructorintelligence:int, constructorstrength:int, constructoragility:int, constructorspeed:int, constructorXTarget:int, constructorYTarget:int):void{ //xTarget and yTarget are to be used to get the location of the enemy //so that the function displayDamage() works properly and the damage //is actually coming from an area near them. trace("constructor"); totalHealth = 40+10*constructorvitality; currentHealth = totalHealth; totalMana = 20+constructorintelligence*10; currentMana = totalMana; vitality=constructorvitality; intelligence=constructorintelligence; strength=constructorstrength; agility=constructoragility; speed=constructorspeed; xTarget = constructorXTarget; yTarget = constructorYTarget; moveRanks = new Array(); } public function attack():int{ var damage:int = 0; var hitType:int = Math.random()*1000; if(hitType<300){ displayDamage(damage,0); } if(hitType>700){ damage = (Math.random()*10+strength)*1.5; displayDamage(damage,2); } if(hitType>=300 && hitType<=700){ damage = Math.random()*10+strength; displayDamage(damage,1); } return damage; } private function displayDamage(damage:int, damageType:int):void{ var eDamageText:TextField = new TextField(); trace("Displaying enemy damage (" + damage + ")!"); switch(damageType){ case 0: eDamageText.text = "Miss"; var missFormat:TextFormat = new TextFormat(); missFormat.color = 0xFFFFFF; missFormat.font = "Rosewood Std"; missFormat.size=40; eDamageText.setTextFormat(missFormat); break; case 1: eDamageText.text = String(damage); var damageFormat:TextFormat = new TextFormat(); damageFormat.color = 0x0000FF; damageFormat.font = "Rosewood Std"; damageFormat.size = 40; eDamageText.setTextFormat(damageFormat); break; case 2: eDamageText.text = String(damage); var critFormat:TextFormat = new TextFormat(); critFormat.color = 0xFF0000; critFormat.font = "Rosewood Std"; critFormat.size = 40; eDamageText.setTextFormat(critFormat); break; default: trace("Jim doesn't have DSL."); } addChild(eDamageText); eDamageText.x=150; var tweenUp:Tween = new Tween(eDamageText, "y", Regular.easeOut, 330, 150, 2, true); var tweenAlpha:Tween = new Tween(eDamageText, "alpha", Regular.easeOut, 1,0,2,true); tweenAlpha.addEventListener(TweenEvent.MOTION_FINISH, removeText); function removeText(e:TweenEvent):void{ removeChild(eDamageText); } } public function heal():int{ var healAmount:int = 0; var healType:int = Math.random()*1000; switch(healType){ case (healType<500): healAmount=Math.random()*5+intelligence; displayHeal(healAmount, 0); return healAmount; break; case (healType>=500): healAmount=(Math.random()*5+intelligence)*1.5; displayHeal(healAmount, 1); return healAmount; break; default: return 0; } } private function displayHeal(healAmount:int, healType:int):void{ var healText = new TextField(); switch(healType){ case 0: healText.text = String(healAmount); var healFormat:TextFormat = new TextFormat(); healFormat.color = 0xFFFFFF; healFormat.font = "Rosewood Std"; healFormat.size = 40; healText.setTextFormat(healFormat); break; case 1: healText.text = String(healAmount); var critHealFormat:TextFormat = new TextFormat(); critHealFormat.color = 0xFF0000; critHealFormat.font = "Rosewood Std"; critHealFormat.size = 40; healText.setTextFormat(critHealFormat); break; default: trace("something messed up with heals"); } } public function getTimerSpeed():int{ return (5000-10*speed); } /* --- Begin enemy attack algorithm methods (AI) --- */ /* findRank() * * Statistics-based heuristic function. * * Currently this needs to be improved. * * IMPORTANT: This must be accurate, as it is * ultimately what is used to determine * which move is picked. */ private function findRank(cHealth:uint, eHealth:uint, moveType:uint, treeDepth:uint):uint { switch(moveType) { case gameMoveTypes.gameMove_flee: /* If char health is low, don't flee */ if (myGlobal.myCharacter.currentHealth < myGlobal.lowHealth) { return 0; } else if (myGlobal.enemy.currentHealth > myGlobal.lowHealth) { /* If enemy health is reasonable, don't flee */ return 0; } else { /* If enemy health is low (< 20%), create a * chance of favoring flee */ if (myGlobal.enemy.totalHealth / myGlobal.enemy.currentHealth > 5) { if (Math.random()*myGlobal.rankMax < 4) { return myGlobal.rankMax; } else { return 0; } } else { return 0; } } break; case gameMoveTypes.gameMove_defend: case gameMoveTypes.gameMove_useItem: case gameMoveTypes.gameMove_weaponAttack: case gameMoveTypes.gameMove_magicAttack: case gameMoveTypes.gameMove_magicHealSelf: case gameMoveTypes.gameMove_magicHealOther: /* Return a rank based on what the move tree * has determined */ if (cHealth == 0) { /* Character has died, this is the best move */ return myGlobal.rankMax; } else if (eHealth == 0) { /* Enemy has died, probably not the best move */ return 0; } else if (cHealth > myGlobal.myCharacter.currentHealth) { /* Character health has increased, probably not * the best move */ return 0; } else { return (1 - cHealth / myGlobal.myCharacter.currentHealth) * myGlobal.rankMax; } break; default: trace("Illegal move type in findRank\n"); break; } return 0; } private function simUpdateEnemyHealth(enemyHealth:int, cMove:uint, eMove:uint):uint { /* Character moves that can modify enemy health */ switch (cMove) { case gameMoveTypes.gameMove_weaponAttack: enemyHealth -= (Math.random()*10 + myGlobal.myCharacter.strength); /* Sanity check enemy health */ if (enemyHealth < 0) { enemyHealth = 0; } break; case gameMoveTypes.gameMove_magicAttack: enemyHealth -= (Math.random()*10 + myGlobal.myCharacter.intelligence); /* Sanity check enemy health */ if (enemyHealth < 0) { enemyHealth = 0; } break; case gameMoveTypes.gameMove_magicHealOther: break; case gameMoveTypes.gameMove_useItem: break; default: break; }; /* Enemy moves that can modify enemy health */ switch (eMove) { case gameMoveTypes.gameMove_magicHealSelf: enemyHealth += (Math.random()*5 + myGlobal.enemy.intelligence); /* Sanity check enemy health */ if (enemyHealth > myGlobal.enemy.totalHealth) { enemyHealth = myGlobal.enemy.totalHealth; } break; case gameMoveTypes.gameMove_useItem: break; default: break; }; return enemyHealth; } private function simUpdateEnemyMana(enemyMana:int, cMove:uint, eMove:uint):uint { /* Enemy moves that can modify enemy mana */ switch (eMove) { case gameMoveTypes.gameMove_magicHealSelf: enemyMana -= myGlobal.magicCost; /* Sanity check enemy mana */ if (enemyMana < 0) { enemyMana = 0; } break; case gameMoveTypes.gameMove_magicHealOther: enemyMana -= myGlobal.magicCost; /* Sanity check enemy mana */ if (enemyMana < 0) { enemyMana = 0; } break; default: break; }; return enemyMana; } private function simUpdateCharacterHealth(characterHealth:int, cMove:uint, eMove:uint):uint{ /* Enemy moves that can modify character health */ switch (eMove) { case gameMoveTypes.gameMove_weaponAttack: characterHealth -= (Math.random()*10 + myGlobal.enemy.strength); /* Sanity check char health */ if (characterHealth < 0) { characterHealth = 0; } break; case gameMoveTypes.gameMove_magicAttack: characterHealth -= (Math.random()*10 + myGlobal.enemy.intelligence); /* Sanity check char health */ if (characterHealth < 0) { characterHealth = 0; } break; case gameMoveTypes.gameMove_magicHealOther: break; case gameMoveTypes.gameMove_useItem: break; default: break; }; /* Character moves that can modify character health */ switch (cMove) { case gameMoveTypes.gameMove_magicHealSelf: characterHealth += (Math.random()*5 + myGlobal.myCharacter.intelligence); /* Sanity check max char health */ if (characterHealth > myGlobal.myCharacter.totalHealth) { characterHealth = myGlobal.myCharacter.totalHealth; } break; case gameMoveTypes.gameMove_useItem: break; default: break; }; return characterHealth; } private function simUpdateCharacterMana(characterMana:int, cMove:uint, eMove:uint):uint { /* Character moves that can modify character mana */ switch (cMove) { case gameMoveTypes.gameMove_magicHealSelf: characterMana -= myGlobal.magicCost; /* Sanity check char mana */ if (characterMana < 0) { characterMana = 0; } break; case gameMoveTypes.gameMove_magicHealOther: characterMana -= myGlobal.magicCost; /* Sanity check char mana */ if (characterMana < 0) { characterMana = 0; } break; default: break; }; return characterMana; } /* enemyEvaluateMoveTree * * Evaluate every combination of character/enemy moves from * the current round to end of battle (or max. depth), and * determine which move is the best for the enemy this round. */ private function enemyEvaluateMoveTree(enemyHealth:uint, enemyMana:uint, characterHealth:uint, characterMana:uint, eMoveType:int, cMoveType:int, treeDepth:uint):void { /* * If we've reached the maximum amount of * graph levels, we terminate. * * Also, if we've reached our goal conditions * (the battle is over), we also terminate. */ if (treeDepth == myGlobal.maxTreeDepth || enemyHealth == 0 || characterHealth == 0 || eMoveType == gameMoveTypes.gameMove_flee || cMoveType == gameMoveTypes.gameMove_flee) { var rank:uint = findRank(enemyHealth, characterHealth, eMoveType, treeDepth); /* Accumulate this node's rank to the actual * move's total rank in the moveRanks array */ moveRanks[eMoveType] += rank; return; } /* * Iterate over each game move type. For each one, * calculate its stat modifiers, then pass the updates * recursively to this function again to dynamically * search through a tree to find which end result * has the highest rank based on a statistical evaluator. */ for (var i:uint = 0; i < gameMoveTypes.gameMove_numMoves; i++) { //for (var j:uint = 0; j < gameMoveTypes.gameMove_numMoves; j++) { var j:uint = gameMoveTypes.gameMove_weaponAttack; var tEnemyHealth:uint = simUpdateEnemyHealth(enemyHealth, i, j); var tEnemyMana:uint = simUpdateEnemyMana(enemyMana, i, j); var tCharacterHealth:uint = simUpdateCharacterHealth(characterHealth, i, j); var tCharacterMana:uint = simUpdateCharacterMana(characterMana, i, j); enemyEvaluateMoveTree(tEnemyHealth, tEnemyMana, tCharacterHealth, tCharacterMana, i, j, treeDepth + 1); //} } } /* chooseEnemyMove() * * AI for determining the enemy's next move. */ public function chooseEnemyMove():uint { var i:int; var bestIndex:uint = 0; var maxRank:uint = 0; /* Initialize each move's rank to 0 */ for (i = 0; i < gameMoveTypes.gameMove_numMoves; i++) { moveRanks.push(0); } /* This is where the real work is done */ enemyEvaluateMoveTree(myGlobal.myCharacter.currentHealth, myGlobal.myCharacter.currentMana, currentHealth, currentMana, gameMoveTypes.gameMove_numMoves, gameMoveTypes.gameMove_numMoves, 0); /* Find maximum move rank in the moveRank array */ for (i = 0; i < gameMoveTypes.gameMove_numMoves; i++) { if (moveRanks[i] > maxRank) { maxRank = moveRanks[i]; bestIndex = i; } } return bestIndex; } } /* Class */ } /* Package */