diff --git a/changelog.md b/changelog.md index b7913bb4..4ee965fd 100644 --- a/changelog.md +++ b/changelog.md @@ -1,18 +1,32 @@ # 2.0.10 -## Features : -* **[UI/UX]** New UI Theme by Deus -* **[UI/UX]** New map backgrounds -* **[Units/Factions]** Added Community A-4E-C support for faction Bluefor Cold War -* **[Units/Factions]** Added MB-339PAN support for faction Bluefor Cold War -* **[Units/Factions]** Added Rafale AI mod support -* **[Units/Factions]** Added faction "France Modded" with units from frenchpack v3.5 mod -* **[Units/Factions]** Added faction "Insurgent modded" with Insurgent units from frenchpack v3.5 mod (Toyota truck) +## Features/Improvements : +* **[UI/UX]** New dark UI Theme and default theme improvement by Deus +* **[UI/UX]** New satellite map backgrounds +* **[UX]** Base menu is opened with a single mouse click +* **[Units/Factions/Mods]** Added Community A-4E-C support for faction Bluefor Cold War +* **[Units/Factions/Mods]** Added MB-339PAN support for faction Bluefor Cold War +* **[Units/Factions/Mods]** Added Rafale AI mod support +* **[Units/Factions/Mods]** Added faction "France Modded" with units from frenchpack v3.5 mod +* **[Units/Factions/Mods]** Added faction "Insurgent modded" with Insurgent units from frenchpack v3.5 mod (Toyota truck) +* **[New Game Wizard]** Added the list of required mods for modded factions. +* **[Mission Generator]** Artillery units will start firing mission after a random delay. It should reduces lag spikes induced by artillery strikes by spreading them out. +* **[Mission Generator]** The briefing will now contain the carrier ATC frequency +* **[Mission Generator]** The briefing contains a small section about the war on the ground. +* **[Mission Generator]** DCS Liberation picture added to the in-game briefing ## Fixed issues : * **[Mission Generator]** Carrier will sail into the wind, not in the same direction * **[Mission Generator]** Carrier cold start was not working (flight was starting warm even when cold was selected) +* **[Mission Generator]** Carrier group ships are more spread out + * **[Units/Factions]** Remove JF-17 from USA 2005 faction +* **[Units/Factions]** Removed Oliver Hazard Perry from cold war factions (too powerful sam system) +* **[Bug]** On the persian gulf full map campaign, the two carriers were sharing the same id, this was causing a lot of bugs +* **[Performance]** Tuned the culling setting so that you cannot run into situation where no flights are generated +* **[Mission Generator]** Fixed radio frequency for german WW2 warbirds + +* **[Other]** Other minor fixes # 2.0 RC 9 diff --git a/game/game.py b/game/game.py index 18d4ad89..0c3b695e 100644 --- a/game/game.py +++ b/game/game.py @@ -1,7 +1,7 @@ from datetime import datetime, timedelta from game.db import REWARDS, PLAYER_BUDGET_BASE, sys -from game.game_stats import GameStats +from game.models.game_stats import GameStats from gen.flights.ai_flight_planner import FlightPlanner from gen.ground_forces.ai_ground_planner import GroundPlanner from .event import * @@ -73,7 +73,8 @@ class Game: self.informations = [] self.informations.append(Information("Game Start", "-" * 40, 0)) self.__culling_points = self.compute_conflicts_position() - + self.__frontlineData = [] + self.__destroyed_units = [] @property def player_faction(self): @@ -175,33 +176,6 @@ class Game: else: return event.name == self.player_name - # 1 = red, 2 = blue - def get_player_coalition_id(self): - if self.player_country in db.BLUEFOR_FACTIONS: - return 2 - else: - return 1 - - def get_enemy_coalition_id(self): - if self.get_player_coalition_id() == 1: - return 2 - else: - return 1 - - def get_player_color(self): - if self.get_player_coalition_id() == 1: - return "red" - else: - return "blue" - - def get_enemy_color(self): - if self.get_player_coalition_id() == 1: - return "blue" - else: - return "red" - - - def pass_turn(self, no_action=False, ignored_cps: typing.Collection[ControlPoint] = None): logging.info("Pass turn") @@ -383,6 +357,12 @@ class Game: return points + def add_destroyed_units(self, destroyed_unit_data): + self.__destroyed_units.append(destroyed_unit_data) + + def get_destroyed_units(self): + return self.__destroyed_units + def position_culled(self, pos): """ Check if unit can be generated at given position depending on culling performance settings @@ -397,3 +377,39 @@ class Game: return False return True + # 1 = red, 2 = blue + def get_player_coalition_id(self): + if self.player_country in db.BLUEFOR_FACTIONS: + return 2 + else: + return 1 + + def get_enemy_coalition_id(self): + if self.get_player_coalition_id() == 1: + return 2 + else: + return 1 + + def get_player_coalition(self): + if self.player_country in db.BLUEFOR_FACTIONS: + return dcs.action.Coalition.Blue + else: + return dcs.action.Coalition.Red + + def get_enemy_coalition(self): + if self.player_country == 1: + return dcs.action.Coalition.Blue + else: + return dcs.action.Coalition.Red + + def get_player_color(self): + if self.get_player_coalition_id() == 1: + return "red" + else: + return "blue" + + def get_enemy_color(self): + if self.get_player_coalition_id() == 1: + return "blue" + else: + return "red" \ No newline at end of file diff --git a/game/models/destroyed_units.py b/game/models/destroyed_units.py new file mode 100644 index 00000000..ade75cc4 --- /dev/null +++ b/game/models/destroyed_units.py @@ -0,0 +1,14 @@ +class DestroyedUnit: + """ + Store info about a destroyed unit + """ + + x: int + y: int + name: str + + def __init__(self, x , y, name): + self.x = x + self.y = y + self.name = name + diff --git a/game/models/frontline_data.py b/game/models/frontline_data.py new file mode 100644 index 00000000..94947135 --- /dev/null +++ b/game/models/frontline_data.py @@ -0,0 +1,13 @@ +from theater import ControlPoint + + +class FrontlineData: + """ + This Data structure will store information about an existing frontline + """ + + def __init__(self, from_cp:ControlPoint, to_cp: ControlPoint): + self.to_cp = to_cp + self.from_cp = from_cp + self.enemy_units_position = [] + self.blue_units_position = [] diff --git a/game/game_stats.py b/game/models/game_stats.py similarity index 100% rename from game/game_stats.py rename to game/models/game_stats.py diff --git a/game/operation/operation.py b/game/operation/operation.py index 1253badc..cf7e7b12 100644 --- a/game/operation/operation.py +++ b/game/operation/operation.py @@ -123,6 +123,21 @@ class Operation: # Generate ground object first self.groundobjectgen.generate() + # Generate destroyed units + for d in self.game.get_destroyed_units(): + utype = db.unit_type_from_name(d["type"]) + pos = Point(d["x"], d["z"]) + if utype is not None and not self.game.position_culled(pos): + self.current_mission.static_group( + country=self.current_mission.country(self.game.player_country), + name="", + _type=utype, + hidden=True, + position=pos, + heading=d["orientation"], + dead=True, + ) + # Air Support (Tanker & Awacs) self.airsupportgen.generate(self.is_awacs_enabled) diff --git a/gen/briefinggen.py b/gen/briefinggen.py index 3b705bb7..6f605904 100644 --- a/gen/briefinggen.py +++ b/gen/briefinggen.py @@ -54,7 +54,6 @@ class BriefingGenerator: flight_unit_name = db.unit_type_name(flight.unit_type) self.description += flight.flight_type.name + " " + flight_unit_name + " x " + str(flight.count) + ", departing in " + str(flight.scheduled_in) + " minutes \n" - def generate(self): self.description = "" @@ -62,8 +61,7 @@ class BriefingGenerator: self.description += "DCS Liberation turn #" + str(self.game.turn) + "\n" self.description += "=" * 15 + "\n\n" - self.description += "Current situation:\n" - self.description += "=" * 15 + "\n\n" + self.generate_ongoing_war_text() self.description += "\n"*2 self.description += "Your flights:" + "\n" @@ -93,7 +91,7 @@ class BriefingGenerator: for cp in self.game.theater.controlpoints: if cp.captured and cp.cptype in [ControlPointType.LHA_GROUP, ControlPointType.AIRCRAFT_CARRIER_GROUP]: self.description += cp.name + "\n" - self.description += "RADIO : 127.5 Mhz AM" + self.description += "RADIO : 127.5 Mhz AM\n" self.description += "TACAN : " self.description += str(cp.tacanN) if cp.tacanY: @@ -108,4 +106,72 @@ class BriefingGenerator: self.m.set_description_text(self.description) + #self.m.add_picture_blue(os.path.abspath("./resources/ui/splash_screen.png")) NOT working + + + def generate_ongoing_war_text(self): + + self.description += "Current situation:\n" + self.description += "=" * 15 + "\n\n" + + conflict_number = 0 + + for c in self.game.theater.conflicts(): + conflict_number = conflict_number + 1 + if c[0].captured: + player_base = c[0] + enemy_base = c[1] + else: + player_base = c[1] + enemy_base = c[0] + + has_numerical_superiority = player_base.base.total_armor > enemy_base.base.total_armor + self.description += self.__random_frontline_sentence(player_base.name, enemy_base.name) + + if enemy_base.id in player_base.stances.keys(): + stance = player_base.stances[enemy_base.id] + + if player_base.base.total_armor == 0: + self.description += "We do not have a single vehicle available to hold our position, the situation is critical, and we will lose ground inevitably.\n" + elif enemy_base.base.total_armor == 0: + self.description += "The enemy forces have been crushed, we will be able to make significant progress toward " + enemy_base.name + ". \n" + if stance == CombatStance.AGGRESIVE: + if has_numerical_superiority: + self.description += "On this location, our ground forces will try to make progress against the enemy" + self.description += ". As the enemy is outnumbered, our forces should have no issue making progress.\n" + elif has_numerical_superiority: + self.description += "On this location, our ground forces will try an audacious assault against enemies in superior numbers. The operation is risky, and the enemy might counter attack.\n" + elif stance == CombatStance.ELIMINATION: + if has_numerical_superiority: + self.description += "On this location, our ground forces will focus on the destruction of enemy assets, before attempting to make progress toward " + enemy_base.name + ". " + self.description += "The enemy is already outnumbered, and this maneuver might draw a final blow to their forces.\n" + elif has_numerical_superiority: + self.description += "On this location, our ground forces will try an audacious assault against enemies in superior numbers. The operation is risky, and the enemy might counter attack.\n" + elif stance == CombatStance.BREAKTHROUGH: + if has_numerical_superiority: + self.description += "On this location, our ground forces will focus on progression toward " + enemy_base.name + ".\n" + elif has_numerical_superiority: + self.description += "On this location, our ground forces have been ordered to rush toward " + enemy_base.name + ". Wish them luck... We are also expecting a counter attack.\n" + elif stance in [CombatStance.DEFENSIVE, CombatStance.AMBUSH]: + if has_numerical_superiority: + self.description += "On this location, our ground forces will hold position. We are not expecting an enemy assault.\n" + elif has_numerical_superiority: + self.description += "On this location, our ground forces have been ordered to hold still, and defend against enemy attacks. An enemy assault might be iminent.\n" + + if conflict_number == 0: + self.description += "There are currently no fights on the ground.\n" + + self.description += "\n\n" + + + def __random_frontline_sentence(self, player_base_name, enemy_base_name): + templates = [ + "There are combats between {} and {}. ", + "The war on the ground is still going on between {} an {}. ", + "Our ground forces in {} are opposed to enemy forces based in {}. ", + "Our forces from {} are fighting enemies based in {}. ", + "There is an active frontline between {} and {}. ", + ] + return random.choice(templates).format(player_base_name, enemy_base_name) + diff --git a/resources/scripts/dcs_liberation.lua b/resources/scripts/dcs_liberation.lua index 2b1782cf..6df898de 100644 --- a/resources/scripts/dcs_liberation.lua +++ b/resources/scripts/dcs_liberation.lua @@ -9,6 +9,7 @@ killed_aircrafts = {} killed_ground_units = {} weapons_fired = {} base_capture_events = {} +destroyed_objects_positions = {} mission_ended = false local function messageAll(message) @@ -21,6 +22,7 @@ end write_state = function() --messageAll("Writing DCS Liberation State...") + --logger.info("Writing DCS LIBERATION state") local fp = io.open(debriefing_file_location, 'w') local game_state = { ["killed_aircrafts"] = killed_aircrafts, @@ -28,9 +30,11 @@ write_state = function() ["weapons_fired"] = weapons_fired, ["base_capture_events"] = base_capture_events, ["mission_ended"] = mission_ended, + ["destroyed_objects_positions"] = destroyed_objects_positions, } fp:write(json:encode(game_state)) fp:close() + --logger.info("Done writing DCS Liberation state") --messageAll("Done writing DCS Liberation state.") end @@ -45,6 +49,14 @@ local function onEvent(event) if event.id == world.event.S_EVENT_DEAD and event.initiator then killed_ground_units[#killed_ground_units + 1] = event.initiator.getName(event.initiator) + local position = event.initiator.getPosition(event.initiator) + local destruction = {} + destruction.x = position.p.x + destruction.y = position.p.y + destruction.z = position.p.z + destruction.type = event.initiator:getTypeName() + destruction.orientation = mist.getHeading(event.initiator) * 57.3 + destroyed_objects_positions[#destroyed_objects_positions + 1] = destruction end if event.id == world.event.S_EVENT_SHOT and event.weapon then diff --git a/userdata/debriefing.py b/userdata/debriefing.py index 77364f5b..be30930e 100644 --- a/userdata/debriefing.py +++ b/userdata/debriefing.py @@ -35,7 +35,9 @@ class Debriefing: self.killed_ground_units = state_data["killed_ground_units"] self.weapons_fired = state_data["weapons_fired"] self.mission_ended = state_data["mission_ended"] + self.destroyed_units = state_data["destroyed_objects_positions"] + self.__destroyed_units = [] logging.info("--------------------------------") logging.info("Starting Debriefing preprocessing") logging.info("--------------------------------") @@ -44,6 +46,7 @@ class Debriefing: logging.info(self.killed_ground_units) logging.info(self.weapons_fired) logging.info(self.mission_ended) + logging.info(self.destroyed_units) logging.info("--------------------------------") self.player_country_id = db.country_id_from_name(game.player_country) @@ -155,6 +158,9 @@ class Debriefing: else: self.enemy_dead_buildings_dict[a.type] = 1 + for destroyed_unit in self.destroyed_units: + game.add_destroyed_units(destroyed_unit) + logging.info("--------------------------------") logging.info("Debriefing pre process results :") logging.info("--------------------------------")