diff --git a/.gitignore b/.gitignore index d8229092..2aa12800 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,7 @@ __pycache__ build/* resources/payloads/*.lua +venv logs.txt .DS_Store # User-specific stuff diff --git a/game/event/frontlineattack.py b/game/event/frontlineattack.py index f89f5908..e3a6d05b 100644 --- a/game/event/frontlineattack.py +++ b/game/event/frontlineattack.py @@ -16,7 +16,7 @@ class FrontlineAttackEvent(Event): ATTACKER_AMOUNT_FACTOR = 0.4 ATTACKER_DEFENDER_FACTOR = 0.7 STRENGTH_INFLUENCE = 0.2 - SUCCESS_TARGETS_HIT_PERCENTAGE = 0.25 + SUCCESS_FACTOR = 1.5 defenders = None # type: db.ArmorDict @@ -28,16 +28,13 @@ class FrontlineAttackEvent(Event): return "Frontline attack from {} at {}".format(self.from_cp, self.to_cp) def is_successfull(self, debriefing: Debriefing): - total_targets = sum(self.defenders.values()) - destroyed_targets = 0 - for unit, count in debriefing.destroyed_units[self.defender_name].items(): - if unit in self.defenders: - destroyed_targets += count - + alive_attackers = sum([v for k, v in debriefing.alive_units[self.attacker_name].items() if db.unit_task(k) == PinpointStrike]) + alive_defenders = sum([v for k, v in debriefing.alive_units[self.defender_name].items() if db.unit_task(k) == PinpointStrike]) + attackers_success = (float(alive_attackers) / alive_defenders) > self.SUCCESS_FACTOR if self.from_cp.captured: - return float(destroyed_targets) / total_targets >= self.SUCCESS_TARGETS_HIT_PERCENTAGE + return attackers_success else: - return float(destroyed_targets) / total_targets < self.SUCCESS_TARGETS_HIT_PERCENTAGE + return not attackers_success def commit(self, debriefing: Debriefing): super(FrontlineAttackEvent, self).commit(debriefing) diff --git a/game/event/frontlinepatrol.py b/game/event/frontlinepatrol.py index 02fb9062..df22d210 100644 --- a/game/event/frontlinepatrol.py +++ b/game/event/frontlinepatrol.py @@ -13,7 +13,7 @@ from userdata.debriefing import Debriefing class FrontlinePatrolEvent(Event): ESCORT_FACTOR = 0.5 STRENGTH_INFLUENCE = 0.2 - SUCCESS_TARGETS_HIT_PERCENTAGE = 0.33 + SUCCESS_FACTOR = 0.8 cas = None # type: db.PlaneDict escort = None # type: db.PlaneDict @@ -25,6 +25,7 @@ class FrontlinePatrolEvent(Event): def __str__(self): return "Frontline CAP from {} at {}".format(self.from_cp, self.to_cp) + """ def is_successfull(self, debriefing: Debriefing): total_targets = sum(self.cas.values()) destroyed_targets = 0 @@ -36,6 +37,16 @@ class FrontlinePatrolEvent(Event): return float(destroyed_targets) / total_targets >= self.SUCCESS_TARGETS_HIT_PERCENTAGE else: return float(destroyed_targets) / total_targets < self.SUCCESS_TARGETS_HIT_PERCENTAGE + """ + + def is_successfull(self, debriefing: Debriefing): + alive_attackers = sum([v for k, v in debriefing.alive_units[self.attacker_name].items() if db.unit_task(k) == PinpointStrike]) + alive_defenders = sum([v for k, v in debriefing.alive_units[self.defender_name].items() if db.unit_task(k) == PinpointStrike]) + attackers_success = (float(alive_attackers) / alive_defenders) >= self.SUCCESS_FACTOR + if self.from_cp.captured: + return attackers_success + else: + return not attackers_success def commit(self, debriefing: Debriefing): super(FrontlinePatrolEvent, self).commit(debriefing) @@ -54,7 +65,7 @@ class FrontlinePatrolEvent(Event): def skip(self): pass - def player_attacking(self, interceptors: db.PlaneDict, clients: db.PlaneDict): + def player_attacking(self, interceptors: db.PlaneDict, clients: db.PlaneDict, armor: db.ArmorDict): self.cas = self.to_cp.base.scramble_cas(self.game.settings.multiplier) self.escort = self.to_cp.base.scramble_sweep(self.game.settings.multiplier * self.ESCORT_FACTOR) @@ -65,10 +76,12 @@ class FrontlinePatrolEvent(Event): defender_clients={}, from_cp=self.from_cp, to_cp=self.to_cp) + + defenders = self.to_cp.base.assemble_attack() op.setup(cas=self.cas, escort=self.escort, interceptors=interceptors, - armor_attackers=self.from_cp.base.assemble_attack(), - armor_defenders=self.to_cp.base.assemble_attack()) + armor_attackers=db.unitdict_restrict_count(armor, sum(defenders.values())), + armor_defenders=defenders) self.operation = op diff --git a/gen/aircraft.py b/gen/aircraft.py index 3071be1a..5b961738 100644 --- a/gen/aircraft.py +++ b/gen/aircraft.py @@ -262,11 +262,12 @@ class AircraftConflictGenerator: client_count=client_count, at=at and at or self._group_point(self.conflict.air_defenders_location)) - pos = self.conflict.air_defenders_location.point_from_heading(self.conflict.heading-90, CAP_CAS_DISTANCE) - waypoint = group.add_waypoint(pos, CAS_ALTITUDE, WARM_START_AIRSPEED) + location = self._group_point(self.conflict.air_defenders_location) + insertion_point = self.conflict.find_insertion_point(location) + waypoint = group.add_waypoint(insertion_point, CAS_ALTITUDE, WARM_START_AIRSPEED) if self.conflict.is_vector: - destination_tail = self.conflict.tail.distance_to_point(pos) > self.conflict.position.distance_to_point(pos) + destination_tail = self.conflict.tail.distance_to_point(insertion_point) > self.conflict.position.distance_to_point(insertion_point) group.add_waypoint(destination_tail and self.conflict.tail or self.conflict.position, CAS_ALTITUDE, WARM_START_AIRSPEED) group.task = CAS.name diff --git a/gen/conflictgen.py b/gen/conflictgen.py index ad31d529..712f5f81 100644 --- a/gen/conflictgen.py +++ b/gen/conflictgen.py @@ -20,7 +20,7 @@ AIR_DISTANCE = 40000 CAPTURE_AIR_ATTACKERS_DISTANCE = 25000 CAPTURE_AIR_DEFENDERS_DISTANCE = 60000 -CAP_CAS_DISTANCE = 10000 +CAP_CAS_DISTANCE = 10000, 120000 GROUND_INTERCEPT_SPREAD = 5000 GROUND_DISTANCE_FACTOR = 1 @@ -123,6 +123,21 @@ class Conflict: def to_size(self): return self.to_cp.size * GROUND_DISTANCE_FACTOR + def find_insertion_point(self, other_point: Point) -> Point: + dx = self.position.x - self.tail.x + dy = self.position.y - self.tail.y + dr2 = float(dx ** 2 + dy ** 2) + + lerp = ((other_point.x - self.tail.x) * dx + (other_point.y - self.tail.y) * dy) / dr2 + if lerp < 0: + lerp = 0 + elif lerp > 1: + lerp = 1 + + x = lerp * dx + self.tail.x + y = lerp * dy + self.tail.y + return Point(x, y) + @classmethod def has_frontline_between(cls, from_cp: ControlPoint, to_cp: ControlPoint) -> bool: return from_cp.has_frontline and to_cp.has_frontline @@ -138,6 +153,7 @@ class Conflict: center_position, heading = cls.frontline_position(from_cp, to_cp) 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): @@ -266,7 +282,7 @@ class Conflict: position, heading, distance = cls.frontline_vector(from_cp, to_cp, theater) attack_position = position.point_from_heading(heading, randint(0, int(distance))) attackers_position = attack_position.point_from_heading(heading - 90, AIR_DISTANCE) - defenders_position = attack_position.point_from_heading(heading + 90, CAP_CAS_DISTANCE) + defenders_position = attack_position.point_from_heading(heading + 90, random.randint(*CAP_CAS_DISTANCE)) return cls( position=position, diff --git a/gen/environmentgen.py b/gen/environmentgen.py index 3e106ffd..08b5265a 100644 --- a/gen/environmentgen.py +++ b/gen/environmentgen.py @@ -18,7 +18,7 @@ from gen import * WEATHER_CLOUD_BASE = 2000, 3000 WEATHER_CLOUD_DENSITY = 1, 8 WEATHER_CLOUD_THICKNESS = 100, 400 -WEATHER_CLOUD_BASE_MIN = 2400 +WEATHER_CLOUD_BASE_MIN = 1600 RANDOM_TIME = { "night": 5, @@ -28,10 +28,10 @@ RANDOM_TIME = { } RANDOM_WEATHER = { - 1: 5, # heavy rain - 2: 15, # rain - 3: 25, # dynamic - 4: 35, # clear + 1: 10, # heavy rain + 2: 20, # rain + 3: 30, # dynamic + 4: 40, # clear 5: 100, # random } @@ -90,8 +90,13 @@ class EnviromentGenerator: self.mission.weather.wind_at_8000 = Wind(wind_direction, wind_speed * 3) if self.mission.weather.clouds_density > 0: + # sometimes clouds are randomized way too low and need to be fixed self.mission.weather.clouds_base = max(self.mission.weather.clouds_base, WEATHER_CLOUD_BASE_MIN) + if self.mission.weather.wind_at_ground == 0: + # frontline smokes look silly w/o any wind + self.mission.weather.wind_at_ground = random.randint(1, 2) + def generate(self) -> EnvironmentSettings: self._gen_random_time() self._gen_random_weather() diff --git a/ui/eventmenu.py b/ui/eventmenu.py index f699100c..35b5e9a8 100644 --- a/ui/eventmenu.py +++ b/ui/eventmenu.py @@ -8,7 +8,7 @@ from game.event import * UNITTYPES_FOR_EVENTS = { FrontlineAttackEvent: [CAS, PinpointStrike], - FrontlinePatrolEvent: [CAP], + FrontlinePatrolEvent: [CAP, PinpointStrike], InterceptEvent: [CAP], InsurgentAttackEvent: [CAS], NavalInterceptEvent: [CAS], @@ -218,7 +218,7 @@ class EventMenu(Menu): e.player_attacking(armor=scrambled_armor, strikegroup=scrambled_aircraft, clients=scrambled_clients) elif type(self.event) is FrontlinePatrolEvent: e = self.event # type: FrontlinePatrolEvent - e.player_attacking(interceptors=scrambled_aircraft, clients=scrambled_clients) + e.player_attacking(interceptors=scrambled_aircraft, clients=scrambled_clients, armor=scrambled_armor) elif type(self.event) is NavalInterceptEvent: e = self.event # type: NavalInterceptEvent