| @@ -1,8 +1,11 @@ | |||||
| <template> | <template> | ||||
| <div id="app"> | <div id="app"> | ||||
| <Header /> | <Header /> | ||||
| <Explore v-if="mode === 'explore'" :world="world" /> | |||||
| <Combat @leaveCombat="mode = 'explore'" v-if="mode === 'combat'" :encounter="encounter" /> | |||||
| <div id="main-area"> | |||||
| <transition name="component-fade" mode='out-in'> | |||||
| <component @leaveCombat="mode = 'Explore'" v-bind:is="mode" :world="world" :encounter="encounter" /> | |||||
| </transition> | |||||
| </div> | |||||
| </div> | </div> | ||||
| </template> | </template> | ||||
| @@ -30,7 +33,12 @@ import moment from 'moment' | |||||
| encounter: null, | encounter: null, | ||||
| encounters: null, | encounters: null, | ||||
| world: null, | world: null, | ||||
| mode: 'explore' | |||||
| mode: 'explore', | |||||
| props: { | |||||
| Explore: { | |||||
| world: this | |||||
| } | |||||
| } | |||||
| } | } | ||||
| } | } | ||||
| }) | }) | ||||
| @@ -42,7 +50,7 @@ export default class App extends Vue { | |||||
| @Emit('startFight') | @Emit('startFight') | ||||
| startFight (encounter: Encounter) { | startFight (encounter: Encounter) { | ||||
| this.$data.encounter = encounter | this.$data.encounter = encounter | ||||
| this.$data.mode = 'combat' | |||||
| this.$data.mode = 'Combat' | |||||
| } | } | ||||
| created () { | created () { | ||||
| @@ -62,9 +70,9 @@ export default class App extends Vue { | |||||
| this.$data.encounter = this.$data.encounters[0] | this.$data.encounter = this.$data.encounters[0] | ||||
| const home = new Place('Home', 'This is not not home') | |||||
| const home = new Place(new ProperNoun('your home'), 'This is not not home') | |||||
| const street = new Place('Street', 'The street') | |||||
| const street = new Place(new ImproperNoun('street'), 'The street') | |||||
| home.biconnect(Direction.North, street) | home.biconnect(Direction.North, street) | ||||
| this.$data.encounters.forEach((encounter: Encounter) => home.choices.push(new Choice( | this.$data.encounters.forEach((encounter: Encounter) => home.choices.push(new Choice( | ||||
| @@ -78,7 +86,7 @@ export default class App extends Vue { | |||||
| } | } | ||||
| ))) | ))) | ||||
| const bar = new Place('Bar', 'This is the bar') | |||||
| const bar = new Place(new ProperNoun('Dave\'s Bar'), 'This is the bar') | |||||
| street.biconnect(Direction.East, bar) | street.biconnect(Direction.East, bar) | ||||
| player.location = home | player.location = home | ||||
| this.$data.world = new World(player) | this.$data.world = new World(player) | ||||
| @@ -144,6 +152,7 @@ body, html { | |||||
| } | } | ||||
| #app { | #app { | ||||
| position: relative; | |||||
| font-family: Avenir, Helvetica, Arial, sans-serif; | font-family: Avenir, Helvetica, Arial, sans-serif; | ||||
| -webkit-font-smoothing: antialiased; | -webkit-font-smoothing: antialiased; | ||||
| -moz-osx-font-smoothing: grayscale; | -moz-osx-font-smoothing: grayscale; | ||||
| @@ -157,6 +166,13 @@ body, html { | |||||
| flex-direction: column; | flex-direction: column; | ||||
| } | } | ||||
| #main-area { | |||||
| position: relative; | |||||
| flex: 10; | |||||
| overflow-x: hidden; | |||||
| overflow-y: hidden; | |||||
| } | |||||
| .tippy-box { | .tippy-box { | ||||
| text-align: center; | text-align: center; | ||||
| border-radius: 10px; | border-radius: 10px; | ||||
| @@ -204,5 +220,11 @@ body, html { | |||||
| *::-webkit-scrollbar-corner { | *::-webkit-scrollbar-corner { | ||||
| background: transparent; | background: transparent; | ||||
| } | } | ||||
| .component-fade-enter-active, .component-fade-leave-active { | |||||
| transition: opacity .3s ease; | |||||
| } | |||||
| .component-fade-enter, .component-fade-leave-to | |||||
| /* .component-fade-leave-active below version 2.1.8 */ { | |||||
| opacity: 0; | |||||
| } | |||||
| </style> | </style> | ||||
| @@ -277,13 +277,12 @@ export default class Combat extends Vue { | |||||
| } | } | ||||
| .combat-layout { | .combat-layout { | ||||
| position: relative; | |||||
| position: absolute; | |||||
| display: grid; | display: grid; | ||||
| grid-template-rows: fit-content(50%) 10% [main-row-start] 1fr 20% [main-row-end] ; | grid-template-rows: fit-content(50%) 10% [main-row-start] 1fr 20% [main-row-end] ; | ||||
| grid-template-columns: 20% [main-col-start] 1fr 1fr [main-col-end] 20%; | grid-template-columns: 20% [main-col-start] 1fr 1fr [main-col-end] 20%; | ||||
| width: 100%; | width: 100%; | ||||
| height: 100%; | height: 100%; | ||||
| flex: 10; | |||||
| overflow-x: hidden; | overflow-x: hidden; | ||||
| overflow-y: hidden; | overflow-y: hidden; | ||||
| } | } | ||||
| @@ -9,11 +9,11 @@ | |||||
| </div> | </div> | ||||
| <Statblock :subject="world.player" :initiative="0" /> | <Statblock :subject="world.player" :initiative="0" /> | ||||
| <div class="explore-info"> | <div class="explore-info"> | ||||
| <h2 class="location-name">{{ location.name }}</h2> | |||||
| <h2 class="location-name">{{ location.name.capital }}</h2> | |||||
| <p class="location-desc">{{ location.desc }}</p> | <p class="location-desc">{{ location.desc }}</p> | ||||
| </div> | </div> | ||||
| <div class="explore-nav"> | <div class="explore-nav"> | ||||
| <NavButton @click.native="location.connections[direction].travel(world, world.player)" v-for="direction in Object.keys(location.connections)" :key="direction" :style="navBtnCss(direction)" :location="location" :direction="direction" /> | |||||
| <NavButton @click.native="writeLog(location.connections[direction].travel(world, world.player))" v-for="direction in Object.keys(location.connections)" :key="direction" :style="navBtnCss(direction)" :location="location" :direction="direction" /> | |||||
| </div> | </div> | ||||
| <div class="explore-choices"> | <div class="explore-choices"> | ||||
| <ChoiceButton @click.native="writeLog(choice.execute(world, world.player))" v-for="(choice, index) in location.choices.filter(choice => choice.visible(world))" :key="'choice' + index" :choice="choice" :world="world" /> | <ChoiceButton @click.native="writeLog(choice.execute(world, world.player))" v-for="(choice, index) in location.choices.filter(choice => choice.visible(world))" :key="'choice' + index" :choice="choice" :world="world" /> | ||||
| @@ -62,17 +62,17 @@ export default class Explore extends Vue { | |||||
| writeLog (entry: LogEntry) { | writeLog (entry: LogEntry) { | ||||
| const log = this.$el.querySelector(".explore-log") | const log = this.$el.querySelector(".explore-log") | ||||
| if (log !== null) { | if (log !== null) { | ||||
| const before = log.querySelector("div.log-entry") | |||||
| const before = log.querySelector("div.explore-log-entry") | |||||
| const holder = document.createElement("div") | const holder = document.createElement("div") | ||||
| holder.classList.add("log-entry") | |||||
| holder.classList.add("explore-log-entry") | |||||
| entry.render().forEach(element => { | entry.render().forEach(element => { | ||||
| holder.appendChild(element) | holder.appendChild(element) | ||||
| }) | }) | ||||
| holder.classList.add("left-move") | |||||
| holder.classList.add("explore-entry") | |||||
| const hline = document.createElement("div") | const hline = document.createElement("div") | ||||
| hline.classList.add("log-separator") | |||||
| hline.classList.add("explore-log-separator") | |||||
| log.insertBefore(hline, before) | log.insertBefore(hline, before) | ||||
| log.insertBefore(holder, hline) | log.insertBefore(holder, hline) | ||||
| @@ -148,7 +148,9 @@ export default class Explore extends Vue { | |||||
| grid-template-rows: 1fr 1fr 1fr; | grid-template-rows: 1fr 1fr 1fr; | ||||
| grid-template-columns: 1fr 1fr 1fr; | grid-template-columns: 1fr 1fr 1fr; | ||||
| width: 100%; | width: 100%; | ||||
| max-width: 1000px; | |||||
| height: 100%; | height: 100%; | ||||
| justify-self: end; | |||||
| } | } | ||||
| .explore-choices { | .explore-choices { | ||||
| @@ -159,3 +161,27 @@ export default class Explore extends Vue { | |||||
| overflow-y: scroll; | overflow-y: scroll; | ||||
| } | } | ||||
| </style> | </style> | ||||
| <style> | |||||
| .explore-log-entry { | |||||
| animation: explore-entry-fade 1s; | |||||
| } | |||||
| @keyframes explore-entry-fade { | |||||
| from { | |||||
| opacity: 0; | |||||
| } | |||||
| to { | |||||
| opacity: 1 | |||||
| } | |||||
| } | |||||
| .explore-log-separator { | |||||
| width: 100%; | |||||
| height: 4px; | |||||
| margin: 16pt 0pt 16pt; | |||||
| background: linear-gradient(90deg, transparent, #444 10%, #444 90%, transparent 100%); | |||||
| } | |||||
| </style> | |||||
| @@ -1,6 +1,6 @@ | |||||
| <template> | <template> | ||||
| <button class="nav-button"> | <button class="nav-button"> | ||||
| {{ location.connections[direction].dst.name }} | |||||
| {{ location.connections[direction].dst.name.capital.all }} | |||||
| <div class="tooltip-template"> | <div class="tooltip-template"> | ||||
| <div class="tooltip-title">{{ location.connections[direction].name }}</div> | <div class="tooltip-title">{{ location.connections[direction].name }}</div> | ||||
| <div class="tooltip-body">{{ location.connections[direction].desc }}</div> | <div class="tooltip-body">{{ location.connections[direction].desc }}</div> | ||||
| @@ -1,4 +1,4 @@ | |||||
| import { TextLike } from './language' | |||||
| import { TextLike, Verb, Noun, ProperNoun } from './language' | |||||
| import { Entity } from './entity' | import { Entity } from './entity' | ||||
| import { Creature } from './creature' | import { Creature } from './creature' | ||||
| import moment, { Moment, Duration } from 'moment' | import moment, { Moment, Duration } from 'moment' | ||||
| @@ -55,9 +55,12 @@ export class Connection { | |||||
| return true | return true | ||||
| } | } | ||||
| travel (world: World, traveler: Creature) { | |||||
| travel (world: World, traveler: Creature): LogEntry { | |||||
| world.advance(moment.duration(5, "minutes")) | world.advance(moment.duration(5, "minutes")) | ||||
| traveler.location = this.dst | traveler.location = this.dst | ||||
| return new LogLine( | |||||
| `${traveler.name.capital} ${traveler.name.conjugate(new Verb('travel'))} to ${this.dst.name}.` | |||||
| ) | |||||
| } | } | ||||
| } | } | ||||
| @@ -65,7 +68,7 @@ export class Place { | |||||
| connections: {[key in Direction]?: Connection} = {} | connections: {[key in Direction]?: Connection} = {} | ||||
| choices: Choice[] = [] | choices: Choice[] = [] | ||||
| constructor (public name: TextLike, public desc: TextLike) { | |||||
| constructor (public name: Noun, public desc: TextLike) { | |||||
| } | } | ||||
| @@ -80,7 +83,7 @@ export class Place { | |||||
| } | } | ||||
| export const Nowhere = new Place( | export const Nowhere = new Place( | ||||
| "Nowhere", | |||||
| new ProperNoun("Nowhere"), | |||||
| "This isn't anywhere!" | "This isn't anywhere!" | ||||
| ) | ) | ||||