GameView.qml 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602
  1. import QtQuick 2.15
  2. import QtQuick.Controls 2.15
  3. import StandardOfIron 1.0
  4. Item {
  5. id: gameView
  6. property bool isPaused: false
  7. property real gameSpeed: 1
  8. property bool setRallyMode: false
  9. property string cursorMode: "normal"
  10. property bool isPlacingFormation: false
  11. property bool isPlacingConstruction: false
  12. property var pressedKeys: ({
  13. })
  14. signal mapClicked(real x, real y)
  15. signal unitSelected(int unitId)
  16. signal areaSelected(real x1, real y1, real x2, real y2)
  17. function setPaused(paused) {
  18. isPaused = paused;
  19. if (typeof game !== 'undefined' && game.set_paused)
  20. game.set_paused(paused);
  21. }
  22. function setGameSpeed(speed) {
  23. gameSpeed = speed;
  24. if (typeof game !== 'undefined' && game.set_game_speed)
  25. game.set_game_speed(speed);
  26. }
  27. function issueCommand(command) {
  28. }
  29. function beginPanKey(e) {
  30. if (!e.isAutoRepeat && !pressedKeys[e.key]) {
  31. pressedKeys[e.key] = true;
  32. renderArea.keyPanCount += 1;
  33. mainWindow.edgeScrollDisabled = true;
  34. }
  35. }
  36. function ensurePanTimerRunning() {
  37. if (!keyPanTimer.running)
  38. keyPanTimer.start();
  39. }
  40. objectName: "GameView"
  41. Keys.onPressed: function(event) {
  42. if (typeof game === 'undefined')
  43. return ;
  44. var yawStep = (event.modifiers & Qt.ShiftModifier) ? 8 : 4;
  45. var inputStep = (event.modifiers & Qt.ShiftModifier) ? 2 : 1;
  46. var shiftHeld = (event.modifiers & Qt.ShiftModifier) !== 0;
  47. switch (event.key) {
  48. case Qt.Key_Escape:
  49. if (typeof mainWindow !== 'undefined' && !mainWindow.menuVisible) {
  50. mainWindow.menuVisible = true;
  51. event.accepted = true;
  52. }
  53. break;
  54. case Qt.Key_Space:
  55. if (typeof mainWindow !== 'undefined') {
  56. mainWindow.gamePaused = !mainWindow.gamePaused;
  57. gameView.setPaused(mainWindow.gamePaused);
  58. event.accepted = true;
  59. }
  60. break;
  61. case Qt.Key_S:
  62. if (game.has_units_selected) {
  63. if (game.on_stop_command)
  64. game.on_stop_command();
  65. event.accepted = true;
  66. }
  67. break;
  68. case Qt.Key_A:
  69. if (game.has_units_selected) {
  70. game.cursor_mode = "attack";
  71. event.accepted = true;
  72. }
  73. break;
  74. case Qt.Key_M:
  75. if (game.has_units_selected) {
  76. game.cursor_mode = "normal";
  77. event.accepted = true;
  78. }
  79. break;
  80. case Qt.Key_Up:
  81. beginPanKey(event);
  82. game.camera_move(0, inputStep);
  83. ensurePanTimerRunning();
  84. event.accepted = true;
  85. break;
  86. case Qt.Key_Down:
  87. beginPanKey(event);
  88. game.camera_move(0, -inputStep);
  89. ensurePanTimerRunning();
  90. event.accepted = true;
  91. break;
  92. case Qt.Key_Left:
  93. beginPanKey(event);
  94. game.camera_move(-inputStep, 0);
  95. ensurePanTimerRunning();
  96. event.accepted = true;
  97. break;
  98. case Qt.Key_Right:
  99. beginPanKey(event);
  100. game.camera_move(inputStep, 0);
  101. ensurePanTimerRunning();
  102. event.accepted = true;
  103. break;
  104. case Qt.Key_Q:
  105. game.camera_yaw(-yawStep);
  106. event.accepted = true;
  107. break;
  108. case Qt.Key_E:
  109. game.camera_yaw(yawStep);
  110. event.accepted = true;
  111. break;
  112. case Qt.Key_R:
  113. game.camera_orbit_direction(1, shiftHeld);
  114. event.accepted = true;
  115. break;
  116. case Qt.Key_F:
  117. game.camera_orbit_direction(-1, shiftHeld);
  118. event.accepted = true;
  119. break;
  120. case Qt.Key_X:
  121. if (game.select_all_troops)
  122. game.select_all_troops();
  123. event.accepted = true;
  124. break;
  125. case Qt.Key_P:
  126. if (game.has_units_selected) {
  127. game.cursor_mode = "patrol";
  128. event.accepted = true;
  129. }
  130. break;
  131. case Qt.Key_G:
  132. if (game.has_units_selected) {
  133. game.cursor_mode = "guard";
  134. event.accepted = true;
  135. }
  136. break;
  137. case Qt.Key_H:
  138. if (game.has_units_selected && game.on_hold_command) {
  139. game.on_hold_command();
  140. event.accepted = true;
  141. }
  142. break;
  143. }
  144. }
  145. Keys.onReleased: function(event) {
  146. if (typeof game === 'undefined')
  147. return ;
  148. var movementKeys = [Qt.Key_Up, Qt.Key_Down, Qt.Key_Left, Qt.Key_Right];
  149. if (movementKeys.indexOf(event.key) !== -1) {
  150. if (pressedKeys[event.key]) {
  151. pressedKeys[event.key] = false;
  152. renderArea.keyPanCount = Math.max(0, renderArea.keyPanCount - 1);
  153. }
  154. var anyHeld = false;
  155. for (var k in pressedKeys) {
  156. if (pressedKeys[k]) {
  157. anyHeld = true;
  158. break;
  159. }
  160. }
  161. if (!anyHeld) {
  162. if (keyPanTimer.running)
  163. keyPanTimer.stop();
  164. if (renderArea.keyPanCount === 0 && !renderArea.mousePanActive)
  165. mainWindow.edgeScrollDisabled = false;
  166. }
  167. }
  168. if (event.key === Qt.Key_Shift) {
  169. if (renderArea.keyPanCount === 0 && !renderArea.mousePanActive)
  170. mainWindow.edgeScrollDisabled = false;
  171. }
  172. }
  173. focus: true
  174. Timer {
  175. id: keyPanTimer
  176. interval: 16
  177. repeat: true
  178. running: false
  179. onTriggered: {
  180. if (typeof game === 'undefined')
  181. return ;
  182. var step = (Qt.inputModifiers & Qt.ShiftModifier) ? 2 : 1;
  183. var dx = 0;
  184. var dz = 0;
  185. if (pressedKeys[Qt.Key_Up])
  186. dz += step;
  187. if (pressedKeys[Qt.Key_Down])
  188. dz -= step;
  189. if (pressedKeys[Qt.Key_Left])
  190. dx -= step;
  191. if (pressedKeys[Qt.Key_Right])
  192. dx += step;
  193. if (dx !== 0 || dz !== 0)
  194. game.camera_move(dx, dz);
  195. }
  196. }
  197. GLView {
  198. id: renderArea
  199. property int keyPanCount: 0
  200. property bool mousePanActive: false
  201. anchors.fill: parent
  202. engine: game
  203. focus: false
  204. Component.onCompleted: {
  205. if (typeof game !== 'undefined' && game.cursor_mode)
  206. gameView.cursorMode = game.cursor_mode;
  207. }
  208. Connections {
  209. function onCursor_mode_changed() {
  210. if (typeof game !== 'undefined' && game.cursor_mode)
  211. gameView.cursorMode = game.cursor_mode;
  212. }
  213. function onPlacing_formation_changed() {
  214. if (typeof game !== 'undefined')
  215. gameView.isPlacingFormation = game.is_placing_formation;
  216. }
  217. function onPlacing_construction_changed() {
  218. if (typeof game !== 'undefined')
  219. gameView.isPlacingConstruction = game.is_placing_construction;
  220. }
  221. target: game
  222. }
  223. MouseArea {
  224. id: mouseArea
  225. property bool isSelecting: false
  226. property real startX: 0
  227. property real startY: 0
  228. anchors.fill: parent
  229. acceptedButtons: Qt.LeftButton | Qt.RightButton
  230. hoverEnabled: true
  231. propagateComposedEvents: true
  232. preventStealing: true
  233. cursorShape: (gameView.cursorMode === "normal") ? Qt.ArrowCursor : Qt.BlankCursor
  234. enabled: gameView.visible
  235. onEntered: {
  236. if (typeof game !== 'undefined' && game.set_hover_at_screen)
  237. game.set_hover_at_screen(0, 0);
  238. }
  239. onExited: {
  240. if (typeof game !== 'undefined' && game.set_hover_at_screen)
  241. game.set_hover_at_screen(-1, -1);
  242. }
  243. onPositionChanged: function(mouse) {
  244. if (isSelecting) {
  245. var endX = mouse.x;
  246. var endY = mouse.y;
  247. selectionBox.x = Math.min(startX, endX);
  248. selectionBox.y = Math.min(startY, endY);
  249. selectionBox.width = Math.abs(endX - startX);
  250. selectionBox.height = Math.abs(endY - startY);
  251. } else {
  252. if (typeof game !== 'undefined' && game.set_hover_at_screen)
  253. game.set_hover_at_screen(mouse.x, mouse.y);
  254. if (gameView.isPlacingFormation) {
  255. if (typeof game !== 'undefined' && game.on_formation_mouse_move)
  256. game.on_formation_mouse_move(mouse.x, mouse.y);
  257. }
  258. if (gameView.isPlacingConstruction) {
  259. if (typeof game !== 'undefined' && game.on_construction_mouse_move)
  260. game.on_construction_mouse_move(mouse.x, mouse.y);
  261. }
  262. }
  263. }
  264. onWheel: function(w) {
  265. var dy = (w.angleDelta ? w.angleDelta.y / 120 : w.delta / 120);
  266. if (typeof game !== 'undefined' && game.is_placing_formation) {
  267. if (game.on_formation_scroll)
  268. game.on_formation_scroll(dy);
  269. w.accepted = true;
  270. return ;
  271. }
  272. if (dy !== 0 && typeof game !== 'undefined' && game.camera_zoom)
  273. game.camera_zoom(dy * 0.8);
  274. w.accepted = true;
  275. }
  276. onPressed: function(mouse) {
  277. if (mouse.button === Qt.LeftButton) {
  278. if (gameView.setRallyMode) {
  279. if (typeof game !== 'undefined' && game.set_rally_at_screen)
  280. game.set_rally_at_screen(mouse.x, mouse.y);
  281. gameView.setRallyMode = false;
  282. return ;
  283. }
  284. if (gameView.cursorMode === "attack") {
  285. if (typeof game !== 'undefined' && game.on_attack_click)
  286. game.on_attack_click(mouse.x, mouse.y);
  287. return ;
  288. }
  289. if (gameView.cursorMode === "guard") {
  290. if (typeof game !== 'undefined' && game.on_guard_click)
  291. game.on_guard_click(mouse.x, mouse.y);
  292. return ;
  293. }
  294. if (gameView.cursorMode === "patrol") {
  295. if (typeof game !== 'undefined' && game.on_patrol_click)
  296. game.on_patrol_click(mouse.x, mouse.y);
  297. return ;
  298. }
  299. if (gameView.cursorMode === "place_building") {
  300. if (typeof game !== 'undefined' && game.place_building_at_screen)
  301. game.place_building_at_screen(mouse.x, mouse.y);
  302. return ;
  303. }
  304. if (typeof game !== 'undefined' && game.is_placing_formation) {
  305. if (game.on_formation_confirm)
  306. game.on_formation_confirm();
  307. return ;
  308. }
  309. if (typeof game !== 'undefined' && game.is_placing_construction) {
  310. if (game.on_construction_confirm)
  311. game.on_construction_confirm();
  312. return ;
  313. }
  314. isSelecting = true;
  315. startX = mouse.x;
  316. startY = mouse.y;
  317. selectionBox.x = startX;
  318. selectionBox.y = startY;
  319. selectionBox.width = 0;
  320. selectionBox.height = 0;
  321. selectionBox.visible = true;
  322. } else if (mouse.button === Qt.RightButton) {
  323. if (typeof game !== 'undefined' && game.is_placing_formation) {
  324. if (game.on_formation_cancel)
  325. game.on_formation_cancel();
  326. return ;
  327. }
  328. if (typeof game !== 'undefined' && game.is_placing_construction) {
  329. if (game.on_construction_cancel)
  330. game.on_construction_cancel();
  331. return ;
  332. }
  333. renderArea.mousePanActive = true;
  334. mainWindow.edgeScrollDisabled = true;
  335. if (gameView.setRallyMode)
  336. gameView.setRallyMode = false;
  337. if (typeof game !== 'undefined' && game.on_right_click)
  338. game.on_right_click(mouse.x, mouse.y);
  339. }
  340. }
  341. onDoubleClicked: function(mouse) {
  342. if (mouse.button === Qt.RightButton) {
  343. if (typeof game !== 'undefined' && game.on_right_double_click)
  344. game.on_right_double_click(mouse.x, mouse.y);
  345. }
  346. }
  347. onReleased: function(mouse) {
  348. if (mouse.button === Qt.LeftButton && isSelecting) {
  349. isSelecting = false;
  350. selectionBox.visible = false;
  351. if (selectionBox.width > 5 && selectionBox.height > 5) {
  352. areaSelected(selectionBox.x, selectionBox.y, selectionBox.x + selectionBox.width, selectionBox.y + selectionBox.height);
  353. if (typeof game !== 'undefined' && game.on_area_selected)
  354. game.on_area_selected(selectionBox.x, selectionBox.y, selectionBox.x + selectionBox.width, selectionBox.y + selectionBox.height, false);
  355. } else {
  356. mapClicked(mouse.x, mouse.y);
  357. if (typeof game !== 'undefined' && game.on_click_select)
  358. game.on_click_select(mouse.x, mouse.y, false);
  359. }
  360. }
  361. if (mouse.button === Qt.RightButton) {
  362. renderArea.mousePanActive = false;
  363. mainWindow.edgeScrollDisabled = (renderArea.keyPanCount > 0) || renderArea.mousePanActive;
  364. }
  365. }
  366. }
  367. Rectangle {
  368. id: selectionBox
  369. visible: false
  370. border.color: "white"
  371. border.width: 1
  372. color: "transparent"
  373. }
  374. }
  375. Item {
  376. id: customCursorContainer
  377. visible: gameView.cursorMode !== "normal"
  378. width: 32
  379. height: 32
  380. z: 999999
  381. x: (typeof game !== 'undefined' && game.global_cursor_x) ? game.global_cursor_x - 16 : 0
  382. y: (typeof game !== 'undefined' && game.global_cursor_y) ? game.global_cursor_y - 16 : 0
  383. Item {
  384. id: attackCursorContainer
  385. property real pulseScale: 1
  386. visible: gameView.cursorMode === "attack"
  387. anchors.fill: parent
  388. Canvas {
  389. id: attackCursor
  390. anchors.fill: parent
  391. scale: attackCursorContainer.pulseScale
  392. transformOrigin: Item.Center
  393. onPaint: {
  394. var ctx = getContext("2d");
  395. ctx.clearRect(0, 0, width, height);
  396. ctx.strokeStyle = "#ff4444";
  397. ctx.lineWidth = 3;
  398. ctx.beginPath();
  399. ctx.moveTo(16, 4);
  400. ctx.lineTo(16, 28);
  401. ctx.stroke();
  402. ctx.beginPath();
  403. ctx.moveTo(4, 16);
  404. ctx.lineTo(28, 16);
  405. ctx.stroke();
  406. ctx.fillStyle = "#ff2222";
  407. ctx.beginPath();
  408. ctx.arc(16, 16, 4, 0, Math.PI * 2);
  409. ctx.fill();
  410. ctx.strokeStyle = "rgba(255, 68, 68, 0.5)";
  411. ctx.lineWidth = 1;
  412. ctx.beginPath();
  413. ctx.arc(16, 16, 7, 0, Math.PI * 2);
  414. ctx.stroke();
  415. ctx.strokeStyle = "#e74c3c";
  416. ctx.lineWidth = 2;
  417. ctx.beginPath();
  418. ctx.moveTo(8, 12);
  419. ctx.lineTo(8, 8);
  420. ctx.lineTo(12, 8);
  421. ctx.stroke();
  422. ctx.beginPath();
  423. ctx.moveTo(20, 8);
  424. ctx.lineTo(24, 8);
  425. ctx.lineTo(24, 12);
  426. ctx.stroke();
  427. ctx.beginPath();
  428. ctx.moveTo(8, 20);
  429. ctx.lineTo(8, 24);
  430. ctx.lineTo(12, 24);
  431. ctx.stroke();
  432. ctx.beginPath();
  433. ctx.moveTo(20, 24);
  434. ctx.lineTo(24, 24);
  435. ctx.lineTo(24, 20);
  436. ctx.stroke();
  437. }
  438. Component.onCompleted: requestPaint()
  439. }
  440. SequentialAnimation on pulseScale {
  441. running: attackCursorContainer.visible
  442. loops: Animation.Infinite
  443. NumberAnimation {
  444. from: 1
  445. to: 1.2
  446. duration: 400
  447. easing.type: Easing.InOutQuad
  448. }
  449. NumberAnimation {
  450. from: 1.2
  451. to: 1
  452. duration: 400
  453. easing.type: Easing.InOutQuad
  454. }
  455. }
  456. }
  457. Canvas {
  458. id: guardCursor
  459. visible: gameView.cursorMode === "guard"
  460. anchors.fill: parent
  461. onPaint: {
  462. var ctx = getContext("2d");
  463. ctx.clearRect(0, 0, width, height);
  464. ctx.fillStyle = "#3498db";
  465. ctx.strokeStyle = "#2980b9";
  466. ctx.lineWidth = 2;
  467. ctx.beginPath();
  468. ctx.moveTo(16, 6);
  469. ctx.lineTo(24, 10);
  470. ctx.lineTo(24, 18);
  471. ctx.lineTo(16, 26);
  472. ctx.lineTo(8, 18);
  473. ctx.lineTo(8, 10);
  474. ctx.closePath();
  475. ctx.fill();
  476. ctx.stroke();
  477. ctx.strokeStyle = "#ecf0f1";
  478. ctx.lineWidth = 2;
  479. ctx.beginPath();
  480. ctx.moveTo(13, 16);
  481. ctx.lineTo(15, 18);
  482. ctx.lineTo(19, 12);
  483. ctx.stroke();
  484. }
  485. Component.onCompleted: requestPaint()
  486. }
  487. Canvas {
  488. id: patrolCursor
  489. visible: gameView.cursorMode === "patrol"
  490. anchors.fill: parent
  491. onPaint: {
  492. var ctx = getContext("2d");
  493. ctx.clearRect(0, 0, width, height);
  494. ctx.strokeStyle = "#27ae60";
  495. ctx.lineWidth = 2;
  496. ctx.beginPath();
  497. ctx.arc(16, 16, 10, 0, Math.PI * 2);
  498. ctx.stroke();
  499. ctx.fillStyle = "#27ae60";
  500. ctx.beginPath();
  501. ctx.moveTo(26, 16);
  502. ctx.lineTo(22, 13);
  503. ctx.lineTo(22, 19);
  504. ctx.closePath();
  505. ctx.fill();
  506. ctx.beginPath();
  507. ctx.moveTo(6, 16);
  508. ctx.lineTo(10, 13);
  509. ctx.lineTo(10, 19);
  510. ctx.closePath();
  511. ctx.fill();
  512. ctx.beginPath();
  513. ctx.arc(16, 16, 3, 0, Math.PI * 2);
  514. ctx.fill();
  515. }
  516. Component.onCompleted: requestPaint()
  517. }
  518. }
  519. }