Advertisement
Guest User

Untitled

a guest
Jul 28th, 2014
208
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 10.02 KB | None | 0 0
  1. #import "DTEnemyMovement.h"
  2. #import "DTTowerFloor.h"
  3.  
  4. /*
  5. Each tick something will happen based on the state, or the state will be processed elsewhere
  6. Towers and Floors both may contain enemies, though they will always be on a floor unless moving between floors
  7. To update the movement, a method converts the current state to a string, and gets the method name to call from a dictionary
  8. This works like a decision table and removes the need for a switch statement
  9. Movement can only be up or down, left or right, no combination of them
  10. */
  11.  
  12. //Two options for the decision table, either access the dictionary directly with 0-x, the enum values, or make strings for their names
  13. //the advantage of strings is that it is more extensible, and the position in the enum doesnt matter
  14. NSString* const kEnemyMovementStateJustSpawned = @"enemyMovementStateJustSpawned";
  15. NSString* const kEnemyMovementStateIdle = @"enemyMovementStateIdle";
  16. NSString* const kEnemyMovementStateNeedsMoving = @"enemyMovementStateNeedsMoving";
  17. NSString* const kEnemyMovementStateToFloor = @"enemyMovementStateToFloor";
  18. NSString *const kEnemyMovementStateAtDestinationFloor = @"enemyMovementStateAtDestinationFloor";
  19. NSString* const kEnemyMovementStateToFloorExit = @"enemyMovementStateToFloorExit";
  20. //NSString* const kEnemyMovementStateAtFloorExit = @"enemyMovementStateAtFloorExit"; //tower handles
  21. NSString* const kEnemyMovementStateToAttackWalls = @"enemyMovementStateToAttackWalls";
  22. //NSString* const kEnemyMovementStateAtAttackWalls = @"enemyMovementStateAtAttackWalls"; //floor handles
  23. NSString* const kEnemyMovementStateToAttackFloor = @"enemyMovementStateToAttackFloor";
  24. //NSString* const kEnemyMovementStateAtAttackFloor = @"enemyMovementStateAtAttackFloor"; //floor handles
  25. NSString* const kEnemyMovementStateToAttackRoom = @"enemyMovementStateToAttackRoom";
  26. //NSString* const kEnemyMovementStateAtAttackRoom = @"enemyMovementStateAtAttackRoom"; //floor handles
  27. //NSString* const kEnemyMovementStateStunned = @"enemyMovementStateStunned"; //not yet used
  28.  
  29.  
  30. @implementation DTEnemyMovement {
  31. NSMutableArray *_floorList;
  32.  
  33. NSMutableArray *_floorRange; //the range will be set based on the initial floor
  34.  
  35. int _startingFloor;
  36.  
  37. NSDictionary *_decisionTable;
  38. }
  39.  
  40. -(id) initWithWorldSizeForCharacter:(CGSize)worldSize andStartingFloor:(int)startingFloor {
  41. self = [super initWithWorldSize:worldSize];
  42. if (self) {
  43. _floorList = [[NSMutableArray alloc]init];
  44. _floorRange = [[NSMutableArray alloc]init];
  45. _startingFloor = startingFloor;
  46.  
  47. _decisionTable = [[NSDictionary alloc]init];
  48. [self setupDecisionTable];
  49. }
  50. return self;
  51. }
  52. -(void) setupDecisionTable {
  53. //the string objects are the names of methods in the class
  54. _decisionTable = @{kEnemyMovementStateJustSpawned: @"doEnemyJustSpawned",
  55. kEnemyMovementStateIdle: @"doEnemyIdle",
  56. kEnemyMovementStateNeedsMoving: @"doEnemyNeedsMoving",
  57. kEnemyMovementStateToFloorExit: @"doFloorMovement",
  58. kEnemyMovementStateToFloor: @"doVerticalMovement",
  59. kEnemyMovementStateAtDestinationFloor: @"doEnemyAtDestinationFloor",
  60. kEnemyMovementStateToAttackWalls: @"doFloorMovement",
  61. kEnemyMovementStateToAttackFloor: @"doFloorMovement",
  62. kEnemyMovementStateToAttackRoom: @"doFloorMovement"
  63. };
  64. }
  65. -(NSString *) stringForState:(EnemyMovementState)state {
  66. switch (state) {
  67. case EnemyMovementStateJustSpawned:
  68. return kEnemyMovementStateJustSpawned;
  69. case EnemyMovementStateIdle:
  70. return kEnemyMovementStateIdle;
  71. case EnemyMovementStateNeedsMoving:
  72. return kEnemyMovementStateNeedsMoving;
  73. case EnemyMovementStateToFloor:
  74. return kEnemyMovementStateToFloor;
  75. case EnemyMovementStateAtDestinationFloor:
  76. return kEnemyMovementStateAtDestinationFloor;
  77. case EnemyMovementStateToFloorExit:
  78. return kEnemyMovementStateToFloorExit;
  79. case EnemyMovementStateAtFloorExit:
  80. return nil;
  81. case EnemyMovementStateToAttackWalls:
  82. return kEnemyMovementStateToAttackWalls;
  83. case EnemyMovementStateAtAttackWalls:
  84. return nil;
  85. case EnemyMovementStateToAttackFloor:
  86. return kEnemyMovementStateToAttackFloor;
  87. case EnemyMovementStateAtAttackFloor:
  88. return nil;
  89. case EnemyMovementStateToAttackRoom:
  90. return kEnemyMovementStateToAttackRoom;
  91. case EnemyMovementStateAtAttackRoom:
  92. return nil;
  93. default:
  94. break;
  95. }
  96. return nil;
  97. }
  98.  
  99. #pragma mark - Update Loop
  100. -(void) doMovement {
  101. //the selector is formed from a string inside the decision table dictionary
  102. SEL methodToCallName = NSSelectorFromString([_decisionTable objectForKey:[self stringForState:self.state]]);
  103. if (methodToCallName) {
  104. IMP functionPointer = [self methodForSelector:methodToCallName];
  105. void (*methodToCall)(id, SEL) = (void *)functionPointer;
  106. methodToCall(self, methodToCallName);
  107. }
  108. }
  109. -(void) doEnemyJustSpawned {
  110. if ([self checkFloorsForJobs]) {
  111. self.state = EnemyMovementStateNeedsMoving;
  112. } else {
  113. self.state = EnemyMovementStateIdle;
  114. }
  115. }
  116. -(void) doEnemyIdle {
  117. if ([self checkFloorsForJobs]) {
  118. self.state = EnemyMovementStateNeedsMoving;
  119. } else {
  120. [self doIdleMovement];
  121. }
  122. }
  123. -(void) doEnemyNeedsMoving {
  124. [self calculateFloorExitPositionByFloor];
  125. self.state = EnemyMovementStateToFloorExit;
  126. }
  127. -(void) doEnemyAtDestinationFloor {
  128. BOOL foundADestination = NO;
  129. for (DTTowerFloor *floor in _floorList) {
  130. if (floor.floorNumber == self.currentFloor) {
  131. //attack room first, then walls, then bottom
  132. if (floor.floorBuildState & FloorHasRoom) {
  133. [self moveToAttackRoom];
  134. foundADestination = YES;
  135. } else if (floor.floorBuildState & FloorHasWalls) {
  136. [self moveToAttackWalls];
  137. foundADestination = YES;
  138. } else if (floor.floorBuildState & FloorHasBottom) {
  139. [self moveToAttackBottom];
  140. foundADestination = YES;
  141. }
  142. }
  143. }
  144. if (!foundADestination) {
  145. self.state = EnemyMovementStateIdle;
  146. }
  147. }
  148. -(void) moveToAttackBottom {
  149. [self pickRandomDestinationOnCurrentFloor];
  150. self.state = EnemyMovementStateToAttackFloor;
  151. }
  152. -(void) moveToAttackRoom {
  153. [self pickRandomDestinationOnCurrentFloor];
  154. self.state = EnemyMovementStateToAttackRoom;
  155. }
  156. -(void) moveToAttackWalls {
  157. //decide which wall to move to, the nearest one
  158. int positiveOrNegative = 0;
  159. if (self.currentPosition.x >= 0) {
  160. positiveOrNegative = 1;
  161. } else {
  162. positiveOrNegative = -1;
  163. }
  164.  
  165. //move to the left or right wall
  166. self.destinationPosition = CGPointMake(([self worldSize].width / 2.1) * positiveOrNegative, self.currentPosition.y);
  167. self.state = EnemyMovementStateToAttackWalls;
  168. }
  169. -(BOOL) checkFloorsForJobs {
  170. NSMutableSet *floorsInRange = [[NSMutableSet alloc]init];
  171.  
  172. for (DTTowerFloor *floor in _floorList) {
  173. if (floor.floorNumber == _startingFloor || floor.floorNumber == _startingFloor - 1 || floor.floorNumber == _startingFloor + 1) {
  174. if (floor.floorBuildState & FloorHasWalls) {
  175. [floorsInRange addObject:floor];
  176. } else if (floor.floorBuildState & FloorHasBottom) {
  177. [floorsInRange addObject:floor];
  178. } else if (floor.floorBuildState & FloorHasRoomUpgrade) {
  179. [floorsInRange addObject:floor];
  180. }
  181. }
  182. }
  183.  
  184. if (floorsInRange.count > 0) {
  185. NSMutableArray *tempArray = [[NSMutableArray alloc]init];
  186. for (DTTowerFloor *floor in floorsInRange) {
  187. [tempArray addObject:floor];
  188. }
  189. self.destinationFloor = [self closestFloor:tempArray];
  190. return YES;
  191. }
  192.  
  193. return NO;
  194. }
  195.  
  196. #pragma mark - Overrides
  197. -(void) arriveAtDestinationFloor {
  198. self.currentPosition = self.destinationPosition;
  199. self.currentFloor = self.destinationFloor;
  200. self.state = EnemyMovementStateAtDestinationFloor;
  201.  
  202. //not sure if this conditional is needed but it is good for debugging
  203. if (self.state == EnemyMovementStateToFloor) {
  204. self.state = EnemyMovementStateAtDestinationFloor;
  205. }
  206. }
  207. -(void) doFloorMovement {
  208. //once close enough to the destination, jump to the destination
  209. if ([self isAtFloorDestinationPosition]) {
  210. self.currentPosition = self.destinationPosition;
  211. if (self.state == EnemyMovementStateToFloorExit) {
  212. self.state = EnemyMovementStateAtFloorExit;
  213. } else if (self.state == EnemyMovementStateToAttackWalls) {
  214. self.state = EnemyMovementStateAtAttackWalls;
  215. } else if (self.state == EnemyMovementStateToAttackFloor) {
  216. self.state = EnemyMovementStateAtAttackFloor;
  217. } else if (self.state == EnemyMovementStateToAttackRoom) {
  218. self.state = EnemyMovementStateAtAttackRoom;
  219. }
  220. } else {
  221. if ([self shouldMoveRight]) {
  222. [self moveRight];
  223. } else {
  224. [self moveLeft];
  225. }
  226. }
  227. }
  228.  
  229. #pragma mark - Floor List
  230. -(void) acceptFloorList:(NSMutableArray *)floorList {
  231. _floorList = floorList;
  232. }
  233.  
  234. #pragma mark - NSCoding methods
  235. -(id) initWithCoder:(NSCoder *)aDecoder {
  236. self = [super initWithCoder:aDecoder];
  237. if (self) {
  238. _state = [aDecoder decodeIntegerForKey:@"enemyMovementState"];
  239. _floorList = [aDecoder decodeObjectForKey:@"floorList"];
  240. _startingFloor = [aDecoder decodeIntForKey:@"startingFloor"];
  241. _decisionTable = [aDecoder decodeObjectForKey:@"decisionTable"];
  242. }
  243. return self;
  244. }
  245. -(void) encodeWithCoder:(NSCoder *)aCoder {
  246. [super encodeWithCoder:aCoder];
  247. [aCoder encodeInteger:self.state forKey:@"enemyMovementState"];
  248. [aCoder encodeObject:_floorList forKey:@"floorList"];
  249. [aCoder encodeInt:_startingFloor forKey:@"startingFloor"];
  250. [aCoder encodeObject:_decisionTable forKey:@"decisionTable"];
  251. }
  252.  
  253. @end
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement