mirror of
https://github.com/dcs-retribution/dcs-retribution.git
synced 2025-11-10 15:41:24 +00:00
game loop; player budget; enemy progression; GUI WIP
This commit is contained in:
committed by
Vasiliy Horbachenko
parent
4cd3c24b49
commit
ad4d183972
180
game/event.py
180
game/event.py
@@ -1,55 +1,185 @@
|
||||
import typing
|
||||
import random
|
||||
import math
|
||||
|
||||
import dcs
|
||||
|
||||
from theater.controlpoint import *
|
||||
from .mission import *
|
||||
from userdata.debriefing_parser import *
|
||||
from game.operation import *
|
||||
|
||||
DIFFICULTY_LOG_BASE = 1.5
|
||||
|
||||
|
||||
class Event:
|
||||
silent = False
|
||||
operation = None # type: Operation
|
||||
operation = None # type: Operation
|
||||
difficulty = 1 # type: int
|
||||
BONUS_BASE = 0
|
||||
|
||||
def failure(self):
|
||||
def __init__(self, attacker_name: str, defender_name: str, from_cp: ControlPoint, to_cp: ControlPoint):
|
||||
self.mission = dcs.mission.Mission()
|
||||
self.attacker = self.mission.country(attacker_name)
|
||||
self.defender = self.mission.country(defender_name)
|
||||
self.to_cp = to_cp
|
||||
self.from_cp = from_cp
|
||||
|
||||
def bonus(self) -> int:
|
||||
return math.ceil(math.log(self.difficulty, DIFFICULTY_LOG_BASE) * self.BONUS_BASE)
|
||||
|
||||
def is_successfull(self, debriefing: Debriefing) -> bool:
|
||||
return self.operation.is_successfull(debriefing)
|
||||
|
||||
def commit(self, debriefing: Debriefing):
|
||||
for country, losses in debriefing.destroyed_units.items():
|
||||
cp = None # type: ControlPoint
|
||||
if country == self.attacker.name:
|
||||
cp = self.from_cp
|
||||
else:
|
||||
cp = self.to_cp
|
||||
|
||||
cp.base.commit_losses(losses)
|
||||
|
||||
def skip(self):
|
||||
pass
|
||||
|
||||
def success(self):
|
||||
pass
|
||||
|
||||
class GroundInterceptEvent(Event):
|
||||
BONUS_BASE = 3
|
||||
TARGET_AMOUNT_FACTOR = 3
|
||||
TARGET_VARIETY = 3
|
||||
|
||||
def __str__(self):
|
||||
return "Ground intercept at {} ({})".format(self.to_cp, "*" * self.difficulty)
|
||||
|
||||
def commit(self, debriefing: Debriefing):
|
||||
super(GroundInterceptEvent, self).commit(debriefing)
|
||||
|
||||
if self.from_cp.captured:
|
||||
if self.is_successfull(debriefing):
|
||||
self.to_cp.base.affect_strength(-0.1)
|
||||
else:
|
||||
self.to_cp.base.affect_strength(+0.1)
|
||||
else:
|
||||
assert False
|
||||
|
||||
def skip(self):
|
||||
if not self.to_cp.captured:
|
||||
self.to_cp.base.affect_strength(+0.1)
|
||||
else:
|
||||
pass
|
||||
|
||||
def player_attacking(self, position: Point, strikegroup: typing.Dict[PlaneType, int]):
|
||||
suitable_unittypes = db.find_unittype(CAP, self.defender.name)
|
||||
random.shuffle(suitable_unittypes)
|
||||
unittypes = suitable_unittypes[:self.TARGET_VARIETY]
|
||||
typecount = max(math.floor(self.difficulty * self.TARGET_AMOUNT_FACTOR), 1)
|
||||
targets = {unittype: typecount for unittype in unittypes}
|
||||
|
||||
self.operation = GroundInterceptOperation(mission=self.mission,
|
||||
attacker=self.attacker,
|
||||
defender=self.defender,
|
||||
position=position,
|
||||
target=targets,
|
||||
strikegroup=strikegroup)
|
||||
|
||||
|
||||
class InterceptEvent(Event):
|
||||
pass
|
||||
ESCORT_AMOUNT_FACTOR = 2
|
||||
BONUS_BASE = 5
|
||||
|
||||
def __str__(self):
|
||||
return "Intercept at {} ({})".format(self.to_cp, "*" * self.difficulty)
|
||||
|
||||
def commit(self, debriefing: Debriefing):
|
||||
super(InterceptEvent, self).commit(debriefing)
|
||||
if self.is_successfull(debriefing):
|
||||
self.to_cp.base.affect_strength(0.1 * self.from_cp.captured and -1 or 1)
|
||||
else:
|
||||
self.to_cp.base.affect_strength(0.1 * self.from_cp.captured and 1 or -1)
|
||||
|
||||
def skip(self):
|
||||
if self.to_cp.captured:
|
||||
self.to_cp.base.affect_strength(-0.2)
|
||||
|
||||
def player_attacking(self, interceptors: typing.Dict[PlaneType, int]):
|
||||
escort = self.to_cp.base.scramble_sweep(self.to_cp)
|
||||
transport_unit = random.choice(db.find_unittype(Transport, self.defender.name))
|
||||
assert transport_unit is not None
|
||||
|
||||
self.operation = InterceptOperation(mission=self.mission,
|
||||
attacker=self.attacker,
|
||||
defender=self.defender,
|
||||
destination=self.to_cp,
|
||||
destination_port=self.to_cp.airport,
|
||||
escort=escort,
|
||||
transport={transport_unit: 1},
|
||||
interceptors=interceptors)
|
||||
|
||||
def player_defending(self, escort: typing.Dict[PlaneType, int]):
|
||||
interceptors = self.from_cp.base.scramble_interceptors_count(self.difficulty * self.ESCORT_AMOUNT_FACTOR)
|
||||
transport_unit = random.choice(db.find_unittype(Transport, self.defender.name))
|
||||
assert transport_unit is not None
|
||||
|
||||
self.operation = InterceptOperation(mission=self.mission,
|
||||
attacker=self.attacker,
|
||||
defender=self.defender,
|
||||
destination=self.to_cp,
|
||||
destination_port=self.to_cp.airport,
|
||||
escort=escort,
|
||||
transport={transport_unit: 1},
|
||||
interceptors=interceptors)
|
||||
|
||||
|
||||
class CaptureEvent(Event):
|
||||
silent = True
|
||||
BONUS_BASE = 7
|
||||
|
||||
def __init__(self, from_cp: ControlPoint, to_cp: ControlPoint):
|
||||
pass
|
||||
def __str__(self):
|
||||
return "Capture {} ({})".format(self.to_cp, "*" * self.difficulty)
|
||||
|
||||
def player_defending(self, from_cp: ControlPoint, to_cp: ControlPoint, interceptors: typing.Dict[PlaneType, int]):
|
||||
assert not self.operation
|
||||
def commit(self, debriefing: Debriefing):
|
||||
super(CaptureEvent, self).commit(debriefing)
|
||||
if self.is_successfull(debriefing):
|
||||
if self.from_cp.captured:
|
||||
self.to_cp.captured = True
|
||||
else:
|
||||
if not self.from_cp.captured:
|
||||
self.to_cp.captured = False
|
||||
self.to_cp.base.affect_strength(+0.5)
|
||||
|
||||
cas = from_cp.base.scramble_cas(to_cp)
|
||||
escort = from_cp.base.scramble_sweep(to_cp)
|
||||
attackers = from_cp.base.assemble_cap(to_cp)
|
||||
def skip(self):
|
||||
if self.to_cp.captured:
|
||||
self.to_cp.captured = False
|
||||
|
||||
self.operation = CaptureOperation(from_cp=from_cp,
|
||||
to_cp=to_cp,
|
||||
def player_defending(self, interceptors: typing.Dict[PlaneType, int]):
|
||||
cas = self.from_cp.base.scramble_cas(self.to_cp)
|
||||
escort = self.from_cp.base.scramble_sweep(self.to_cp)
|
||||
attackers = self.from_cp.base.assemble_cap(self.to_cp)
|
||||
|
||||
self.operation = CaptureOperation(mission=self.mission,
|
||||
attacker=self.attacker,
|
||||
defender=self.defender,
|
||||
from_cp=self.from_cp,
|
||||
to_cp=self.to_cp,
|
||||
cas=cas,
|
||||
escort=escort,
|
||||
attack=attackers,
|
||||
intercept=interceptors,
|
||||
defense=to_cp.base.armor,
|
||||
aa=to_cp.base.aa)
|
||||
defense=self.to_cp.base.armor,
|
||||
aa=self.to_cp.base.aa)
|
||||
|
||||
def player_attacking(self, from_cp: ControlPoint, to_cp: ControlPoint, cas: typing.Dict[PlaneType, int], escort: typing.Dict[PlaneType, int], armor: typing.Dict[Armor, int]):
|
||||
assert not self.operation
|
||||
def player_attacking(self, cas: typing.Dict[PlaneType, int], escort: typing.Dict[PlaneType, int], armor: typing.Dict[Armor, int]):
|
||||
interceptors = self.to_cp.base.scramble_sweep(for_target=self.to_cp)
|
||||
|
||||
interceptors = to_cp.base.scramble_sweep()
|
||||
|
||||
self.operation = CaptureOperation(from_cp=from_cp,
|
||||
to_cp=to_cp,
|
||||
self.operation = CaptureOperation(mission=self.mission,
|
||||
attacker=self.attacker,
|
||||
defender=self.defender,
|
||||
from_cp=self.from_cp,
|
||||
to_cp=self.to_cp,
|
||||
cas=cas,
|
||||
escort=escort,
|
||||
attack=armor,
|
||||
intercept=interceptors,
|
||||
defense=to_cp.base.armor,
|
||||
aa=to_cp.base.aa)
|
||||
defense=self.to_cp.base.armor,
|
||||
aa=self.to_cp.base.aa)
|
||||
8
game/event_results.py
Normal file
8
game/event_results.py
Normal file
@@ -0,0 +1,8 @@
|
||||
import typing
|
||||
import dcs
|
||||
|
||||
from game.event import *
|
||||
|
||||
|
||||
|
||||
|
||||
128
game/game.py
128
game/game.py
@@ -1,21 +1,135 @@
|
||||
import typing
|
||||
import random
|
||||
|
||||
from theater.conflicttheater import *
|
||||
from theater.controlpoint import *
|
||||
from .event import *
|
||||
from userdata.debriefing_parser import *
|
||||
from game.event import *
|
||||
|
||||
COMMISION_LIMITS_SCALE = 2
|
||||
COMMISION_LIMITS_FACTORS = {
|
||||
CAP: 2,
|
||||
CAS: 1,
|
||||
FighterSweep: 3,
|
||||
AirDefence: 2,
|
||||
}
|
||||
|
||||
COMMISION_AMOUNTS_SCALE = 2
|
||||
COMMISION_AMOUNTS_FACTORS = {
|
||||
CAP: 0.6,
|
||||
CAS: 0.3,
|
||||
FighterSweep: 0.5,
|
||||
AirDefence: 0.3,
|
||||
}
|
||||
|
||||
|
||||
ENEMY_INTERCEPT_PROBABILITY_BASE = 25
|
||||
ENEMY_CAPTURE_PROBABILITY_BASE = 15
|
||||
|
||||
PLAYER_INTERCEPT_PROBABILITY_BASE = 30
|
||||
PLAYER_GROUNDINTERCEPT_PROBABILITY_BASE = 30
|
||||
|
||||
PLAYER_BUDGET_BASE = 25
|
||||
PLAYER_BUDGET_IMPORTANCE_LOG = 2
|
||||
|
||||
|
||||
class Game:
|
||||
events = [] # type: typing.List[Event]
|
||||
budget = 45
|
||||
events = None # type: typing.List[Event]
|
||||
|
||||
def __init__(self, theater: ConflictTheater):
|
||||
self.events = []
|
||||
self.theater = theater
|
||||
self.player = "USA"
|
||||
self.enemy = "Russia"
|
||||
|
||||
def _roll(self, prob, mult):
|
||||
return random.randint(0, 100) <= prob * mult
|
||||
|
||||
def _fill_cap_events(self):
|
||||
for cp in [x for x in self.theater.controlpoints if x.captured]:
|
||||
for connected_cp in [x for x in cp.connected_points if not x.captured]:
|
||||
self.events.append(CaptureEvent(cp, connected_cp))
|
||||
for from_cp, to_cp in self.theater.conflicts(True):
|
||||
self.events.append(CaptureEvent(attacker_name=self.player,
|
||||
defender_name=self.enemy,
|
||||
from_cp=from_cp,
|
||||
to_cp=to_cp))
|
||||
|
||||
def _generate_enemy_caps(self):
|
||||
for from_cp, to_cp in self.theater.conflicts(False):
|
||||
if self._roll(ENEMY_CAPTURE_PROBABILITY_BASE, from_cp.base.strength):
|
||||
self.events.append(CaptureEvent(attacker_name=self.enemy,
|
||||
defender_name=self.player,
|
||||
from_cp=from_cp,
|
||||
to_cp=to_cp))
|
||||
break
|
||||
|
||||
def _generate_interceptions(self):
|
||||
for from_cp, to_cp in self.theater.conflicts(False):
|
||||
if self._roll(ENEMY_INTERCEPT_PROBABILITY_BASE, from_cp.base.strength):
|
||||
self.events.append(InterceptEvent(attacker_name=self.enemy,
|
||||
defender_name=self.player,
|
||||
from_cp=from_cp,
|
||||
to_cp=to_cp))
|
||||
break
|
||||
|
||||
for from_cp, to_cp in self.theater.conflicts(True):
|
||||
if self._roll(PLAYER_INTERCEPT_PROBABILITY_BASE, from_cp.base.strength):
|
||||
self.events.append(InterceptEvent(attacker_name=self.player,
|
||||
defender_name=self.enemy,
|
||||
from_cp=from_cp,
|
||||
to_cp=to_cp))
|
||||
break
|
||||
|
||||
def _generate_groundinterceptions(self):
|
||||
for from_cp, to_cp in self.theater.conflicts(True):
|
||||
if self._roll(PLAYER_GROUNDINTERCEPT_PROBABILITY_BASE, from_cp.base.strength):
|
||||
self.events.append(GroundInterceptEvent(attacker_name=self.player,
|
||||
defender_name=self.enemy,
|
||||
from_cp=from_cp,
|
||||
to_cp=to_cp))
|
||||
break
|
||||
|
||||
def _commision_units(self, cp: ControlPoint):
|
||||
for for_task in [CAP, CAS, FighterSweep, AirDefence]:
|
||||
limit = COMMISION_LIMITS_FACTORS[for_task] * math.pow(cp.importance, COMMISION_LIMITS_SCALE)
|
||||
missing_units = limit - cp.base.total_units(for_task)
|
||||
if missing_units > 0:
|
||||
awarded_points = COMMISION_AMOUNTS_FACTORS[for_task] * math.pow(cp.importance, COMMISION_AMOUNTS_SCALE)
|
||||
points_to_spend = cp.base.append_commision_points(for_task, awarded_points)
|
||||
if points_to_spend > 0:
|
||||
unit_type = random.choice(db.find_unittype(for_task, self.enemy))
|
||||
cp.base.commision_units({unit_type: points_to_spend})
|
||||
|
||||
def _budget_player(self):
|
||||
total_importance = sum([x.importance for x in self.theater.player_points()])
|
||||
total_strength = sum([x.base.strength for x in self.theater.player_points()]) / len(self.theater.player_points())
|
||||
|
||||
self.budget += math.ceil(math.log(total_importance * total_strength + 1, PLAYER_BUDGET_IMPORTANCE_LOG) * PLAYER_BUDGET_BASE)
|
||||
|
||||
def initiate_event(self, event: Event):
|
||||
event.operation.generate()
|
||||
event.mission.save("build/next_mission.miz")
|
||||
|
||||
def finish_event(self, event: Event, debriefing: Debriefing):
|
||||
event.commit(debriefing)
|
||||
if event.is_successfull(debriefing):
|
||||
self.budget += event.bonus()
|
||||
|
||||
self.events.remove(event)
|
||||
|
||||
def is_player_attack(self, event: Event):
|
||||
return event.attacker.name == self.player
|
||||
|
||||
def pass_turn(self):
|
||||
self.events = [] # type: typing.List[Event]
|
||||
self._fill_cap_events()
|
||||
for event in self.events:
|
||||
event.skip()
|
||||
|
||||
self._budget_player()
|
||||
for cp in self.theater.enemy_bases():
|
||||
self._commision_units(cp)
|
||||
|
||||
self.events = [] # type: typing.List[Event]
|
||||
self._fill_cap_events()
|
||||
self._generate_enemy_caps()
|
||||
self._generate_interceptions()
|
||||
self._generate_groundinterceptions()
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import typing
|
||||
|
||||
from globals import *
|
||||
from userdata.debriefing_parser import *
|
||||
from dcs.mission import *
|
||||
from dcs.unitgroup import *
|
||||
from dcs.vehicles import *
|
||||
@@ -22,6 +23,15 @@ class Operation:
|
||||
self.airgen = AircraftConflictGenerator(self.mission, self.conflict)
|
||||
self.aagen = AAConflictGenerator(self.mission, self.conflict)
|
||||
|
||||
def units_of(self, country_name: str) -> typing.Collection[UnitType]:
|
||||
return []
|
||||
|
||||
def is_successfull(self, debriefing: Debriefing) -> bool:
|
||||
return True
|
||||
|
||||
def generate(self):
|
||||
pass
|
||||
|
||||
|
||||
class CaptureOperation(Operation):
|
||||
def __init__(self,
|
||||
Reference in New Issue
Block a user