mirror of
https://github.com/dcs-retribution/dcs-retribution.git
synced 2025-11-10 15:41:24 +00:00
updates to strike missions; frontline operations invalid units placement fixed; minor UI updates
This commit is contained in:
parent
e0d82da6cb
commit
4ba1dd87e8
@ -17,7 +17,7 @@ from game.game import Game
|
||||
from theater import start_generator
|
||||
from userdata import persistency, logging as logging_module
|
||||
|
||||
assert len(sys.argv) == 3, "__init__.py should be started with two mandatory arguments: %UserProfile% location and application version"
|
||||
assert len(sys.argv) >= 3, "__init__.py should be started with two mandatory arguments: %UserProfile% location and application version"
|
||||
|
||||
persistency.setup(sys.argv[1])
|
||||
dcs.planes.FlyingType.payload_dirs = [os.path.join(os.path.dirname(os.path.realpath(__file__)), "resources\\payloads")]
|
||||
@ -36,6 +36,9 @@ def is_version_compatible(save_version):
|
||||
current_version = VERSION_STRING.split(".")
|
||||
save_version = save_version.split(".")
|
||||
|
||||
if "--ignore-save" in sys.argv:
|
||||
return False
|
||||
|
||||
if current_version[:2] == save_version[:2]:
|
||||
return True
|
||||
|
||||
|
||||
@ -32,6 +32,7 @@ class BaseAttackEvent(Event):
|
||||
if self.is_successfull(debriefing):
|
||||
if self.from_cp.captured:
|
||||
self.to_cp.captured = True
|
||||
self.to_cp.ground_objects = []
|
||||
self.to_cp.base.filter_units(db.UNIT_BY_COUNTRY[self.attacker_name])
|
||||
|
||||
self.to_cp.base.affect_strength(+self.STRENGTH_RECOVERY)
|
||||
|
||||
11
game/game.py
11
game/game.py
@ -127,7 +127,10 @@ class Game:
|
||||
if event_class == BaseAttackEvent and enemy_cp.base.strength > PLAYER_BASEATTACK_THRESHOLD:
|
||||
pass
|
||||
else:
|
||||
self.events.append(event_class(self.player, self.enemy, player_cp, enemy_cp, self))
|
||||
if event_class == StrikeEvent and not enemy_cp.ground_objects:
|
||||
pass
|
||||
else:
|
||||
self.events.append(event_class(self.player, self.enemy, player_cp, enemy_cp, self))
|
||||
elif self._roll(enemy_probability, enemy_cp.base.strength):
|
||||
if event_class in enemy_generated_types:
|
||||
continue
|
||||
@ -141,15 +144,15 @@ class Game:
|
||||
if event_class == NavalInterceptEvent:
|
||||
if player_cp.radials == LAND:
|
||||
continue
|
||||
elif event_class == StrikeEvent:
|
||||
if not player_cp.ground_objects:
|
||||
continue
|
||||
elif event_class == BaseAttackEvent:
|
||||
if enemy_cap_generated:
|
||||
continue
|
||||
if enemy_cp.base.total_armor == 0:
|
||||
continue
|
||||
enemy_cap_generated = True
|
||||
elif event_class == AntiAAStrikeEvent:
|
||||
if player_cp.base.total_aa == 0:
|
||||
continue
|
||||
|
||||
enemy_generated_types.append(event_class)
|
||||
self.events.append(event_class(self.enemy, self.player, enemy_cp, player_cp, self))
|
||||
|
||||
@ -47,7 +47,13 @@ class StrikeOperation(Operation):
|
||||
def generate(self):
|
||||
targets = [] # type: typing.List[typing.Tuple[str, Point]]
|
||||
category_counters = {} # type: typing.Dict[str, int]
|
||||
processed_groups = []
|
||||
for object in self.to_cp.ground_objects:
|
||||
if object.group_id in processed_groups:
|
||||
continue
|
||||
|
||||
processed_groups.append(object.group_id)
|
||||
|
||||
category_counters[object.category] = category_counters.get(object.category, 0) + 1
|
||||
markpoint_name = "{}{}".format(object.name_abbrev, category_counters[object.category])
|
||||
targets.append((markpoint_name, object.position))
|
||||
@ -68,4 +74,6 @@ class StrikeOperation(Operation):
|
||||
clients={},
|
||||
at=self.defenders_starting_position)
|
||||
|
||||
self.briefinggen.title = "Strike"
|
||||
self.briefinggen.description = "Destroy infrastructure assets and military supplies in the region. Each building destroyed will lower targets strength."
|
||||
super(StrikeOperation, self).generate()
|
||||
|
||||
@ -100,8 +100,10 @@ class ArmorConflictGenerator:
|
||||
attacker_groups = list(db.unitdict_split(attackers, single_fight_attackers_count))
|
||||
|
||||
for attacker_group_dict, target_group_dict in zip_longest(attacker_groups, defender_groups):
|
||||
padding = FRONTLINE_CAS_PADDING if FRONTLINE_CAS_PADDING < self.conflict.distance else 0
|
||||
|
||||
position = self.conflict.position.point_from_heading(self.conflict.heading,
|
||||
random.randint(FRONTLINE_CAS_PADDING, int(self.conflict.distance - FRONTLINE_CAS_PADDING)))
|
||||
random.randint(padding, int(self.conflict.distance - padding)))
|
||||
self._generate_fight_at(attacker_group_dict, target_group_dict, position)
|
||||
|
||||
def generate_passengers(self, count: int):
|
||||
|
||||
@ -152,36 +152,48 @@ class Conflict:
|
||||
@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, right_position = None, None
|
||||
|
||||
left_position = center_position
|
||||
|
||||
for offset in range(0, int(FRONTLINE_LENGTH / 2), 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, int(FRONTLINE_LENGTH / 2), 1000):
|
||||
pos = center_position.point_from_heading(_heading_sum(heading, 90), offset)
|
||||
if not theater.is_on_land(pos):
|
||||
break
|
||||
else:
|
||||
if not theater.is_on_land(center_position):
|
||||
pos = cls._find_ground_position(center_position, FRONTLINE_LENGTH, _heading_sum(heading, -90), theater)
|
||||
if pos:
|
||||
right_position = pos
|
||||
center_position = pos
|
||||
else:
|
||||
pos = cls._find_ground_position(center_position, FRONTLINE_LENGTH, _heading_sum(heading, +90), theater)
|
||||
if pos:
|
||||
left_position = pos
|
||||
center_position = pos
|
||||
print("{} - {} {}".format(from_cp, to_cp, center_position))
|
||||
|
||||
return left_position, _heading_sum(heading, 90), right_position.distance_to_point(left_position)
|
||||
if left_position is None:
|
||||
left_position = cls._extend_ground_position(center_position, int(FRONTLINE_LENGTH/2), _heading_sum(heading, -90), theater)
|
||||
|
||||
if right_position is None:
|
||||
right_position = cls._extend_ground_position(center_position, int(FRONTLINE_LENGTH/2), _heading_sum(heading, 90), theater)
|
||||
|
||||
return left_position, _heading_sum(heading, 90), int(right_position.distance_to_point(left_position))
|
||||
|
||||
@classmethod
|
||||
def _find_ground_location(cls, initial: Point, max_distance: int, heading: int, theater: ConflictTheater) -> Point:
|
||||
for _ in range(0, int(max_distance), 800):
|
||||
for _ in range(3):
|
||||
if theater.is_on_land(initial):
|
||||
return initial
|
||||
def _extend_ground_position(cls, initial: Point, max_distance: int, heading: int, theater: ConflictTheater) -> Point:
|
||||
pos = initial
|
||||
for offset in range(0, int(max_distance), 500):
|
||||
new_pos = initial.point_from_heading(heading, offset)
|
||||
if theater.is_on_land(new_pos):
|
||||
pos = new_pos
|
||||
else:
|
||||
return pos
|
||||
|
||||
initial = initial.random_point_within(1000, 1000)
|
||||
return pos
|
||||
|
||||
initial = initial.point_from_heading(heading, 800)
|
||||
@classmethod
|
||||
def _find_ground_position(cls, initial: Point, max_distance: int, heading: int, theater: ConflictTheater) -> Point:
|
||||
pos = initial
|
||||
for _ in range(0, int(max_distance), 500):
|
||||
if theater.is_on_land(pos):
|
||||
return pos
|
||||
|
||||
pos = pos.point_from_heading(heading, 500)
|
||||
|
||||
logging.info("Didn't find ground position!")
|
||||
return None
|
||||
@ -195,10 +207,10 @@ class Conflict:
|
||||
|
||||
distance = to_cp.size * GROUND_DISTANCE_FACTOR
|
||||
attackers_location = position.point_from_heading(attack_heading, distance)
|
||||
attackers_location = Conflict._find_ground_location(attackers_location, distance * 2, _heading_sum(attack_heading, 180), theater)
|
||||
attackers_location = Conflict._find_ground_position(attackers_location, distance * 2, _heading_sum(attack_heading, 180), theater)
|
||||
|
||||
defenders_location = position.point_from_heading(defense_heading, distance)
|
||||
defenders_location = Conflict._find_ground_location(defenders_location, distance * 2, _heading_sum(defense_heading, 180), theater)
|
||||
defenders_location = Conflict._find_ground_position(defenders_location, distance * 2, _heading_sum(defense_heading, 180), theater)
|
||||
|
||||
return cls(
|
||||
position=position,
|
||||
@ -238,7 +250,7 @@ class Conflict:
|
||||
def ground_attack_conflict(cls, attacker: Country, defender: Country, from_cp: ControlPoint, to_cp: ControlPoint, theater: ConflictTheater):
|
||||
heading = random.choice(to_cp.radials)
|
||||
initial_location = to_cp.position.random_point_within(*GROUND_ATTACK_DISTANCE)
|
||||
position = Conflict._find_ground_location(initial_location, GROUND_INTERCEPT_SPREAD, _heading_sum(heading, 180), theater)
|
||||
position = Conflict._find_ground_position(initial_location, GROUND_INTERCEPT_SPREAD, _heading_sum(heading, 180), 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)
|
||||
@ -306,7 +318,7 @@ class Conflict:
|
||||
|
||||
distance = to_cp.size * GROUND_DISTANCE_FACTOR
|
||||
defenders_location = position.point_from_heading(defense_heading, distance)
|
||||
defenders_location = Conflict._find_ground_location(defenders_location, distance * 2, _heading_sum(defense_heading, 180), theater)
|
||||
defenders_location = Conflict._find_ground_position(defenders_location, distance * 2, _heading_sum(defense_heading, 180), theater)
|
||||
|
||||
return cls(
|
||||
position=position,
|
||||
@ -351,7 +363,7 @@ class Conflict:
|
||||
def transport_conflict(cls, attacker: Country, defender: Country, from_cp: ControlPoint, to_cp: ControlPoint, theater: ConflictTheater):
|
||||
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)
|
||||
dest = cls._find_ground_position(initial_dest, from_cp.position.distance_to_point(to_cp.position) / 3, heading, theater)
|
||||
if not dest:
|
||||
radial = to_cp.find_radial(to_cp.position.heading_between_point(from_cp.position))
|
||||
dest = to_cp.position.point_from_heading(radial, to_cp.size * GROUND_DISTANCE_FACTOR)
|
||||
|
||||
@ -13,6 +13,7 @@ CATEGORY_MAPPING = {
|
||||
"warehouse": [Warehouse.Warehouse],
|
||||
"fuel": [Warehouse.Tank],
|
||||
"ammo": [Warehouse.Ammunition_depot],
|
||||
"farp": [Fortification.FARP_Tent],
|
||||
}
|
||||
|
||||
|
||||
@ -40,7 +41,7 @@ class GroundObjectsGenerator:
|
||||
country=side,
|
||||
name=ground_object.string_identifier,
|
||||
_type=unit_type,
|
||||
position=Point(*ground_object.location),
|
||||
position=ground_object.position,
|
||||
heading=ground_object.heading
|
||||
)
|
||||
|
||||
@ -50,7 +51,7 @@ class GroundObjectsGenerator:
|
||||
country=side,
|
||||
name=ground_object.string_identifier,
|
||||
_type=random.choice(CATEGORY_MAPPING[ground_object.category]),
|
||||
position=Point(*ground_object.location),
|
||||
position=ground_object.position,
|
||||
heading=ground_object.heading
|
||||
)
|
||||
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -4,41 +4,66 @@ import typing
|
||||
from game import db
|
||||
from gen.groundobjectsgen import TheaterGroundObject
|
||||
from dcs.mission import Mission
|
||||
from dcs.terrain import PersianGulf
|
||||
from dcs.mapping import Point
|
||||
|
||||
m = Mission()
|
||||
m.load_file("./cau_groundobjects.miz")
|
||||
|
||||
result = {}
|
||||
result_by_groups = {} # type: typing.Dict[int, TheaterGroundObject]
|
||||
ids_counters = {}
|
||||
|
||||
|
||||
def append_group(cp_id, category, group_id, object_id, position, heading):
|
||||
global result
|
||||
global result_by_groups
|
||||
|
||||
ground_object = TheaterGroundObject(category, cp_id, group_id, object_id, position, heading)
|
||||
|
||||
if cp_id not in result:
|
||||
result[cp_id] = []
|
||||
result[cp_id].append(ground_object)
|
||||
|
||||
result[cp_id].append(TheaterGroundObject(category, cp_id, group_id, object_id, position, heading))
|
||||
result_by_groups_key = "{}_{}_{}".format(cp_id, category, group_id)
|
||||
if result_by_groups_key not in result_by_groups:
|
||||
result_by_groups[result_by_groups_key] = []
|
||||
result_by_groups[result_by_groups_key].append(ground_object)
|
||||
|
||||
|
||||
def parse_name(name: str) -> typing.Tuple:
|
||||
args = str(name).split("|")
|
||||
if len(args) == 3:
|
||||
args.append("1")
|
||||
args = str(name.split()[0]).split("|")
|
||||
|
||||
return args[0], int(args[1]), int(args[2]), int(args[3])
|
||||
return args[0], int(args[1]), int(args[2])
|
||||
|
||||
|
||||
for group in m.country("Russia").static_group + m.country("Russia").vehicle_group:
|
||||
try:
|
||||
category, cp_id, group_id, object_id = parse_name(str(group.name))
|
||||
category, cp_id, group_id = parse_name(str(group.name))
|
||||
except:
|
||||
print("Failed to parse {}".format(group.name))
|
||||
continue
|
||||
|
||||
append_group(cp_id, category, group_id, object_id, [group.position.x, group.position.y], group.units[0].heading)
|
||||
ids_counters_key = "{}_{}".format(cp_id, group_id)
|
||||
ids_counters[ids_counters_key] = ids_counters.get(ids_counters_key, 0) + 1
|
||||
object_id = ids_counters[ids_counters_key]
|
||||
append_group(cp_id, category, group_id, object_id, group.position, group.units[0].heading)
|
||||
|
||||
GROUP_TRESHOLD = 300
|
||||
did_check_pairs = []
|
||||
for group_id, objects_in_group in result_by_groups.items():
|
||||
for a in objects_in_group:
|
||||
for b in objects_in_group:
|
||||
if (a, b) in did_check_pairs:
|
||||
continue
|
||||
|
||||
did_check_pairs.append((a, b))
|
||||
distance = a.position.distance_to_point(b.position)
|
||||
if distance > GROUP_TRESHOLD:
|
||||
print("Objects {} and {} in group {} are too far apart ({})!".format(a.string_identifier, b.string_identifier, group_id, distance))
|
||||
|
||||
print("Total {} objects".format(sum([len(x) for x in result.values()])))
|
||||
|
||||
|
||||
with open("../cau_groundobjects.p", "wb") as f:
|
||||
pickle.dump(result, f)
|
||||
|
||||
|
||||
@ -1,14 +1,14 @@
|
||||
import pickle
|
||||
|
||||
from dcs.mission import Mission
|
||||
from dcs.terrain import PersianGulf
|
||||
|
||||
m = Mission()
|
||||
m.load_file("./gulf_terrain.miz")
|
||||
for terrain in ["cau", "gulf"]:
|
||||
m = Mission()
|
||||
m.load_file("./{}_terrain.miz".format(terrain))
|
||||
|
||||
landmap = []
|
||||
for plane_group in m.country("USA").plane_group:
|
||||
landmap.append([(x.position.x, x.position.y) for x in plane_group.points])
|
||||
landmap = []
|
||||
for plane_group in m.country("USA").plane_group:
|
||||
landmap.append([(x.position.x, x.position.y) for x in plane_group.points])
|
||||
|
||||
with open("../gulflandmap.p", "wb") as f:
|
||||
pickle.dump(landmap, f)
|
||||
with open("../{}landmap.p".format(terrain), "wb") as f:
|
||||
pickle.dump(landmap, f)
|
||||
|
||||
@ -13,7 +13,7 @@ class CaucasusTheater(ConflictTheater):
|
||||
overview_image = "caumap.gif"
|
||||
reference_points = {(-317948.32727306, 635639.37385346): (282.5, 319),
|
||||
(-355692.3067714, 617269.96285781): (269, 352), }
|
||||
landmap_poly = load_poly("resources\\caulandmap.p")
|
||||
landmap = load_landmap("resources\\caulandmap.p")
|
||||
daytime_map = {
|
||||
"dawn": (6, 9),
|
||||
"day": (9, 18),
|
||||
|
||||
@ -4,7 +4,7 @@ import itertools
|
||||
import dcs
|
||||
from dcs.mapping import Point
|
||||
|
||||
from .landmap import ray_tracing
|
||||
from .landmap import Landmap, poly_contains
|
||||
from .controlpoint import ControlPoint
|
||||
from .theatergroundobject import TheaterGroundObject
|
||||
|
||||
@ -52,12 +52,11 @@ class ConflictTheater:
|
||||
|
||||
reference_points = None # type: typing.Dict
|
||||
overview_image = None # type: str
|
||||
landmap_poly = None
|
||||
landmap = None # type: landmap.Landmap
|
||||
daytime_map = None # type: typing.Dict[str, typing.Tuple[int, int]]
|
||||
|
||||
def __init__(self):
|
||||
self.controlpoints = []
|
||||
self.groundobjects = []
|
||||
|
||||
def set_groundobject(self, dictionary: typing.Dict[int, typing.Collection[TheaterGroundObject]]):
|
||||
for id, value in dictionary.items():
|
||||
@ -73,14 +72,20 @@ class ConflictTheater:
|
||||
self.controlpoints.append(point)
|
||||
|
||||
def is_on_land(self, point: Point) -> bool:
|
||||
if not self.landmap_poly:
|
||||
if not self.landmap:
|
||||
return True
|
||||
|
||||
for poly in self.landmap_poly:
|
||||
if ray_tracing(point.x, point.y, poly):
|
||||
return True
|
||||
# check first poly (main land poly)
|
||||
if not poly_contains(point.x, point.y, self.landmap[0]):
|
||||
return False
|
||||
|
||||
return False
|
||||
# check others polys (exclusion zones from main)
|
||||
for poly in self.landmap[1:]:
|
||||
if poly_contains(point.x, point.y, poly):
|
||||
# point is in one of the exclusion zones, meaning that it's in the lake or something
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
def player_points(self) -> typing.Collection[ControlPoint]:
|
||||
return [point for point in self.controlpoints if point.captured]
|
||||
|
||||
@ -9,7 +9,7 @@ from .theatergroundobject import TheaterGroundObject
|
||||
|
||||
|
||||
class ControlPoint:
|
||||
connected_points = [] # type: typing.List[ControlPoint]
|
||||
connected_points = None # type: typing.List[ControlPoint]
|
||||
ground_objects = None # type: typing.Collection[TheaterGroundObject]
|
||||
position = None # type: Point
|
||||
captured = False
|
||||
@ -26,6 +26,7 @@ class ControlPoint:
|
||||
self.full_name = name
|
||||
self.position = position
|
||||
self.at = at
|
||||
self.ground_objects = []
|
||||
|
||||
self.size = size
|
||||
self.importance = importance
|
||||
|
||||
@ -1,7 +1,10 @@
|
||||
import pickle
|
||||
import typing
|
||||
|
||||
Landmap = typing.Collection[typing.Collection[typing.Tuple[float, float]]]
|
||||
|
||||
|
||||
def load_poly(filename: str):
|
||||
def load_landmap(filename: str) -> Landmap:
|
||||
try:
|
||||
with open(filename, "rb") as f:
|
||||
return pickle.load(f)
|
||||
@ -9,7 +12,7 @@ def load_poly(filename: str):
|
||||
return None
|
||||
|
||||
|
||||
def ray_tracing(x, y, poly):
|
||||
def poly_contains(x, y, poly):
|
||||
n = len(poly)
|
||||
inside = False
|
||||
xints = 0.0
|
||||
@ -25,3 +28,11 @@ def ray_tracing(x, y, poly):
|
||||
inside = not inside
|
||||
p1x, p1y = p2x, p2y
|
||||
return inside
|
||||
|
||||
def poly_centroid(poly) -> typing.Tuple[float, float]:
|
||||
x_list = [vertex[0] for vertex in poly]
|
||||
y_list = [vertex[1] for vertex in poly]
|
||||
x = sum(x_list) / len(poly)
|
||||
y = sum(y_list) / len(poly)
|
||||
return (x, y)
|
||||
|
||||
|
||||
@ -3,7 +3,7 @@ from dcs import mapping
|
||||
|
||||
from .conflicttheater import *
|
||||
from .base import *
|
||||
from .landmap import load_poly
|
||||
from .landmap import load_landmap
|
||||
|
||||
|
||||
class PersianGulfTheater(ConflictTheater):
|
||||
@ -11,7 +11,7 @@ class PersianGulfTheater(ConflictTheater):
|
||||
overview_image = "persiangulf.gif"
|
||||
reference_points = {(persiangulf.Sir_Abu_Nuayr.position.x, persiangulf.Sir_Abu_Nuayr.position.y): (321, 145),
|
||||
(persiangulf.Sirri_Island.position.x, persiangulf.Sirri_Island.position.y): (347, 82), }
|
||||
landmap_poly = load_poly("resources\\gulflandmap.p")
|
||||
landmap = load_landmap("resources\\gulflandmap.p")
|
||||
daytime_map = {
|
||||
"dawn": (6, 8),
|
||||
"day": (8, 16),
|
||||
|
||||
@ -8,6 +8,7 @@ NAME_BY_CATEGORY = {
|
||||
"fuel": "Fuel depot",
|
||||
"defense": "AA Defense Site",
|
||||
"warehouse": "Warehouse",
|
||||
"farp": "FARP",
|
||||
}
|
||||
|
||||
ABBREV_NAME = {
|
||||
@ -16,6 +17,7 @@ ABBREV_NAME = {
|
||||
"fuel": "FUEL",
|
||||
"defense": "AA",
|
||||
"warehouse": "WARE",
|
||||
"farp": "FARP",
|
||||
}
|
||||
|
||||
|
||||
@ -24,25 +26,21 @@ class TheaterGroundObject:
|
||||
cp_id = 0
|
||||
group_id = 0
|
||||
heading = 0
|
||||
location = None # type: typing.Collection[int]
|
||||
position = None # type: Point
|
||||
category = None # type: str
|
||||
|
||||
def __init__(self, category, cp_id, group_id, object_id, location, heading):
|
||||
def __init__(self, category, cp_id, group_id, object_id, position, heading):
|
||||
self.category = category
|
||||
self.cp_id = cp_id
|
||||
self.group_id = group_id
|
||||
self.object_id = object_id
|
||||
self.location = location
|
||||
self.position = position
|
||||
self.heading = heading
|
||||
|
||||
@property
|
||||
def string_identifier(self):
|
||||
return "{}|{}|{}|{}".format(self.category, self.cp_id, self.group_id, self.object_id)
|
||||
|
||||
@property
|
||||
def position(self) -> Point:
|
||||
return Point(*self.location)
|
||||
|
||||
@property
|
||||
def name_abbrev(self) -> str:
|
||||
return ABBREV_NAME[self.category]
|
||||
|
||||
@ -1,3 +1,5 @@
|
||||
import webbrowser
|
||||
|
||||
from tkinter import *
|
||||
from tkinter.ttk import *
|
||||
from .styles import STYLES
|
||||
@ -64,7 +66,16 @@ class ConfigurationMenu(Menu):
|
||||
Checkbutton(body, variable=self.night_var, **STYLES["radiobutton"]).grid(row=4, column=1, sticky=E)
|
||||
|
||||
Button(body, text="Back", command=self.dismiss, **STYLES["btn-primary"]).grid(row=5, column=1, sticky=E, pady=30)
|
||||
Button(body, text="Cheat +200m", command=self.cheat_money, **STYLES["btn-danger"]).grid(row=6, column=1)
|
||||
|
||||
Label(body, text="Contributors: ", **STYLES["widget"]).grid(row=6, column=0, sticky=W)
|
||||
|
||||
Label(body, text="shdwp - author, maintainer", **STYLES["widget"]).grid(row=7, column=0, sticky=W)
|
||||
Button(body, text="[github]", command=lambda: webbrowser.open_new_tab("http://github.com/shdwp"), **STYLES["widget"]).grid(row=7, column=1, sticky=E)
|
||||
|
||||
Label(body, text="Khopa - contributions", **STYLES["widget"]).grid(row=8, column=0, sticky=W)
|
||||
Button(body, text="[github]", command=lambda: webbrowser.open_new_tab("http://github.com/Khopa"), **STYLES["widget"]).grid(row=8, column=1, sticky=E)
|
||||
|
||||
Button(body, text="Cheat +200m", command=self.cheat_money, **STYLES["btn-danger"]).grid(row=10, column=1, pady=30)
|
||||
|
||||
def cheat_money(self):
|
||||
self.game.budget += 200
|
||||
|
||||
@ -13,6 +13,6 @@ class CorruptedSaveMenu(Menu):
|
||||
def display(self):
|
||||
self.window.clear_right_pane()
|
||||
|
||||
Label(text="Your save game was corrupted!", **STYLES["widget"]).grid(row=0, column=0)
|
||||
Label(text="Your save game is either incompatible or was corrupted!", **STYLES["widget"]).grid(row=0, column=0)
|
||||
Label(text="Please restore it by replacing \"liberation_save\" file with \"liberation_save_tmp\" to restore last saved copy.", **STYLES["widget"]).grid(row=1, column=0)
|
||||
Label(text="You can find those files under user DCS directory.", **STYLES["widget"]).grid(row=2, column=0)
|
||||
Label(text="You can find those files under user Saved Games\\DCS directory.", **STYLES["widget"]).grid(row=2, column=0)
|
||||
|
||||
@ -8,14 +8,14 @@ from .styles import STYLES, RED
|
||||
|
||||
|
||||
UNITTYPES_FOR_EVENTS = {
|
||||
FrontlineAttackEvent: [CAS, PinpointStrike],
|
||||
FrontlinePatrolEvent: [CAP, PinpointStrike],
|
||||
BaseAttackEvent: [CAP, CAS, PinpointStrike],
|
||||
StrikeEvent: [CAP, CAS],
|
||||
InterceptEvent: [CAP],
|
||||
InsurgentAttackEvent: [CAS],
|
||||
NavalInterceptEvent: [CAS],
|
||||
InfantryTransportEvent: [Embarking],
|
||||
FrontlineAttackEvent: [[CAS, PinpointStrike], [CAP]],
|
||||
FrontlinePatrolEvent: [[CAP, PinpointStrike], [CAP]],
|
||||
BaseAttackEvent: [[CAP, CAS, PinpointStrike], [CAP, CAS, PinpointStrike]],
|
||||
StrikeEvent: [[CAP, CAS], [CAP]],
|
||||
InterceptEvent: [[CAP], [CAP]],
|
||||
InsurgentAttackEvent: [[CAS], [CAP]],
|
||||
NavalInterceptEvent: [[CAS], [CAP]],
|
||||
InfantryTransportEvent: [[Embarking], [CAP]],
|
||||
}
|
||||
|
||||
AI_BAN_FOR_EVENTS = {
|
||||
@ -115,7 +115,8 @@ class EventMenu(Menu):
|
||||
Label(self.frame, text="Client slots", **STYLES["widget"]).grid(row=row, column=3, columnspan=2)
|
||||
row += 1
|
||||
|
||||
filter_to = UNITTYPES_FOR_EVENTS[self.event.__class__]
|
||||
filter_attackers_index = 0 if self.game.is_player_attack(self.event) else 1
|
||||
filter_to = UNITTYPES_FOR_EVENTS[self.event.__class__][filter_attackers_index]
|
||||
for unit_type, count in self.base.aircraft.items():
|
||||
if filter_to and db.unit_task(unit_type) not in filter_to:
|
||||
continue
|
||||
@ -262,12 +263,6 @@ class EventMenu(Menu):
|
||||
elif type(self.event) is NavalInterceptEvent:
|
||||
e = self.event # type: NavalInterceptEvent
|
||||
|
||||
if self.game.is_player_attack(self.event):
|
||||
e.player_attacking(strikegroup=scrambled_aircraft, clients=scrambled_clients)
|
||||
else:
|
||||
e.player_defending(interceptors=scrambled_aircraft, clients=scrambled_clients)
|
||||
elif type(self.event) is AntiAAStrikeEvent:
|
||||
e = self.event # type: AntiAAStrikeEvent
|
||||
if self.game.is_player_attack(self.event):
|
||||
e.player_attacking(strikegroup=scrambled_aircraft, clients=scrambled_clients)
|
||||
else:
|
||||
|
||||
@ -86,6 +86,10 @@ class EventResultsMenu(Menu):
|
||||
|
||||
header("Enemy losses")
|
||||
|
||||
if self.debriefing.destroyed_objects:
|
||||
Label(self.frame, text="Ground assets", **STYLES["widget"]).grid(row=row)
|
||||
Label(self.frame, text="{}".format(len(self.debriefing.destroyed_objects)), **STYLES["widget"]).grid(column=1, row=row)
|
||||
|
||||
for unit_type, count in self.enemy_losses.items():
|
||||
if count == 0:
|
||||
continue
|
||||
|
||||
@ -51,10 +51,9 @@ class MainMenu(Menu):
|
||||
nonlocal row, body
|
||||
frame = LabelFrame(body, **STYLES["label-frame"])
|
||||
frame.grid(row=row, sticky=NSEW)
|
||||
Message(frame, text="{}{} at {}".format(
|
||||
Message(frame, text="{}{}".format(
|
||||
event.defender_name == self.game.player and "Enemy attacking: " or "",
|
||||
event,
|
||||
event.to_cp,
|
||||
event
|
||||
), aspect=1600, **STYLES["widget"]).grid(column=0, row=0, sticky=NSEW)
|
||||
Button(body, text=">", command=self.start_event(event), **STYLES["btn-primary"]).grid(column=1, row=row, sticky=E)
|
||||
row += 1
|
||||
@ -63,9 +62,8 @@ class MainMenu(Menu):
|
||||
nonlocal row, body
|
||||
Label(body, text=text, **STYLES["strong"]).grid(column=0, columnspan=2, row=row, sticky=N+EW, pady=(pady,0)); row += 1
|
||||
|
||||
#Separator(self.frame, orient='horizontal').grid(row=row, sticky=EW); row += 1
|
||||
|
||||
events = self.game.events
|
||||
events.sort(key=lambda x: x.to_cp.name)
|
||||
events.sort(key=lambda x: x.from_cp.name)
|
||||
events.sort(key=lambda x: x.informational and 2 or (self.game.is_player_attack(x) and 1 or 0))
|
||||
|
||||
@ -74,7 +72,7 @@ class MainMenu(Menu):
|
||||
for event in events:
|
||||
if not event.informational:
|
||||
if self.game.is_player_attack(event):
|
||||
new_destination = event.from_cp.name
|
||||
new_destination = "From {} to {}".format(event.from_cp.name, event.to_cp.name)
|
||||
else:
|
||||
new_destination = "Enemy attack"
|
||||
if destination != new_destination:
|
||||
|
||||
@ -81,6 +81,7 @@ class OverviewCanvas:
|
||||
|
||||
if cp.captured and not connected_cp.captured and Conflict.has_frontline_between(cp, connected_cp):
|
||||
frontline_pos, heading, distance = Conflict.frontline_vector(cp, connected_cp, self.game.theater)
|
||||
distance = max(distance, 1000)
|
||||
start_coords = self.transform_point(frontline_pos, treshold=10)
|
||||
end_coords = self.transform_point(frontline_pos.point_from_heading(heading, distance), treshold=60)
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user