mirror of
https://github.com/dcs-liberation/dcs_liberation.git
synced 2025-11-10 14:22:26 +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 theater import start_generator
|
||||||
from userdata import persistency, logging as logging_module
|
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])
|
persistency.setup(sys.argv[1])
|
||||||
dcs.planes.FlyingType.payload_dirs = [os.path.join(os.path.dirname(os.path.realpath(__file__)), "resources\\payloads")]
|
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(".")
|
current_version = VERSION_STRING.split(".")
|
||||||
save_version = save_version.split(".")
|
save_version = save_version.split(".")
|
||||||
|
|
||||||
|
if "--ignore-save" in sys.argv:
|
||||||
|
return False
|
||||||
|
|
||||||
if current_version[:2] == save_version[:2]:
|
if current_version[:2] == save_version[:2]:
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|||||||
@ -32,6 +32,7 @@ class BaseAttackEvent(Event):
|
|||||||
if self.is_successfull(debriefing):
|
if self.is_successfull(debriefing):
|
||||||
if self.from_cp.captured:
|
if self.from_cp.captured:
|
||||||
self.to_cp.captured = True
|
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.filter_units(db.UNIT_BY_COUNTRY[self.attacker_name])
|
||||||
|
|
||||||
self.to_cp.base.affect_strength(+self.STRENGTH_RECOVERY)
|
self.to_cp.base.affect_strength(+self.STRENGTH_RECOVERY)
|
||||||
|
|||||||
@ -126,6 +126,9 @@ class Game:
|
|||||||
else:
|
else:
|
||||||
if event_class == BaseAttackEvent and enemy_cp.base.strength > PLAYER_BASEATTACK_THRESHOLD:
|
if event_class == BaseAttackEvent and enemy_cp.base.strength > PLAYER_BASEATTACK_THRESHOLD:
|
||||||
pass
|
pass
|
||||||
|
else:
|
||||||
|
if event_class == StrikeEvent and not enemy_cp.ground_objects:
|
||||||
|
pass
|
||||||
else:
|
else:
|
||||||
self.events.append(event_class(self.player, self.enemy, player_cp, enemy_cp, self))
|
self.events.append(event_class(self.player, self.enemy, player_cp, enemy_cp, self))
|
||||||
elif self._roll(enemy_probability, enemy_cp.base.strength):
|
elif self._roll(enemy_probability, enemy_cp.base.strength):
|
||||||
@ -141,15 +144,15 @@ class Game:
|
|||||||
if event_class == NavalInterceptEvent:
|
if event_class == NavalInterceptEvent:
|
||||||
if player_cp.radials == LAND:
|
if player_cp.radials == LAND:
|
||||||
continue
|
continue
|
||||||
|
elif event_class == StrikeEvent:
|
||||||
|
if not player_cp.ground_objects:
|
||||||
|
continue
|
||||||
elif event_class == BaseAttackEvent:
|
elif event_class == BaseAttackEvent:
|
||||||
if enemy_cap_generated:
|
if enemy_cap_generated:
|
||||||
continue
|
continue
|
||||||
if enemy_cp.base.total_armor == 0:
|
if enemy_cp.base.total_armor == 0:
|
||||||
continue
|
continue
|
||||||
enemy_cap_generated = True
|
enemy_cap_generated = True
|
||||||
elif event_class == AntiAAStrikeEvent:
|
|
||||||
if player_cp.base.total_aa == 0:
|
|
||||||
continue
|
|
||||||
|
|
||||||
enemy_generated_types.append(event_class)
|
enemy_generated_types.append(event_class)
|
||||||
self.events.append(event_class(self.enemy, self.player, enemy_cp, player_cp, self))
|
self.events.append(event_class(self.enemy, self.player, enemy_cp, player_cp, self))
|
||||||
|
|||||||
@ -47,7 +47,13 @@ class StrikeOperation(Operation):
|
|||||||
def generate(self):
|
def generate(self):
|
||||||
targets = [] # type: typing.List[typing.Tuple[str, Point]]
|
targets = [] # type: typing.List[typing.Tuple[str, Point]]
|
||||||
category_counters = {} # type: typing.Dict[str, int]
|
category_counters = {} # type: typing.Dict[str, int]
|
||||||
|
processed_groups = []
|
||||||
for object in self.to_cp.ground_objects:
|
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
|
category_counters[object.category] = category_counters.get(object.category, 0) + 1
|
||||||
markpoint_name = "{}{}".format(object.name_abbrev, category_counters[object.category])
|
markpoint_name = "{}{}".format(object.name_abbrev, category_counters[object.category])
|
||||||
targets.append((markpoint_name, object.position))
|
targets.append((markpoint_name, object.position))
|
||||||
@ -68,4 +74,6 @@ class StrikeOperation(Operation):
|
|||||||
clients={},
|
clients={},
|
||||||
at=self.defenders_starting_position)
|
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()
|
super(StrikeOperation, self).generate()
|
||||||
|
|||||||
@ -100,8 +100,10 @@ class ArmorConflictGenerator:
|
|||||||
attacker_groups = list(db.unitdict_split(attackers, single_fight_attackers_count))
|
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):
|
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,
|
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)
|
self._generate_fight_at(attacker_group_dict, target_group_dict, position)
|
||||||
|
|
||||||
def generate_passengers(self, count: int):
|
def generate_passengers(self, count: int):
|
||||||
|
|||||||
@ -152,36 +152,48 @@ class Conflict:
|
|||||||
@classmethod
|
@classmethod
|
||||||
def frontline_vector(cls, from_cp: ControlPoint, to_cp: ControlPoint, theater: ConflictTheater) -> typing.Tuple[Point, int, int]:
|
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)
|
center_position, heading = cls.frontline_position(from_cp, to_cp)
|
||||||
|
left_position, right_position = None, None
|
||||||
|
|
||||||
left_position = center_position
|
if not theater.is_on_land(center_position):
|
||||||
|
pos = cls._find_ground_position(center_position, FRONTLINE_LENGTH, _heading_sum(heading, -90), theater)
|
||||||
for offset in range(0, int(FRONTLINE_LENGTH / 2), 1000):
|
if pos:
|
||||||
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:
|
|
||||||
right_position = 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
|
@classmethod
|
||||||
def _find_ground_location(cls, initial: Point, max_distance: int, heading: int, theater: ConflictTheater) -> Point:
|
def _extend_ground_position(cls, initial: Point, max_distance: int, heading: int, theater: ConflictTheater) -> Point:
|
||||||
for _ in range(0, int(max_distance), 800):
|
pos = initial
|
||||||
for _ in range(3):
|
for offset in range(0, int(max_distance), 500):
|
||||||
if theater.is_on_land(initial):
|
new_pos = initial.point_from_heading(heading, offset)
|
||||||
return initial
|
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!")
|
logging.info("Didn't find ground position!")
|
||||||
return None
|
return None
|
||||||
@ -195,10 +207,10 @@ class Conflict:
|
|||||||
|
|
||||||
distance = to_cp.size * GROUND_DISTANCE_FACTOR
|
distance = to_cp.size * GROUND_DISTANCE_FACTOR
|
||||||
attackers_location = position.point_from_heading(attack_heading, distance)
|
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 = 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(
|
return cls(
|
||||||
position=position,
|
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):
|
def ground_attack_conflict(cls, attacker: Country, defender: Country, from_cp: ControlPoint, to_cp: ControlPoint, theater: ConflictTheater):
|
||||||
heading = random.choice(to_cp.radials)
|
heading = random.choice(to_cp.radials)
|
||||||
initial_location = to_cp.position.random_point_within(*GROUND_ATTACK_DISTANCE)
|
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:
|
if not position:
|
||||||
heading = to_cp.find_radial(to_cp.position.heading_between_point(from_cp.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)
|
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
|
distance = to_cp.size * GROUND_DISTANCE_FACTOR
|
||||||
defenders_location = position.point_from_heading(defense_heading, distance)
|
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(
|
return cls(
|
||||||
position=position,
|
position=position,
|
||||||
@ -351,7 +363,7 @@ class Conflict:
|
|||||||
def transport_conflict(cls, attacker: Country, defender: Country, from_cp: ControlPoint, to_cp: ControlPoint, theater: ConflictTheater):
|
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)
|
frontline_position, heading = cls.frontline_position(from_cp, to_cp)
|
||||||
initial_dest = frontline_position.point_from_heading(heading, TRANSPORT_FRONTLINE_DIST)
|
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:
|
if not dest:
|
||||||
radial = to_cp.find_radial(to_cp.position.heading_between_point(from_cp.position))
|
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)
|
dest = to_cp.position.point_from_heading(radial, to_cp.size * GROUND_DISTANCE_FACTOR)
|
||||||
|
|||||||
@ -13,6 +13,7 @@ CATEGORY_MAPPING = {
|
|||||||
"warehouse": [Warehouse.Warehouse],
|
"warehouse": [Warehouse.Warehouse],
|
||||||
"fuel": [Warehouse.Tank],
|
"fuel": [Warehouse.Tank],
|
||||||
"ammo": [Warehouse.Ammunition_depot],
|
"ammo": [Warehouse.Ammunition_depot],
|
||||||
|
"farp": [Fortification.FARP_Tent],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -40,7 +41,7 @@ class GroundObjectsGenerator:
|
|||||||
country=side,
|
country=side,
|
||||||
name=ground_object.string_identifier,
|
name=ground_object.string_identifier,
|
||||||
_type=unit_type,
|
_type=unit_type,
|
||||||
position=Point(*ground_object.location),
|
position=ground_object.position,
|
||||||
heading=ground_object.heading
|
heading=ground_object.heading
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -50,7 +51,7 @@ class GroundObjectsGenerator:
|
|||||||
country=side,
|
country=side,
|
||||||
name=ground_object.string_identifier,
|
name=ground_object.string_identifier,
|
||||||
_type=random.choice(CATEGORY_MAPPING[ground_object.category]),
|
_type=random.choice(CATEGORY_MAPPING[ground_object.category]),
|
||||||
position=Point(*ground_object.location),
|
position=ground_object.position,
|
||||||
heading=ground_object.heading
|
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 game import db
|
||||||
from gen.groundobjectsgen import TheaterGroundObject
|
from gen.groundobjectsgen import TheaterGroundObject
|
||||||
from dcs.mission import Mission
|
from dcs.mission import Mission
|
||||||
from dcs.terrain import PersianGulf
|
from dcs.mapping import Point
|
||||||
|
|
||||||
m = Mission()
|
m = Mission()
|
||||||
m.load_file("./cau_groundobjects.miz")
|
m.load_file("./cau_groundobjects.miz")
|
||||||
|
|
||||||
result = {}
|
result = {}
|
||||||
|
result_by_groups = {} # type: typing.Dict[int, TheaterGroundObject]
|
||||||
|
ids_counters = {}
|
||||||
|
|
||||||
|
|
||||||
def append_group(cp_id, category, group_id, object_id, position, heading):
|
def append_group(cp_id, category, group_id, object_id, position, heading):
|
||||||
global result
|
global result
|
||||||
|
global result_by_groups
|
||||||
|
|
||||||
|
ground_object = TheaterGroundObject(category, cp_id, group_id, object_id, position, heading)
|
||||||
|
|
||||||
if cp_id not in result:
|
if cp_id not in result:
|
||||||
result[cp_id] = []
|
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:
|
def parse_name(name: str) -> typing.Tuple:
|
||||||
args = str(name).split("|")
|
args = str(name.split()[0]).split("|")
|
||||||
if len(args) == 3:
|
|
||||||
args.append("1")
|
|
||||||
|
|
||||||
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:
|
for group in m.country("Russia").static_group + m.country("Russia").vehicle_group:
|
||||||
try:
|
try:
|
||||||
category, cp_id, group_id, object_id = parse_name(str(group.name))
|
category, cp_id, group_id = parse_name(str(group.name))
|
||||||
except:
|
except:
|
||||||
print("Failed to parse {}".format(group.name))
|
print("Failed to parse {}".format(group.name))
|
||||||
continue
|
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()])))
|
print("Total {} objects".format(sum([len(x) for x in result.values()])))
|
||||||
|
|
||||||
|
|
||||||
with open("../cau_groundobjects.p", "wb") as f:
|
with open("../cau_groundobjects.p", "wb") as f:
|
||||||
pickle.dump(result, f)
|
pickle.dump(result, f)
|
||||||
|
|
||||||
|
|||||||
@ -1,14 +1,14 @@
|
|||||||
import pickle
|
import pickle
|
||||||
|
|
||||||
from dcs.mission import Mission
|
from dcs.mission import Mission
|
||||||
from dcs.terrain import PersianGulf
|
|
||||||
|
|
||||||
|
for terrain in ["cau", "gulf"]:
|
||||||
m = Mission()
|
m = Mission()
|
||||||
m.load_file("./gulf_terrain.miz")
|
m.load_file("./{}_terrain.miz".format(terrain))
|
||||||
|
|
||||||
landmap = []
|
landmap = []
|
||||||
for plane_group in m.country("USA").plane_group:
|
for plane_group in m.country("USA").plane_group:
|
||||||
landmap.append([(x.position.x, x.position.y) for x in plane_group.points])
|
landmap.append([(x.position.x, x.position.y) for x in plane_group.points])
|
||||||
|
|
||||||
with open("../gulflandmap.p", "wb") as f:
|
with open("../{}landmap.p".format(terrain), "wb") as f:
|
||||||
pickle.dump(landmap, f)
|
pickle.dump(landmap, f)
|
||||||
|
|||||||
@ -13,7 +13,7 @@ class CaucasusTheater(ConflictTheater):
|
|||||||
overview_image = "caumap.gif"
|
overview_image = "caumap.gif"
|
||||||
reference_points = {(-317948.32727306, 635639.37385346): (282.5, 319),
|
reference_points = {(-317948.32727306, 635639.37385346): (282.5, 319),
|
||||||
(-355692.3067714, 617269.96285781): (269, 352), }
|
(-355692.3067714, 617269.96285781): (269, 352), }
|
||||||
landmap_poly = load_poly("resources\\caulandmap.p")
|
landmap = load_landmap("resources\\caulandmap.p")
|
||||||
daytime_map = {
|
daytime_map = {
|
||||||
"dawn": (6, 9),
|
"dawn": (6, 9),
|
||||||
"day": (9, 18),
|
"day": (9, 18),
|
||||||
|
|||||||
@ -4,7 +4,7 @@ import itertools
|
|||||||
import dcs
|
import dcs
|
||||||
from dcs.mapping import Point
|
from dcs.mapping import Point
|
||||||
|
|
||||||
from .landmap import ray_tracing
|
from .landmap import Landmap, poly_contains
|
||||||
from .controlpoint import ControlPoint
|
from .controlpoint import ControlPoint
|
||||||
from .theatergroundobject import TheaterGroundObject
|
from .theatergroundobject import TheaterGroundObject
|
||||||
|
|
||||||
@ -52,12 +52,11 @@ class ConflictTheater:
|
|||||||
|
|
||||||
reference_points = None # type: typing.Dict
|
reference_points = None # type: typing.Dict
|
||||||
overview_image = None # type: str
|
overview_image = None # type: str
|
||||||
landmap_poly = None
|
landmap = None # type: landmap.Landmap
|
||||||
daytime_map = None # type: typing.Dict[str, typing.Tuple[int, int]]
|
daytime_map = None # type: typing.Dict[str, typing.Tuple[int, int]]
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.controlpoints = []
|
self.controlpoints = []
|
||||||
self.groundobjects = []
|
|
||||||
|
|
||||||
def set_groundobject(self, dictionary: typing.Dict[int, typing.Collection[TheaterGroundObject]]):
|
def set_groundobject(self, dictionary: typing.Dict[int, typing.Collection[TheaterGroundObject]]):
|
||||||
for id, value in dictionary.items():
|
for id, value in dictionary.items():
|
||||||
@ -73,15 +72,21 @@ class ConflictTheater:
|
|||||||
self.controlpoints.append(point)
|
self.controlpoints.append(point)
|
||||||
|
|
||||||
def is_on_land(self, point: Point) -> bool:
|
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
|
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]:
|
def player_points(self) -> typing.Collection[ControlPoint]:
|
||||||
return [point for point in self.controlpoints if point.captured]
|
return [point for point in self.controlpoints if point.captured]
|
||||||
|
|
||||||
|
|||||||
@ -9,7 +9,7 @@ from .theatergroundobject import TheaterGroundObject
|
|||||||
|
|
||||||
|
|
||||||
class ControlPoint:
|
class ControlPoint:
|
||||||
connected_points = [] # type: typing.List[ControlPoint]
|
connected_points = None # type: typing.List[ControlPoint]
|
||||||
ground_objects = None # type: typing.Collection[TheaterGroundObject]
|
ground_objects = None # type: typing.Collection[TheaterGroundObject]
|
||||||
position = None # type: Point
|
position = None # type: Point
|
||||||
captured = False
|
captured = False
|
||||||
@ -26,6 +26,7 @@ class ControlPoint:
|
|||||||
self.full_name = name
|
self.full_name = name
|
||||||
self.position = position
|
self.position = position
|
||||||
self.at = at
|
self.at = at
|
||||||
|
self.ground_objects = []
|
||||||
|
|
||||||
self.size = size
|
self.size = size
|
||||||
self.importance = importance
|
self.importance = importance
|
||||||
|
|||||||
@ -1,7 +1,10 @@
|
|||||||
import pickle
|
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:
|
try:
|
||||||
with open(filename, "rb") as f:
|
with open(filename, "rb") as f:
|
||||||
return pickle.load(f)
|
return pickle.load(f)
|
||||||
@ -9,7 +12,7 @@ def load_poly(filename: str):
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
def ray_tracing(x, y, poly):
|
def poly_contains(x, y, poly):
|
||||||
n = len(poly)
|
n = len(poly)
|
||||||
inside = False
|
inside = False
|
||||||
xints = 0.0
|
xints = 0.0
|
||||||
@ -25,3 +28,11 @@ def ray_tracing(x, y, poly):
|
|||||||
inside = not inside
|
inside = not inside
|
||||||
p1x, p1y = p2x, p2y
|
p1x, p1y = p2x, p2y
|
||||||
return inside
|
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 .conflicttheater import *
|
||||||
from .base import *
|
from .base import *
|
||||||
from .landmap import load_poly
|
from .landmap import load_landmap
|
||||||
|
|
||||||
|
|
||||||
class PersianGulfTheater(ConflictTheater):
|
class PersianGulfTheater(ConflictTheater):
|
||||||
@ -11,7 +11,7 @@ class PersianGulfTheater(ConflictTheater):
|
|||||||
overview_image = "persiangulf.gif"
|
overview_image = "persiangulf.gif"
|
||||||
reference_points = {(persiangulf.Sir_Abu_Nuayr.position.x, persiangulf.Sir_Abu_Nuayr.position.y): (321, 145),
|
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), }
|
(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 = {
|
daytime_map = {
|
||||||
"dawn": (6, 8),
|
"dawn": (6, 8),
|
||||||
"day": (8, 16),
|
"day": (8, 16),
|
||||||
|
|||||||
@ -8,6 +8,7 @@ NAME_BY_CATEGORY = {
|
|||||||
"fuel": "Fuel depot",
|
"fuel": "Fuel depot",
|
||||||
"defense": "AA Defense Site",
|
"defense": "AA Defense Site",
|
||||||
"warehouse": "Warehouse",
|
"warehouse": "Warehouse",
|
||||||
|
"farp": "FARP",
|
||||||
}
|
}
|
||||||
|
|
||||||
ABBREV_NAME = {
|
ABBREV_NAME = {
|
||||||
@ -16,6 +17,7 @@ ABBREV_NAME = {
|
|||||||
"fuel": "FUEL",
|
"fuel": "FUEL",
|
||||||
"defense": "AA",
|
"defense": "AA",
|
||||||
"warehouse": "WARE",
|
"warehouse": "WARE",
|
||||||
|
"farp": "FARP",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -24,25 +26,21 @@ class TheaterGroundObject:
|
|||||||
cp_id = 0
|
cp_id = 0
|
||||||
group_id = 0
|
group_id = 0
|
||||||
heading = 0
|
heading = 0
|
||||||
location = None # type: typing.Collection[int]
|
position = None # type: Point
|
||||||
category = None # type: str
|
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.category = category
|
||||||
self.cp_id = cp_id
|
self.cp_id = cp_id
|
||||||
self.group_id = group_id
|
self.group_id = group_id
|
||||||
self.object_id = object_id
|
self.object_id = object_id
|
||||||
self.location = location
|
self.position = position
|
||||||
self.heading = heading
|
self.heading = heading
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def string_identifier(self):
|
def string_identifier(self):
|
||||||
return "{}|{}|{}|{}".format(self.category, self.cp_id, self.group_id, self.object_id)
|
return "{}|{}|{}|{}".format(self.category, self.cp_id, self.group_id, self.object_id)
|
||||||
|
|
||||||
@property
|
|
||||||
def position(self) -> Point:
|
|
||||||
return Point(*self.location)
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def name_abbrev(self) -> str:
|
def name_abbrev(self) -> str:
|
||||||
return ABBREV_NAME[self.category]
|
return ABBREV_NAME[self.category]
|
||||||
|
|||||||
@ -1,3 +1,5 @@
|
|||||||
|
import webbrowser
|
||||||
|
|
||||||
from tkinter import *
|
from tkinter import *
|
||||||
from tkinter.ttk import *
|
from tkinter.ttk import *
|
||||||
from .styles import STYLES
|
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)
|
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="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):
|
def cheat_money(self):
|
||||||
self.game.budget += 200
|
self.game.budget += 200
|
||||||
|
|||||||
@ -13,6 +13,6 @@ class CorruptedSaveMenu(Menu):
|
|||||||
def display(self):
|
def display(self):
|
||||||
self.window.clear_right_pane()
|
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="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 = {
|
UNITTYPES_FOR_EVENTS = {
|
||||||
FrontlineAttackEvent: [CAS, PinpointStrike],
|
FrontlineAttackEvent: [[CAS, PinpointStrike], [CAP]],
|
||||||
FrontlinePatrolEvent: [CAP, PinpointStrike],
|
FrontlinePatrolEvent: [[CAP, PinpointStrike], [CAP]],
|
||||||
BaseAttackEvent: [CAP, CAS, PinpointStrike],
|
BaseAttackEvent: [[CAP, CAS, PinpointStrike], [CAP, CAS, PinpointStrike]],
|
||||||
StrikeEvent: [CAP, CAS],
|
StrikeEvent: [[CAP, CAS], [CAP]],
|
||||||
InterceptEvent: [CAP],
|
InterceptEvent: [[CAP], [CAP]],
|
||||||
InsurgentAttackEvent: [CAS],
|
InsurgentAttackEvent: [[CAS], [CAP]],
|
||||||
NavalInterceptEvent: [CAS],
|
NavalInterceptEvent: [[CAS], [CAP]],
|
||||||
InfantryTransportEvent: [Embarking],
|
InfantryTransportEvent: [[Embarking], [CAP]],
|
||||||
}
|
}
|
||||||
|
|
||||||
AI_BAN_FOR_EVENTS = {
|
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)
|
Label(self.frame, text="Client slots", **STYLES["widget"]).grid(row=row, column=3, columnspan=2)
|
||||||
row += 1
|
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():
|
for unit_type, count in self.base.aircraft.items():
|
||||||
if filter_to and db.unit_task(unit_type) not in filter_to:
|
if filter_to and db.unit_task(unit_type) not in filter_to:
|
||||||
continue
|
continue
|
||||||
@ -262,12 +263,6 @@ class EventMenu(Menu):
|
|||||||
elif type(self.event) is NavalInterceptEvent:
|
elif type(self.event) is NavalInterceptEvent:
|
||||||
e = self.event # type: 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):
|
if self.game.is_player_attack(self.event):
|
||||||
e.player_attacking(strikegroup=scrambled_aircraft, clients=scrambled_clients)
|
e.player_attacking(strikegroup=scrambled_aircraft, clients=scrambled_clients)
|
||||||
else:
|
else:
|
||||||
|
|||||||
@ -86,6 +86,10 @@ class EventResultsMenu(Menu):
|
|||||||
|
|
||||||
header("Enemy losses")
|
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():
|
for unit_type, count in self.enemy_losses.items():
|
||||||
if count == 0:
|
if count == 0:
|
||||||
continue
|
continue
|
||||||
|
|||||||
@ -51,10 +51,9 @@ class MainMenu(Menu):
|
|||||||
nonlocal row, body
|
nonlocal row, body
|
||||||
frame = LabelFrame(body, **STYLES["label-frame"])
|
frame = LabelFrame(body, **STYLES["label-frame"])
|
||||||
frame.grid(row=row, sticky=NSEW)
|
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.defender_name == self.game.player and "Enemy attacking: " or "",
|
||||||
event,
|
event
|
||||||
event.to_cp,
|
|
||||||
), aspect=1600, **STYLES["widget"]).grid(column=0, row=0, sticky=NSEW)
|
), 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)
|
Button(body, text=">", command=self.start_event(event), **STYLES["btn-primary"]).grid(column=1, row=row, sticky=E)
|
||||||
row += 1
|
row += 1
|
||||||
@ -63,9 +62,8 @@ class MainMenu(Menu):
|
|||||||
nonlocal row, body
|
nonlocal row, body
|
||||||
Label(body, text=text, **STYLES["strong"]).grid(column=0, columnspan=2, row=row, sticky=N+EW, pady=(pady,0)); row += 1
|
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 = 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.from_cp.name)
|
||||||
events.sort(key=lambda x: x.informational and 2 or (self.game.is_player_attack(x) and 1 or 0))
|
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:
|
for event in events:
|
||||||
if not event.informational:
|
if not event.informational:
|
||||||
if self.game.is_player_attack(event):
|
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:
|
else:
|
||||||
new_destination = "Enemy attack"
|
new_destination = "Enemy attack"
|
||||||
if destination != new_destination:
|
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):
|
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)
|
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)
|
start_coords = self.transform_point(frontline_pos, treshold=10)
|
||||||
end_coords = self.transform_point(frontline_pos.point_from_heading(heading, distance), treshold=60)
|
end_coords = self.transform_point(frontline_pos.point_from_heading(heading, distance), treshold=60)
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user