Browse Source

Add waves to container views and more vore types for wolves

master
Fen Dweller 5 years ago
parent
commit
593a85dc45
7 changed files with 179 additions and 45 deletions
  1. +73
    -22
      src/components/ContainerView.vue
  2. +20
    -8
      src/components/Explore.vue
  3. +1
    -1
      src/game/creatures/human.ts
  4. +19
    -4
      src/game/creatures/wolf.ts
  5. +16
    -1
      src/game/maps/town.ts
  6. +40
    -5
      src/game/vore.ts
  7. +10
    -4
      src/game/world.ts

+ 73
- 22
src/components/ContainerView.vue View File

@@ -1,8 +1,12 @@
<template> <template>
<div v-if="container.fullness > 0" class="statblock">
<h3>{{container.name.capital}}</h3>
<div>{{container.fullness}} / {{container.capacity}}</div>
<div v-for="(prey, index) in container.contents" :key="'prey-' + index">{{prey.name}}</div>
<div v-show="container.fullness > 0" class="vore-container">
<div class="container-name">{{container.name.capital}}</div>
<div class="container-fullness">{{container.fullness}} / {{container.capacity}}</div>
<p v-if="container.contents.length > 0">Live prey:</p>
<div class="container-prey" v-for="(prey, index) in container.contents" :key="'live-prey-' + index">{{prey.name}}</div>
<p v-if="container.digested.length > 0">Digested:</p>
<div class="container-prey" v-for="(prey, index) in container.digested" :key="'dead-prey-' + index">{{prey.name}}</div>
<canvas class="container-waves"></canvas>
</div> </div>
</template> </template>


@@ -11,34 +15,81 @@ import { Component, Prop, Vue, Watch, Emit } from 'vue-property-decorator'
import { Creature } from '@/game/creature' import { Creature } from '@/game/creature'
import { POV } from '@/game/language' import { POV } from '@/game/language'
import { Stats, Stat } from '@/game/combat' import { Stats, Stat } from '@/game/combat'
import { Container } from '@/game/vore'
import { Container, VoreContainer, Vore } from '@/game/vore'

// yoinked from https://jsfiddle.net/yckart/0adfw47y/

function draw (delta: number, dt: number, total: number, parent: HTMLElement, canvas: HTMLCanvasElement, container: VoreContainer) {
const ctx = canvas.getContext('2d') as CanvasRenderingContext2D

canvas.width = parent.clientWidth
canvas.height = parent.clientHeight
ctx.fillStyle = container.fluidColor
const fraction = container.fullness / container.capacity
const livingFraction = container.contents.reduce((total: number, prey: Vore) => total + prey.voreStats.Bulk, 0) / container.capacity
const deadFraction = container.digested.reduce((total: number, prey: Vore) => total + prey.voreStats.Bulk, 0) / container.capacity
const liveliness = livingFraction + deadFraction * 0.5

total += dt * liveliness

requestAnimationFrame((newDelta: number) => draw(newDelta, newDelta - delta, total, parent, canvas, container))

const randomLeft = Math.abs(Math.pow(Math.sin(total / 1000), 2)) * fraction * 100 + (1 - fraction) * canvas.height
const randomRight = Math.abs(Math.pow(Math.sin((total / 1000) + 10), 2)) * fraction * 100 + (1 - fraction) * canvas.height
const randomLeftConstraint = Math.abs(Math.pow(Math.sin((total / 1000) + 2), 2)) * fraction * 100 + (1 - fraction) * canvas.height
const randomRightConstraint = Math.abs(Math.pow(Math.sin((total / 1000) + 1), 2)) * fraction * 100 + (1 - fraction) * canvas.height

ctx.beginPath()
ctx.moveTo(0, randomLeft)

ctx.bezierCurveTo(canvas.width / 3, randomLeftConstraint, canvas.width / 3 * 2, randomRightConstraint, canvas.width, randomRight)
ctx.lineTo(canvas.width, canvas.height)
ctx.lineTo(0, canvas.height)
ctx.lineTo(0, randomLeft)

ctx.closePath()
ctx.fill()
}


@Component @Component
export default class ContainerView extends Vue { export default class ContainerView extends Vue {
@Prop({ required: true }) @Prop({ required: true })
container!: Container container!: Container


constructor () {
super()
mounted () {
if ((this.container as VoreContainer).fluidColor !== undefined) {
const canvas = this.$el.querySelector('.container-waves') as HTMLCanvasElement

canvas.width = (this.$el as HTMLElement).clientWidth
canvas.height = (this.$el as HTMLElement).clientHeight
canvas.width = canvas.width + 0
requestAnimationFrame((delta: number) => draw(delta, delta, Math.random() * 1000, this.$el as HTMLElement, canvas, (this.container as VoreContainer)))
}
} }
} }

</script> </script>


<!-- Add "scoped" attribute to limit CSS to this component only --> <!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped> <style scoped>
h3 {
margin: 40px 0 0;
font-size: 125%;
}
ul {
list-style-type: none;
padding: 0;
}
li {
display: inline-block;
margin: 0 10px;
}
a {
color: #42b983;
}
.vore-container {
position: relative;
min-width: 100pt;
}
.container-name {
margin: 8pt;
font-size: 150%;
}

.container-fullness {
margin: 6pt;
font-size: 125%;
}

.container-waves {
position: absolute;
top: 0;
left: 0;
}

</style> </style>

+ 20
- 8
src/components/Explore.vue View File

@@ -8,6 +8,9 @@
<p class="worldinfo-date">{{ world.time.format("hh:mm:ss a") }}</p> <p class="worldinfo-date">{{ world.time.format("hh:mm:ss a") }}</p>
</div> </div>
<Statblock :subject="world.player" :initiative="0" /> <Statblock :subject="world.player" :initiative="0" />
<div class="explore-containers">
<ContainerView :container="container" v-for="(container, index) in world.player.containers" :key="'explore-container-' + index" />
</div>
<div class="explore-info"> <div class="explore-info">
<h2 class="location-name">{{ location.name.capital }}</h2> <h2 class="location-name">{{ location.name.capital }}</h2>
<p class="location-desc">{{ location.desc }}</p> <p class="location-desc">{{ location.desc }}</p>
@@ -28,11 +31,12 @@ import { Direction, World, Place } from '@/game/world'
import NavButton from './NavButton.vue' import NavButton from './NavButton.vue'
import ChoiceButton from './ChoiceButton.vue' import ChoiceButton from './ChoiceButton.vue'
import Statblock from './Statblock.vue' import Statblock from './Statblock.vue'
import ContainerView from './ContainerView.vue'
import { LogEntry } from '@/game/interface' import { LogEntry } from '@/game/interface'


@Component({ @Component({
components: { components: {
NavButton, ChoiceButton, Statblock
NavButton, ChoiceButton, Statblock, ContainerView
}, },
data () { data () {
return { return {
@@ -89,18 +93,26 @@ export default class Explore extends Vue {
flex: 10; flex: 10;
position: relative; position: relative;
display: grid; display: grid;
grid-template-areas: "log worldinfo"
"log statblock"
"log info "
"log choices "
"nav choices ";
grid-template-rows: 0.5fr fit-content(250pt) 2fr 1fr 1fr;
grid-template-columns: 2fr 1fr;
grid-template-areas: "statblock containers containers"
"log log worldinfo"
"log log info "
"log log choices "
"nav nav choices ";
grid-template-rows: fit-content(30%) fit-content(250pt) 2fr 1fr 1fr;
grid-template-columns: 1fr 1fr 1fr;
width: 100%; width: 100%;
height: 100%; height: 100%;
overflow: hidden; overflow: hidden;
} }


.explore-containers {
grid-area: containers;
display: flex;
flex-direction: row;
flex-wrap: nowrap;
overflow-x: scroll;
}

.explore-log { .explore-log {
grid-area: log; grid-area: log;
background: #222; background: #222;


+ 1
- 1
src/game/creatures/human.ts View File

@@ -21,7 +21,7 @@ export class Human extends Creature {
} else { } else {
stats = options.stats stats = options.stats
} }
super(name, new ImproperNoun('human', 'humans'), pronouns, stats, new Set([VoreType.Oral, VoreType.Anal]), new Set([VoreType.Oral, VoreType.Anal]), 25)
super(name, new ImproperNoun('human', 'humans'), pronouns, stats, new Set([VoreType.Oral, VoreType.Anal, VoreType.Cock, VoreType.Unbirth]), new Set([VoreType.Oral, VoreType.Anal]), 25)


this.title = "Snack" this.title = "Snack"
this.desc = "Definitely going on an adventure" this.desc = "Definitely going on an adventure"


+ 19
- 4
src/game/creatures/wolf.ts View File

@@ -1,12 +1,13 @@
import { Creature } from "../creature" import { Creature } from "../creature"
import { Damage, DamageType, ConstantDamageFormula, Vigor, Side } from '../combat' import { Damage, DamageType, ConstantDamageFormula, Vigor, Side } from '../combat'
import { MalePronouns, ImproperNoun } from '../language'
import { VoreType, Stomach, Bowels } from '../vore'
import { MalePronouns, ImproperNoun, ProperNoun, ObjectPronouns } from '../language'
import { VoreType, Stomach, Bowels, Cock, Balls } from '../vore'
import { AttackAction, TransferAction, FeedAction } from '../combat/actions' import { AttackAction, TransferAction, FeedAction } from '../combat/actions'
import { Human } from '../creatures'


export class Wolf extends Creature { export class Wolf extends Creature {
constructor () { constructor () {
super(new ImproperNoun('wolf', 'wolves'), new ImproperNoun('wolf', 'wolves'), MalePronouns, { Toughness: 20, Power: 20, Speed: 20, Willpower: 20, Charm: 20 }, new Set([VoreType.Oral, VoreType.Anal]), new Set([VoreType.Oral, VoreType.Anal]), 25)
super(new ImproperNoun('wolf', 'wolves'), new ImproperNoun('wolf', 'wolves'), MalePronouns, { Toughness: 20, Power: 20, Speed: 20, Willpower: 20, Charm: 20 }, new Set([VoreType.Oral, VoreType.Anal, VoreType.Cock]), new Set([VoreType.Oral, VoreType.Anal, VoreType.Cock]), 25)
this.actions.push( this.actions.push(
new AttackAction( new AttackAction(
new ConstantDamageFormula( new ConstantDamageFormula(
@@ -24,7 +25,6 @@ export class Wolf extends Creature {
{ amount: 30, type: DamageType.Crush, target: Vigor.Stamina }, { amount: 30, type: DamageType.Crush, target: Vigor.Stamina },
{ amount: 30, type: DamageType.Dominance, target: Vigor.Resolve } { amount: 30, type: DamageType.Dominance, target: Vigor.Resolve }
)) ))

this.containers.push(stomach) this.containers.push(stomach)


const bowels = new Bowels(this, 50, new Damage( const bowels = new Bowels(this, 50, new Damage(
@@ -38,5 +38,20 @@ export class Wolf extends Creature {
this.actions.push(new TransferAction(bowels, stomach)) this.actions.push(new TransferAction(bowels, stomach))


this.otherActions.push(new FeedAction(stomach)) this.otherActions.push(new FeedAction(stomach))

const cock = new Cock(this, 50, new Damage(
{ amount: 30, type: DamageType.Crush, target: Vigor.Health },
{ amount: 60, type: DamageType.Crush, target: Vigor.Stamina },
{ amount: 60, type: DamageType.Dominance, target: Vigor.Resolve }
))

const balls = new Balls(this, 50, new Damage(
{ amount: 30, type: DamageType.Crush, target: Vigor.Health },
{ amount: 60, type: DamageType.Crush, target: Vigor.Stamina },
{ amount: 60, type: DamageType.Dominance, target: Vigor.Resolve }
), cock)

this.containers.push(balls)
this.containers.push(cock)
} }
} }

+ 16
- 1
src/game/maps/town.ts View File

@@ -1,10 +1,12 @@
import { Place, Choice, Direction } from '../world' import { Place, Choice, Direction } from '../world'
import { ProperNoun, ImproperNoun, MalePronouns, FemalePronouns } from '../language'
import { ProperNoun, ImproperNoun, MalePronouns, FemalePronouns, TheyPronouns } from '../language'
import { Encounter } from '../combat' import { Encounter } from '../combat'
import * as Creatures from '../creatures' import * as Creatures from '../creatures'
import * as Items from '../items' import * as Items from '../items'
import { LogLine, nilLog } from '../interface' import { LogLine, nilLog } from '../interface'
import { Creature } from '../creature' import { Creature } from '../creature'
import { DevourAction } from '../combat/actions'
import { SurrenderEffect } from '../combat/effects'


function makeParty (): Creature[] { function makeParty (): Creature[] {
const fighter = new Creatures.Human(new ProperNoun("Redgar"), MalePronouns, { const fighter = new Creatures.Human(new ProperNoun("Redgar"), MalePronouns, {
@@ -126,6 +128,19 @@ export const Town = (): Place => {
) )
] ]


home.choices.push(
new Choice(
"Eat someone",
"Slurp",
(world, executor) => {
const snack = new Creatures.Human(new ProperNoun("Snack"), TheyPronouns)
snack.applyEffect(new SurrenderEffect())
const options = executor.validActions(snack).filter(action => action instanceof DevourAction)
return options[Math.floor(options.length * Math.random())].execute(executor, snack)
}
)
)

bossEncounters.forEach(encounter => { bossEncounters.forEach(encounter => {
bosses.choices.push( bosses.choices.push(
new Choice( new Choice(


+ 40
- 5
src/game/vore.ts View File

@@ -123,9 +123,9 @@ export abstract class NormalContainer implements Container {
contents: Array<Vore> = [] contents: Array<Vore> = []
actions: Array<Action> = [] actions: Array<Action> = []


abstract consumeVerb: Verb
abstract releaseVerb: Verb
abstract struggleVerb: Verb
consumeVerb = new Verb('trap')
releaseVerb = new Verb('release', 'releases', 'releasing', 'released')
struggleVerb = new Verb('struggle', 'struggles', 'struggling', 'struggled')


constructor (name: Noun, public owner: Vore, public voreTypes: Set<VoreType>, public capacity: number) { constructor (name: Noun, public owner: Vore, public voreTypes: Set<VoreType>, public capacity: number) {
this.name = name.all this.name = name.all
@@ -214,12 +214,14 @@ export interface VoreContainer extends Container {
digested: Array<Vore>; digested: Array<Vore>;
tick: (dt: number) => LogEntry; tick: (dt: number) => LogEntry;
digest: (preys: Vore[]) => LogEntry; digest: (preys: Vore[]) => LogEntry;
fluidColor: string;
} }


export abstract class NormalVoreContainer extends NormalContainer implements VoreContainer { export abstract class NormalVoreContainer extends NormalContainer implements VoreContainer {
consumeVerb = new Verb('devour') consumeVerb = new Verb('devour')
releaseVerb = new Verb('release', 'releases', 'releasing', 'released') releaseVerb = new Verb('release', 'releases', 'releasing', 'released')
struggleVerb = new Verb('struggle', 'struggles', 'struggling', 'struggled') struggleVerb = new Verb('struggle', 'struggles', 'struggling', 'struggled')
fluidColor = "#00ff0088"


digested: Array<Vore> = [] digested: Array<Vore> = []


@@ -231,6 +233,10 @@ export abstract class NormalVoreContainer extends NormalContainer implements Vor
this.actions.push(new DigestAction(this)) this.actions.push(new DigestAction(this))
} }


get fullness (): number {
return Array.from(this.contents.concat(this.digested).values()).reduce((total: number, prey: Vore) => total + prey.voreStats.Bulk, 0)
}

consumeLine: PairLineArgs<Vore, { container: Container }> = (user, target, args) => { consumeLine: PairLineArgs<Vore, { container: Container }> = (user, target, args) => {
return new LogLine(`${user.name.capital} ${user.name.conjugate(this.consumeVerb)} ${target.name.objective}, forcing ${target.pronouns.objective} into ${user.pronouns.possessive} ${args.container.name}.`) return new LogLine(`${user.name.capital} ${user.name.conjugate(this.consumeVerb)} ${target.name.objective}, forcing ${target.pronouns.objective} into ${user.pronouns.possessive} ${args.container.name}.`)
} }
@@ -240,7 +246,7 @@ export abstract class NormalVoreContainer extends NormalContainer implements Vor
} }


digestLine: PairLineArgs<Vore, { container: VoreContainer }> = (user, target, args) => { digestLine: PairLineArgs<Vore, { container: VoreContainer }> = (user, target, args) => {
return new LogLine(`${user.name.capital.possessive} ${args.container.name} finishes ${Words.Digests.present} ${target.name.objective} down, ${target.pronouns.possessive} ${Words.Struggles.singular} fading away.`)
return new LogLine(`${user.name.capital.possessive} ${args.container.name} ${args.container.name.conjugate(new Verb('finish', 'finishes'))} ${Words.Digests.present} ${target.name.objective} down, ${target.pronouns.possessive} ${Words.Struggles.singular} fading away.`)
} }


tick (dt: number): LogEntry { tick (dt: number): LogEntry {
@@ -328,6 +334,35 @@ export class InnerStomach extends InnerVoreContainer {


export class Bowels extends NormalVoreContainer { export class Bowels extends NormalVoreContainer {
constructor (owner: Vore, capacity: number, damage: Damage) { constructor (owner: Vore, capacity: number, damage: Damage) {
super(new ImproperNoun('bowel', 'bowels').plural, owner, new Set([VoreType.Anal]), capacity, damage)
super(new ImproperNoun('bowel', 'bowels').plural.all, owner, new Set([VoreType.Anal]), capacity, damage)
}
}

export class Cock extends NormalVoreContainer {
fluidColor = "#eeeeee66";

constructor (owner: Vore, capacity: number, damage: Damage) {
super(
new ImproperNoun('cock').all,
owner,
new Set([VoreType.Cock]),
capacity,
damage
)
}
}

export class Balls extends InnerVoreContainer {
fluidColor = "#eeeeeecc";

constructor (owner: Vore, capacity: number, damage: Damage, escape: Container) {
super(
new ImproperNoun('ball', 'balls').all.plural,
owner,
new Set([VoreType.Cock]),
capacity,
damage,
escape
)
} }
} }

+ 10
- 4
src/game/world.ts View File

@@ -2,7 +2,7 @@ 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'
import { LogEntry, LogLine } from './interface'
import { LogEntry, LogLine, LogLines } from './interface'
import { Encounter } from './combat' import { Encounter } from './combat'


export enum Direction { export enum Direction {
@@ -57,9 +57,10 @@ export class Connection {
} }


travel (world: World, traveler: Creature): LogEntry { travel (world: World, traveler: Creature): LogEntry {
world.advance(moment.duration(5, "minutes"))
const advanceLogs = world.advance(moment.duration(5, "minutes"))
traveler.location = this.dst traveler.location = this.dst
return new LogLine(
return new LogLines(
advanceLogs,
`${traveler.name.capital} ${traveler.name.conjugate(new Verb('travel'))} to ${this.dst.name}.` `${traveler.name.capital} ${traveler.name.conjugate(new Verb('travel'))} to ${this.dst.name}.`
) )
} }
@@ -98,7 +99,12 @@ export class World {
this.creatures.push(player) this.creatures.push(player)
} }


advance (dt: Duration) {
advance (dt: Duration): LogEntry {
this.time.add(dt) this.time.add(dt)
return new LogLines(
...this.player.containers.map(
container => container.tick(dt.asSeconds())
)
)
} }
} }

Loading…
Cancel
Save