mirror of
https://github.com/dcs-retribution/dcs-retribution.git
synced 2025-11-10 15:41:24 +00:00
427 lines
14 KiB
Python
427 lines
14 KiB
Python
import logging
|
|
import math
|
|
import pickle
|
|
import random
|
|
import typing
|
|
|
|
from dcs.mapping import Point
|
|
from dcs.task import CAP, CAS, PinpointStrike
|
|
from dcs.vehicles import AirDefence
|
|
|
|
from game import db
|
|
from game.data.building_data import DEFAULT_AVAILABLE_BUILDINGS
|
|
from game.settings import Settings
|
|
from gen import namegen
|
|
from gen.defenses.armor_group_generator import generate_armor_group
|
|
from gen.fleet.ship_group_generator import (
|
|
generate_carrier_group,
|
|
generate_lha_group,
|
|
generate_ship_group,
|
|
)
|
|
from gen.missiles.missiles_group_generator import generate_missile_group
|
|
from gen.sam.sam_group_generator import (
|
|
generate_anti_air_group,
|
|
generate_shorad_group,
|
|
)
|
|
from theater import (
|
|
ConflictTheater,
|
|
ControlPoint,
|
|
ControlPointType,
|
|
TheaterGroundObject,
|
|
)
|
|
from theater.conflicttheater import IMPORTANCE_HIGH, IMPORTANCE_LOW
|
|
|
|
UNIT_VARIETY = 6
|
|
UNIT_AMOUNT_FACTOR = 16
|
|
UNIT_COUNT_IMPORTANCE_LOG = 1.3
|
|
|
|
COUNT_BY_TASK = {
|
|
PinpointStrike: 12,
|
|
CAP: 8,
|
|
CAS: 4,
|
|
AirDefence: 1,
|
|
}
|
|
|
|
|
|
def generate_initial_units(theater: ConflictTheater, enemy_country: str, sams: bool, multiplier: float):
|
|
for cp in theater.enemy_points():
|
|
if cp.captured:
|
|
continue
|
|
|
|
# Force reset cp on generation
|
|
cp.base.aircraft = {}
|
|
cp.base.armor = {}
|
|
cp.base.aa = {}
|
|
cp.base.commision_points = {}
|
|
cp.base.strength = 1
|
|
|
|
for task in [PinpointStrike, CAP, CAS, AirDefence]:
|
|
assert cp.importance <= IMPORTANCE_HIGH, "invalid importance {}".format(cp.importance)
|
|
assert cp.importance >= IMPORTANCE_LOW, "invalid importance {}".format(cp.importance)
|
|
|
|
importance_factor = (cp.importance - IMPORTANCE_LOW) / (IMPORTANCE_HIGH - IMPORTANCE_LOW)
|
|
variety = int(UNIT_VARIETY)
|
|
unittypes = db.choose_units(task, importance_factor, variety, enemy_country)
|
|
|
|
if not sams and task == AirDefence:
|
|
unittypes = [x for x in db.find_unittype(AirDefence, enemy_country) if x not in db.SAM_BAN]
|
|
|
|
count_log = math.log(cp.importance + 0.01, UNIT_COUNT_IMPORTANCE_LOG)
|
|
count = max(COUNT_BY_TASK[task] * multiplier * (1+count_log), 1)
|
|
|
|
if len(unittypes) > 0:
|
|
count_per_type = max(int(float(count) / len(unittypes)), 1)
|
|
for unit_type in unittypes:
|
|
logging.info("{} - {} {}".format(cp.name, db.unit_type_name(unit_type), count_per_type))
|
|
cp.base.commision_units({unit_type: count_per_type})
|
|
|
|
|
|
def generate_groundobjects(theater: ConflictTheater, game):
|
|
with open("resources/groundobject_templates.p", "rb") as f:
|
|
tpls = pickle.load(f)
|
|
|
|
group_id = 0
|
|
cp_to_remove = []
|
|
for cp in theater.controlpoints:
|
|
group_id = generate_cp_ground_points(cp, theater, game, group_id, tpls)
|
|
|
|
# CP
|
|
if cp.captured:
|
|
faction_name = game.player_name
|
|
else:
|
|
faction_name = game.enemy_name
|
|
faction = db.FACTIONS[faction_name]
|
|
|
|
if cp.cptype == ControlPointType.AIRCRAFT_CARRIER_GROUP:
|
|
# Create ground object group
|
|
group_id = game.next_group_id()
|
|
g = TheaterGroundObject("CARRIER")
|
|
g.group_id = group_id
|
|
g.object_id = 0
|
|
g.cp_id = cp.id
|
|
g.airbase_group = True
|
|
g.dcs_identifier = "CARRIER"
|
|
g.sea_object = True
|
|
g.obj_name = namegen.random_objective_name()
|
|
g.heading = 0
|
|
g.position = Point(cp.position.x, cp.position.y)
|
|
group = generate_carrier_group(faction_name, game, g)
|
|
g.groups = []
|
|
if group is not None:
|
|
g.groups.append(group)
|
|
cp.ground_objects.append(g)
|
|
# Set new name :
|
|
if len(faction.carrier_names) > 0:
|
|
cp.name = random.choice(faction.carrier_names)
|
|
else:
|
|
cp_to_remove.append(cp)
|
|
elif cp.cptype == ControlPointType.LHA_GROUP:
|
|
# Create ground object group
|
|
group_id = game.next_group_id()
|
|
g = TheaterGroundObject("LHA")
|
|
g.group_id = group_id
|
|
g.object_id = 0
|
|
g.cp_id = cp.id
|
|
g.airbase_group = True
|
|
g.dcs_identifier = "LHA"
|
|
g.sea_object = True
|
|
g.obj_name = namegen.random_objective_name()
|
|
g.heading = 0
|
|
g.position = Point(cp.position.x, cp.position.y)
|
|
group = generate_lha_group(faction_name, game, g)
|
|
g.groups = []
|
|
if group is not None:
|
|
g.groups.append(group)
|
|
cp.ground_objects.append(g)
|
|
# Set new name :
|
|
if len(faction.helicopter_carrier_names) > 0:
|
|
cp.name = random.choice(faction.helicopter_carrier_names)
|
|
else:
|
|
cp_to_remove.append(cp)
|
|
else:
|
|
|
|
for i in range(random.randint(3, 6)):
|
|
|
|
logging.info("GENERATE BASE DEFENSE")
|
|
point = find_location(True, cp.position, theater, 800, 3200, [], True)
|
|
logging.info(point)
|
|
|
|
if point is None:
|
|
logging.info("Couldn't find point for {} base defense".format(cp))
|
|
continue
|
|
|
|
group_id = game.next_group_id()
|
|
|
|
g = TheaterGroundObject("aa")
|
|
g.group_id = group_id
|
|
g.object_id = 0
|
|
g.cp_id = cp.id
|
|
g.airbase_group = True
|
|
g.dcs_identifier = "AA"
|
|
g.sea_object = False
|
|
g.obj_name = namegen.random_objective_name()
|
|
g.heading = 0
|
|
g.position = Point(point.x, point.y)
|
|
|
|
generate_airbase_defense_group(i, g, faction_name, game, cp)
|
|
cp.ground_objects.append(g)
|
|
|
|
logging.info("---------------------------")
|
|
logging.info("CP Generation : " + cp.name)
|
|
for ground_object in cp.ground_objects:
|
|
logging.info(ground_object.groups)
|
|
|
|
# Generate navy groups
|
|
if len(faction.navy_generators) > 0 and cp.allow_sea_units:
|
|
|
|
if cp.captured and game.settings.do_not_generate_player_navy:
|
|
continue
|
|
if not cp.captured and game.settings.do_not_generate_enemy_navy:
|
|
continue
|
|
|
|
for i in range(faction.navy_group_count):
|
|
|
|
point = find_location(False, cp.position, theater, 5000, 40000, [], False)
|
|
|
|
if point is None:
|
|
logging.info("Couldn't find point for {} ships".format(cp))
|
|
continue
|
|
|
|
group_id = game.next_group_id()
|
|
|
|
g = TheaterGroundObject("aa")
|
|
g.group_id = group_id
|
|
g.object_id = 0
|
|
g.cp_id = cp.id
|
|
g.airbase_group = False
|
|
g.dcs_identifier = "AA"
|
|
g.sea_object = True
|
|
g.obj_name = namegen.random_objective_name()
|
|
g.heading = 0
|
|
g.position = Point(point.x, point.y)
|
|
|
|
group = generate_ship_group(game, g, faction_name)
|
|
g.groups = []
|
|
if group is not None:
|
|
g.groups.append(group)
|
|
cp.ground_objects.append(g)
|
|
|
|
if len(faction.missiles) > 0:
|
|
|
|
for i in range(faction.missiles_group_count):
|
|
|
|
point = find_location(True, cp.position, theater, 2500, 40000, [], False)
|
|
|
|
if point is None:
|
|
logging.info("Couldn't find point for {} missiles".format(cp))
|
|
continue
|
|
|
|
group_id = game.next_group_id()
|
|
|
|
g = TheaterGroundObject("aa")
|
|
g.group_id = group_id
|
|
g.object_id = 0
|
|
g.cp_id = cp.id
|
|
g.airbase_group = False
|
|
g.dcs_identifier = "AA"
|
|
g.sea_object = False
|
|
g.obj_name = namegen.random_objective_name()
|
|
g.heading = 0
|
|
g.position = Point(point.x, point.y)
|
|
|
|
group = generate_missile_group(game, g, faction_name)
|
|
g.groups = []
|
|
if group is not None:
|
|
g.groups.append(group)
|
|
cp.ground_objects.append(g)
|
|
|
|
for cp in cp_to_remove:
|
|
theater.controlpoints.remove(cp)
|
|
|
|
|
|
|
|
def generate_airbase_defense_group(airbase_defense_group_id, ground_obj:TheaterGroundObject, faction, game, cp):
|
|
|
|
logging.info("GENERATE AIR DEFENSE GROUP")
|
|
logging.info(faction)
|
|
logging.info(airbase_defense_group_id)
|
|
|
|
if airbase_defense_group_id == 0:
|
|
group = generate_armor_group(faction, game, ground_obj)
|
|
elif airbase_defense_group_id == 1 and random.randint(0, 1) == 0:
|
|
group = generate_anti_air_group(game, cp, ground_obj, faction)
|
|
elif random.randint(0, 2) == 1:
|
|
group = generate_shorad_group(game, cp, ground_obj, faction)
|
|
else:
|
|
group = generate_armor_group(faction, game, ground_obj)
|
|
|
|
ground_obj.groups = []
|
|
if group is not None:
|
|
ground_obj.groups.append(group)
|
|
|
|
|
|
def find_location(on_ground, near, theater, min, max, others, is_base_defense=False) -> typing.Optional[Point]:
|
|
"""
|
|
Find a valid ground object location
|
|
:param on_ground: Whether it should be on ground or on sea (True = on ground)
|
|
:param near: Point
|
|
:param theater: Theater object
|
|
:param min: Minimal range from point
|
|
:param max: Max range from point
|
|
:param others: Other already existing ground objects
|
|
:return:
|
|
"""
|
|
point = None
|
|
for _ in range(300):
|
|
|
|
# Check if on land or sea
|
|
p = near.random_point_within(max, min)
|
|
if on_ground and theater.is_on_land(p):
|
|
point = p
|
|
elif not on_ground and theater.is_in_sea(p):
|
|
point = p
|
|
|
|
if point:
|
|
for angle in range(0, 360, 45):
|
|
p = point.point_from_heading(angle, 2500)
|
|
if on_ground and not theater.is_on_land(p):
|
|
point = None
|
|
break
|
|
elif not on_ground and not theater.is_in_sea(p):
|
|
point = None
|
|
break
|
|
if point:
|
|
for other in others:
|
|
if other.position.distance_to_point(point) < 10000:
|
|
point = None
|
|
break
|
|
|
|
if point:
|
|
for other in theater.controlpoints:
|
|
if is_base_defense: break
|
|
if other.position != near:
|
|
if point is None:
|
|
break
|
|
if other.position.distance_to_point(point) < 30000:
|
|
point = None
|
|
break
|
|
for ground_obj in other.ground_objects:
|
|
if ground_obj.position.distance_to_point(point) < 10000:
|
|
point = None
|
|
break
|
|
|
|
if point:
|
|
return point
|
|
return None
|
|
|
|
|
|
def generate_cp_ground_points(cp: ControlPoint, theater, game, group_id, templates):
|
|
"""
|
|
Generate inital ground objects and AA site for given control point
|
|
:param cp: Control point to initialize
|
|
:param theater: Theater
|
|
:param game: Game object
|
|
:param group_id: Group id
|
|
:param templates: Ground object templates
|
|
:return: True if something was generated
|
|
"""
|
|
# Reset cp ground objects
|
|
cp.ground_objects = []
|
|
|
|
if cp.is_global:
|
|
return False
|
|
|
|
if cp.captured:
|
|
faction = game.player_name
|
|
else:
|
|
faction = game.enemy_name
|
|
faction_data = db.FACTIONS[faction]
|
|
|
|
available_categories = faction_data.building_set
|
|
|
|
if len(available_categories) == 0:
|
|
return False
|
|
|
|
amount = random.randrange(3, 8)
|
|
for i in range(0, amount):
|
|
|
|
obj_name = namegen.random_objective_name()
|
|
|
|
if i >= amount - 1:
|
|
tpl_category = "aa"
|
|
else:
|
|
if random.randint(0, 3) == 0:
|
|
tpl_category = "aa"
|
|
else:
|
|
tpl_category = random.choice(available_categories)
|
|
|
|
tpl = random.choice(list(templates[tpl_category].values()))
|
|
point = find_location(tpl_category != "oil", cp.position, theater, 10000, 40000, cp.ground_objects)
|
|
|
|
if point is None:
|
|
logging.info("Couldn't find point for {}".format(cp))
|
|
continue
|
|
|
|
object_id = 0
|
|
group_id = game.next_group_id()
|
|
|
|
logging.info("generated {} for {}".format(tpl_category, cp))
|
|
|
|
for object in tpl:
|
|
object_id += 1
|
|
|
|
g = TheaterGroundObject(tpl_category)
|
|
g.group_id = group_id
|
|
g.object_id = object_id
|
|
g.cp_id = cp.id
|
|
g.airbase_group = False
|
|
g.obj_name = obj_name
|
|
|
|
g.dcs_identifier = object["type"]
|
|
g.heading = object["heading"]
|
|
g.sea_object = False
|
|
g.position = Point(point.x + object["offset"].x, point.y + object["offset"].y)
|
|
|
|
if g.dcs_identifier == "AA":
|
|
g.groups = []
|
|
group = generate_anti_air_group(game, cp, g, faction)
|
|
if group is not None:
|
|
g.groups.append(group)
|
|
|
|
cp.ground_objects.append(g)
|
|
return group_id
|
|
|
|
|
|
def prepare_theater(theater: ConflictTheater, settings:Settings, midgame):
|
|
|
|
to_remove = []
|
|
|
|
# autocapture half the base if midgame
|
|
if midgame:
|
|
for i in range(0, int(len(theater.controlpoints) / 2)):
|
|
theater.controlpoints[i].captured = True
|
|
|
|
# Remove carrier and lha, invert situation if needed
|
|
for cp in theater.controlpoints:
|
|
if cp.cptype is ControlPointType.AIRCRAFT_CARRIER_GROUP and settings.do_not_generate_carrier:
|
|
to_remove.append(cp)
|
|
elif cp.cptype is ControlPointType.LHA_GROUP and settings.do_not_generate_lha:
|
|
to_remove.append(cp)
|
|
|
|
if settings.inverted:
|
|
cp.captured = cp.captured_invert
|
|
|
|
# do remove
|
|
for cp in to_remove:
|
|
theater.controlpoints.remove(cp)
|
|
|
|
# reapply midgame inverted if needed
|
|
if midgame and settings.inverted:
|
|
for i, cp in enumerate(reversed(theater.controlpoints)):
|
|
if i > len(theater.controlpoints):
|
|
break
|
|
else:
|
|
cp.captured = True
|
|
|
|
|