def movementCost(self, cur, dest): """Return the cost of moving from cur tile to dest tile, taking into account surface type. Only single step movement is considered. Returns 100 when movement is not possible.""" if gridhelper.isDifficultSurface(cur.getSurface()): # moving from difficult terrain if gridhelper.horizontalDistance(cur.coords, dest.coords) == 1: if (cur.coords.z - dest.coords.z) <= 3 and ( cur.coords.z - dest.coords.z) >= -1: return 2 else: # moving from normal terrain if gridhelper.horizontalDistance(cur.coords, dest.coords) == 1: if (cur.coords.z - dest.coords.z) <= 3 and ( cur.coords.z - dest.coords.z) >= -2: return 1 elif gridhelper.horizontalDistance(cur.coords, dest.coords) == 2: if (cur.coords.z - dest.coords.z) <= 3 and (cur.coords.z >= dest.coords.z): x1 = int(cur.coords.x + floor((dest.coords.x - cur.coords.x) / 2.0)) y1 = int(cur.coords.y + floor((dest.coords.y - cur.coords.y) / 2.0)) x2 = int(cur.coords.x + ceil((dest.coords.x - cur.coords.x) / 2.0)) y2 = int(cur.coords.y + ceil((dest.coords.y - cur.coords.y) / 2.0)) loc1 = fife.ExactModelCoordinate(x1, y2, cur.coords.z) loc2 = fife.ExactModelCoordinate(x2, y1, cur.coords.z) if (not self.isBlocked(loc1, 4)) and (not self.isBlocked( loc2, 4)): return 3 return 100
def isValidTarget(self, source, target, targeting_rules): """Return whether target can be targeted by source under targeting_rules. Source is a character and target is a character, tile or obstacle.""" if targeting_rules.can_target_self and (source == target): return True if (not targeting_rules.can_target_self) and (source.coords == target.coords): return False if type(target) == TacticsCharacter: if target.cur_HP <= 0: return False if (source != target) and ((targeting_rules.can_target_other and (type(target)) == TacticsCharacter) or (targeting_rules.can_target_tile and (type(target)) == TacticsTile) or (targeting_rules.can_target_obstacle and (type(target)) == Obstacle)): if targeting_rules.trajectory.type == targeting_rules.trajectory.TRAJECTORY_SINGLE: if (gridhelper.horizontalDistance(source.coords, target.coords) <= targeting_rules.trajectory.range) and ( (source.coords.z - target.coords.z) <= targeting_rules.trajectory.range_down) and ( (target.coords.z - source.coords.z) <= targeting_rules.trajectory.range_up): return True elif targeting_rules.trajectory.type == targeting_rules.trajectory.TRAJECTORY_LINE: if (gridhelper.horizontalDistance(source.coords, target.coords) <= targeting_rules.trajectory.range) and self.isLOS( source, target) and ( (source.coords.z - target.coords.z) <= targeting_rules.trajectory.range_down) and ( (target.coords.z - source.coords.z) <= targeting_rules.trajectory.range_up): return True elif targeting_rules.trajectory.type == targeting_rules.trajectory.TRAJECTORY_ARC: if self.isTrajectory(source, target): return True return False
def testTrajectory(self, coords1, coords2, trajectory, visualize=False): """ Return False if an arc from loc1 to loc2 defined by trajectory is blocked by a wall or outside range, True if the arc is clear and in range. Uses locations slightly above the ones given. Creates an optional visual trajectory. """ # TODO: visualization probably should not be done here ex_coords1 = gridhelper.toExact(coords1) ex_coords2 = gridhelper.toExact(coords2) z_scale = 3.0 dist = gridhelper.horizontalDistance(coords1, coords2) * z_scale if dist == 0: return False z = coords2.z - coords1.z g = 5.0 / z_scale d2 = pow(trajectory.range, 4) - g * (g * pow(dist, 2) + 2 * z * pow(trajectory.range, 2)) if d2 < 0: return False angle1 = atan((pow(trajectory.range, 2) - sqrt(d2)) / (g * dist)) #angle2 = atan((pow(trajectory.range, 2) + sqrt(d2)) / (g * dist)) step = (ex_coords2 - ex_coords1) / 20.0 if self.visual and visualize: los = VisualLOS(self.application) self.application.real_timeline.addTimer( TacticsTimer("line of sight", 1, 1, los.destroy)) for i in range(1, 20): ex_coords = ex_coords1 + step * float(i) ex_coords.z = ex_coords1.z + float(dist) / 20.0 * float(i) * tan( angle1) - g * pow(float(dist) / 20.0 * float(i), 2) / ( 2 * pow(trajectory.range * cos(angle1), 2)) if self.visual and visualize: location = fife.Location(self.maplayer) location.setExactLayerCoordinates( fife.ExactModelCoordinate(ex_coords.x, ex_coords.y, ex_coords.z + 2)) los.addDot(location, self.shadowLocation(location)) if self.isBlockedByWall( fife.ExactModelCoordinate(ex_coords.x, ex_coords.y, ex_coords.z + 1.5), 1): if self.visual and visualize: self.application.gui.sayBubble(los.instances[-1], "blocked", 50) return False return True
def targetAction(self): if self.source.visual: self.application.sound_fire.play() for target in self.target_list: if type(target) == TacticsCharacter: target.takeDamage( DamagePacket( self.source, target, 5 - 2 * gridhelper.horizontalDistance( target.coords, self.initial_target.coords), "fire", "burn")) if type(target) == TacticsTile: if target.visual: self.explosions.append( Explosion(self.application, target.coords, "explosion")) for surface in target.surface_effects: surface.startBurning()
def follow(self): instance_coords = self.map_object.instance.getLocation( ).getExactLayerCoordinates() # distance from the current partial position to the destination dx = (self.destination.x - instance_coords.x) dy = (self.destination.y - instance_coords.y) dz = (self.destination.z - instance_coords.z) distance = sqrt(dx * dx + dy * dy) # distance from the origin to the destination path_dx = (self.destination.x - self.origin.x) path_dy = (self.destination.y - self.origin.y) path_dz = (self.destination.z - self.origin.z) path_distance = sqrt(path_dx * path_dx + path_dy * path_dy) z_scale = 3.0 grid_distance = gridhelper.horizontalDistance( self.origin, self.destination) * z_scale if (distance < self.speed) or (grid_distance == 0): # pop to the destination self.map_object.moveInstance(self.destination) return False else: g = 5.0 / z_scale d2 = pow(self.trajectory.range, 4) - g * (g * pow(grid_distance, 2) + 2 * path_dz * pow(self.trajectory.range, 2)) angle1 = atan((pow(self.trajectory.range, 2) - sqrt(d2)) / (g * grid_distance)) # normal partial movement instance_coords.x += (dx / distance) * self.speed instance_coords.y += (dy / distance) * self.speed # z calculation instance_coords.z = self.origin.z + grid_distance * ( 1 - distance / path_distance) * tan(angle1) - g * pow( grid_distance * (1 - distance / path_distance), 2) / (2 * pow(self.trajectory.range * cos(angle1), 2)) + 2 self.map_object.moveInstance(instance_coords) return True
def follow(self): # fixed?: inconsistent speed because of using exact layer coordinates instead of map coordinates # there's also a rotation problem at some angles, probably for the same reason if self.next >= len(self.path): # print "Warning: Next node out of range. Path:" # for node in self.path: # print node.coords.x, node.coords.y, node.coords.z return False grid = self.map_object.visual.instance.getLocation().getLayer( ).getCellGrid() next_node = self.path[self.next] next_node_coords = grid.toMapCoordinates(next_node.coords) instance_coords = self.map_object.visual.instance.getLocation( ).getMapCoordinates() prev_node = self.path[self.next - 1] prev_node_coords = grid.toMapCoordinates(prev_node.coords) # distance from the current partial position to the target node dx = (next_node_coords.x - instance_coords.x) dy = (next_node_coords.y - instance_coords.y) dz = (next_node_coords.z - instance_coords.z) distance = sqrt(dx * dx + dy * dy) # distance from the previous node to the target node cell_dx = (next_node_coords.x - prev_node_coords.x) cell_dy = (next_node_coords.y - prev_node_coords.y) cell_dz = (next_node_coords.z - prev_node_coords.z) cell_distance = sqrt(cell_dx * cell_dx + cell_dy * cell_dy) if distance < self.speed: # pop to next node self.next += 1 self.map_object.visual.moveInstance(next_node.coords) continue_route = (self.next < len(self.path)) if continue_route: #print "A:", fife.getAngleBetween(instance_coords, grid.toMapCoordinates(self.path[self.next].coords)) self.map_object.visual.instance.setRotation( fife.getAngleBetween( instance_coords, grid.toMapCoordinates(self.path[self.next].coords))) return continue_route else: #print "B:", fife.getAngleBetween(instance_coords, next_node_coords) self.map_object.visual.instance.setRotation( fife.getAngleBetween(instance_coords, next_node_coords)) # normal partial movement instance_coords.x += (dx / distance) * self.speed instance_coords.y += (dy / distance) * self.speed # z calculation if gridhelper.horizontalDistance(prev_node.coords, next_node.coords) == 1: # walking between adjacent nodes if cell_dz > 0: # uphill if (distance / cell_distance) < 0.5: # flat movement on target tile instance_coords.z = next_node_coords.z else: # jumping up from previous tile instance_coords.z = prev_node_coords.z + cell_dz - 4 * ( 0.5 - distance / cell_distance) * ( 0.5 - distance / cell_distance) * cell_dz elif cell_dz < 0: # downhill if (distance / cell_distance) < 0.5: # jumping down on target tile instance_coords.z = prev_node_coords.z + 4 * ( 0.5 - distance / cell_distance) * ( 0.5 - distance / cell_distance) * cell_dz elif gridhelper.horizontalDistance(prev_node_coords, next_node_coords) == 2: # jumping 2 tiles if (distance / cell_distance) > 0.5: # up instance_coords.z = prev_node_coords.z + 1 - 4 * ( 0.5 - distance / cell_distance) * ( 0.5 - distance / cell_distance) else: # down instance_coords.z = prev_node_coords.z + 1 + 4 * ( 0.5 - distance / cell_distance) * ( 0.5 - distance / cell_distance) * (cell_dz - 1) self.map_object.visual.moveInstance( grid.toExactLayerCoordinates(instance_coords)) return True
def scoreAction(self, action, target, world): score = 0 if action.name == "Kick": score += self.scoreDamage(DamagePacket(world.current_character_turn, target, 6, "kinetic", "push")) elif action.name == "Throw Stone": score += self.scoreDamage(DamagePacket(world.current_character_turn, target, 4, "kinetic")) elif action.name == "Fireball": targeting_rules = TargetingRules(action.targeting_rules.trajectory, action.targeting_rules.area, False, True, False, False) targets = world.getTargetsInArea(world.current_character_turn, target, targeting_rules) for aoe_target in targets: if type(aoe_target) == TacticsCharacter: if aoe_target.cur_HP > 0: score += self.scoreDamage(DamagePacket(world.current_character_turn, aoe_target, 5 - 2 * gridhelper.horizontalDistance(target.coords, aoe_target.coords), "fire", "burn")) elif action.name == "Cone of Cold": targeting_rules = TargetingRules(action.targeting_rules.trajectory, action.targeting_rules.area, False, True, False, False) targets = world.getTargetsInArea(world.current_character_turn, target, targeting_rules) for aoe_target in targets: if type(aoe_target) == TacticsCharacter: if aoe_target.cur_HP > 0: score += self.scoreDamage(DamagePacket(world.current_character_turn, aoe_target, 4, "cold", "freeze")) self.actions_considered += 1 return score * uniform(1, 1.1)
def getTargetsInArea(self, source, target, targeting_rules): """Return all objects affected by an action performed by source targeting target under targeting_rules. Source is a character and target is a character, tile or obstacle.""" targets = [] if targeting_rules.area.type == targeting_rules.area.AREA_SINGLE: targets.append(target) if targeting_rules.area.type == targeting_rules.area.AREA_CIRCLE: if targeting_rules.can_target_tile: for tile in self.tiles: if gridhelper.horizontalDistance( target.coords, tile.coords) < targeting_rules.area.radius and ( (target.coords.z - tile.coords.z) <= targeting_rules.area.range_down) and ( (tile.coords.z - target.coords.z) <= targeting_rules.area.range_up): if self.isLOS(target, tile): targets.append(tile) if targeting_rules.can_target_other: for char in self.characters: if gridhelper.horizontalDistance( target.coords, char.coords) < targeting_rules.area.radius and ( (target.coords.z - char.coords.z) <= targeting_rules.area.range_down) and ( (char.coords.z - target.coords.z) <= targeting_rules.area.range_up): if self.isLOS(target, char): targets.append(char) if targeting_rules.can_target_obstacle: for obstacle in self.obstacles: if gridhelper.horizontalDistance( target.coords, obstacle.coords ) < targeting_rules.area.radius and ( (target.coords.z - obstacle.coords.z) <= targeting_rules.area.range_down) and ( (obstacle.coords.z - target.coords.z) <= targeting_rules.area.range_up): if self.isLOS(target, obstacle): targets.append(obstacle) if targeting_rules.area.type == targeting_rules.area.AREA_LINE: targets.append(target) if targeting_rules.area.type == targeting_rules.area.AREA_CONE: if targeting_rules.can_target_tile: for tile in self.tiles: if gridhelper.horizontalDistance( source.coords, tile.coords) < targeting_rules.area.radius and ( (source.coords.z - tile.coords.z) <= targeting_rules.area.range_down) and ( (tile.coords.z - source.coords.z) <= targeting_rules.area.range_up): if (gridhelper.calcDirectionDiagonal( source.coords, target.coords) == gridhelper.calcDirection( source.coords, tile.coords)) or ( gridhelper.calcDirectionDiagonal( source.coords, target.coords) == gridhelper.calcDirectionDiagonal( source.coords, tile.coords)): if self.isLOS(source, tile): targets.append(tile) if targeting_rules.can_target_other: for char in self.characters: if gridhelper.horizontalDistance( source.coords, char.coords) < targeting_rules.area.radius and ( (source.coords.z - char.coords.z) <= targeting_rules.area.range_down) and ( (char.coords.z - source.coords.z) <= targeting_rules.area.range_up): if (gridhelper.calcDirectionDiagonal( source.coords, target.coords) == gridhelper.calcDirection( source.coords, char.coords)) or ( gridhelper.calcDirectionDiagonal( source.coords, target.coords) == gridhelper.calcDirectionDiagonal( source.coords, char.coords)): if self.isLOS(source, char): targets.append(char) if targeting_rules.can_target_obstacle: for obstacle in self.obstacles: if gridhelper.horizontalDistance( source.coords, obstacle.coords ) < targeting_rules.area.radius and ( (source.coords.z - obstacle.coords.z) <= targeting_rules.area.range_down) and ( (obstacle.coords.z - source.coords.z) <= targeting_rules.area.range_up): if (gridhelper.calcDirectionDiagonal( source.coords, target.coords) == gridhelper.calcDirection( source.coords, obstacle.coords)) or ( gridhelper.calcDirectionDiagonal( source.coords, target.coords) == gridhelper.calcDirectionDiagonal( source.coords, obstacle.coords)): if self.isLOS(source, obstacle): targets.append(obstacle) return targets