| @@ -0,0 +1,39 @@ | |||
| <template> | |||
| <button class="action-button" @click="execute"> | |||
| {{ action.name }} | |||
| </button> | |||
| </template> | |||
| <script lang="ts"> | |||
| import { Component, Prop, Vue, Watch, Emit } from 'vue-property-decorator' | |||
| import { Action } from '@/game/combat' | |||
| import { Creature } from '@/game/entity' | |||
| @Component({}) | |||
| export default class ActionButton extends Vue { | |||
| @Prop() | |||
| action!: Action | |||
| @Prop() | |||
| user!: Creature | |||
| @Prop() | |||
| target!: Creature | |||
| @Emit("execute") | |||
| execute () { | |||
| this.$emit('executed', this.action.execute(this.user, this.target)) | |||
| } | |||
| } | |||
| </script> | |||
| <style scoped> | |||
| .action-button { | |||
| width: 100px; | |||
| height: 100px; | |||
| } | |||
| </style> | |||
| @@ -5,21 +5,28 @@ | |||
| <Statblock :subject="player" /> | |||
| <Statblock :subject="enemy" /> | |||
| </div> | |||
| <div id="log"></div> | |||
| <div id="log"> | |||
| <div v-for="(entry, index) in combatLog" :key="'log' + index"> | |||
| <div v-for="(line, lineIndex) in entry.render()" :key="index + ' ' + lineIndex"> | |||
| {{ line }} | |||
| </div> | |||
| <br> | |||
| </div> | |||
| </div> | |||
| <div class="horiz-display"> | |||
| <div> | |||
| <h2>Your moves</h2> | |||
| <div class="vert-display"> | |||
| <button class="combat-button" @mouseleave="actionDescription= ''" @mouseover="actionDescription = action.desc" v-for="action in player.validActions(enemy)" :key="'player-' + action.name" v-on:click="log(action.execute(player, enemy))">{{action.name}}</button> | |||
| <button class="combat-button" @mouseleave="actionDescription= ''" @mouseover="actionDescription = action.desc" v-for="action in player.validActions(player)" :key="'player-' + action.name" v-on:click="log(action.execute(player, player))">{{action.name}}</button> | |||
| <ActionButton @executed="executed" v-for="action in player.validActions(enemy)" :key="'player' + action.name" :action="action" :user="player" :target="enemy" /> | |||
| <ActionButton @executed="executed" v-for="action in player.validActions(player)" :key="'player' + action.name" :action="action" :user="player" :target="enemy" /> | |||
| </div> | |||
| <div>{{actionDescription}}</div> | |||
| </div> | |||
| <div> | |||
| <h2>Enemy moves</h2> | |||
| <div class="vert-display"> | |||
| <button class="combat-button" v-for="action in enemy.validActions(player)" :key="'enemy-' + action.name" v-on:click="log(action.execute(enemy, player))">{{action.name}}</button> | |||
| <button class="combat-button" v-for="action in enemy.validActions(enemy)" :key="'enemy-' + action.name" v-on:click="log(action.execute(enemy, enemy))">{{action.name}}</button> | |||
| <ActionButton @executed="executed" v-for="action in enemy.validActions(player)" :key="'player' + action.name" :action="action" :user="enemy" :target="player" /> | |||
| <ActionButton @executed="executed" v-for="action in enemy.validActions(enemy)" :key="'player' + action.name" :action="action" :user="enemy" :target="player" /> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| @@ -29,12 +36,13 @@ | |||
| <script lang="ts"> | |||
| import { Component, Prop, Vue, Watch, Emit } from 'vue-property-decorator' | |||
| import { Creature, POV } from '@/game/entity' | |||
| import { log, LogEntry } from '@/game/interface' | |||
| import { LogEntry } from '@/game/interface' | |||
| import Statblock from './Statblock.vue' | |||
| import ActionButton from './ActionButton.vue' | |||
| @Component( | |||
| { | |||
| components: { Statblock } | |||
| components: { Statblock, ActionButton } | |||
| } | |||
| ) | |||
| export default class Combat extends Vue { | |||
| @@ -46,11 +54,16 @@ export default class Combat extends Vue { | |||
| actionDescription = '' | |||
| private log: (entry: LogEntry) => void; | |||
| private combatLog: Array<LogEntry> | |||
| constructor () { | |||
| super() | |||
| this.log = log | |||
| this.combatLog = [] | |||
| } | |||
| @Emit("executed") | |||
| executed (entry: LogEntry) { | |||
| this.combatLog.push(entry) | |||
| } | |||
| } | |||
| </script> | |||
| @@ -79,10 +92,6 @@ a { | |||
| display: flex; | |||
| flex-direction: column; | |||
| } | |||
| .combat-button { | |||
| width: 100px; | |||
| height: 100px; | |||
| } | |||
| </style> | |||
| <style> | |||
| @@ -1,10 +1,10 @@ | |||
| <template> | |||
| <div class="statblock"> | |||
| <h2 v-if="subject.perspective === firstperson">Player</h2> | |||
| <h2 v-if="subject.perspective === firstperson">You</h2> | |||
| <h2 v-if="subject.perspective !== firstperson">{{subject.name.all.capital}}</h2> | |||
| <div>Health: {{subject.health.toFixed(0)}} / {{subject.maxHealth.toFixed(0)}}</div> | |||
| <div v-for="stat in Object.keys(subject.stats)" v-bind:key="stat">{{stat}}: {{subject.stats[stat]}}</div> | |||
| <div>Status: {{subject.state}}</div> | |||
| <div>Status: {{subject.status}}</div> | |||
| <ContainerView v-for="container in subject.containers" :key="container.name" :container="container" /> | |||
| </div> | |||
| </template> | |||
| @@ -33,8 +33,9 @@ export default class Statblock extends Vue { | |||
| <!-- Add "scoped" attribute to limit CSS to this component only --> | |||
| <style scoped> | |||
| h3 { | |||
| margin: 40px 0 0; | |||
| h2 { | |||
| margin-bottom: 16pt; | |||
| font-size: 200%; | |||
| } | |||
| ul { | |||
| list-style-type: none; | |||
| @@ -225,9 +225,9 @@ export class StruggleAction extends PairAction { | |||
| private test: StatTest | |||
| protected failLines: POVPair<Entity, Entity> = new POVPair([ | |||
| [[POV.First, POV.Third], (user, target) => new LogLines(`You fail to make a meal out of ${target.name}`)], | |||
| [[POV.Third, POV.First], (user, target) => new LogLines(`${user.name.capital} tries to devour you, but fails`)], | |||
| [[POV.Third, POV.Third], (user, target) => new LogLines(`${target.name} unsuccessfully tries to swallow ${target.name}`)] | |||
| [[POV.First, POV.Third], (user, target) => new LogLines(`You fail to escape from ${target.name}`)], | |||
| [[POV.Third, POV.First], (user, target) => new LogLines(`${user.name.capital} tries to escape from you, but fails`)], | |||
| [[POV.Third, POV.Third], (user, target) => new LogLines(`${target.name} unsuccessfully struggles within ${target.name}`)] | |||
| ]) | |||
| allowed (user: Creature, target: Creature) { | |||
| @@ -17,6 +17,7 @@ export interface Mortal extends Entity { | |||
| resistances: Map<DamageType, number>; | |||
| takeDamage: (damage: Damage) => void; | |||
| stats: Stats; | |||
| status: string; | |||
| } | |||
| export class Creature implements Mortal, Pred, Prey, Combatant { | |||
| @@ -53,6 +54,17 @@ export class Creature implements Mortal, Pred, Prey, Combatant { | |||
| }) | |||
| } | |||
| get status (): string { | |||
| if (this.health < 0) { | |||
| return "Unconscious" | |||
| } | |||
| if (this.containedIn !== null) { | |||
| return "Devoured" | |||
| } | |||
| return "Normal" | |||
| } | |||
| validActions (target: Creature): Array<Action> { | |||
| let choices = this.actions.concat(this.containers.flatMap(container => container.actions)) | |||
| @@ -1,5 +1,5 @@ | |||
| export interface LogEntry { | |||
| render: () => HTMLElement[]; | |||
| render: () => string[]; | |||
| } | |||
| export class LogLines implements LogEntry { | |||
| @@ -9,15 +9,8 @@ export class LogLines implements LogEntry { | |||
| this.lines = lines | |||
| } | |||
| render (): HTMLElement[] { | |||
| const p = document.createElement('p') | |||
| this.lines.forEach(line => { | |||
| const div = document.createElement('div') | |||
| div.innerText = line | |||
| p.appendChild(div) | |||
| }) | |||
| return [p] | |||
| render (): string[] { | |||
| return this.lines | |||
| } | |||
| } | |||
| @@ -28,13 +21,7 @@ export class CompositeLog implements LogEntry { | |||
| this.entries = entries | |||
| } | |||
| render (): HTMLElement[] { | |||
| return this.entries.map(entry => entry.render()).reduce((results: HTMLElement[], next: HTMLElement[]) => results.concat(next), []) | |||
| render (): string[] { | |||
| return this.entries.flatMap(e => e.render()) | |||
| } | |||
| } | |||
| export function log (entry: LogEntry): void { | |||
| entry.render().forEach(elem => { | |||
| document.querySelector('#log')!.appendChild(elem) | |||
| }) | |||
| } | |||