| @@ -1,8 +1,11 @@ | |||
| <template> | |||
| <div id="app"> | |||
| <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> | |||
| </template> | |||
| @@ -30,7 +33,12 @@ import moment from 'moment' | |||
| encounter: null, | |||
| encounters: null, | |||
| world: null, | |||
| mode: 'explore' | |||
| mode: 'explore', | |||
| props: { | |||
| Explore: { | |||
| world: this | |||
| } | |||
| } | |||
| } | |||
| } | |||
| }) | |||
| @@ -42,7 +50,7 @@ export default class App extends Vue { | |||
| @Emit('startFight') | |||
| startFight (encounter: Encounter) { | |||
| this.$data.encounter = encounter | |||
| this.$data.mode = 'combat' | |||
| this.$data.mode = 'Combat' | |||
| } | |||
| created () { | |||
| @@ -62,9 +70,9 @@ export default class App extends Vue { | |||
| 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) | |||
| 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) | |||
| player.location = home | |||
| this.$data.world = new World(player) | |||
| @@ -144,6 +152,7 @@ body, html { | |||
| } | |||
| #app { | |||
| position: relative; | |||
| font-family: Avenir, Helvetica, Arial, sans-serif; | |||
| -webkit-font-smoothing: antialiased; | |||
| -moz-osx-font-smoothing: grayscale; | |||
| @@ -157,6 +166,13 @@ body, html { | |||
| flex-direction: column; | |||
| } | |||
| #main-area { | |||
| position: relative; | |||
| flex: 10; | |||
| overflow-x: hidden; | |||
| overflow-y: hidden; | |||
| } | |||
| .tippy-box { | |||
| text-align: center; | |||
| border-radius: 10px; | |||
| @@ -204,5 +220,11 @@ body, html { | |||
| *::-webkit-scrollbar-corner { | |||
| 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> | |||
| @@ -277,13 +277,12 @@ export default class Combat extends Vue { | |||
| } | |||
| .combat-layout { | |||
| position: relative; | |||
| position: absolute; | |||
| display: grid; | |||
| 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%; | |||
| width: 100%; | |||
| height: 100%; | |||
| flex: 10; | |||
| overflow-x: hidden; | |||
| overflow-y: hidden; | |||
| } | |||
| @@ -9,11 +9,11 @@ | |||
| </div> | |||
| <Statblock :subject="world.player" :initiative="0" /> | |||
| <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> | |||
| </div> | |||
| <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 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" /> | |||
| @@ -62,17 +62,17 @@ export default class Explore extends Vue { | |||
| writeLog (entry: LogEntry) { | |||
| const log = this.$el.querySelector(".explore-log") | |||
| if (log !== null) { | |||
| const before = log.querySelector("div.log-entry") | |||
| const before = log.querySelector("div.explore-log-entry") | |||
| const holder = document.createElement("div") | |||
| holder.classList.add("log-entry") | |||
| holder.classList.add("explore-log-entry") | |||
| entry.render().forEach(element => { | |||
| holder.appendChild(element) | |||
| }) | |||
| holder.classList.add("left-move") | |||
| holder.classList.add("explore-entry") | |||
| const hline = document.createElement("div") | |||
| hline.classList.add("log-separator") | |||
| hline.classList.add("explore-log-separator") | |||
| log.insertBefore(hline, before) | |||
| log.insertBefore(holder, hline) | |||
| @@ -148,7 +148,9 @@ export default class Explore extends Vue { | |||
| grid-template-rows: 1fr 1fr 1fr; | |||
| grid-template-columns: 1fr 1fr 1fr; | |||
| width: 100%; | |||
| max-width: 1000px; | |||
| height: 100%; | |||
| justify-self: end; | |||
| } | |||
| .explore-choices { | |||
| @@ -159,3 +161,27 @@ export default class Explore extends Vue { | |||
| overflow-y: scroll; | |||
| } | |||
| </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> | |||
| <button class="nav-button"> | |||
| {{ location.connections[direction].dst.name }} | |||
| {{ location.connections[direction].dst.name.capital.all }} | |||
| <div class="tooltip-template"> | |||
| <div class="tooltip-title">{{ location.connections[direction].name }}</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 { Creature } from './creature' | |||
| import moment, { Moment, Duration } from 'moment' | |||
| @@ -55,9 +55,12 @@ export class Connection { | |||
| return true | |||
| } | |||
| travel (world: World, traveler: Creature) { | |||
| travel (world: World, traveler: Creature): LogEntry { | |||
| world.advance(moment.duration(5, "minutes")) | |||
| 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} = {} | |||
| 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( | |||
| "Nowhere", | |||
| new ProperNoun("Nowhere"), | |||
| "This isn't anywhere!" | |||
| ) | |||