def _portion_of_fleet_needed_here(self): """Calculate the portion of the fleet needed in target system considering enemy forces.""" # TODO check rating against planets if assertion_fails(self.type in COMBAT_MISSION_TYPES, msg=str(self)): return 0 if assertion_fails(self.target and self.target.id != INVALID_ID, msg=str(self)): return 0 system_id = self.target.id aistate = get_aistate() local_defenses = MilitaryAI.get_my_defense_rating_in_system(system_id) potential_threat = CombatRatingsAI.combine_ratings( MilitaryAI.get_system_local_threat(system_id), MilitaryAI.get_system_neighbor_threat(system_id) ) universe = fo.getUniverse() system = universe.getSystem(system_id) # tally planetary defenses total_defense = total_shields = 0 for planet_id in system.planetIDs: planet = universe.getPlanet(planet_id) total_defense += planet.currentMeterValue(fo.meterType.defense) total_shields += planet.currentMeterValue(fo.meterType.shield) planetary_ratings = total_defense * (total_shields + total_defense) potential_threat += planetary_ratings # TODO: rewrite to return min rating vs planets as well # consider safety factor just once here rather than everywhere below safety_factor = aistate.character.military_safety_factor() potential_threat *= safety_factor fleet_rating = CombatRatingsAI.get_fleet_rating(self.fleet.id) return CombatRatingsAI.rating_needed(potential_threat, local_defenses) / float(fleet_rating)
def issue_fleet_orders(self): """issues AIFleetOrders which can be issued in system and moves to next one if is possible""" # TODO: priority order_completed = True debug("\nChecking orders for fleet %s (on turn %d), with mission type %s and target %s", self.fleet.get_object(), fo.currentTurn(), self.type or 'No mission', self.target or 'No Target') if MissionType.INVASION == self.type: self._check_retarget_invasion() just_issued_move_order = False last_move_target_id = INVALID_ID # Note: the following abort check somewhat assumes only one major mission type for fleet_order in self.orders: if (isinstance(fleet_order, (OrderColonize, OrderOutpost, OrderInvade)) and self._check_abort_mission(fleet_order)): return aistate = get_aistate() for fleet_order in self.orders: if just_issued_move_order and self.fleet.get_object().systemID != last_move_target_id: # having just issued a move order, we will normally stop issuing orders this turn, except that if there # are consecutive move orders we will consider moving through the first destination rather than stopping # Without the below noinspection directive, PyCharm is concerned about the 2nd part of the test # noinspection PyTypeChecker if (not isinstance(fleet_order, OrderMove) or self.need_to_pause_movement(last_move_target_id, fleet_order)): break debug("Checking order: %s" % fleet_order) self.check_mergers(context=str(fleet_order)) if fleet_order.can_issue_order(verbose=False): # only move if all other orders completed if isinstance(fleet_order, OrderMove) and order_completed: debug("Issuing fleet order %s" % fleet_order) fleet_order.issue_order() just_issued_move_order = True last_move_target_id = fleet_order.target.id elif not isinstance(fleet_order, OrderMove): debug("Issuing fleet order %s" % fleet_order) fleet_order.issue_order() else: debug("NOT issuing (even though can_issue) fleet order %s" % fleet_order) status_words = tuple(["not", ""][_s] for _s in [fleet_order.order_issued, fleet_order.executed]) debug("Order %s issued and %s fully executed." % status_words) if not fleet_order.executed: order_completed = False else: # check that we're not held up by a Big Monster if fleet_order.order_issued: # A previously issued order that wasn't instantly executed must have had cirumstances change so that # the order can't currently be reissued (or perhaps simply a savegame has been reloaded on the same # turn the order was issued). if not fleet_order.executed: order_completed = False # Go on to the next order. continue debug("CAN'T issue fleet order %s because:" % fleet_order) fleet_order.can_issue_order(verbose=True) if isinstance(fleet_order, OrderMove): this_system_id = fleet_order.target.id this_status = aistate.systemStatus.setdefault(this_system_id, {}) threat_threshold = fo.currentTurn() * MilitaryAI.cur_best_mil_ship_rating() / 4.0 if this_status.get('monsterThreat', 0) > threat_threshold: # if this move order is not this mil fleet's final destination, and blocked by Big Monster, # release and hope for more effective reassignment if (self.type not in (MissionType.MILITARY, MissionType.SECURE) or fleet_order != self.orders[-1]): debug("Aborting mission due to being blocked by Big Monster at system %d, threat %d" % ( this_system_id, aistate.systemStatus[this_system_id]['monsterThreat'])) debug("Full set of orders were:") for this_order in self.orders: debug(" - %s" % this_order) self.clear_fleet_orders() self.clear_target() return break # do not order the next order until this one is finished. else: # went through entire order list if order_completed: debug("Final order is completed") orders = self.orders last_order = orders[-1] if orders else None universe = fo.getUniverse() if last_order and isinstance(last_order, OrderColonize): planet = universe.getPlanet(last_order.target.id) sys_partial_vis_turn = get_partial_visibility_turn(planet.systemID) planet_partial_vis_turn = get_partial_visibility_turn(planet.id) if (planet_partial_vis_turn == sys_partial_vis_turn and not planet.initialMeterValue(fo.meterType.population)): warn("Fleet %d has tentatively completed its " "colonize mission but will wait to confirm population." % self.fleet.id) debug(" Order details are %s" % last_order) debug(" Order is valid: %s; issued: %s; executed: %s" % ( last_order.is_valid(), last_order.order_issued, last_order.executed)) if not last_order.is_valid(): source_target = last_order.fleet target_target = last_order.target debug(" source target validity: %s; target target validity: %s " % ( bool(source_target), bool(target_target))) return # colonize order must not have completed yet clear_all = True last_sys_target = INVALID_ID if last_order and isinstance(last_order, OrderMilitary): last_sys_target = last_order.target.id # not doing this until decide a way to release from a SECURE mission # if (MissionType.SECURE == self.type) or secure_targets = set(AIstate.colonyTargetedSystemIDs + AIstate.outpostTargetedSystemIDs + AIstate.invasionTargetedSystemIDs) if last_sys_target in secure_targets: # consider a secure mission if last_sys_target in AIstate.colonyTargetedSystemIDs: secure_type = "Colony" elif last_sys_target in AIstate.outpostTargetedSystemIDs: secure_type = "Outpost" elif last_sys_target in AIstate.invasionTargetedSystemIDs: secure_type = "Invasion" else: secure_type = "Unidentified" debug("Fleet %d has completed initial stage of its mission " "to secure system %d (targeted for %s), " "may release a portion of ships" % (self.fleet.id, last_sys_target, secure_type)) clear_all = False # for PROTECT_REGION missions, only release fleet if no more threat if self.type == MissionType.PROTECT_REGION: # use military logic code below to determine if can release # any or even all of the ships. clear_all = False last_sys_target = self.target.id debug("Check if PROTECT_REGION mission with target %d is finished.", last_sys_target) fleet_id = self.fleet.id if clear_all: if orders: debug("Fleet %d has completed its mission; clearing all orders and targets." % self.fleet.id) debug("Full set of orders were:") for this_order in orders: debug("\t\t %s" % this_order) self.clear_fleet_orders() self.clear_target() if aistate.get_fleet_role(fleet_id) in (MissionType.MILITARY, MissionType.SECURE): allocations = MilitaryAI.get_military_fleets(mil_fleets_ids=[fleet_id], try_reset=False, thisround="Fleet %d Reassignment" % fleet_id) if allocations: MilitaryAI.assign_military_fleets_to_systems(use_fleet_id_list=[fleet_id], allocations=allocations) else: # no orders debug("No Current Orders") else: potential_threat = CombatRatingsAI.combine_ratings( MilitaryAI.get_system_local_threat(last_sys_target), MilitaryAI.get_system_neighbor_threat(last_sys_target) ) threat_present = potential_threat > 0 debug("Fleet threat present? %s", threat_present) target_system = universe.getSystem(last_sys_target) if not threat_present and target_system: for pid in target_system.planetIDs: planet = universe.getPlanet(pid) if (planet and planet.owner != fo.empireID() and planet.currentMeterValue(fo.meterType.maxDefense) > 0): debug("Found local planetary threat: %s", planet) threat_present = True break if not threat_present: debug("No current threat in target system; releasing a portion of ships.") # at least first stage of current task is done; # release extra ships for potential other deployments new_fleets = FleetUtilsAI.split_fleet(self.fleet.id) if self.type == MissionType.PROTECT_REGION: self.clear_fleet_orders() self.clear_target() new_fleets.append(self.fleet.id) else: debug("Threat remains in target system; Considering to release some ships.") new_fleets = [] fleet_portion_to_remain = self._portion_of_fleet_needed_here() if fleet_portion_to_remain > 1: debug("Can not release fleet yet due to large threat.") elif fleet_portion_to_remain > 0: debug("Not all ships are needed here - considering releasing a few") fleet_remaining_rating = CombatRatingsAI.get_fleet_rating(fleet_id) fleet_min_rating = fleet_portion_to_remain * fleet_remaining_rating debug("Starting rating: %.1f, Target rating: %.1f", fleet_remaining_rating, fleet_min_rating) allowance = CombatRatingsAI.rating_needed(fleet_remaining_rating, fleet_min_rating) debug("May release ships with total rating of %.1f", allowance) ship_ids = list(self.fleet.get_object().shipIDs) for ship_id in ship_ids: ship_rating = CombatRatingsAI.get_ship_rating(ship_id) debug("Considering to release ship %d with rating %.1f", ship_id, ship_rating) if ship_rating > allowance: debug("Remaining rating insufficient. Not released.") continue debug("Splitting from fleet.") new_fleet_id = FleetUtilsAI.split_ship_from_fleet(fleet_id, ship_id) if assertion_fails(new_fleet_id and new_fleet_id != INVALID_ID): break new_fleets.append(new_fleet_id) fleet_remaining_rating = CombatRatingsAI.rating_difference( fleet_remaining_rating, ship_rating) allowance = CombatRatingsAI.rating_difference( fleet_remaining_rating, fleet_min_rating) debug("Remaining fleet rating: %.1f - Allowance: %.1f", fleet_remaining_rating, allowance) if new_fleets: aistate.get_fleet_role(fleet_id, force_new=True) aistate.update_fleet_rating(fleet_id) aistate.ensure_have_fleet_missions(new_fleets) else: debug("Planetary defenses are deemed sufficient. Release fleet.") new_fleets = FleetUtilsAI.split_fleet(self.fleet.id) new_military_fleets = [] for fleet_id in new_fleets: if aistate.get_fleet_role(fleet_id) in COMBAT_MISSION_TYPES: new_military_fleets.append(fleet_id) allocations = [] if new_military_fleets: allocations = MilitaryAI.get_military_fleets( mil_fleets_ids=new_military_fleets, try_reset=False, thisround="Fleet Reassignment %s" % new_military_fleets ) if allocations: MilitaryAI.assign_military_fleets_to_systems(use_fleet_id_list=new_military_fleets, allocations=allocations)