canvas map overview WIP

This commit is contained in:
Vasyl Horbachenko 2018-06-06 00:18:25 +03:00
parent fb377cb6ca
commit 2915287afb
18 changed files with 334 additions and 125 deletions

3
.gitmodules vendored Normal file
View File

@ -0,0 +1,3 @@
[submodule "submodules/dcs"]
path = submodules/dcs
url = https://github.com/pydcs/dcs

View File

@ -4,7 +4,7 @@
<content url="file://$MODULE_DIR$"> <content url="file://$MODULE_DIR$">
<excludeFolder url="file://$MODULE_DIR$/venv" /> <excludeFolder url="file://$MODULE_DIR$/venv" />
</content> </content>
<orderEntry type="jdk" jdkName="Python 3.6 (venv)" jdkType="Python SDK" /> <orderEntry type="jdk" jdkName="Python 3.6" jdkType="Python SDK" />
<orderEntry type="sourceFolder" forTests="false" /> <orderEntry type="sourceFolder" forTests="false" />
</component> </component>
<component name="TestRunnerService"> <component name="TestRunnerService">

2
.idea/misc.xml generated
View File

@ -1,4 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<project version="4"> <project version="4">
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.6 (venv)" project-jdk-type="Python SDK" /> <component name="ProjectRootManager" version="2" project-jdk-name="Python 3.6" project-jdk-type="Python SDK" />
</project> </project>

1
dcs Symbolic link
View File

@ -0,0 +1 @@
submodules/dcs/dcs

View File

@ -83,6 +83,7 @@ class GroundInterceptEvent(Event):
defender=self.defender, defender=self.defender,
attacker_clients=clients, attacker_clients=clients,
defender_clients={}, defender_clients={},
attacker_airport=self.from_cp.at,
position=position, position=position,
target=targets, target=targets,
strikegroup=strikegroup) strikegroup=strikegroup)
@ -92,6 +93,7 @@ class InterceptEvent(Event):
ESCORT_AMOUNT_FACTOR = 2 ESCORT_AMOUNT_FACTOR = 2
BONUS_BASE = 5 BONUS_BASE = 5
STRENGTH_INFLUENCE = 0.25 STRENGTH_INFLUENCE = 0.25
AIRDEFENSE_COUNT = 3
def __str__(self): def __str__(self):
return "Intercept at {} ({})".format(self.to_cp, "*" * self.difficulty) return "Intercept at {} ({})".format(self.to_cp, "*" * self.difficulty)
@ -112,15 +114,19 @@ class InterceptEvent(Event):
transport_unit = random.choice(db.find_unittype(Transport, self.defender.name)) transport_unit = random.choice(db.find_unittype(Transport, self.defender.name))
assert transport_unit is not None assert transport_unit is not None
airdefense_unit = db.find_unittype(AirDefence, self.defender.name)[0]
self.operation = InterceptOperation(mission=self.mission, self.operation = InterceptOperation(mission=self.mission,
attacker=self.attacker, attacker=self.attacker,
defender=self.defender, defender=self.defender,
attacker_clients=clients, attacker_clients=clients,
defender_clients={}, defender_clients={},
attacker_airport=self.from_cp.at,
destination=self.to_cp, destination=self.to_cp,
destination_port=self.to_cp.airport, destination_port=self.to_cp.at,
escort=escort, escort=escort,
transport={transport_unit: 1}, transport={transport_unit: 1},
airdefense={airdefense_unit: self.AIRDEFENSE_COUNT},
interceptors=interceptors) interceptors=interceptors)
def player_defending(self, escort: db.PlaneDict, clients: db.PlaneDict): def player_defending(self, escort: db.PlaneDict, clients: db.PlaneDict):
@ -133,11 +139,13 @@ class InterceptEvent(Event):
defender=self.defender, defender=self.defender,
attacker_clients={}, attacker_clients={},
defender_clients=clients, defender_clients=clients,
attacker_airport=None,
destination=self.to_cp, destination=self.to_cp,
destination_port=self.to_cp.airport, destination_port=self.to_cp.at,
escort=escort, escort=escort,
transport={transport_unit: 1}, transport={transport_unit: 1},
interceptors=interceptors) interceptors=interceptors,
airdefense={})
class CaptureEvent(Event): class CaptureEvent(Event):
@ -172,6 +180,7 @@ class CaptureEvent(Event):
defender=self.defender, defender=self.defender,
attacker_clients={}, attacker_clients={},
defender_clients=clients, defender_clients=clients,
attacker_airport=None,
from_cp=self.from_cp, from_cp=self.from_cp,
to_cp=self.to_cp, to_cp=self.to_cp,
cas=cas, cas=cas,
@ -189,6 +198,7 @@ class CaptureEvent(Event):
defender=self.defender, defender=self.defender,
attacker_clients=clients, attacker_clients=clients,
defender_clients={}, defender_clients={},
attacker_airport=self.from_cp.at,
from_cp=self.from_cp, from_cp=self.from_cp,
to_cp=self.to_cp, to_cp=self.to_cp,
cas=cas, cas=cas,

View File

@ -23,8 +23,8 @@ COMMISION_AMOUNTS_FACTORS = {
} }
ENEMY_INTERCEPT_PROBABILITY_BASE = 25 ENEMY_INTERCEPT_PROBABILITY_BASE = 15
ENEMY_CAPTURE_PROBABILITY_BASE = 15 ENEMY_CAPTURE_PROBABILITY_BASE = 5
PLAYER_INTERCEPT_PROBABILITY_BASE = 30 PLAYER_INTERCEPT_PROBABILITY_BASE = 30
PLAYER_GROUNDINTERCEPT_PROBABILITY_BASE = 30 PLAYER_GROUNDINTERCEPT_PROBABILITY_BASE = 30
@ -49,10 +49,11 @@ class Game:
def _fill_cap_events(self): def _fill_cap_events(self):
for from_cp, to_cp in self.theater.conflicts(True): for from_cp, to_cp in self.theater.conflicts(True):
self.events.append(CaptureEvent(attacker_name=self.player, if to_cp not in [x.to_cp for x in self.events]:
defender_name=self.enemy, self.events.append(CaptureEvent(attacker_name=self.player,
from_cp=from_cp, defender_name=self.enemy,
to_cp=to_cp)) from_cp=from_cp,
to_cp=to_cp))
def _generate_enemy_caps(self): def _generate_enemy_caps(self):
for from_cp, to_cp in self.theater.conflicts(False): for from_cp, to_cp in self.theater.conflicts(False):
@ -106,12 +107,17 @@ class Game:
unit_type = random.choice(db.find_unittype(for_task, self.enemy)) unit_type = random.choice(db.find_unittype(for_task, self.enemy))
cp.base.commision_units({unit_type: points_to_spend}) cp.base.commision_units({unit_type: points_to_spend})
def _budget_player(self): @property
def budget_reward_amount(self):
if len(self.theater.player_points()) > 0: if len(self.theater.player_points()) > 0:
total_importance = sum([x.importance for x in self.theater.player_points()]) total_importance = sum([x.importance for x in self.theater.player_points()])
total_strength = sum([x.base.strength for x in self.theater.player_points()]) / len(self.theater.player_points()) total_strength = sum([x.base.strength for x in self.theater.player_points()]) / len(self.theater.player_points())
return math.ceil(math.log(total_importance * total_strength + 1, PLAYER_BUDGET_IMPORTANCE_LOG) * PLAYER_BUDGET_BASE)
else:
return 0
self.budget += math.ceil(math.log(total_importance * total_strength + 1, PLAYER_BUDGET_IMPORTANCE_LOG) * PLAYER_BUDGET_BASE) def _budget_player(self):
self.budget += self.budget_reward_amount
def units_delivery_event(self, to_cp: ControlPoint) -> UnitsDeliveryEvent: def units_delivery_event(self, to_cp: ControlPoint) -> UnitsDeliveryEvent:
event = UnitsDeliveryEvent(attacker_name=self.player, event = UnitsDeliveryEvent(attacker_name=self.player,
@ -121,6 +127,9 @@ class Game:
self.events.append(event) self.events.append(event)
return event return event
def units_delivery_remove(self, event: Event):
self.events.remove(event)
def initiate_event(self, event: Event): def initiate_event(self, event: Event):
event.operation.generate() event.operation.generate()
event.mission.save("build/next_mission.miz") event.mission.save("build/next_mission.miz")

View File

@ -13,6 +13,7 @@ from shop import *
from gen.armor import * from gen.armor import *
from gen.aircraft import * from gen.aircraft import *
from gen.aaa import * from gen.aaa import *
from gen.shipgen import *
from gen.conflictgen import * from gen.conflictgen import *
@ -23,6 +24,7 @@ class Operation:
self.armorgen = ArmorConflictGenerator(self.mission, self.conflict) self.armorgen = ArmorConflictGenerator(self.mission, self.conflict)
self.airgen = AircraftConflictGenerator(self.mission, self.conflict) self.airgen = AircraftConflictGenerator(self.mission, self.conflict)
self.aagen = AAConflictGenerator(self.mission, self.conflict) self.aagen = AAConflictGenerator(self.mission, self.conflict)
self.shipgen = ShipGenerator(self.mission, self.conflict)
def units_of(self, country_name: str) -> typing.Collection[UnitType]: def units_of(self, country_name: str) -> typing.Collection[UnitType]:
return [] return []
@ -41,6 +43,7 @@ class CaptureOperation(Operation):
defender: Country, defender: Country,
attacker_clients: db.PlaneDict, attacker_clients: db.PlaneDict,
defender_clients: db.PlaneDict, defender_clients: db.PlaneDict,
attacker_airport: typing.Optional[Airport],
from_cp: ControlPoint, from_cp: ControlPoint,
to_cp: ControlPoint, to_cp: ControlPoint,
cas: db.PlaneDict, cas: db.PlaneDict,
@ -48,7 +51,7 @@ class CaptureOperation(Operation):
attack: db.ArmorDict, attack: db.ArmorDict,
intercept: db.PlaneDict, intercept: db.PlaneDict,
defense: db.ArmorDict, defense: db.ArmorDict,
aa: db.AADict): aa: db.AirDefenseDict):
conflict = to_cp.conflict_attack(from_cp, attacker, defender) conflict = to_cp.conflict_attack(from_cp, attacker, defender)
super(CaptureOperation, self).__init__(mission, conflict) super(CaptureOperation, self).__init__(mission, conflict)
@ -56,6 +59,7 @@ class CaptureOperation(Operation):
self.to_cp = to_cp self.to_cp = to_cp
self.attacker_clients = attacker_clients self.attacker_clients = attacker_clients
self.defender_clients = defender_clients self.defender_clients = defender_clients
self.attacker_airport = attacker_airport
self.cas = cas self.cas = cas
self.escort = escort self.escort = escort
self.intercept = intercept self.intercept = intercept
@ -67,10 +71,11 @@ class CaptureOperation(Operation):
def generate(self): def generate(self):
self.armorgen.generate(self.attack, self.defense) self.armorgen.generate(self.attack, self.defense)
self.airgen.generate_cas(self.cas, clients=self.attacker_clients)
self.airgen.generate_cas_escort(self.escort, clients=self.attacker_clients)
self.airgen.generate_defense(self.intercept, clients=self.defender_clients)
self.aagen.generate(self.aa) self.aagen.generate(self.aa)
self.airgen.generate_defense(self.intercept, clients=self.defender_clients)
self.airgen.generate_cas(self.cas, clients=self.attacker_clients, at=self.attacker_airport)
self.airgen.generate_cas_escort(self.escort, clients=self.attacker_clients, at=self.attacker_airport)
class InterceptOperation(Operation): class InterceptOperation(Operation):
@ -80,10 +85,12 @@ class InterceptOperation(Operation):
defender: Country, defender: Country,
attacker_clients: db.PlaneDict, attacker_clients: db.PlaneDict,
defender_clients: db.PlaneDict, defender_clients: db.PlaneDict,
attacker_airport: typing.Optional[Airport],
destination: ControlPoint, destination: ControlPoint,
destination_port: Airport, destination_port: Airport,
escort: db.PlaneDict, escort: db.PlaneDict,
transport: db.PlaneDict, transport: db.PlaneDict,
airdefense: db.AirDefenseDict,
interceptors: db.PlaneDict): interceptors: db.PlaneDict):
conflict = Conflict.intercept_conflict( conflict = Conflict.intercept_conflict(
attacker=attacker, attacker=attacker,
@ -95,16 +102,20 @@ class InterceptOperation(Operation):
super(InterceptOperation, self).__init__(mission, conflict) super(InterceptOperation, self).__init__(mission, conflict)
self.destination_port = destination_port self.destination_port = destination_port
self.attacker_airport = attacker_airport
self.attacker_clients = attacker_clients self.attacker_clients = attacker_clients
self.defender_clients = defender_clients self.defender_clients = defender_clients
self.escort = escort self.escort = escort
self.transport = transport self.transport = transport
self.airdefense = airdefense
self.interceptors = interceptors self.interceptors = interceptors
def generate(self): def generate(self):
self.airgen.generate_transport(self.transport, self.destination_port) self.airgen.generate_transport(self.transport, self.destination_port)
self.airgen.generate_transport_escort(self.escort, clients=self.defender_clients) self.airgen.generate_transport_escort(self.escort, clients=self.defender_clients)
self.airgen.generate_interception(self.interceptors, clients=self.attacker_clients) self.aagen.generate(self.airdefense)
self.airgen.generate_interception(self.interceptors, clients=self.attacker_clients, airport=self.attacker_airport)
class GroundInterceptOperation(Operation): class GroundInterceptOperation(Operation):
@ -114,6 +125,7 @@ class GroundInterceptOperation(Operation):
defender: Country, defender: Country,
attacker_clients: db.PlaneDict, attacker_clients: db.PlaneDict,
defender_clients: db.PlaneDict, defender_clients: db.PlaneDict,
attacker_airport: typing.Optional[Airport],
position: Point, position: Point,
target: db.ArmorDict, target: db.ArmorDict,
strikegroup: db.PlaneDict): strikegroup: db.PlaneDict):
@ -128,10 +140,10 @@ class GroundInterceptOperation(Operation):
super(GroundInterceptOperation, self).__init__(mission, conflict) super(GroundInterceptOperation, self).__init__(mission, conflict)
self.attacker_clients = attacker_clients self.attacker_clients = attacker_clients
self.defender_clients = defender_clients self.defender_clients = defender_clients
self.attacker_airport = attacker_airport
self.strikegroup = strikegroup self.strikegroup = strikegroup
self.target = target self.target = target
def generate(self): def generate(self):
self.airgen.generate_cas(self.strikegroup, clients=self.attacker_clients) self.airgen.generate_cas(self.strikegroup, clients=self.attacker_clients, at=self.attacker_airport)
self.armorgen.generate({}, self.target) self.armorgen.generate({}, self.target)

View File

@ -45,28 +45,39 @@ class AircraftConflictGenerator:
) )
return point.random_point_within(distance, self.conflict.size * SPREAD_DISTANCE_FACTOR[0]) return point.random_point_within(distance, self.conflict.size * SPREAD_DISTANCE_FACTOR[0])
def _generate_group( def _generate_at_airport(self, name: str, side: Country, unit_type: FlyingType, count: int, client_count: int, airport: Airport = None) -> FlyingGroup:
self,
name: str,
side: Country,
unit: PlaneType,
count: int,
client_count: int,
at: Point = None,
airport: Airport = None) -> FlyingGroup:
starttype = airport is None and StartType.Warm or StartType.Cold
assert count > 0 assert count > 0
assert unit is not None
group = self.m.flight_group_from_airport(
country=side,
name=name,
aircraft_type=unit_type,
airport=airport,
maintask=None,
start_type=StartType.Cold,
group_size=count,
parking_slots=None)
for idx in range(client_count):
group.units[idx].set_client()
return group
def _generate_inflight(self, name: str, side: Country, unit_type: FlyingType, count: int, client_count: int, at: Point) -> FlyingGroup:
assert count > 0
assert unit is not None
group = self.m.flight_group( group = self.m.flight_group(
country=side, country=side,
name=name, name=name,
aircraft_type=unit, aircraft_type=unit_type,
airport=airport, airport=None,
position=at, position=at,
altitude=WARM_START_ALTITUDE, altitude=WARM_START_ALTITUDE,
speed=WARM_START_AIRSPEED, speed=WARM_START_AIRSPEED,
maintask=None, maintask=None,
start_type=starttype, start_type=StartType.Warm,
group_size=count) group_size=count)
for idx in range(client_count): for idx in range(client_count):
@ -74,7 +85,35 @@ class AircraftConflictGenerator:
return group return group
def _generate_escort(self, units: db.PlaneDict, clients: db.PlaneDict, airport: Airport, side: Country, location: Point): def _generate_at_carrier(self, name: str, side: Country, unit_type: FlyingType, count: int, client_count: int, at: ShipGroup) -> FlyingGroup:
assert count > 0
assert unit is not None
group = self.m.flight_group_from_unit(
country=side,
name=name,
aircraft_type=unit_type,
pad_group=at,
maintask=None,
start_type=StartType.Warm,
group_size=count)
for idx in range(client_count):
group.units[idx].set_client()
return group
def _generate_group(self, name: str, side: Country, unit_type: FlyingType, count: int, client_count: int, at: db.StartingPosition):
if type(at) == Point:
return self._generate_inflight(name, side, unit_type, count, client_count, at)
elif type(at) == Airport:
return self._generate_at_airport(name, side, unit_type, count, client_count, at)
elif type(at) == ShipGroup:
return self._generate_at_carrier(name, side, unit_type, count, client_count, at)
else:
assert False
def _generate_escort(self, side: Country, units: db.PlaneDict, clients: db.PlaneDict, at: db.StartingPosition):
if len(self.escort_targets) == 0: if len(self.escort_targets) == 0:
return return
@ -82,11 +121,10 @@ class AircraftConflictGenerator:
group = self._generate_group( group = self._generate_group(
name=namegen.next_escort_group_name(), name=namegen.next_escort_group_name(),
side=side, side=side,
unit=type, unit_type=type,
count=count, count=count,
client_count=clients.get(type, 0), client_count=clients.get(type, 0),
at=location, at=at)
airport=airport)
group.task = Escort.name group.task = Escort.name
group.load_task_default_loadout(dcs.task.Escort) group.load_task_default_loadout(dcs.task.Escort)
@ -98,52 +136,46 @@ class AircraftConflictGenerator:
for group in self.escort_targets: for group in self.escort_targets:
wayp.tasks.append(EscortTaskAction(group.id, engagement_max_dist=ESCORT_MAX_DIST)) wayp.tasks.append(EscortTaskAction(group.id, engagement_max_dist=ESCORT_MAX_DIST))
def generate_cas(self, attackers: db.PlaneDict, clients: db.PlaneDict, airport: Airport = None): def generate_cas(self, attackers: db.PlaneDict, clients: db.PlaneDict, at: db.StartingPosition = None):
assert len(self.escort_targets) == 0 assert len(self.escort_targets) == 0
for type, count in attackers.items(): for type, count in attackers.items():
group = self._generate_group( group = self._generate_group(
name=namegen.next_cas_group_name(), name=namegen.next_cas_group_name(),
side=self.conflict.attackers_side, side=self.conflict.attackers_side,
unit=type, unit_type=type,
count=count, count=count,
client_count=clients.get(type, 0), client_count=clients.get(type, 0),
at=airport is None and self._group_point(self.conflict.air_attackers_location) or None, at=at and at or self._group_point(self.conflict.air_attackers_location))
airport=airport)
self.escort_targets.append(group) self.escort_targets.append(group)
group.add_waypoint(self.conflict.position, CAS_ALTITUDE) group.add_waypoint(self.conflict.position, CAS_ALTITUDE)
group.task = CAS.name group.task = CAS.name
group.load_task_default_loadout(CAS) group.load_task_default_loadout(CAS)
def generate_cas_escort(self, attackers: db.PlaneDict, clients: db.PlaneDict, airport: Airport = None): def generate_cas_escort(self, attackers: db.PlaneDict, clients: db.PlaneDict, at: db.StartingPosition = None):
self._generate_escort( self._generate_escort(
side=self.conflict.attackers_side,
units=attackers, units=attackers,
clients=clients, clients=clients,
airport=airport, at=at and at or self._group_point(self.conflict.air_attackers_location))
side=self.conflict.attackers_side,
location=airport is None and self._group_point(self.conflict.air_attackers_location) or None
)
def generate_transport_escort(self, escort: db.PlaneDict, clients: db.PlaneDict, airport: Airport = None): def generate_transport_escort(self, escort: db.PlaneDict, clients: db.PlaneDict, at: db.StartingPosition = None):
self._generate_escort( self._generate_escort(
side=self.conflict.defenders_side,
units=escort, units=escort,
clients=clients, clients=clients,
airport=airport, at=at and at or self._group_point(self.conflict.air_defenders_location))
side=self.conflict.defenders_side,
location=airport is None and self._group_point(self.conflict.air_defenders_location) or None
)
def generate_defense(self, defenders: db.PlaneDict, clients: db.PlaneDict, airport: Airport = None): def generate_defense(self, defenders: db.PlaneDict, clients: db.PlaneDict, at: db.StartingPosition = None):
for type, count in defenders.items(): for type, count in defenders.items():
group = self._generate_group( group = self._generate_group(
name=namegen.next_intercept_group_name(), name=namegen.next_intercept_group_name(),
side=self.conflict.defenders_side, side=self.conflict.defenders_side,
unit=type, unit_type=type,
count=count, count=count,
client_count=clients.get(type, 0), client_count=clients.get(type, 0),
at=airport is None and self._group_point(self.conflict.air_defenders_location) or None, at=at and at or self._group_point(self.conflict.air_defenders_location))
airport=airport)
group.task = FighterSweep.name group.task = FighterSweep.name
group.load_task_default_loadout(FighterSweep) group.load_task_default_loadout(FighterSweep)
@ -158,29 +190,25 @@ class AircraftConflictGenerator:
group = self._generate_group( group = self._generate_group(
name=namegen.next_transport_group_name(), name=namegen.next_transport_group_name(),
side=self.conflict.defenders_side, side=self.conflict.defenders_side,
unit=type, unit_type=type,
count=count, count=count,
client_count=0, client_count=0,
at=self._group_point(self.conflict.air_defenders_location), at=self._group_point(self.conflict.air_defenders_location))
airport=None
)
group.task = Transport.name group.task = Transport.name
self.escort_targets.append(group) self.escort_targets.append(group)
group.land_at(destination) group.land_at(destination)
def generate_interception(self, interceptors: db.PlaneDict, clients: db.PlaneDict, airport: Airport = None): def generate_interception(self, interceptors: db.PlaneDict, clients: db.PlaneDict, at: db.StartingPosition = None):
for type, count in interceptors.items(): for type, count in interceptors.items():
group = self._generate_group( group = self._generate_group(
name=namegen.next_intercept_group_name(), name=namegen.next_intercept_group_name(),
side=self.conflict.attackers_side, side=self.conflict.attackers_side,
unit=type, unit_type=type,
count=count, count=count,
client_count=clients.get(type, 0), client_count=clients.get(type, 0),
at=airport is None and self._group_point(self.conflict.air_attackers_location) or None, at=at and at or self._group_point(self.conflict.air_attackers_location))
airport=airport
)
group.task = FighterSweep.name group.task = FighterSweep.name
group.load_task_default_loadout(FighterSweep) group.load_task_default_loadout(FighterSweep)

32
gen/shipgen.py Normal file
View File

@ -0,0 +1,32 @@
import typing
import pdb
import dcs
from random import randint
import globals
from .conflictgen import *
from .naming import *
from dcs.mission import *
from dcs.vehicles import *
from dcs.unitgroup import *
from dcs.unittype import *
from dcs.mapping import *
from dcs.point import *
from dcs.task import *
class ShipGenerator:
def __init__(self, mission: Mission, conflict: Conflict, position: Point):
self.m = mission
self.conflict = conflict
self.position = position
def generate(self):
self.m.ship_group(
country=self.conflict.attackers_side,
name=namegen.next_transport_group_name(),
_type=dcs.ships.CVN_74_John_C__Stennis,
position=self.position)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 186 KiB

After

Width:  |  Height:  |  Size: 157 KiB

View File

@ -17,6 +17,9 @@ PRICES = {
A_10A: 18, A_10A: 18,
A_10C: 20, A_10C: 20,
F_A_18C: 18,
AV8BNA: 15,
Su_27: 30, Su_27: 30,
Su_33: 33, Su_33: 33,
F_15C: 30, F_15C: 30,
@ -45,7 +48,7 @@ PRICES = {
} }
UNIT_BY_TASK = { UNIT_BY_TASK = {
FighterSweep: [Su_27, Su_33, Su_25, F_15C, MiG_15bis, MiG_21Bis, MiG_29A, ], FighterSweep: [Su_27, Su_33, Su_25, F_15C, MiG_15bis, MiG_21Bis, MiG_29A, F_A_18C, AV8BNA],
CAS: [Su_25T, A_10A, A_10C, ], CAS: [Su_25T, A_10A, A_10C, ],
CAP: [Armor.MBT_T_90, Armor.MBT_T_80U, Armor.MBT_T_55, Armor.MBT_M1A2_Abrams, Armor.MBT_M60A3_Patton, Armor.ATGM_M1134_Stryker, Armor.APC_BTR_80, ], CAP: [Armor.MBT_T_90, Armor.MBT_T_80U, Armor.MBT_T_55, Armor.MBT_M1A2_Abrams, Armor.MBT_M60A3_Patton, Armor.ATGM_M1134_Stryker, Armor.APC_BTR_80, ],
AirDefence: [AirDefence.AAA_ZU_23_on_Ural_375, ], AirDefence: [AirDefence.AAA_ZU_23_on_Ural_375, ],
@ -53,14 +56,15 @@ UNIT_BY_TASK = {
} }
UNIT_BY_COUNTRY = { UNIT_BY_COUNTRY = {
"Russia": [Su_25T, Su_27, Su_33, Su_25, MiG_15bis, MiG_21Bis, MiG_29A, AirDefence.AAA_ZU_23_on_Ural_375, Armor.APC_BTR_80, Armor.MBT_T_90, Armor.MBT_T_80U, Armor.MBT_T_55, IL_76MD, ], "Russia": [Su_25T, Su_27, Su_33, Su_25, MiG_15bis, MiG_21Bis, MiG_29A, AirDefence.AAA_ZU_23_on_Ural_375, Armor.APC_BTR_80, Armor.MBT_T_90, Armor.MBT_T_80U, Armor.MBT_T_55, IL_76MD, ],
"USA": [F_15C, A_10C, Armor.MBT_M1A2_Abrams, Armor.MBT_M60A3_Patton, Armor.ATGM_M1134_Stryker, S_3B_Tanker], "USA": [F_15C, A_10C, F_A_18C, AV8BNA, Armor.MBT_M1A2_Abrams, Armor.MBT_M60A3_Patton, Armor.ATGM_M1134_Stryker, S_3B_Tanker],
} }
UnitsDict = typing.Dict[UnitType, int] UnitsDict = typing.Dict[UnitType, int]
PlaneDict = typing.Dict[PlaneType, int] PlaneDict = typing.Dict[PlaneType, int]
ArmorDict = typing.Dict[VehicleType, int] ArmorDict = typing.Dict[VehicleType, int]
AADict = typing.Dict[AirDefence, int] AirDefenseDict = typing.Dict[AirDefence, int]
StartingPosition = typing.Optional[typing.Union[ShipGroup, Airport, Point]]
def unit_task(unit: UnitType) -> Task: def unit_task(unit: UnitType) -> Task:

1
submodules/dcs Submodule

@ -0,0 +1 @@
Subproject commit d982dfe594e16a45851050e8f63cec03c91aeed9

View File

@ -1,8 +1,10 @@
import typing import typing
import dcs
import math import math
import random
import itertools import itertools
import dcs
from shop import db from shop import db
from theater.controlpoint import ControlPoint from theater.controlpoint import ControlPoint

View File

@ -3,24 +3,50 @@ from dcs.terrain import caucasus
from .conflicttheater import * from .conflicttheater import *
from .base import * from .base import *
class CaucasusTheater(ConflictTheater): class CaucasusTheater(ConflictTheater):
kutaisi = ControlPoint(caucasus.Kutaisi, ALL_RADIALS, SIZE_SMALL, IMPORTANCE_LOW) soganlug = ControlPoint.from_airport(caucasus.Soganlug, ALL_RADIALS, SIZE_SMALL, IMPORTANCE_LOW)
senaki = ControlPoint(caucasus.Senaki, ALL_RADIALS, SIZE_REGULAR, IMPORTANCE_LOW) kutaisi = ControlPoint.from_airport(caucasus.Kutaisi, ALL_RADIALS, SIZE_SMALL, IMPORTANCE_LOW)
kobuleti = ControlPoint(caucasus.Kobuleti, COAST_VERTICAL, SIZE_SMALL, IMPORTANCE_LOW) senaki = ControlPoint.from_airport(caucasus.Senaki_Kolkhi, ALL_RADIALS, SIZE_REGULAR, IMPORTANCE_LOW)
batumi = ControlPoint(caucasus.Batumi, COAST_VERTICAL, SIZE_SMALL, IMPORTANCE_MEDIUM) kobuleti = ControlPoint.from_airport(caucasus.Kobuleti, COAST_VERTICAL, SIZE_SMALL, IMPORTANCE_LOW)
sukhumi = ControlPoint(caucasus.Sukhumi, COAST_VERTICAL, SIZE_REGULAR, IMPORTANCE_MEDIUM) batumi = ControlPoint.from_airport(caucasus.Batumi, COAST_VERTICAL, SIZE_SMALL, IMPORTANCE_MEDIUM)
gudauta = ControlPoint(caucasus.Gudauta, COAST_VERTICAL, SIZE_REGULAR, IMPORTANCE_MEDIUM) sukhumi = ControlPoint.from_airport(caucasus.Sukhumi_Babushara, COAST_VERTICAL, SIZE_REGULAR, IMPORTANCE_MEDIUM)
sochi = ControlPoint(caucasus.Sochi, COAST_VERTICAL, SIZE_BIG, IMPORTANCE_HIGH) gudauta = ControlPoint.from_airport(caucasus.Gudauta, COAST_VERTICAL, SIZE_REGULAR, IMPORTANCE_MEDIUM)
sochi = ControlPoint.from_airport(caucasus.Sochi_Adler, COAST_VERTICAL, SIZE_BIG, IMPORTANCE_HIGH)
maykop = ControlPoint.from_airport(caucasus.Maykop_Khanskaya, ALL_RADIALS, SIZE_LARGE, IMPORTANCE_HIGH)
krasnodar = ControlPoint.from_airport(caucasus.Krasnodar_Center, ALL_RADIALS, SIZE_LARGE, IMPORTANCE_HIGH)
novorossiysk = ControlPoint.from_airport(caucasus.Novorossiysk, COAST_VERTICAL, SIZE_BIG, IMPORTANCE_HIGH)
gelendzhik = ControlPoint.from_airport(caucasus.Gelendzhik, COAST_VERTICAL, SIZE_BIG, IMPORTANCE_HIGH)
krymsk = ControlPoint.from_airport(caucasus.Krymsk, ALL_RADIALS, SIZE_LARGE, IMPORTANCE_HIGH)
anapa = ControlPoint.from_airport(caucasus.Anapa_Vityazevo, ALL_RADIALS, SIZE_LARGE, IMPORTANCE_HIGH)
beslan = ControlPoint.from_airport(caucasus.Beslan, ALL_RADIALS, SIZE_REGULAR, IMPORTANCE_MEDIUM)
nalchik = ControlPoint.from_airport(caucasus.Nalchik, ALL_RADIALS, SIZE_REGULAR, IMPORTANCE_MEDIUM)
mineralnye = ControlPoint.from_airport(caucasus.Mineralnye_Vody, ALL_RADIALS, SIZE_BIG, IMPORTANCE_HIGH)
mozdok = ControlPoint.from_airport(caucasus.Mozdok, ALL_RADIALS, SIZE_BIG, IMPORTANCE_HIGH)
#carrier_1 = ControlPoint()
def __init__(self): def __init__(self):
self.kutaisi.captured = True self.soganlug.captured = True
self.add_controlpoint(self.kutaisi, connected_to=[self.senaki]) self.add_controlpoint(self.soganlug, connected_to=[self.kutaisi, self.beslan])
self.add_controlpoint(self.beslan, connected_to=[self.soganlug, self.mozdok, self.nalchik])
self.add_controlpoint(self.nalchik, connected_to=[self.beslan, self.mozdok, self.mineralnye])
self.add_controlpoint(self.mozdok, connected_to=[self.nalchik, self.beslan, self.mineralnye])
self.add_controlpoint(self.mineralnye, connected_to=[self.nalchik, self.mozdok, self.maykop])
self.add_controlpoint(self.maykop, connected_to=[self.mineralnye, self.krasnodar])
self.add_controlpoint(self.kutaisi, connected_to=[self.soganlug, self.senaki])
self.add_controlpoint(self.senaki, connected_to=[self.kobuleti, self.sukhumi, self.kutaisi]) self.add_controlpoint(self.senaki, connected_to=[self.kobuleti, self.sukhumi, self.kutaisi])
self.add_controlpoint(self.kobuleti, connected_to=[self.batumi, self.senaki]) self.add_controlpoint(self.kobuleti, connected_to=[self.batumi, self.senaki])
self.add_controlpoint(self.batumi, connected_to=[self.kobuleti]) self.add_controlpoint(self.batumi, connected_to=[self.kobuleti])
self.add_controlpoint(self.sukhumi, connected_to=[self.gudauta, self.senaki]) self.add_controlpoint(self.sukhumi, connected_to=[self.gudauta, self.senaki])
self.add_controlpoint(self.gudauta, connected_to=[self.sochi, self.sukhumi]) self.add_controlpoint(self.gudauta, connected_to=[self.sochi, self.sukhumi])
self.add_controlpoint(self.sochi, connected_to=[self.gudauta]) self.add_controlpoint(self.sochi, connected_to=[self.gudauta, self.gelendzhik])
self.add_controlpoint(self.gelendzhik, connected_to=[self.sochi, self.novorossiysk])
self.add_controlpoint(self.novorossiysk, connected_to=[self.gelendzhik, self.anapa])
self.add_controlpoint(self.krymsk, connected_to=[self.novorossiysk, self.anapa, self.krasnodar])
self.add_controlpoint(self.anapa, connected_to=[self.novorossiysk, self.krymsk])
self.add_controlpoint(self.krasnodar, connected_to=[self.krymsk, self.maykop])

View File

@ -13,14 +13,15 @@ class ControlPoint:
position = None # type: Point position = None # type: Point
captured = False captured = False
base: None # type: theater.base.Base base: None # type: theater.base.Base
airport: None # type: Airport at: None # type: db.StartPosition
def __init__(self, airport: Airport, radials: typing.Collection[int], size: int, importance: int): def __init__(self, name: str, position: Point, at, radials: typing.Collection[int], size: int, importance: int):
import theater.base import theater.base
self.name = airport.name self.name = name
self.position = airport.position self.position = position
self.airport = airport self.at = at
self.size = size self.size = size
self.importance = importance self.importance = importance
self.captured = False self.captured = False
@ -28,6 +29,10 @@ class ControlPoint:
self.connected_points = [] self.connected_points = []
self.base = theater.base.Base() self.base = theater.base.Base()
@classmethod
def from_airport(cls, airport: Airport, radials: typing.Collection[int], size: int, importance: int):
return cls(airport.name, airport.position, airport, radials, size, importance)
def __str__(self): def __str__(self):
return self.name return self.name
@ -61,5 +66,3 @@ class ControlPoint:
position=self.position, position=self.position,
size=self.size, size=self.size,
radials=self.radials) radials=self.radials)

View File

@ -49,6 +49,12 @@ class BaseMenu(Menu):
for unit_type in units: for unit_type in units:
purchase_row(unit_type, db.PRICES[unit_type]) purchase_row(unit_type, db.PRICES[unit_type])
def dismiss(self):
if sum([x for x in self.event.units.values()]) == 0:
self.game.units_delivery_remove(self.event)
super(BaseMenu, self).dismiss()
def buy(self, unit_type): def buy(self, unit_type):
def action(): def action():
price = db.PRICES[unit_type] price = db.PRICES[unit_type]

View File

@ -4,23 +4,26 @@ from tkinter.ttk import *
from ui.window import * from ui.window import *
from ui.eventmenu import * from ui.eventmenu import *
from ui.basemenu import * from ui.basemenu import *
from ui.overviewcanvas import *
from game.game import * from game.game import *
class MainMenu(Menu): class MainMenu(Menu):
basemenu = None # type: BaseMenu
def __init__(self, window: Window, parent, game: Game): def __init__(self, window: Window, parent, game: Game):
super(MainMenu, self).__init__(window, parent, game) super(MainMenu, self).__init__(window, parent, game)
self.image = PhotoImage(file="resources/caumap.gif") self.upd = OverviewCanvas(self.window.left_pane, self, game)
map = Label(window.left_pane, image=self.image) self.upd.update()
map.grid()
self.frame = self.window.right_pane self.frame = self.window.right_pane
self.frame.grid_columnconfigure(0, weight=1) self.frame.grid_columnconfigure(0, weight=1)
def display(self): def display(self):
self.window.clear_right_pane() self.window.clear_right_pane()
self.upd.update()
row = 1 row = 1
@ -34,23 +37,14 @@ class MainMenu(Menu):
Button(self.frame, text=text, command=self.start_event(event)).grid(row=row, sticky=N) Button(self.frame, text=text, command=self.start_event(event)).grid(row=row, sticky=N)
row += 1 row += 1
def cp_button(cp): Button(self.frame, text="Pass turn", command=self.pass_turn).grid(column=0, row=0, sticky=NE)
nonlocal row Label(self.frame, text="Budget: {}m (+{}m)".format(self.game.budget, self.game.budget_reward_amount)).grid(column=0, row=0, sticky=NW)
title = "{}{}{}{}".format( Separator(self.frame, orient='horizontal').grid(row=row, sticky=EW); row += 1
cp.name,
"^" * cp.base.total_planes,
"." * cp.base.total_armor,
"*" * cp.base.total_aa)
Button(self.frame, text=title, command=self.go_cp(cp)).grid(row=row, sticky=NW)
row += 1
Label(self.frame, text="Budget: {}m".format(self.game.budget)).grid(column=0, row=0, sticky=NW)
Button(self.frame, text="Pass turn", command=self.pass_turn).grid(column=1, row=0, sticky=NE)
row += 1
for event in self.game.events: for event in self.game.events:
if not event.informational: if not event.informational:
continue continue
label(str(event)) label(str(event))
for event in self.game.events: for event in self.game.events:
@ -59,21 +53,6 @@ class MainMenu(Menu):
event_button(event, "{} {}".format(event.attacker.name != self.game.player and "!" or " ", event)) event_button(event, "{} {}".format(event.attacker.name != self.game.player and "!" or " ", event))
Separator(self.frame, orient='horizontal').grid(row=row, sticky=EW); row += 1
for cp in self.game.theater.player_points():
cp_button(cp)
Separator(self.frame, orient='horizontal').grid(row=row, sticky=EW); row += 1
for cp in self.game.theater.enemy_bases():
title = "[{}] {}{}{}{}".format(
int(cp.base.strength * 10),
cp.name,
"^" * cp.base.total_planes,
"." * cp.base.total_armor,
"*" * cp.base.total_aa)
Label(self.frame, text=title).grid(row=row, sticky=NE)
row += 1
def pass_turn(self): def pass_turn(self):
self.game.pass_turn(no_action=True) self.game.pass_turn(no_action=True)
self.display() self.display()
@ -81,5 +60,10 @@ class MainMenu(Menu):
def start_event(self, event) -> typing.Callable: def start_event(self, event) -> typing.Callable:
return lambda: EventMenu(self.window, self, self.game, event).display() return lambda: EventMenu(self.window, self, self.game, event).display()
def go_cp(self, cp: ControlPoint) -> typing.Callable: def go_cp(self, cp: ControlPoint):
return lambda: BaseMenu(self.window, self, self.game, cp).display() if self.basemenu:
self.basemenu.dismiss()
self.basemenu = None
self.basemenu = BaseMenu(self.window, self, self.game, cp)
self.basemenu.display()

88
ui/overviewcanvas.py Normal file
View File

@ -0,0 +1,88 @@
from tkinter import *
from tkinter.ttk import *
from ui.window import *
from game.game import *
class OverviewCanvas:
mainmenu = None # type: ui.mainmenu.MainMenu
def __init__(self, frame: Frame, parent, game: Game):
self.canvas = Canvas(frame, width=616, height=350)
self.canvas.grid(column=0, row=0, sticky=NSEW)
self.image = PhotoImage(file="resources/caumap.gif")
self.parent = parent
self.game = game
def cp_coordinates(self, cp: ControlPoint) -> (int, int):
point_a = (-317948.32727306, 635639.37385346)
point_a_img = 282.5, 319
point_b = (-355692.3067714, 617269.96285781)
point_b_img = 269, 352
x_dist = point_a_img[0] - point_b_img[0]
lon_dist = point_a[1] - point_b[1]
y_dist = point_a_img[1] - point_b_img[1]
lat_dist = point_b[0] - point_a[0]
x_scale = float(x_dist) / float(lon_dist)
y_scale = float(y_dist) / float(lat_dist)
# ---
x_offset = cp.position.x - point_a[0]
y_offset = cp.position.y - point_a[1]
return point_b_img[1] + y_offset * y_scale, point_a_img[0] - x_offset * x_scale
def create_cp_title(self, coords, cp: ControlPoint):
title = cp.name
font = ("Helvetica", 13)
self.canvas.create_text(coords[0]+1, coords[1]+1, text=title, fill='white', font=font)
self.canvas.create_text(coords[0], coords[1], text=title, font=font)
def update(self):
self.canvas.delete(ALL)
self.canvas.create_image((self.image.width()/2, self.image.height()/2), image=self.image)
for cp in self.game.theater.controlpoints:
coords = self.cp_coordinates(cp)
for connected_cp in cp.connected_points:
connected_coords = self.cp_coordinates(connected_cp)
if connected_cp.captured != cp.captured:
color = "red"
elif connected_cp.captured and cp.captured:
color = "blue"
else:
color = "black"
self.canvas.create_line((coords[0], coords[1], connected_coords[0], connected_coords[1]), width=2, fill=color)
for cp in self.game.theater.controlpoints:
coords = self.cp_coordinates(cp)
arc_size = 18 * math.pow(cp.importance, 1)
extent = max(cp.base.strength * 180, 10)
start = (180 - extent) / 2
color = cp.captured and 'blue' or 'red'
cp_id = self.canvas.create_arc((coords[0] - arc_size/2, coords[1] - arc_size/2),
(coords[0]+arc_size/2, coords[1]+arc_size/2),
fill=color,
style=PIESLICE,
start=start,
extent=extent)
self.canvas.tag_bind(cp_id, "<Button-1>", self.display(cp))
self.create_cp_title((coords[0] + arc_size/2, coords[1] + arc_size/2), cp)
self.canvas.create_text(coords[0], coords[1] - arc_size / 1.5, text="8/4/2", font=("Helvetica", 10))
def display(self, cp: ControlPoint):
def action(_):
return self.parent.go_cp(cp)
return action