# # Enemy.py # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; version 2 of the License. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # Copyright # Author: Nicholas F. Hoover # Contributors: Salvatore S. Gionfriddo # Created: 2007.06.25 # Last Modified: 2007.11.07 # import random, pygame from Character import Character from Animation import Animation class Enemy(Character): ############## # ANIMATIONS # ############## NUM_ANIMATIONS = 5 (IDLE, WALK_UP, WALK_DOWN, WALK_SIDE, ATTACK1) = range(NUM_ANIMATIONS) LEFT = NUM_ANIMATIONS ############### # FRAME TICKS # ############### IDLE_ANIMATION_FRAME_TICKS = 1 WALK_ANIMATION_FRAME_TICKS = 2 ATTACK1_ANIMATION_FRAME_TICKS = 7 ############## # KEY FRAMES # ############## ATTACK1_KEY_FRAME = 1 DEFAULT_ANIMATION = IDLE ################### # ANIMATION TABLE # ################### ANIMATION_TABLE = [ (IDLE, 'BasicKid_Run', IDLE_ANIMATION_FRAME_TICKS, Animation.NO_KEY_FRAME), (WALK_UP, 'BasicKid_Run', WALK_ANIMATION_FRAME_TICKS, Animation.NO_KEY_FRAME), (WALK_DOWN, 'BasicKid_Run', WALK_ANIMATION_FRAME_TICKS, Animation.NO_KEY_FRAME), (WALK_SIDE, 'BasicKid_Run', WALK_ANIMATION_FRAME_TICKS, Animation.NO_KEY_FRAME), (ATTACK1, 'BasicKid_Bite', ATTACK1_ANIMATION_FRAME_TICKS, ATTACK1_KEY_FRAME)] ######### # STATS # ######### SPEED = 4 MAX_HEALTH = 100 ATTACK1_DAMAGE = 5 ######## # MISC # ######## TARGET_REACHED_X_RANGE = 5 TARGET_REACHED_Y_RANGE = 4 DEFAULT_POSITION = (0,0) RECT_SIZE = (30,50) COLLISION_RECT_SIZE = (10,2) COLLISION_RECT_POSITION = (10,48) GROUPABLE = True GROUPER = False GROUP_ATTACH_RANGE = 20 PARACHUTING = False CHANCE_TO_THROW = 1 # one in one-thousand CAN_THROW = False ACTIONS = [ATTACK1] AVERT = True LOCKED_TO_SCREEN = False def __init__(self): self.throwing = False self.grouped = False self.speed_bonus = 0 self.MOVEMENTS = [[0,0],[0,-self.SPEED],[0,self.SPEED],[self.SPEED,0],[0,0]] Character.__init__(self) Character.reset(self) self.nextAction = self.IDLE self.nextFacing = self.facing self.behavior = Behavior() # AI Sub Functions def doMovement(self, collision, targetRect): facing = self.facing xRange = 0 xRangeR = (targetRect.centerx - Enemy.TARGET_REACHED_X_RANGE) - self.rect.right xRangeL = self.rect.left - (targetRect.centerx + Enemy.TARGET_REACHED_X_RANGE) if xRangeR > 0: xwalk = self.WALK_SIDE facing = self.RIGHT xRange = xRangeR xSpeed = self.SPEED elif xRangeL > 0: xwalk = self.WALK_SIDE facing = self.LEFT xRange = xRangeL xSpeed = -self.SPEED else: xSpeed = 0 yRange = 0 yRangeD = (targetRect.bottom - Enemy.TARGET_REACHED_Y_RANGE) - self.rect.bottom yRangeU = self.rect.bottom - (targetRect.bottom + Enemy.TARGET_REACHED_Y_RANGE) if yRangeU > 0: ywalk = self.WALK_UP yRange = yRangeU ySpeed = -self.SPEED elif yRangeD > 0: ywalk = self.WALK_DOWN yRange = yRangeD ySpeed = self.SPEED else: ySpeed = 0 self.MOVEMENTS[self.WALK_UP] = [xSpeed, ySpeed] self.MOVEMENTS[self.WALK_DOWN] = [xSpeed, ySpeed] self.MOVEMENTS[self.WALK_SIDE] = [self.SPEED, ySpeed] if xRange == 0 and yRange == 0: #self.attack1() pass else: if xRange > yRange: walk = xwalk else: walk = ywalk if walk != self.nextAction or facing != self.facing: self.walk(walk, facing) def stalk(self, collision, targetRect): self.doMovement(collision, targetRect) def getFleeRect(self, tRect): dx = self.rect.left - tRect.left dy = self.rect.bottom - tRect.bottom fRect = pygame.Rect(self.rect) fRect.left += dx fRect.bottom += dy return fRect def getWanderRect(self, tRect): fRect = pygame.Rect(tRect) if self.rect.left >= 400 and self.rect.bottom >= 300: (x,y) = (700,150) elif self.rect.left >= 400 and self.rect.bottom <= 300: (x,y) = (200,50) elif self.rect.left <= 400 and self.rect.bottom >= 300: (x,y) = (600,550) elif self.rect.left <= 400 and self.rect.bottom <= 300: (x,y) = (100,450) else: (x,y) = (0,0) fRect.left = x fRect.bottom = y return fRect def wander(self, collision, targetRect): targetRect = self.getWanderRect(targetRect) self.doMovement(collision, targetRect) def flee(self, collision, targetRect): #change target targetRect = self.getFleeRect(targetRect) #do movement self.doMovement(collision, targetRect) def attack(self): self.attack1() def throw(self): self.throwing = True # status checks def inAttackRange(self, targetRect): (xRange, yRange) = self.rangeFromTarget(targetRect) return (xRange == 0 and yRange == 0) def inThrowRange(self, targetRect): (xRange, yRange) = self.rangeFromTarget(targetRect) return (self.CAN_THROW and (xRange > (2*self.TARGET_REACHED_X_RANGE) or yRange > (2*self.TARGET_REACHED_Y_RANGE)) and self.CHANCE_TO_THROW >= random.randint(0,1000)) def rangeFromTarget(self, targetRect): xRangeR = (targetRect.centerx - Enemy.TARGET_REACHED_X_RANGE) - self.rect.right # R rC : (C-r) - R xRangeL = self.rect.left - (targetRect.centerx + Enemy.TARGET_REACHED_X_RANGE) # Cr L : L - (C+r) yRangeD = (targetRect.bottom - Enemy.TARGET_REACHED_Y_RANGE) - self.rect.bottom # yRangeU = self.rect.bottom - (targetRect.bottom + Enemy.TARGET_REACHED_Y_RANGE) # xRange = max([xRangeR, xRangeL, 0]) yRange = max([yRangeD, yRangeU, 0]) return (xRange, yRange) def getKidStatus(self, collision): return (0,len(collision)) def performAI(self, collision, targetRect): if self.PARACHUTING: self.MOVE = False return else: self.MOVE = True #do ai (deadKids, livingKids) = self.getKidStatus(collision) behavior = self.behavior.getBehavior(deadKids, livingKids, self.inAttackRange(targetRect), self.inThrowRange(targetRect)) if behavior == Behavior.WANDER: self.wander(collision, targetRect) elif behavior == Behavior.STALK: self.stalk(collision, targetRect) elif behavior == Behavior.FLEE: self.flee(collision, targetRect) elif behavior == Behavior.THROW: self.throw() elif behavior == Behavior.ATTACK: self.attack() class Behavior: NUM_BEHAVIORS = 5 (WANDER, STALK, FLEE, THROW, ATTACK) = range(NUM_BEHAVIORS) MIN_AGGRESSION = 0 MAX_AGGRESSION = 10 MIN_FEAR = 0 MAX_FEAR = 10 NUM_PERSONALITIES = 3 (WUSSY, NORMAL, AGGRESSIVE) = range(NUM_PERSONALITIES) def __init__(self, personality = NORMAL): self.aggression = 5 + personality self.fear = 5 self.wander_time = 0 self.personality = personality self.current_behavior = Behavior.WANDER self.behavior_time = 0 def getBehavior(self, deadKidsNearBy, livingKidsNearBy, inAttackRange, inThrowRange): self.modifyState(deadKidsNearBy, livingKidsNearBy) behavior = Behavior.WANDER if inAttackRange: behavior = Behavior.ATTACK elif inThrowRange: behavior = Behavior.THROW else: if self.fear > self.aggression: behavior = Behavior.FLEE elif self.aggression > self.fear: behavior = Behavior.STALK else: behavior = Behavior.WANDER if self.current_behavior == behavior: self.behavior_time += 1 else: self.behavior_time = 0 self.current_behavior = behavior #print behavior return behavior def modifyState(self, deadKidsNearBy, livingKidsNearBy): timeInBehavior = 100 if self.current_behavior == Behavior.WANDER: if self.behavior_time > timeInBehavior: self.aggression += 1 else: pass elif self.current_behavior == Behavior.FLEE: if self.behavior_time > timeInBehavior: self.aggression += 1 else: pass elif self.current_behavior == Behavior.THROW: if self.behavior_time > timeInBehavior: self.fear += 1 else: self.fear = 5 self.aggression = 5 #self.aggression += self.personality if self.fear > 10: self.fear = 10 elif self.fear < 0: self.fear = 0 if self.aggression > 10: self.aggression = 10 elif self.aggression < 0: self.aggression = 0