mirror of
https://github.com/dcs-retribution/dcs-retribution.git
synced 2025-11-10 15:41:24 +00:00
balance improvements; general fixes
This commit is contained in:
parent
002ee75a26
commit
fab2968825
@ -53,7 +53,6 @@ class Event:
|
|||||||
|
|
||||||
def commit(self, debriefing: Debriefing):
|
def commit(self, debriefing: Debriefing):
|
||||||
for country, losses in debriefing.destroyed_units.items():
|
for country, losses in debriefing.destroyed_units.items():
|
||||||
cp = None # type: ControlPoint
|
|
||||||
if country == self.attacker_name:
|
if country == self.attacker_name:
|
||||||
cp = self.from_cp
|
cp = self.from_cp
|
||||||
else:
|
else:
|
||||||
@ -69,7 +68,7 @@ class GroundInterceptEvent(Event):
|
|||||||
BONUS_BASE = 3
|
BONUS_BASE = 3
|
||||||
TARGET_AMOUNT_FACTOR = 2
|
TARGET_AMOUNT_FACTOR = 2
|
||||||
TARGET_VARIETY = 2
|
TARGET_VARIETY = 2
|
||||||
STRENGTH_INFLUENCE = 0.1
|
STRENGTH_INFLUENCE = 0.3
|
||||||
SUCCESS_TARGETS_HIT_PERCENTAGE = 0.5
|
SUCCESS_TARGETS_HIT_PERCENTAGE = 0.5
|
||||||
|
|
||||||
targets = None # type: db.ArmorDict
|
targets = None # type: db.ArmorDict
|
||||||
@ -84,7 +83,7 @@ class GroundInterceptEvent(Event):
|
|||||||
if unit in self.targets:
|
if unit in self.targets:
|
||||||
destroyed_targets += count
|
destroyed_targets += count
|
||||||
|
|
||||||
return (float(destroyed_targets) / float(total_targets)) > self.SUCCESS_TARGETS_HIT_PERCENTAGE
|
return (float(destroyed_targets) / float(total_targets)) >= self.SUCCESS_TARGETS_HIT_PERCENTAGE
|
||||||
|
|
||||||
def commit(self, debriefing: Debriefing):
|
def commit(self, debriefing: Debriefing):
|
||||||
super(GroundInterceptEvent, self).commit(debriefing)
|
super(GroundInterceptEvent, self).commit(debriefing)
|
||||||
@ -125,8 +124,8 @@ class GroundInterceptEvent(Event):
|
|||||||
|
|
||||||
class InterceptEvent(Event):
|
class InterceptEvent(Event):
|
||||||
BONUS_BASE = 5
|
BONUS_BASE = 5
|
||||||
STRENGTH_INFLUENCE = 0.25
|
STRENGTH_INFLUENCE = 0.3
|
||||||
GLOBAL_STRENGTH_INFLUENCE = 0.05
|
GLOBAL_STRENGTH_INFLUENCE = 0.3
|
||||||
AIRDEFENSE_COUNT = 3
|
AIRDEFENSE_COUNT = 3
|
||||||
|
|
||||||
transport_unit = None # type: FlyingType
|
transport_unit = None # type: FlyingType
|
||||||
@ -148,14 +147,17 @@ class InterceptEvent(Event):
|
|||||||
def commit(self, debriefing: Debriefing):
|
def commit(self, debriefing: Debriefing):
|
||||||
super(InterceptEvent, self).commit(debriefing)
|
super(InterceptEvent, self).commit(debriefing)
|
||||||
|
|
||||||
if self.is_successfull(debriefing):
|
if self.attacker_name == self.game.player:
|
||||||
if self.from_cp.is_global:
|
if self.is_successfull(debriefing):
|
||||||
for cp in self.game.theater.enemy_points():
|
self.to_cp.base.affect_strength(-self.STRENGTH_INFLUENCE)
|
||||||
cp.base.affect_strength(-self.GLOBAL_STRENGTH_INFLUENCE)
|
|
||||||
else:
|
else:
|
||||||
self.to_cp.base.affect_strength(self.STRENGTH_INFLUENCE * float(self.from_cp.captured and -1 or 1))
|
self.from_cp.base.affect_strength(-self.STRENGTH_INFLUENCE)
|
||||||
else:
|
else:
|
||||||
self.to_cp.base.affect_strength(self.STRENGTH_INFLUENCE * float(self.from_cp.captured and 1 or -1))
|
# enemy attacking
|
||||||
|
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):
|
def skip(self):
|
||||||
if self.to_cp.captured:
|
if self.to_cp.captured:
|
||||||
|
|||||||
19
game/game.py
19
game/game.py
@ -19,17 +19,17 @@ COMMISION_AMOUNTS_FACTORS = {
|
|||||||
|
|
||||||
|
|
||||||
ENEMY_INTERCEPT_PROBABILITY_BASE = 8
|
ENEMY_INTERCEPT_PROBABILITY_BASE = 8
|
||||||
ENEMY_INTERCEPT_GLOBAL_PROBABILITY_BASE = 3
|
ENEMY_INTERCEPT_GLOBAL_PROBABILITY_BASE = 5
|
||||||
ENEMY_CAPTURE_PROBABILITY_BASE = 4
|
ENEMY_CAPTURE_PROBABILITY_BASE = 4
|
||||||
|
|
||||||
PLAYER_INTERCEPT_PROBABILITY_BASE = 30
|
PLAYER_INTERCEPT_PROBABILITY_BASE = 35
|
||||||
PLAYER_GROUNDINTERCEPT_PROBABILITY_BASE = 30
|
PLAYER_GROUNDINTERCEPT_PROBABILITY_BASE = 35
|
||||||
|
|
||||||
PLAYER_INTERCEPT_GLOBAL_PROBABILITY_BASE = 10
|
PLAYER_INTERCEPT_GLOBAL_PROBABILITY_BASE = 20
|
||||||
PLAYER_INTERCEPT_GLOBAL_PROBABILITY_LOG = 2
|
PLAYER_INTERCEPT_GLOBAL_PROBABILITY_LOG = 2
|
||||||
|
|
||||||
PLAYER_BUDGET_INITIAL = 90
|
PLAYER_BUDGET_INITIAL = 90
|
||||||
PLAYER_BUDGET_BASE = 30
|
PLAYER_BUDGET_BASE = 20
|
||||||
PLAYER_BUDGET_IMPORTANCE_LOG = 2
|
PLAYER_BUDGET_IMPORTANCE_LOG = 2
|
||||||
|
|
||||||
AWACS_BUDGET_COST = 4
|
AWACS_BUDGET_COST = 4
|
||||||
@ -60,11 +60,14 @@ class Game:
|
|||||||
to_cp=to_cp,
|
to_cp=to_cp,
|
||||||
game=self))
|
game=self))
|
||||||
|
|
||||||
def _generate_enemy_caps(self):
|
def _generate_enemy_caps(self, ignored_cps: typing.Collection[ControlPoint] = None):
|
||||||
for from_cp, to_cp in self.theater.conflicts(False):
|
for from_cp, to_cp in self.theater.conflicts(False):
|
||||||
if from_cp.base.total_planes == 0 or from_cp.base.total_armor == 0:
|
if from_cp.base.total_planes == 0 or from_cp.base.total_armor == 0:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
if ignored_cps and to_cp in ignored_cps:
|
||||||
|
continue
|
||||||
|
|
||||||
if self._roll(ENEMY_CAPTURE_PROBABILITY_BASE, from_cp.base.strength):
|
if self._roll(ENEMY_CAPTURE_PROBABILITY_BASE, from_cp.base.strength):
|
||||||
self.events.append(CaptureEvent(attacker_name=self.enemy,
|
self.events.append(CaptureEvent(attacker_name=self.enemy,
|
||||||
defender_name=self.player,
|
defender_name=self.player,
|
||||||
@ -191,7 +194,7 @@ class Game:
|
|||||||
def is_player_attack(self, event: Event):
|
def is_player_attack(self, event: Event):
|
||||||
return event.attacker_name == self.player
|
return event.attacker_name == self.player
|
||||||
|
|
||||||
def pass_turn(self, no_action=False):
|
def pass_turn(self, no_action=False, ignored_cps: typing.Collection[ControlPoint]=None):
|
||||||
for event in self.events:
|
for event in self.events:
|
||||||
event.skip()
|
event.skip()
|
||||||
|
|
||||||
@ -202,7 +205,7 @@ class Game:
|
|||||||
|
|
||||||
self.events = [] # type: typing.List[Event]
|
self.events = [] # type: typing.List[Event]
|
||||||
self._fill_cap_events()
|
self._fill_cap_events()
|
||||||
self._generate_enemy_caps()
|
self._generate_enemy_caps(ignored_cps=ignored_cps)
|
||||||
self._generate_interceptions()
|
self._generate_interceptions()
|
||||||
self._generate_globalinterceptions()
|
self._generate_globalinterceptions()
|
||||||
self._generate_groundinterceptions()
|
self._generate_groundinterceptions()
|
||||||
|
|||||||
@ -128,10 +128,14 @@ class CaptureOperation(Operation):
|
|||||||
if self.game.player == self.defender_name:
|
if self.game.player == self.defender_name:
|
||||||
self.attackers_starting_position = None
|
self.attackers_starting_position = None
|
||||||
|
|
||||||
|
conflict = Conflict.capture_conflict(
|
||||||
|
attacker=self.mission.country(self.attacker_name),
|
||||||
|
defender=self.mission.country(self.defender_name),
|
||||||
|
from_cp=self.from_cp,
|
||||||
|
to_cp=self.to_cp
|
||||||
|
)
|
||||||
self.initialize(mission=self.mission,
|
self.initialize(mission=self.mission,
|
||||||
conflict=self.to_cp.conflict_attack(self.from_cp,
|
conflict=conflict)
|
||||||
self.mission.country(self.attacker_name),
|
|
||||||
self.mission.country(self.defender_name)))
|
|
||||||
|
|
||||||
def generate(self):
|
def generate(self):
|
||||||
self.armorgen.generate(self.attack, self.defense)
|
self.armorgen.generate(self.attack, self.defense)
|
||||||
@ -203,7 +207,8 @@ class GroundInterceptOperation(Operation):
|
|||||||
attacker=self.mission.country(self.attacker_name),
|
attacker=self.mission.country(self.attacker_name),
|
||||||
defender=self.mission.country(self.defender_name),
|
defender=self.mission.country(self.defender_name),
|
||||||
heading=self.to_cp.position.heading_between_point(self.from_cp.position),
|
heading=self.to_cp.position.heading_between_point(self.from_cp.position),
|
||||||
cp=self.to_cp
|
from_cp=self.from_cp,
|
||||||
|
to_cp=self.to_cp
|
||||||
)
|
)
|
||||||
|
|
||||||
self.initialize(mission=self.mission,
|
self.initialize(mission=self.mission,
|
||||||
|
|||||||
@ -6,10 +6,11 @@ from .naming import *
|
|||||||
|
|
||||||
from dcs.mission import *
|
from dcs.mission import *
|
||||||
|
|
||||||
DISTANCE_FACTOR = 4, 5
|
DISTANCE_FACTOR = 2, 4
|
||||||
EXTRA_AA_MIN_DISTANCE = 35000
|
EXTRA_AA_MIN_DISTANCE = 35000
|
||||||
EXTRA_AA_POSITION_FROM_CP = 550
|
EXTRA_AA_POSITION_FROM_CP = 550
|
||||||
|
|
||||||
|
|
||||||
class AAConflictGenerator:
|
class AAConflictGenerator:
|
||||||
def __init__(self, mission: Mission, conflict: Conflict):
|
def __init__(self, mission: Mission, conflict: Conflict):
|
||||||
self.m = mission
|
self.m = mission
|
||||||
|
|||||||
@ -20,9 +20,9 @@ TRANSPORT_LANDING_ALT = 500
|
|||||||
|
|
||||||
INTERCEPTION_ALT = 3600
|
INTERCEPTION_ALT = 3600
|
||||||
CAS_ALTITUDE = 1000
|
CAS_ALTITUDE = 1000
|
||||||
|
RTB_ALTITUDE = 1000
|
||||||
|
|
||||||
INTERCEPT_MAX_DISTANCE_FACTOR = 15
|
INTERCEPT_MAX_DISTANCE = 80000
|
||||||
|
|
||||||
|
|
||||||
class AircraftConflictGenerator:
|
class AircraftConflictGenerator:
|
||||||
escort_targets = [] # type: typing.List[PlaneGroup]
|
escort_targets = [] # type: typing.List[PlaneGroup]
|
||||||
@ -54,7 +54,7 @@ class AircraftConflictGenerator:
|
|||||||
count -= group_size
|
count -= group_size
|
||||||
client_count -= client_size
|
client_count -= client_size
|
||||||
|
|
||||||
def _setup_group(self, group: FlyingGroup, for_task: Task):
|
def _setup_group(self, group: FlyingGroup, for_task: Task, clients: db.PlaneDict):
|
||||||
did_load_loadout = False
|
did_load_loadout = False
|
||||||
unit_type = group.units[0].unit_type
|
unit_type = group.units[0].unit_type
|
||||||
if unit_type in db.PLANE_PAYLOAD_OVERRIDES:
|
if unit_type in db.PLANE_PAYLOAD_OVERRIDES:
|
||||||
@ -77,11 +77,19 @@ class AircraftConflictGenerator:
|
|||||||
for unit_instance in group.units:
|
for unit_instance in group.units:
|
||||||
unit_instance.livery_id = db.PLANE_LIVERY_OVERRIDES[unit_type]
|
unit_instance.livery_id = db.PLANE_LIVERY_OVERRIDES[unit_type]
|
||||||
|
|
||||||
|
single_client = sum(clients.values()) == 1
|
||||||
|
client_count = clients.get(unit_type, 0)
|
||||||
|
for idx in range(min(client_count, len(group.units))):
|
||||||
|
if single_client:
|
||||||
|
group.units[idx].set_player()
|
||||||
|
else:
|
||||||
|
group.units[idx].set_client()
|
||||||
|
|
||||||
def _generate_at_airport(self, name: str, side: Country, unit_type: FlyingType, count: int, client_count: int, airport: Airport = None) -> FlyingGroup:
|
def _generate_at_airport(self, name: str, side: Country, unit_type: FlyingType, count: int, client_count: int, airport: Airport = None) -> FlyingGroup:
|
||||||
assert count > 0
|
assert count > 0
|
||||||
assert unit is not None
|
assert unit is not None
|
||||||
|
|
||||||
group = self.m.flight_group_from_airport(
|
return self.m.flight_group_from_airport(
|
||||||
country=side,
|
country=side,
|
||||||
name=name,
|
name=name,
|
||||||
aircraft_type=unit_type,
|
aircraft_type=unit_type,
|
||||||
@ -91,16 +99,11 @@ class AircraftConflictGenerator:
|
|||||||
group_size=count,
|
group_size=count,
|
||||||
parking_slots=None)
|
parking_slots=None)
|
||||||
|
|
||||||
for idx in range(client_count):
|
|
||||||
group.units[idx].set_client()
|
|
||||||
|
|
||||||
return group
|
|
||||||
|
|
||||||
def _generate_inflight(self, name: str, side: Country, unit_type: FlyingType, count: int, client_count: int, at: Point) -> FlyingGroup:
|
def _generate_inflight(self, name: str, side: Country, unit_type: FlyingType, count: int, client_count: int, at: Point) -> FlyingGroup:
|
||||||
assert count > 0
|
assert count > 0
|
||||||
assert unit is not None
|
assert unit is not None
|
||||||
|
|
||||||
group = self.m.flight_group(
|
return self.m.flight_group(
|
||||||
country=side,
|
country=side,
|
||||||
name=name,
|
name=name,
|
||||||
aircraft_type=unit_type,
|
aircraft_type=unit_type,
|
||||||
@ -112,16 +115,11 @@ class AircraftConflictGenerator:
|
|||||||
start_type=StartType.Warm,
|
start_type=StartType.Warm,
|
||||||
group_size=count)
|
group_size=count)
|
||||||
|
|
||||||
for idx in range(client_count):
|
|
||||||
group.units[idx].set_client()
|
|
||||||
|
|
||||||
return group
|
|
||||||
|
|
||||||
def _generate_at_carrier(self, name: str, side: Country, unit_type: FlyingType, count: int, client_count: int, at: ShipGroup) -> FlyingGroup:
|
def _generate_at_carrier(self, name: str, side: Country, unit_type: FlyingType, count: int, client_count: int, at: ShipGroup) -> FlyingGroup:
|
||||||
assert count > 0
|
assert count > 0
|
||||||
assert unit is not None
|
assert unit is not None
|
||||||
|
|
||||||
group = self.m.flight_group_from_unit(
|
return self.m.flight_group_from_unit(
|
||||||
country=side,
|
country=side,
|
||||||
name=name,
|
name=name,
|
||||||
aircraft_type=unit_type,
|
aircraft_type=unit_type,
|
||||||
@ -130,11 +128,6 @@ class AircraftConflictGenerator:
|
|||||||
start_type=StartType.Warm,
|
start_type=StartType.Warm,
|
||||||
group_size=count)
|
group_size=count)
|
||||||
|
|
||||||
for idx in range(client_count):
|
|
||||||
group.units[idx].set_client()
|
|
||||||
|
|
||||||
return group
|
|
||||||
|
|
||||||
def _generate_group(self, name: str, side: Country, unit_type: FlyingType, count: int, client_count: int, at: db.StartingPosition):
|
def _generate_group(self, name: str, side: Country, unit_type: FlyingType, count: int, client_count: int, at: db.StartingPosition):
|
||||||
if isinstance(at, Point):
|
if isinstance(at, Point):
|
||||||
return self._generate_inflight(name, side, unit_type, count, client_count, at)
|
return self._generate_inflight(name, side, unit_type, count, client_count, at)
|
||||||
@ -148,10 +141,18 @@ class AircraftConflictGenerator:
|
|||||||
else:
|
else:
|
||||||
assert False
|
assert False
|
||||||
|
|
||||||
def _generate_escort(self, side: Country, units: db.PlaneDict, clients: db.PlaneDict, at: db.StartingPosition):
|
def _at_position(self, at) -> Point:
|
||||||
if len(self.escort_targets) == 0:
|
if isinstance(at, Point):
|
||||||
return
|
return at
|
||||||
|
elif isinstance(at, ShipGroup):
|
||||||
|
return at.position
|
||||||
|
elif issubclass(at, Airport):
|
||||||
|
return at.position
|
||||||
|
else:
|
||||||
|
assert False
|
||||||
|
|
||||||
|
def _generate_escort(self, side: Country, units: db.PlaneDict, clients: db.PlaneDict, at: db.StartingPosition):
|
||||||
|
groups = []
|
||||||
for flying_type, count, client_count in self._split_to_groups(units, clients):
|
for flying_type, count, client_count in self._split_to_groups(units, clients):
|
||||||
group = self._generate_group(
|
group = self._generate_group(
|
||||||
name=namegen.next_escort_group_name(),
|
name=namegen.next_escort_group_name(),
|
||||||
@ -167,11 +168,14 @@ class AircraftConflictGenerator:
|
|||||||
position = group.position # type: Point
|
position = group.position # type: Point
|
||||||
wayp = group.add_waypoint(position.point_from_heading(heading, WORKAROUND_WAYP_DIST), CAS_ALTITUDE, WARM_START_AIRSPEED)
|
wayp = group.add_waypoint(position.point_from_heading(heading, WORKAROUND_WAYP_DIST), CAS_ALTITUDE, WARM_START_AIRSPEED)
|
||||||
|
|
||||||
self._setup_group(group, CAP)
|
self._setup_group(group, CAP, clients)
|
||||||
|
|
||||||
for group in self.escort_targets:
|
for group in self.escort_targets:
|
||||||
wayp.tasks.append(EscortTaskAction(group.id, engagement_max_dist=ESCORT_MAX_DIST))
|
wayp.tasks.append(EscortTaskAction(group.id, engagement_max_dist=ESCORT_MAX_DIST))
|
||||||
|
|
||||||
|
groups.append(group)
|
||||||
|
return groups
|
||||||
|
|
||||||
def generate_cas(self, attackers: db.PlaneDict, clients: db.PlaneDict, at: db.StartingPosition = None):
|
def generate_cas(self, attackers: db.PlaneDict, clients: db.PlaneDict, at: db.StartingPosition = None):
|
||||||
assert len(self.escort_targets) == 0
|
assert len(self.escort_targets) == 0
|
||||||
|
|
||||||
@ -187,21 +191,25 @@ class AircraftConflictGenerator:
|
|||||||
|
|
||||||
group.add_waypoint(self.conflict.position, CAS_ALTITUDE, WARM_START_AIRSPEED)
|
group.add_waypoint(self.conflict.position, CAS_ALTITUDE, WARM_START_AIRSPEED)
|
||||||
group.task = CAS.name
|
group.task = CAS.name
|
||||||
self._setup_group(group, CAS)
|
self._setup_group(group, CAS, clients)
|
||||||
|
|
||||||
|
group.add_waypoint(self.conflict.from_cp.position, RTB_ALTITUDE)
|
||||||
|
|
||||||
def generate_cas_escort(self, attackers: db.PlaneDict, clients: db.PlaneDict, at: db.StartingPosition = None):
|
def generate_cas_escort(self, attackers: db.PlaneDict, clients: db.PlaneDict, at: db.StartingPosition = None):
|
||||||
self._generate_escort(
|
for g in self._generate_escort(
|
||||||
side=self.conflict.attackers_side,
|
side=self.conflict.attackers_side,
|
||||||
units=attackers,
|
units=attackers,
|
||||||
clients=clients,
|
clients=clients,
|
||||||
at=at and at or self._group_point(self.conflict.air_attackers_location))
|
at=at and at or self._group_point(self.conflict.air_attackers_location)):
|
||||||
|
g.add_waypoint(self.conflict.from_cp.position, RTB_ALTITUDE)
|
||||||
|
|
||||||
def generate_transport_escort(self, escort: db.PlaneDict, clients: db.PlaneDict, at: db.StartingPosition = None):
|
def generate_transport_escort(self, escort: db.PlaneDict, clients: db.PlaneDict, at: db.StartingPosition = None):
|
||||||
self._generate_escort(
|
for g in self._generate_escort(
|
||||||
side=self.conflict.defenders_side,
|
side=self.conflict.defenders_side,
|
||||||
units=escort,
|
units=escort,
|
||||||
clients=clients,
|
clients=clients,
|
||||||
at=at and at or self._group_point(self.conflict.air_defenders_location))
|
at=at and at or self._group_point(self.conflict.air_defenders_location)):
|
||||||
|
g.add_waypoint(self.conflict.to_cp.position, RTB_ALTITUDE)
|
||||||
|
|
||||||
def generate_defense(self, defenders: db.PlaneDict, clients: db.PlaneDict, at: db.StartingPosition = None):
|
def generate_defense(self, defenders: db.PlaneDict, clients: db.PlaneDict, at: db.StartingPosition = None):
|
||||||
for flying_type, count, client_count in self._split_to_groups(defenders, clients):
|
for flying_type, count, client_count in self._split_to_groups(defenders, clients):
|
||||||
@ -215,9 +223,11 @@ class AircraftConflictGenerator:
|
|||||||
|
|
||||||
group.task = CAP.name
|
group.task = CAP.name
|
||||||
wayp = group.add_waypoint(self.conflict.position, CAS_ALTITUDE, WARM_START_AIRSPEED)
|
wayp = group.add_waypoint(self.conflict.position, CAS_ALTITUDE, WARM_START_AIRSPEED)
|
||||||
wayp.tasks.append(dcs.task.EngageTargets(max_distance=self.conflict.size * INTERCEPT_MAX_DISTANCE_FACTOR))
|
wayp.tasks.append(dcs.task.EngageTargets(max_distance=INTERCEPT_MAX_DISTANCE))
|
||||||
wayp.tasks.append(dcs.task.OrbitAction())
|
wayp.tasks.append(dcs.task.OrbitAction())
|
||||||
self._setup_group(group, CAP)
|
self._setup_group(group, CAP, clients)
|
||||||
|
|
||||||
|
group.add_waypoint(self.conflict.to_cp.position, RTB_ALTITUDE)
|
||||||
|
|
||||||
def generate_transport(self, transport: db.PlaneDict, destination: Airport):
|
def generate_transport(self, transport: db.PlaneDict, destination: Airport):
|
||||||
assert len(self.escort_targets) == 0
|
assert len(self.escort_targets) == 0
|
||||||
@ -251,8 +261,10 @@ class AircraftConflictGenerator:
|
|||||||
|
|
||||||
heading = group.position.heading_between_point(self.conflict.position)
|
heading = group.position.heading_between_point(self.conflict.position)
|
||||||
initial_wayp = group.add_waypoint(group.position.point_from_heading(heading, WORKAROUND_WAYP_DIST), INTERCEPTION_ALT, INTERCEPTION_AIRSPEED)
|
initial_wayp = group.add_waypoint(group.position.point_from_heading(heading, WORKAROUND_WAYP_DIST), INTERCEPTION_ALT, INTERCEPTION_AIRSPEED)
|
||||||
initial_wayp.tasks.append(EngageTargets())
|
initial_wayp.tasks.append(EngageTargets(max_distance=INTERCEPT_MAX_DISTANCE))
|
||||||
|
|
||||||
wayp = group.add_waypoint(self.conflict.position, 0)
|
wayp = group.add_waypoint(self.conflict.position, 0)
|
||||||
wayp.tasks.append(EngageTargets())
|
wayp.tasks.append(EngageTargets(max_distance=INTERCEPT_MAX_DISTANCE))
|
||||||
self._setup_group(group, CAP)
|
self._setup_group(group, CAP, clients)
|
||||||
|
|
||||||
|
group.add_waypoint(self.conflict.from_cp.position, RTB_ALTITUDE)
|
||||||
|
|||||||
@ -34,7 +34,7 @@ class ArmorConflictGenerator:
|
|||||||
position=self._group_point(at),
|
position=self._group_point(at),
|
||||||
group_size=1,
|
group_size=1,
|
||||||
move_formation=PointAction.OffRoad)
|
move_formation=PointAction.OffRoad)
|
||||||
wayp = group.add_waypoint(self.conflict.position)
|
wayp = group.add_waypoint(self.conflict.position.point_from_heading(0, 500))
|
||||||
wayp.tasks = []
|
wayp.tasks = []
|
||||||
|
|
||||||
def generate(self, attackers: db.ArmorDict, defenders: db.ArmorDict):
|
def generate(self, attackers: db.ArmorDict, defenders: db.ArmorDict):
|
||||||
|
|||||||
@ -14,8 +14,8 @@ from dcs.point import *
|
|||||||
from dcs.task import *
|
from dcs.task import *
|
||||||
from dcs.country import *
|
from dcs.country import *
|
||||||
|
|
||||||
GROUND_DISTANCE_FACTOR = 2
|
GROUND_DISTANCE_FACTOR = 0.8
|
||||||
GROUNDINTERCEPT_DISTANCE_FACTOR = 6
|
GROUNDINTERCEPT_DISTANCE_FACTOR = 3
|
||||||
AIR_DISTANCE = 32000
|
AIR_DISTANCE = 32000
|
||||||
|
|
||||||
INTERCEPT_ATTACKERS_HEADING = -45, 45
|
INTERCEPT_ATTACKERS_HEADING = -45, 45
|
||||||
@ -43,6 +43,8 @@ def _heading_sum(h, a) -> int:
|
|||||||
class Conflict:
|
class Conflict:
|
||||||
attackers_side = None # type: Country
|
attackers_side = None # type: Country
|
||||||
defenders_side = None # type: Country
|
defenders_side = None # type: Country
|
||||||
|
from_cp = None # type: ControlPoint
|
||||||
|
to_cp = None # type: ControlPoint
|
||||||
position = None # type: Point
|
position = None # type: Point
|
||||||
size = None # type: int
|
size = None # type: int
|
||||||
radials = None # type: typing.List[int]
|
radials = None # type: typing.List[int]
|
||||||
@ -53,13 +55,19 @@ class Conflict:
|
|||||||
air_defenders_location = None # type: Point
|
air_defenders_location = None # type: Point
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def capture_conflict(self, attacker: Country, attack_heading: int, defender: Country, defense_heading: int, position: Point, size: int, radials: typing.Collection[int]):
|
def capture_conflict(self, attacker: Country, defender: Country, from_cp, to_cp):
|
||||||
|
attack_heading = to_cp.find_radial(to_cp.position.heading_between_point(from_cp.position))
|
||||||
|
defense_heading = to_cp.find_radial(from_cp.position.heading_between_point(to_cp.position), ignored_radial=attack_heading)
|
||||||
|
position = to_cp.position
|
||||||
|
|
||||||
instance = self()
|
instance = self()
|
||||||
instance.attackers_side = attacker
|
instance.attackers_side = attacker
|
||||||
instance.defenders_side = defender
|
instance.defenders_side = defender
|
||||||
|
instance.from_cp = from_cp
|
||||||
|
instance.to_cp = to_cp
|
||||||
instance.position = position
|
instance.position = position
|
||||||
instance.size = size
|
instance.size = to_cp.size
|
||||||
instance.radials = radials
|
instance.radials = to_cp.radials
|
||||||
|
|
||||||
instance.ground_attackers_location = instance.position.point_from_heading(attack_heading, instance.size * GROUND_DISTANCE_FACTOR)
|
instance.ground_attackers_location = instance.position.point_from_heading(attack_heading, instance.size * GROUND_DISTANCE_FACTOR)
|
||||||
instance.ground_defenders_location = instance.position.point_from_heading(defense_heading, instance.size * GROUND_DISTANCE_FACTOR)
|
instance.ground_defenders_location = instance.position.point_from_heading(defense_heading, instance.size * GROUND_DISTANCE_FACTOR)
|
||||||
@ -74,12 +82,15 @@ class Conflict:
|
|||||||
from theater.conflicttheater import SIZE_REGULAR
|
from theater.conflicttheater import SIZE_REGULAR
|
||||||
from theater.conflicttheater import ALL_RADIALS
|
from theater.conflicttheater import ALL_RADIALS
|
||||||
|
|
||||||
heading = _heading_sum(from_cp.position.heading_between_point(to_cp.position), +90)
|
heading = _heading_sum(from_cp.position.heading_between_point(to_cp.position), random.choice([-1, 1]) * random.randint(60, 100))
|
||||||
raw_distance = from_cp.position.distance_to_point(to_cp.position) / 2.5
|
|
||||||
|
raw_distance = from_cp.position.distance_to_point(to_cp.position) * 0.4
|
||||||
distance = max(min(raw_distance, INTERCEPT_MAX_DISTANCE), INTERCEPT_MIN_DISTANCE)
|
distance = max(min(raw_distance, INTERCEPT_MAX_DISTANCE), INTERCEPT_MIN_DISTANCE)
|
||||||
position = from_cp.position.point_from_heading(heading, distance)
|
position = from_cp.position.point_from_heading(heading, distance)
|
||||||
|
|
||||||
instance = self()
|
instance = self()
|
||||||
|
instance.from_cp = from_cp
|
||||||
|
instance.to_cp = to_cp
|
||||||
instance.attackers_side = attacker
|
instance.attackers_side = attacker
|
||||||
instance.defenders_side = defender
|
instance.defenders_side = defender
|
||||||
|
|
||||||
@ -93,18 +104,18 @@ class Conflict:
|
|||||||
return instance
|
return instance
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def ground_intercept_conflict(self, attacker: Country, defender: Country, heading: int, cp):
|
def ground_intercept_conflict(self, attacker: Country, defender: Country, heading: int, from_cp, to_cp):
|
||||||
from theater.conflicttheater import SIZE_SMALL
|
|
||||||
|
|
||||||
instance = self()
|
instance = self()
|
||||||
|
instance.from_cp = from_cp
|
||||||
|
instance.to_cp = to_cp
|
||||||
instance.attackers_side = attacker
|
instance.attackers_side = attacker
|
||||||
instance.defenders_side = defender
|
instance.defenders_side = defender
|
||||||
|
|
||||||
instance.position = cp.position
|
instance.position = to_cp.position
|
||||||
instance.size = cp.size
|
instance.size = to_cp.size
|
||||||
instance.radials = cp.radials
|
instance.radials = to_cp.radials
|
||||||
|
|
||||||
instance.air_attackers_location = instance.position.point_from_heading(random.randint(*INTERCEPT_ATTACKERS_HEADING) + heading, AIR_DISTANCE)
|
instance.air_attackers_location = instance.position.point_from_heading(random.randint(*INTERCEPT_ATTACKERS_HEADING) + heading, AIR_DISTANCE)
|
||||||
instance.ground_defenders_location = instance.position.point_from_heading(random.choice(cp.radials), instance.size * GROUNDINTERCEPT_DISTANCE_FACTOR)
|
instance.ground_defenders_location = instance.position.point_from_heading(random.choice(to_cp.radials), instance.size * GROUNDINTERCEPT_DISTANCE_FACTOR)
|
||||||
|
|
||||||
return instance
|
return instance
|
||||||
|
|||||||
@ -52,8 +52,8 @@ FRONT_SMOKE_LENGTH = 80000
|
|||||||
FRONT_SMOKE_SPACING = 600
|
FRONT_SMOKE_SPACING = 600
|
||||||
FRONT_SMOKE_RANDOM_SPREAD = 1200
|
FRONT_SMOKE_RANDOM_SPREAD = 1200
|
||||||
FRONT_SMOKE_TYPE_CHANCES = {
|
FRONT_SMOKE_TYPE_CHANCES = {
|
||||||
5: MassiveSmoke,
|
10: MassiveSmoke,
|
||||||
40: BigSmoke,
|
60: BigSmoke,
|
||||||
100: Smoke,
|
100: Smoke,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -119,7 +119,6 @@ class VisualGenerator:
|
|||||||
"",
|
"",
|
||||||
_type=v,
|
_type=v,
|
||||||
position=position)
|
position=position)
|
||||||
|
|
||||||
break
|
break
|
||||||
|
|
||||||
def generate(self):
|
def generate(self):
|
||||||
|
|||||||
@ -12,6 +12,7 @@ from dcs.task import *
|
|||||||
PLANES_IN_GROUP = 2
|
PLANES_IN_GROUP = 2
|
||||||
|
|
||||||
PLANES_SCRAMBLE_MIN = 4
|
PLANES_SCRAMBLE_MIN = 4
|
||||||
|
PLANES_SCRAMBLE_MAX = 8
|
||||||
PLANES_SCRAMBLE_FACTOR = 0.5
|
PLANES_SCRAMBLE_FACTOR = 0.5
|
||||||
|
|
||||||
|
|
||||||
@ -151,7 +152,7 @@ class Base:
|
|||||||
|
|
||||||
def scramble_count(self) -> int:
|
def scramble_count(self) -> int:
|
||||||
count = int(self.total_planes * PLANES_SCRAMBLE_FACTOR * self.strength)
|
count = int(self.total_planes * PLANES_SCRAMBLE_FACTOR * self.strength)
|
||||||
return min(max(count, PLANES_SCRAMBLE_MIN), self.total_planes)
|
return min(min(max(count, PLANES_SCRAMBLE_MIN), PLANES_SCRAMBLE_MAX), self.total_planes)
|
||||||
|
|
||||||
def assemble_count(self):
|
def assemble_count(self):
|
||||||
return self.total_armor * self.strength
|
return self.total_armor * self.strength
|
||||||
|
|||||||
@ -68,10 +68,11 @@ class ControlPoint:
|
|||||||
attack_radial = self.find_radial(self.position.heading_between_point(from_cp.position))
|
attack_radial = self.find_radial(self.position.heading_between_point(from_cp.position))
|
||||||
defense_radial = self.find_radial(from_cp.position.heading_between_point(self.position), ignored_radial=attack_radial)
|
defense_radial = self.find_radial(from_cp.position.heading_between_point(self.position), ignored_radial=attack_radial)
|
||||||
|
|
||||||
|
pos = self.position.point_from_heading(0, 1000)
|
||||||
return Conflict.capture_conflict(attacker=attacker,
|
return Conflict.capture_conflict(attacker=attacker,
|
||||||
attack_heading=attack_radial,
|
attack_heading=attack_radial,
|
||||||
defender=defender,
|
defender=defender,
|
||||||
defense_heading=defense_radial,
|
defense_heading=defense_radial,
|
||||||
position=self.position,
|
position=pos,
|
||||||
size=self.size,
|
size=self.size,
|
||||||
radials=self.radials)
|
radials=self.radials)
|
||||||
|
|||||||
@ -13,9 +13,9 @@ class PersianGulfTheater(ConflictTheater):
|
|||||||
(persiangulf.Sirri_Island.position.x, persiangulf.Sirri_Island.position.y): (389, 22), }
|
(persiangulf.Sirri_Island.position.x, persiangulf.Sirri_Island.position.y): (389, 22), }
|
||||||
landmap_poly = load_poly("resources\\gulflandmap.p")
|
landmap_poly = load_poly("resources\\gulflandmap.p")
|
||||||
daytime_map = {
|
daytime_map = {
|
||||||
"dawn": (5, 7),
|
"dawn": (6, 8),
|
||||||
"day": (7, 17),
|
"day": (8, 16),
|
||||||
"dusk": (17, 19),
|
"dusk": (16, 18),
|
||||||
"night": (0, 5),
|
"night": (0, 5),
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -31,7 +31,7 @@ class PersianGulfTheater(ConflictTheater):
|
|||||||
|
|
||||||
sirri = ControlPoint.from_airport(persiangulf.Sirri_Island, ALL_RADIALS, SIZE_TINY, IMPORTANCE_MEDIUM)
|
sirri = ControlPoint.from_airport(persiangulf.Sirri_Island, ALL_RADIALS, SIZE_TINY, IMPORTANCE_MEDIUM)
|
||||||
abu_musa = ControlPoint.from_airport(persiangulf.Abu_Musa_Island_Airport, ALL_RADIALS, SIZE_TINY, IMPORTANCE_MEDIUM)
|
abu_musa = ControlPoint.from_airport(persiangulf.Abu_Musa_Island_Airport, ALL_RADIALS, SIZE_TINY, IMPORTANCE_MEDIUM)
|
||||||
tunb_island = ControlPoint.from_airport(persiangulf.Tunb_Island_AFB, COAST_EW_N, SIZE_SMALL, IMPORTANCE_HIGH)
|
tunb_island = ControlPoint.from_airport(persiangulf.Tunb_Island_AFB, [0, 270, 330], SIZE_SMALL, IMPORTANCE_HIGH)
|
||||||
tunb_kochak = ControlPoint.from_airport(persiangulf.Tunb_Kochak, COAST_EW_S, SIZE_TINY, IMPORTANCE_HIGH)
|
tunb_kochak = ControlPoint.from_airport(persiangulf.Tunb_Kochak, COAST_EW_S, SIZE_TINY, IMPORTANCE_HIGH)
|
||||||
|
|
||||||
bandar_lengeh = ControlPoint.from_airport(persiangulf.Bandar_Lengeh, COAST_EW_N, SIZE_SMALL, IMPORTANCE_HIGH)
|
bandar_lengeh = ControlPoint.from_airport(persiangulf.Bandar_Lengeh, COAST_EW_N, SIZE_SMALL, IMPORTANCE_HIGH)
|
||||||
@ -72,4 +72,17 @@ class PersianGulfTheater(ConflictTheater):
|
|||||||
|
|
||||||
self.east_carrier.captured = True
|
self.east_carrier.captured = True
|
||||||
self.west_carrier.captured = True
|
self.west_carrier.captured = True
|
||||||
|
|
||||||
self.al_dhafra.captured = True
|
self.al_dhafra.captured = True
|
||||||
|
|
||||||
|
"""
|
||||||
|
Mid game:
|
||||||
|
"""
|
||||||
|
|
||||||
|
self.al_maktoum.captured = True
|
||||||
|
self.al_minhad.captured = True
|
||||||
|
self.dubai.captured = True
|
||||||
|
self.sharjah.captured = True
|
||||||
|
self.fujairah.captured = True
|
||||||
|
self.khasab.captured = True
|
||||||
|
self.sir_abu_nuayr.captured = True
|
||||||
|
|||||||
@ -7,7 +7,7 @@ UNIT_VARIETY = 3
|
|||||||
UNIT_AMOUNT_FACTOR = 16
|
UNIT_AMOUNT_FACTOR = 16
|
||||||
|
|
||||||
COUNT_BY_TASK = {
|
COUNT_BY_TASK = {
|
||||||
PinpointStrike: 12,
|
PinpointStrike: 24,
|
||||||
CAP: 16,
|
CAP: 16,
|
||||||
CAS: 8,
|
CAS: 8,
|
||||||
AirDefence: 0.5,
|
AirDefence: 0.5,
|
||||||
|
|||||||
@ -29,4 +29,7 @@ class ConfigurationMenu(Menu):
|
|||||||
OptionMenu(self.frame, self.enemy_skill_var, "Average", "Good", "High", "Excellent").grid(row=1, column=1)
|
OptionMenu(self.frame, self.enemy_skill_var, "Average", "Good", "High", "Excellent").grid(row=1, column=1)
|
||||||
|
|
||||||
Button(self.frame, text="Back", command=self.dismiss).grid(row=2, column=0, columnspan=1)
|
Button(self.frame, text="Back", command=self.dismiss).grid(row=2, column=0, columnspan=1)
|
||||||
|
Button(self.frame, text="Cheat +200m", command=self.cheat_money).grid(row=3, column=0)
|
||||||
|
|
||||||
|
def cheat_money(self):
|
||||||
|
self.game.budget += 200
|
||||||
|
|||||||
@ -68,7 +68,7 @@ class EventResultsMenu(Menu):
|
|||||||
enemy_name=self.game.enemy)
|
enemy_name=self.game.enemy)
|
||||||
|
|
||||||
self.game.finish_event(event=self.event, debriefing=debriefing)
|
self.game.finish_event(event=self.event, debriefing=debriefing)
|
||||||
self.game.pass_turn()
|
self.game.pass_turn(ignored_cps=[self.event.to_cp, ])
|
||||||
|
|
||||||
self.finished = True
|
self.finished = True
|
||||||
self.player_losses = debriefing.destroyed_units.get(self.game.player, {})
|
self.player_losses = debriefing.destroyed_units.get(self.game.player, {})
|
||||||
|
|||||||
@ -113,5 +113,8 @@ def _poll_new_debriefing_log(snapshot: typing.Dict[str, float], callback: typing
|
|||||||
|
|
||||||
|
|
||||||
def wait_for_debriefing(callback: typing.Callable):
|
def wait_for_debriefing(callback: typing.Callable):
|
||||||
|
if not os.path.exists(debriefing_directory_location()):
|
||||||
|
os.mkdir(debriefing_directory_location())
|
||||||
|
|
||||||
threading.Thread(target=_poll_new_debriefing_log, args=(_logfiles_snapshot(), callback)).start()
|
threading.Thread(target=_poll_new_debriefing_log, args=(_logfiles_snapshot(), callback)).start()
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user