6
.gitignore
vendored
@ -1,10 +1,14 @@
|
|||||||
*.pyc
|
*.pyc
|
||||||
__pycache__
|
__pycache__
|
||||||
build/*
|
build/**
|
||||||
resources/payloads/*.lua
|
resources/payloads/*.lua
|
||||||
venv
|
venv
|
||||||
logs.txt
|
logs.txt
|
||||||
.DS_Store
|
.DS_Store
|
||||||
|
dist/**
|
||||||
|
a.py
|
||||||
|
resources/tools/a.miz
|
||||||
|
tests/**
|
||||||
# User-specific stuff
|
# User-specific stuff
|
||||||
.idea/**/workspace.xml
|
.idea/**/workspace.xml
|
||||||
.idea/**/tasks.xml
|
.idea/**/tasks.xml
|
||||||
|
|||||||
1
.idea/modules.xml
generated
@ -3,7 +3,6 @@
|
|||||||
<component name="ProjectModuleManager">
|
<component name="ProjectModuleManager">
|
||||||
<modules>
|
<modules>
|
||||||
<module fileurl="file://$PROJECT_DIR$/.idea/dcs_pmcliberation.iml" filepath="$PROJECT_DIR$/.idea/dcs_pmcliberation.iml" />
|
<module fileurl="file://$PROJECT_DIR$/.idea/dcs_pmcliberation.iml" filepath="$PROJECT_DIR$/.idea/dcs_pmcliberation.iml" />
|
||||||
<module fileurl="file://$PROJECT_DIR$/.idea/dcs_pmcliberation.iml" filepath="$PROJECT_DIR$/.idea/dcs_pmcliberation.iml" />
|
|
||||||
</modules>
|
</modules>
|
||||||
</component>
|
</component>
|
||||||
</project>
|
</project>
|
||||||
46
__init__.py
@ -1,21 +1,16 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
|
import logging
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
import dcs
|
import dcs
|
||||||
import logging
|
|
||||||
|
|
||||||
import theater.caucasus
|
import ui.corruptedsavemenu
|
||||||
import theater.persiangulf
|
|
||||||
import theater.nevada
|
|
||||||
|
|
||||||
import ui.window
|
|
||||||
import ui.mainmenu
|
import ui.mainmenu
|
||||||
import ui.newgamemenu
|
import ui.newgamemenu
|
||||||
import ui.corruptedsavemenu
|
import ui.window
|
||||||
|
|
||||||
from game.game import Game
|
from game.game import Game
|
||||||
from theater import start_generator
|
|
||||||
from userdata import persistency, logging as logging_module
|
from userdata import persistency, logging as logging_module
|
||||||
|
|
||||||
assert len(sys.argv) >= 3, "__init__.py should be started with two mandatory arguments: %UserProfile% location and application version"
|
assert len(sys.argv) >= 3, "__init__.py should be started with two mandatory arguments: %UserProfile% location and application version"
|
||||||
@ -53,40 +48,11 @@ def is_version_compatible(save_version):
|
|||||||
|
|
||||||
|
|
||||||
w = ui.window.Window()
|
w = ui.window.Window()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
game = persistency.restore_game()
|
game = persistency.restore_game()
|
||||||
if not game or not is_version_compatible(game.settings.version):
|
if not game or not is_version_compatible(game.settings.version):
|
||||||
new_game_menu = None # type: NewGameMenu
|
ui.newgamemenu.NewGameMenu(w, w.start_new_game).display()
|
||||||
|
|
||||||
def start_new_game(player_name: str, enemy_name: str, terrain: str, sams: bool, midgame: bool, multiplier: float):
|
|
||||||
if terrain == "persiangulf":
|
|
||||||
conflicttheater = theater.persiangulf.PersianGulfTheater()
|
|
||||||
elif terrain == "nevada":
|
|
||||||
conflicttheater = theater.nevada.NevadaTheater()
|
|
||||||
else:
|
|
||||||
conflicttheater = theater.caucasus.CaucasusTheater()
|
|
||||||
|
|
||||||
if midgame:
|
|
||||||
for i in range(0, int(len(conflicttheater.controlpoints) / 2)):
|
|
||||||
conflicttheater.controlpoints[i].captured = True
|
|
||||||
|
|
||||||
start_generator.generate_inital_units(conflicttheater, enemy_name, sams, multiplier)
|
|
||||||
start_generator.generate_groundobjects(conflicttheater)
|
|
||||||
game = Game(player_name=player_name,
|
|
||||||
enemy_name=enemy_name,
|
|
||||||
theater=conflicttheater)
|
|
||||||
game.budget = int(game.budget * multiplier)
|
|
||||||
game.settings.multiplier = multiplier
|
|
||||||
game.settings.sams = sams
|
|
||||||
game.settings.version = VERSION_STRING
|
|
||||||
|
|
||||||
if midgame:
|
|
||||||
game.budget = game.budget * 4 * len(list(conflicttheater.conflicts()))
|
|
||||||
|
|
||||||
proceed_to_main_menu(game)
|
|
||||||
|
|
||||||
new_game_menu = ui.newgamemenu.NewGameMenu(w, start_new_game)
|
|
||||||
new_game_menu.display()
|
|
||||||
else:
|
else:
|
||||||
game.settings.version = VERSION_STRING
|
game.settings.version = VERSION_STRING
|
||||||
proceed_to_main_menu(game)
|
proceed_to_main_menu(game)
|
||||||
|
|||||||
38
a.py
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
from theater.caucasus import *
|
||||||
|
from gen.conflictgen import Conflict
|
||||||
|
|
||||||
|
from matplotlib import pyplot
|
||||||
|
from matplotlib import lines
|
||||||
|
from shapely import geometry
|
||||||
|
from shapely.geometry import Polygon
|
||||||
|
from descartes.patch import PolygonPatch
|
||||||
|
|
||||||
|
def put_lines(ls, ax):
|
||||||
|
for g in ls.geoms:
|
||||||
|
ax.plot([g.xy[0][0], g.xy[0][1]], [g.xy[1][0], g.xy[1][1]])
|
||||||
|
|
||||||
|
cau = CaucasusTheater()
|
||||||
|
#left, heading, dist = Conflict.frontline_vector(cau.soganlug, cau.kutaisi, cau)
|
||||||
|
#right = left.point_from_heading(heading, dist)
|
||||||
|
|
||||||
|
left, heading = Conflict.frontline_position(cau, cau.soganlug, cau.kutaisi)
|
||||||
|
right = left.point_from_heading(heading+90, 80000)
|
||||||
|
left = left.point_from_heading(heading-90, 80000)
|
||||||
|
|
||||||
|
line = geometry.LineString([(left.x, left.y), (right.x, right.y)])
|
||||||
|
line = line.intersection(cau.land_poly)
|
||||||
|
|
||||||
|
fig = pyplot.figure(1, figsize=(20, 20), dpi=90)
|
||||||
|
ax = fig.add_subplot(121)
|
||||||
|
ax.set_ylim([0, 1500000])
|
||||||
|
ax.set_xlim([-600000, 400000])
|
||||||
|
|
||||||
|
patch = PolygonPatch(cau.land_poly, facecolor=(0, 0, 0), edgecolor=(0, 0, 0), alpha=0.5, zorder=2)
|
||||||
|
ax.add_patch(patch)
|
||||||
|
ax.plot([left.x, right.x], [left.y, right.y], 'k-', lw=2)
|
||||||
|
|
||||||
|
ax.plot([cau.soganlug.position.x, cau.soganlug.position.x+1000], [cau.soganlug.position.y, cau.soganlug.position.y+1000], lw=5)
|
||||||
|
ax.plot([cau.kutaisi.position.x, cau.kutaisi.position.x+1000], [cau.kutaisi.position.y, cau.kutaisi.position.y+1000], lw=5)
|
||||||
|
put_lines(line, ax)
|
||||||
|
pyplot.show()
|
||||||
|
|
||||||
41
game/db.py
@ -39,12 +39,11 @@ and prioritization for the enemy (i.e. less important bases will receive units w
|
|||||||
"""
|
"""
|
||||||
PRICES = {
|
PRICES = {
|
||||||
# fighter
|
# fighter
|
||||||
C_101CC: 8,
|
MiG_23MLD: 13,
|
||||||
MiG_23MLD: 18,
|
Su_27: 18,
|
||||||
Su_27: 20,
|
|
||||||
Su_33: 22,
|
Su_33: 22,
|
||||||
MiG_29A: 23,
|
MiG_29A: 18,
|
||||||
MiG_29S: 25,
|
MiG_29S: 20,
|
||||||
|
|
||||||
F_5E_3: 6,
|
F_5E_3: 6,
|
||||||
MiG_15bis: 5,
|
MiG_15bis: 5,
|
||||||
@ -55,6 +54,7 @@ PRICES = {
|
|||||||
M_2000C: 13,
|
M_2000C: 13,
|
||||||
FA_18C_hornet: 18,
|
FA_18C_hornet: 18,
|
||||||
F_15C: 20,
|
F_15C: 20,
|
||||||
|
F_14B: 14,
|
||||||
|
|
||||||
# bomber
|
# bomber
|
||||||
Su_25: 15,
|
Su_25: 15,
|
||||||
@ -85,14 +85,14 @@ PRICES = {
|
|||||||
C_130: 8,
|
C_130: 8,
|
||||||
|
|
||||||
# armor
|
# armor
|
||||||
Armor.APC_BTR_80: 12,
|
Armor.APC_BTR_80: 16,
|
||||||
Armor.MBT_T_55: 14,
|
Armor.MBT_T_55: 22,
|
||||||
Armor.MBT_T_80U: 18,
|
Armor.MBT_T_80U: 28,
|
||||||
Armor.MBT_T_90: 20,
|
Armor.MBT_T_90: 35,
|
||||||
|
|
||||||
Armor.ATGM_M1134_Stryker: 12,
|
Armor.ATGM_M1134_Stryker: 18,
|
||||||
Armor.MBT_M60A3_Patton: 14,
|
Armor.MBT_M60A3_Patton: 24,
|
||||||
Armor.MBT_M1A2_Abrams: 18,
|
Armor.MBT_M1A2_Abrams: 35,
|
||||||
|
|
||||||
Unarmed.Transport_UAZ_469: 3,
|
Unarmed.Transport_UAZ_469: 3,
|
||||||
Unarmed.Transport_Ural_375: 3,
|
Unarmed.Transport_Ural_375: 3,
|
||||||
@ -137,7 +137,6 @@ Following tasks are present:
|
|||||||
"""
|
"""
|
||||||
UNIT_BY_TASK = {
|
UNIT_BY_TASK = {
|
||||||
CAP: [
|
CAP: [
|
||||||
C_101CC,
|
|
||||||
F_5E_3,
|
F_5E_3,
|
||||||
MiG_23MLD,
|
MiG_23MLD,
|
||||||
Su_27,
|
Su_27,
|
||||||
@ -147,6 +146,7 @@ UNIT_BY_TASK = {
|
|||||||
MiG_29S,
|
MiG_29S,
|
||||||
FA_18C_hornet,
|
FA_18C_hornet,
|
||||||
F_15C,
|
F_15C,
|
||||||
|
F_14B,
|
||||||
M_2000C,
|
M_2000C,
|
||||||
],
|
],
|
||||||
CAS: [
|
CAS: [
|
||||||
@ -235,7 +235,6 @@ SAM_BAN = [
|
|||||||
Units that will always be spawned in the air
|
Units that will always be spawned in the air
|
||||||
"""
|
"""
|
||||||
TAKEOFF_BAN = [
|
TAKEOFF_BAN = [
|
||||||
AV8BNA, # AI takeoff currently bugged attempting VTOL with no regards for the total weight
|
|
||||||
]
|
]
|
||||||
|
|
||||||
"""
|
"""
|
||||||
@ -259,7 +258,6 @@ Be advised that putting unit to the country that have not access to the unit in
|
|||||||
"""
|
"""
|
||||||
UNIT_BY_COUNTRY = {
|
UNIT_BY_COUNTRY = {
|
||||||
"Russia": [
|
"Russia": [
|
||||||
C_101CC,
|
|
||||||
AJS37,
|
AJS37,
|
||||||
MiG_23MLD,
|
MiG_23MLD,
|
||||||
F_5E_3,
|
F_5E_3,
|
||||||
@ -308,6 +306,7 @@ UNIT_BY_COUNTRY = {
|
|||||||
"USA": [
|
"USA": [
|
||||||
F_5E_3,
|
F_5E_3,
|
||||||
F_15C,
|
F_15C,
|
||||||
|
F_14B,
|
||||||
FA_18C_hornet,
|
FA_18C_hornet,
|
||||||
AJS37,
|
AJS37,
|
||||||
M_2000C,
|
M_2000C,
|
||||||
@ -345,6 +344,7 @@ UNIT_BY_COUNTRY = {
|
|||||||
|
|
||||||
CARRIER_TYPE_BY_PLANE = {
|
CARRIER_TYPE_BY_PLANE = {
|
||||||
FA_18C_hornet: CVN_74_John_C__Stennis,
|
FA_18C_hornet: CVN_74_John_C__Stennis,
|
||||||
|
F_14B: CVN_74_John_C__Stennis,
|
||||||
Ka_50: LHA_1_Tarawa,
|
Ka_50: LHA_1_Tarawa,
|
||||||
SA342M: LHA_1_Tarawa,
|
SA342M: LHA_1_Tarawa,
|
||||||
UH_1H: LHA_1_Tarawa,
|
UH_1H: LHA_1_Tarawa,
|
||||||
@ -371,16 +371,25 @@ Payload will be used for operation of following type, "*" category will be used
|
|||||||
PLANE_PAYLOAD_OVERRIDES = {
|
PLANE_PAYLOAD_OVERRIDES = {
|
||||||
FA_18C_hornet: {
|
FA_18C_hornet: {
|
||||||
CAP: "AIM-120*4,AIM-9*2,AIM-7*2,Fuel",
|
CAP: "AIM-120*4,AIM-9*2,AIM-7*2,Fuel",
|
||||||
|
Escort: "AIM-120*4,AIM-9*2,AIM-7*2,Fuel",
|
||||||
PinpointStrike: "MK-82*8,AIM-9*2,AIM-7,FLIR Pod,Fuel",
|
PinpointStrike: "MK-82*8,AIM-9*2,AIM-7,FLIR Pod,Fuel",
|
||||||
AntishipStrike: "MK-82*8,AIM-9*2,AIM-7,FLIR Pod,Fuel",
|
AntishipStrike: "MK-82*8,AIM-9*2,AIM-7,FLIR Pod,Fuel",
|
||||||
},
|
},
|
||||||
|
|
||||||
|
F_14B: {
|
||||||
|
CAP: "AIM-54A-MK47*4, AIM-7M*2, AIM-9M*2, XT*2",
|
||||||
|
Escort: "AIM-54A-MK47*4, AIM-7M*2, AIM-9M*2, XT*2",
|
||||||
|
CAS: "AIM-54A-MK60*1, AIM-7M*1, AIM-9M*2, XT*2, Mk-82*2, LANTIRN",
|
||||||
|
GroundAttack: "AIM54, AIM-9M*2, XT*2, GBU-12*4, LANTIRN",
|
||||||
|
},
|
||||||
|
|
||||||
Su_25T: {
|
Su_25T: {
|
||||||
CAS: "APU-8 Vikhr-M*2,Kh-25ML,R-73*2,SPPU-22*2,Mercury LLTV Pod,MPS-410",
|
CAS: "APU-8 Vikhr-M*2,Kh-25ML,R-73*2,SPPU-22*2,Mercury LLTV Pod,MPS-410",
|
||||||
},
|
},
|
||||||
|
|
||||||
Su_33: {
|
Su_33: {
|
||||||
CAP: "R-73*4,R-27R*2,R-27ER*6",
|
CAP: "R-73*4,R-27R*2,R-27ER*6",
|
||||||
|
Escort: "R-73*4,R-27R*2,R-27ER*6",
|
||||||
},
|
},
|
||||||
|
|
||||||
AJS37: {
|
AJS37: {
|
||||||
@ -403,6 +412,7 @@ PLANE_PAYLOAD_OVERRIDES = {
|
|||||||
|
|
||||||
M_2000C: {
|
M_2000C: {
|
||||||
CAP: "Combat Air Patrol",
|
CAP: "Combat Air Patrol",
|
||||||
|
Escort: "Combat Air Patrol",
|
||||||
GroundAttack: "MK-82S Heavy Strike",
|
GroundAttack: "MK-82S Heavy Strike",
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -487,6 +497,7 @@ def task_name(task) -> str:
|
|||||||
|
|
||||||
def choose_units(for_task: Task, factor: float, count: int, country: str) -> typing.Collection[UnitType]:
|
def choose_units(for_task: Task, factor: float, count: int, country: str) -> typing.Collection[UnitType]:
|
||||||
suitable_unittypes = find_unittype(for_task, country)
|
suitable_unittypes = find_unittype(for_task, country)
|
||||||
|
suitable_unittypes = [x for x in suitable_unittypes if x not in helicopter_map.values()]
|
||||||
suitable_unittypes.sort(key=lambda x: PRICES[x])
|
suitable_unittypes.sort(key=lambda x: PRICES[x])
|
||||||
|
|
||||||
idx = int(len(suitable_unittypes) * factor)
|
idx = int(len(suitable_unittypes) * factor)
|
||||||
|
|||||||
@ -5,5 +5,6 @@ from .intercept import *
|
|||||||
from .baseattack import *
|
from .baseattack import *
|
||||||
from .navalintercept import *
|
from .navalintercept import *
|
||||||
from .insurgentattack import *
|
from .insurgentattack import *
|
||||||
|
from .convoystrike import *
|
||||||
from .infantrytransport import *
|
from .infantrytransport import *
|
||||||
from .strike import *
|
from .strike import *
|
||||||
|
|||||||
@ -25,10 +25,10 @@ class BaseAttackEvent(Event):
|
|||||||
return "Ground attack"
|
return "Ground attack"
|
||||||
|
|
||||||
def is_successfull(self, debriefing: Debriefing):
|
def is_successfull(self, debriefing: Debriefing):
|
||||||
alive_attackers = sum([v for k, v in debriefing.alive_units[self.attacker_name].items() if db.unit_task(k) == PinpointStrike])
|
alive_attackers = sum([v for k, v in debriefing.alive_units.get(self.attacker_name, {}).items() if db.unit_task(k) == PinpointStrike])
|
||||||
alive_defenders = sum([v for k, v in debriefing.alive_units[self.defender_name].items() if db.unit_task(k) == PinpointStrike])
|
alive_defenders = sum([v for k, v in debriefing.alive_units.get(self.defender_name, {}).items() if db.unit_task(k) == PinpointStrike])
|
||||||
attackers_success = alive_attackers >= alive_defenders
|
attackers_success = alive_attackers >= alive_defenders
|
||||||
if self.from_cp.captured:
|
if self.departure_cp.captured:
|
||||||
return attackers_success
|
return attackers_success
|
||||||
else:
|
else:
|
||||||
return not attackers_success
|
return not attackers_success
|
||||||
@ -36,14 +36,14 @@ class BaseAttackEvent(Event):
|
|||||||
def commit(self, debriefing: Debriefing):
|
def commit(self, debriefing: Debriefing):
|
||||||
super(BaseAttackEvent, self).commit(debriefing)
|
super(BaseAttackEvent, self).commit(debriefing)
|
||||||
if self.is_successfull(debriefing):
|
if self.is_successfull(debriefing):
|
||||||
if self.from_cp.captured:
|
if self.departure_cp.captured:
|
||||||
self.to_cp.captured = True
|
self.to_cp.captured = True
|
||||||
self.to_cp.ground_objects = []
|
self.to_cp.ground_objects = []
|
||||||
self.to_cp.base.filter_units(db.UNIT_BY_COUNTRY[self.attacker_name])
|
self.to_cp.base.filter_units(db.UNIT_BY_COUNTRY[self.attacker_name])
|
||||||
|
|
||||||
self.to_cp.base.affect_strength(+self.STRENGTH_RECOVERY)
|
self.to_cp.base.affect_strength(+self.STRENGTH_RECOVERY)
|
||||||
else:
|
else:
|
||||||
if not self.from_cp.captured:
|
if not self.departure_cp.captured:
|
||||||
self.to_cp.captured = False
|
self.to_cp.captured = False
|
||||||
self.to_cp.base.affect_strength(+self.STRENGTH_RECOVERY)
|
self.to_cp.base.affect_strength(+self.STRENGTH_RECOVERY)
|
||||||
|
|
||||||
@ -54,14 +54,15 @@ class BaseAttackEvent(Event):
|
|||||||
def player_defending(self, flights: db.TaskForceDict):
|
def player_defending(self, flights: db.TaskForceDict):
|
||||||
assert CAP in flights and len(flights) == 1, "Invalid scrambled flights"
|
assert CAP in flights and len(flights) == 1, "Invalid scrambled flights"
|
||||||
|
|
||||||
cas = self.from_cp.base.scramble_cas(self.game.settings.multiplier)
|
cas = self.departure_cp.base.scramble_cas(self.game.settings.multiplier)
|
||||||
escort = self.from_cp.base.scramble_sweep(self.game.settings.multiplier)
|
escort = self.departure_cp.base.scramble_sweep(self.game.settings.multiplier)
|
||||||
attackers = self.from_cp.base.armor
|
attackers = self.departure_cp.base.armor
|
||||||
|
|
||||||
op = BaseAttackOperation(game=self.game,
|
op = BaseAttackOperation(game=self.game,
|
||||||
attacker_name=self.attacker_name,
|
attacker_name=self.attacker_name,
|
||||||
defender_name=self.defender_name,
|
defender_name=self.defender_name,
|
||||||
from_cp=self.from_cp,
|
from_cp=self.from_cp,
|
||||||
|
departure_cp=self.departure_cp,
|
||||||
to_cp=self.to_cp)
|
to_cp=self.to_cp)
|
||||||
|
|
||||||
op.setup(cas=assigned_units_from(cas),
|
op.setup(cas=assigned_units_from(cas),
|
||||||
@ -80,6 +81,7 @@ class BaseAttackEvent(Event):
|
|||||||
attacker_name=self.attacker_name,
|
attacker_name=self.attacker_name,
|
||||||
defender_name=self.defender_name,
|
defender_name=self.defender_name,
|
||||||
from_cp=self.from_cp,
|
from_cp=self.from_cp,
|
||||||
|
departure_cp=self.departure_cp,
|
||||||
to_cp=self.to_cp)
|
to_cp=self.to_cp)
|
||||||
|
|
||||||
defenders = self.to_cp.base.scramble_sweep(self.game.settings.multiplier)
|
defenders = self.to_cp.base.scramble_sweep(self.game.settings.multiplier)
|
||||||
|
|||||||
83
game/event/convoystrike.py
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
import math
|
||||||
|
import random
|
||||||
|
|
||||||
|
from dcs.task import *
|
||||||
|
|
||||||
|
from game import *
|
||||||
|
from game.event import *
|
||||||
|
from game.event.frontlineattack import FrontlineAttackEvent
|
||||||
|
|
||||||
|
from .event import *
|
||||||
|
from game.operation.convoystrike import ConvoyStrikeOperation
|
||||||
|
|
||||||
|
TRANSPORT_COUNT = 4, 6
|
||||||
|
DEFENDERS_AMOUNT_FACTOR = 4
|
||||||
|
|
||||||
|
|
||||||
|
class ConvoyStrikeEvent(Event):
|
||||||
|
SUCCESS_FACTOR = 0.6
|
||||||
|
STRENGTH_INFLUENCE = 0.25
|
||||||
|
|
||||||
|
targets = None # type: db.ArmorDict
|
||||||
|
|
||||||
|
@property
|
||||||
|
def threat_description(self):
|
||||||
|
return ""
|
||||||
|
|
||||||
|
@property
|
||||||
|
def tasks(self):
|
||||||
|
return [CAS]
|
||||||
|
|
||||||
|
@property
|
||||||
|
def global_cp_available(self) -> bool:
|
||||||
|
return True
|
||||||
|
|
||||||
|
def flight_name(self, for_task: typing.Type[Task]) -> str:
|
||||||
|
if for_task == CAS:
|
||||||
|
return "Strike flight"
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return "Convoy Strike"
|
||||||
|
|
||||||
|
def skip(self):
|
||||||
|
self.to_cp.base.affect_strength(-self.STRENGTH_INFLUENCE)
|
||||||
|
|
||||||
|
def commit(self, debriefing: Debriefing):
|
||||||
|
super(ConvoyStrikeEvent, self).commit(debriefing)
|
||||||
|
|
||||||
|
if self.from_cp.captured:
|
||||||
|
if self.is_successfull(debriefing):
|
||||||
|
self.to_cp.base.affect_strength(-self.STRENGTH_INFLUENCE)
|
||||||
|
else:
|
||||||
|
if self.is_successfull(debriefing):
|
||||||
|
self.from_cp.base.affect_strength(-self.STRENGTH_INFLUENCE)
|
||||||
|
|
||||||
|
def is_successfull(self, debriefing: Debriefing):
|
||||||
|
killed_units = sum([v for k, v in debriefing.destroyed_units.get(self.defender_name, {}).items() if db.unit_task(k) in [PinpointStrike, Reconnaissance]])
|
||||||
|
all_units = sum(self.targets.values())
|
||||||
|
attackers_success = (float(killed_units) / (all_units + 0.01)) > self.SUCCESS_FACTOR
|
||||||
|
if self.from_cp.captured:
|
||||||
|
return attackers_success
|
||||||
|
else:
|
||||||
|
return not attackers_success
|
||||||
|
|
||||||
|
def player_attacking(self, flights: db.TaskForceDict):
|
||||||
|
assert CAS in flights and len(flights) == 1, "Invalid flights"
|
||||||
|
|
||||||
|
convoy_unittype = db.find_unittype(Reconnaissance, self.defender_name)[0]
|
||||||
|
defense_unittype = db.find_unittype(PinpointStrike, self.defender_name)[0]
|
||||||
|
|
||||||
|
defenders_count = int(math.ceil(self.from_cp.base.strength * self.from_cp.importance * DEFENDERS_AMOUNT_FACTOR))
|
||||||
|
self.targets = {convoy_unittype: random.randrange(*TRANSPORT_COUNT),
|
||||||
|
defense_unittype: defenders_count, }
|
||||||
|
|
||||||
|
op = ConvoyStrikeOperation(game=self.game,
|
||||||
|
attacker_name=self.attacker_name,
|
||||||
|
defender_name=self.defender_name,
|
||||||
|
from_cp=self.from_cp,
|
||||||
|
departure_cp=self.departure_cp,
|
||||||
|
to_cp=self.to_cp)
|
||||||
|
op.setup(target=self.targets,
|
||||||
|
strikegroup=flights[CAS])
|
||||||
|
|
||||||
|
self.operation = op
|
||||||
@ -9,12 +9,14 @@ from dcs.unittype import UnitType
|
|||||||
from game import *
|
from game import *
|
||||||
from theater import *
|
from theater import *
|
||||||
from gen.environmentgen import EnvironmentSettings
|
from gen.environmentgen import EnvironmentSettings
|
||||||
|
from gen.conflictgen import Conflict
|
||||||
from game.db import assigned_units_from, unitdict_from
|
from game.db import assigned_units_from, unitdict_from
|
||||||
|
|
||||||
from userdata.debriefing import Debriefing
|
from userdata.debriefing import Debriefing
|
||||||
from userdata import persistency
|
from userdata import persistency
|
||||||
|
|
||||||
DIFFICULTY_LOG_BASE = 1.1
|
DIFFICULTY_LOG_BASE = 1.1
|
||||||
|
EVENT_DEPARTURE_MAX_DISTANCE = 340000
|
||||||
|
|
||||||
|
|
||||||
class Event:
|
class Event:
|
||||||
@ -22,18 +24,26 @@ class Event:
|
|||||||
informational = False
|
informational = False
|
||||||
is_awacs_enabled = False
|
is_awacs_enabled = False
|
||||||
ca_slots = 0
|
ca_slots = 0
|
||||||
|
|
||||||
|
game = None # type: Game
|
||||||
|
location = None # type: Point
|
||||||
|
from_cp = None # type: ControlPoint
|
||||||
|
departure_cp = None # type: ControlPoint
|
||||||
|
to_cp = None # type: ControlPoint
|
||||||
|
|
||||||
operation = None # type: Operation
|
operation = None # type: Operation
|
||||||
difficulty = 1 # type: int
|
difficulty = 1 # type: int
|
||||||
game = None # type: Game
|
|
||||||
environment_settings = None # type: EnvironmentSettings
|
environment_settings = None # type: EnvironmentSettings
|
||||||
BONUS_BASE = 5
|
BONUS_BASE = 5
|
||||||
|
|
||||||
def __init__(self, attacker_name: str, defender_name: str, from_cp: ControlPoint, to_cp: ControlPoint, game):
|
def __init__(self, game, from_cp: ControlPoint, target_cp: ControlPoint, location: Point, attacker_name: str, defender_name: str):
|
||||||
|
self.game = game
|
||||||
|
self.departure_cp = None
|
||||||
|
self.from_cp = from_cp
|
||||||
|
self.to_cp = target_cp
|
||||||
|
self.location = location
|
||||||
self.attacker_name = attacker_name
|
self.attacker_name = attacker_name
|
||||||
self.defender_name = defender_name
|
self.defender_name = defender_name
|
||||||
self.to_cp = to_cp
|
|
||||||
self.from_cp = from_cp
|
|
||||||
self.game = game
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def is_player_attacking(self) -> bool:
|
def is_player_attacking(self) -> bool:
|
||||||
@ -44,7 +54,7 @@ class Event:
|
|||||||
if self.attacker_name == self.game.player:
|
if self.attacker_name == self.game.player:
|
||||||
return self.to_cp
|
return self.to_cp
|
||||||
else:
|
else:
|
||||||
return self.from_cp
|
return self.departure_cp
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def threat_description(self) -> str:
|
def threat_description(self) -> str:
|
||||||
@ -61,17 +71,43 @@ class Event:
|
|||||||
def ai_banned_tasks(self) -> typing.Collection[typing.Type[Task]]:
|
def ai_banned_tasks(self) -> typing.Collection[typing.Type[Task]]:
|
||||||
return []
|
return []
|
||||||
|
|
||||||
|
@property
|
||||||
|
def player_banned_tasks(self) -> typing.Collection[typing.Type[Task]]:
|
||||||
|
return []
|
||||||
|
|
||||||
|
@property
|
||||||
|
def global_cp_available(self) -> bool:
|
||||||
|
return False
|
||||||
|
|
||||||
|
def is_departure_available_from(self, cp: ControlPoint) -> bool:
|
||||||
|
if not cp.captured:
|
||||||
|
return False
|
||||||
|
|
||||||
|
if self.location.distance_to_point(cp.position) > EVENT_DEPARTURE_MAX_DISTANCE:
|
||||||
|
return False
|
||||||
|
|
||||||
|
if cp.is_global and not self.global_cp_available:
|
||||||
|
return False
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
def bonus(self) -> int:
|
def bonus(self) -> int:
|
||||||
return int(math.log(self.to_cp.importance + 1, DIFFICULTY_LOG_BASE) * self.BONUS_BASE)
|
return int(math.log(self.to_cp.importance + 1, DIFFICULTY_LOG_BASE) * self.BONUS_BASE)
|
||||||
|
|
||||||
def is_successfull(self, debriefing: Debriefing) -> bool:
|
def is_successfull(self, debriefing: Debriefing) -> bool:
|
||||||
return self.operation.is_successfull(debriefing)
|
return self.operation.is_successfull(debriefing)
|
||||||
|
|
||||||
def player_attacking(self, flights: db.TaskForceDict):
|
def player_attacking(self, cp: ControlPoint, flights: db.TaskForceDict):
|
||||||
assert False
|
if self.is_player_attacking:
|
||||||
|
self.departure_cp = cp
|
||||||
|
else:
|
||||||
|
self.to_cp = cp
|
||||||
|
|
||||||
def player_defending(self, flights: db.TaskForceDict):
|
def player_defending(self, cp: ControlPoint, flights: db.TaskForceDict):
|
||||||
assert False
|
if self.is_player_attacking:
|
||||||
|
self.departure_cp = cp
|
||||||
|
else:
|
||||||
|
self.to_cp = cp
|
||||||
|
|
||||||
def generate(self):
|
def generate(self):
|
||||||
self.operation.is_awacs_enabled = self.is_awacs_enabled
|
self.operation.is_awacs_enabled = self.is_awacs_enabled
|
||||||
@ -93,7 +129,7 @@ class Event:
|
|||||||
def commit(self, debriefing: Debriefing):
|
def commit(self, debriefing: Debriefing):
|
||||||
for country, losses in debriefing.destroyed_units.items():
|
for country, losses in debriefing.destroyed_units.items():
|
||||||
if country == self.attacker_name:
|
if country == self.attacker_name:
|
||||||
cp = self.from_cp
|
cp = self.departure_cp
|
||||||
else:
|
else:
|
||||||
cp = self.to_cp
|
cp = self.to_cp
|
||||||
|
|
||||||
@ -122,11 +158,12 @@ class UnitsDeliveryEvent(Event):
|
|||||||
units = None # type: typing.Dict[UnitType, int]
|
units = None # type: typing.Dict[UnitType, int]
|
||||||
|
|
||||||
def __init__(self, attacker_name: str, defender_name: str, from_cp: ControlPoint, to_cp: ControlPoint, game):
|
def __init__(self, attacker_name: str, defender_name: str, from_cp: ControlPoint, to_cp: ControlPoint, game):
|
||||||
super(UnitsDeliveryEvent, self).__init__(attacker_name=attacker_name,
|
super(UnitsDeliveryEvent, self).__init__(game=game,
|
||||||
defender_name=defender_name,
|
location=to_cp.position,
|
||||||
from_cp=from_cp,
|
from_cp=from_cp,
|
||||||
to_cp=to_cp,
|
target_cp=to_cp,
|
||||||
game=game)
|
attacker_name=attacker_name,
|
||||||
|
defender_name=defender_name)
|
||||||
|
|
||||||
self.units = {}
|
self.units = {}
|
||||||
|
|
||||||
|
|||||||
@ -11,8 +11,6 @@ class FrontlineAttackEvent(Event):
|
|||||||
STRENGTH_INFLUENCE = 0.3
|
STRENGTH_INFLUENCE = 0.3
|
||||||
SUCCESS_FACTOR = 1.5
|
SUCCESS_FACTOR = 1.5
|
||||||
|
|
||||||
defenders = None # type: db.ArmorDict
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def threat_description(self):
|
def threat_description(self):
|
||||||
return "{} vehicles".format(self.to_cp.base.assemble_count())
|
return "{} vehicles".format(self.to_cp.base.assemble_count())
|
||||||
@ -20,9 +18,13 @@ class FrontlineAttackEvent(Event):
|
|||||||
@property
|
@property
|
||||||
def tasks(self) -> typing.Collection[typing.Type[Task]]:
|
def tasks(self) -> typing.Collection[typing.Type[Task]]:
|
||||||
if self.is_player_attacking:
|
if self.is_player_attacking:
|
||||||
return [CAS, PinpointStrike]
|
return [CAS, CAP]
|
||||||
else:
|
else:
|
||||||
return [CAP, PinpointStrike]
|
return [CAP]
|
||||||
|
|
||||||
|
@property
|
||||||
|
def global_cp_available(self) -> bool:
|
||||||
|
return True
|
||||||
|
|
||||||
def flight_name(self, for_task: typing.Type[Task]) -> str:
|
def flight_name(self, for_task: typing.Type[Task]) -> str:
|
||||||
if for_task == CAS:
|
if for_task == CAS:
|
||||||
@ -36,8 +38,8 @@ class FrontlineAttackEvent(Event):
|
|||||||
return "Frontline attack"
|
return "Frontline attack"
|
||||||
|
|
||||||
def is_successfull(self, debriefing: Debriefing):
|
def is_successfull(self, debriefing: Debriefing):
|
||||||
alive_attackers = sum([v for k, v in debriefing.alive_units[self.attacker_name].items() if db.unit_task(k) == PinpointStrike])
|
alive_attackers = sum([v for k, v in debriefing.alive_units.get(self.attacker_name, {}).items() if db.unit_task(k) == PinpointStrike])
|
||||||
alive_defenders = sum([v for k, v in debriefing.alive_units[self.defender_name].items() if db.unit_task(k) == PinpointStrike])
|
alive_defenders = sum([v for k, v in debriefing.alive_units.get(self.defender_name, {}).items() if db.unit_task(k) == PinpointStrike])
|
||||||
attackers_success = (float(alive_attackers) / (alive_defenders + 0.01)) > self.SUCCESS_FACTOR
|
attackers_success = (float(alive_attackers) / (alive_defenders + 0.01)) > self.SUCCESS_FACTOR
|
||||||
if self.from_cp.captured:
|
if self.from_cp.captured:
|
||||||
return attackers_success
|
return attackers_success
|
||||||
@ -63,20 +65,46 @@ class FrontlineAttackEvent(Event):
|
|||||||
self.to_cp.base.affect_strength(-0.1)
|
self.to_cp.base.affect_strength(-0.1)
|
||||||
|
|
||||||
def player_attacking(self, flights: db.TaskForceDict):
|
def player_attacking(self, flights: db.TaskForceDict):
|
||||||
assert CAS in flights and PinpointStrike in flights and len(flights) == 2, "Invalid flights"
|
assert CAS in flights and CAP in flights and len(flights) == 2, "Invalid flights"
|
||||||
|
|
||||||
self.defenders = self.to_cp.base.assemble_attack()
|
|
||||||
|
|
||||||
op = FrontlineAttackOperation(game=self.game,
|
op = FrontlineAttackOperation(game=self.game,
|
||||||
attacker_name=self.attacker_name,
|
attacker_name=self.attacker_name,
|
||||||
defender_name=self.defender_name,
|
defender_name=self.defender_name,
|
||||||
from_cp=self.from_cp,
|
from_cp=self.from_cp,
|
||||||
|
departure_cp=self.departure_cp,
|
||||||
to_cp=self.to_cp)
|
to_cp=self.to_cp)
|
||||||
|
|
||||||
armor = unitdict_from(flights[PinpointStrike])
|
defenders = self.to_cp.base.assemble_attack()
|
||||||
op.setup(target=self.defenders,
|
max_attackers = int(math.ceil(sum(defenders.values()) * self.ATTACKER_DEFENDER_FACTOR))
|
||||||
attackers=db.unitdict_restrict_count(armor, sum(self.defenders.values())),
|
attackers = db.unitdict_restrict_count(self.from_cp.base.assemble_attack(), max_attackers)
|
||||||
strikegroup=flights[CAS])
|
op.setup(defenders=defenders,
|
||||||
|
attackers=attackers,
|
||||||
|
strikegroup=flights[CAS],
|
||||||
|
escort=flights[CAP],
|
||||||
|
interceptors=assigned_units_from(self.to_cp.base.scramble_interceptors(1)))
|
||||||
|
|
||||||
|
self.operation = op
|
||||||
|
|
||||||
|
def player_defending(self, flights: db.TaskForceDict):
|
||||||
|
assert CAP in flights and len(flights) == 1, "Invalid flights"
|
||||||
|
|
||||||
|
op = FrontlineAttackOperation(game=self.game,
|
||||||
|
attacker_name=self.attacker_name,
|
||||||
|
defender_name=self.defender_name,
|
||||||
|
from_cp=self.from_cp,
|
||||||
|
departure_cp=self.departure_cp,
|
||||||
|
to_cp=self.to_cp)
|
||||||
|
|
||||||
|
defenders = self.to_cp.base.assemble_attack()
|
||||||
|
|
||||||
|
max_attackers = int(math.ceil(sum(defenders.values())))
|
||||||
|
attackers = db.unitdict_restrict_count(self.from_cp.base.assemble_attack(), max_attackers)
|
||||||
|
|
||||||
|
op.setup(defenders=defenders,
|
||||||
|
attackers=attackers,
|
||||||
|
strikegroup=assigned_units_from(self.from_cp.base.scramble_cas(1)),
|
||||||
|
escort=assigned_units_from(self.from_cp.base.scramble_sweep(1)),
|
||||||
|
interceptors=flights[CAP])
|
||||||
|
|
||||||
self.operation = op
|
self.operation = op
|
||||||
|
|
||||||
|
|||||||
@ -17,7 +17,7 @@ class FrontlinePatrolEvent(Event):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def tasks(self):
|
def tasks(self):
|
||||||
return [CAP, PinpointStrike]
|
return [CAP]
|
||||||
|
|
||||||
def flight_name(self, for_task: typing.Type[Task]) -> str:
|
def flight_name(self, for_task: typing.Type[Task]) -> str:
|
||||||
if for_task == CAP:
|
if for_task == CAP:
|
||||||
@ -55,7 +55,7 @@ class FrontlinePatrolEvent(Event):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
def player_attacking(self, flights: db.TaskForceDict):
|
def player_attacking(self, flights: db.TaskForceDict):
|
||||||
assert CAP in flights and PinpointStrike in flights and len(flights) == 2, "Invalid flights"
|
assert CAP in flights and len(flights) == 1, "Invalid flights"
|
||||||
|
|
||||||
self.cas = self.to_cp.base.scramble_cas(self.game.settings.multiplier)
|
self.cas = self.to_cp.base.scramble_cas(self.game.settings.multiplier)
|
||||||
self.escort = self.to_cp.base.scramble_sweep(self.game.settings.multiplier * self.ESCORT_FACTOR)
|
self.escort = self.to_cp.base.scramble_sweep(self.game.settings.multiplier * self.ESCORT_FACTOR)
|
||||||
@ -64,13 +64,15 @@ class FrontlinePatrolEvent(Event):
|
|||||||
attacker_name=self.attacker_name,
|
attacker_name=self.attacker_name,
|
||||||
defender_name=self.defender_name,
|
defender_name=self.defender_name,
|
||||||
from_cp=self.from_cp,
|
from_cp=self.from_cp,
|
||||||
|
departure_cp=self.departure_cp,
|
||||||
to_cp=self.to_cp)
|
to_cp=self.to_cp)
|
||||||
|
|
||||||
defenders = self.to_cp.base.assemble_attack()
|
defenders = self.to_cp.base.assemble_attack()
|
||||||
|
attackers = db.unitdict_restrict_count(self.from_cp.base.assemble_attack(), sum(defenders.values()))
|
||||||
op.setup(cas=assigned_units_from(self.cas),
|
op.setup(cas=assigned_units_from(self.cas),
|
||||||
escort=assigned_units_from(self.escort),
|
escort=assigned_units_from(self.escort),
|
||||||
interceptors=flights[CAP],
|
interceptors=flights[CAP],
|
||||||
armor_attackers=db.unitdict_restrict_count(db.unitdict_from(flights[PinpointStrike]), sum(defenders.values())),
|
armor_attackers=attackers,
|
||||||
armor_defenders=defenders)
|
armor_defenders=defenders)
|
||||||
|
|
||||||
self.operation = op
|
self.operation = op
|
||||||
|
|||||||
@ -35,7 +35,7 @@ class InfantryTransportEvent(Event):
|
|||||||
if self.is_successfull(debriefing):
|
if self.is_successfull(debriefing):
|
||||||
self.to_cp.base.affect_strength(-self.STRENGTH_INFLUENCE)
|
self.to_cp.base.affect_strength(-self.STRENGTH_INFLUENCE)
|
||||||
else:
|
else:
|
||||||
self.from_cp.base.affect_strength(-self.STRENGTH_INFLUENCE)
|
self.departure_cp.base.affect_strength(-self.STRENGTH_INFLUENCE)
|
||||||
|
|
||||||
def player_attacking(self, flights: db.TaskForceDict):
|
def player_attacking(self, flights: db.TaskForceDict):
|
||||||
assert Embarking in flights and len(flights) == 1, "Invalid flights"
|
assert Embarking in flights and len(flights) == 1, "Invalid flights"
|
||||||
@ -45,6 +45,7 @@ class InfantryTransportEvent(Event):
|
|||||||
attacker_name=self.attacker_name,
|
attacker_name=self.attacker_name,
|
||||||
defender_name=self.defender_name,
|
defender_name=self.defender_name,
|
||||||
from_cp=self.from_cp,
|
from_cp=self.from_cp,
|
||||||
|
departure_cp=self.departure_cp,
|
||||||
to_cp=self.to_cp
|
to_cp=self.to_cp
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@ -57,6 +57,7 @@ class InsurgentAttackEvent(Event):
|
|||||||
attacker_name=self.attacker_name,
|
attacker_name=self.attacker_name,
|
||||||
defender_name=self.defender_name,
|
defender_name=self.defender_name,
|
||||||
from_cp=self.from_cp,
|
from_cp=self.from_cp,
|
||||||
|
departure_cp=self.departure_cp,
|
||||||
to_cp=self.to_cp)
|
to_cp=self.to_cp)
|
||||||
op.setup(target=self.targets,
|
op.setup(target=self.targets,
|
||||||
strikegroup=flights[CAS])
|
strikegroup=flights[CAS])
|
||||||
|
|||||||
@ -10,6 +10,11 @@ class InterceptEvent(Event):
|
|||||||
|
|
||||||
transport_unit = None # type: FlyingType
|
transport_unit = None # type: FlyingType
|
||||||
|
|
||||||
|
def __init__(self, game, from_cp: ControlPoint, target_cp: ControlPoint, location: Point, attacker_name: str,
|
||||||
|
defender_name: str):
|
||||||
|
super().__init__(game, from_cp, target_cp, location, attacker_name, defender_name)
|
||||||
|
self.location = Conflict.intercept_position(self.from_cp, self.to_cp)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return "Air Intercept"
|
return "Air Intercept"
|
||||||
|
|
||||||
@ -25,15 +30,19 @@ class InterceptEvent(Event):
|
|||||||
return "Escort flight"
|
return "Escort flight"
|
||||||
|
|
||||||
def _enemy_scramble_multiplier(self) -> float:
|
def _enemy_scramble_multiplier(self) -> float:
|
||||||
is_global = self.from_cp.is_global or self.to_cp.is_global
|
is_global = self.departure_cp.is_global or self.to_cp.is_global
|
||||||
return self.game.settings.multiplier * is_global and 0.5 or 1
|
return self.game.settings.multiplier * is_global and 0.5 or 1
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def threat_description(self):
|
def threat_description(self):
|
||||||
return "{} aircraft".format(self.enemy_cp.base.scramble_count(self._enemy_scramble_multiplier(), CAP))
|
return "{} aircraft".format(self.enemy_cp.base.scramble_count(self._enemy_scramble_multiplier(), CAP))
|
||||||
|
|
||||||
|
@property
|
||||||
|
def global_cp_available(self) -> bool:
|
||||||
|
return True
|
||||||
|
|
||||||
def is_successfull(self, debriefing: Debriefing):
|
def is_successfull(self, debriefing: Debriefing):
|
||||||
units_destroyed = debriefing.destroyed_units[self.defender_name].get(self.transport_unit, 0)
|
units_destroyed = debriefing.destroyed_units.get(self.defender_name, {}).get(self.transport_unit, 0)
|
||||||
if self.from_cp.captured:
|
if self.from_cp.captured:
|
||||||
return units_destroyed > 0
|
return units_destroyed > 0
|
||||||
else:
|
else:
|
||||||
@ -72,9 +81,11 @@ class InterceptEvent(Event):
|
|||||||
attacker_name=self.attacker_name,
|
attacker_name=self.attacker_name,
|
||||||
defender_name=self.defender_name,
|
defender_name=self.defender_name,
|
||||||
from_cp=self.from_cp,
|
from_cp=self.from_cp,
|
||||||
|
departure_cp=self.departure_cp,
|
||||||
to_cp=self.to_cp)
|
to_cp=self.to_cp)
|
||||||
|
|
||||||
op.setup(escort=assigned_units_from(escort),
|
op.setup(location=self.location,
|
||||||
|
escort=assigned_units_from(escort),
|
||||||
transport={self.transport_unit: 1},
|
transport={self.transport_unit: 1},
|
||||||
airdefense={airdefense_unit: self.AIRDEFENSE_COUNT},
|
airdefense={airdefense_unit: self.AIRDEFENSE_COUNT},
|
||||||
interceptors=flights[CAP])
|
interceptors=flights[CAP])
|
||||||
@ -93,9 +104,11 @@ class InterceptEvent(Event):
|
|||||||
attacker_name=self.attacker_name,
|
attacker_name=self.attacker_name,
|
||||||
defender_name=self.defender_name,
|
defender_name=self.defender_name,
|
||||||
from_cp=self.from_cp,
|
from_cp=self.from_cp,
|
||||||
|
departure_cp=self.departure_cp,
|
||||||
to_cp=self.to_cp)
|
to_cp=self.to_cp)
|
||||||
|
|
||||||
op.setup(escort=flights[CAP],
|
op.setup(location=self.location,
|
||||||
|
escort=flights[CAP],
|
||||||
transport={self.transport_unit: 1},
|
transport={self.transport_unit: 1},
|
||||||
interceptors=assigned_units_from(interceptors),
|
interceptors=assigned_units_from(interceptors),
|
||||||
airdefense={})
|
airdefense={})
|
||||||
|
|||||||
@ -9,6 +9,11 @@ class NavalInterceptEvent(Event):
|
|||||||
|
|
||||||
targets = None # type: db.ShipDict
|
targets = None # type: db.ShipDict
|
||||||
|
|
||||||
|
def __init__(self, game, from_cp: ControlPoint, target_cp: ControlPoint, location: Point, attacker_name: str,
|
||||||
|
defender_name: str):
|
||||||
|
super().__init__(game, from_cp, target_cp, location, attacker_name, defender_name)
|
||||||
|
self.location = Conflict.naval_intercept_position(from_cp, target_cp, game.theater)
|
||||||
|
|
||||||
def _targets_count(self) -> int:
|
def _targets_count(self) -> int:
|
||||||
from gen.conflictgen import IMPORTANCE_LOW
|
from gen.conflictgen import IMPORTANCE_LOW
|
||||||
factor = (self.to_cp.importance - IMPORTANCE_LOW + 0.1) * 20
|
factor = (self.to_cp.importance - IMPORTANCE_LOW + 0.1) * 20
|
||||||
@ -33,18 +38,22 @@ class NavalInterceptEvent(Event):
|
|||||||
@property
|
@property
|
||||||
def threat_description(self):
|
def threat_description(self):
|
||||||
s = "{} ship(s)".format(self._targets_count())
|
s = "{} ship(s)".format(self._targets_count())
|
||||||
if not self.from_cp.captured:
|
if not self.departure_cp.captured:
|
||||||
s += ", {} aircraft".format(self.from_cp.base.scramble_count(self.game.settings.multiplier))
|
s += ", {} aircraft".format(self.departure_cp.base.scramble_count(self.game.settings.multiplier))
|
||||||
return s
|
return s
|
||||||
|
|
||||||
|
@property
|
||||||
|
def global_cp_available(self) -> bool:
|
||||||
|
return True
|
||||||
|
|
||||||
def is_successfull(self, debriefing: Debriefing):
|
def is_successfull(self, debriefing: Debriefing):
|
||||||
total_targets = sum(self.targets.values())
|
total_targets = sum(self.targets.values())
|
||||||
destroyed_targets = 0
|
destroyed_targets = 0
|
||||||
for unit, count in debriefing.destroyed_units[self.defender_name].items():
|
for unit, count in debriefing.destroyed_units.get(self.defender_name, {}).items():
|
||||||
if unit in self.targets:
|
if unit in self.targets:
|
||||||
destroyed_targets += count
|
destroyed_targets += count
|
||||||
|
|
||||||
if self.from_cp.captured:
|
if self.departure_cp.captured:
|
||||||
return math.ceil(float(destroyed_targets) / total_targets) > self.SUCCESS_RATE
|
return math.ceil(float(destroyed_targets) / total_targets) > self.SUCCESS_RATE
|
||||||
else:
|
else:
|
||||||
return math.ceil(float(destroyed_targets) / total_targets) < self.SUCCESS_RATE
|
return math.ceil(float(destroyed_targets) / total_targets) < self.SUCCESS_RATE
|
||||||
@ -56,11 +65,11 @@ class NavalInterceptEvent(Event):
|
|||||||
if self.is_successfull(debriefing):
|
if self.is_successfull(debriefing):
|
||||||
self.to_cp.base.affect_strength(-self.STRENGTH_INFLUENCE)
|
self.to_cp.base.affect_strength(-self.STRENGTH_INFLUENCE)
|
||||||
else:
|
else:
|
||||||
self.from_cp.base.affect_strength(-self.STRENGTH_INFLUENCE)
|
self.departure_cp.base.affect_strength(-self.STRENGTH_INFLUENCE)
|
||||||
else:
|
else:
|
||||||
# enemy attacking
|
# enemy attacking
|
||||||
if self.is_successfull(debriefing):
|
if self.is_successfull(debriefing):
|
||||||
self.from_cp.base.affect_strength(-self.STRENGTH_INFLUENCE)
|
self.departure_cp.base.affect_strength(-self.STRENGTH_INFLUENCE)
|
||||||
else:
|
else:
|
||||||
self.to_cp.base.affect_strength(-self.STRENGTH_INFLUENCE)
|
self.to_cp.base.affect_strength(-self.STRENGTH_INFLUENCE)
|
||||||
|
|
||||||
@ -80,10 +89,12 @@ class NavalInterceptEvent(Event):
|
|||||||
attacker_name=self.attacker_name,
|
attacker_name=self.attacker_name,
|
||||||
defender_name=self.defender_name,
|
defender_name=self.defender_name,
|
||||||
from_cp=self.from_cp,
|
from_cp=self.from_cp,
|
||||||
|
departure_cp=self.departure_cp,
|
||||||
to_cp=self.to_cp
|
to_cp=self.to_cp
|
||||||
)
|
)
|
||||||
|
|
||||||
op.setup(strikegroup=flights[CAS],
|
op.setup(location=self.location,
|
||||||
|
strikegroup=flights[CAS],
|
||||||
interceptors={},
|
interceptors={},
|
||||||
targets=self.targets)
|
targets=self.targets)
|
||||||
|
|
||||||
@ -101,10 +112,11 @@ class NavalInterceptEvent(Event):
|
|||||||
attacker_name=self.attacker_name,
|
attacker_name=self.attacker_name,
|
||||||
defender_name=self.defender_name,
|
defender_name=self.defender_name,
|
||||||
from_cp=self.from_cp,
|
from_cp=self.from_cp,
|
||||||
|
departure_cp=self.departure_cp,
|
||||||
to_cp=self.to_cp
|
to_cp=self.to_cp
|
||||||
)
|
)
|
||||||
|
|
||||||
strikegroup = self.from_cp.base.scramble_cas(self.game.settings.multiplier)
|
strikegroup = self.departure_cp.base.scramble_cas(self.game.settings.multiplier)
|
||||||
op.setup(strikegroup=assigned_units_from(strikegroup),
|
op.setup(strikegroup=assigned_units_from(strikegroup),
|
||||||
interceptors=flights[CAP],
|
interceptors=flights[CAP],
|
||||||
targets=self.targets)
|
targets=self.targets)
|
||||||
|
|||||||
@ -8,7 +8,7 @@ class StrikeEvent(Event):
|
|||||||
SINGLE_OBJECT_STRENGTH_INFLUENCE = 0.05
|
SINGLE_OBJECT_STRENGTH_INFLUENCE = 0.05
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return "Strike"
|
return "Strike / SEAD"
|
||||||
|
|
||||||
def is_successfull(self, debriefing: Debriefing):
|
def is_successfull(self, debriefing: Debriefing):
|
||||||
return True
|
return True
|
||||||
@ -20,7 +20,7 @@ class StrikeEvent(Event):
|
|||||||
@property
|
@property
|
||||||
def tasks(self):
|
def tasks(self):
|
||||||
if self.is_player_attacking:
|
if self.is_player_attacking:
|
||||||
return [CAP, CAS]
|
return [CAP, CAS, SEAD]
|
||||||
else:
|
else:
|
||||||
return [CAP]
|
return [CAP]
|
||||||
|
|
||||||
@ -28,12 +28,22 @@ class StrikeEvent(Event):
|
|||||||
def ai_banned_tasks(self):
|
def ai_banned_tasks(self):
|
||||||
return [CAS]
|
return [CAS]
|
||||||
|
|
||||||
|
@property
|
||||||
|
def player_banned_tasks(self):
|
||||||
|
return [SEAD]
|
||||||
|
|
||||||
|
@property
|
||||||
|
def global_cp_available(self) -> bool:
|
||||||
|
return True
|
||||||
|
|
||||||
def flight_name(self, for_task: typing.Type[Task]) -> str:
|
def flight_name(self, for_task: typing.Type[Task]) -> str:
|
||||||
if for_task == CAP:
|
if for_task == CAP:
|
||||||
if self.is_player_attacking:
|
if self.is_player_attacking:
|
||||||
return "Escort flight"
|
return "Escort flight"
|
||||||
else:
|
else:
|
||||||
return "CAP flight"
|
return "CAP flight"
|
||||||
|
elif for_task == SEAD:
|
||||||
|
return "SEAD flight"
|
||||||
elif for_task == CAS:
|
elif for_task == CAS:
|
||||||
return "Strike flight"
|
return "Strike flight"
|
||||||
|
|
||||||
@ -43,18 +53,20 @@ class StrikeEvent(Event):
|
|||||||
self.to_cp.base.affect_strength(-self.SINGLE_OBJECT_STRENGTH_INFLUENCE * len(debriefing.destroyed_objects))
|
self.to_cp.base.affect_strength(-self.SINGLE_OBJECT_STRENGTH_INFLUENCE * len(debriefing.destroyed_objects))
|
||||||
|
|
||||||
def player_attacking(self, flights: db.TaskForceDict):
|
def player_attacking(self, flights: db.TaskForceDict):
|
||||||
assert CAP in flights and CAS in flights and len(flights) == 2, "Invalid flights"
|
assert CAP in flights and CAS in flights and SEAD in flights and len(flights) == 3, "Invalid flights"
|
||||||
|
|
||||||
op = StrikeOperation(
|
op = StrikeOperation(
|
||||||
self.game,
|
self.game,
|
||||||
attacker_name=self.attacker_name,
|
attacker_name=self.attacker_name,
|
||||||
defender_name=self.defender_name,
|
defender_name=self.defender_name,
|
||||||
from_cp=self.from_cp,
|
from_cp=self.from_cp,
|
||||||
|
departure_cp=self.departure_cp,
|
||||||
to_cp=self.to_cp
|
to_cp=self.to_cp
|
||||||
)
|
)
|
||||||
|
|
||||||
interceptors = self.to_cp.base.scramble_interceptors(self.game.settings.multiplier)
|
interceptors = self.to_cp.base.scramble_interceptors(self.game.settings.multiplier)
|
||||||
op.setup(strikegroup=flights[CAS],
|
op.setup(strikegroup=flights[CAS],
|
||||||
|
sead=flights[SEAD],
|
||||||
escort=flights[CAP],
|
escort=flights[CAP],
|
||||||
interceptors=assigned_units_from(interceptors))
|
interceptors=assigned_units_from(interceptors))
|
||||||
|
|
||||||
|
|||||||
68
game/game.py
@ -43,19 +43,19 @@ Events:
|
|||||||
* BaseAttackEvent - capture base
|
* BaseAttackEvent - capture base
|
||||||
* InterceptEvent - air intercept
|
* InterceptEvent - air intercept
|
||||||
* FrontlineAttackEvent - frontline attack
|
* FrontlineAttackEvent - frontline attack
|
||||||
* FrontlineCAPEvent - frontline attack
|
|
||||||
* NavalInterceptEvent - naval intercept
|
* NavalInterceptEvent - naval intercept
|
||||||
* StrikeEvent - strike event
|
* StrikeEvent - strike event
|
||||||
* InfantryTransportEvent - helicopter infantry transport
|
* InfantryTransportEvent - helicopter infantry transport
|
||||||
"""
|
"""
|
||||||
EVENT_PROBABILITIES = {
|
EVENT_PROBABILITIES = {
|
||||||
# events always present; only for the player
|
# events always present; only for the player
|
||||||
FrontlineAttackEvent: [100, 0],
|
FrontlineAttackEvent: [100, 9],
|
||||||
FrontlinePatrolEvent: [100, 0],
|
#FrontlinePatrolEvent: [100, 0],
|
||||||
StrikeEvent: [100, 0],
|
StrikeEvent: [100, 0],
|
||||||
|
|
||||||
# events randomly present; only for the player
|
# events randomly present; only for the player
|
||||||
InfantryTransportEvent: [25, 0],
|
#InfantryTransportEvent: [25, 0],
|
||||||
|
ConvoyStrikeEvent: [25, 0],
|
||||||
|
|
||||||
# events conditionally present; for both enemy and player
|
# events conditionally present; for both enemy and player
|
||||||
BaseAttackEvent: [100, 9],
|
BaseAttackEvent: [100, 9],
|
||||||
@ -100,28 +100,18 @@ class Game:
|
|||||||
self.enemy = enemy_name
|
self.enemy = enemy_name
|
||||||
|
|
||||||
def _roll(self, prob, mult):
|
def _roll(self, prob, mult):
|
||||||
return random.randint(1, 100) <= prob * mult
|
if self.settings.version == "dev":
|
||||||
|
# always generate all events for dev
|
||||||
def _generate_globalinterceptions(self):
|
return 100
|
||||||
global_count = len([x for x in self.theater.player_points() if x.is_global])
|
else:
|
||||||
for from_cp in [x for x in self.theater.player_points() if x.is_global]:
|
return random.randint(1, 100) <= prob * mult
|
||||||
probability_base = max(PLAYER_INTERCEPT_GLOBAL_PROBABILITY_BASE / global_count, 1)
|
|
||||||
probability = probability_base * math.log(len(self.theater.player_points()) + 1, PLAYER_INTERCEPT_GLOBAL_PROBABILITY_LOG)
|
|
||||||
if self._roll(probability, from_cp.base.strength):
|
|
||||||
to_cp = random.choice([x for x in self.theater.enemy_points() if x not in self.theater.conflicts()])
|
|
||||||
self.events.append(InterceptEvent(attacker_name=self.player,
|
|
||||||
defender_name=self.enemy,
|
|
||||||
from_cp=from_cp,
|
|
||||||
to_cp=to_cp,
|
|
||||||
game=self))
|
|
||||||
break
|
|
||||||
|
|
||||||
def _generate_player_event(self, event_class, player_cp, enemy_cp):
|
def _generate_player_event(self, event_class, player_cp, enemy_cp):
|
||||||
if event_class == NavalInterceptEvent and enemy_cp.radials == LAND:
|
if event_class == NavalInterceptEvent and enemy_cp.radials == LAND:
|
||||||
# skip naval events for non-coastal CPs
|
# skip naval events for non-coastal CPs
|
||||||
return
|
return
|
||||||
|
|
||||||
if event_class == BaseAttackEvent and enemy_cp.base.strength > PLAYER_BASEATTACK_THRESHOLD:
|
if event_class == BaseAttackEvent and enemy_cp.base.strength > PLAYER_BASEATTACK_THRESHOLD and self.settings.version != "dev":
|
||||||
# skip base attack events for CPs yet too strong
|
# skip base attack events for CPs yet too strong
|
||||||
return
|
return
|
||||||
|
|
||||||
@ -129,7 +119,7 @@ class Game:
|
|||||||
# skip strikes in case of no targets
|
# skip strikes in case of no targets
|
||||||
return
|
return
|
||||||
|
|
||||||
self.events.append(event_class(self.player, self.enemy, player_cp, enemy_cp, self))
|
self.events.append(event_class(self, player_cp, enemy_cp, enemy_cp.position, self.player, self.enemy))
|
||||||
|
|
||||||
def _generate_enemy_event(self, event_class, player_cp, enemy_cp):
|
def _generate_enemy_event(self, event_class, player_cp, enemy_cp):
|
||||||
if event_class in [type(x) for x in self.events if not self.is_player_attack(x)]:
|
if event_class in [type(x) for x in self.events if not self.is_player_attack(x)]:
|
||||||
@ -169,28 +159,35 @@ class Game:
|
|||||||
# skip base attack if strength is too high
|
# skip base attack if strength is too high
|
||||||
return
|
return
|
||||||
|
|
||||||
self.events.append(event_class(self.enemy, self.player, enemy_cp, player_cp, self))
|
self.events.append(event_class(self, enemy_cp, player_cp, player_cp.position, self.enemy, self.player))
|
||||||
|
|
||||||
def _generate_events(self):
|
def _generate_events(self):
|
||||||
for player_cp, enemy_cp in self.theater.conflicts(True):
|
strikes_generated_for = set()
|
||||||
if enemy_cp.is_global:
|
base_attack_generated_for = set()
|
||||||
continue
|
|
||||||
|
|
||||||
|
for player_cp, enemy_cp in self.theater.conflicts(True):
|
||||||
for event_class, (player_probability, enemy_probability) in EVENT_PROBABILITIES.items():
|
for event_class, (player_probability, enemy_probability) in EVENT_PROBABILITIES.items():
|
||||||
if event_class in [FrontlineAttackEvent, FrontlinePatrolEvent, InfantryTransportEvent]:
|
if event_class in [FrontlineAttackEvent, FrontlinePatrolEvent, InfantryTransportEvent, ConvoyStrikeEvent]:
|
||||||
# skip events requiring frontline
|
# skip events requiring frontline
|
||||||
if not Conflict.has_frontline_between(player_cp, enemy_cp):
|
if not Conflict.has_frontline_between(player_cp, enemy_cp):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if player_cp.is_global:
|
# don't generate multiple 100% events from each attack direction
|
||||||
# skip events requiring ground CP
|
if event_class is StrikeEvent:
|
||||||
if event_class not in [InterceptEvent, StrikeEvent, NavalInterceptEvent]:
|
if enemy_cp in strikes_generated_for:
|
||||||
|
continue
|
||||||
|
if event_class is BaseAttackEvent:
|
||||||
|
if enemy_cp in base_attack_generated_for:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if player_probability == 100 or self._roll(player_probability, player_cp.base.strength):
|
if player_probability == 100 or player_probability > 0 and self._roll(player_probability, player_cp.base.strength):
|
||||||
self._generate_player_event(event_class, player_cp, enemy_cp)
|
self._generate_player_event(event_class, player_cp, enemy_cp)
|
||||||
|
if event_class is StrikeEvent:
|
||||||
|
strikes_generated_for.add(enemy_cp)
|
||||||
|
if event_class is BaseAttackEvent:
|
||||||
|
base_attack_generated_for.add(enemy_cp)
|
||||||
|
|
||||||
if enemy_probability == 100 or self._roll(enemy_probability, enemy_cp.base.strength):
|
if enemy_probability == 100 or enemy_probability > 0 and self._roll(enemy_probability, enemy_cp.base.strength):
|
||||||
self._generate_enemy_event(event_class, player_cp, enemy_cp)
|
self._generate_enemy_event(event_class, player_cp, enemy_cp)
|
||||||
|
|
||||||
def commision_unit_types(self, cp: ControlPoint, for_task: Task) -> typing.Collection[UnitType]:
|
def commision_unit_types(self, cp: ControlPoint, for_task: Task) -> typing.Collection[UnitType]:
|
||||||
@ -269,7 +266,12 @@ class Game:
|
|||||||
def pass_turn(self, no_action=False, ignored_cps: typing.Collection[ControlPoint]=None):
|
def pass_turn(self, no_action=False, ignored_cps: typing.Collection[ControlPoint]=None):
|
||||||
logging.info("Pass turn")
|
logging.info("Pass turn")
|
||||||
for event in self.events:
|
for event in self.events:
|
||||||
event.skip()
|
if self.settings.version == "dev":
|
||||||
|
# don't damage player CPs in by skipping in dev mode
|
||||||
|
if isinstance(event, UnitsDeliveryEvent):
|
||||||
|
event.skip()
|
||||||
|
else:
|
||||||
|
event.skip()
|
||||||
|
|
||||||
if not no_action:
|
if not no_action:
|
||||||
self._budget_player()
|
self._budget_player()
|
||||||
@ -286,5 +288,5 @@ class Game:
|
|||||||
|
|
||||||
self.events = [] # type: typing.List[Event]
|
self.events = [] # type: typing.List[Event]
|
||||||
self._generate_events()
|
self._generate_events()
|
||||||
self._generate_globalinterceptions()
|
#self._generate_globalinterceptions()
|
||||||
|
|
||||||
|
|||||||
49
game/operation/convoystrike.py
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
from game.db import assigned_units_split
|
||||||
|
|
||||||
|
from .operation import *
|
||||||
|
|
||||||
|
|
||||||
|
class ConvoyStrikeOperation(Operation):
|
||||||
|
strikegroup = None # type: db.AssignedUnitsDict
|
||||||
|
target = None # type: db.ArmorDict
|
||||||
|
|
||||||
|
def setup(self,
|
||||||
|
target: db.ArmorDict,
|
||||||
|
strikegroup: db.AssignedUnitsDict):
|
||||||
|
self.strikegroup = strikegroup
|
||||||
|
self.target = target
|
||||||
|
|
||||||
|
def prepare(self, terrain: Terrain, is_quick: bool):
|
||||||
|
super(ConvoyStrikeOperation, self).prepare(terrain, is_quick)
|
||||||
|
|
||||||
|
conflict = Conflict.convoy_strike_conflict(
|
||||||
|
attacker=self.current_mission.country(self.attacker_name),
|
||||||
|
defender=self.current_mission.country(self.defender_name),
|
||||||
|
from_cp=self.from_cp,
|
||||||
|
to_cp=self.to_cp,
|
||||||
|
theater=self.game.theater
|
||||||
|
)
|
||||||
|
|
||||||
|
self.initialize(mission=self.current_mission,
|
||||||
|
conflict=conflict)
|
||||||
|
|
||||||
|
def generate(self):
|
||||||
|
if self.is_player_attack:
|
||||||
|
self.prepare_carriers(db.unitdict_from(self.strikegroup))
|
||||||
|
|
||||||
|
planes_flights = {k: v for k, v in self.strikegroup.items() if k in plane_map.values()}
|
||||||
|
self.airgen.generate_cas_strikegroup(*assigned_units_split(planes_flights), at=self.attackers_starting_position)
|
||||||
|
|
||||||
|
heli_flights = {k: v for k, v in self.strikegroup.items() if k in helicopters.helicopter_map.values()}
|
||||||
|
if heli_flights:
|
||||||
|
self.briefinggen.append_frequency("FARP + Heli flights", "127.5 MHz AM")
|
||||||
|
for farp, dict in zip(self.groundobjectgen.generate_farps(sum([x[0] for x in heli_flights.values()])),
|
||||||
|
db.assignedunits_split_to_count(heli_flights, self.groundobjectgen.FARP_CAPACITY)):
|
||||||
|
self.airgen.generate_cas_strikegroup(*assigned_units_split(dict),
|
||||||
|
at=farp,
|
||||||
|
escort=len(planes_flights) == 0)
|
||||||
|
|
||||||
|
self.armorgen.generate_convoy(self.target)
|
||||||
|
|
||||||
|
self.briefinggen.append_waypoint("TARGET")
|
||||||
|
super(ConvoyStrikeOperation, self).generate()
|
||||||
@ -7,16 +7,24 @@ MAX_DISTANCE_BETWEEN_GROUPS = 12000
|
|||||||
|
|
||||||
|
|
||||||
class FrontlineAttackOperation(Operation):
|
class FrontlineAttackOperation(Operation):
|
||||||
|
interceptors = None # type: db.AssignedUnitsDict
|
||||||
|
escort = None # type: db.AssignedUnitsDict
|
||||||
strikegroup = None # type: db.AssignedUnitsDict
|
strikegroup = None # type: db.AssignedUnitsDict
|
||||||
|
|
||||||
attackers = None # type: db.ArmorDict
|
attackers = None # type: db.ArmorDict
|
||||||
target = None # type: db.ArmorDict
|
defenders = None # type: db.ArmorDict
|
||||||
|
|
||||||
def setup(self,
|
def setup(self,
|
||||||
target: db.ArmorDict,
|
defenders: db.ArmorDict,
|
||||||
attackers: db.ArmorDict,
|
attackers: db.ArmorDict,
|
||||||
strikegroup: db.AssignedUnitsDict):
|
strikegroup: db.AssignedUnitsDict,
|
||||||
|
escort: db.AssignedUnitsDict,
|
||||||
|
interceptors: db.AssignedUnitsDict):
|
||||||
self.strikegroup = strikegroup
|
self.strikegroup = strikegroup
|
||||||
self.target = target
|
self.escort = escort
|
||||||
|
self.interceptors = interceptors
|
||||||
|
|
||||||
|
self.defenders = defenders
|
||||||
self.attackers = attackers
|
self.attackers = attackers
|
||||||
|
|
||||||
def prepare(self, terrain: Terrain, is_quick: bool):
|
def prepare(self, terrain: Terrain, is_quick: bool):
|
||||||
@ -37,8 +45,13 @@ class FrontlineAttackOperation(Operation):
|
|||||||
conflict=conflict)
|
conflict=conflict)
|
||||||
|
|
||||||
def generate(self):
|
def generate(self):
|
||||||
self.armorgen.generate_vec(self.attackers, self.target)
|
if self.is_player_attack:
|
||||||
|
self.prepare_carriers(db.unitdict_from(self.strikegroup))
|
||||||
|
|
||||||
|
# ground units
|
||||||
|
self.armorgen.generate_vec(self.attackers, self.defenders)
|
||||||
|
|
||||||
|
# strike group w/ heli support
|
||||||
planes_flights = {k: v for k, v in self.strikegroup.items() if k in plane_map.values()}
|
planes_flights = {k: v for k, v in self.strikegroup.items() if k in plane_map.values()}
|
||||||
self.airgen.generate_cas_strikegroup(*assigned_units_split(planes_flights), at=self.attackers_starting_position)
|
self.airgen.generate_cas_strikegroup(*assigned_units_split(planes_flights), at=self.attackers_starting_position)
|
||||||
|
|
||||||
@ -51,6 +64,10 @@ class FrontlineAttackOperation(Operation):
|
|||||||
at=farp,
|
at=farp,
|
||||||
escort=len(planes_flights) == 0)
|
escort=len(planes_flights) == 0)
|
||||||
|
|
||||||
|
self.airgen.generate_attackers_escort(*assigned_units_split(self.escort), at=self.attackers_starting_position)
|
||||||
|
|
||||||
|
self.airgen.generate_defense(*assigned_units_split(self.interceptors), at=self.defenders_starting_position)
|
||||||
|
|
||||||
self.briefinggen.title = "Frontline CAS"
|
self.briefinggen.title = "Frontline CAS"
|
||||||
self.briefinggen.description = "Provide CAS for the ground forces attacking enemy lines. Operation will be considered successful if total number of enemy units will be lower than your own by a factor of 1.5 (i.e. with 12 units from both sides, enemy forces need to be reduced to at least 8), meaning that you (and, probably, your wingmans) should concentrate on destroying the enemy units. Target base strength will be lowered as a result. Be advised that your flight will not attack anything until you explicitly tell them so by comms menu."
|
self.briefinggen.description = "Provide CAS for the ground forces attacking enemy lines. Operation will be considered successful if total number of enemy units will be lower than your own by a factor of 1.5 (i.e. with 12 units from both sides, enemy forces need to be reduced to at least 8), meaning that you (and, probably, your wingmans) should concentrate on destroying the enemy units. Target base strength will be lowered as a result. Be advised that your flight will not attack anything until you explicitly tell them so by comms menu."
|
||||||
self.briefinggen.append_waypoint("CAS AREA IP")
|
self.briefinggen.append_waypoint("CAS AREA IP")
|
||||||
|
|||||||
@ -43,6 +43,9 @@ class FrontlinePatrolOperation(Operation):
|
|||||||
conflict=conflict)
|
conflict=conflict)
|
||||||
|
|
||||||
def generate(self):
|
def generate(self):
|
||||||
|
if self.is_player_attack:
|
||||||
|
self.prepare_carriers(db.unitdict_from(self.interceptors))
|
||||||
|
|
||||||
self.airgen.generate_defenders_cas(*assigned_units_split(self.cas), at=self.defenders_starting_position)
|
self.airgen.generate_defenders_cas(*assigned_units_split(self.cas), at=self.defenders_starting_position)
|
||||||
self.airgen.generate_defenders_escort(*assigned_units_split(self.escort), at=self.defenders_starting_position)
|
self.airgen.generate_defenders_escort(*assigned_units_split(self.escort), at=self.defenders_starting_position)
|
||||||
self.airgen.generate_migcap(*assigned_units_split(self.interceptors), at=self.attackers_starting_position)
|
self.airgen.generate_migcap(*assigned_units_split(self.interceptors), at=self.attackers_starting_position)
|
||||||
|
|||||||
@ -4,6 +4,7 @@ from .operation import *
|
|||||||
|
|
||||||
|
|
||||||
class InterceptOperation(Operation):
|
class InterceptOperation(Operation):
|
||||||
|
location = None # type: Point
|
||||||
escort = None # type: db.AssignedUnitsDict
|
escort = None # type: db.AssignedUnitsDict
|
||||||
transport = None # type: db.PlaneDict
|
transport = None # type: db.PlaneDict
|
||||||
interceptors = None # type: db.AssignedUnitsDict
|
interceptors = None # type: db.AssignedUnitsDict
|
||||||
@ -12,10 +13,12 @@ class InterceptOperation(Operation):
|
|||||||
trigger_radius = TRIGGER_RADIUS_LARGE
|
trigger_radius = TRIGGER_RADIUS_LARGE
|
||||||
|
|
||||||
def setup(self,
|
def setup(self,
|
||||||
|
location: Point,
|
||||||
escort: db.AssignedUnitsDict,
|
escort: db.AssignedUnitsDict,
|
||||||
transport: db.PlaneDict,
|
transport: db.PlaneDict,
|
||||||
airdefense: db.AirDefenseDict,
|
airdefense: db.AirDefenseDict,
|
||||||
interceptors: db.AssignedUnitsDict):
|
interceptors: db.AssignedUnitsDict):
|
||||||
|
self.location = location
|
||||||
self.escort = escort
|
self.escort = escort
|
||||||
self.transport = transport
|
self.transport = transport
|
||||||
self.airdefense = airdefense
|
self.airdefense = airdefense
|
||||||
@ -30,6 +33,7 @@ class InterceptOperation(Operation):
|
|||||||
conflict = Conflict.intercept_conflict(
|
conflict = Conflict.intercept_conflict(
|
||||||
attacker=self.current_mission.country(self.attacker_name),
|
attacker=self.current_mission.country(self.attacker_name),
|
||||||
defender=self.current_mission.country(self.defender_name),
|
defender=self.current_mission.country(self.defender_name),
|
||||||
|
position=self.location,
|
||||||
from_cp=self.from_cp,
|
from_cp=self.from_cp,
|
||||||
to_cp=self.to_cp,
|
to_cp=self.to_cp,
|
||||||
theater=self.game.theater
|
theater=self.game.theater
|
||||||
@ -39,7 +43,8 @@ class InterceptOperation(Operation):
|
|||||||
conflict=conflict)
|
conflict=conflict)
|
||||||
|
|
||||||
def generate(self):
|
def generate(self):
|
||||||
self.prepare_carriers(db.unitdict_from(self.interceptors))
|
if self.is_player_attack:
|
||||||
|
self.prepare_carriers(db.unitdict_from(self.interceptors))
|
||||||
|
|
||||||
self.airgen.generate_transport(self.transport, self.to_cp.at)
|
self.airgen.generate_transport(self.transport, self.to_cp.at)
|
||||||
self.airgen.generate_defenders_escort(*assigned_units_split(self.escort), at=self.defenders_starting_position)
|
self.airgen.generate_defenders_escort(*assigned_units_split(self.escort), at=self.defenders_starting_position)
|
||||||
|
|||||||
@ -4,15 +4,18 @@ from .operation import *
|
|||||||
|
|
||||||
|
|
||||||
class NavalInterceptionOperation(Operation):
|
class NavalInterceptionOperation(Operation):
|
||||||
|
location = None # type: Point
|
||||||
strikegroup = None # type: db.AssignedUnitsDict
|
strikegroup = None # type: db.AssignedUnitsDict
|
||||||
interceptors = None # type: db.AssignedUnitsDict
|
interceptors = None # type: db.AssignedUnitsDict
|
||||||
targets = None # type: db.ShipDict
|
targets = None # type: db.ShipDict
|
||||||
trigger_radius = TRIGGER_RADIUS_LARGE
|
trigger_radius = TRIGGER_RADIUS_LARGE
|
||||||
|
|
||||||
def setup(self,
|
def setup(self,
|
||||||
|
location: Point,
|
||||||
strikegroup: db.AssignedUnitsDict,
|
strikegroup: db.AssignedUnitsDict,
|
||||||
interceptors: db.AssignedUnitsDict,
|
interceptors: db.AssignedUnitsDict,
|
||||||
targets: db.ShipDict):
|
targets: db.ShipDict):
|
||||||
|
self.location = location
|
||||||
self.strikegroup = strikegroup
|
self.strikegroup = strikegroup
|
||||||
self.interceptors = interceptors
|
self.interceptors = interceptors
|
||||||
self.targets = targets
|
self.targets = targets
|
||||||
@ -25,6 +28,7 @@ class NavalInterceptionOperation(Operation):
|
|||||||
conflict = Conflict.naval_intercept_conflict(
|
conflict = Conflict.naval_intercept_conflict(
|
||||||
attacker=self.current_mission.country(self.attacker_name),
|
attacker=self.current_mission.country(self.attacker_name),
|
||||||
defender=self.current_mission.country(self.defender_name),
|
defender=self.current_mission.country(self.defender_name),
|
||||||
|
position=self.location,
|
||||||
from_cp=self.from_cp,
|
from_cp=self.from_cp,
|
||||||
to_cp=self.to_cp,
|
to_cp=self.to_cp,
|
||||||
theater=self.game.theater
|
theater=self.game.theater
|
||||||
@ -33,7 +37,8 @@ class NavalInterceptionOperation(Operation):
|
|||||||
self.initialize(self.current_mission, conflict)
|
self.initialize(self.current_mission, conflict)
|
||||||
|
|
||||||
def generate(self):
|
def generate(self):
|
||||||
self.prepare_carriers(db.unitdict_from(self.strikegroup))
|
if self.is_player_attack:
|
||||||
|
self.prepare_carriers(db.unitdict_from(self.strikegroup))
|
||||||
|
|
||||||
target_groups = self.shipgen.generate_cargo(units=self.targets)
|
target_groups = self.shipgen.generate_cargo(units=self.targets)
|
||||||
|
|
||||||
|
|||||||
@ -26,6 +26,7 @@ class Operation:
|
|||||||
envgen = None # type: EnvironmentGenerator
|
envgen = None # type: EnvironmentGenerator
|
||||||
groundobjectgen = None # type: GroundObjectsGenerator
|
groundobjectgen = None # type: GroundObjectsGenerator
|
||||||
briefinggen = None # type: BriefingGenerator
|
briefinggen = None # type: BriefingGenerator
|
||||||
|
forcedoptionsgen = None # type: ForcedOptionsGenerator
|
||||||
|
|
||||||
environment_settings = None
|
environment_settings = None
|
||||||
trigger_radius = TRIGGER_RADIUS_MEDIUM
|
trigger_radius = TRIGGER_RADIUS_MEDIUM
|
||||||
@ -38,11 +39,13 @@ class Operation:
|
|||||||
attacker_name: str,
|
attacker_name: str,
|
||||||
defender_name: str,
|
defender_name: str,
|
||||||
from_cp: ControlPoint,
|
from_cp: ControlPoint,
|
||||||
|
departure_cp: ControlPoint,
|
||||||
to_cp: ControlPoint = None):
|
to_cp: ControlPoint = None):
|
||||||
self.game = game
|
self.game = game
|
||||||
self.attacker_name = attacker_name
|
self.attacker_name = attacker_name
|
||||||
self.defender_name = defender_name
|
self.defender_name = defender_name
|
||||||
self.from_cp = from_cp
|
self.from_cp = from_cp
|
||||||
|
self.departure_cp = departure_cp
|
||||||
self.to_cp = to_cp
|
self.to_cp = to_cp
|
||||||
self.is_quick = False
|
self.is_quick = False
|
||||||
|
|
||||||
@ -52,6 +55,10 @@ class Operation:
|
|||||||
def is_successfull(self, debriefing: Debriefing) -> bool:
|
def is_successfull(self, debriefing: Debriefing) -> bool:
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
@property
|
||||||
|
def is_player_attack(self) -> bool:
|
||||||
|
return self.from_cp.captured
|
||||||
|
|
||||||
def initialize(self, mission: Mission, conflict: Conflict):
|
def initialize(self, mission: Mission, conflict: Conflict):
|
||||||
self.current_mission = mission
|
self.current_mission = mission
|
||||||
self.conflict = conflict
|
self.conflict = conflict
|
||||||
@ -63,6 +70,7 @@ class Operation:
|
|||||||
self.triggersgen = TriggersGenerator(mission, conflict, self.game)
|
self.triggersgen = TriggersGenerator(mission, conflict, self.game)
|
||||||
self.visualgen = VisualGenerator(mission, conflict, self.game)
|
self.visualgen = VisualGenerator(mission, conflict, self.game)
|
||||||
self.envgen = EnviromentGenerator(mission, conflict, self.game)
|
self.envgen = EnviromentGenerator(mission, conflict, self.game)
|
||||||
|
self.forcedoptionsgen = ForcedOptionsGenerator(mission, conflict, self.game)
|
||||||
self.groundobjectgen = GroundObjectsGenerator(mission, conflict, self.game)
|
self.groundobjectgen = GroundObjectsGenerator(mission, conflict, self.game)
|
||||||
self.briefinggen = BriefingGenerator(mission, conflict, self.game)
|
self.briefinggen = BriefingGenerator(mission, conflict, self.game)
|
||||||
|
|
||||||
@ -87,24 +95,24 @@ class Operation:
|
|||||||
self.attackers_starting_position = None
|
self.attackers_starting_position = None
|
||||||
self.defenders_starting_position = None
|
self.defenders_starting_position = None
|
||||||
else:
|
else:
|
||||||
self.attackers_starting_position = self.from_cp.at
|
self.attackers_starting_position = self.departure_cp.at
|
||||||
self.defenders_starting_position = self.to_cp.at
|
self.defenders_starting_position = self.to_cp.at
|
||||||
|
|
||||||
def prepare_carriers(self, for_units: db.UnitsDict):
|
def prepare_carriers(self, for_units: db.UnitsDict):
|
||||||
for global_cp in self.game.theater.controlpoints:
|
if not self.departure_cp.is_global:
|
||||||
if not global_cp.is_global:
|
return
|
||||||
continue
|
|
||||||
|
|
||||||
ship = self.shipgen.generate_carrier(for_units=[t for t, c in for_units.items() if c > 0],
|
ship = self.shipgen.generate_carrier(for_units=[t for t, c in for_units.items() if c > 0],
|
||||||
country=self.game.player,
|
country=self.game.player,
|
||||||
at=global_cp.at)
|
at=self.departure_cp.at)
|
||||||
|
|
||||||
if global_cp == self.from_cp and not self.is_quick:
|
if not self.is_quick:
|
||||||
|
if not self.to_cp.captured:
|
||||||
self.attackers_starting_position = ship
|
self.attackers_starting_position = ship
|
||||||
|
else:
|
||||||
|
self.defenders_starting_position = ship
|
||||||
|
|
||||||
def generate(self):
|
def generate(self):
|
||||||
self.visualgen.generate()
|
|
||||||
|
|
||||||
# air support
|
# air support
|
||||||
self.airsupportgen.generate(self.is_awacs_enabled)
|
self.airsupportgen.generate(self.is_awacs_enabled)
|
||||||
for i, tanker_type in enumerate(self.airsupportgen.generated_tankers):
|
for i, tanker_type in enumerate(self.airsupportgen.generated_tankers):
|
||||||
@ -141,10 +149,17 @@ class Operation:
|
|||||||
else:
|
else:
|
||||||
self.envgen.load(self.environment_settings)
|
self.envgen.load(self.environment_settings)
|
||||||
|
|
||||||
|
# options
|
||||||
|
self.forcedoptionsgen.generate()
|
||||||
|
|
||||||
# main frequencies
|
# main frequencies
|
||||||
self.briefinggen.append_frequency("Flight", "251 MHz AM")
|
self.briefinggen.append_frequency("Flight", "251 MHz AM")
|
||||||
if self.conflict.from_cp.is_global or self.conflict.to_cp.is_global:
|
if self.departure_cp.is_global or self.conflict.to_cp.is_global:
|
||||||
self.briefinggen.append_frequency("Carrier", "20X/ICLS CHAN1")
|
self.briefinggen.append_frequency("Carrier", "20X/ICLS CHAN1")
|
||||||
|
|
||||||
# briefing
|
# briefing
|
||||||
self.briefinggen.generate()
|
self.briefinggen.generate()
|
||||||
|
|
||||||
|
# visuals
|
||||||
|
self.visualgen.generate()
|
||||||
|
|
||||||
|
|||||||
@ -5,6 +5,7 @@ from .operation import *
|
|||||||
|
|
||||||
class StrikeOperation(Operation):
|
class StrikeOperation(Operation):
|
||||||
strikegroup = None # type: db.AssignedUnitsDict
|
strikegroup = None # type: db.AssignedUnitsDict
|
||||||
|
sead = None # type: db.AssignedUnitsDict
|
||||||
escort = None # type: db.AssignedUnitsDict
|
escort = None # type: db.AssignedUnitsDict
|
||||||
interceptors = None # type: db.AssignedUnitsDict
|
interceptors = None # type: db.AssignedUnitsDict
|
||||||
|
|
||||||
@ -12,9 +13,11 @@ class StrikeOperation(Operation):
|
|||||||
|
|
||||||
def setup(self,
|
def setup(self,
|
||||||
strikegroup: db.AssignedUnitsDict,
|
strikegroup: db.AssignedUnitsDict,
|
||||||
|
sead: db.AssignedUnitsDict,
|
||||||
escort: db.AssignedUnitsDict,
|
escort: db.AssignedUnitsDict,
|
||||||
interceptors: db.AssignedUnitsDict):
|
interceptors: db.AssignedUnitsDict):
|
||||||
self.strikegroup = strikegroup
|
self.strikegroup = strikegroup
|
||||||
|
self.sead = sead
|
||||||
self.escort = escort
|
self.escort = escort
|
||||||
self.interceptors = interceptors
|
self.interceptors = interceptors
|
||||||
|
|
||||||
@ -40,8 +43,10 @@ class StrikeOperation(Operation):
|
|||||||
self.prepare_carriers(db.unitdict_merge(db.unitdict_from(self.strikegroup), db.unitdict_from(self.escort)))
|
self.prepare_carriers(db.unitdict_merge(db.unitdict_from(self.strikegroup), db.unitdict_from(self.escort)))
|
||||||
|
|
||||||
targets = [] # type: typing.List[typing.Tuple[str, str, Point]]
|
targets = [] # type: typing.List[typing.Tuple[str, str, Point]]
|
||||||
|
sead_targets = [] # type: typing.List[typing.Tuple[str, str, Point]]
|
||||||
category_counters = {} # type: typing.Dict[str, int]
|
category_counters = {} # type: typing.Dict[str, int]
|
||||||
processed_groups = []
|
processed_groups = []
|
||||||
|
|
||||||
for object in self.to_cp.ground_objects:
|
for object in self.to_cp.ground_objects:
|
||||||
if object.group_identifier in processed_groups:
|
if object.group_identifier in processed_groups:
|
||||||
continue
|
continue
|
||||||
@ -49,6 +54,10 @@ class StrikeOperation(Operation):
|
|||||||
processed_groups.append(object.group_identifier)
|
processed_groups.append(object.group_identifier)
|
||||||
category_counters[object.category] = category_counters.get(object.category, 0) + 1
|
category_counters[object.category] = category_counters.get(object.category, 0) + 1
|
||||||
markpoint_name = "{}{}".format(object.name_abbrev, category_counters[object.category])
|
markpoint_name = "{}{}".format(object.name_abbrev, category_counters[object.category])
|
||||||
|
|
||||||
|
if object.category == "aa":
|
||||||
|
sead_targets.append((str(object), markpoint_name, object.position))
|
||||||
|
|
||||||
targets.append((str(object), markpoint_name, object.position))
|
targets.append((str(object), markpoint_name, object.position))
|
||||||
|
|
||||||
targets.sort(key=lambda x: self.from_cp.position.distance_to_point(x[2]))
|
targets.sort(key=lambda x: self.from_cp.position.distance_to_point(x[2]))
|
||||||
@ -59,7 +68,13 @@ class StrikeOperation(Operation):
|
|||||||
planes_flights = {k: v for k, v in self.strikegroup.items() if k in plane_map.values()}
|
planes_flights = {k: v for k, v in self.strikegroup.items() if k in plane_map.values()}
|
||||||
self.airgen.generate_ground_attack_strikegroup(*assigned_units_split(planes_flights),
|
self.airgen.generate_ground_attack_strikegroup(*assigned_units_split(planes_flights),
|
||||||
targets=[(mp, pos) for (n, mp, pos) in targets],
|
targets=[(mp, pos) for (n, mp, pos) in targets],
|
||||||
at=self.attackers_starting_position)
|
at=self.attackers_starting_position,
|
||||||
|
escort=len(self.sead) == 0)
|
||||||
|
|
||||||
|
self.airgen.generate_sead_strikegroup(*assigned_units_split(self.sead),
|
||||||
|
targets=[(mp, pos) for (n, mp, pos) in sead_targets],
|
||||||
|
at=self.attackers_starting_position,
|
||||||
|
escort=len(self.sead) > 0)
|
||||||
|
|
||||||
heli_flights = {k: v for k, v in self.strikegroup.items() if k in helicopters.helicopter_map.values()}
|
heli_flights = {k: v for k, v in self.strikegroup.items() if k in helicopters.helicopter_map.values()}
|
||||||
if heli_flights:
|
if heli_flights:
|
||||||
|
|||||||
@ -3,6 +3,8 @@ class Settings:
|
|||||||
player_skill = "Good"
|
player_skill = "Good"
|
||||||
enemy_skill = "Average"
|
enemy_skill = "Average"
|
||||||
enemy_vehicle_skill = "Average"
|
enemy_vehicle_skill = "Average"
|
||||||
|
map_coalition_visibility = "All Units"
|
||||||
|
labels = "Full"
|
||||||
only_player_takeoff = True
|
only_player_takeoff = True
|
||||||
night_disabled = False
|
night_disabled = False
|
||||||
|
|
||||||
|
|||||||
@ -9,6 +9,7 @@ from .triggergen import *
|
|||||||
from .environmentgen import *
|
from .environmentgen import *
|
||||||
from .groundobjectsgen import *
|
from .groundobjectsgen import *
|
||||||
from .briefinggen import *
|
from .briefinggen import *
|
||||||
|
from .forcedoptionsgen import *
|
||||||
|
|
||||||
from . import naming
|
from . import naming
|
||||||
|
|
||||||
|
|||||||
123
gen/aircraft.py
@ -17,23 +17,23 @@ ESCORT_ENGAGEMENT_MAX_DIST = 100000
|
|||||||
WORKAROUND_WAYP_DIST = 1000
|
WORKAROUND_WAYP_DIST = 1000
|
||||||
|
|
||||||
WARM_START_HELI_AIRSPEED = 120
|
WARM_START_HELI_AIRSPEED = 120
|
||||||
WARM_START_HELI_ALT = 1000
|
WARM_START_HELI_ALT = 500
|
||||||
|
|
||||||
WARM_START_ALTITUDE = 3000
|
WARM_START_ALTITUDE = 3000
|
||||||
WARM_START_AIRSPEED = 550
|
WARM_START_AIRSPEED = 550
|
||||||
|
|
||||||
INTERCEPTION_ALT = 3000
|
|
||||||
INTERCEPTION_AIRSPEED = 1000
|
INTERCEPTION_AIRSPEED = 1000
|
||||||
BARCAP_RACETRACK_DISTANCE = 20000
|
BARCAP_RACETRACK_DISTANCE = 20000
|
||||||
|
|
||||||
ATTACK_CIRCLE_ALT = 5000
|
ATTACK_CIRCLE_ALT = 1000
|
||||||
ATTACK_CIRCLE_DURATION = 15
|
ATTACK_CIRCLE_DURATION = 15
|
||||||
|
|
||||||
CAS_ALTITUDE = 1000
|
CAS_ALTITUDE = 800
|
||||||
RTB_ALTITUDE = 1000
|
RTB_ALTITUDE = 800
|
||||||
HELI_ALT = 900
|
RTB_DISTANCE = 5000
|
||||||
|
HELI_ALT = 500
|
||||||
|
|
||||||
TRANSPORT_LANDING_ALT = 1000
|
TRANSPORT_LANDING_ALT = 2000
|
||||||
|
|
||||||
DEFENCE_ENGAGEMENT_MAX_DISTANCE = 60000
|
DEFENCE_ENGAGEMENT_MAX_DISTANCE = 60000
|
||||||
INTERCEPT_MAX_DISTANCE = 200000
|
INTERCEPT_MAX_DISTANCE = 200000
|
||||||
@ -69,9 +69,15 @@ class AircraftConflictGenerator:
|
|||||||
else:
|
else:
|
||||||
client_count = 0
|
client_count = 0
|
||||||
|
|
||||||
|
if flying_type == F_14B:
|
||||||
|
# workaround since 2 and 3 tomcat collide on carrier
|
||||||
|
group_size = 2
|
||||||
|
else:
|
||||||
|
group_size = 4
|
||||||
|
|
||||||
while count > 0:
|
while count > 0:
|
||||||
group_size = min(count, 4)
|
group_size = min(count, group_size)
|
||||||
client_size = max(min(client_count, 4), 0)
|
client_size = max(min(client_count, group_size), 0)
|
||||||
|
|
||||||
yield (flying_type, group_size, client_size)
|
yield (flying_type, group_size, client_size)
|
||||||
count -= group_size
|
count -= group_size
|
||||||
@ -149,7 +155,7 @@ class AircraftConflictGenerator:
|
|||||||
pos = Point(at.x + random.randint(100, 1000), at.y + random.randint(100, 1000))
|
pos = Point(at.x + random.randint(100, 1000), at.y + random.randint(100, 1000))
|
||||||
|
|
||||||
logging.info("airgen: {} for {} at {} at {}".format(unit_type, side.id, alt, speed))
|
logging.info("airgen: {} for {} at {} at {}".format(unit_type, side.id, alt, speed))
|
||||||
return self.m.flight_group(
|
group = self.m.flight_group(
|
||||||
country=side,
|
country=side,
|
||||||
name=name,
|
name=name,
|
||||||
aircraft_type=unit_type,
|
aircraft_type=unit_type,
|
||||||
@ -161,6 +167,9 @@ class AircraftConflictGenerator:
|
|||||||
start_type=self._start_type(),
|
start_type=self._start_type(),
|
||||||
group_size=count)
|
group_size=count)
|
||||||
|
|
||||||
|
group.points[0].alt_type = "RADIO"
|
||||||
|
return group
|
||||||
|
|
||||||
def _generate_at_group(self, name: str, side: Country, unit_type: FlyingType, count: int, client_count: int, at: typing.Union[ShipGroup, StaticGroup]) -> FlyingGroup:
|
def _generate_at_group(self, name: str, side: Country, unit_type: FlyingType, count: int, client_count: int, at: typing.Union[ShipGroup, StaticGroup]) -> FlyingGroup:
|
||||||
assert count > 0
|
assert count > 0
|
||||||
assert unit is not None
|
assert unit is not None
|
||||||
@ -180,7 +189,9 @@ class AircraftConflictGenerator:
|
|||||||
return self._generate_inflight(name, side, unit_type, count, client_count, at)
|
return self._generate_inflight(name, side, unit_type, count, client_count, at)
|
||||||
elif isinstance(at, Group):
|
elif isinstance(at, Group):
|
||||||
takeoff_ban = unit_type in db.CARRIER_TAKEOFF_BAN
|
takeoff_ban = unit_type in db.CARRIER_TAKEOFF_BAN
|
||||||
if not takeoff_ban:
|
ai_ban = client_count == 0 and self.settings.only_player_takeoff
|
||||||
|
|
||||||
|
if not takeoff_ban and not ai_ban:
|
||||||
return self._generate_at_group(name, side, unit_type, count, client_count, at)
|
return self._generate_at_group(name, side, unit_type, count, client_count, at)
|
||||||
else:
|
else:
|
||||||
return self._generate_inflight(name, side, unit_type, count, client_count, at.position)
|
return self._generate_inflight(name, side, unit_type, count, client_count, at.position)
|
||||||
@ -197,17 +208,26 @@ class AircraftConflictGenerator:
|
|||||||
else:
|
else:
|
||||||
assert False
|
assert False
|
||||||
|
|
||||||
|
def _add_radio_waypoint(self, group: FlyingGroup, position, altitude: int, airspeed: int = 600):
|
||||||
|
point = group.add_waypoint(position, altitude, airspeed)
|
||||||
|
point.alt_type = "RADIO"
|
||||||
|
return point
|
||||||
|
|
||||||
def _rtb_for(self, group: FlyingGroup, cp: ControlPoint, at: db.StartingPosition = None):
|
def _rtb_for(self, group: FlyingGroup, cp: ControlPoint, at: db.StartingPosition = None):
|
||||||
if not at:
|
if not at:
|
||||||
at = cp.at
|
at = cp.at
|
||||||
|
position = at if isinstance(at, Point) else at.position
|
||||||
|
|
||||||
if isinstance(at, Point):
|
last_waypoint = group.points[-1]
|
||||||
group.add_waypoint(at, RTB_ALTITUDE)
|
if last_waypoint is not None:
|
||||||
elif isinstance(at, Group):
|
heading = position.heading_between_point(last_waypoint.position)
|
||||||
group.add_waypoint(at.position, RTB_ALTITUDE)
|
tod_location = position.point_from_heading(heading, RTB_DISTANCE)
|
||||||
elif issubclass(at, Airport):
|
self._add_radio_waypoint(group, tod_location, last_waypoint.alt)
|
||||||
group.add_waypoint(at.position, RTB_ALTITUDE)
|
|
||||||
|
destination_waypoint = self._add_radio_waypoint(group, position, RTB_ALTITUDE)
|
||||||
|
if isinstance(at, Airport):
|
||||||
group.land_at(at)
|
group.land_at(at)
|
||||||
|
return destination_waypoint
|
||||||
|
|
||||||
def _at_position(self, at) -> Point:
|
def _at_position(self, at) -> Point:
|
||||||
if isinstance(at, Point):
|
if isinstance(at, Point):
|
||||||
@ -244,7 +264,7 @@ class AircraftConflictGenerator:
|
|||||||
orbit_task = ControlledTask(OrbitAction(ATTACK_CIRCLE_ALT, pattern=OrbitAction.OrbitPattern.Circle))
|
orbit_task = ControlledTask(OrbitAction(ATTACK_CIRCLE_ALT, pattern=OrbitAction.OrbitPattern.Circle))
|
||||||
orbit_task.stop_after_duration(ATTACK_CIRCLE_DURATION * 60)
|
orbit_task.stop_after_duration(ATTACK_CIRCLE_DURATION * 60)
|
||||||
|
|
||||||
orbit_waypoint = group.add_waypoint(self.conflict.position, CAS_ALTITUDE)
|
orbit_waypoint = self._add_radio_waypoint(group, self.conflict.position, CAS_ALTITUDE)
|
||||||
orbit_waypoint.tasks.append(orbit_task)
|
orbit_waypoint.tasks.append(orbit_task)
|
||||||
orbit_waypoint.tasks.append(EngageTargets(max_distance=DEFENCE_ENGAGEMENT_MAX_DISTANCE))
|
orbit_waypoint.tasks.append(EngageTargets(max_distance=DEFENCE_ENGAGEMENT_MAX_DISTANCE))
|
||||||
|
|
||||||
@ -263,9 +283,9 @@ class AircraftConflictGenerator:
|
|||||||
client_count=client_count,
|
client_count=client_count,
|
||||||
at=at and at or self._group_point(self.conflict.air_attackers_location))
|
at=at and at or self._group_point(self.conflict.air_attackers_location))
|
||||||
|
|
||||||
waypoint = group.add_waypoint(self.conflict.position, CAS_ALTITUDE, WARM_START_AIRSPEED)
|
waypoint = self._add_radio_waypoint(group, self.conflict.position, CAS_ALTITUDE, WARM_START_AIRSPEED)
|
||||||
if self.conflict.is_vector:
|
if self.conflict.is_vector:
|
||||||
group.add_waypoint(self.conflict.tail, CAS_ALTITUDE, WARM_START_AIRSPEED)
|
self._add_radio_waypoint(group, self.conflict.tail, CAS_ALTITUDE, WARM_START_AIRSPEED)
|
||||||
|
|
||||||
group.task = CAS.name
|
group.task = CAS.name
|
||||||
self._setup_group(group, CAS, client_count)
|
self._setup_group(group, CAS, client_count)
|
||||||
@ -289,6 +309,7 @@ class AircraftConflictGenerator:
|
|||||||
|
|
||||||
for name, pos in targets:
|
for name, pos in targets:
|
||||||
waypoint = group.add_waypoint(pos, 0, WARM_START_AIRSPEED, self.m.translation.create_string(name))
|
waypoint = group.add_waypoint(pos, 0, WARM_START_AIRSPEED, self.m.translation.create_string(name))
|
||||||
|
waypoint.tasks.append(Bombing(pos, attack_qty=2))
|
||||||
if escort_until_waypoint is None:
|
if escort_until_waypoint is None:
|
||||||
escort_until_waypoint = waypoint
|
escort_until_waypoint = waypoint
|
||||||
|
|
||||||
@ -298,6 +319,32 @@ class AircraftConflictGenerator:
|
|||||||
self.escort_targets.append((group, group.points.index(escort_until_waypoint)))
|
self.escort_targets.append((group, group.points.index(escort_until_waypoint)))
|
||||||
self._rtb_for(group, self.conflict.from_cp, at)
|
self._rtb_for(group, self.conflict.from_cp, at)
|
||||||
|
|
||||||
|
def generate_sead_strikegroup(self, strikegroup: db.PlaneDict, clients: db.PlaneDict, targets: typing.List[typing.Tuple[str, Point]], at: db.StartingPosition, escort=True):
|
||||||
|
assert not escort or len(self.escort_targets) == 0
|
||||||
|
|
||||||
|
for flying_type, count, client_count in self._split_to_groups(strikegroup, clients):
|
||||||
|
group = self._generate_group(
|
||||||
|
name=namegen.next_unit_name(self.conflict.attackers_side, flying_type),
|
||||||
|
side=self.conflict.attackers_side,
|
||||||
|
unit_type=flying_type,
|
||||||
|
count=count,
|
||||||
|
client_count=client_count,
|
||||||
|
at=at and at or self._group_point(self.conflict.air_attackers_location))
|
||||||
|
|
||||||
|
escort_until_waypoint = None
|
||||||
|
|
||||||
|
for name, pos in targets:
|
||||||
|
waypoint = group.add_waypoint(pos, 0, WARM_START_AIRSPEED, self.m.translation.create_string(name))
|
||||||
|
if escort_until_waypoint is None:
|
||||||
|
escort_until_waypoint = waypoint
|
||||||
|
|
||||||
|
group.task = SEAD.name
|
||||||
|
self._setup_group(group, SEAD, client_count)
|
||||||
|
if escort:
|
||||||
|
self.escort_targets.append((group, group.points.index(escort_until_waypoint)))
|
||||||
|
|
||||||
|
self._rtb_for(group, self.conflict.from_cp, at)
|
||||||
|
|
||||||
def generate_defenders_cas(self, defenders: db.PlaneDict, clients: db.PlaneDict, at: db.StartingPosition = None, escort=True):
|
def generate_defenders_cas(self, defenders: db.PlaneDict, clients: db.PlaneDict, at: db.StartingPosition = None, escort=True):
|
||||||
assert not escort or len(self.escort_targets) == 0
|
assert not escort or len(self.escort_targets) == 0
|
||||||
|
|
||||||
@ -312,11 +359,11 @@ class AircraftConflictGenerator:
|
|||||||
|
|
||||||
location = self._group_point(self.conflict.air_defenders_location)
|
location = self._group_point(self.conflict.air_defenders_location)
|
||||||
insertion_point = self.conflict.find_insertion_point(location)
|
insertion_point = self.conflict.find_insertion_point(location)
|
||||||
waypoint = group.add_waypoint(insertion_point, CAS_ALTITUDE, WARM_START_AIRSPEED)
|
waypoint = self._add_radio_waypoint(group, insertion_point, CAS_ALTITUDE, WARM_START_AIRSPEED)
|
||||||
|
|
||||||
if self.conflict.is_vector:
|
if self.conflict.is_vector:
|
||||||
destination_tail = self.conflict.tail.distance_to_point(insertion_point) > self.conflict.position.distance_to_point(insertion_point)
|
destination_tail = self.conflict.tail.distance_to_point(insertion_point) > self.conflict.position.distance_to_point(insertion_point)
|
||||||
group.add_waypoint(destination_tail and self.conflict.tail or self.conflict.position, CAS_ALTITUDE, WARM_START_AIRSPEED)
|
self._add_radio_waypoint(group, destination_tail and self.conflict.tail or self.conflict.position, CAS_ALTITUDE, WARM_START_AIRSPEED)
|
||||||
|
|
||||||
group.task = CAS.name
|
group.task = CAS.name
|
||||||
self._setup_group(group, CAS, client_count)
|
self._setup_group(group, CAS, client_count)
|
||||||
@ -336,7 +383,7 @@ class AircraftConflictGenerator:
|
|||||||
client_count=client_count,
|
client_count=client_count,
|
||||||
at=at and at or self._group_point(self.conflict.air_attackers_location))
|
at=at and at or self._group_point(self.conflict.air_attackers_location))
|
||||||
|
|
||||||
wayp = group.add_waypoint(self.conflict.position, CAS_ALTITUDE, WARM_START_AIRSPEED)
|
wayp = self._add_radio_waypoint(group, self.conflict.position, CAS_ALTITUDE, WARM_START_AIRSPEED)
|
||||||
for target_group in target_groups:
|
for target_group in target_groups:
|
||||||
wayp.tasks.append(AttackGroup(target_group.id))
|
wayp.tasks.append(AttackGroup(target_group.id))
|
||||||
|
|
||||||
@ -377,7 +424,7 @@ class AircraftConflictGenerator:
|
|||||||
at=at and at or self._group_point(self.conflict.air_defenders_location))
|
at=at and at or self._group_point(self.conflict.air_defenders_location))
|
||||||
|
|
||||||
group.task = CAP.name
|
group.task = CAP.name
|
||||||
wayp = group.add_waypoint(self.conflict.position, CAS_ALTITUDE, WARM_START_AIRSPEED)
|
wayp = self._add_radio_waypoint(group, self.conflict.position, CAS_ALTITUDE, WARM_START_AIRSPEED)
|
||||||
wayp.tasks.append(dcs.task.EngageTargets(max_distance=DEFENCE_ENGAGEMENT_MAX_DISTANCE))
|
wayp.tasks.append(dcs.task.EngageTargets(max_distance=DEFENCE_ENGAGEMENT_MAX_DISTANCE))
|
||||||
wayp.tasks.append(dcs.task.OrbitAction(ATTACK_CIRCLE_ALT, pattern=OrbitAction.OrbitPattern.Circle))
|
wayp.tasks.append(dcs.task.OrbitAction(ATTACK_CIRCLE_ALT, pattern=OrbitAction.OrbitPattern.Circle))
|
||||||
self._setup_group(group, CAP, client_count)
|
self._setup_group(group, CAP, client_count)
|
||||||
@ -393,9 +440,9 @@ class AircraftConflictGenerator:
|
|||||||
client_count=client_count,
|
client_count=client_count,
|
||||||
at=at and at or self._group_point(self.conflict.air_attackers_location))
|
at=at and at or self._group_point(self.conflict.air_attackers_location))
|
||||||
|
|
||||||
waypoint = group.add_waypoint(self.conflict.position, WARM_START_ALTITUDE, WARM_START_AIRSPEED)
|
waypoint = self._add_radio_waypoint(group, self.conflict.position, WARM_START_ALTITUDE, WARM_START_AIRSPEED)
|
||||||
if self.conflict.is_vector:
|
if self.conflict.is_vector:
|
||||||
group.add_waypoint(self.conflict.tail, WARM_START_ALTITUDE, WARM_START_AIRSPEED)
|
self._add_radio_waypoint(group, self.conflict.tail, WARM_START_ALTITUDE, WARM_START_AIRSPEED)
|
||||||
|
|
||||||
group.task = CAP.name
|
group.task = CAP.name
|
||||||
self._setup_group(group, CAP, client_count)
|
self._setup_group(group, CAP, client_count)
|
||||||
@ -411,14 +458,14 @@ class AircraftConflictGenerator:
|
|||||||
client_count=client_count,
|
client_count=client_count,
|
||||||
at=at and at or self._group_point(self.conflict.air_defenders_location))
|
at=at and at or self._group_point(self.conflict.air_defenders_location))
|
||||||
|
|
||||||
waypoint = group.add_waypoint(self.conflict.position, WARM_START_ALTITUDE, WARM_START_AIRSPEED)
|
waypoint = self._add_radio_waypoint(group, self.conflict.position, WARM_START_ALTITUDE, WARM_START_AIRSPEED)
|
||||||
if self.conflict.is_vector:
|
if self.conflict.is_vector:
|
||||||
group.add_waypoint(self.conflict.tail, WARM_START_ALTITUDE, WARM_START_AIRSPEED)
|
self._add_radio_waypoint(group, self.conflict.tail, WARM_START_ALTITUDE, WARM_START_AIRSPEED)
|
||||||
else:
|
else:
|
||||||
heading = group.position.heading_between_point(self.conflict.position)
|
heading = group.position.heading_between_point(self.conflict.position)
|
||||||
waypoint = group.add_waypoint(self.conflict.position.point_from_heading(heading, BARCAP_RACETRACK_DISTANCE),
|
waypoint = self._add_radio_waypoint(group, self.conflict.position.point_from_heading(heading, BARCAP_RACETRACK_DISTANCE),
|
||||||
WARM_START_ALTITUDE,
|
WARM_START_ALTITUDE,
|
||||||
WARM_START_AIRSPEED)
|
WARM_START_AIRSPEED)
|
||||||
waypoint.tasks.append(OrbitAction(WARM_START_ALTITUDE, WARM_START_AIRSPEED))
|
waypoint.tasks.append(OrbitAction(WARM_START_ALTITUDE, WARM_START_AIRSPEED))
|
||||||
|
|
||||||
group.task = CAP.name
|
group.task = CAP.name
|
||||||
@ -437,9 +484,11 @@ class AircraftConflictGenerator:
|
|||||||
client_count=client_count,
|
client_count=client_count,
|
||||||
at=self._group_point(self.conflict.air_defenders_location))
|
at=self._group_point(self.conflict.air_defenders_location))
|
||||||
|
|
||||||
waypoint = group.add_waypoint(destination.position.random_point_within(0, 0), TRANSPORT_LANDING_ALT)
|
waypoint = self._rtb_for(group, self.conflict.to_cp)
|
||||||
if escort:
|
if escort:
|
||||||
self.escort_targets.append((group, group.points.index(waypoint)))
|
self.escort_targets.append((group, group.points.index(waypoint)))
|
||||||
|
|
||||||
|
self._add_radio_waypoint(group, destination.position, RTB_ALTITUDE)
|
||||||
group.task = Transport.name
|
group.task = Transport.name
|
||||||
group.land_at(destination)
|
group.land_at(destination)
|
||||||
|
|
||||||
@ -456,11 +505,11 @@ class AircraftConflictGenerator:
|
|||||||
group.task = CAP.name
|
group.task = CAP.name
|
||||||
group.points[0].tasks.append(EngageTargets(max_distance=INTERCEPT_MAX_DISTANCE))
|
group.points[0].tasks.append(EngageTargets(max_distance=INTERCEPT_MAX_DISTANCE))
|
||||||
|
|
||||||
wayp = group.add_waypoint(self.conflict.position, WARM_START_ALTITUDE, INTERCEPTION_AIRSPEED)
|
wayp = self._add_radio_waypoint(group, self.conflict.position, WARM_START_ALTITUDE, INTERCEPTION_AIRSPEED)
|
||||||
wayp.tasks.append(EngageTargets(max_distance=INTERCEPT_MAX_DISTANCE))
|
wayp.tasks.append(EngageTargets(max_distance=INTERCEPT_MAX_DISTANCE))
|
||||||
|
|
||||||
if self.conflict.is_vector:
|
if self.conflict.is_vector:
|
||||||
group.add_waypoint(self.conflict.tail, CAS_ALTITUDE, WARM_START_ALTITUDE)
|
self._add_radio_waypoint(group, self.conflict.tail, CAS_ALTITUDE, WARM_START_ALTITUDE)
|
||||||
|
|
||||||
self._setup_group(group, CAP, client_count)
|
self._setup_group(group, CAP, client_count)
|
||||||
self._rtb_for(group, self.conflict.from_cp, at)
|
self._rtb_for(group, self.conflict.from_cp, at)
|
||||||
@ -476,9 +525,5 @@ class AircraftConflictGenerator:
|
|||||||
at=at and at or self._group_point(self.conflict.air_attackers_location)
|
at=at and at or self._group_point(self.conflict.air_attackers_location)
|
||||||
)
|
)
|
||||||
|
|
||||||
group.add_waypoint(
|
self._add_radio_waypoint(group, self.conflict.position, HELI_ALT)
|
||||||
pos=self.conflict.position,
|
|
||||||
altitude=HELI_ALT,
|
|
||||||
)
|
|
||||||
|
|
||||||
self._setup_group(group, Transport, client_count)
|
self._setup_group(group, Transport, client_count)
|
||||||
|
|||||||
@ -49,12 +49,14 @@ class AirSupportConflictGenerator:
|
|||||||
)
|
)
|
||||||
|
|
||||||
tanker_group.points[0].tasks.append(ActivateBeaconCommand(channel=97 + i, unit_id=tanker_group.id, aa=False))
|
tanker_group.points[0].tasks.append(ActivateBeaconCommand(channel=97 + i, unit_id=tanker_group.id, aa=False))
|
||||||
|
tanker_group.points[0].tasks.append(SetInvisibleCommand(True))
|
||||||
|
tanker_group.points[0].tasks.append(SetImmortalCommand(True))
|
||||||
|
|
||||||
if is_awacs_enabled:
|
if is_awacs_enabled:
|
||||||
awacs_unit = db.find_unittype(AWACS, self.conflict.attackers_side.name)[0]
|
awacs_unit = db.find_unittype(AWACS, self.conflict.attackers_side.name)[0]
|
||||||
self.mission.awacs_flight(
|
awacs_flight = self.mission.awacs_flight(
|
||||||
country=self.mission.country(self.game.player),
|
country=self.mission.country(self.game.player),
|
||||||
name=namegen.next_awacs_name(self.mission.country(self.game.player),),
|
name=namegen.next_awacs_name(self.mission.country(self.game.player)),
|
||||||
plane_type=awacs_unit,
|
plane_type=awacs_unit,
|
||||||
altitude=AWACS_ALT,
|
altitude=AWACS_ALT,
|
||||||
airport=None,
|
airport=None,
|
||||||
@ -62,3 +64,6 @@ class AirSupportConflictGenerator:
|
|||||||
frequency=133,
|
frequency=133,
|
||||||
start_type=StartType.Warm,
|
start_type=StartType.Warm,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
awacs_flight.points[0].tasks.append(SetInvisibleCommand(True))
|
||||||
|
awacs_flight.points[0].tasks.append(SetImmortalCommand(True))
|
||||||
|
|||||||
16
gen/armor.py
@ -36,7 +36,7 @@ class ArmorConflictGenerator:
|
|||||||
|
|
||||||
return point.random_point_within(distance, self.conflict.size * SPREAD_DISTANCE_SIZE_FACTOR)
|
return point.random_point_within(distance, self.conflict.size * SPREAD_DISTANCE_SIZE_FACTOR)
|
||||||
|
|
||||||
def _generate_group(self, side: Country, unit: VehicleType, count: int, at: Point, to: Point = None):
|
def _generate_group(self, side: Country, unit: VehicleType, count: int, at: Point, to: Point = None, move_formation: PointAction = PointAction.OffRoad):
|
||||||
for c in range(count):
|
for c in range(count):
|
||||||
logging.info("armorgen: {} for {}".format(unit, side.id))
|
logging.info("armorgen: {} for {}".format(unit, side.id))
|
||||||
group = self.m.vehicle_group(
|
group = self.m.vehicle_group(
|
||||||
@ -45,7 +45,7 @@ class ArmorConflictGenerator:
|
|||||||
unit,
|
unit,
|
||||||
position=self._group_point(at),
|
position=self._group_point(at),
|
||||||
group_size=1,
|
group_size=1,
|
||||||
move_formation=PointAction.OffRoad)
|
move_formation=move_formation)
|
||||||
|
|
||||||
vehicle: Vehicle = group.units[0]
|
vehicle: Vehicle = group.units[0]
|
||||||
vehicle.player_can_drive = True
|
vehicle.player_can_drive = True
|
||||||
@ -53,7 +53,7 @@ class ArmorConflictGenerator:
|
|||||||
if not to:
|
if not to:
|
||||||
to = self.conflict.position.point_from_heading(0, 500)
|
to = self.conflict.position.point_from_heading(0, 500)
|
||||||
|
|
||||||
wayp = group.add_waypoint(self._group_point(to))
|
wayp = group.add_waypoint(self._group_point(to), move_formation=move_formation)
|
||||||
wayp.tasks = []
|
wayp.tasks = []
|
||||||
|
|
||||||
def _generate_fight_at(self, attackers: db.ArmorDict, defenders: db.ArmorDict, position: Point):
|
def _generate_fight_at(self, attackers: db.ArmorDict, defenders: db.ArmorDict, position: Point):
|
||||||
@ -109,6 +109,16 @@ class ArmorConflictGenerator:
|
|||||||
random.randint(0, self.conflict.distance))
|
random.randint(0, self.conflict.distance))
|
||||||
self._generate_fight_at(attacker_group_dict, target_group_dict, position)
|
self._generate_fight_at(attacker_group_dict, target_group_dict, position)
|
||||||
|
|
||||||
|
def generate_convoy(self, units: db.ArmorDict):
|
||||||
|
for type, count in units.items():
|
||||||
|
self._generate_group(
|
||||||
|
side=self.conflict.defenders_side,
|
||||||
|
unit=type,
|
||||||
|
count=count,
|
||||||
|
at=self.conflict.ground_defenders_location,
|
||||||
|
to=self.conflict.position,
|
||||||
|
move_formation=PointAction.OnRoad)
|
||||||
|
|
||||||
def generate_passengers(self, count: int):
|
def generate_passengers(self, count: int):
|
||||||
unit_type = random.choice(db.find_unittype(Nothing, self.conflict.attackers_side.name))
|
unit_type = random.choice(db.find_unittype(Nothing, self.conflict.attackers_side.name))
|
||||||
|
|
||||||
|
|||||||
@ -28,7 +28,7 @@ CAP_CAS_DISTANCE = 10000, 120000
|
|||||||
|
|
||||||
GROUND_INTERCEPT_SPREAD = 5000
|
GROUND_INTERCEPT_SPREAD = 5000
|
||||||
GROUND_DISTANCE_FACTOR = 1
|
GROUND_DISTANCE_FACTOR = 1
|
||||||
GROUND_DISTANCE = 4000
|
GROUND_DISTANCE = 2000
|
||||||
|
|
||||||
GROUND_ATTACK_DISTANCE = 25000, 13000
|
GROUND_ATTACK_DISTANCE = 25000, 13000
|
||||||
|
|
||||||
@ -162,6 +162,8 @@ class Conflict:
|
|||||||
|
|
||||||
strength_delta = (from_cp.base.strength - to_cp.base.strength) / 1.0
|
strength_delta = (from_cp.base.strength - to_cp.base.strength) / 1.0
|
||||||
position = middle_point.point_from_heading(attack_heading, strength_delta * attack_distance / 2 - FRONTLINE_MIN_CP_DISTANCE)
|
position = middle_point.point_from_heading(attack_heading, strength_delta * attack_distance / 2 - FRONTLINE_MIN_CP_DISTANCE)
|
||||||
|
return position, _opposite_heading(attack_heading)
|
||||||
|
|
||||||
ground_position = cls._find_ground_position(position, attack_distance / 2 - FRONTLINE_MIN_CP_DISTANCE, attack_heading, theater)
|
ground_position = cls._find_ground_position(position, attack_distance / 2 - FRONTLINE_MIN_CP_DISTANCE, attack_heading, theater)
|
||||||
if ground_position:
|
if ground_position:
|
||||||
return ground_position, _opposite_heading(attack_heading)
|
return ground_position, _opposite_heading(attack_heading)
|
||||||
@ -172,6 +174,23 @@ class Conflict:
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def frontline_vector(cls, from_cp: ControlPoint, to_cp: ControlPoint, theater: ConflictTheater) -> typing.Optional[typing.Tuple[Point, int, int]]:
|
def frontline_vector(cls, from_cp: ControlPoint, to_cp: ControlPoint, theater: ConflictTheater) -> typing.Optional[typing.Tuple[Point, int, int]]:
|
||||||
|
initial, heading = cls.frontline_position(theater, from_cp, to_cp)
|
||||||
|
|
||||||
|
"""
|
||||||
|
probe_end_point = initial.point_from_heading(heading, FRONTLINE_LENGTH)
|
||||||
|
probe = geometry.LineString([(initial.x, initial.y), (probe_end_point.x, probe_end_point.y) ])
|
||||||
|
intersection = probe.intersection(theater.land_poly)
|
||||||
|
|
||||||
|
if isinstance(intersection, geometry.LineString):
|
||||||
|
intersection = intersection
|
||||||
|
elif isinstance(intersection, geometry.MultiLineString):
|
||||||
|
intersection = intersection.geoms[0]
|
||||||
|
else:
|
||||||
|
print(intersection)
|
||||||
|
return None
|
||||||
|
|
||||||
|
return Point(*intersection.xy[0]), _heading_sum(heading, 90), intersection.length
|
||||||
|
"""
|
||||||
frontline = cls.frontline_position(theater, from_cp, to_cp)
|
frontline = cls.frontline_position(theater, from_cp, to_cp)
|
||||||
if not frontline:
|
if not frontline:
|
||||||
return None
|
return None
|
||||||
@ -207,9 +226,21 @@ class Conflict:
|
|||||||
pos = new_pos
|
pos = new_pos
|
||||||
else:
|
else:
|
||||||
return pos
|
return pos
|
||||||
|
|
||||||
return pos
|
return pos
|
||||||
|
|
||||||
|
"""
|
||||||
|
probe_end_point = initial.point_from_heading(heading, max_distance)
|
||||||
|
probe = geometry.LineString([(initial.x, initial.y), (probe_end_point.x, probe_end_point.y)])
|
||||||
|
|
||||||
|
intersection = probe.intersection(theater.land_poly)
|
||||||
|
if intersection is geometry.LineString:
|
||||||
|
return Point(*intersection.xy[1])
|
||||||
|
elif intersection is geometry.MultiLineString:
|
||||||
|
return Point(*intersection.geoms[0].xy[1])
|
||||||
|
|
||||||
|
return None
|
||||||
|
"""
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _find_ground_position(cls, initial: Point, max_distance: int, heading: int, theater: ConflictTheater) -> typing.Optional[Point]:
|
def _find_ground_position(cls, initial: Point, max_distance: int, heading: int, theater: ConflictTheater) -> typing.Optional[Point]:
|
||||||
pos = initial
|
pos = initial
|
||||||
@ -218,9 +249,19 @@ class Conflict:
|
|||||||
return pos
|
return pos
|
||||||
|
|
||||||
pos = pos.point_from_heading(heading, 500)
|
pos = pos.point_from_heading(heading, 500)
|
||||||
|
"""
|
||||||
|
probe_end_point = initial.point_from_heading(heading, max_distance)
|
||||||
|
probe = geometry.LineString([(initial.x, initial.y), (probe_end_point.x, probe_end_point.y) ])
|
||||||
|
|
||||||
logging.info("Didn't find ground position!")
|
intersection = probe.intersection(theater.land_poly)
|
||||||
return None
|
if isinstance(intersection, geometry.LineString):
|
||||||
|
return Point(*intersection.xy[1])
|
||||||
|
elif isinstance(intersection, geometry.MultiLineString):
|
||||||
|
return Point(*intersection.geoms[0].xy[1])
|
||||||
|
"""
|
||||||
|
|
||||||
|
logging.error("Didn't find ground position ({})!".format(initial))
|
||||||
|
return initial
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def capture_conflict(cls, attacker: Country, defender: Country, from_cp: ControlPoint, to_cp: ControlPoint, theater: ConflictTheater):
|
def capture_conflict(cls, attacker: Country, defender: Country, from_cp: ControlPoint, to_cp: ControlPoint, theater: ConflictTheater):
|
||||||
@ -277,13 +318,15 @@ class Conflict:
|
|||||||
)
|
)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def intercept_conflict(cls, attacker: Country, defender: Country, from_cp: ControlPoint, to_cp: ControlPoint, theater: ConflictTheater):
|
def intercept_position(cls, from_cp: ControlPoint, to_cp: ControlPoint) -> Point:
|
||||||
raw_distance = from_cp.position.distance_to_point(to_cp.position) * 1.5
|
raw_distance = from_cp.position.distance_to_point(to_cp.position) * 1.5
|
||||||
distance = max(min(raw_distance, INTERCEPT_MAX_DISTANCE), INTERCEPT_MIN_DISTANCE)
|
distance = max(min(raw_distance, INTERCEPT_MAX_DISTANCE), INTERCEPT_MIN_DISTANCE)
|
||||||
|
|
||||||
heading = _heading_sum(from_cp.position.heading_between_point(to_cp.position), random.choice([-1, 1]) * random.randint(60, 100))
|
heading = _heading_sum(from_cp.position.heading_between_point(to_cp.position), random.choice([-1, 1]) * random.randint(60, 100))
|
||||||
position = from_cp.position.point_from_heading(heading, distance)
|
return from_cp.position.point_from_heading(heading, distance)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def intercept_conflict(cls, attacker: Country, defender: Country, position: Point, from_cp: ControlPoint, to_cp: ControlPoint, theater: ConflictTheater):
|
||||||
|
heading = from_cp.position.heading_between_point(position)
|
||||||
return cls(
|
return cls(
|
||||||
position=position.point_from_heading(position.heading_between_point(to_cp.position), INTERCEPT_CONFLICT_DISTANCE),
|
position=position.point_from_heading(position.heading_between_point(to_cp.position), INTERCEPT_CONFLICT_DISTANCE),
|
||||||
theater=theater,
|
theater=theater,
|
||||||
@ -319,6 +362,35 @@ class Conflict:
|
|||||||
air_defenders_location=position.point_from_heading(heading, AIR_DISTANCE),
|
air_defenders_location=position.point_from_heading(heading, AIR_DISTANCE),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def convoy_strike_conflict(cls, attacker: Country, defender: Country, from_cp: ControlPoint, to_cp: ControlPoint, theater: ConflictTheater):
|
||||||
|
frontline_position, frontline_heading, frontline_length = Conflict.frontline_vector(from_cp, to_cp, theater)
|
||||||
|
if not frontline_position:
|
||||||
|
assert False
|
||||||
|
|
||||||
|
heading = frontline_heading
|
||||||
|
starting_position = Conflict._find_ground_position(frontline_position.point_from_heading(heading, 7000),
|
||||||
|
GROUND_INTERCEPT_SPREAD,
|
||||||
|
_opposite_heading(heading), theater)
|
||||||
|
if not starting_position:
|
||||||
|
starting_position = frontline_position
|
||||||
|
destination_position = frontline_position
|
||||||
|
else:
|
||||||
|
destination_position = frontline_position
|
||||||
|
|
||||||
|
return cls(
|
||||||
|
position=destination_position,
|
||||||
|
theater=theater,
|
||||||
|
from_cp=from_cp,
|
||||||
|
to_cp=to_cp,
|
||||||
|
attackers_side=attacker,
|
||||||
|
defenders_side=defender,
|
||||||
|
ground_attackers_location=None,
|
||||||
|
ground_defenders_location=starting_position,
|
||||||
|
air_attackers_location=starting_position.point_from_heading(_opposite_heading(heading), AIR_DISTANCE),
|
||||||
|
air_defenders_location=starting_position.point_from_heading(heading, AIR_DISTANCE),
|
||||||
|
)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def frontline_cas_conflict(cls, attacker: Country, defender: Country, from_cp: ControlPoint, to_cp: ControlPoint, theater: ConflictTheater):
|
def frontline_cas_conflict(cls, attacker: Country, defender: Country, from_cp: ControlPoint, to_cp: ControlPoint, theater: ConflictTheater):
|
||||||
assert cls.has_frontline_between(from_cp, to_cp)
|
assert cls.has_frontline_between(from_cp, to_cp)
|
||||||
@ -385,7 +457,7 @@ class Conflict:
|
|||||||
)
|
)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def naval_intercept_conflict(cls, attacker: Country, defender: Country, from_cp: ControlPoint, to_cp: ControlPoint, theater: ConflictTheater):
|
def naval_intercept_position(cls, from_cp: ControlPoint, to_cp: ControlPoint, theater: ConflictTheater):
|
||||||
radial = random.choice(to_cp.sea_radials)
|
radial = random.choice(to_cp.sea_radials)
|
||||||
|
|
||||||
initial_distance = min(int(from_cp.position.distance_to_point(to_cp.position) * NAVAL_INTERCEPT_DISTANCE_FACTOR), NAVAL_INTERCEPT_DISTANCE_MAX)
|
initial_distance = min(int(from_cp.position.distance_to_point(to_cp.position) * NAVAL_INTERCEPT_DISTANCE_FACTOR), NAVAL_INTERCEPT_DISTANCE_MAX)
|
||||||
@ -395,7 +467,10 @@ class Conflict:
|
|||||||
|
|
||||||
if not theater.is_on_land(position):
|
if not theater.is_on_land(position):
|
||||||
break
|
break
|
||||||
|
return position
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def naval_intercept_conflict(cls, attacker: Country, defender: Country, position: Point, from_cp: ControlPoint, to_cp: ControlPoint, theater: ConflictTheater):
|
||||||
attacker_heading = from_cp.position.heading_between_point(to_cp.position)
|
attacker_heading = from_cp.position.heading_between_point(to_cp.position)
|
||||||
return cls(
|
return cls(
|
||||||
position=position,
|
position=position,
|
||||||
|
|||||||
@ -26,9 +26,9 @@ WEATHER_FOG_VISIBILITY = 2500, 5000
|
|||||||
WEATHER_FOG_THICKNESS = 100, 500
|
WEATHER_FOG_THICKNESS = 100, 500
|
||||||
|
|
||||||
RANDOM_TIME = {
|
RANDOM_TIME = {
|
||||||
"night": 5,
|
"night": 7,
|
||||||
"dusk": 30,
|
"dusk": 40,
|
||||||
"dawn": 30,
|
"dawn": 40,
|
||||||
"day": 100,
|
"day": 100,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
45
gen/forcedoptionsgen.py
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
import logging
|
||||||
|
import typing
|
||||||
|
from enum import IntEnum
|
||||||
|
|
||||||
|
from dcs.mission import Mission
|
||||||
|
from dcs.forcedoptions import ForcedOptions
|
||||||
|
|
||||||
|
from .conflictgen import *
|
||||||
|
|
||||||
|
|
||||||
|
class Labels(IntEnum):
|
||||||
|
Off = 0
|
||||||
|
Full = 1
|
||||||
|
Abbreviated = 2
|
||||||
|
Dot = 3
|
||||||
|
|
||||||
|
|
||||||
|
class ForcedOptionsGenerator:
|
||||||
|
def __init__(self, mission: Mission, conflict: Conflict, game):
|
||||||
|
self.mission = mission
|
||||||
|
self.conflict = conflict
|
||||||
|
self.game = game
|
||||||
|
|
||||||
|
def _set_options_view(self):
|
||||||
|
if self.game.settings.map_coalition_visibility == "All Units":
|
||||||
|
self.mission.forced_options.options_view = ForcedOptions.Views.All
|
||||||
|
elif self.game.settings.map_coalition_visibility == "Allied Units":
|
||||||
|
self.mission.forced_options.options_view = ForcedOptions.Views.Allies
|
||||||
|
elif self.game.settings.map_coalition_visibility == "Own Aircraft":
|
||||||
|
self.mission.forced_options.options_view = ForcedOptions.Views.MyAircraft
|
||||||
|
elif self.game.settings.map_coalition_visibility == "None":
|
||||||
|
self.mission.forced_options.options_view = ForcedOptions.Views.OnlyMap
|
||||||
|
|
||||||
|
def _set_labels(self):
|
||||||
|
if self.game.settings.labels == "Abbreviated":
|
||||||
|
self.mission.forced_options.labels = int(Labels.Abbreviated)
|
||||||
|
elif self.game.settings.labels == "Dot Only":
|
||||||
|
self.mission.forced_options.labels = int(Labels.Dot)
|
||||||
|
elif self.game.settings.labels == "Off":
|
||||||
|
self.mission.forced_options.labels = int(Labels.Off)
|
||||||
|
|
||||||
|
def generate(self):
|
||||||
|
self._set_options_view()
|
||||||
|
self._set_labels()
|
||||||
|
|
||||||
@ -35,17 +35,19 @@ class ShipGenerator:
|
|||||||
|
|
||||||
def generate_cargo(self, units: db.ShipDict) -> typing.Collection[ShipGroup]:
|
def generate_cargo(self, units: db.ShipDict) -> typing.Collection[ShipGroup]:
|
||||||
groups = []
|
groups = []
|
||||||
|
offset = 0
|
||||||
for unit_type, unit_count in units.items():
|
for unit_type, unit_count in units.items():
|
||||||
logging.info("shipgen: {} ({}) for {}".format(unit_type, unit_count, self.conflict.defenders_side))
|
for _ in range(unit_count):
|
||||||
group = self.m.ship_group(
|
offset += 1
|
||||||
country=self.conflict.defenders_side,
|
logging.info("shipgen: {} ({}) for {}".format(unit_type, unit_count, self.conflict.defenders_side))
|
||||||
name=namegen.next_unit_name(self.conflict.defenders_side, unit_type),
|
group = self.m.ship_group(
|
||||||
_type=unit_type,
|
country=self.conflict.defenders_side,
|
||||||
position=self.conflict.ground_defenders_location.random_point_within(SHIP_RANDOM_SPREAD, SHIP_RANDOM_SPREAD),
|
name=namegen.next_unit_name(self.conflict.defenders_side, unit_type),
|
||||||
group_size=unit_count,
|
_type=unit_type,
|
||||||
)
|
position=self.conflict.ground_defenders_location.random_point_within(SHIP_RANDOM_SPREAD, SHIP_RANDOM_SPREAD).point_from_heading(0, offset * SHIP_RANDOM_SPREAD)
|
||||||
|
)
|
||||||
|
|
||||||
group.add_waypoint(self.conflict.to_cp.position)
|
group.add_waypoint(self.conflict.to_cp.position)
|
||||||
groups.append(group)
|
groups.append(group)
|
||||||
|
|
||||||
return groups
|
return groups
|
||||||
|
|||||||
@ -54,14 +54,12 @@ class TriggersGenerator:
|
|||||||
vehicle_group.late_activation = True
|
vehicle_group.late_activation = True
|
||||||
activate_by_trigger.append(vehicle_group)
|
activate_by_trigger.append(vehicle_group)
|
||||||
|
|
||||||
"""
|
|
||||||
conflict_distance = player_cp.position.distance_to_point(self.conflict.position)
|
conflict_distance = player_cp.position.distance_to_point(self.conflict.position)
|
||||||
minimum_radius = max(conflict_distance - TRIGGER_MIN_DISTANCE_FROM_START, TRIGGER_RADIUS_MINIMUM)
|
minimum_radius = max(conflict_distance - TRIGGER_MIN_DISTANCE_FROM_START, TRIGGER_RADIUS_MINIMUM)
|
||||||
if minimum_radius < 0:
|
if minimum_radius < 0:
|
||||||
minimum_radius = 0
|
minimum_radius = 0
|
||||||
|
|
||||||
result_radius = min(minimum_radius, radius)
|
radius = min(minimum_radius, radius)
|
||||||
"""
|
|
||||||
|
|
||||||
activation_trigger_zone = self.mission.triggers.add_triggerzone(self.conflict.position, radius, name="Activation zone")
|
activation_trigger_zone = self.mission.triggers.add_triggerzone(self.conflict.position, radius, name="Activation zone")
|
||||||
activation_trigger = TriggerOnce(Event.NoEvent, "Activation trigger")
|
activation_trigger = TriggerOnce(Event.NoEvent, "Activation trigger")
|
||||||
|
|||||||
@ -9,6 +9,7 @@ from dcs.unit import Static
|
|||||||
from theater import *
|
from theater import *
|
||||||
from .conflictgen import *
|
from .conflictgen import *
|
||||||
#from game.game import Game
|
#from game.game import Game
|
||||||
|
from game import db
|
||||||
|
|
||||||
|
|
||||||
class MarkerSmoke(unittype.StaticType):
|
class MarkerSmoke(unittype.StaticType):
|
||||||
@ -124,6 +125,17 @@ class VisualGenerator:
|
|||||||
position=pos)
|
position=pos)
|
||||||
break
|
break
|
||||||
|
|
||||||
|
def _generate_stub_planes(self):
|
||||||
|
mission_units = set()
|
||||||
|
for coalition_name, coalition in self.mission.coalition.items():
|
||||||
|
for country in coalition.countries.values():
|
||||||
|
for group in country.plane_group + country.helicopter_group + country.vehicle_group:
|
||||||
|
for unit in group.units:
|
||||||
|
mission_units.add(db.unit_type_of(unit))
|
||||||
|
|
||||||
|
for unit_type in mission_units:
|
||||||
|
self.mission.static_group(self.mission.country("USA"), "a", unit_type, Point(0, 300000), hidden=True)
|
||||||
|
|
||||||
def generate_target_smokes(self, target):
|
def generate_target_smokes(self, target):
|
||||||
spread = target.size * DESTINATION_SMOKE_DISTANCE_FACTOR
|
spread = target.size * DESTINATION_SMOKE_DISTANCE_FACTOR
|
||||||
for _ in range(0, int(target.size * DESTINATION_SMOKE_AMOUNT_FACTOR * (1.1 - target.base.strength))):
|
for _ in range(0, int(target.size * DESTINATION_SMOKE_AMOUNT_FACTOR * (1.1 - target.base.strength))):
|
||||||
@ -159,3 +171,4 @@ class VisualGenerator:
|
|||||||
|
|
||||||
def generate(self):
|
def generate(self):
|
||||||
self._generate_frontline_smokes()
|
self._generate_frontline_smokes()
|
||||||
|
self._generate_stub_planes()
|
||||||
|
|||||||
40
pyinstaller.spec
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
# -*- mode: python -*-
|
||||||
|
|
||||||
|
block_cipher = None
|
||||||
|
|
||||||
|
|
||||||
|
a = Analysis(['__init__.py'],
|
||||||
|
pathex=['C:\\Users\\shdwp\\PycharmProjects\\dcs_liberation'],
|
||||||
|
binaries=[],
|
||||||
|
datas=[
|
||||||
|
('resources', 'resources'),
|
||||||
|
('submodules/dcs/dcs/terrain/caucasus.p', 'dcs/terrain/'),
|
||||||
|
('submodules/dcs/dcs/terrain/nevada.p', 'dcs/terrain/'),
|
||||||
|
],
|
||||||
|
hookspath=[],
|
||||||
|
runtime_hooks=[],
|
||||||
|
excludes=[],
|
||||||
|
win_no_prefer_redirects=False,
|
||||||
|
win_private_assemblies=False,
|
||||||
|
cipher=block_cipher,
|
||||||
|
noarchive=False)
|
||||||
|
pyz = PYZ(a.pure, a.zipped_data,
|
||||||
|
cipher=block_cipher)
|
||||||
|
exe = EXE(pyz,
|
||||||
|
a.scripts,
|
||||||
|
[],
|
||||||
|
icon="resources/icon.ico",
|
||||||
|
exclude_binaries=True,
|
||||||
|
name='liberation_main',
|
||||||
|
debug=False,
|
||||||
|
bootloader_ignore_signals=False,
|
||||||
|
strip=False,
|
||||||
|
upx=True,
|
||||||
|
console=True )
|
||||||
|
coll = COLLECT(exe,
|
||||||
|
a.binaries,
|
||||||
|
a.zipfiles,
|
||||||
|
a.datas,
|
||||||
|
strip=False,
|
||||||
|
upx=True,
|
||||||
|
name='dcs_liberation')
|
||||||
|
Before Width: | Height: | Size: 157 KiB After Width: | Height: | Size: 247 KiB |
|
Before Width: | Height: | Size: 81 KiB After Width: | Height: | Size: 81 KiB |
|
Before Width: | Height: | Size: 117 KiB After Width: | Height: | Size: 467 KiB |
|
Before Width: | Height: | Size: 62 KiB After Width: | Height: | Size: 260 KiB |
@ -1,4 +1,5 @@
|
|||||||
import os
|
import os
|
||||||
|
import sys
|
||||||
import dcs
|
import dcs
|
||||||
|
|
||||||
from game import db
|
from game import db
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
import os
|
import os
|
||||||
|
import shutil
|
||||||
|
|
||||||
from zipfile import *
|
from zipfile import *
|
||||||
|
|
||||||
@ -42,11 +43,13 @@ def _mk_archieve():
|
|||||||
print("version already exists")
|
print("version already exists")
|
||||||
return
|
return
|
||||||
|
|
||||||
|
shutil.rmtree("./dist")
|
||||||
|
|
||||||
|
os.system("pyinstaller.exe pyinstaller.spec")
|
||||||
|
|
||||||
archieve = ZipFile(path, "w")
|
archieve = ZipFile(path, "w")
|
||||||
archieve.writestr("start.bat", "py.exe __init__.py \"%UserProfile%\\Saved Games\" \"{}\"".format(VERSION))
|
archieve.writestr("dcs_liberation.bat", "cd dist\\dcs_liberation\r\nliberation_main \"%UserProfile%\\Saved Games\" \"{}\"".format(VERSION))
|
||||||
_zip_dir(archieve, ".")
|
_zip_dir(archieve, "./dist/dcs_liberation")
|
||||||
os.chdir("submodules\\dcs")
|
|
||||||
_zip_dir(archieve, "dcs")
|
|
||||||
|
|
||||||
|
|
||||||
_mk_archieve()
|
_mk_archieve()
|
||||||
BIN
resources/ui/events/air_intercept.png
Normal file
|
After Width: | Height: | Size: 3.2 KiB |
BIN
resources/ui/events/attack.PNG
Normal file
|
After Width: | Height: | Size: 637 B |
BIN
resources/ui/events/capture.PNG
Normal file
|
After Width: | Height: | Size: 697 B |
BIN
resources/ui/events/convoy.png
Normal file
|
After Width: | Height: | Size: 804 B |
BIN
resources/ui/events/delivery.PNG
Normal file
|
After Width: | Height: | Size: 1.2 KiB |
BIN
resources/ui/events/infantry.PNG
Normal file
|
After Width: | Height: | Size: 910 B |
BIN
resources/ui/events/insurgent_attack.PNG
Normal file
|
After Width: | Height: | Size: 824 B |
BIN
resources/ui/events/naval_intercept.PNG
Normal file
|
After Width: | Height: | Size: 688 B |
BIN
resources/ui/events/strike.PNG
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
BIN
resources/ui/ground_assets/aa.png
Normal file
|
After Width: | Height: | Size: 229 B |
BIN
resources/ui/ground_assets/ammo.png
Normal file
|
After Width: | Height: | Size: 197 B |
BIN
resources/ui/ground_assets/cleared.png
Normal file
|
After Width: | Height: | Size: 315 B |
BIN
resources/ui/ground_assets/comms.png
Normal file
|
After Width: | Height: | Size: 196 B |
BIN
resources/ui/ground_assets/factory.png
Normal file
|
After Width: | Height: | Size: 220 B |
BIN
resources/ui/ground_assets/farp.png
Normal file
|
After Width: | Height: | Size: 253 B |
BIN
resources/ui/ground_assets/fob.png
Normal file
|
After Width: | Height: | Size: 213 B |
BIN
resources/ui/ground_assets/fuel.png
Normal file
|
After Width: | Height: | Size: 224 B |
BIN
resources/ui/ground_assets/oil.png
Normal file
|
After Width: | Height: | Size: 227 B |
BIN
resources/ui/ground_assets/power.png
Normal file
|
After Width: | Height: | Size: 235 B |
BIN
resources/ui/ground_assets/target.png
Normal file
|
After Width: | Height: | Size: 230 B |
BIN
resources/ui/ground_assets/warehouse.png
Normal file
|
After Width: | Height: | Size: 232 B |
@ -1 +1 @@
|
|||||||
Subproject commit 54eab60f228847f2d92e344e6885eca855354f41
|
Subproject commit 4fbb7ad3e0e2eecedc4e1dd14f2eb18025fef9f5
|
||||||
0
tests/__init__.py
Normal file
10
tests/integration/__init__.py
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
from tests.integration import baseattack, convoystrike, frontlineattack, insurgentattack, intercept, navalintercept, strike
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
baseattack.execute_all()
|
||||||
|
convoystrike.execute_all()
|
||||||
|
frontlineattack.execute_all()
|
||||||
|
insurgentattack.execute_all()
|
||||||
|
intercept.execute_all()
|
||||||
|
navalintercept.execute_all()
|
||||||
|
strike.execute_all()
|
||||||
46
tests/integration/baseattack.py
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
from theater.caucasus import CaucasusTheater
|
||||||
|
from theater.nevada import NevadaTheater
|
||||||
|
|
||||||
|
from tests.integration.util import *
|
||||||
|
|
||||||
|
PLAYER_COUNTRY = "USA"
|
||||||
|
ENEMY_COUNTRY = "Russia"
|
||||||
|
|
||||||
|
|
||||||
|
def execute(game, player_cp, enemy_cp, departure_cp = None):
|
||||||
|
e = BaseAttackEvent(game, player_cp, enemy_cp, enemy_cp.position, PLAYER_COUNTRY, ENEMY_COUNTRY)
|
||||||
|
|
||||||
|
departures = [departure_cp] if departure_cp else game.theater.player_points()
|
||||||
|
for departure_cp in departures:
|
||||||
|
if e.is_departure_available_from(departure_cp):
|
||||||
|
print("{} for {} ({}) - {}".format(e, player_cp, departure_cp, enemy_cp))
|
||||||
|
e.departure_cp = departure_cp
|
||||||
|
e.player_attacking(autoflights_for(e, PLAYER_COUNTRY))
|
||||||
|
|
||||||
|
e.generate()
|
||||||
|
execute_autocommit(e)
|
||||||
|
e.generate_quick()
|
||||||
|
execute_autocommit(e)
|
||||||
|
|
||||||
|
|
||||||
|
def execute_theater(theater_klass):
|
||||||
|
print("Theater: {}".format(theater_klass))
|
||||||
|
game, theater = init(PLAYER_COUNTRY, ENEMY_COUNTRY, theater_klass)
|
||||||
|
|
||||||
|
total_events = 0
|
||||||
|
while len(theater.enemy_points()) > 0:
|
||||||
|
for player_cp, enemy_cp in theater.conflicts():
|
||||||
|
execute(game, player_cp, enemy_cp)
|
||||||
|
|
||||||
|
enemy_cp.captured = True
|
||||||
|
|
||||||
|
print("Total: {}".format(total_events))
|
||||||
|
|
||||||
|
|
||||||
|
def execute_all():
|
||||||
|
for theater_klass in [CaucasusTheater, PersianGulfTheater, NevadaTheater]:
|
||||||
|
execute_theater(theater_klass)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
execute_all()
|
||||||
50
tests/integration/convoystrike.py
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
from theater.caucasus import CaucasusTheater
|
||||||
|
from theater.nevada import NevadaTheater
|
||||||
|
|
||||||
|
from tests.integration.util import *
|
||||||
|
|
||||||
|
PLAYER_COUNTRY = "USA"
|
||||||
|
ENEMY_COUNTRY = "Russia"
|
||||||
|
|
||||||
|
|
||||||
|
def execute(game, player_cp, enemy_cp, departure_cp = None):
|
||||||
|
e = ConvoyStrikeEvent(game, player_cp, enemy_cp, enemy_cp.position, PLAYER_COUNTRY, ENEMY_COUNTRY)
|
||||||
|
|
||||||
|
departures = [departure_cp] if departure_cp else game.theater.player_points()
|
||||||
|
for departure_cp in departures:
|
||||||
|
if e.is_departure_available_from(departure_cp):
|
||||||
|
enemy_cp.base.strength = 1
|
||||||
|
for _ in range(10):
|
||||||
|
print("{} for {} ({}) - {} ({})".format(e, player_cp, departure_cp, enemy_cp, enemy_cp.base.strength))
|
||||||
|
e.departure_cp = departure_cp
|
||||||
|
e.player_attacking(autoflights_for(e, PLAYER_COUNTRY))
|
||||||
|
|
||||||
|
e.generate()
|
||||||
|
execute_autocommit(e)
|
||||||
|
e.generate_quick()
|
||||||
|
execute_autocommit(e)
|
||||||
|
|
||||||
|
enemy_cp.base.affect_strength(-0.1)
|
||||||
|
|
||||||
|
|
||||||
|
def execute_theater(theater_klass):
|
||||||
|
print("Theater: {}".format(theater_klass))
|
||||||
|
game, theater = init(PLAYER_COUNTRY, ENEMY_COUNTRY, theater_klass)
|
||||||
|
|
||||||
|
total_events = 0
|
||||||
|
while len(theater.enemy_points()) > 0:
|
||||||
|
for player_cp, enemy_cp in theater.conflicts():
|
||||||
|
execute(game, player_cp, enemy_cp)
|
||||||
|
|
||||||
|
enemy_cp.captured = True
|
||||||
|
|
||||||
|
print("Total: {}".format(total_events))
|
||||||
|
|
||||||
|
|
||||||
|
def execute_all():
|
||||||
|
for theater_klass in [CaucasusTheater, PersianGulfTheater, NevadaTheater]:
|
||||||
|
execute_theater(theater_klass)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
execute_all()
|
||||||
52
tests/integration/frontlineattack.py
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
from theater.caucasus import CaucasusTheater
|
||||||
|
from theater.nevada import NevadaTheater
|
||||||
|
|
||||||
|
from game.event.frontlineattack import FrontlineAttackEvent
|
||||||
|
|
||||||
|
from tests.integration.util import *
|
||||||
|
|
||||||
|
PLAYER_COUNTRY = "USA"
|
||||||
|
ENEMY_COUNTRY = "Russia"
|
||||||
|
|
||||||
|
|
||||||
|
def execute(game, player_cp, enemy_cp, departure_cp = None):
|
||||||
|
e = FrontlineAttackEvent(game, player_cp, enemy_cp, enemy_cp.position, PLAYER_COUNTRY, ENEMY_COUNTRY)
|
||||||
|
|
||||||
|
departures = [departure_cp] if departure_cp else game.theater.player_points()
|
||||||
|
for departure_cp in departures:
|
||||||
|
if e.is_departure_available_from(departure_cp):
|
||||||
|
enemy_cp.base.strength = 1
|
||||||
|
for _ in range(10):
|
||||||
|
print("{} for {} ({}) - {} ({})".format(e, player_cp, departure_cp, enemy_cp, enemy_cp.base.strength))
|
||||||
|
e.departure_cp = departure_cp
|
||||||
|
e.player_attacking(autoflights_for(e, PLAYER_COUNTRY))
|
||||||
|
|
||||||
|
e.generate()
|
||||||
|
execute_autocommit(e)
|
||||||
|
e.generate_quick()
|
||||||
|
execute_autocommit(e)
|
||||||
|
|
||||||
|
enemy_cp.base.affect_strength(-0.1)
|
||||||
|
|
||||||
|
|
||||||
|
def execute_theater(theater_klass):
|
||||||
|
print("Theater: {}".format(theater_klass))
|
||||||
|
game, theater = init(PLAYER_COUNTRY, ENEMY_COUNTRY, theater_klass)
|
||||||
|
|
||||||
|
total_events = 0
|
||||||
|
while len(theater.enemy_points()) > 0:
|
||||||
|
for player_cp, enemy_cp in theater.conflicts():
|
||||||
|
execute(game, player_cp, enemy_cp)
|
||||||
|
|
||||||
|
enemy_cp.captured = True
|
||||||
|
|
||||||
|
print("Total: {}".format(total_events))
|
||||||
|
|
||||||
|
|
||||||
|
def execute_all():
|
||||||
|
for theater_klass in [CaucasusTheater, PersianGulfTheater, NevadaTheater]:
|
||||||
|
execute_theater(theater_klass)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
execute_all()
|
||||||
48
tests/integration/insurgentattack.py
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
from theater.caucasus import CaucasusTheater
|
||||||
|
from theater.nevada import NevadaTheater
|
||||||
|
|
||||||
|
from game.event.insurgentattack import InsurgentAttackEvent
|
||||||
|
|
||||||
|
from tests.integration.util import *
|
||||||
|
|
||||||
|
PLAYER_COUNTRY = "USA"
|
||||||
|
ENEMY_COUNTRY = "Russia"
|
||||||
|
|
||||||
|
|
||||||
|
def execute(game, player_cp, enemy_cp, departure_cp = None):
|
||||||
|
e = InsurgentAttackEvent(game, enemy_cp, player_cp, player_cp.position, ENEMY_COUNTRY, PLAYER_COUNTRY)
|
||||||
|
|
||||||
|
departures = [departure_cp] if departure_cp else game.theater.player_points()
|
||||||
|
for departure_cp in departures:
|
||||||
|
if e.is_departure_available_from(departure_cp):
|
||||||
|
print("{} for {} ({}) - {} ({})".format(e, player_cp, departure_cp, enemy_cp, enemy_cp.base.strength))
|
||||||
|
e.departure_cp = departure_cp
|
||||||
|
e.player_defending(autoflights_for(e, PLAYER_COUNTRY))
|
||||||
|
|
||||||
|
e.generate()
|
||||||
|
execute_autocommit(e)
|
||||||
|
e.generate_quick()
|
||||||
|
execute_autocommit(e)
|
||||||
|
|
||||||
|
|
||||||
|
def execute_theater(theater_klass):
|
||||||
|
print("Theater: {}".format(theater_klass))
|
||||||
|
game, theater = init(PLAYER_COUNTRY, ENEMY_COUNTRY, theater_klass)
|
||||||
|
|
||||||
|
total_events = 0
|
||||||
|
while len(theater.enemy_points()) > 0:
|
||||||
|
for player_cp, enemy_cp in theater.conflicts():
|
||||||
|
execute(game, player_cp, enemy_cp)
|
||||||
|
|
||||||
|
enemy_cp.captured = True
|
||||||
|
|
||||||
|
print("Total: {}".format(total_events))
|
||||||
|
|
||||||
|
|
||||||
|
def execute_all():
|
||||||
|
for theater_klass in [CaucasusTheater, PersianGulfTheater, NevadaTheater]:
|
||||||
|
execute_theater(theater_klass)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
execute_all()
|
||||||
48
tests/integration/intercept.py
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
from theater.caucasus import CaucasusTheater
|
||||||
|
from theater.nevada import NevadaTheater
|
||||||
|
|
||||||
|
from game.event.intercept import InterceptEvent
|
||||||
|
|
||||||
|
from tests.integration.util import *
|
||||||
|
|
||||||
|
PLAYER_COUNTRY = "USA"
|
||||||
|
ENEMY_COUNTRY = "Russia"
|
||||||
|
|
||||||
|
|
||||||
|
def execute(game, player_cp, enemy_cp, departure_cp = None):
|
||||||
|
e = InterceptEvent(game, player_cp, enemy_cp, enemy_cp.position, PLAYER_COUNTRY, ENEMY_COUNTRY)
|
||||||
|
|
||||||
|
departures = [departure_cp] if departure_cp else game.theater.player_points()
|
||||||
|
for departure_cp in departures:
|
||||||
|
if e.is_departure_available_from(departure_cp):
|
||||||
|
print("{} for {} ({}) - {} ({})".format(e, player_cp, departure_cp, enemy_cp, enemy_cp.base.strength))
|
||||||
|
e.departure_cp = departure_cp
|
||||||
|
e.player_attacking(autoflights_for(e, PLAYER_COUNTRY))
|
||||||
|
|
||||||
|
e.generate()
|
||||||
|
execute_autocommit(e)
|
||||||
|
e.generate_quick()
|
||||||
|
execute_autocommit(e)
|
||||||
|
|
||||||
|
|
||||||
|
def execute_theater(theater_klass):
|
||||||
|
print("Theater: {}".format(theater_klass))
|
||||||
|
game, theater = init(PLAYER_COUNTRY, ENEMY_COUNTRY, theater_klass)
|
||||||
|
|
||||||
|
total_events = 0
|
||||||
|
while len(theater.enemy_points()) > 0:
|
||||||
|
for player_cp, enemy_cp in theater.conflicts():
|
||||||
|
execute(game, player_cp, enemy_cp)
|
||||||
|
|
||||||
|
enemy_cp.captured = True
|
||||||
|
|
||||||
|
print("Total: {}".format(total_events))
|
||||||
|
|
||||||
|
|
||||||
|
def execute_all():
|
||||||
|
for theater_klass in [CaucasusTheater, PersianGulfTheater, NevadaTheater]:
|
||||||
|
execute_theater(theater_klass)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
execute_all()
|
||||||
49
tests/integration/navalintercept.py
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
from theater.caucasus import CaucasusTheater
|
||||||
|
from theater.nevada import NevadaTheater
|
||||||
|
|
||||||
|
from game.event.intercept import InterceptEvent
|
||||||
|
|
||||||
|
from tests.integration.util import *
|
||||||
|
|
||||||
|
PLAYER_COUNTRY = "USA"
|
||||||
|
ENEMY_COUNTRY = "Russia"
|
||||||
|
|
||||||
|
|
||||||
|
def execute(game, player_cp, enemy_cp, departure_cp = None):
|
||||||
|
e = NavalInterceptEvent(game, player_cp, enemy_cp, enemy_cp.position, PLAYER_COUNTRY, ENEMY_COUNTRY)
|
||||||
|
|
||||||
|
departures = [departure_cp] if departure_cp else game.theater.player_points()
|
||||||
|
for departure_cp in departures:
|
||||||
|
if e.is_departure_available_from(departure_cp):
|
||||||
|
print("{} for {} ({}) - {} ({})".format(e, player_cp, departure_cp, enemy_cp, enemy_cp.base.strength))
|
||||||
|
e.departure_cp = departure_cp
|
||||||
|
e.player_attacking(autoflights_for(e, PLAYER_COUNTRY))
|
||||||
|
|
||||||
|
e.generate()
|
||||||
|
execute_autocommit(e)
|
||||||
|
e.generate_quick()
|
||||||
|
execute_autocommit(e)
|
||||||
|
|
||||||
|
|
||||||
|
def execute_theater(theater_klass):
|
||||||
|
print("Theater: {}".format(theater_klass))
|
||||||
|
game, theater = init(PLAYER_COUNTRY, ENEMY_COUNTRY, theater_klass)
|
||||||
|
|
||||||
|
total_events = 0
|
||||||
|
while len(theater.enemy_points()) > 0:
|
||||||
|
for player_cp, enemy_cp in theater.conflicts():
|
||||||
|
if enemy_cp.radials != LAND:
|
||||||
|
execute(game, player_cp, enemy_cp)
|
||||||
|
|
||||||
|
enemy_cp.captured = True
|
||||||
|
|
||||||
|
print("Total: {}".format(total_events))
|
||||||
|
|
||||||
|
|
||||||
|
def execute_all():
|
||||||
|
for theater_klass in [CaucasusTheater, PersianGulfTheater, NevadaTheater]:
|
||||||
|
execute_theater(theater_klass)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
execute_all()
|
||||||
48
tests/integration/strike.py
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
from theater.caucasus import CaucasusTheater
|
||||||
|
from theater.nevada import NevadaTheater
|
||||||
|
|
||||||
|
from game.event.intercept import InterceptEvent
|
||||||
|
|
||||||
|
from tests.integration.util import *
|
||||||
|
|
||||||
|
PLAYER_COUNTRY = "USA"
|
||||||
|
ENEMY_COUNTRY = "Russia"
|
||||||
|
|
||||||
|
|
||||||
|
def execute(game, player_cp, enemy_cp, departure_cp = None):
|
||||||
|
e = StrikeEvent(game, player_cp, enemy_cp, enemy_cp.position, PLAYER_COUNTRY, ENEMY_COUNTRY)
|
||||||
|
|
||||||
|
departures = [departure_cp] if departure_cp else game.theater.player_points()
|
||||||
|
for departure_cp in departures:
|
||||||
|
if e.is_departure_available_from(departure_cp):
|
||||||
|
print("{} for {} ({}) - {} ({})".format(e, player_cp, departure_cp, enemy_cp, enemy_cp.base.strength))
|
||||||
|
e.departure_cp = departure_cp
|
||||||
|
e.player_attacking(autoflights_for(e, PLAYER_COUNTRY))
|
||||||
|
|
||||||
|
e.generate()
|
||||||
|
execute_autocommit(e)
|
||||||
|
e.generate_quick()
|
||||||
|
execute_autocommit(e)
|
||||||
|
|
||||||
|
|
||||||
|
def execute_theater(theater_klass):
|
||||||
|
print("Theater: {}".format(theater_klass))
|
||||||
|
game, theater = init(PLAYER_COUNTRY, ENEMY_COUNTRY, theater_klass)
|
||||||
|
|
||||||
|
total_events = 0
|
||||||
|
while len(theater.enemy_points()) > 0:
|
||||||
|
for player_cp, enemy_cp in theater.conflicts():
|
||||||
|
execute(game, player_cp, enemy_cp)
|
||||||
|
|
||||||
|
enemy_cp.captured = True
|
||||||
|
|
||||||
|
print("Total: {}".format(total_events))
|
||||||
|
|
||||||
|
|
||||||
|
def execute_all():
|
||||||
|
for theater_klass in [CaucasusTheater, PersianGulfTheater, NevadaTheater]:
|
||||||
|
execute_theater(theater_klass)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
execute_all()
|
||||||
80
tests/integration/util.py
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
from dcs.mission import Mission
|
||||||
|
|
||||||
|
from game import *
|
||||||
|
from game.event import *
|
||||||
|
from game.db import *
|
||||||
|
|
||||||
|
from theater.persiangulf import *
|
||||||
|
from theater import start_generator
|
||||||
|
|
||||||
|
PLAYER_COUNTRY = None
|
||||||
|
ENEMY_COUNTRY = None
|
||||||
|
|
||||||
|
|
||||||
|
def init(player_country: str, enemy_country: str, theater_klass: typing.Type[ConflictTheater]) -> typing.Tuple[Game, ConflictTheater]:
|
||||||
|
global PLAYER_COUNTRY
|
||||||
|
global ENEMY_COUNTRY
|
||||||
|
|
||||||
|
PLAYER_COUNTRY = player_country
|
||||||
|
ENEMY_COUNTRY = enemy_country
|
||||||
|
|
||||||
|
# prerequisites
|
||||||
|
persistency.setup("./tests/userfolder/")
|
||||||
|
theater = theater_klass()
|
||||||
|
start_generator.generate_inital_units(theater, ENEMY_COUNTRY, True, 1)
|
||||||
|
start_generator.generate_groundobjects(theater)
|
||||||
|
return Game(PLAYER_COUNTRY, ENEMY_COUNTRY, theater), theater
|
||||||
|
|
||||||
|
|
||||||
|
def autoflights_for(event: Event, country: str) -> TaskForceDict:
|
||||||
|
result = {}
|
||||||
|
for task in event.tasks:
|
||||||
|
result[task] = {find_unittype(task, country)[0]: (1, 1)}
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
class AutodebriefType(Enum):
|
||||||
|
EVERYONE_DEAD = 0
|
||||||
|
PLAYER_DEAD = 1
|
||||||
|
ENEMY_DEAD = 2
|
||||||
|
|
||||||
|
|
||||||
|
def autodebrief_for(event: Event, type: AutodebriefType) -> Debriefing:
|
||||||
|
mission = event.operation.current_mission # type: Mission
|
||||||
|
|
||||||
|
countries = []
|
||||||
|
if type == AutodebriefType.PLAYER_DEAD or type == AutodebriefType.EVERYONE_DEAD:
|
||||||
|
countries.append(mission.country(PLAYER_COUNTRY))
|
||||||
|
|
||||||
|
if type == AutodebriefType.ENEMY_DEAD or type == AutodebriefType.EVERYONE_DEAD:
|
||||||
|
countries.append(mission.country(ENEMY_COUNTRY))
|
||||||
|
|
||||||
|
dead_units = []
|
||||||
|
for country in countries:
|
||||||
|
for group in country.plane_group + country.vehicle_group + country.helicopter_group:
|
||||||
|
for unit in group.units:
|
||||||
|
dead_units.append(str(unit.name))
|
||||||
|
|
||||||
|
return Debriefing(dead_units, [])
|
||||||
|
|
||||||
|
|
||||||
|
def event_state_save(e: Event) -> typing.Tuple[Base, Base]:
|
||||||
|
return (copy.deepcopy(e.from_cp.base), copy.deepcopy(e.to_cp.base))
|
||||||
|
|
||||||
|
|
||||||
|
def event_state_restore(e: Event, state: typing.Tuple[Base, Base]):
|
||||||
|
e.from_cp.base, e.to_cp.base = state[0], state[1]
|
||||||
|
|
||||||
|
|
||||||
|
def execute_autocommit(e: Event):
|
||||||
|
state = event_state_save(e)
|
||||||
|
e.commit(autodebrief_for(e, AutodebriefType.EVERYONE_DEAD))
|
||||||
|
event_state_restore(e, state)
|
||||||
|
|
||||||
|
state = event_state_save(e)
|
||||||
|
e.commit(autodebrief_for(e, AutodebriefType.PLAYER_DEAD))
|
||||||
|
event_state_restore(e, state)
|
||||||
|
|
||||||
|
state = event_state_save(e)
|
||||||
|
e.commit(autodebrief_for(e, AutodebriefType.ENEMY_DEAD))
|
||||||
|
event_state_restore(e, state)
|
||||||
0
tests/userfolder/DCS/Missions/empty.txt
Normal file
@ -56,7 +56,7 @@ class Base:
|
|||||||
|
|
||||||
def _find_best_unit(self, dict, for_type: Task, count: int) -> typing.Dict:
|
def _find_best_unit(self, dict, for_type: Task, count: int) -> typing.Dict:
|
||||||
if count <= 0:
|
if count <= 0:
|
||||||
logging.info("{}: no units for {}".format(self, for_type))
|
logging.warning("{}: no units for {}".format(self, for_type))
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
sorted_units = [key for key in dict.keys() if key in db.UNIT_BY_TASK[for_type]]
|
sorted_units = [key for key in dict.keys() if key in db.UNIT_BY_TASK[for_type]]
|
||||||
|
|||||||
@ -11,8 +11,8 @@ from .base import *
|
|||||||
class CaucasusTheater(ConflictTheater):
|
class CaucasusTheater(ConflictTheater):
|
||||||
terrain = caucasus.Caucasus()
|
terrain = caucasus.Caucasus()
|
||||||
overview_image = "caumap.gif"
|
overview_image = "caumap.gif"
|
||||||
reference_points = {(-317948.32727306, 635639.37385346): (278.5, 319),
|
reference_points = {(-317948.32727306, 635639.37385346): (278.5*2, 319*2),
|
||||||
(-355692.3067714, 617269.96285781): (263, 352), }
|
(-355692.3067714, 617269.96285781): (263*2, 352*2), }
|
||||||
landmap = load_landmap("resources\\caulandmap.p")
|
landmap = load_landmap("resources\\caulandmap.p")
|
||||||
daytime_map = {
|
daytime_map = {
|
||||||
"dawn": (6, 9),
|
"dawn": (6, 9),
|
||||||
|
|||||||
@ -18,8 +18,6 @@ IMPORTANCE_LOW = 1
|
|||||||
IMPORTANCE_MEDIUM = 1.2
|
IMPORTANCE_MEDIUM = 1.2
|
||||||
IMPORTANCE_HIGH = 1.4
|
IMPORTANCE_HIGH = 1.4
|
||||||
|
|
||||||
GLOBAL_CP_CONFLICT_DISTANCE_MIN = 340000
|
|
||||||
|
|
||||||
"""
|
"""
|
||||||
ALL_RADIALS = [0, 45, 90, 135, 180, 225, 270, 315, ]
|
ALL_RADIALS = [0, 45, 90, 135, 180, 225, 270, 315, ]
|
||||||
COAST_NS_E = [45, 90, 135, ]
|
COAST_NS_E = [45, 90, 135, ]
|
||||||
@ -55,10 +53,18 @@ class ConflictTheater:
|
|||||||
reference_points = None # type: typing.Dict
|
reference_points = None # type: typing.Dict
|
||||||
overview_image = None # type: str
|
overview_image = None # type: str
|
||||||
landmap = None # type: landmap.Landmap
|
landmap = None # type: landmap.Landmap
|
||||||
|
"""
|
||||||
|
land_poly = None # type: Polygon
|
||||||
|
"""
|
||||||
daytime_map = None # type: typing.Dict[str, typing.Tuple[int, int]]
|
daytime_map = None # type: typing.Dict[str, typing.Tuple[int, int]]
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.controlpoints = []
|
self.controlpoints = []
|
||||||
|
"""
|
||||||
|
self.land_poly = geometry.Polygon(self.landmap[0][0])
|
||||||
|
for x in self.landmap[1]:
|
||||||
|
self.land_poly = self.land_poly.difference(geometry.Polygon(x))
|
||||||
|
"""
|
||||||
|
|
||||||
def add_controlpoint(self, point: ControlPoint, connected_to: typing.Collection[ControlPoint] = []):
|
def add_controlpoint(self, point: ControlPoint, connected_to: typing.Collection[ControlPoint] = []):
|
||||||
for connected_point in connected_to:
|
for connected_point in connected_to:
|
||||||
@ -102,9 +108,5 @@ class ConflictTheater:
|
|||||||
for connected_point in [x for x in cp.connected_points if x.captured != from_player]:
|
for connected_point in [x for x in cp.connected_points if x.captured != from_player]:
|
||||||
yield (cp, connected_point)
|
yield (cp, connected_point)
|
||||||
|
|
||||||
for global_cp in [x for x in self.controlpoints if x.is_global and x.captured == from_player]:
|
|
||||||
if global_cp.position.distance_to_point(connected_point.position) < GLOBAL_CP_CONFLICT_DISTANCE_MIN:
|
|
||||||
yield (global_cp, connected_point)
|
|
||||||
|
|
||||||
def enemy_points(self) -> typing.Collection[ControlPoint]:
|
def enemy_points(self) -> typing.Collection[ControlPoint]:
|
||||||
return [point for point in self.controlpoints if not point.captured]
|
return [point for point in self.controlpoints if not point.captured]
|
||||||
|
|||||||
@ -9,8 +9,8 @@ from .base import *
|
|||||||
class NevadaTheater(ConflictTheater):
|
class NevadaTheater(ConflictTheater):
|
||||||
terrain = dcs.terrain.Nevada()
|
terrain = dcs.terrain.Nevada()
|
||||||
overview_image = "nevada.gif"
|
overview_image = "nevada.gif"
|
||||||
reference_points = {(nevada.Mina_Airport_3Q0.position.x, nevada.Mina_Airport_3Q0.position.y): (45, -360),
|
reference_points = {(nevada.Mina_Airport_3Q0.position.x, nevada.Mina_Airport_3Q0.position.y): (45*2, -360*2),
|
||||||
(nevada.Laughlin_Airport.position.x, nevada.Laughlin_Airport.position.y): (440, 80), }
|
(nevada.Laughlin_Airport.position.x, nevada.Laughlin_Airport.position.y): (440*2, 80*2), }
|
||||||
landmap = load_landmap("resources\\nev_landmap.p")
|
landmap = load_landmap("resources\\nev_landmap.p")
|
||||||
daytime_map = {
|
daytime_map = {
|
||||||
"dawn": (4, 6),
|
"dawn": (4, 6),
|
||||||
@ -19,7 +19,6 @@ class NevadaTheater(ConflictTheater):
|
|||||||
"night": (0, 5),
|
"night": (0, 5),
|
||||||
}
|
}
|
||||||
|
|
||||||
mina = ControlPoint.from_airport(nevada.Mina_Airport_3Q0, LAND, SIZE_SMALL, IMPORTANCE_LOW)
|
|
||||||
tonopah = ControlPoint.from_airport(nevada.Tonopah_Airport, LAND, SIZE_SMALL, IMPORTANCE_LOW)
|
tonopah = ControlPoint.from_airport(nevada.Tonopah_Airport, LAND, SIZE_SMALL, IMPORTANCE_LOW)
|
||||||
tonopah_test_range = ControlPoint.from_airport(nevada.Tonopah_Test_Range_Airfield, LAND, SIZE_SMALL, IMPORTANCE_LOW)
|
tonopah_test_range = ControlPoint.from_airport(nevada.Tonopah_Test_Range_Airfield, LAND, SIZE_SMALL, IMPORTANCE_LOW)
|
||||||
lincoln_conty = ControlPoint.from_airport(nevada.Lincoln_County, LAND, SIZE_SMALL, 1.2)
|
lincoln_conty = ControlPoint.from_airport(nevada.Lincoln_County, LAND, SIZE_SMALL, 1.2)
|
||||||
@ -37,8 +36,7 @@ class NevadaTheater(ConflictTheater):
|
|||||||
def __init__(self):
|
def __init__(self):
|
||||||
super(NevadaTheater, self).__init__()
|
super(NevadaTheater, self).__init__()
|
||||||
|
|
||||||
self.add_controlpoint(self.mina, connected_to=[self.tonopah])
|
self.add_controlpoint(self.tonopah, connected_to=[self.tonopah_test_range, self.lincoln_conty])
|
||||||
self.add_controlpoint(self.tonopah, connected_to=[self.mina, self.tonopah_test_range, self.lincoln_conty])
|
|
||||||
self.add_controlpoint(self.tonopah_test_range, connected_to=[self.tonopah, self.lincoln_conty, self.groom_lake, self.pahute_mesa])
|
self.add_controlpoint(self.tonopah_test_range, connected_to=[self.tonopah, self.lincoln_conty, self.groom_lake, self.pahute_mesa])
|
||||||
self.add_controlpoint(self.lincoln_conty, connected_to=[self.tonopah_test_range, self.mesquite])
|
self.add_controlpoint(self.lincoln_conty, connected_to=[self.tonopah_test_range, self.mesquite])
|
||||||
|
|
||||||
@ -52,5 +50,5 @@ class NevadaTheater(ConflictTheater):
|
|||||||
self.add_controlpoint(self.jean, connected_to=[self.laughlin, self.las_vegas])
|
self.add_controlpoint(self.jean, connected_to=[self.laughlin, self.las_vegas])
|
||||||
self.add_controlpoint(self.laughlin, connected_to=[self.jean, self.las_vegas])
|
self.add_controlpoint(self.laughlin, connected_to=[self.jean, self.las_vegas])
|
||||||
|
|
||||||
self.mina.captured = True
|
self.tonopah.captured = True
|
||||||
|
|
||||||
|
|||||||
@ -9,8 +9,8 @@ from .landmap import load_landmap
|
|||||||
class PersianGulfTheater(ConflictTheater):
|
class PersianGulfTheater(ConflictTheater):
|
||||||
terrain = dcs.terrain.PersianGulf()
|
terrain = dcs.terrain.PersianGulf()
|
||||||
overview_image = "persiangulf.gif"
|
overview_image = "persiangulf.gif"
|
||||||
reference_points = {(persiangulf.Sir_Abu_Nuayr.position.x, persiangulf.Sir_Abu_Nuayr.position.y): (321, 145),
|
reference_points = {(persiangulf.Sir_Abu_Nuayr.position.x, persiangulf.Sir_Abu_Nuayr.position.y): (321*4, 145*4),
|
||||||
(persiangulf.Sirri_Island.position.x, persiangulf.Sirri_Island.position.y): (347, 82), }
|
(persiangulf.Sirri_Island.position.x, persiangulf.Sirri_Island.position.y): (347*4, 82*4), }
|
||||||
landmap = load_landmap("resources\\gulflandmap.p")
|
landmap = load_landmap("resources\\gulflandmap.p")
|
||||||
daytime_map = {
|
daytime_map = {
|
||||||
"dawn": (6, 8),
|
"dawn": (6, 8),
|
||||||
|
|||||||
@ -72,10 +72,20 @@ def generate_groundobjects(theater: ConflictTheater):
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
group_id = 0
|
group_id = 0
|
||||||
for cp in theater.enemy_points():
|
for cp in theater.controlpoints:
|
||||||
for _ in range(0, random.randrange(2, 4)):
|
if cp.is_global:
|
||||||
available_categories = list(tpls) + ["aa", "aa"]
|
continue
|
||||||
tpl_category = random.choice(available_categories)
|
|
||||||
|
if not cp.has_frontline:
|
||||||
|
continue
|
||||||
|
|
||||||
|
amount = random.randrange(5, 7)
|
||||||
|
for i in range(0, amount):
|
||||||
|
available_categories = list(tpls)
|
||||||
|
if i >= amount - 1:
|
||||||
|
tpl_category = "aa"
|
||||||
|
else:
|
||||||
|
tpl_category = random.choice(available_categories)
|
||||||
|
|
||||||
tpl = random.choice(list(tpls[tpl_category].values()))
|
tpl = random.choice(list(tpls[tpl_category].values()))
|
||||||
|
|
||||||
@ -85,13 +95,6 @@ def generate_groundobjects(theater: ConflictTheater):
|
|||||||
print("Couldn't find point for {}".format(cp))
|
print("Couldn't find point for {}".format(cp))
|
||||||
continue
|
continue
|
||||||
|
|
||||||
"""
|
|
||||||
dist = point.distance_to_point(cp.position) - 15000
|
|
||||||
for another_cp in theater.enemy_points():
|
|
||||||
if another_cp.position.distance_to_point(point) < dist:
|
|
||||||
cp = another_cp
|
|
||||||
"""
|
|
||||||
|
|
||||||
group_id += 1
|
group_id += 1
|
||||||
object_id = 0
|
object_id = 0
|
||||||
|
|
||||||
|
|||||||
@ -21,6 +21,12 @@ class ConfigurationMenu(Menu):
|
|||||||
self.enemy_vehicle_var = StringVar()
|
self.enemy_vehicle_var = StringVar()
|
||||||
self.enemy_vehicle_var.set(self.game.settings.enemy_vehicle_skill)
|
self.enemy_vehicle_var.set(self.game.settings.enemy_vehicle_skill)
|
||||||
|
|
||||||
|
self.map_coalition_visibility_var = StringVar()
|
||||||
|
self.map_coalition_visibility_var.set(self.game.settings.map_coalition_visibility)
|
||||||
|
|
||||||
|
self.labels_var = StringVar()
|
||||||
|
self.labels_var.set(self.game.settings.labels)
|
||||||
|
|
||||||
self.takeoff_var = BooleanVar()
|
self.takeoff_var = BooleanVar()
|
||||||
self.takeoff_var.set(self.game.settings.only_player_takeoff)
|
self.takeoff_var.set(self.game.settings.only_player_takeoff)
|
||||||
|
|
||||||
@ -34,6 +40,8 @@ class ConfigurationMenu(Menu):
|
|||||||
self.game.settings.player_skill = self.player_skill_var.get()
|
self.game.settings.player_skill = self.player_skill_var.get()
|
||||||
self.game.settings.enemy_skill = self.enemy_skill_var.get()
|
self.game.settings.enemy_skill = self.enemy_skill_var.get()
|
||||||
self.game.settings.enemy_vehicle_skill = self.enemy_vehicle_var.get()
|
self.game.settings.enemy_vehicle_skill = self.enemy_vehicle_var.get()
|
||||||
|
self.game.settings.map_coalition_visibility = self.map_coalition_visibility_var.get()
|
||||||
|
self.game.settings.labels = self.labels_var.get()
|
||||||
self.game.settings.only_player_takeoff = self.takeoff_var.get()
|
self.game.settings.only_player_takeoff = self.takeoff_var.get()
|
||||||
self.game.settings.night_disabled = self.night_var.get()
|
self.game.settings.night_disabled = self.night_var.get()
|
||||||
self.game.settings.cold_start = self.cold_start_var.get()
|
self.game.settings.cold_start = self.cold_start_var.get()
|
||||||
@ -72,6 +80,18 @@ class ConfigurationMenu(Menu):
|
|||||||
e_skill.configure(**STYLES["btn-primary"])
|
e_skill.configure(**STYLES["btn-primary"])
|
||||||
row += 1
|
row += 1
|
||||||
|
|
||||||
|
Label(body, text="F10 Map Coalition Visibility", **STYLES["widget"]).grid(row=row, column=0, sticky=W)
|
||||||
|
map_vis = OptionMenu(body, self.map_coalition_visibility_var, "All Units", "Allied Units", "Own Aircraft", "None")
|
||||||
|
map_vis.grid(row=row, column=1, sticky=E)
|
||||||
|
map_vis.configure(**STYLES["btn-primary"])
|
||||||
|
row += 1
|
||||||
|
|
||||||
|
Label(body, text="In Game Labels", **STYLES["widget"]).grid(row=row, column=0, sticky=W)
|
||||||
|
g_labels = OptionMenu(body, self.labels_var, "Full", "Abbreviated", "Dot Only", "Off")
|
||||||
|
g_labels.grid(row=row, column=1, sticky=E)
|
||||||
|
g_labels.configure(**STYLES["btn-primary"])
|
||||||
|
row += 1
|
||||||
|
|
||||||
Label(body, text="Aircraft cold start", **STYLES["widget"]).grid(row=row, column=0, sticky=W)
|
Label(body, text="Aircraft cold start", **STYLES["widget"]).grid(row=row, column=0, sticky=W)
|
||||||
Checkbutton(body, variable=self.cold_start_var, **STYLES["radiobutton"]).grid(row=row, column=1, sticky=E)
|
Checkbutton(body, variable=self.cold_start_var, **STYLES["radiobutton"]).grid(row=row, column=1, sticky=E)
|
||||||
row += 1
|
row += 1
|
||||||
@ -84,9 +104,6 @@ class ConfigurationMenu(Menu):
|
|||||||
Checkbutton(body, variable=self.night_var, **STYLES["radiobutton"]).grid(row=row, column=1, sticky=E)
|
Checkbutton(body, variable=self.night_var, **STYLES["radiobutton"]).grid(row=row, column=1, sticky=E)
|
||||||
row += 1
|
row += 1
|
||||||
|
|
||||||
Button(body, text="Display logs", command=self.display_logs, **STYLES["btn-primary"]).grid(row=row, column=1, sticky=E, pady=30)
|
|
||||||
row += 1
|
|
||||||
|
|
||||||
Label(body, text="Contributors: ", **STYLES["strong"]).grid(row=row, column=0, columnspan=2, sticky=EW)
|
Label(body, text="Contributors: ", **STYLES["strong"]).grid(row=row, column=0, columnspan=2, sticky=EW)
|
||||||
row += 1
|
row += 1
|
||||||
|
|
||||||
@ -98,7 +115,8 @@ class ConfigurationMenu(Menu):
|
|||||||
Button(body, text="[github]", command=lambda: webbrowser.open_new_tab("http://github.com/Khopa"), **STYLES["widget"]).grid(row=row, column=1, sticky=E)
|
Button(body, text="[github]", command=lambda: webbrowser.open_new_tab("http://github.com/Khopa"), **STYLES["widget"]).grid(row=row, column=1, sticky=E)
|
||||||
row += 1
|
row += 1
|
||||||
|
|
||||||
Button(body, text="Cheat +200m", command=self.cheat_money, **STYLES["btn-danger"]).grid(row=row, column=1, pady=30)
|
Button(body, text="Display logs", command=self.display_logs, **STYLES["btn-primary"]).grid(row=row, column=0, pady=5)
|
||||||
|
Button(body, text="Cheat +200m", command=self.cheat_money, **STYLES["btn-danger"]).grid(row=row, column=1)
|
||||||
|
|
||||||
def display_logs(self):
|
def display_logs(self):
|
||||||
raise ShowLogsException()
|
raise ShowLogsException()
|
||||||
|
|||||||
@ -20,7 +20,7 @@ class EventMenu(Menu):
|
|||||||
self.scramble_entries = {k: {} for k in self.event.tasks}
|
self.scramble_entries = {k: {} for k in self.event.tasks}
|
||||||
|
|
||||||
if self.event.attacker_name == self.game.player:
|
if self.event.attacker_name == self.game.player:
|
||||||
self.base = self.event.from_cp.base
|
self.base = self.event.departure_cp.base
|
||||||
else:
|
else:
|
||||||
self.base = self.event.to_cp.base
|
self.base = self.event.to_cp.base
|
||||||
|
|
||||||
@ -194,15 +194,24 @@ class EventMenu(Menu):
|
|||||||
self.error_label["text"] = "Need at least one player in flight {}".format(self.event.flight_name(task))
|
self.error_label["text"] = "Need at least one player in flight {}".format(self.event.flight_name(task))
|
||||||
return
|
return
|
||||||
|
|
||||||
if isinstance(self.event, FrontlineAttackEvent) or isinstance(self.event, FrontlinePatrolEvent):
|
for task in self.event.player_banned_tasks:
|
||||||
if tasks_scramble_counts.get(PinpointStrike, 0) == 0:
|
if tasks_clients_counts.get(task, 0) != 0:
|
||||||
self.error_label["text"] = "No ground vehicles assigned to attack!"
|
self.error_label["text"] = "Players are not allowed on flight {}".format(self.event.flight_name(task))
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
||||||
if self.game.is_player_attack(self.event):
|
if self.game.is_player_attack(self.event):
|
||||||
|
if isinstance(self.event, FrontlineAttackEvent) or isinstance(self.event, FrontlinePatrolEvent):
|
||||||
|
if self.event.from_cp.base.total_armor == 0:
|
||||||
|
self.error_label["text"] = "No ground vehicles available to attack!"
|
||||||
|
return
|
||||||
|
|
||||||
self.event.player_attacking(flights)
|
self.event.player_attacking(flights)
|
||||||
else:
|
else:
|
||||||
|
if isinstance(self.event, FrontlineAttackEvent) or isinstance(self.event, FrontlinePatrolEvent):
|
||||||
|
if self.event.to_cp.base.total_armor == 0:
|
||||||
|
self.error_label["text"] = "No ground vehicles available to defend!"
|
||||||
|
return
|
||||||
|
|
||||||
self.event.player_defending(flights)
|
self.event.player_defending(flights)
|
||||||
|
|
||||||
self.game.initiate_event(self.event)
|
self.game.initiate_event(self.event)
|
||||||
|
|||||||
@ -25,85 +25,11 @@ class MainMenu(Menu):
|
|||||||
|
|
||||||
def display(self):
|
def display(self):
|
||||||
persistency.save_game(self.game)
|
persistency.save_game(self.game)
|
||||||
|
|
||||||
self.window.clear_right_pane()
|
self.window.clear_right_pane()
|
||||||
self.upd.update()
|
self.upd.update()
|
||||||
|
|
||||||
# Header :
|
|
||||||
header = Frame(self.frame, **STYLES["header"])
|
header = Frame(self.frame, **STYLES["header"])
|
||||||
Button(header, text="Configuration", command=self.configuration_menu, **STYLES["btn-primary"]).grid(column=0, row=0, sticky=NW)
|
header.grid(column=0, row=0, sticky=NSEW)
|
||||||
Label(header, text="Budget: {}m (+{}m)".format(self.game.budget, self.game.budget_reward_amount), **STYLES["strong"]).grid(column=1, row=0, sticky=N+EW, padx=50)
|
|
||||||
Button(header, text="Pass turn", command=self.pass_turn, **STYLES["btn-primary"]).grid(column=2, row=0, sticky=NE)
|
|
||||||
header.grid(column=0, row=0, sticky=N+EW)
|
|
||||||
|
|
||||||
content = Frame(self.frame, **STYLES["body"])
|
|
||||||
content.grid(column=0, row=1, sticky=NSEW)
|
|
||||||
column = 0
|
|
||||||
row = 0
|
|
||||||
|
|
||||||
def label(text):
|
|
||||||
nonlocal row, body
|
|
||||||
frame = LabelFrame(body, **STYLES["label-frame"])
|
|
||||||
frame.grid(row=row, sticky=N+EW, columnspan=2)
|
|
||||||
Label(frame, text=text, **STYLES["widget"]).grid(row=row, sticky=NS)
|
|
||||||
row += 1
|
|
||||||
|
|
||||||
def event_button(event):
|
|
||||||
nonlocal row, body
|
|
||||||
frame = LabelFrame(body, **STYLES["label-frame"])
|
|
||||||
frame.grid(row=row, sticky=N+EW)
|
|
||||||
Message(frame, text="{}".format(
|
|
||||||
event
|
|
||||||
), aspect=1600, **STYLES["widget"]).grid(column=0, row=0, sticky=N+EW)
|
|
||||||
Button(body, text=">", command=self.start_event(event), **STYLES["btn-primary"]).grid(column=1, row=row, sticky=E)
|
|
||||||
row += 1
|
|
||||||
|
|
||||||
def departure_header(text, style="strong"):
|
|
||||||
nonlocal row, body
|
|
||||||
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):
|
|
||||||
nonlocal row, body
|
|
||||||
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)
|
|
||||||
events.sort(key=lambda x: x.from_cp.name)
|
|
||||||
events.sort(key=lambda x: x.informational and 1 or (self.game.is_player_attack(x) and 2 or 0))
|
|
||||||
|
|
||||||
destination = None
|
|
||||||
departure = None
|
|
||||||
|
|
||||||
for event in events:
|
|
||||||
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 = Frame(content, **STYLES["body"])
|
|
||||||
body.grid(column=column, row=1, sticky=N+EW)
|
|
||||||
row = 0
|
|
||||||
column += 1
|
|
||||||
|
|
||||||
departure = new_departure
|
|
||||||
departure_header(new_departure, style="strong" if self.game.is_player_attack(event) else "supstrong")
|
|
||||||
destination = None
|
|
||||||
|
|
||||||
if not event.informational:
|
|
||||||
new_destination = "At {}".format(event.to_cp.name)
|
|
||||||
if destination != new_destination:
|
|
||||||
destination_header(new_destination)
|
|
||||||
destination = new_destination
|
|
||||||
|
|
||||||
if event.informational:
|
|
||||||
label(str(event))
|
|
||||||
else:
|
|
||||||
event_button(event)
|
|
||||||
|
|
||||||
def pass_turn(self):
|
def pass_turn(self):
|
||||||
self.game.pass_turn(no_action=True)
|
self.game.pass_turn(no_action=True)
|
||||||
@ -113,7 +39,7 @@ class MainMenu(Menu):
|
|||||||
ConfigurationMenu(self.window, self, self.game).display()
|
ConfigurationMenu(self.window, self, self.game).display()
|
||||||
|
|
||||||
def start_event(self, event) -> typing.Callable:
|
def start_event(self, event) -> typing.Callable:
|
||||||
return lambda: EventMenu(self.window, self, self.game, event).display()
|
EventMenu(self.window, self, self.game, event).display()
|
||||||
|
|
||||||
def go_cp(self, cp: ControlPoint):
|
def go_cp(self, cp: ControlPoint):
|
||||||
if not cp.captured:
|
if not cp.captured:
|
||||||
|
|||||||
@ -1,27 +1,536 @@
|
|||||||
import os
|
import os
|
||||||
|
import platform
|
||||||
from tkinter import *
|
from threading import Thread
|
||||||
from tkinter.ttk import *
|
from tkinter.ttk import *
|
||||||
|
|
||||||
|
import pygame
|
||||||
|
|
||||||
|
from theater.theatergroundobject import CATEGORY_MAP
|
||||||
|
from ui.styles import STYLES
|
||||||
from ui.window import *
|
from ui.window import *
|
||||||
|
|
||||||
from game.game import *
|
|
||||||
from gen.conflictgen import Conflict
|
EVENT_COLOR_ATTACK = (100, 100, 255)
|
||||||
from theater.conflicttheater import *
|
EVENT_COLOR_DEFENSE = (255, 100, 100)
|
||||||
|
|
||||||
|
RED = (255, 125, 125)
|
||||||
|
BRIGHT_RED = (200, 64, 64)
|
||||||
|
BLUE = (164, 164, 255)
|
||||||
|
DARK_BLUE = (45, 62, 80)
|
||||||
|
WHITE = (255, 255, 255)
|
||||||
|
GREEN = (128, 186, 128)
|
||||||
|
BRIGHT_GREEN = (64, 200, 64)
|
||||||
|
BLACK = (0, 0, 0)
|
||||||
|
|
||||||
|
BACKGROUND = pygame.Color(0, 64, 64)
|
||||||
|
ANTIALIASING = True
|
||||||
|
|
||||||
|
WIDTH = 800
|
||||||
|
HEIGHT = 600
|
||||||
|
MAP_PADDING = 100
|
||||||
|
|
||||||
|
|
||||||
class OverviewCanvas:
|
class OverviewCanvas:
|
||||||
mainmenu = None # type: ui.mainmenu.MainMenu
|
mainmenu = None # type: ui.mainmenu.MainMenu
|
||||||
|
budget_label = None # type: Label
|
||||||
|
|
||||||
|
started = None
|
||||||
|
ground_assets_icons = None # type: typing.Dict[str, pygame.Surface]
|
||||||
|
event_icons = None # type: typing.Dict[typing.Type, pygame.Surface]
|
||||||
|
selected_event_info = None # type: typing.Tuple[Event, typing.Tuple[int, int]]
|
||||||
|
frontline_vector_cache = None # type: typing.Dict[str, typing.Tuple[Point, int, int]]
|
||||||
|
|
||||||
def __init__(self, frame: Frame, parent, game: Game):
|
def __init__(self, frame: Frame, parent, game: Game):
|
||||||
|
|
||||||
self.parent = parent
|
self.parent = parent
|
||||||
self.game = game
|
self.game = game
|
||||||
|
|
||||||
self.image = PhotoImage(file=os.path.join("resources", game.theater.overview_image))
|
# Remove any previously existing pygame instance
|
||||||
self.canvas = Canvas(frame, width=self.image.width(), height=self.image.height())
|
pygame.quit()
|
||||||
self.canvas.grid(column=0, row=0, sticky=NSEW)
|
|
||||||
|
|
||||||
def transform_point(self, p: Point, treshold=30) -> (int, int):
|
# Pygame objects
|
||||||
|
self.map = None
|
||||||
|
self.screen = None
|
||||||
|
self.surface: pygame.Surface = None
|
||||||
|
self.thread: Thread = None
|
||||||
|
self.clock = pygame.time.Clock()
|
||||||
|
self.expanded = True
|
||||||
|
|
||||||
|
pygame.font.init()
|
||||||
|
self.font: pygame.font.SysFont = pygame.font.SysFont("arial", 15)
|
||||||
|
self.fontsmall: pygame.font.SysFont = pygame.font.SysFont("arial", 10)
|
||||||
|
self.ground_assets_icons = {}
|
||||||
|
|
||||||
|
# Frontline are too heavy on performance to compute in realtime, so keep them in a cache
|
||||||
|
self.frontline_vector_cache = {}
|
||||||
|
|
||||||
|
# Map state
|
||||||
|
self.redraw_required = True
|
||||||
|
self.zoom = 1
|
||||||
|
self.scroll = [0, 0]
|
||||||
|
self.exited = False
|
||||||
|
|
||||||
|
# Display options
|
||||||
|
self.display_ground_targets = BooleanVar(value=True)
|
||||||
|
self.display_forces = BooleanVar(value=True)
|
||||||
|
self.display_bases = BooleanVar(value=True)
|
||||||
|
self.display_road = BooleanVar(value=True)
|
||||||
|
self.display_rules = self.compute_display_rules()
|
||||||
|
|
||||||
|
parent.window.tk.protocol("<WM_DELETE_WINDOW>", self.on_close)
|
||||||
|
|
||||||
|
self.wrapper = Frame(frame, **STYLES["frame-wrapper"])
|
||||||
|
self.wrapper.grid(column=0, row=0, sticky=NSEW) # Adds grid
|
||||||
|
self.wrapper.pack(side=LEFT) # packs window to the left
|
||||||
|
|
||||||
|
self.embed = Frame(self.wrapper, width=WIDTH, height=HEIGHT, borderwidth=2, **STYLES["frame-wrapper"])
|
||||||
|
self.embed.grid(column=0, row=1, sticky=NSEW) # Adds grid
|
||||||
|
|
||||||
|
self.options = Frame(self.wrapper, borderwidth=2, **STYLES["frame-wrapper"])
|
||||||
|
self.options.grid(column=0, row=0, sticky=NSEW)
|
||||||
|
self.options.grid_columnconfigure(1, weight=1)
|
||||||
|
self.build_map_options_panel()
|
||||||
|
|
||||||
|
self.init_sdl_layer()
|
||||||
|
self.init_sdl_thread()
|
||||||
|
|
||||||
|
def build_map_options_panel(self):
|
||||||
|
col = 0
|
||||||
|
Button(self.options, text="Configuration", command=self.parent.configuration_menu, **STYLES["btn-primary"]).grid(column=col, row=0, sticky=NE)
|
||||||
|
col += 1
|
||||||
|
|
||||||
|
self.budget_label = Label(self.options, text="Budget: {}m (+{}m)".format(self.game.budget, self.game.budget_reward_amount), **STYLES["widget"])
|
||||||
|
self.budget_label.grid(column=col, row=0, sticky=N+EW)
|
||||||
|
col += 1
|
||||||
|
|
||||||
|
Button(self.options, text="Pass turn", command=self.parent.pass_turn, **STYLES["btn-primary"]).grid(column=col, row=0, sticky=NW)
|
||||||
|
col += 1
|
||||||
|
|
||||||
|
def map_size_toggle(self):
|
||||||
|
if self.expanded:
|
||||||
|
self.embed.configure(width=0)
|
||||||
|
self.options.configure(width=0)
|
||||||
|
self.expanded = False
|
||||||
|
else:
|
||||||
|
self.embed.configure(width=WIDTH)
|
||||||
|
self.options.configure(width=WIDTH)
|
||||||
|
self.expanded = True
|
||||||
|
|
||||||
|
def on_close(self):
|
||||||
|
self.exited = True
|
||||||
|
if self.thread is not None:
|
||||||
|
self.thread.join()
|
||||||
|
|
||||||
|
def init_sdl_layer(self):
|
||||||
|
# Setup pygame to run in tk frame
|
||||||
|
os.environ['SDL_WINDOWID'] = str(self.embed.winfo_id())
|
||||||
|
if platform.system == "Windows":
|
||||||
|
os.environ['SDL_VIDEODRIVER'] = 'windib'
|
||||||
|
|
||||||
|
# Create pygame 'screen'
|
||||||
|
self.screen = pygame.display.set_mode((WIDTH, HEIGHT), pygame.DOUBLEBUF | pygame.HWSURFACE)
|
||||||
|
self.screen.fill(pygame.Color(*BLACK))
|
||||||
|
|
||||||
|
# Load icons resources
|
||||||
|
self.ground_assets_icons = {}
|
||||||
|
self.ground_assets_icons["target"] = pygame.image.load(os.path.join("resources", "ui", "ground_assets", "target.png"))
|
||||||
|
self.ground_assets_icons["cleared"] = pygame.image.load(os.path.join("resources", "ui", "ground_assets", "cleared.png"))
|
||||||
|
for category in CATEGORY_MAP.keys():
|
||||||
|
self.ground_assets_icons[category] = pygame.image.load(os.path.join("resources", "ui", "ground_assets", category + ".png"))
|
||||||
|
|
||||||
|
self.event_icons = {}
|
||||||
|
for category, image in {BaseAttackEvent: "capture",
|
||||||
|
FrontlinePatrolEvent: "attack",
|
||||||
|
FrontlineAttackEvent: "attack",
|
||||||
|
InfantryTransportEvent: "infantry",
|
||||||
|
InsurgentAttackEvent: "insurgent_attack",
|
||||||
|
ConvoyStrikeEvent: "convoy",
|
||||||
|
InterceptEvent: "air_intercept",
|
||||||
|
NavalInterceptEvent: "naval_intercept",
|
||||||
|
StrikeEvent: "strike",
|
||||||
|
UnitsDeliveryEvent: "delivery"}.items():
|
||||||
|
self.event_icons[category] = pygame.image.load(os.path.join("resources", "ui", "events", image + ".png"))
|
||||||
|
|
||||||
|
|
||||||
|
# Load the map image
|
||||||
|
self.map = pygame.image.load(os.path.join("resources", self.game.theater.overview_image)).convert()
|
||||||
|
pygame.draw.rect(self.map, BLACK, (0, 0, self.map.get_width(), self.map.get_height()), 10)
|
||||||
|
pygame.draw.rect(self.map, WHITE, (0, 0, self.map.get_width(), self.map.get_height()), 5)
|
||||||
|
|
||||||
|
# Create surfaces for drawing
|
||||||
|
self.surface = pygame.Surface((self.map.get_width() + MAP_PADDING * 2,
|
||||||
|
self.map.get_height() + MAP_PADDING * 2))
|
||||||
|
self.surface.set_alpha(None)
|
||||||
|
self.overlay = pygame.Surface((WIDTH, HEIGHT), pygame.SRCALPHA)
|
||||||
|
|
||||||
|
# Init pygame display
|
||||||
|
pygame.display.init()
|
||||||
|
pygame.display.update()
|
||||||
|
|
||||||
|
def init_sdl_thread(self):
|
||||||
|
if OverviewCanvas.started is not None:
|
||||||
|
OverviewCanvas.started.exited = True
|
||||||
|
self.thread = Thread(target=self.sdl_thread)
|
||||||
|
self.thread.start()
|
||||||
|
OverviewCanvas.started = self
|
||||||
|
print("Started SDL app")
|
||||||
|
|
||||||
|
def sdl_thread(self):
|
||||||
|
self.redraw_required = True
|
||||||
|
i = 0
|
||||||
|
while not self.exited:
|
||||||
|
self.clock.tick(30)
|
||||||
|
self.draw()
|
||||||
|
i += 1
|
||||||
|
if i == 600:
|
||||||
|
self.frontline_vector_cache = {}
|
||||||
|
i = 0
|
||||||
|
print("Stopped SDL app")
|
||||||
|
|
||||||
|
def draw(self):
|
||||||
|
try:
|
||||||
|
self.embed.winfo_ismapped()
|
||||||
|
self.embed.winfo_manager()
|
||||||
|
except:
|
||||||
|
self.exited = True
|
||||||
|
|
||||||
|
right_down = False
|
||||||
|
left_down = False
|
||||||
|
|
||||||
|
# Detect changes on display rules
|
||||||
|
r = self.compute_display_rules()
|
||||||
|
if r != self.display_rules:
|
||||||
|
self.display_rules = r
|
||||||
|
self.redraw_required = True
|
||||||
|
|
||||||
|
for event in pygame.event.get():
|
||||||
|
if event.type == pygame.MOUSEMOTION:
|
||||||
|
self.redraw_required = True
|
||||||
|
elif event.type == pygame.MOUSEBUTTONDOWN:
|
||||||
|
"""
|
||||||
|
Due to rendering not really supporting the zoom this is currently disabled.
|
||||||
|
@TODO: improve rendering so zoom would actually make sense
|
||||||
|
|
||||||
|
# Scroll wheel
|
||||||
|
if event.button == 4:
|
||||||
|
self.zoom += 0.25
|
||||||
|
self.redraw_required = True
|
||||||
|
elif event.button == 5:
|
||||||
|
self.zoom -= 0.25
|
||||||
|
self.redraw_required = True
|
||||||
|
"""
|
||||||
|
|
||||||
|
if event.button == 3:
|
||||||
|
right_down = True
|
||||||
|
pygame.mouse.get_rel()
|
||||||
|
if event.button == 1:
|
||||||
|
left_down = True
|
||||||
|
self.redraw_required = True
|
||||||
|
|
||||||
|
# If Right click pressed
|
||||||
|
if pygame.mouse.get_pressed()[2] == 1 and not right_down:
|
||||||
|
scr = pygame.mouse.get_rel()
|
||||||
|
self.scroll[0] += scr[0]
|
||||||
|
self.scroll[1] += scr[1]
|
||||||
|
self.redraw_required = True
|
||||||
|
|
||||||
|
if self.zoom <= 0.5:
|
||||||
|
self.zoom = 0.5
|
||||||
|
elif self.zoom > 3:
|
||||||
|
self.zoom = 3
|
||||||
|
|
||||||
|
if self.redraw_required:
|
||||||
|
# Fill
|
||||||
|
self.screen.fill(BACKGROUND)
|
||||||
|
self.surface.fill(BACKGROUND)
|
||||||
|
self.overlay.fill(pygame.Color(0, 0, 0, 0))
|
||||||
|
|
||||||
|
# Surface
|
||||||
|
cursor_pos = pygame.mouse.get_pos()
|
||||||
|
cursor_pos = (
|
||||||
|
cursor_pos[0] / self.zoom - self.scroll[0], cursor_pos[1] / self.zoom - self.scroll[1])
|
||||||
|
self.draw_map(self.surface, self.overlay, cursor_pos, [left_down, right_down])
|
||||||
|
|
||||||
|
# Scaling
|
||||||
|
scaled = pygame.transform.scale(self.surface, (
|
||||||
|
int(self.surface.get_width() * self.zoom), int(self.surface.get_height() * self.zoom)))
|
||||||
|
self.screen.blit(scaled, (self.scroll[0]*self.zoom, self.scroll[1]*self.zoom))
|
||||||
|
self.screen.blit(self.overlay, (0, 0))
|
||||||
|
|
||||||
|
pygame.display.flip()
|
||||||
|
|
||||||
|
self.redraw_required = False
|
||||||
|
|
||||||
|
def draw_map(self, surface: pygame.Surface, overlay: pygame.Surface, mouse_pos: (int, int), mouse_down: [bool, bool]):
|
||||||
|
self.surface.blit(self.map, (MAP_PADDING, MAP_PADDING))
|
||||||
|
|
||||||
|
# Display zoom level on overlay
|
||||||
|
zoom_lvl = self.font.render(" x " + str(self.zoom) + " ", ANTIALIASING, WHITE, DARK_BLUE)
|
||||||
|
self.overlay.blit(zoom_lvl, (self.overlay.get_width()-zoom_lvl.get_width()-5,
|
||||||
|
self.overlay.get_height()-zoom_lvl.get_height()-5))
|
||||||
|
|
||||||
|
# Debug
|
||||||
|
# pygame.draw.rect(surface, (255, 0, 255), (mouse_pos[0], mouse_pos[1], 5, 5), 2)
|
||||||
|
|
||||||
|
for cp in self.game.theater.controlpoints:
|
||||||
|
coords = self._transform_point(cp.position)
|
||||||
|
|
||||||
|
if self.display_road.get():
|
||||||
|
for connected_cp in cp.connected_points:
|
||||||
|
connected_coords = self._transform_point(connected_cp.position)
|
||||||
|
if connected_cp.captured != cp.captured:
|
||||||
|
color = self._enemy_color()
|
||||||
|
elif connected_cp.captured and cp.captured:
|
||||||
|
color = self._player_color()
|
||||||
|
else:
|
||||||
|
color = BLACK
|
||||||
|
|
||||||
|
pygame.draw.line(surface, color, coords, connected_coords, 2)
|
||||||
|
|
||||||
|
if cp.captured and not connected_cp.captured and Conflict.has_frontline_between(cp, connected_cp):
|
||||||
|
frontline = self._frontline_vector(cp, connected_cp)
|
||||||
|
if not frontline:
|
||||||
|
continue
|
||||||
|
|
||||||
|
frontline_pos, heading, distance = frontline
|
||||||
|
|
||||||
|
if distance < 10000:
|
||||||
|
frontline_pos = frontline_pos.point_from_heading(heading + 180, 5000)
|
||||||
|
distance = 10000
|
||||||
|
|
||||||
|
start_coords = self._transform_point(frontline_pos, treshold=10)
|
||||||
|
end_coords = self._transform_point(frontline_pos.point_from_heading(heading, distance),
|
||||||
|
treshold=60)
|
||||||
|
|
||||||
|
pygame.draw.line(surface, color, start_coords, end_coords, 4)
|
||||||
|
|
||||||
|
if self.display_ground_targets.get():
|
||||||
|
for ground_object in cp.ground_objects:
|
||||||
|
self.draw_ground_object(ground_object, surface, cp.captured, mouse_pos)
|
||||||
|
|
||||||
|
if self.display_bases.get():
|
||||||
|
mouse_down = self.draw_bases(mouse_pos, mouse_down)
|
||||||
|
|
||||||
|
mouse_down = self.draw_events(self.surface, mouse_pos, mouse_down)
|
||||||
|
|
||||||
|
if mouse_down[0]:
|
||||||
|
self.selected_event_info = None
|
||||||
|
|
||||||
|
def draw_bases(self, mouse_pos, mouse_down):
|
||||||
|
for cp in self.game.theater.controlpoints:
|
||||||
|
coords = self._transform_point(cp.position)
|
||||||
|
radius = 12 * math.pow(cp.importance, 1)
|
||||||
|
radius_m = max(radius * cp.base.strength - 2, 0)
|
||||||
|
|
||||||
|
if cp.captured:
|
||||||
|
color = self._player_color()
|
||||||
|
else:
|
||||||
|
color = self._enemy_color()
|
||||||
|
|
||||||
|
pygame.draw.circle(self.surface, BLACK, (int(coords[0]), int(coords[1])), int(radius))
|
||||||
|
pygame.draw.circle(self.surface, color, (int(coords[0]), int(coords[1])), int(radius_m))
|
||||||
|
|
||||||
|
label = self.font.render(cp.name, ANTIALIASING, (225, 225, 225), BLACK)
|
||||||
|
labelHover = self.font.render(cp.name, ANTIALIASING, (255, 255, 255), (128, 186, 128))
|
||||||
|
labelClick = self.font.render(cp.name, ANTIALIASING, (255, 255, 255), (122, 122, 255))
|
||||||
|
|
||||||
|
point = coords[0] - label.get_width() / 2 + 1, coords[1] + 1
|
||||||
|
rect = pygame.Rect(*point, label.get_width(), label.get_height())
|
||||||
|
|
||||||
|
if rect.collidepoint(*mouse_pos):
|
||||||
|
if mouse_down[0]:
|
||||||
|
self.surface.blit(labelClick, (coords[0] - label.get_width() / 2 + 1, coords[1] + 1))
|
||||||
|
self._selected_cp(cp)
|
||||||
|
mouse_down[0] = False
|
||||||
|
else:
|
||||||
|
self.surface.blit(labelHover, (coords[0] - label.get_width() / 2 + 1, coords[1] + 1))
|
||||||
|
|
||||||
|
self.draw_base_info(self.overlay, cp, (0, 0))
|
||||||
|
if self.selected_event_info:
|
||||||
|
if self._cp_available_for_selected_event(cp):
|
||||||
|
pygame.draw.line(self.surface, WHITE, rect.center, self.selected_event_info[1])
|
||||||
|
|
||||||
|
else:
|
||||||
|
self.surface.blit(label, (coords[0] - label.get_width() / 2 + 1, coords[1] + 1))
|
||||||
|
|
||||||
|
if self.display_forces.get():
|
||||||
|
units_title = " {} / {} / {} ".format(cp.base.total_planes, cp.base.total_armor, cp.base.total_aa)
|
||||||
|
label2 = self.fontsmall.render(units_title, ANTIALIASING, color, (30, 30, 30))
|
||||||
|
self.surface.blit(label2, (coords[0] - label2.get_width() / 2, coords[1] + label.get_height() + 1))
|
||||||
|
|
||||||
|
return mouse_down
|
||||||
|
|
||||||
|
def draw_base_info(self, surface: pygame.Surface, control_point: ControlPoint, pos):
|
||||||
|
title = self.font.render(control_point.name, ANTIALIASING, BLACK, GREEN)
|
||||||
|
hp = self.font.render("Strength : ", ANTIALIASING, (225, 225, 225), BLACK)
|
||||||
|
|
||||||
|
armor_txt = "ARMOR > "
|
||||||
|
for key, value in control_point.base.armor.items():
|
||||||
|
armor_txt += key.id + " x " + str(value) + " | "
|
||||||
|
armor = self.font.render(armor_txt, ANTIALIASING, (225, 225, 225), BLACK)
|
||||||
|
|
||||||
|
aircraft_txt = "AIRCRAFT > "
|
||||||
|
for key, value in control_point.base.aircraft.items():
|
||||||
|
aircraft_txt += key.id + " x " + str(value) + " | "
|
||||||
|
aircraft = self.font.render(aircraft_txt, ANTIALIASING, (225, 225, 225), BLACK)
|
||||||
|
|
||||||
|
aa_txt = "AA/SAM > "
|
||||||
|
for key, value in control_point.base.aa.items():
|
||||||
|
aa_txt += key.id + " x " + str(value) + " | "
|
||||||
|
aa = self.font.render(aa_txt, ANTIALIASING, (225, 225, 225), BLACK)
|
||||||
|
|
||||||
|
lineheight = title.get_height()
|
||||||
|
w = max([max([a.get_width() for a in [title, armor, aircraft, aa]]), 150])
|
||||||
|
h = 5 * lineheight + 4 * 5
|
||||||
|
|
||||||
|
# Draw frame
|
||||||
|
pygame.draw.rect(surface, GREEN, (pos[0], pos[1], w + 8, h + 8))
|
||||||
|
pygame.draw.rect(surface, BLACK, (pos[0] + 2, pos[1] + 2, w + 4, h + 4))
|
||||||
|
pygame.draw.rect(surface, GREEN, (pos[0] + 2, pos[1], w + 4, lineheight + 4))
|
||||||
|
|
||||||
|
# Title
|
||||||
|
surface.blit(title, (pos[0] + 4, 4 + pos[1]))
|
||||||
|
surface.blit(hp, (pos[0] + 4, 4 + pos[1] + lineheight + 5))
|
||||||
|
|
||||||
|
# Draw gauge
|
||||||
|
pygame.draw.rect(surface, WHITE,
|
||||||
|
(pos[0] + hp.get_width() + 3, 4 + pos[1] + lineheight + 5, 54, lineheight))
|
||||||
|
pygame.draw.rect(surface, BRIGHT_RED,
|
||||||
|
(pos[0] + hp.get_width() + 5, 4 + pos[1] + lineheight + 5 + 2, 50, lineheight - 4))
|
||||||
|
pygame.draw.rect(surface, BRIGHT_GREEN, (
|
||||||
|
pos[0] + hp.get_width() + 5, 4 + pos[1] + lineheight + 5 + 2, 50 * control_point.base.strength, lineheight - 4))
|
||||||
|
|
||||||
|
# Text
|
||||||
|
surface.blit(armor, (pos[0] + 4, 4 + pos[1] + lineheight * 2 + 10))
|
||||||
|
surface.blit(aircraft, (pos[0] + 4, 4 + pos[1] + lineheight * 3 + 15))
|
||||||
|
surface.blit(aa, (pos[0] + 4, 4 + pos[1] + lineheight * 4 + 20))
|
||||||
|
|
||||||
|
def draw_selected_event_info(self):
|
||||||
|
event = self.selected_event_info[0]
|
||||||
|
title = self.font.render(str(event), ANTIALIASING, BLACK, GREEN)
|
||||||
|
hint = self.font.render("Select CP to depart from.", ANTIALIASING, (225, 225, 225), BLACK)
|
||||||
|
|
||||||
|
w = hint.get_width()
|
||||||
|
h = title.get_height() + hint.get_height() + 20
|
||||||
|
|
||||||
|
pos = self.overlay.get_width() / 2 - w / 2, self.overlay.get_height() - h
|
||||||
|
|
||||||
|
# Draw frame
|
||||||
|
pygame.draw.rect(self.overlay, GREEN, (pos[0], pos[1], w + 8, h + 8))
|
||||||
|
pygame.draw.rect(self.overlay, BLACK, (pos[0] + 2, pos[1] + 2, w + 4, h + 4))
|
||||||
|
pygame.draw.rect(self.overlay, GREEN, (pos[0] + 2, pos[1], w + 4, title.get_height() + 4))
|
||||||
|
|
||||||
|
# Title
|
||||||
|
self.overlay.blit(title, (pos[0] + 4, 4 + pos[1]))
|
||||||
|
self.overlay.blit(hint, (pos[0] + 4, 4 + pos[1] + title.get_height() + 5))
|
||||||
|
|
||||||
|
def draw_ground_object(self, ground_object: TheaterGroundObject, surface: pygame.Surface, captured: bool, mouse_pos):
|
||||||
|
if captured:
|
||||||
|
color = self._player_color()
|
||||||
|
else:
|
||||||
|
color = self._enemy_color()
|
||||||
|
|
||||||
|
x, y = self._transform_point(ground_object.position)
|
||||||
|
rect = pygame.Rect(x, y, 16, 16)
|
||||||
|
|
||||||
|
if ground_object.is_dead or captured:
|
||||||
|
surface.blit(self.ground_assets_icons["cleared"], (x, y))
|
||||||
|
else:
|
||||||
|
if ground_object.category in self.ground_assets_icons.keys():
|
||||||
|
icon = self.ground_assets_icons[ground_object.category]
|
||||||
|
else:
|
||||||
|
icon = self.ground_assets_icons["target"]
|
||||||
|
surface.blit(icon, (x, y))
|
||||||
|
|
||||||
|
if rect.collidepoint(*mouse_pos):
|
||||||
|
self.draw_ground_object_info(ground_object, (x, y), color, surface)
|
||||||
|
|
||||||
|
def draw_ground_object_info(self, ground_object: TheaterGroundObject, pos, color, surface: pygame.Surface):
|
||||||
|
lb = self.font.render(str(ground_object), ANTIALIASING, color, BLACK)
|
||||||
|
surface.blit(lb, (pos[0] + 18, pos[1]))
|
||||||
|
|
||||||
|
def draw_events(self, surface: pygame.Surface, mouse_pos, mouse_down):
|
||||||
|
occupied_rects = []
|
||||||
|
for cp in self.game.theater.controlpoints:
|
||||||
|
point = self._transform_point(cp.position)
|
||||||
|
occupied_rects.append(pygame.Rect(point[0] - 16, point[1] - 16, 32, 48))
|
||||||
|
|
||||||
|
def _location_to_rect(location: Point) -> pygame.Rect:
|
||||||
|
nonlocal occupied_rects
|
||||||
|
point = self._transform_point(location)
|
||||||
|
rect = pygame.Rect(point[0] - 16, point[1] - 16, 32, 32)
|
||||||
|
|
||||||
|
i = 0
|
||||||
|
while True:
|
||||||
|
result = True
|
||||||
|
for occupied_rect in occupied_rects:
|
||||||
|
if rect.colliderect(occupied_rect):
|
||||||
|
i += 1
|
||||||
|
|
||||||
|
if i % 2:
|
||||||
|
rect.y += occupied_rect.height
|
||||||
|
else:
|
||||||
|
rect.x += occupied_rect.width
|
||||||
|
|
||||||
|
result = False
|
||||||
|
break
|
||||||
|
if result:
|
||||||
|
break
|
||||||
|
|
||||||
|
occupied_rects.append(rect)
|
||||||
|
return rect
|
||||||
|
|
||||||
|
def _events_priority_key(event: Event) -> int:
|
||||||
|
priority_list = [InfantryTransportEvent, StrikeEvent, BaseAttackEvent, UnitsDeliveryEvent]
|
||||||
|
if type(event) not in priority_list:
|
||||||
|
return 0
|
||||||
|
else:
|
||||||
|
return priority_list.index(type(event)) + 1
|
||||||
|
|
||||||
|
events = self.game.events
|
||||||
|
events.sort(key=_events_priority_key, reverse=True)
|
||||||
|
|
||||||
|
label_to_draw = None
|
||||||
|
for event in self.game.events:
|
||||||
|
location = event.location
|
||||||
|
if type(event) in [FrontlineAttackEvent, FrontlinePatrolEvent, ConvoyStrikeEvent]:
|
||||||
|
location = self._frontline_center(event.from_cp, event.to_cp)
|
||||||
|
|
||||||
|
rect = _location_to_rect(location)
|
||||||
|
pygame.draw.rect(surface, EVENT_COLOR_ATTACK if event.is_player_attacking else EVENT_COLOR_DEFENSE, rect)
|
||||||
|
self.surface.blit(self.event_icons[event.__class__], rect.topleft)
|
||||||
|
|
||||||
|
if rect.collidepoint(*mouse_pos) or self.selected_event_info == (event, rect.center):
|
||||||
|
if not label_to_draw:
|
||||||
|
label_to_draw = self.font.render(str(event), ANTIALIASING, WHITE, BLACK), rect.center
|
||||||
|
|
||||||
|
if rect.collidepoint(*mouse_pos):
|
||||||
|
if mouse_down[0]:
|
||||||
|
self.selected_event_info = event, rect.center
|
||||||
|
mouse_down[0] = False
|
||||||
|
|
||||||
|
if label_to_draw:
|
||||||
|
surface.blit(*label_to_draw)
|
||||||
|
|
||||||
|
if self.selected_event_info:
|
||||||
|
self.draw_selected_event_info()
|
||||||
|
|
||||||
|
return mouse_down
|
||||||
|
|
||||||
|
def _selected_cp(self, cp):
|
||||||
|
if self.selected_event_info:
|
||||||
|
if self. _cp_available_for_selected_event(cp):
|
||||||
|
event = self.selected_event_info[0]
|
||||||
|
event.departure_cp = cp
|
||||||
|
|
||||||
|
self.selected_event_info = None
|
||||||
|
self.parent.start_event(event)
|
||||||
|
else:
|
||||||
|
return
|
||||||
|
else:
|
||||||
|
self.parent.go_cp(cp)
|
||||||
|
|
||||||
|
def _transform_point(self, p: Point, treshold=30) -> (int, int):
|
||||||
point_a = list(self.game.theater.reference_points.keys())[0]
|
point_a = list(self.game.theater.reference_points.keys())[0]
|
||||||
point_a_img = self.game.theater.reference_points[point_a]
|
point_a_img = self.game.theater.reference_points[point_a]
|
||||||
|
|
||||||
@ -44,101 +553,48 @@ class OverviewCanvas:
|
|||||||
X = point_b_img[1] + X_offset * X_scale
|
X = point_b_img[1] + X_offset * X_scale
|
||||||
Y = point_a_img[0] - Y_offset * Y_scale
|
Y = point_a_img[0] - Y_offset * Y_scale
|
||||||
|
|
||||||
|
X += MAP_PADDING
|
||||||
|
Y += MAP_PADDING
|
||||||
|
|
||||||
return X > treshold and X or treshold, Y > treshold and Y or treshold
|
return X > treshold and X or treshold, Y > treshold and Y or treshold
|
||||||
|
|
||||||
def create_cp_title(self, coords, cp: ControlPoint):
|
def _frontline_vector(self, from_cp: ControlPoint, to_cp: ControlPoint):
|
||||||
title = cp.name
|
# Cache mechanism to avoid performing frontline vector computation on every frame
|
||||||
font = ("Helvetica", 10)
|
key = str(from_cp.id) + "_" + str(to_cp.id)
|
||||||
|
if key in self.frontline_vector_cache:
|
||||||
|
return self.frontline_vector_cache[key]
|
||||||
|
else:
|
||||||
|
frontline = Conflict.frontline_vector(from_cp, to_cp, self.game.theater)
|
||||||
|
self.frontline_vector_cache[key] = frontline
|
||||||
|
return frontline
|
||||||
|
|
||||||
id = self.canvas.create_text(coords[0], coords[1], text=title, font=font)
|
def _frontline_center(self, from_cp: ControlPoint, to_cp: ControlPoint) -> typing.Optional[Point]:
|
||||||
self.canvas.tag_bind(id, "<Button-1>", self.display(cp))
|
frontline_vector = self._frontline_vector(from_cp, to_cp)
|
||||||
|
if frontline_vector:
|
||||||
|
return frontline_vector[0].point_from_heading(frontline_vector[1], frontline_vector[2]/2)
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
id = self.canvas.create_text(coords[0]+1, coords[1]+1, text=title, fill='white', font=font)
|
def _cp_available_for_selected_event(self, cp: ControlPoint) -> bool:
|
||||||
self.canvas.tag_bind(id, "<Button-1>", self.display(cp))
|
event = self.selected_event_info[0]
|
||||||
|
return event.is_departure_available_from(cp)
|
||||||
|
|
||||||
def _player_color(self):
|
def _player_color(self):
|
||||||
return self.game.player == "USA" and "blue" or "red"
|
return self.game.player == "USA" and BLUE or RED
|
||||||
|
|
||||||
def _enemy_color(self):
|
def _enemy_color(self):
|
||||||
return self.game.player == "USA" and "red" or "blue"
|
return self.game.player == "USA" and RED or BLUE
|
||||||
|
|
||||||
def update(self):
|
def update(self):
|
||||||
self.canvas.delete(ALL)
|
self.redraw_required = True
|
||||||
self.canvas.create_image((self.image.width()/2, self.image.height()/2), image=self.image)
|
self.draw()
|
||||||
|
self.budget_label.text = "Budget: {}m (+{}m)".format(self.game.budget, self.game.budget_reward_amount)
|
||||||
|
|
||||||
for cp in self.game.theater.controlpoints:
|
def compute_display_rules(self):
|
||||||
for ground_object in cp.ground_objects:
|
return sum([1 if a.get() else 0 for a in [self.display_forces, self.display_road, self.display_bases, self.display_ground_targets]])
|
||||||
x, y = self.transform_point(ground_object.position)
|
|
||||||
self.canvas.create_text(x,
|
|
||||||
y,
|
|
||||||
text=".",
|
|
||||||
fill="black" if ground_object.is_dead else self._enemy_color(),
|
|
||||||
font=("Helvetica", 18))
|
|
||||||
|
|
||||||
coords = self.transform_point(cp.position)
|
|
||||||
for connected_cp in cp.connected_points:
|
|
||||||
connected_coords = self.transform_point(connected_cp.position)
|
|
||||||
if connected_cp.captured != cp.captured:
|
|
||||||
color = self._enemy_color()
|
|
||||||
elif connected_cp.captured and cp.captured:
|
|
||||||
color = self._player_color()
|
|
||||||
else:
|
|
||||||
color = "black"
|
|
||||||
|
|
||||||
self.canvas.create_line((coords[0], coords[1], connected_coords[0], connected_coords[1]), width=2, fill=color)
|
|
||||||
|
|
||||||
if cp.captured and not connected_cp.captured and Conflict.has_frontline_between(cp, connected_cp):
|
|
||||||
frontline = Conflict.frontline_vector(cp, connected_cp, self.game.theater)
|
|
||||||
if not frontline:
|
|
||||||
continue
|
|
||||||
|
|
||||||
frontline_pos, heading, distance = frontline
|
|
||||||
if distance < 10000:
|
|
||||||
frontline_pos = frontline_pos.point_from_heading(heading + 180, 5000)
|
|
||||||
distance = 10000
|
|
||||||
|
|
||||||
start_coords = self.transform_point(frontline_pos, treshold=10)
|
|
||||||
end_coords = self.transform_point(frontline_pos.point_from_heading(heading, distance), treshold=60)
|
|
||||||
|
|
||||||
self.canvas.create_line((*start_coords, *end_coords), width=2, fill=color)
|
|
||||||
|
|
||||||
for cp in self.game.theater.controlpoints:
|
|
||||||
coords = self.transform_point(cp.position)
|
|
||||||
arc_size = 16 * math.pow(cp.importance, 1)
|
|
||||||
extent = max(cp.base.strength * 180, 10)
|
|
||||||
start = (180 - extent) / 2
|
|
||||||
|
|
||||||
if cp.captured:
|
|
||||||
color = self._player_color()
|
|
||||||
else:
|
|
||||||
color = self._enemy_color()
|
|
||||||
|
|
||||||
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)
|
|
||||||
|
|
||||||
"""
|
|
||||||
#For debugging purposes
|
|
||||||
|
|
||||||
for r in cp.radials:
|
|
||||||
p = self.transform_point(cp.position.point_from_heading(r, 20000))
|
|
||||||
self.canvas.create_text(p[0], p[1], text="{}".format(r))
|
|
||||||
continue
|
|
||||||
"""
|
|
||||||
|
|
||||||
self.canvas.tag_bind(cp_id, "<Button-1>", self.display(cp))
|
|
||||||
self.create_cp_title((coords[0] + arc_size/4, coords[1] + arc_size/4), cp)
|
|
||||||
|
|
||||||
units_title = "{}/{}/{}".format(cp.base.total_planes, cp.base.total_armor, cp.base.total_aa)
|
|
||||||
self.canvas.create_text(coords[0]+1, coords[1] - arc_size / 1.5 +1, text=units_title, font=("Helvetica", 8), fill=color)
|
|
||||||
self.canvas.create_text(coords[0], coords[1] - arc_size / 1.5, text=units_title, font=("Helvetica", 8), fill="white")
|
|
||||||
|
|
||||||
def display(self, cp: ControlPoint):
|
def display(self, cp: ControlPoint):
|
||||||
def action(_):
|
def action(_):
|
||||||
return self.parent.go_cp(cp)
|
return self.parent.go_cp(cp)
|
||||||
|
|
||||||
return action
|
return action
|
||||||
|
|
||||||
|
|||||||
111
ui/window.py
@ -1,8 +1,18 @@
|
|||||||
from tkinter import *
|
from tkinter import *
|
||||||
from game.game import *
|
from tkinter import Menu as TkMenu
|
||||||
|
from tkinter import messagebox
|
||||||
|
|
||||||
from .styles import BG_COLOR,BG_TITLE_COLOR
|
from .styles import BG_COLOR,BG_TITLE_COLOR
|
||||||
|
from game.game import *
|
||||||
|
from theater import persiangulf, nevada, caucasus, start_generator
|
||||||
|
from userdata import logging as logging_module
|
||||||
|
|
||||||
|
import sys
|
||||||
|
import webbrowser
|
||||||
|
|
||||||
|
|
||||||
class Window:
|
class Window:
|
||||||
|
|
||||||
image = None
|
image = None
|
||||||
left_pane = None # type: Frame
|
left_pane = None # type: Frame
|
||||||
right_pane = None # type: Frame
|
right_pane = None # type: Frame
|
||||||
@ -10,11 +20,38 @@ class Window:
|
|||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.tk = Tk()
|
self.tk = Tk()
|
||||||
self.tk.title("DCS Liberation")
|
self.tk.title("DCS Liberation")
|
||||||
self.tk.iconbitmap("icon.ico")
|
self.tk.iconbitmap("resources/icon.ico")
|
||||||
self.tk.resizable(False, False)
|
self.tk.resizable(False, False)
|
||||||
self.tk.grid_columnconfigure(0, weight=1)
|
self.tk.grid_columnconfigure(0, weight=1)
|
||||||
self.tk.grid_rowconfigure(0, weight=1)
|
self.tk.grid_rowconfigure(0, weight=1)
|
||||||
|
|
||||||
|
self.frame = None
|
||||||
|
self.right_pane = None
|
||||||
|
self.left_pane = None
|
||||||
|
self.build()
|
||||||
|
|
||||||
|
menubar = TkMenu(self.tk)
|
||||||
|
filemenu = TkMenu(menubar, tearoff=0)
|
||||||
|
filemenu.add_command(label="New Game", command=lambda: self.new_game_confirm())
|
||||||
|
filemenu.add_separator()
|
||||||
|
filemenu.add_command(label="Exit", command=lambda: self.exit())
|
||||||
|
menubar.add_cascade(label="File", menu=filemenu)
|
||||||
|
|
||||||
|
helpmenu = TkMenu(menubar, tearoff=0)
|
||||||
|
helpmenu.add_command(label="Online Manual", command=lambda: webbrowser.open_new_tab("https://github.com/shdwp/dcs_liberation/wiki/Manual"))
|
||||||
|
helpmenu.add_command(label="Troubleshooting Guide", command=lambda: webbrowser.open_new_tab("https://github.com/shdwp/dcs_liberation/wiki/Troubleshooting"))
|
||||||
|
helpmenu.add_command(label="Modding Guide", command=lambda: webbrowser.open_new_tab("https://github.com/shdwp/dcs_liberation/wiki/Modding-tutorial"))
|
||||||
|
helpmenu.add_separator()
|
||||||
|
helpmenu.add_command(label="Contribute", command=lambda: webbrowser.open_new_tab("https://github.com/shdwp/dcs_liberation"))
|
||||||
|
helpmenu.add_command(label="Forum Thread", command=lambda: webbrowser.open_new_tab("https://forums.eagle.ru/showthread.php?t=214834"))
|
||||||
|
helpmenu.add_command(label="Report an issue", command=self.report_issue)
|
||||||
|
menubar.add_cascade(label="Help", menu=helpmenu)
|
||||||
|
|
||||||
|
self.tk.config(menu=menubar)
|
||||||
|
self.tk.focus()
|
||||||
|
|
||||||
|
|
||||||
|
def build(self):
|
||||||
self.frame = Frame(self.tk, bg=BG_COLOR)
|
self.frame = Frame(self.tk, bg=BG_COLOR)
|
||||||
self.frame.grid(column=0, row=0, sticky=NSEW)
|
self.frame.grid(column=0, row=0, sticky=NSEW)
|
||||||
self.frame.grid_columnconfigure(0)
|
self.frame.grid_columnconfigure(0)
|
||||||
@ -29,8 +66,6 @@ class Window:
|
|||||||
self.right_pane = Frame(self.frame, bg=BG_COLOR)
|
self.right_pane = Frame(self.frame, bg=BG_COLOR)
|
||||||
self.right_pane.grid(row=0, column=1, sticky=NSEW)
|
self.right_pane.grid(row=0, column=1, sticky=NSEW)
|
||||||
|
|
||||||
self.tk.focus()
|
|
||||||
|
|
||||||
def clear_right_pane(self):
|
def clear_right_pane(self):
|
||||||
for i in range(100):
|
for i in range(100):
|
||||||
self.right_pane.grid_columnconfigure(1, weight=0)
|
self.right_pane.grid_columnconfigure(1, weight=0)
|
||||||
@ -40,10 +75,70 @@ class Window:
|
|||||||
x.grid_remove()
|
x.grid_remove()
|
||||||
|
|
||||||
def clear(self):
|
def clear(self):
|
||||||
for x in self.left_pane.winfo_children():
|
def clear_recursive(x, n=50):
|
||||||
x.grid_remove()
|
if n < 0:
|
||||||
for x in self.right_pane.winfo_children():
|
return
|
||||||
x.grid_remove()
|
for y in x.winfo_children():
|
||||||
|
clear_recursive(y, n-1)
|
||||||
|
x.grid_forget()
|
||||||
|
|
||||||
|
clear_recursive(self.frame, 50)
|
||||||
|
self.left_pane.grid_remove()
|
||||||
|
self.right_pane.grid_remove()
|
||||||
|
self.build()
|
||||||
|
|
||||||
|
def start_new_game(self, player_name: str, enemy_name: str, terrain: str, sams: bool, midgame: bool, multiplier: float):
|
||||||
|
if terrain == "persiangulf":
|
||||||
|
conflicttheater = persiangulf.PersianGulfTheater()
|
||||||
|
elif terrain == "nevada":
|
||||||
|
conflicttheater = nevada.NevadaTheater()
|
||||||
|
else:
|
||||||
|
conflicttheater = caucasus.CaucasusTheater()
|
||||||
|
|
||||||
|
if midgame:
|
||||||
|
for i in range(0, int(len(conflicttheater.controlpoints) / 2)):
|
||||||
|
conflicttheater.controlpoints[i].captured = True
|
||||||
|
|
||||||
|
start_generator.generate_inital_units(conflicttheater, enemy_name, sams, multiplier)
|
||||||
|
start_generator.generate_groundobjects(conflicttheater)
|
||||||
|
game = Game(player_name=player_name,
|
||||||
|
enemy_name=enemy_name,
|
||||||
|
theater=conflicttheater)
|
||||||
|
game.budget = int(game.budget * multiplier)
|
||||||
|
game.settings.multiplier = multiplier
|
||||||
|
game.settings.sams = sams
|
||||||
|
game.settings.version = logging_module.version_string()
|
||||||
|
|
||||||
|
if midgame:
|
||||||
|
game.budget = game.budget * 4 * len(list(conflicttheater.conflicts()))
|
||||||
|
|
||||||
|
self.proceed_to_main_menu(game)
|
||||||
|
|
||||||
|
def proceed_to_main_menu(self, game: Game):
|
||||||
|
from ui.mainmenu import MainMenu
|
||||||
|
self.clear()
|
||||||
|
m = MainMenu(self, None, game)
|
||||||
|
m.display()
|
||||||
|
|
||||||
|
def proceed_to_new_game_menu(self):
|
||||||
|
from ui.newgamemenu import NewGameMenu
|
||||||
|
self.clear()
|
||||||
|
new_game_menu = NewGameMenu(self, self.start_new_game)
|
||||||
|
new_game_menu.display()
|
||||||
|
|
||||||
|
def new_game_confirm(self):
|
||||||
|
result = messagebox.askquestion("Start a new game", "Are you sure you want to start a new game ? Your current campaign will be overriden and there is no going back !", icon='warning')
|
||||||
|
if result == 'yes':
|
||||||
|
self.proceed_to_new_game_menu()
|
||||||
|
else:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def report_issue(self):
|
||||||
|
raise logging_module.ShowLogsException()
|
||||||
|
|
||||||
|
def exit(self):
|
||||||
|
self.tk.destroy()
|
||||||
|
sys.exit(0)
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
self.tk.mainloop()
|
self.tk.mainloop()
|
||||||
|
|||||||
@ -42,6 +42,10 @@ def parse_mutliplayer_debriefing(contents: str):
|
|||||||
key = "initiator"
|
key = "initiator"
|
||||||
if element is None:
|
if element is None:
|
||||||
element = {}
|
element = {}
|
||||||
|
elif line.startswith("initiatorMissionID\t"):
|
||||||
|
key = "initiatorMissionID"
|
||||||
|
if element is None:
|
||||||
|
element = {}
|
||||||
elif line.startswith("type\t"):
|
elif line.startswith("type\t"):
|
||||||
key = "type"
|
key = "type"
|
||||||
if element is None:
|
if element is None:
|
||||||
@ -76,7 +80,7 @@ class Debriefing:
|
|||||||
nonlocal dead_units
|
nonlocal dead_units
|
||||||
object_mission_id = int(object_mission_id_str)
|
object_mission_id = int(object_mission_id_str)
|
||||||
if object_mission_id in dead_units:
|
if object_mission_id in dead_units:
|
||||||
logging.info("debriefing: failed to append_dead_object {}: already exists!".format(object_mission_id))
|
logging.error("debriefing: failed to append_dead_object {}: already exists!".format(object_mission_id))
|
||||||
return
|
return
|
||||||
|
|
||||||
dead_units.append(object_mission_id)
|
dead_units.append(object_mission_id)
|
||||||
|
|||||||
@ -35,6 +35,10 @@ def setup_version_string(str):
|
|||||||
_version_string = str
|
_version_string = str
|
||||||
|
|
||||||
|
|
||||||
|
def version_string():
|
||||||
|
return _version_string
|
||||||
|
|
||||||
|
|
||||||
if "--stdout" in sys.argv:
|
if "--stdout" in sys.argv:
|
||||||
logging.basicConfig(stream=sys.stdout, level=logging.INFO)
|
logging.basicConfig(stream=sys.stdout, level=logging.INFO)
|
||||||
else:
|
else:
|
||||||
|
|||||||