diff --git a/game/db.py b/game/db.py index 47cc07da..2a2e44c5 100644 --- a/game/db.py +++ b/game/db.py @@ -432,6 +432,22 @@ def choose_units(for_task: Task, factor: float, count: int, country: str) -> typ return list(set(suitable_unittypes[index_start:index_end])) +def unitdict_append(unit_dict: UnitsDict, unit_type: UnitType, count: int): + unit_dict[unit_type] = unit_dict.get(unit_type, 0) + 1 + + +def unitdict_split(unit_dict: UnitsDict, count: int): + buffer_dict = {} + for unit_type, unit_count in unit_dict.items(): + for _ in range(unit_count): + unitdict_append(buffer_dict, unit_type, 1) + if sum(buffer_dict.values()) >= count: + yield buffer_dict + buffer_dict = {} + + if len(buffer_dict): + yield buffer_dict + def _validate_db(): # check unit by task uniquity total_set = set() diff --git a/game/event/__init__.py b/game/event/__init__.py index ef0eb8d9..f43dc91a 100644 --- a/game/event/__init__.py +++ b/game/event/__init__.py @@ -1,5 +1,5 @@ from .event import * -from .groundintercept import * +from .frontlinecas import * from .intercept import * from .capture import * from .navalintercept import * diff --git a/game/event/frontlinecas.py b/game/event/frontlinecas.py new file mode 100644 index 00000000..07c3e610 --- /dev/null +++ b/game/event/frontlinecas.py @@ -0,0 +1,91 @@ +import math +import random + +from dcs.task import * +from dcs.vehicles import AirDefence + +from game import * +from game.event import * +from game.operation.frontlinecas import FrontlineCASOperation +from userdata.debriefing import Debriefing + + +class FrontlineCASEvent(Event): + TARGET_VARIETY = 2 + TARGET_AMOUNT_FACTOR = 0.5 + ATTACKER_AMOUNT_FACTOR = 0.4 + STRENGTH_INFLUENCE = 0.3 + SUCCESS_MIN_TARGETS = 3 + + targets = None # type: db.ArmorDict + + @property + def threat_description(self): + if not self.game.is_player_attack(self): + return "{} aicraft".format(self.from_cp.base.scramble_count(self.game.settings.multiplier, CAS)) + else: + return super(FrontlineCASEvent, self).threat_description + + def __str__(self): + return "Frontline CAS from {} at {}".format(self.from_cp, self.to_cp) + + def is_successfull(self, debriefing: Debriefing): + total_targets = sum(self.targets.values()) + destroyed_targets = 0 + for unit, count in debriefing.destroyed_units[self.defender_name].items(): + if unit in self.targets: + destroyed_targets += count + + if self.from_cp.captured: + return float(destroyed_targets) >= min(self.SUCCESS_MIN_TARGETS, total_targets) + else: + return float(destroyed_targets) < min(self.SUCCESS_MIN_TARGETS, total_targets) + + def commit(self, debriefing: Debriefing): + super(FrontlineCASEvent, self).commit(debriefing) + + if self.from_cp.captured: + if self.is_successfull(debriefing): + self.to_cp.base.affect_strength(-self.STRENGTH_INFLUENCE) + else: + self.to_cp.base.affect_strength(+self.STRENGTH_INFLUENCE) + else: + if self.is_successfull(debriefing): + self.from_cp.base.affect_strength(-self.STRENGTH_INFLUENCE) + else: + self.to_cp.base.affect_strength(-self.STRENGTH_INFLUENCE) + + def skip(self): + if self.to_cp.captured: + self.to_cp.base.affect_strength(-0.1) + + def player_attacking(self, strikegroup: db.PlaneDict, clients: db.PlaneDict): + suitable_armor_targets = db.find_unittype(PinpointStrike, self.defender_name) + random.shuffle(suitable_armor_targets) + + target_types = suitable_armor_targets[:self.TARGET_VARIETY] + typecount = max(math.floor(self.to_cp.base.assemble_count() * self.TARGET_AMOUNT_FACTOR), 1) + self.targets = {unittype: typecount for unittype in target_types} + + defense_aa_unit = random.choice(self.game.commision_unit_types(self.to_cp, AirDefence)) + self.targets[defense_aa_unit] = 1 + + suitable_armor_attackers = db.find_unittype(PinpointStrike, self.attacker_name) + random.shuffle(suitable_armor_attackers) + attacker_types = suitable_armor_attackers[:self.TARGET_VARIETY] + typecount = max(math.floor(self.from_cp.base.assemble_count() * self.ATTACKER_AMOUNT_FACTOR), 1) + attackers = {unittype: typecount for unittype in attacker_types} + + op = FrontlineCASOperation(game=self.game, + attacker_name=self.attacker_name, + defender_name=self.defender_name, + attacker_clients=clients, + defender_clients={}, + from_cp=self.from_cp, + to_cp=self.to_cp) + op.setup(target=self.targets, + attackers=attackers, + strikegroup=strikegroup) + + self.operation = op + diff --git a/game/event/groundattack.py b/game/event/groundattack.py index ad55d4d5..9b32523f 100644 --- a/game/event/groundattack.py +++ b/game/event/groundattack.py @@ -5,11 +5,11 @@ from dcs.task import * from game import * from game.event import * -from game.event.groundintercept import GroundInterceptEvent +from game.event.frontlinecas import FrontlineCASEvent from game.operation.groundattack import GroundAttackOperation -class GroundAttackEvent(GroundInterceptEvent): +class GroundAttackEvent(FrontlineCASEvent): def __str__(self): return "Destroy insurgents at {}".format(self.to_cp) diff --git a/game/event/groundintercept.py b/game/event/groundintercept.py deleted file mode 100644 index 1cac6288..00000000 --- a/game/event/groundintercept.py +++ /dev/null @@ -1,106 +0,0 @@ -import math -import random - -from dcs.task import * -from dcs.vehicles import AirDefence - -from game import * -from game.event import * -from game.operation.groundintercept import GroundInterceptOperation -from userdata.debriefing import Debriefing - - -class GroundInterceptEvent(Event): - TARGET_AMOUNT_FACTOR = 2 - TARGET_VARIETY = 2 - STRENGTH_INFLUENCE = 0.3 - SUCCESS_TARGETS_HIT_PERCENTAGE = 0.5 - - targets = None # type: db.ArmorDict - - @property - def threat_description(self): - if not self.game.is_player_attack(self): - return "{} aicraft".format(self.from_cp.base.scramble_count(self.game.settings.multiplier, CAS)) - else: - return super(GroundInterceptEvent, self).threat_description - - def __str__(self): - return "Frontline CAS from {} at {}".format(self.from_cp, self.to_cp) - - def is_successfull(self, debriefing: Debriefing): - total_targets = sum(self.targets.values()) - destroyed_targets = 0 - for unit, count in debriefing.destroyed_units[self.defender_name].items(): - if unit in self.targets: - destroyed_targets += count - - if self.from_cp.captured: - return float(destroyed_targets) / total_targets >= self.SUCCESS_TARGETS_HIT_PERCENTAGE - else: - return float(destroyed_targets) / total_targets < self.SUCCESS_TARGETS_HIT_PERCENTAGE - - def commit(self, debriefing: Debriefing): - super(GroundInterceptEvent, self).commit(debriefing) - - if self.from_cp.captured: - if self.is_successfull(debriefing): - self.to_cp.base.affect_strength(-self.STRENGTH_INFLUENCE) - else: - self.to_cp.base.affect_strength(+self.STRENGTH_INFLUENCE) - else: - if self.is_successfull(debriefing): - self.from_cp.base.affect_strength(-self.STRENGTH_INFLUENCE) - else: - self.to_cp.base.affect_strength(-self.STRENGTH_INFLUENCE) - - def skip(self): - if self.to_cp.captured: - self.to_cp.base.affect_strength(-0.1) - - def player_attacking(self, strikegroup: db.PlaneDict, clients: db.PlaneDict): - suitable_unittypes = db.find_unittype(PinpointStrike, self.defender_name) - random.shuffle(suitable_unittypes) - unittypes = suitable_unittypes[:self.TARGET_VARIETY] - typecount = max(math.floor(self.difficulty * self.TARGET_AMOUNT_FACTOR), 1) - self.targets = {unittype: typecount for unittype in unittypes} - - defense_aa_unit = random.choice(self.game.commision_unit_types(self.to_cp, AirDefence)) - self.targets[defense_aa_unit] = 1 - - op = GroundInterceptOperation(game=self.game, - attacker_name=self.attacker_name, - defender_name=self.defender_name, - attacker_clients=clients, - defender_clients={}, - from_cp=self.from_cp, - to_cp=self.to_cp) - op.setup(target=self.targets, - strikegroup=strikegroup, - interceptors={}) - - self.operation = op - - def player_defending(self, interceptors: db.PlaneDict, clients: db.PlaneDict): - suitable_unittypes = db.find_unittype(PinpointStrike, self.defender_name) - random.shuffle(suitable_unittypes) - unittypes = suitable_unittypes[:self.TARGET_VARIETY] - typecount = max(math.floor(self.difficulty * self.TARGET_AMOUNT_FACTOR), 1) - self.targets = {unittype: typecount for unittype in unittypes} - - op = GroundInterceptOperation( - self.game, - attacker_name=self.attacker_name, - defender_name=self.defender_name, - attacker_clients={}, - defender_clients=clients, - from_cp=self.from_cp, - to_cp=self.to_cp - ) - - strikegroup = self.from_cp.base.scramble_cas(self.game.settings.multiplier) - op.setup(target=self.targets, - strikegroup=strikegroup, - interceptors=interceptors) - - self.operation = op diff --git a/game/game.py b/game/game.py index b0e51337..1964b4e4 100644 --- a/game/game.py +++ b/game/game.py @@ -39,7 +39,7 @@ For the enemy events, only 1 event of each type could be generated for a turn. Events: * CaptureEvent - capture base * InterceptEvent - air intercept -* GroundInterceptEvent - frontline CAS +* FrontlineCASEvent - frontline CAS * GroundAttackEvent - destroy insurgents * NavalInterceptEvent - naval intercept * AntiAAStrikeEvent - anti-AA strike @@ -48,7 +48,7 @@ Events: EVENT_PROBABILITIES = { CaptureEvent: [100, 10], InterceptEvent: [25, 10], - GroundInterceptEvent: [25, 10], + FrontlineCASEvent: [250, 0], GroundAttackEvent: [0, 10], NavalInterceptEvent: [25, 10], AntiAAStrikeEvent: [25, 10], @@ -212,7 +212,8 @@ class Game: def pass_turn(self, no_action=False, ignored_cps: typing.Collection[ControlPoint]=None): for event in self.events: - event.skip() + if isinstance(event, UnitsDeliveryEvent): + event.skip() if not no_action: self._budget_player() diff --git a/game/operation/groundintercept.py b/game/operation/frontlinecas.py similarity index 66% rename from game/operation/groundintercept.py rename to game/operation/frontlinecas.py index 715b1b9e..3f2556b4 100644 --- a/game/operation/groundintercept.py +++ b/game/operation/frontlinecas.py @@ -1,3 +1,5 @@ +from itertools import zip_longest + from dcs.terrain import Terrain from game import db @@ -13,26 +15,29 @@ from gen.conflictgen import Conflict from .operation import Operation -class GroundInterceptOperation(Operation): +MAX_DISTANCE_BETWEEN_GROUPS = 12000 + + +class FrontlineCASOperation(Operation): + attackers = None # type: db.ArmorDict strikegroup = None # type: db.PlaneDict - interceptors = None # type: db.PlaneDict target = None # type: db.ArmorDict def setup(self, target: db.ArmorDict, - strikegroup: db.PlaneDict, - interceptors: db.PlaneDict): + attackers: db.ArmorDict, + strikegroup: db.PlaneDict): self.strikegroup = strikegroup - self.interceptors = interceptors self.target = target + self.attackers = attackers def prepare(self, terrain: Terrain, is_quick: bool): - super(GroundInterceptOperation, self).prepare(terrain, is_quick) + super(FrontlineCASOperation, self).prepare(terrain, is_quick) if self.defender_name == self.game.player: self.attackers_starting_position = None self.defenders_starting_position = None - conflict = Conflict.ground_intercept_conflict( + conflict = Conflict.frontline_cas_conflict( attacker=self.mission.country(self.attacker_name), defender=self.mission.country(self.defender_name), from_cp=self.from_cp, @@ -44,10 +49,6 @@ class GroundInterceptOperation(Operation): conflict=conflict) def generate(self): + self.armorgen.generate_vec(self.attackers, self.target) self.airgen.generate_cas_strikegroup(self.strikegroup, clients=self.attacker_clients, at=self.attackers_starting_position) - - if self.interceptors: - self.airgen.generate_defense(self.interceptors, clients=self.defender_clients, at=self.defenders_starting_position) - - self.armorgen.generate({}, self.target) - super(GroundInterceptOperation, self).generate() + super(FrontlineCASOperation, self).generate() diff --git a/gen/aircraft.py b/gen/aircraft.py index b1359219..48ee4c8c 100644 --- a/gen/aircraft.py +++ b/gen/aircraft.py @@ -242,6 +242,9 @@ class AircraftConflictGenerator: at=at and at or self._group_point(self.conflict.air_attackers_location)) waypoint = group.add_waypoint(self.conflict.position, CAS_ALTITUDE, WARM_START_AIRSPEED) + if self.conflict.is_vector: + group.add_waypoint(self.conflict.tail, CAS_ALTITUDE, WARM_START_ALTITUDE) + group.task = CAS.name self._setup_group(group, CAS, client_count) self.escort_targets.append((group, group.points.index(waypoint))) @@ -341,6 +344,10 @@ class AircraftConflictGenerator: wayp = group.add_waypoint(self.conflict.position, WARM_START_ALTITUDE, INTERCEPTION_AIRSPEED) wayp.tasks.append(EngageTargets(max_distance=INTERCEPT_MAX_DISTANCE)) + + if self.conflict.is_vector: + group.add_waypoint(self.conflict.tail, CAS_ALTITUDE, WARM_START_ALTITUDE) + self._setup_group(group, CAP, client_count) self._rtb_for(group, self.conflict.from_cp, at) diff --git a/gen/armor.py b/gen/armor.py index 2a465fec..80895b04 100644 --- a/gen/armor.py +++ b/gen/armor.py @@ -1,3 +1,5 @@ +from itertools import zip_longest + from game import db from .conflictgen import * from .naming import * @@ -25,7 +27,7 @@ class ArmorConflictGenerator: return point.random_point_within(distance, self.conflict.size * SPREAD_DISTANCE_SIZE_FACTOR) - def _generate_group(self, side: Country, unit: VehicleType, count: int, at: Point): + def _generate_group(self, side: Country, unit: VehicleType, count: int, at: Point, to: Point = None): for c in range(count): group = self.m.vehicle_group( side, @@ -34,24 +36,59 @@ class ArmorConflictGenerator: position=self._group_point(at), group_size=1, move_formation=PointAction.OffRoad) - initial_position = self.conflict.position.point_from_heading(0, 500) - wayp = group.add_waypoint(self._group_point(initial_position)) + + if not to: + to = self.conflict.position.point_from_heading(0, 500) + + wayp = group.add_waypoint(self._group_point(to)) wayp.tasks = [] + def _generate_fight_at(self, attackers: db.ArmorDict, defenders: db.ArmorDict, position: Point): + if attackers: + for type, count in attackers.items(): + self._generate_group( + side=self.conflict.attackers_side, + unit=type, + count=count, + at=position.point_from_heading(self.conflict.heading - 90, 600), + to=position) + + if defenders: + for type, count in defenders.items(): + self._generate_group( + side=self.conflict.defenders_side, + unit=type, + count=count, + at=position.point_from_heading(self.conflict.heading + 90, 600), + to=position) + def generate(self, attackers: db.ArmorDict, defenders: db.ArmorDict): for type, count in attackers.items(): self._generate_group( - side=self.conflict.attackers_side, - unit=type, - count=count, - at=self.conflict.ground_attackers_location) + side=self.conflict.attackers_side, + unit=type, + count=count, + at=self.conflict.ground_attackers_location) for type, count in defenders.items(): self._generate_group( - side=self.conflict.defenders_side, - unit=type, - count=count, - at=self.conflict.ground_defenders_location) + side=self.conflict.defenders_side, + unit=type, + count=count, + at=self.conflict.ground_defenders_location) + + def generate_vec(self, attackers: db.ArmorDict, defenders: db.ArmorDict): + defender_groups = list(db.unitdict_split(defenders, 6)) + distance_between_groups = min(self.conflict.distance / len(defender_groups), 12000) + total_distance = distance_between_groups * len(defender_groups) + + attacker_groups = list(db.unitdict_split(attackers, + int(sum(attackers.values()) / len(defender_groups)))) + + position = self.conflict.center.point_from_heading(self.conflict.opposite_heading, total_distance / 2) + for attacker_group_dict, target_group_dict in zip_longest(attacker_groups, defender_groups): + position = position.point_from_heading(self.conflict.heading, distance_between_groups) + self._generate_fight_at(attacker_group_dict, target_group_dict, position) def generate_passengers(self, count: int): unit_type = random.choice(db.find_unittype(Nothing, self.conflict.attackers_side.name)) diff --git a/gen/conflictgen.py b/gen/conflictgen.py index fefa91ce..299e6e14 100644 --- a/gen/conflictgen.py +++ b/gen/conflictgen.py @@ -65,28 +65,35 @@ class Conflict: size = None # type: int radials = None # type: typing.List[int] + heading = None # type: int + distance = None # type: int + ground_attackers_location = None # type: Point ground_defenders_location = None # type: Point air_attackers_location = None # type: Point air_defenders_location = None # type: Point def __init__(self, - position: Point, theater: ConflictTheater, from_cp: ControlPoint, to_cp: ControlPoint, attackers_side: Country, defenders_side: Country, - ground_attackers_location: Point, - ground_defenders_location: Point, - air_attackers_location: Point, - air_defenders_location: Point): + position: Point, + heading=None, + distance=None, + ground_attackers_location: Point = None, + ground_defenders_location: Point = None, + air_attackers_location: Point = None, + air_defenders_location: Point = None): self.attackers_side = attackers_side self.defenders_side = defenders_side self.from_cp = from_cp self.to_cp = to_cp self.theater = theater self.position = position + self.heading = heading + self.distance = distance self.size = to_cp.size self.radials = to_cp.radials self.ground_attackers_location = ground_attackers_location @@ -94,15 +101,57 @@ class Conflict: self.air_attackers_location = air_attackers_location self.air_defenders_location = air_defenders_location + @property + def center(self) -> Point: + return self.position.point_from_heading(self.heading, self.distance / 2) + + @property + def tail(self) -> Point: + return self.position.point_from_heading(self.heading, self.distance) + + @property + def is_vector(self) -> bool: + return self.heading is not None + + @property + def opposite_heading(self) -> int: + return _heading_sum(self.heading, 180) + @property def to_size(self): return self.to_cp.size * GROUND_DISTANCE_FACTOR @classmethod - def frontline_position(cls, from_cp: ControlPoint, to_cp: ControlPoint): + def has_frontline_between(cls, from_cp: ControlPoint, to_cp: ControlPoint) -> bool: + return from_cp.has_frontline and to_cp.has_frontline + + @classmethod + def frontline_position(cls, from_cp: ControlPoint, to_cp: ControlPoint) -> typing.Tuple[Point, int]: distance = max(from_cp.position.distance_to_point(to_cp.position) * FRONT_SMOKE_DISTANCE_FACTOR * to_cp.base.strength, FRONT_SMOKE_MIN_DISTANCE) heading = to_cp.position.heading_between_point(from_cp.position) - return to_cp.position.point_from_heading(heading, distance) + return to_cp.position.point_from_heading(heading, distance), heading + + @classmethod + def frontline_vector(cls, from_cp: ControlPoint, to_cp: ControlPoint, theater: ConflictTheater) -> typing.Tuple[Point, int, int]: + center_position, heading = cls.frontline_position(from_cp, to_cp) + + left_position = center_position + for offset in range(0, 80000, 1000): + pos = center_position.point_from_heading(_heading_sum(heading, -90), offset) + if not theater.is_on_land(pos): + break + else: + left_position = pos + + right_position = center_position + for offset in range(0, 80000, 1000): + pos = center_position.point_from_heading(_heading_sum(heading, 90), offset) + if not theater.is_on_land(pos): + break + else: + right_position = pos + + return left_position, _heading_sum(heading, 90), right_position.distance_to_point(left_position) @classmethod def _find_ground_location(cls, initial: Point, max_distance: int, heading: int, theater: ConflictTheater) -> Point: @@ -189,25 +238,44 @@ class Conflict: ) @classmethod - def ground_intercept_conflict(cls, attacker: Country, defender: Country, from_cp: ControlPoint, to_cp: ControlPoint, theater: ConflictTheater): - heading = to_cp.position.heading_between_point(from_cp.position) - initial_location = cls.frontline_position(from_cp, to_cp).random_point_within(GROUND_INTERCEPT_SPREAD) - position = Conflict._find_ground_location(initial_location, GROUND_INTERCEPT_SPREAD, heading, theater) - if not position: - heading = to_cp.find_radial(to_cp.position.heading_between_point(from_cp.position)) - position = to_cp.position.point_from_heading(heading, to_cp.size * GROUND_DISTANCE_FACTOR) + def frontline_cas_conflict(cls, attacker: Country, defender: Country, from_cp: ControlPoint, to_cp: ControlPoint, theater: ConflictTheater): + assert cls.has_frontline_between(from_cp, to_cp) + position, heading, distance = cls.frontline_vector(from_cp, to_cp, theater) return cls( position=position, + heading=heading, + distance=distance, theater=theater, from_cp=from_cp, to_cp=to_cp, attackers_side=attacker, defenders_side=defender, ground_attackers_location=None, - ground_defenders_location=position, + ground_defenders_location=None, air_attackers_location=position.point_from_heading(random.randint(*INTERCEPT_ATTACKERS_HEADING) + heading, AIR_DISTANCE), - air_defenders_location=position.point_from_heading(random.randint(*INTERCEPT_ATTACKERS_HEADING) + _opposite_heading(heading), AIR_DISTANCE) + air_defenders_location=position.point_from_heading(random.randint(*INTERCEPT_ATTACKERS_HEADING) + _opposite_heading(heading), AIR_DISTANCE), + ) + + @classmethod + def frontline_cap_conflict(cls, attacker: Country, defender: Country, from_cp: ControlPoint, to_cp: ControlPoint, theater: ConflictTheater): + assert cls.has_frontline_between(from_cp, to_cp) + position, heading, distance = cls.frontline_vector(from_cp, to_cp, theater) + defenders_distance = random.randint(distance/3, distance) + + return cls( + position=position, + heading=heading, + distance=distance, + theater=theater, + from_cp=from_cp, + to_cp=to_cp, + attackers_side=attacker, + defenders_side=defender, + ground_attackers_location=None, + ground_defenders_location=None, + air_attackers_location=position.point_from_heading(random.randint(*INTERCEPT_ATTACKERS_HEADING) + heading, AIR_DISTANCE), + air_defenders_location=position.point_from_heading(heading, max(AIR_DISTANCE, defenders_distance)), ) @classmethod @@ -261,8 +329,7 @@ class Conflict: @classmethod def transport_conflict(cls, attacker: Country, defender: Country, from_cp: ControlPoint, to_cp: ControlPoint, theater: ConflictTheater): - frontline_position = cls.frontline_position(from_cp, to_cp) - heading = to_cp.position.heading_between_point(from_cp.position) + frontline_position, heading = cls.frontline_position(from_cp, to_cp) initial_dest = frontline_position.point_from_heading(heading, TRANSPORT_FRONTLINE_DIST) dest = cls._find_ground_location(initial_dest, from_cp.position.distance_to_point(to_cp.position) / 3, heading, theater) if not dest: diff --git a/gen/visualgen.py b/gen/visualgen.py index e399d140..a59c9986 100644 --- a/gen/visualgen.py +++ b/gen/visualgen.py @@ -99,8 +99,7 @@ class VisualGenerator: def _generate_frontline_smokes(self): for from_cp, to_cp in self.game.theater.conflicts(): - heading = to_cp.position.heading_between_point(from_cp.position) - point = Conflict.frontline_position(from_cp, to_cp) + point, heading = Conflict.frontline_position(from_cp, to_cp) plane_start = point.point_from_heading(turn_heading(heading, 90), FRONT_SMOKE_LENGTH / 2) for offset in range(0, FRONT_SMOKE_LENGTH, FRONT_SMOKE_SPACING): diff --git a/theater/controlpoint.py b/theater/controlpoint.py index 9d851382..dff1deab 100644 --- a/theater/controlpoint.py +++ b/theater/controlpoint.py @@ -10,10 +10,11 @@ class ControlPoint: connected_points = [] # type: typing.List[ControlPoint] position = None # type: Point captured = False - base: None # type: theater.base.Base - at: None # type: db.StartPosition + has_frontline = True + base = None # type: theater.base.Base + at = None # type: db.StartPosition - def __init__(self, name: str, position: Point, at, radials: typing.Collection[int], size: int, importance: int): + def __init__(self, name: str, position: Point, at, radials: typing.Collection[int], size: int, importance: int, has_frontline=True): import theater.base self.name = " ".join(re.split(r" |-", name)[:2]) @@ -24,14 +25,15 @@ class ControlPoint: self.size = size self.importance = importance self.captured = False + self.has_frontline = has_frontline self.radials = radials self.connected_points = [] self.base = theater.base.Base() @classmethod - def from_airport(cls, airport: Airport, radials: typing.Collection[int], size: int, importance: int): + def from_airport(cls, airport: Airport, radials: typing.Collection[int], size: int, importance: int, has_frontline=True): assert airport - return cls(airport.name, airport.position, airport, radials, size, importance) + return cls(airport.name, airport.position, airport, radials, size, importance, has_frontline) @classmethod def carrier(cls, name: str, at: Point): diff --git a/theater/persiangulf.py b/theater/persiangulf.py index d2c9ee8f..bdac7a97 100644 --- a/theater/persiangulf.py +++ b/theater/persiangulf.py @@ -29,13 +29,13 @@ class PersianGulfTheater(ConflictTheater): fujairah = ControlPoint.from_airport(persiangulf.Fujairah_Intl, COAST_V_W, SIZE_REGULAR, 1.3) khasab = ControlPoint.from_airport(persiangulf.Khasab, LAND, SIZE_SMALL, 1.3) - sirri = ControlPoint.from_airport(persiangulf.Sirri_Island, COAST_DL_W, SIZE_REGULAR, 1.2) - abu_musa = ControlPoint.from_airport(persiangulf.Abu_Musa_Island_Airport, LAND, SIZE_SMALL, 1.0) - tunb_island = ControlPoint.from_airport(persiangulf.Tunb_Island_AFB, [0, 270, 330], SIZE_REGULAR, 1.1) - tunb_kochak = ControlPoint.from_airport(persiangulf.Tunb_Kochak, [135, 180], SIZE_SMALL, IMPORTANCE_HIGH) + sirri = ControlPoint.from_airport(persiangulf.Sirri_Island, COAST_DL_W, SIZE_REGULAR, 1.2, has_frontline=False) + abu_musa = ControlPoint.from_airport(persiangulf.Abu_Musa_Island_Airport, LAND, SIZE_SMALL, 1.0, has_frontline=False) + tunb_island = ControlPoint.from_airport(persiangulf.Tunb_Island_AFB, [0, 270, 330], SIZE_REGULAR, 1.1, has_frontline=False) + tunb_kochak = ControlPoint.from_airport(persiangulf.Tunb_Kochak, [135, 180], SIZE_SMALL, IMPORTANCE_HIGH, has_frontline=False) bandar_lengeh = ControlPoint.from_airport(persiangulf.Bandar_Lengeh, [270, 315, 0, 45], SIZE_SMALL, IMPORTANCE_HIGH) - qeshm = ControlPoint.from_airport(persiangulf.Qeshm_Island, [270, 315, 0, 45, 90, 135, 180], SIZE_SMALL, 1.1) + qeshm = ControlPoint.from_airport(persiangulf.Qeshm_Island, [270, 315, 0, 45, 90, 135, 180], SIZE_SMALL, 1.1, has_frontline=False) havadarya = ControlPoint.from_airport(persiangulf.Havadarya, COAST_DL_W, SIZE_REGULAR, IMPORTANCE_HIGH) bandar_abbas = ControlPoint.from_airport(persiangulf.Bandar_Abbas_Intl, LAND, SIZE_BIG, IMPORTANCE_HIGH) diff --git a/ui/eventmenu.py b/ui/eventmenu.py index 6538d9e6..e1b2b81a 100644 --- a/ui/eventmenu.py +++ b/ui/eventmenu.py @@ -190,8 +190,8 @@ class EventMenu(Menu): else: e.player_defending(escort=scrambled_aircraft, clients=scrambled_clients) - elif type(self.event) is GroundInterceptEvent: - e = self.event # type: GroundInterceptEvent + elif type(self.event) is FrontlineCASEvent: + e = self.event # type: FrontlineCASEvent if self.game.is_player_attack(self.event): e.player_attacking(strikegroup=scrambled_aircraft, clients=scrambled_clients) else: