mirror of
https://github.com/dcs-retribution/dcs-retribution.git
synced 2025-11-10 15:41:24 +00:00
prompt window with logs on raised exception; minor UI updates; minor fixes
This commit is contained in:
parent
73d4a2d414
commit
9dbc9a8a56
@ -14,7 +14,7 @@ import ui.corruptedsavemenu
|
||||
|
||||
from game.game import Game
|
||||
from theater import start_generator
|
||||
from userdata import persistency
|
||||
from userdata import persistency, logging
|
||||
|
||||
|
||||
persistency.setup(sys.argv[1])
|
||||
|
||||
@ -463,6 +463,9 @@ def unitdict_split(unit_dict: UnitsDict, count: int):
|
||||
|
||||
|
||||
def unitdict_restrict_count(unit_dict: UnitsDict, total_count: int) -> UnitsDict:
|
||||
if total_count == 0:
|
||||
return {}
|
||||
|
||||
groups = list(unitdict_split(unit_dict, total_count))
|
||||
if len(groups) > 0:
|
||||
return groups[0]
|
||||
|
||||
@ -17,7 +17,7 @@ class AntiAAStrikeEvent(Event):
|
||||
targets = None # type: db.ArmorDict
|
||||
|
||||
def __str__(self):
|
||||
return "Anti-AA strike from {} at {}".format(self.from_cp, self.to_cp)
|
||||
return "Anti-AA strike"
|
||||
|
||||
def is_successfull(self, debriefing: Debriefing):
|
||||
total_targets = sum(self.targets.values())
|
||||
|
||||
@ -16,7 +16,7 @@ class BaseAttackEvent(Event):
|
||||
STRENGTH_RECOVERY = 0.55
|
||||
|
||||
def __str__(self):
|
||||
return "Base attack from {} to {}".format(self.from_cp, self.to_cp)
|
||||
return "Base attack"
|
||||
|
||||
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])
|
||||
|
||||
@ -25,7 +25,7 @@ class FrontlineAttackEvent(Event):
|
||||
return "{} vehicles".format(self.to_cp.base.assemble_count())
|
||||
|
||||
def __str__(self):
|
||||
return "Frontline attack from {} at {}".format(self.from_cp, self.to_cp)
|
||||
return "Frontline attack"
|
||||
|
||||
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])
|
||||
|
||||
@ -23,7 +23,7 @@ class FrontlinePatrolEvent(Event):
|
||||
return "{} aircraft + ? CAS".format(self.to_cp.base.scramble_count(self.game.settings.multiplier * self.ESCORT_FACTOR, CAP))
|
||||
|
||||
def __str__(self):
|
||||
return "Frontline CAP from {} at {}".format(self.from_cp, self.to_cp)
|
||||
return "Frontline CAP"
|
||||
|
||||
"""
|
||||
def is_successfull(self, debriefing: Debriefing):
|
||||
|
||||
@ -16,7 +16,7 @@ class InfantryTransportEvent(Event):
|
||||
STRENGTH_INFLUENCE = 0.3
|
||||
|
||||
def __str__(self):
|
||||
return "Frontline transport troops to {}".format(self.to_cp)
|
||||
return "Frontline transport troops"
|
||||
|
||||
def is_successfull(self, debriefing: Debriefing):
|
||||
return True
|
||||
|
||||
@ -19,7 +19,7 @@ class InsurgentAttackEvent(Event):
|
||||
return ""
|
||||
|
||||
def __str__(self):
|
||||
return "Destroy insurgents at {}".format(self.to_cp)
|
||||
return "Destroy insurgents"
|
||||
|
||||
def is_successfull(self, debriefing: Debriefing):
|
||||
killed_units = sum([v for k, v in debriefing.destroyed_units[self.attacker_name].items() if db.unit_task(k) == PinpointStrike])
|
||||
|
||||
@ -20,7 +20,7 @@ class InterceptEvent(Event):
|
||||
transport_unit = None # type: FlyingType
|
||||
|
||||
def __str__(self):
|
||||
return "Intercept from {} at {}".format(self.from_cp, self.to_cp)
|
||||
return "Intercept"
|
||||
|
||||
def _enemy_scramble_multiplier(self) -> float:
|
||||
is_global = self.from_cp.is_global or self.to_cp.is_global
|
||||
|
||||
@ -24,7 +24,7 @@ class NavalInterceptEvent(Event):
|
||||
return max(int(factor), 1)
|
||||
|
||||
def __str__(self) -> str:
|
||||
return "Naval intercept at {}".format(self.to_cp)
|
||||
return "Naval intercept"
|
||||
|
||||
@property
|
||||
def threat_description(self):
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import logging
|
||||
import typing
|
||||
import random
|
||||
import math
|
||||
@ -171,7 +172,7 @@ class Game:
|
||||
if points_to_spend > 0:
|
||||
unittypes = self.commision_unit_types(cp, for_task)
|
||||
d = {random.choice(unittypes): points_to_spend}
|
||||
print("Commision {}: {}".format(cp, d))
|
||||
logging.info("Commision {}: {}".format(cp, d))
|
||||
cp.base.commision_units(d)
|
||||
|
||||
@property
|
||||
@ -204,10 +205,13 @@ class Game:
|
||||
def initiate_event(self, event: Event):
|
||||
assert event in self.events
|
||||
|
||||
logging.info("Generating {} (regular)".format(event))
|
||||
event.generate()
|
||||
logging.info("Generating {} (quick)".format(event))
|
||||
event.generate_quick()
|
||||
|
||||
def finish_event(self, event: Event, debriefing: Debriefing):
|
||||
logging.info("Finishing event {}".format(event))
|
||||
event.commit(debriefing)
|
||||
if event.is_successfull(debriefing):
|
||||
self.budget += event.bonus()
|
||||
@ -215,7 +219,7 @@ class Game:
|
||||
if event in self.events:
|
||||
self.events.remove(event)
|
||||
else:
|
||||
print("finish_event: event not in the events!")
|
||||
logging.info("finish_event: event not in the events!")
|
||||
|
||||
def is_player_attack(self, event):
|
||||
if isinstance(event, Event):
|
||||
@ -224,6 +228,7 @@ class Game:
|
||||
return event.name == self.player
|
||||
|
||||
def pass_turn(self, no_action=False, ignored_cps: typing.Collection[ControlPoint]=None):
|
||||
logging.info("Pass turn")
|
||||
for event in self.events:
|
||||
event.skip()
|
||||
|
||||
|
||||
@ -1,3 +1,5 @@
|
||||
import logging
|
||||
|
||||
from game import db
|
||||
from game.settings import Settings
|
||||
from .conflictgen import *
|
||||
@ -14,7 +16,7 @@ SPREAD_DISTANCE_FACTOR = 1, 2
|
||||
ESCORT_ENGAGEMENT_MAX_DIST = 100000
|
||||
WORKAROUND_WAYP_DIST = 1000
|
||||
|
||||
WARM_START_HELI_AIRSPEED = 200
|
||||
WARM_START_HELI_AIRSPEED = 120
|
||||
WARM_START_HELI_ALT = 1000
|
||||
|
||||
WARM_START_ALTITUDE = 3000
|
||||
@ -106,7 +108,7 @@ class AircraftConflictGenerator:
|
||||
assert count > 0
|
||||
assert unit is not None
|
||||
|
||||
print("airgen: {} for {} at {}".format(unit_type, side.id, airport))
|
||||
logging.info("airgen: {} for {} at {}".format(unit_type, side.id, airport))
|
||||
return self.m.flight_group_from_airport(
|
||||
country=side,
|
||||
name=name,
|
||||
@ -121,7 +123,7 @@ class AircraftConflictGenerator:
|
||||
assert count > 0
|
||||
assert unit is not None
|
||||
|
||||
if unit_type in helicopters.helicopter_map:
|
||||
if unit_type in helicopters.helicopter_map.values():
|
||||
alt = WARM_START_HELI_ALT + random.randint(50, 200)
|
||||
speed = WARM_START_HELI_AIRSPEED
|
||||
else:
|
||||
@ -130,7 +132,7 @@ class AircraftConflictGenerator:
|
||||
|
||||
pos = Point(at.x + random.randint(100, 200), at.y + random.randint(100, 200))
|
||||
|
||||
print("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(
|
||||
country=side,
|
||||
name=name,
|
||||
@ -147,7 +149,7 @@ class AircraftConflictGenerator:
|
||||
assert count > 0
|
||||
assert unit is not None
|
||||
|
||||
print("airgen: {} for {} at carrier {}".format(unit_type, side.id, at))
|
||||
logging.info("airgen: {} for {} at carrier {}".format(unit_type, side.id, at))
|
||||
return self.m.flight_group_from_unit(
|
||||
country=side,
|
||||
name=name,
|
||||
|
||||
@ -1,3 +1,5 @@
|
||||
import logging
|
||||
|
||||
from random import randint
|
||||
from itertools import zip_longest
|
||||
|
||||
@ -34,7 +36,7 @@ class ArmorConflictGenerator:
|
||||
|
||||
def _generate_group(self, side: Country, unit: VehicleType, count: int, at: Point, to: Point = None):
|
||||
for c in range(count):
|
||||
print("armorgen: {} for {}".format(unit, side.id))
|
||||
logging.info("armorgen: {} for {}".format(unit, side.id))
|
||||
group = self.m.vehicle_group(
|
||||
side,
|
||||
namegen.next_unit_name(side, unit),
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import logging
|
||||
import typing
|
||||
import pdb
|
||||
import dcs
|
||||
@ -182,7 +183,7 @@ class Conflict:
|
||||
|
||||
initial = initial.point_from_heading(heading, 800)
|
||||
|
||||
print("Didn't find ground position!")
|
||||
logging.info("Didn't find ground position!")
|
||||
return None
|
||||
|
||||
@classmethod
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import logging
|
||||
import typing
|
||||
import random
|
||||
from datetime import datetime, timedelta, time
|
||||
@ -68,7 +69,7 @@ class EnviromentGenerator:
|
||||
weather_type = k
|
||||
break
|
||||
|
||||
print("generated weather {}".format(weather_type))
|
||||
logging.info("generated weather {}".format(weather_type))
|
||||
if weather_type == 1:
|
||||
self.mission.weather.heavy_rain()
|
||||
elif weather_type == 2:
|
||||
|
||||
@ -1,3 +1,5 @@
|
||||
import logging
|
||||
|
||||
from game import db
|
||||
from .conflictgen import *
|
||||
from .naming import *
|
||||
@ -28,7 +30,7 @@ class ShipGenerator:
|
||||
def generate_cargo(self, units: db.ShipDict) -> typing.Collection[ShipGroup]:
|
||||
groups = []
|
||||
for unit_type, unit_count in units.items():
|
||||
print("shipgen: {} ({}) for {}".format(unit_type, unit_count, self.conflict.defenders_side))
|
||||
logging.info("shipgen: {} ({}) for {}".format(unit_type, unit_count, self.conflict.defenders_side))
|
||||
group = self.m.ship_group(
|
||||
country=self.conflict.defenders_side,
|
||||
name=namegen.next_unit_name(self.conflict.defenders_side, unit_type),
|
||||
|
||||
@ -1 +1 @@
|
||||
py.exe __init__.py "%UserProfile%" > logs.txt 2>&1
|
||||
py.exe __init__.py "%UserProfile%"
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import logging
|
||||
import typing
|
||||
import math
|
||||
import itertools
|
||||
@ -53,7 +54,7 @@ class Base:
|
||||
|
||||
def _find_best_unit(self, dict, for_type: Task, count: int) -> typing.Dict:
|
||||
if count <= 0:
|
||||
print("{}: no units for {}".format(self, for_type))
|
||||
logging.info("{}: no units for {}".format(self, for_type))
|
||||
return {}
|
||||
|
||||
sorted_units = [key for key in dict.keys() if key in db.UNIT_BY_TASK[for_type]]
|
||||
@ -74,7 +75,7 @@ class Base:
|
||||
assert result_unit_count > 0
|
||||
result[unit_type] = result.get(unit_type, 0) + result_unit_count
|
||||
|
||||
print("{} for {} ({}): {}".format(self, for_type, count, result))
|
||||
logging.info("{} for {} ({}): {}".format(self, for_type, count, result))
|
||||
return result
|
||||
|
||||
def _find_best_planes(self, for_type: Task, count: int) -> typing.Dict[PlaneType, int]:
|
||||
|
||||
@ -35,5 +35,5 @@ def generate_initial(theater: ConflictTheater, enemy: str, sams: bool, multiplie
|
||||
count = max(COUNT_BY_TASK[task] * multiplier * (1+count_log), 1)
|
||||
count_per_type = max(int(float(count) / len(unittypes)), 1)
|
||||
for unit_type in unittypes:
|
||||
print("{} - {} {}".format(cp.name, db.unit_type_name(unit_type), count_per_type))
|
||||
logging.info("{} - {} {}".format(cp.name, db.unit_type_name(unit_type), count_per_type))
|
||||
cp.base.commision_units({unit_type: count_per_type})
|
||||
|
||||
@ -9,6 +9,7 @@ from game.event import *
|
||||
UNITTYPES_FOR_EVENTS = {
|
||||
FrontlineAttackEvent: [CAS, PinpointStrike],
|
||||
FrontlinePatrolEvent: [CAP, PinpointStrike],
|
||||
BaseAttackEvent: [CAP, CAS, PinpointStrike],
|
||||
InterceptEvent: [CAP],
|
||||
InsurgentAttackEvent: [CAS],
|
||||
NavalInterceptEvent: [CAS],
|
||||
|
||||
@ -22,21 +22,20 @@ class EventResultsMenu(Menu):
|
||||
self.window.clear_right_pane()
|
||||
|
||||
if not self.finished:
|
||||
"""
|
||||
For debugging purposes
|
||||
|
||||
Button(self.frame, text="no losses, succ", command=self.simulate_result(0, 1)).grid()
|
||||
Button(self.frame, text="no losses, fail", command=self.simulate_result(0, 1)).grid(row=1, column=1)
|
||||
|
||||
Button(self.frame, text="half losses, succ", command=self.simulate_result(0.5, 0.5)).grid(row=2, )
|
||||
Button(self.frame, text="half losses, fail", command=self.simulate_result(0.5, 0.5)).grid(row=2, column=1)
|
||||
|
||||
Button(self.frame, text="full losses, succ", command=self.simulate_result(1, 0)).grid(row=3, )
|
||||
Button(self.frame, text="full losses, fail", command=self.simulate_result(1, 0)).grid(row=3, column=1)
|
||||
"""
|
||||
|
||||
Label(self.frame, text="Play the mission and save debriefing to").grid(row=0, column=0)
|
||||
Label(self.frame, text=debriefing_directory_location()).grid(row=1, column=0)
|
||||
|
||||
"""
|
||||
For debugging purposes
|
||||
"""
|
||||
|
||||
row = 3
|
||||
Separator(self.frame, orient=HORIZONTAL).grid(row=row, sticky=EW); row += 1
|
||||
Label(self.frame, text="Cheat operation results: ").grid(row=row); row += 1
|
||||
Button(self.frame, text="full enemy losses", command=self.simulate_result(0, 1)).grid(row=row); row += 1
|
||||
Button(self.frame, text="full player losses", command=self.simulate_result(1, 0)).grid(row=row); row += 1
|
||||
Button(self.frame, text="some enemy losses", command=self.simulate_result(0, 0.8)).grid(row=row); row += 1
|
||||
Button(self.frame, text="some player losses", command=self.simulate_result(0.8, 0)).grid(row=row); row += 1
|
||||
else:
|
||||
row = 0
|
||||
if self.event.is_successfull(self.debriefing):
|
||||
@ -81,27 +80,53 @@ class EventResultsMenu(Menu):
|
||||
def action():
|
||||
debriefing = Debriefing({})
|
||||
|
||||
def count_planes(groups: typing.List[FlyingGroup], mult: float) -> typing.Dict[UnitType, int]:
|
||||
def count(country: Country) -> typing.Dict[UnitType, int]:
|
||||
result = {}
|
||||
for group in groups:
|
||||
for g in country.plane_group + country.vehicle_group + country.helicopter_group + country.ship_group:
|
||||
group = g # type: Group
|
||||
for unit in group.units:
|
||||
result[unit.unit_type] = result.get(unit.unit_type, 0) + 1 * mult
|
||||
unit_type = None
|
||||
if isinstance(unit, Vehicle):
|
||||
unit_type = vehicle_map[unit.type]
|
||||
elif isinstance(unit, Ship):
|
||||
unit_type = ship_map[unit.type]
|
||||
else:
|
||||
unit_type = unit.unit_type
|
||||
|
||||
return {x: math.ceil(y) for x, y in result.items() if y >= 1}
|
||||
if unit_type in db.EXTRA_AA.values():
|
||||
continue
|
||||
|
||||
player_planes = self.event.operation.mission.country(self.game.player).plane_group
|
||||
enemy_planes = self.event.operation.mission.country(self.game.enemy).plane_group
|
||||
result[unit_type] = result.get(unit_type, 0) + 1
|
||||
|
||||
self.player_losses = count_planes(player_planes, player_factor)
|
||||
self.enemy_losses = count_planes(enemy_planes, enemy_factor)
|
||||
return result
|
||||
|
||||
player = self.event.operation.mission.country(self.game.player)
|
||||
enemy = self.event.operation.mission.country(self.game.enemy)
|
||||
|
||||
alive_player_units = count(player)
|
||||
alive_enemy_units = count(enemy)
|
||||
|
||||
destroyed_player_units = db.unitdict_restrict_count(alive_player_units, math.ceil(sum(alive_player_units.values()) * player_factor))
|
||||
destroyed_enemy_units = db.unitdict_restrict_count(alive_enemy_units, math.ceil(sum(alive_enemy_units.values()) * enemy_factor))
|
||||
|
||||
alive_player_units = {k: v - destroyed_player_units.get(k, 0) for k, v in alive_player_units.items()}
|
||||
alive_enemy_units = {k: v - destroyed_enemy_units.get(k, 0) for k, v in alive_enemy_units.items()}
|
||||
|
||||
debriefing.alive_units = {
|
||||
enemy.name: alive_enemy_units,
|
||||
player.name: alive_player_units,
|
||||
}
|
||||
|
||||
debriefing.destroyed_units = {
|
||||
self.game.player: self.player_losses,
|
||||
self.game.enemy: self.enemy_losses,
|
||||
player.name: destroyed_player_units,
|
||||
enemy.name: destroyed_enemy_units,
|
||||
}
|
||||
|
||||
self.finished = True
|
||||
self.debriefing = debriefing
|
||||
self.player_losses = debriefing.destroyed_units.get(self.game.player, {})
|
||||
self.enemy_losses = debriefing.destroyed_units.get(self.game.enemy, {})
|
||||
|
||||
self.game.finish_event(self.event, debriefing)
|
||||
self.display()
|
||||
self.game.pass_turn()
|
||||
|
||||
@ -34,13 +34,18 @@ class MainMenu(Menu):
|
||||
|
||||
def event_button(event):
|
||||
nonlocal row
|
||||
Message(self.frame, text="{}{}".format(
|
||||
Message(self.frame, text="{}{} at {}".format(
|
||||
event.defender_name == self.game.player and "Enemy attacking: " or "",
|
||||
event
|
||||
event,
|
||||
event.to_cp,
|
||||
), aspect=1600).grid(column=0, row=row, sticky=NW)
|
||||
Button(self.frame, text=">", command=self.start_event(event)).grid(column=0, row=row, sticky=NE+S)
|
||||
row += 1
|
||||
Separator(self.frame, orient='horizontal').grid(row=row, sticky=EW); row += 1
|
||||
Button(self.frame, text=">", command=self.start_event(event)).grid(column=0, row=row, sticky=NE+S); row += 1
|
||||
|
||||
def destination_header(text, separator=True):
|
||||
nonlocal row
|
||||
if separator:
|
||||
Separator(self.frame, orient=HORIZONTAL).grid(row=row, sticky=EW); row += 1
|
||||
Label(self.frame, text=text).grid(column=0, row=row, sticky=N); row += 1
|
||||
|
||||
Button(self.frame, text="Configuration", command=self.configuration_menu).grid(column=0, row=0, sticky=NE)
|
||||
Button(self.frame, text="Pass turn", command=self.pass_turn).grid(column=0, row=0, sticky=NW)
|
||||
@ -48,9 +53,20 @@ class MainMenu(Menu):
|
||||
Separator(self.frame, orient='horizontal').grid(row=row, sticky=EW); row += 1
|
||||
|
||||
events = self.game.events
|
||||
events.sort(key=lambda x: x.from_cp.name)
|
||||
events.sort(key=lambda x: x.informational and 2 or (self.game.is_player_attack(x) and 1 or 0))
|
||||
|
||||
destination = None
|
||||
for event in events:
|
||||
if not event.informational:
|
||||
if self.game.is_player_attack(event):
|
||||
new_destination = event.from_cp.name
|
||||
else:
|
||||
new_destination = "Enemy attack"
|
||||
if destination != new_destination:
|
||||
destination_header(new_destination, destination is not None)
|
||||
destination = new_destination
|
||||
|
||||
if event.informational:
|
||||
label(str(event))
|
||||
else:
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import logging
|
||||
import typing
|
||||
import re
|
||||
import threading
|
||||
@ -83,17 +84,16 @@ class Debriefing:
|
||||
|
||||
try:
|
||||
components = event["initiator"].split("|")
|
||||
print(components)
|
||||
category, country_id, group_id, unit_type = components[0], int(components[1]), int(components[2]), db.unit_type_from_name(components[3])
|
||||
if unit_type is None:
|
||||
print("Skipped due to no unit type")
|
||||
logging.info("Skipped due to no unit type")
|
||||
continue
|
||||
|
||||
if category != "unit":
|
||||
print("Skipped due to category")
|
||||
logging.info("Skipped due to category")
|
||||
continue
|
||||
except Exception as e:
|
||||
print(e)
|
||||
logging.error(e)
|
||||
continue
|
||||
|
||||
if country_id not in dead_units:
|
||||
|
||||
29
userdata/logging.py
Normal file
29
userdata/logging.py
Normal file
@ -0,0 +1,29 @@
|
||||
import logging
|
||||
import traceback
|
||||
|
||||
from io import StringIO
|
||||
from tkinter import *
|
||||
from tkinter.scrolledtext import *
|
||||
|
||||
log_stream = StringIO()
|
||||
logging.basicConfig(stream=log_stream, level=logging.INFO)
|
||||
|
||||
|
||||
def _error_prompt():
|
||||
tk = Tk()
|
||||
Label(tk, text="Oops, something went wrong.").grid(row=0)
|
||||
Label(tk, text="Please send following text to the developer:").grid(row=1)
|
||||
|
||||
text = ScrolledText(tk)
|
||||
text.insert("0.0", log_stream.getvalue())
|
||||
text.grid(row=2, sticky=NSEW)
|
||||
tk.focus()
|
||||
|
||||
|
||||
def _handle_exception(self, exception: BaseException, *args):
|
||||
logging.exception(exception)
|
||||
_error_prompt()
|
||||
|
||||
|
||||
Tk.report_callback_exception = _handle_exception
|
||||
logging.info("DCS Libration 1.13 RC2")
|
||||
@ -1,3 +1,4 @@
|
||||
import logging
|
||||
import typing
|
||||
import pickle
|
||||
import os
|
||||
@ -56,5 +57,5 @@ def save_game(game) -> bool:
|
||||
shutil.copy(_temporary_save_file(), _save_file())
|
||||
return True
|
||||
except Exception as e:
|
||||
print(e)
|
||||
logging.error(e)
|
||||
return False
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user