diff --git a/game/db.py b/game/db.py index 6b4b863a..88de9284 100644 --- a/game/db.py +++ b/game/db.py @@ -343,6 +343,10 @@ PLANE_PAYLOAD_OVERRIDES = { CAP: "AIM-120*4,AIM-9*2,AIM-7*2,Fuel", }, + Su_25T: { + CAS: "APU-8 Vikhr-M*2,Kh-25ML,R-73*2,SPPU-22*2,Mercury LLTV Pod,MPS-410", + }, + Su_33: { CAP: "R-73*4,R-27R*2,R-27ER*6", }, diff --git a/game/event/frontlineattack.py b/game/event/frontlineattack.py index 5560c714..a4a923b0 100644 --- a/game/event/frontlineattack.py +++ b/game/event/frontlineattack.py @@ -8,7 +8,7 @@ class FrontlineAttackEvent(Event): TARGET_AMOUNT_FACTOR = 0.5 ATTACKER_AMOUNT_FACTOR = 0.4 ATTACKER_DEFENDER_FACTOR = 0.7 - STRENGTH_INFLUENCE = 0.2 + STRENGTH_INFLUENCE = 0.3 SUCCESS_FACTOR = 1.5 defenders = None # type: db.ArmorDict diff --git a/game/event/frontlinepatrol.py b/game/event/frontlinepatrol.py index 4e137533..ef891c3e 100644 --- a/game/event/frontlinepatrol.py +++ b/game/event/frontlinepatrol.py @@ -5,7 +5,7 @@ from userdata.debriefing import Debriefing class FrontlinePatrolEvent(Event): ESCORT_FACTOR = 0.5 - STRENGTH_INFLUENCE = 0.2 + STRENGTH_INFLUENCE = 0.3 SUCCESS_FACTOR = 0.8 cas = None # type: db.PlaneDict diff --git a/game/event/strike.py b/game/event/strike.py index 5785b7cc..e8bea076 100644 --- a/game/event/strike.py +++ b/game/event/strike.py @@ -5,7 +5,7 @@ from .event import * class StrikeEvent(Event): STRENGTH_INFLUENCE = 0.0 - SINGLE_OBJECT_STRENGTH_INFLUENCE = 0.03 + SINGLE_OBJECT_STRENGTH_INFLUENCE = 0.05 def __str__(self): return "Strike" @@ -39,6 +39,7 @@ class StrikeEvent(Event): def commit(self, debriefing: Debriefing): super(StrikeEvent, self).commit(debriefing) + self.to_cp.base.affect_strength(-self.SINGLE_OBJECT_STRENGTH_INFLUENCE * len(debriefing.destroyed_objects)) def player_attacking(self, flights: db.TaskForceDict): diff --git a/game/game.py b/game/game.py index 3fee2d3c..c73f519c 100644 --- a/game/game.py +++ b/game/game.py @@ -25,7 +25,7 @@ COMMISION_LIMITS_FACTORS = { COMMISION_AMOUNTS_SCALE = 1.5 COMMISION_AMOUNTS_FACTORS = { - PinpointStrike: 6, + PinpointStrike: 3, CAS: 1, CAP: 2, AirDefence: 0.3, @@ -33,30 +33,39 @@ COMMISION_AMOUNTS_FACTORS = { PLAYER_INTERCEPT_GLOBAL_PROBABILITY_BASE = 30 PLAYER_INTERCEPT_GLOBAL_PROBABILITY_LOG = 2 -PLAYER_BASEATTACK_THRESHOLD = 0.2 +PLAYER_BASEATTACK_THRESHOLD = 0.4 """ Various events probabilities. First key is player probabilty, second is enemy probability. For the enemy events, only 1 event of each type could be generated for a turn. Events: -* CaptureEvent - capture base +* BaseAttackEvent - capture base * InterceptEvent - air intercept -* FrontlineAttack - frontline attack -* GroundAttackEvent - destroy insurgents +* FrontlineAttackEvent - frontline attack +* FrontlineCAPEvent - frontline attack * NavalInterceptEvent - naval intercept -* AntiAAStrikeEvent - anti-AA strike +* StrikeEvent - strike event * InfantryTransportEvent - helicopter infantry transport """ EVENT_PROBABILITIES = { - BaseAttackEvent: [100, 15], + # events always present; only for the player FrontlineAttackEvent: [100, 0], FrontlinePatrolEvent: [100, 0], StrikeEvent: [100, 0], - InterceptEvent: [25, 15], - InsurgentAttackEvent: [0, 10], - NavalInterceptEvent: [25, 15], + + # events randomly present; only for the player InfantryTransportEvent: [25, 0], + + # events conditionally present; for both enemy and player + BaseAttackEvent: [100, 15], + + # events randomly present; for both enemy and player + InterceptEvent: [25, 15], + NavalInterceptEvent: [25, 15], + + # events randomly present; only for the enemy + InsurgentAttackEvent: [0, 10], } # amount of strength player bases recover for the turn @@ -117,47 +126,62 @@ class Game: for event_class, (player_probability, enemy_probability) in EVENT_PROBABILITIES.items(): if event_class in [FrontlineAttackEvent, FrontlinePatrolEvent, InfantryTransportEvent]: + # skip events requiring frontline if not Conflict.has_frontline_between(player_cp, enemy_cp): continue if player_cp.is_global: + # skip events requiring ground CP if event_class not in [InterceptEvent, StrikeEvent, NavalInterceptEvent]: continue if player_probability == 100 or self._roll(player_probability, player_cp.base.strength): if event_class == NavalInterceptEvent and enemy_cp.radials == LAND: + # skip naval events for non-coastal CPs pass else: if event_class == BaseAttackEvent and enemy_cp.base.strength > PLAYER_BASEATTACK_THRESHOLD: + # skip base attack events for CPs yet too strong pass else: if event_class == StrikeEvent and not enemy_cp.ground_objects: + # skip strikes in case of no targets pass else: + # finally append the event self.events.append(event_class(self.player, self.enemy, player_cp, enemy_cp, self)) elif enemy_probability == 100 or self._roll(enemy_probability, enemy_cp.base.strength): if event_class in enemy_generated_types: + # skip already generated event types continue if player_cp in self.ignored_cps: + # skip attacks against ignored CPs (for example just captured ones) continue if enemy_cp.base.total_planes == 0: + # skip event if there's no planes on the base continue if event_class == NavalInterceptEvent: if player_cp.radials == LAND: + # skip naval events for non-coastal CPs continue elif event_class == StrikeEvent: if not player_cp.ground_objects: + # skip strikes if there's no ground objects continue elif event_class == BaseAttackEvent: if enemy_cap_generated: + # skip base attack event if there's another one going on continue if enemy_cp.base.total_armor == 0: + # skip base attack if there's no armor continue + enemy_cap_generated = True + # finally append the event enemy_generated_types.append(event_class) self.events.append(event_class(self.enemy, self.player, enemy_cp, player_cp, self)) diff --git a/gen/triggergen.py b/gen/triggergen.py index d551d429..aa3f06c0 100644 --- a/gen/triggergen.py +++ b/gen/triggergen.py @@ -23,7 +23,7 @@ REGROUP_ALT = 5000 TRIGGER_WAYPOINT_OFFSET = 2 TRIGGER_MIN_DISTANCE_FROM_START = 10000 -TRIGGER_RADIUS_MINIMUM = 25000 +TRIGGER_RADIUS_MINIMUM = 20000 TRIGGER_RADIUS_SMALL = 30000 TRIGGER_RADIUS_MEDIUM = 100000 diff --git a/resources/caulandmap.p b/resources/caulandmap.p index 8fc89015..f07d541f 100644 Binary files a/resources/caulandmap.p and b/resources/caulandmap.p differ diff --git a/resources/tools/cau_terrain.miz b/resources/tools/cau_terrain.miz index cfd7c7df..03674769 100644 Binary files a/resources/tools/cau_terrain.miz and b/resources/tools/cau_terrain.miz differ diff --git a/submodules/dcs b/submodules/dcs index e2f8478c..54eab60f 160000 --- a/submodules/dcs +++ b/submodules/dcs @@ -1 +1 @@ -Subproject commit e2f8478c4ed95588cea5837e62fc982decb7e133 +Subproject commit 54eab60f228847f2d92e344e6885eca855354f41 diff --git a/ui/eventmenu.py b/ui/eventmenu.py index 86e4a3b8..4cb7335b 100644 --- a/ui/eventmenu.py +++ b/ui/eventmenu.py @@ -96,15 +96,15 @@ class EventMenu(Menu): header("Support:") # Options awacs_enabled = self.game.budget >= AWACS_BUDGET_COST and NORMAL or DISABLED - Checkbutton(self.frame, var=self.awacs, state=awacs_enabled, **STYLES["radiobutton"]).grid(row=row, column=2, sticky=E) Label(self.frame, text="AWACS ({}m)".format(AWACS_BUDGET_COST), **STYLES["widget"]).grid(row=row, column=0, sticky=W, pady=5) + Checkbutton(self.frame, var=self.awacs, state=awacs_enabled, **STYLES["radiobutton"]).grid(row=row, column=4, sticky=E) row += 1 Label(self.frame, text="Combined Arms Slots", **STYLES["widget"]).grid(row=row, sticky=W) self.ca_slot_entry = Entry(self.frame, width=2) self.ca_slot_entry.insert(0, "0") - self.ca_slot_entry.grid(column=1, row=row, sticky=W, padx=5) - Button(self.frame, text="+", command=self.add_ca_slot, **STYLES["btn-primary"]).grid(column=2, row=row, padx=5, sticky=W) + self.ca_slot_entry.grid(column=3, row=row, sticky=E, padx=5) + Button(self.frame, text="+", command=self.add_ca_slot, **STYLES["btn-primary"]).grid(column=4, row=row, padx=5, sticky=W) row += 1 header("Ready?") diff --git a/ui/eventresultsmenu.py b/ui/eventresultsmenu.py index 5f4e3ec9..40d7ef05 100644 --- a/ui/eventresultsmenu.py +++ b/ui/eventresultsmenu.py @@ -118,7 +118,7 @@ class EventResultsMenu(Menu): def simulate_result(self, player_factor: float, enemy_factor: float): def action(): - debriefing = Debriefing({}, []) + debriefing = Debriefing({}) def count(country: Country) -> typing.Dict[UnitType, int]: result = {} diff --git a/ui/mainmenu.py b/ui/mainmenu.py index 88f13bd8..dd828da1 100644 --- a/ui/mainmenu.py +++ b/ui/mainmenu.py @@ -50,20 +50,21 @@ class MainMenu(Menu): nonlocal row, body frame = LabelFrame(body, **STYLES["label-frame"]) frame.grid(row=row, sticky=NSEW) - Message(frame, text="{}{}".format( - event.defender_name == self.game.player and "Enemy attacking: " or "", + Message(frame, text="{}".format( 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 - def departure_header(text): + def departure_header(text, style="strong"): nonlocal row, body - Label(body, text=text, **STYLES["strong"]).grid(column=0, columnspan=2, row=row, sticky=N+EW, pady=(0, 5)); row += 1 + Label(body, text=text, **STYLES[style]).grid(column=0, columnspan=2, row=row, sticky=N+EW, pady=(0, 5)) + row += 1 - def destination_header(text, pady=0): + def destination_header(text): nonlocal row, body - Label(body, text=text, **STYLES["substrong"]).grid(column=0, columnspan=2, row=row, sticky=N+EW, pady=(pady,0)); row += 1 + Label(body, text=text, **STYLES["substrong"]).grid(column=0, columnspan=2, row=row, sticky=N+EW) + row += 1 events = self.game.events events.sort(key=lambda x: x.to_cp.name) @@ -74,22 +75,25 @@ class MainMenu(Menu): departure = None for event in events: - new_departure = event.from_cp.name if not event.informational else "Deliveries" + if event.informational: + new_departure = "Deliveries" + elif not self.game.is_player_attack(event): + new_departure = "Enemy attack" + else: + new_departure = event.from_cp.name + if new_departure != departure: body = LabelFrame(self.frame, **STYLES["body"]) - body.grid(column=column, row=1, sticky=NSEW) + body.grid(column=column, row=1, sticky=N+EW) row = 0 column += 1 departure = new_departure - departure_header(new_departure) + departure_header(new_departure, style="strong" if self.game.is_player_attack(event) else "supstrong") destination = None if not event.informational: - if self.game.is_player_attack(event): - new_destination = "At {}".format(event.to_cp.name) - else: - new_destination = "Enemy attack" + new_destination = "At {}".format(event.to_cp.name) if destination != new_destination: destination_header(new_destination) destination = new_destination diff --git a/ui/styles.py b/ui/styles.py index bd5911ec..66aab755 100644 --- a/ui/styles.py +++ b/ui/styles.py @@ -29,6 +29,7 @@ STYLES["frame-wrapper"] = {"bg": BG_COLOR, "relief":"sunken"} STYLES["body"] = {"bg": BG_COLOR, "padx": 10, "pady": 10} STYLES["strong"] = {"font": BOLD_FONT, "bg": BG_TITLE_COLOR, "fg": FG_COLOR} STYLES["substrong"] = {"font": BOLD_FONT, "bg": BG_SUBTITLE_COLOR, "fg": FG_COLOR} +STYLES["supstrong"] = {"font": BOLD_FONT, "bg": RED, "fg": FG_COLOR} STYLES["strong-grey"] = {"font": BOLD_FONT, "bg": BG_TITLE_COLOR, "fg": FG_COLOR_LIGHT} STYLES["mission-preview"] = {"font": BOLD_FONT, "bg": YELLOW, "fg": FG_COLOR}