Feast 2.0!
Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.
 
 
 
 
 

387 строки
9.4 KiB

  1. <template>
  2. <div class="combat-layout">
  3. <div @wheel="horizWheelLeft" class="stat-column" id="left-stats">
  4. <Statblock @selectAlly="right = combatant" @select="left = combatant" class="left-stats" :data-active="combatant === left" :data-active-ally="combatant === right" :data-eaten="combatant.containedIn !== null" :data-dead="combatant.vigors.Health <= 0" v-for="(combatant, index) in combatants.filter(c => c.side == Side.Heroes && !c.destroyed).slice().reverse()" v-bind:key="'left-stat-' + index" :subject="combatant" />
  5. </div>
  6. <div @wheel="horizWheelRight" class="stat-column" id="right-stats">
  7. <Statblock @selectAlly="left = combatant" @select="right = combatant" class="right-stats" :data-active="combatant === right" :data-active-ally="combatant === left" :data-eaten="combatant.containedIn !== null" :data-dead="combatant.vigors.Health <= 0" v-for="(combatant, index) in combatants.filter(c => c.side == Side.Monsters && !c.destroyed)" v-bind:key="'right-stat-' + index" :subject="combatant" />
  8. </div>
  9. <div id="log">
  10. </div>
  11. <div class="left-fader">
  12. </div>
  13. <div class="left-actions">
  14. <div class="vert-display">
  15. <i class="action-label fas fa-users" v-if="left.validGroupActions(combatants).length > 0"></i>
  16. <ActionButton @described="described" @executed="executedLeft" v-for="(action, index) in left.validGroupActions(combatants)" :key="'left-' + action.name + '-' + index" :action="action" :user="left" :target="right" :combatants="combatants" />
  17. <i class="action-label fas fa-user-friends" v-if="left.validActions(right).length > 0"></i>
  18. <ActionButton @described="described" @executed="executedLeft" v-for="(action, index) in left.validActions(right)" :key="'left-' + action.name + '-' + index" :action="action" :user="left" :target="right" :combatants="combatants" />
  19. <i class="action-label fas fa-user" v-if="left.validActions(left).length > 0"></i>
  20. <ActionButton @described="described" @executed="executedLeft" v-for="(action, index) in left.validActions(left)" :key="'left-' + action.name + '-' + index" :action="action" :user="left" :target="left" :combatants="combatants" />
  21. </div>
  22. <div>{{actionDescription}}</div>
  23. </div>
  24. <div class="right-fader">
  25. </div>
  26. <div class="right-actions">
  27. <div class="vert-display">
  28. <i class="action-label fas fa-users" v-if="right.validGroupActions(combatants).length > 0"></i>
  29. <ActionButton @described="described" @executed="executedRight" v-for="(action, index) in right.validGroupActions(combatants)" :key="'right-' + action.name + '-' + index" :action="action" :user="right" :target="left" :combatants="combatants" />
  30. <i class="action-label fas fa-user-friends" v-if="right.validActions(left).length > 0"></i>
  31. <ActionButton @described="described" @executed="executedRight" v-for="(action, index) in right.validActions(left)" :key="'right-' + action.name + '-' + index" :action="action" :user="right" :target="left" :combatants="combatants" />
  32. <i class="action-label fas fa-user" v-if="right.validActions(right).length > 0"></i>
  33. <ActionButton @described="described" @executed="executedRight" v-for="(action, index) in right.validActions(right)" :key="'right-' + action.name + '-' + index" :action="action" :user="right" :target="right" :combatants="combatants" />
  34. </div>
  35. </div>
  36. <div id="action-desc">
  37. </div>
  38. </div>
  39. </template>
  40. <script lang="ts">
  41. import { Component, Prop, Vue, Watch, Emit } from 'vue-property-decorator'
  42. import { Creature, POV } from '@/game/entity'
  43. import { LogEntry } from '@/game/interface'
  44. import Statblock from './Statblock.vue'
  45. import ActionButton from './ActionButton.vue'
  46. import { Side } from '@/game/combat'
  47. @Component(
  48. {
  49. components: { Statblock, ActionButton },
  50. data () {
  51. return {
  52. left: null,
  53. right: null
  54. }
  55. },
  56. methods: {
  57. horizWheelLeft (event: MouseWheelEvent) {
  58. const target = this.$el.querySelector("#left-stats")
  59. if (target !== null) {
  60. target.scrollBy({ top: 0, left: event.deltaY, behavior: 'smooth' })
  61. }
  62. },
  63. horizWheelRight (event: MouseWheelEvent) {
  64. const target = this.$el.querySelector("#right-stats")
  65. if (target !== null) {
  66. target.scrollBy({ top: 0, left: -event.deltaY, behavior: 'smooth' })
  67. }
  68. }
  69. }
  70. }
  71. )
  72. export default class Combat extends Vue {
  73. @Prop()
  74. combatants!: Array<Creature>
  75. Side = Side
  76. actionDescription = ''
  77. created () {
  78. this.$data.left = this.combatants.filter(x => x.side === Side.Heroes)[0]
  79. this.$data.right = this.combatants.filter(x => x.side === Side.Monsters)[0]
  80. }
  81. mounted () {
  82. const leftStats = this.$el.querySelector("#left-stats")
  83. if (leftStats !== null) {
  84. leftStats.scrollTo(leftStats.getBoundingClientRect().width * 2, 0)
  85. }
  86. }
  87. @Emit("executedLeft")
  88. executedLeft (entry: LogEntry) {
  89. const log = document.querySelector("#log")
  90. if (log !== null) {
  91. const holder = document.createElement("div")
  92. entry.render().forEach(element => {
  93. holder.appendChild(element)
  94. })
  95. holder.classList.add("left-move")
  96. log.appendChild(holder)
  97. log.scrollTo({ top: log.scrollHeight, left: 0 })
  98. }
  99. }
  100. @Emit("executedRight")
  101. executedRight (entry: LogEntry) {
  102. const log = document.querySelector("#log")
  103. if (log !== null) {
  104. const holder = document.createElement("div")
  105. entry.render().forEach(element => {
  106. holder.appendChild(element)
  107. })
  108. holder.classList.add("right-move")
  109. log.appendChild(holder)
  110. log.scrollTo({ top: log.scrollHeight, left: 0 })
  111. }
  112. }
  113. @Emit("described")
  114. described (entry: LogEntry) {
  115. const actionDesc = document.querySelector("#action-desc")
  116. if (actionDesc !== null) {
  117. const holder = document.createElement("div")
  118. entry.render().forEach(element => {
  119. holder.appendChild(element)
  120. })
  121. actionDesc.innerHTML = ''
  122. actionDesc.appendChild(holder)
  123. }
  124. }
  125. }
  126. </script>
  127. <!-- Add "scoped" attribute to limit CSS to this component only -->
  128. <style scoped>
  129. .combat-layout {
  130. display: grid;
  131. grid-template-rows: fit-content(50%) 10% [main-row-start] 1fr 20% [main-row-end] ;
  132. grid-template-columns: 20% [main-col-start] 1fr 1fr [main-col-end] 20%;
  133. width: 100%;
  134. height: 100%;
  135. flex: 10;
  136. overflow: hidden;
  137. }
  138. #log {
  139. grid-area: main-row-start / main-col-start / main-row-end / main-col-end;
  140. overflow-y: scroll;
  141. font-size: 12pt;
  142. width: 100%;
  143. max-height: 100%;
  144. align-self: flex-start;
  145. }
  146. #left-stats,
  147. #right-stats {
  148. display: flex;
  149. }
  150. #left-stats {
  151. flex-direction: row;
  152. }
  153. #right-stats {
  154. flex-direction: row;
  155. }
  156. #left-stats {
  157. grid-area: 1 / 1 / 2 / 3
  158. }
  159. #right-stats {
  160. grid-area: 1 / 3 / 2 / 5;
  161. }
  162. .stat-column {
  163. overflow-x: scroll;
  164. overflow-y: auto;
  165. }
  166. .left-fader {
  167. grid-area: 2 / 1 / 4 / 2;
  168. }
  169. .right-fader {
  170. grid-area: 2 / 4 / 4 / 5;
  171. }
  172. .left-fader,
  173. .right-fader {
  174. z-index: 1;
  175. pointer-events: none;
  176. background: linear-gradient(to bottom, #111, #00000000 10%, #00000000 90%, #111 100%);
  177. height: 100%;
  178. width: 100%;
  179. }
  180. .left-actions {
  181. grid-area: 2 / 1 / 4 / 2;
  182. }
  183. .right-actions {
  184. grid-area: 2 / 4 / 4 / 5;
  185. }
  186. .left-actions,
  187. .right-actions {
  188. overflow-y: hidden;
  189. display: flex;
  190. flex-direction: column;
  191. height: 100%;
  192. width: 100%;
  193. }
  194. #action-desc {
  195. grid-area: 2 / main-col-start / main-row-start / main-col-end;
  196. padding: 8pt;
  197. text-align: center;
  198. font-size: 16px;
  199. }
  200. h3 {
  201. margin: 40px 0 0;
  202. }
  203. ul {
  204. list-style-type: none;
  205. padding: 0;
  206. }
  207. li {
  208. display: inline-block;
  209. margin: 0 10px;
  210. }
  211. a {
  212. color: #42b983;
  213. }
  214. .horiz-display {
  215. display: flex;
  216. justify-content: center;
  217. }
  218. .vert-display {
  219. display: flex;
  220. flex-direction: column;
  221. align-items: center;
  222. flex-wrap: nowrap;
  223. justify-content: start;
  224. height: 100%;
  225. overflow-y: auto;
  226. padding: 64px 0 64px;
  227. }
  228. .action-label {
  229. font-size: 200%;
  230. }
  231. </style>
  232. <style>
  233. .log-damage {
  234. font-weight: bold;
  235. }
  236. .damage-instance {
  237. white-space: nowrap;
  238. }
  239. #log > div {
  240. color: #888;
  241. padding-top: 4pt;
  242. padding-bottom: 4pt;
  243. }
  244. div.left-move,
  245. div.right-move {
  246. color: #888;
  247. }
  248. div.left-move {
  249. text-align: start;
  250. margin-right: 25%;
  251. margin-left: 2%;
  252. }
  253. div.right-move {
  254. text-align: end;
  255. margin-left: 25%;
  256. margin-right: 2%;
  257. }
  258. #log img {
  259. width: 75%;
  260. }
  261. #log > div.left-move:nth-last-child(7) {
  262. padding-top: 8pt;
  263. color: #988;
  264. }
  265. #log > div.left-move:nth-last-child(6) {
  266. padding-top: 12pt;
  267. color: #a88;
  268. }
  269. #log > div.left-move:nth-last-child(5) {
  270. padding-top: 16pt;
  271. color: #b88;
  272. }
  273. #log > div.left-move:nth-last-child(4) {
  274. padding-top: 20pt;
  275. color: #c88;
  276. }
  277. #log > div.left-move:nth-last-child(3) {
  278. padding-top: 24pt;
  279. color: #d88;
  280. }
  281. #log > div.left-move:nth-last-child(2) {
  282. padding-top: 28pt;
  283. color: #e88;
  284. }
  285. #log > div.left-move:nth-last-child(1) {
  286. padding-top: 32pt;
  287. color: #f88;
  288. }
  289. #log > div.right-move:nth-last-child(7) {
  290. padding-top: 8pt;
  291. color: #988;
  292. }
  293. #log > div.right-move:nth-last-child(6) {
  294. padding-top: 12pt;
  295. color: #a88;
  296. }
  297. #log > div.right-move:nth-last-child(5) {
  298. padding-top: 16pt;
  299. color: #b88;
  300. }
  301. #log > div.right-move:nth-last-child(4) {
  302. padding-top: 20pt;
  303. color: #c88;
  304. }
  305. #log > div.right-move:nth-last-child(3) {
  306. padding-top: 24pt;
  307. color: #d88;
  308. }
  309. #log > div.right-move:nth-last-child(2) {
  310. padding-top: 28pt;
  311. color: #e88;
  312. }
  313. #log > div.right-move:nth-last-child(1) {
  314. padding-top: 32pt;
  315. color: #f88;
  316. }
  317. .left-selector,
  318. .right-selector {
  319. display: flex;
  320. flex-wrap: wrap;
  321. }
  322. .combatant-picker {
  323. flex: 1 1;
  324. }
  325. </style>