HUDBottom.qml 34 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882
  1. import QtQuick 2.15
  2. import QtQuick.Controls 2.15
  3. import QtQuick.Layouts 1.15
  4. import StandardOfIron 1.0
  5. RowLayout {
  6. id: bottomRoot
  7. property string currentCommandMode
  8. property int selectionTick
  9. property bool hasMovableUnits
  10. property var modeAvailability: ({
  11. "canAttack": true,
  12. "canGuard": true,
  13. "canHold": true,
  14. "canPatrol": true,
  15. "canHeal": true,
  16. "canBuild": true
  17. })
  18. signal commandModeChanged(string mode)
  19. signal recruit(string unitType)
  20. function updateModeAvailability() {
  21. if (typeof game !== 'undefined' && game.get_selected_units_mode_availability)
  22. modeAvailability = game.get_selected_units_mode_availability();
  23. }
  24. function unitIconSource(unitType, nationKey) {
  25. if (typeof StyleGuide === "undefined" || !StyleGuide.unitIconSources || !unitType)
  26. return "";
  27. var sources = StyleGuide.unitIconSources[unitType];
  28. if (!sources)
  29. sources = StyleGuide.unitIconSources["default"];
  30. var key = (nationKey && sources[nationKey]) ? nationKey : "default";
  31. return sources[key] || "";
  32. }
  33. function unitIconEmoji(unitType) {
  34. if (typeof StyleGuide !== "undefined" && StyleGuide.unitIcons)
  35. return StyleGuide.unitIcons[unitType] || StyleGuide.unitIcons["default"] || "👤";
  36. return "👤";
  37. }
  38. function unitTypeKeyFromDisplayName(displayName) {
  39. if (!displayName)
  40. return "";
  41. var s = displayName.toString().trim().toLowerCase();
  42. s = s.replace(/[^a-z0-9]+/g, "_");
  43. s = s.replace(/^_+|_+$/g, "");
  44. return s;
  45. }
  46. function commandIcon(filename) {
  47. if (typeof StyleGuide === "undefined" || !filename)
  48. return "";
  49. return StyleGuide.iconPath(filename);
  50. }
  51. function hasSelectedUnit(type) {
  52. if (typeof game !== 'undefined' && game.has_selected_type)
  53. return game.has_selected_type(type);
  54. return false;
  55. }
  56. Component.onCompleted: updateModeAvailability()
  57. onSelectionTickChanged: updateModeAvailability()
  58. anchors.fill: parent
  59. anchors.margins: 10
  60. spacing: 12
  61. Rectangle {
  62. Layout.fillWidth: true
  63. Layout.preferredWidth: Math.max(240, bottomPanel.width * 0.3)
  64. Layout.fillHeight: true
  65. Layout.alignment: Qt.AlignTop
  66. color: "#0f1419"
  67. border.color: "#3498db"
  68. border.width: 2
  69. radius: 6
  70. ColumnLayout {
  71. anchors.fill: parent
  72. anchors.margins: 6
  73. spacing: 6
  74. Rectangle {
  75. Layout.fillWidth: true
  76. Layout.preferredHeight: 25
  77. color: "#1a252f"
  78. radius: 4
  79. Text {
  80. anchors.centerIn: parent
  81. text: qsTr("SELECTED UNITS")
  82. color: "#3498db"
  83. font.pointSize: 10
  84. font.bold: true
  85. }
  86. }
  87. ScrollView {
  88. Layout.fillWidth: true
  89. Layout.fillHeight: true
  90. clip: true
  91. ScrollBar.vertical.policy: ScrollBar.AsNeeded
  92. ListView {
  93. id: selectedUnitsList
  94. model: (typeof game !== 'undefined' && game.selected_units_model) ? game.selected_units_model : null
  95. boundsBehavior: Flickable.StopAtBounds
  96. flickableDirection: Flickable.VerticalFlick
  97. spacing: 3
  98. delegate: Rectangle {
  99. id: selectedUnitItem
  100. property bool isHovered: false
  101. property string unitDisplayName: (typeof name !== "undefined") ? name : ""
  102. property string unitTypeKey: (typeof unit_type !== "undefined" && unit_type !== "") ? unit_type : bottomRoot.unitTypeKeyFromDisplayName(unitDisplayName)
  103. property string nationKey: (typeof nation !== "undefined" && nation !== "") ? nation : "default"
  104. width: selectedUnitsList.width - 10
  105. height: 36
  106. color: isHovered ? "#243346" : "#1a252f"
  107. radius: 4
  108. border.color: isHovered ? "#4aa3ff" : "#34495e"
  109. border.width: isHovered ? 2 : 1
  110. MouseArea {
  111. id: selectedUnitMouseArea
  112. anchors.fill: parent
  113. hoverEnabled: true
  114. propagateComposedEvents: false
  115. cursorShape: Qt.PointingHandCursor
  116. onEntered: selectedUnitItem.isHovered = true
  117. onExited: selectedUnitItem.isHovered = false
  118. onClicked: function(mouse) {
  119. if (mouse.button === Qt.LeftButton && typeof game !== 'undefined' && game.select_unit_by_id && typeof unit_id !== 'undefined')
  120. game.select_unit_by_id(unit_id);
  121. }
  122. }
  123. Row {
  124. anchors.left: parent.left
  125. anchors.right: parent.right
  126. anchors.verticalCenter: parent.verticalCenter
  127. anchors.leftMargin: 6
  128. anchors.rightMargin: 6
  129. spacing: 8
  130. height: 28
  131. Item {
  132. width: 32
  133. height: 28
  134. Image {
  135. id: selectedUnitIcon
  136. anchors.centerIn: parent
  137. width: 24
  138. height: 24
  139. fillMode: Image.PreserveAspectFit
  140. source: bottomRoot.unitIconSource(selectedUnitItem.unitTypeKey, selectedUnitItem.nationKey)
  141. visible: source !== "" && status !== Image.Error
  142. }
  143. Text {
  144. anchors.centerIn: parent
  145. text: bottomRoot.unitIconEmoji(selectedUnitItem.unitTypeKey)
  146. color: "#ecf0f1"
  147. font.pixelSize: 16
  148. visible: selectedUnitIcon.source === "" || selectedUnitIcon.status === Image.Error
  149. }
  150. }
  151. Column {
  152. anchors.verticalCenter: parent.verticalCenter
  153. spacing: 2
  154. Rectangle {
  155. width: 60
  156. height: 10
  157. color: "#2c3e50"
  158. radius: 5
  159. border.color: "#1a252f"
  160. border.width: 1
  161. Rectangle {
  162. width: parent.width * (typeof health_ratio !== 'undefined' ? health_ratio : 0)
  163. height: parent.height
  164. color: {
  165. var ratio = (typeof health_ratio !== 'undefined' ? health_ratio : 0);
  166. if (ratio > 0.6)
  167. return "#27ae60";
  168. if (ratio > 0.3)
  169. return "#f39c12";
  170. return "#e74c3c";
  171. }
  172. radius: 5
  173. }
  174. }
  175. Rectangle {
  176. width: 60
  177. height: 6
  178. color: "#2c3e50"
  179. radius: 3
  180. border.color: "#1a252f"
  181. border.width: 1
  182. visible: (typeof can_run !== 'undefined') ? can_run : false
  183. Rectangle {
  184. width: parent.width * (typeof stamina_ratio !== 'undefined' ? stamina_ratio : 1)
  185. height: parent.height
  186. color: {
  187. var running = (typeof is_running !== 'undefined') ? is_running : false;
  188. if (running)
  189. return "#e67e22";
  190. return "#3498db";
  191. }
  192. radius: 3
  193. }
  194. }
  195. }
  196. Text {
  197. anchors.verticalCenter: parent.verticalCenter
  198. text: (typeof name !== 'undefined' ? name : selectedUnitItem.unitDisplayName)
  199. color: "#ecf0f1"
  200. font.pointSize: 8
  201. font.bold: false
  202. elide: Text.ElideRight
  203. width: parent.width - 108
  204. }
  205. }
  206. }
  207. }
  208. }
  209. }
  210. }
  211. Column {
  212. Layout.fillWidth: true
  213. Layout.preferredWidth: Math.max(320, bottomPanel.width * 0.4)
  214. Layout.fillHeight: true
  215. Layout.alignment: Qt.AlignTop
  216. spacing: 8
  217. Rectangle {
  218. width: parent.width
  219. height: 36
  220. color: bottomRoot.currentCommandMode === "normal" ? "#0f1419" : (bottomRoot.currentCommandMode === "attack" ? "#8b1a1a" : "#1a252f")
  221. border.color: bottomRoot.currentCommandMode === "normal" ? "#34495e" : (bottomRoot.currentCommandMode === "attack" ? "#e74c3c" : "#3498db")
  222. border.width: 2
  223. radius: 6
  224. opacity: bottomRoot.hasMovableUnits ? 1 : 0.5
  225. Rectangle {
  226. anchors.fill: parent
  227. anchors.margins: -4
  228. color: "transparent"
  229. border.color: bottomRoot.currentCommandMode === "attack" ? "#e74c3c" : "#3498db"
  230. border.width: bottomRoot.currentCommandMode !== "normal" && bottomRoot.hasMovableUnits ? 1 : 0
  231. radius: 8
  232. opacity: 0.4
  233. visible: bottomRoot.currentCommandMode !== "normal" && bottomRoot.hasMovableUnits
  234. }
  235. Text {
  236. anchors.centerIn: parent
  237. text: !bottomRoot.hasMovableUnits ? qsTr("◉ Select Troops for Commands") : (bottomRoot.currentCommandMode === "normal" ? qsTr("◉ Normal Mode") : bottomRoot.currentCommandMode === "attack" ? qsTr("Attack mode - click enemy") : bottomRoot.currentCommandMode === "guard" ? qsTr("Guard mode - click position") : bottomRoot.currentCommandMode === "patrol" ? qsTr("Patrol mode - set waypoints") : bottomRoot.currentCommandMode === "heal" ? qsTr("Heal mode - click ally") : bottomRoot.currentCommandMode === "build" ? qsTr("Build mode - choose structure") : qsTr("Stop command"))
  238. color: !bottomRoot.hasMovableUnits ? "#5a6c7d" : (bottomRoot.currentCommandMode === "normal" ? "#7f8c8d" : (bottomRoot.currentCommandMode === "attack" ? "#ff6b6b" : "#3498db"))
  239. font.pointSize: bottomRoot.currentCommandMode === "normal" ? 10 : 11
  240. font.bold: bottomRoot.currentCommandMode !== "normal" && bottomRoot.hasMovableUnits
  241. }
  242. SequentialAnimation on opacity {
  243. running: bottomRoot.currentCommandMode === "attack" && bottomRoot.hasMovableUnits
  244. loops: Animation.Infinite
  245. NumberAnimation {
  246. from: 0.8
  247. to: 1
  248. duration: 600
  249. }
  250. NumberAnimation {
  251. from: 1
  252. to: 0.8
  253. duration: 600
  254. }
  255. }
  256. }
  257. GridLayout {
  258. id: cmdGrid
  259. property int cmdIconSize: 42
  260. function getButtonColor(btn, baseColor) {
  261. if (btn.pressed)
  262. return Qt.darker(baseColor, 1.3);
  263. if (btn.checked)
  264. return baseColor;
  265. if (btn.hovered)
  266. return Qt.lighter(baseColor, 1.2);
  267. return "#2c3e50";
  268. }
  269. width: parent.width
  270. columns: 4
  271. rowSpacing: 6
  272. columnSpacing: 6
  273. Button {
  274. id: attackButton
  275. property bool modeAvailable: bottomRoot.modeAvailability.canAttack !== false
  276. Layout.fillWidth: true
  277. Layout.preferredHeight: 48
  278. text: qsTr("Attack")
  279. focusPolicy: Qt.NoFocus
  280. enabled: bottomRoot.hasMovableUnits && modeAvailable
  281. checkable: true
  282. checked: bottomRoot.currentCommandMode === "attack" && bottomRoot.hasMovableUnits
  283. onClicked: {
  284. bottomRoot.commandModeChanged(checked ? "attack" : "normal");
  285. }
  286. ToolTip.visible: hovered
  287. ToolTip.text: !modeAvailable ? qsTr("Attack not available for selected units (e.g. healers)") : (bottomRoot.hasMovableUnits ? qsTr("Attack enemy units or buildings.\nUnits will chase targets.") : qsTr("Select troops first"))
  288. ToolTip.delay: 500
  289. background: Rectangle {
  290. color: parent.enabled ? (parent.checked ? "#e74c3c" : (parent.hovered ? "#c0392b" : "#34495e")) : "#1a252f"
  291. radius: 6
  292. border.color: parent.checked ? "#c0392b" : "#1a252f"
  293. border.width: 2
  294. }
  295. contentItem: Row {
  296. anchors.centerIn: parent
  297. spacing: 8
  298. anchors.verticalCenter: parent.verticalCenter
  299. Image {
  300. width: cmdGrid.cmdIconSize
  301. height: cmdGrid.cmdIconSize
  302. source: bottomRoot.commandIcon("attack_mode.png")
  303. fillMode: Image.PreserveAspectFit
  304. smooth: true
  305. mipmap: true
  306. visible: source !== ""
  307. }
  308. Text {
  309. text: attackButton.text
  310. font.pointSize: 11
  311. font.bold: true
  312. color: attackButton.enabled ? "#ecf0f1" : "#7f8c8d"
  313. horizontalAlignment: Text.AlignLeft
  314. verticalAlignment: Text.AlignVCenter
  315. }
  316. }
  317. }
  318. Button {
  319. id: guardButton
  320. property bool modeAvailable: bottomRoot.modeAvailability.canGuard !== false
  321. Layout.fillWidth: true
  322. Layout.preferredHeight: 48
  323. text: qsTr("Guard")
  324. focusPolicy: Qt.NoFocus
  325. enabled: bottomRoot.hasMovableUnits && modeAvailable
  326. checkable: true
  327. checked: bottomRoot.currentCommandMode === "guard" && bottomRoot.hasMovableUnits
  328. onClicked: {
  329. bottomRoot.commandModeChanged(checked ? "guard" : "normal");
  330. }
  331. ToolTip.visible: hovered
  332. ToolTip.text: !modeAvailable ? qsTr("Guard not available for selected units") : (bottomRoot.hasMovableUnits ? qsTr("Guard a position.\nUnits will defend from all sides.") : qsTr("Select troops first"))
  333. ToolTip.delay: 500
  334. background: Rectangle {
  335. color: parent.enabled ? (parent.checked ? "#3498db" : (parent.hovered ? "#2980b9" : "#34495e")) : "#1a252f"
  336. radius: 6
  337. border.color: parent.checked ? "#2980b9" : "#1a252f"
  338. border.width: 2
  339. }
  340. contentItem: Row {
  341. anchors.centerIn: parent
  342. spacing: 8
  343. anchors.verticalCenter: parent.verticalCenter
  344. Image {
  345. width: cmdGrid.cmdIconSize
  346. height: cmdGrid.cmdIconSize
  347. source: bottomRoot.commandIcon("defend_mode.png")
  348. fillMode: Image.PreserveAspectFit
  349. smooth: true
  350. mipmap: true
  351. visible: source !== ""
  352. }
  353. Text {
  354. text: guardButton.text
  355. font.pointSize: 11
  356. font.bold: true
  357. color: guardButton.enabled ? "#ecf0f1" : "#7f8c8d"
  358. horizontalAlignment: Text.AlignLeft
  359. verticalAlignment: Text.AlignVCenter
  360. }
  361. }
  362. }
  363. Button {
  364. id: patrolButton
  365. property bool modeAvailable: bottomRoot.modeAvailability.canPatrol !== false
  366. Layout.fillWidth: true
  367. Layout.preferredHeight: 48
  368. text: qsTr("Patrol")
  369. focusPolicy: Qt.NoFocus
  370. enabled: bottomRoot.hasMovableUnits && modeAvailable
  371. checkable: true
  372. checked: bottomRoot.currentCommandMode === "patrol" && bottomRoot.hasMovableUnits
  373. onClicked: {
  374. bottomRoot.commandModeChanged(checked ? "patrol" : "normal");
  375. }
  376. ToolTip.visible: hovered
  377. ToolTip.text: !modeAvailable ? qsTr("Patrol not available for selected units") : (bottomRoot.hasMovableUnits ? qsTr("Patrol between waypoints.\nClick start and end points.") : qsTr("Select troops first"))
  378. ToolTip.delay: 500
  379. background: Rectangle {
  380. color: parent.enabled ? (parent.checked ? "#27ae60" : (parent.hovered ? "#229954" : "#34495e")) : "#1a252f"
  381. radius: 6
  382. border.color: parent.checked ? "#229954" : "#1a252f"
  383. border.width: 2
  384. }
  385. contentItem: Row {
  386. anchors.centerIn: parent
  387. spacing: 8
  388. anchors.verticalCenter: parent.verticalCenter
  389. Image {
  390. width: cmdGrid.cmdIconSize
  391. height: cmdGrid.cmdIconSize
  392. source: bottomRoot.commandIcon("patrol_mode.png")
  393. fillMode: Image.PreserveAspectFit
  394. smooth: true
  395. mipmap: true
  396. visible: source !== ""
  397. }
  398. Text {
  399. text: patrolButton.text
  400. font.pointSize: 11
  401. font.bold: true
  402. color: patrolButton.enabled ? "#ecf0f1" : "#7f8c8d"
  403. horizontalAlignment: Text.AlignLeft
  404. verticalAlignment: Text.AlignVCenter
  405. }
  406. }
  407. }
  408. Button {
  409. id: healButton
  410. property bool modeAvailable: bottomRoot.modeAvailability.canHeal !== false
  411. property bool hasHealerSelected: {
  412. bottomRoot.selectionTick;
  413. return bottomRoot.hasSelectedUnit("healer");
  414. }
  415. Layout.fillWidth: true
  416. Layout.preferredHeight: 48
  417. text: qsTr("Heal")
  418. focusPolicy: Qt.NoFocus
  419. enabled: bottomRoot.hasMovableUnits && modeAvailable && hasHealerSelected
  420. checkable: true
  421. checked: bottomRoot.currentCommandMode === "heal" && bottomRoot.hasMovableUnits
  422. onClicked: {
  423. if (typeof game !== 'undefined' && game.on_heal_command)
  424. game.on_heal_command();
  425. bottomRoot.commandModeChanged(checked ? "heal" : "normal");
  426. }
  427. ToolTip.visible: hovered
  428. ToolTip.text: !modeAvailable ? qsTr("Heal not available for selected units") : (!hasHealerSelected ? qsTr("Select healer units") : qsTr("Heal allies in range"))
  429. ToolTip.delay: 500
  430. background: Rectangle {
  431. color: parent.enabled ? (parent.checked ? "#1abc9c" : (parent.hovered ? "#16a085" : "#34495e")) : "#1a252f"
  432. radius: 6
  433. border.color: parent.checked ? "#16a085" : "#1a252f"
  434. border.width: 2
  435. }
  436. contentItem: Row {
  437. anchors.centerIn: parent
  438. spacing: 8
  439. anchors.verticalCenter: parent.verticalCenter
  440. Image {
  441. width: cmdGrid.cmdIconSize
  442. height: cmdGrid.cmdIconSize
  443. source: bottomRoot.commandIcon("heal_mode.png")
  444. fillMode: Image.PreserveAspectFit
  445. smooth: true
  446. mipmap: true
  447. visible: source !== ""
  448. }
  449. Text {
  450. text: healButton.text
  451. font.pointSize: 11
  452. font.bold: true
  453. color: healButton.enabled ? "#ecf0f1" : "#7f8c8d"
  454. horizontalAlignment: Text.AlignLeft
  455. verticalAlignment: Text.AlignVCenter
  456. }
  457. }
  458. }
  459. Button {
  460. id: stopButton
  461. Layout.fillWidth: true
  462. Layout.preferredHeight: 48
  463. text: qsTr("Stop")
  464. focusPolicy: Qt.NoFocus
  465. enabled: bottomRoot.hasMovableUnits
  466. onClicked: {
  467. if (typeof game !== 'undefined' && game.on_stop_command)
  468. game.on_stop_command();
  469. }
  470. ToolTip.visible: hovered
  471. ToolTip.text: bottomRoot.hasMovableUnits ? qsTr("Stop all actions immediately") : qsTr("Select troops first")
  472. ToolTip.delay: 500
  473. background: Rectangle {
  474. color: parent.enabled ? (parent.pressed ? "#d35400" : (parent.hovered ? "#e67e22" : "#34495e")) : "#1a252f"
  475. radius: 6
  476. border.color: parent.enabled ? "#d35400" : "#1a252f"
  477. border.width: 2
  478. }
  479. contentItem: Row {
  480. anchors.centerIn: parent
  481. spacing: 8
  482. anchors.verticalCenter: parent.verticalCenter
  483. Text {
  484. text: "\u25a0"
  485. font.pointSize: 18
  486. font.bold: true
  487. color: stopButton.enabled ? "#ecf0f1" : "#7f8c8d"
  488. horizontalAlignment: Text.AlignHCenter
  489. verticalAlignment: Text.AlignVCenter
  490. }
  491. Text {
  492. text: stopButton.text
  493. font.pointSize: 11
  494. font.bold: true
  495. color: stopButton.enabled ? "#ecf0f1" : "#7f8c8d"
  496. horizontalAlignment: Text.AlignLeft
  497. verticalAlignment: Text.AlignVCenter
  498. }
  499. }
  500. }
  501. Button {
  502. id: buildButton
  503. property bool modeAvailable: bottomRoot.modeAvailability.canBuild !== false
  504. property bool hasBuilderSelected: {
  505. bottomRoot.selectionTick;
  506. return bottomRoot.hasSelectedUnit("builder");
  507. }
  508. Layout.fillWidth: true
  509. Layout.preferredHeight: 48
  510. text: qsTr("Build")
  511. focusPolicy: Qt.NoFocus
  512. enabled: bottomRoot.hasMovableUnits && modeAvailable && hasBuilderSelected
  513. checkable: true
  514. checked: bottomRoot.currentCommandMode === "build" && bottomRoot.hasMovableUnits
  515. onClicked: {
  516. if (typeof game !== 'undefined' && game.on_build_command)
  517. game.on_build_command();
  518. bottomRoot.commandModeChanged(checked ? "build" : "normal");
  519. }
  520. ToolTip.visible: hovered
  521. ToolTip.text: !modeAvailable ? qsTr("Build not available for selected units") : (!hasBuilderSelected ? qsTr("Select builder units") : qsTr("Build structures or place foundations"))
  522. ToolTip.delay: 500
  523. background: Rectangle {
  524. color: parent.enabled ? (parent.checked ? "#f1c40f" : (parent.hovered ? "#f39c12" : "#34495e")) : "#1a252f"
  525. radius: 6
  526. border.color: parent.checked ? "#f39c12" : "#1a252f"
  527. border.width: 2
  528. }
  529. contentItem: Row {
  530. anchors.centerIn: parent
  531. spacing: 8
  532. anchors.verticalCenter: parent.verticalCenter
  533. Image {
  534. width: cmdGrid.cmdIconSize
  535. height: cmdGrid.cmdIconSize
  536. source: bottomRoot.commandIcon("build_mode.png")
  537. fillMode: Image.PreserveAspectFit
  538. smooth: true
  539. mipmap: true
  540. visible: source !== ""
  541. }
  542. Text {
  543. text: buildButton.text
  544. font.pointSize: 11
  545. font.bold: true
  546. color: buildButton.enabled ? "#ecf0f1" : "#7f8c8d"
  547. horizontalAlignment: Text.AlignLeft
  548. verticalAlignment: Text.AlignVCenter
  549. }
  550. }
  551. }
  552. Button {
  553. id: holdButton
  554. property bool isHoldActive: {
  555. bottomRoot.selectionTick;
  556. return (typeof game !== 'undefined' && game.any_selected_in_hold_mode) ? game.any_selected_in_hold_mode() : false;
  557. }
  558. property bool modeAvailable: bottomRoot.modeAvailability.canHold !== false
  559. Layout.fillWidth: true
  560. Layout.preferredHeight: 48
  561. text: qsTr("Hold")
  562. focusPolicy: Qt.NoFocus
  563. enabled: bottomRoot.hasMovableUnits && modeAvailable
  564. onClicked: {
  565. if (typeof game !== 'undefined' && game.on_hold_command)
  566. game.on_hold_command();
  567. }
  568. ToolTip.visible: hovered
  569. ToolTip.text: !modeAvailable ? qsTr("Hold not available for selected units") : (bottomRoot.hasMovableUnits ? (isHoldActive ? qsTr("Exit hold mode (toggle)") : qsTr("Hold position and defend")) : qsTr("Select troops first"))
  570. ToolTip.delay: 500
  571. Connections {
  572. function onHold_mode_changed(active) {
  573. holdButton.isHoldActive = (typeof game !== 'undefined' && game.any_selected_in_hold_mode) ? game.any_selected_in_hold_mode() : false;
  574. }
  575. target: (typeof game !== 'undefined') ? game : null
  576. }
  577. background: Rectangle {
  578. color: {
  579. if (!parent.enabled)
  580. return "#1a252f";
  581. if (parent.isHoldActive)
  582. return "#8e44ad";
  583. if (parent.pressed)
  584. return "#8e44ad";
  585. if (parent.hovered)
  586. return "#9b59b6";
  587. return "#34495e";
  588. }
  589. radius: 6
  590. border.color: parent.enabled ? (parent.isHoldActive ? "#d35400" : "#8e44ad") : "#1a252f"
  591. border.width: parent.isHoldActive ? 3 : 2
  592. }
  593. contentItem: Row {
  594. anchors.centerIn: parent
  595. spacing: 8
  596. anchors.verticalCenter: parent.verticalCenter
  597. Image {
  598. width: cmdGrid.cmdIconSize
  599. height: cmdGrid.cmdIconSize
  600. source: bottomRoot.commandIcon("hold_mode.png")
  601. fillMode: Image.PreserveAspectFit
  602. smooth: true
  603. mipmap: true
  604. visible: source !== ""
  605. }
  606. Text {
  607. text: (holdButton.isHoldActive ? qsTr("Active ") : "") + holdButton.text
  608. font.pointSize: 11
  609. font.bold: true
  610. color: holdButton.enabled ? "#ecf0f1" : "#7f8c8d"
  611. horizontalAlignment: Text.AlignLeft
  612. verticalAlignment: Text.AlignVCenter
  613. }
  614. }
  615. }
  616. Button {
  617. id: formationButton
  618. property bool isFormationActive: {
  619. bottomRoot.selectionTick;
  620. return (typeof game !== 'undefined' && game.any_selected_in_formation_mode) ? game.any_selected_in_formation_mode() : false;
  621. }
  622. property int selectedCount: {
  623. bottomRoot.selectionTick;
  624. return (typeof game !== 'undefined' && game.selected_units_model) ? game.selected_units_model.rowCount() : 0;
  625. }
  626. Layout.fillWidth: true
  627. Layout.preferredHeight: 48
  628. text: qsTr("Formation")
  629. focusPolicy: Qt.NoFocus
  630. enabled: bottomRoot.hasMovableUnits && selectedCount > 1
  631. onClicked: {
  632. if (typeof game !== 'undefined' && game.on_formation_command)
  633. game.on_formation_command();
  634. }
  635. ToolTip.visible: hovered
  636. ToolTip.text: {
  637. if (!bottomRoot.hasMovableUnits)
  638. return qsTr("Select troops first");
  639. if (selectedCount <= 1)
  640. return qsTr("Select multiple units to use formation");
  641. return isFormationActive ? qsTr("Exit formation mode (toggle)") : qsTr("Arrange units in tactical formation");
  642. }
  643. ToolTip.delay: 500
  644. Connections {
  645. function onFormation_mode_changed(active) {
  646. formationButton.isFormationActive = (typeof game !== 'undefined' && game.any_selected_in_formation_mode) ? game.any_selected_in_formation_mode() : false;
  647. }
  648. target: (typeof game !== 'undefined') ? game : null
  649. }
  650. background: Rectangle {
  651. color: {
  652. if (!parent.enabled)
  653. return "#1a252f";
  654. if (parent.isFormationActive)
  655. return "#16a085";
  656. if (parent.pressed)
  657. return "#16a085";
  658. if (parent.hovered)
  659. return "#1abc9c";
  660. return "#34495e";
  661. }
  662. radius: 6
  663. border.color: parent.enabled ? (parent.isFormationActive ? "#d35400" : "#16a085") : "#1a252f"
  664. border.width: parent.isFormationActive ? 3 : 2
  665. }
  666. contentItem: Row {
  667. anchors.centerIn: parent
  668. spacing: 8
  669. anchors.verticalCenter: parent.verticalCenter
  670. Image {
  671. width: cmdGrid.cmdIconSize
  672. height: cmdGrid.cmdIconSize
  673. source: bottomRoot.commandIcon("formation_mode.png")
  674. fillMode: Image.PreserveAspectFit
  675. smooth: true
  676. mipmap: true
  677. visible: source !== ""
  678. }
  679. Text {
  680. text: (formationButton.isFormationActive ? qsTr("Active ") : "") + formationButton.text
  681. font.pointSize: 11
  682. font.bold: true
  683. color: formationButton.enabled ? "#ecf0f1" : "#7f8c8d"
  684. horizontalAlignment: Text.AlignLeft
  685. verticalAlignment: Text.AlignVCenter
  686. }
  687. }
  688. }
  689. }
  690. }
  691. ProductionPanel {
  692. Layout.fillWidth: true
  693. Layout.preferredWidth: Math.max(280, bottomPanel.width * 0.35)
  694. Layout.fillHeight: true
  695. Layout.alignment: Qt.AlignTop
  696. selectionTick: bottomRoot.selectionTick
  697. gameInstance: (typeof game !== 'undefined') ? game : null
  698. onRecruitUnit: function(unitType) {
  699. bottomRoot.recruit(unitType);
  700. }
  701. onRallyModeToggled: {
  702. if (typeof gameView !== 'undefined')
  703. gameView.setRallyMode = !gameView.setRallyMode;
  704. }
  705. onBuildTower: {
  706. if (typeof game !== 'undefined' && game.start_building_placement)
  707. game.start_building_placement("defense_tower");
  708. }
  709. onBuilderConstruction: function(itemType) {
  710. if (typeof game !== 'undefined' && game.start_builder_construction)
  711. game.start_builder_construction(itemType);
  712. }
  713. }
  714. }