dcs-retribution/theater/start_generator.py

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