From bbb08aa1db6a6d90ea9cecf1adc87add463cc456 Mon Sep 17 00:00:00 2001 From: RndName Date: Tue, 22 Mar 2022 19:43:55 +0100 Subject: [PATCH] Add orientation info and allow user to rotate TGO - Give user information about the heading of a TGO - Add a button to change the heading to head to conflic - Change the orientation of a TGO during Buy - Run the heading to conflict calculation to change the orientation of the newly bought group. It will then head to the center of the conflict --- game/theater/theatergroundobject.py | 12 +++++ qt_ui/uiconstants.py | 2 + .../groundobject/QGroundObjectBuyMenu.py | 9 ++-- .../windows/groundobject/QGroundObjectMenu.py | 50 +++++++++++++++++- resources/ui/misc/heading.png | Bin 0 -> 4913 bytes 5 files changed, 68 insertions(+), 5 deletions(-) create mode 100644 resources/ui/misc/heading.png diff --git a/game/theater/theatergroundobject.py b/game/theater/theatergroundobject.py index a748c28d..00203ade 100644 --- a/game/theater/theatergroundobject.py +++ b/game/theater/theatergroundobject.py @@ -260,6 +260,18 @@ class TheaterGroundObject(MissionTarget, SidcDescribable, ABC): return group return None + def rotate(self, heading: Heading) -> None: + """Rotate the whole TGO clockwise to the new heading""" + rotation = heading - self.heading + if rotation.degrees < 0: + rotation = Heading.from_degrees(rotation.degrees + 360) + + self.heading = heading + # Rotate the whole TGO to match the new heading + for unit in self.units: + unit.position.heading += rotation + unit.position.rotate(self.position, rotation) + class BuildingGroundObject(TheaterGroundObject): def __init__( diff --git a/qt_ui/uiconstants.py b/qt_ui/uiconstants.py index bc646469..0409cf4a 100644 --- a/qt_ui/uiconstants.py +++ b/qt_ui/uiconstants.py @@ -166,6 +166,8 @@ def load_icons(): "./resources/ui/conditions/weather/night-totally-cloud.png" ) + ICONS["heading"] = QPixmap("./resources/ui/misc/heading.png") + EVENT_ICONS: Dict[str, QPixmap] = {} diff --git a/qt_ui/windows/groundobject/QGroundObjectBuyMenu.py b/qt_ui/windows/groundobject/QGroundObjectBuyMenu.py index 0a7bfc6e..2cbdc3c8 100644 --- a/qt_ui/windows/groundobject/QGroundObjectBuyMenu.py +++ b/qt_ui/windows/groundobject/QGroundObjectBuyMenu.py @@ -27,8 +27,6 @@ from game.layout.layout import ( TgoLayout, TgoLayoutGroup, ) -from game.server import EventStream -from game.sim.gameupdateevents import GameUpdateEvents from game.theater import TheaterGroundObject from game.theater.theatergroundobject import ( EwrGroundObject, @@ -36,7 +34,6 @@ from game.theater.theatergroundobject import ( VehicleGroupGroundObject, ) from qt_ui.uiconstants import EVENT_ICONS -from qt_ui.windows.GameUpdateSignal import GameUpdateSignal @dataclass @@ -208,6 +205,12 @@ class QGroundObjectTemplateLayout(QGroupBox): # Something went wrong. Buy button should be disabled! logging.error("Not enough money to buy the group") return + + # Change the heading of the new group to head to the conflict + self.ground_object.heading = ( + self.game.theater.heading_to_conflict_from(self.ground_object.position) + or self.ground_object.heading + ) self.game.blue.budget -= price - self.current_group_value self.ground_object.groups = [] for group_name, groups in self.layout_model.groups.items(): diff --git a/qt_ui/windows/groundobject/QGroundObjectMenu.py b/qt_ui/windows/groundobject/QGroundObjectMenu.py index 4353a3cb..ae846347 100644 --- a/qt_ui/windows/groundobject/QGroundObjectMenu.py +++ b/qt_ui/windows/groundobject/QGroundObjectMenu.py @@ -1,5 +1,6 @@ import logging +from PySide2.QtGui import QTransform from PySide2.QtWidgets import ( QDialog, QGridLayout, @@ -8,6 +9,7 @@ from PySide2.QtWidgets import ( QLabel, QPushButton, QVBoxLayout, + QSpinBox, ) from dcs import Point @@ -20,7 +22,8 @@ from game.theater import ControlPoint, TheaterGroundObject from game.theater.theatergroundobject import ( BuildingGroundObject, ) -from qt_ui.uiconstants import EVENT_ICONS +from game.utils import Heading +from qt_ui.uiconstants import EVENT_ICONS, ICONS from qt_ui.widgets.QBudgetBox import QBudgetBox from qt_ui.windows.GameUpdateSignal import GameUpdateSignal from qt_ui.windows.groundobject.QBuildingInfo import QBuildingInfo @@ -46,6 +49,7 @@ class QGroundObjectMenu(QDialog): self.setWindowIcon(EVENT_ICONS["capture"]) self.intelBox = QGroupBox("Units :") self.buildingBox = QGroupBox("Buildings :") + self.orientationBox = QGroupBox("Orientation :") self.intelLayout = QGridLayout() self.buildingsLayout = QGridLayout() self.sell_all_button = None @@ -66,6 +70,7 @@ class QGroundObjectMenu(QDialog): self.mainLayout.addWidget(self.financesBox) else: self.mainLayout.addWidget(self.intelBox) + self.mainLayout.addWidget(self.orientationBox) self.actionLayout = QHBoxLayout() @@ -141,13 +146,49 @@ class QGroundObjectMenu(QDialog): QLabel("Receiving: " + str(received_income) + "M"), 2, 2 ) + # Orientation Box + self.orientationBox = QGroupBox("Orientation :") + self.orientationBoxLayout = QHBoxLayout() + + heading_image = QLabel() + heading_image.setPixmap( + ICONS["heading"].transformed( + QTransform().rotate(self.ground_object.heading.degrees) + ) + ) + self.orientationBoxLayout.addWidget(heading_image) + self.headingLabel = QLabel("Heading:") + self.orientationBoxLayout.addWidget(self.headingLabel) + self.headingSelector = QSpinBox() + self.headingSelector.setEnabled(False) # Disable for now + self.headingSelector.setMinimum(0) + self.headingSelector.setMaximum(360) + self.headingSelector.setValue(self.ground_object.heading.degrees) + self.orientationBoxLayout.addWidget(self.headingSelector) + if self.cp.captured: + # TODO Let the user choose the heading with the SpinBox + self.headingSelector.setEnabled(False) + self.head_to_conflict_button = QPushButton("Head to conflict") + heading = ( + self.game.theater.heading_to_conflict_from(self.ground_object.position) + or self.ground_object.heading + ) + self.head_to_conflict_button.clicked.connect( + lambda: self.rotate_tgo(heading) + ) + self.orientationBoxLayout.addWidget(self.head_to_conflict_button) + else: + self.headingSelector.setEnabled(False) + + # Set the layouts self.financesBox.setLayout(self.financesBoxLayout) self.buildingBox.setLayout(self.buildingsLayout) self.intelBox.setLayout(self.intelLayout) + self.orientationBox.setLayout(self.orientationBoxLayout) def do_refresh_layout(self): try: - for i in range(self.mainLayout.count()): + for i in reversed(range(self.mainLayout.count())): item = self.mainLayout.itemAt(i) if item is not None and item.widget() is not None: item.widget().setParent(None) @@ -160,6 +201,7 @@ class QGroundObjectMenu(QDialog): self.mainLayout.addWidget(self.buildingBox) else: self.mainLayout.addWidget(self.intelBox) + self.mainLayout.addWidget(self.orientationBox) self.actionLayout = QHBoxLayout() if self.total_value > 0: @@ -198,6 +240,10 @@ class QGroundObjectMenu(QDialog): self.do_refresh_layout() + def rotate_tgo(self, heading: Heading) -> None: + self.ground_object.rotate(heading) + self.do_refresh_layout() + def sell_all(self): self.update_total_value() self.game.blue.budget = self.game.blue.budget + self.total_value diff --git a/resources/ui/misc/heading.png b/resources/ui/misc/heading.png new file mode 100644 index 0000000000000000000000000000000000000000..f05288cfbcd7dd24ed455186a3d110ac33c37f4a GIT binary patch literal 4913 zcmeHLX;f3!7QTRpA_xu$Qj{1`u^4g_GTcPMj07PR60BfRxw*N402xRwB!DBLqEBVA zqCTpiK0vVgs)7Ud38+j?wA29vMV13aTTy(q@>F^fP+`6Pajn<2-Vd@??r_exzrFW& z_Sq{ZTS7wuEY0oBAqcVzLtE=0$oNHScmyFtwIr2VDU~JQBqBwH zlW?6(3PHNIU-Fmde&%Aa@m{3=uI60p$(M^>A8wj>{Eb8G(`~$K8=kFR{B@CithZx^ zN3~ngKtHF}%Bg9ePk7%>5jR`BW6uQpHBpC;(gsZts8RbaGzq&p*68ATK(%G2__8HsgoOZsr`nb8Ak)+p;_I zIcs<*^K8N8@^cIK-#s`hRY1#vwX-;(TNBJA9u9{JXE{Hwy?k;(a_gyi;~L75jwy33 zu(S3nC#Dvvl|RI%>56Sqta3_7C24PeoXKA)iYywSYhHGA1ynW8vDs15eLZH|BkbGh z<<7KOKgp;)l{;_LB3@fGaaPSio9~D5Z#PYE{jS`l(q~~$B6(@k#cHOr@Sx9z{xX*( zr|r&|u^78uF4NE2#T+FqnjBMHGdFTCqPw|~XMS&W5mW9F7jc3kRb&(eTOX_#fA`A$ z4y(G|0qeVa(~fl7_F1sncR!GBK}kNC*^;H}l&?90qsHmngYH@UMbX!Ty2AT4QyPpZ zcBQ4q-1s*uA)k!HHrKM(L8Up)bGX}DGp+e%g~qoc$@@uOW$m3#0Xbt>*EDgf#3wf$ zr(ZEjj*^m!a#ghs8RpTKN^g5kn~_#p8XJD&Q1Q{opxmi>#XA>V4f<|(ghlbLD_1CA zB|h)&X}sETBL7OHN){K=di-%gP)&hplj%xZ3+IOqMAvk2NQFbIvA9$CRB5E4Kgpt2Gfr!E)({77{s=2lA&byQovOXK7!})INiXR!GZQX-1`caVIQ{mbcu-TgwzyZ*xC)#L6Z>bWMX$j=>GEdQq4pp&-z$aywpb3~Xh^ZJ&0 z%d@2Cvv1+~_Yhl$X*y{Gf8OIQz8A$s+aI!|{=Ree&7J5{w&=A_9lo(|T1-V0el_id z?O09Q;;l~@0oE5Lck#V4wtc?X`GQZzm)oaK32e(s{eAO{wdqmxGmE1iM0lBg?o@B} z&g^^@vrm+#5W?ofzi$6l?`(bB)Sh|gok!&_b>lnNjy-yE*Lk6t)HCo{#mU}D&8IA* zqE0bVtWLKZZQ4gPW;--~A!>NM{*_yQ@q*^H6J8!U^F8xbVn1}~aQVZ}Iy3QjELmuxPq8HoI-+quvaCi6AbA{Fe;va%K=maywXOv35F?YT z3|2tw5lBKN{V3Lm*z_}obcP24%s=psK)=u300t<5fa|BklJwyP`titm|6GX@lS#OS zM>iG%)7>Nt3Y*3fQy8d(P2s>8PGL$B1V(8XhsHvNQ3WbA1ggMrJry8#l>r`9g24=q zm_cE|G#Z70U>pj9pfH8ThH<)>$%L8kaEdR~GEkLh!tki{R1!c%LpgL73L_L2od%+j z&|nHe!x3~O7b(?p6fCDqj>h0rl_JJ4p% zAq56t56Bb}rB?HCDqJSVMFgtXlg47OIfxsb&TvE64EiwpFLAX7)S{l12D`cq&gd70 z3(^77qWVe&1O{4=4cAwVql8i&u2d%Q$oim2dP_rrliUZFg)h?pM2f!VhpJwLCl0<1 zZh{1vVTwdD6fPIV2AycoWL#o!1ndTZ!zK&BuYP(p@mNX|oI&L4@_#^QRVV6eYo zHwv8|PN#EWHWy})sYA(9^?Uk#w(ivb(!<>V7}f-U-QXB#E}&OYKQyZ$z4Q|Pg`c5y z_zOJ%>QN`3r0=L)qjG(c0-ppPt*%kIK1qR30*_YL|4c6PkH;Nc0e%5#!C|JyQo;cT zp