Browse Source

Get a decent boss-fight layout going (with Withers!)

vintage
Fen Dweller 5 years ago
parent
commit
9a4f92bc27
8 changed files with 153 additions and 48 deletions
  1. +33
    -10
      src/App.vue
  2. +17
    -20
      src/components/Combat.vue
  3. +24
    -11
      src/components/Statblock.vue
  4. +7
    -3
      src/game/combat.ts
  5. +3
    -1
      src/game/creatures.ts
  6. +26
    -0
      src/game/creatures/human.ts
  7. +39
    -0
      src/game/creatures/withers.ts
  8. +4
    -3
      src/game/entity.ts

+ 33
- 10
src/App.vue View File

@@ -11,7 +11,7 @@ import Combat from './components/Combat.vue'
import Header from './components/Header.vue'
import * as Creatures from '@/game/creatures'
import { Creature, POV } from '@/game/entity'
import { ProperNoun } from '@/game/language'
import { ProperNoun, TheyPronouns, FemalePronouns, MalePronouns } from '@/game/language'

@Component({
components: {
@@ -24,15 +24,38 @@ export default class App extends Vue {
combatants: Array<Creature>
constructor () {
super()
this.left = new Creatures.Wolf()
this.left.name = new ProperNoun("Wolf")
this.right = new Creatures.Cafat()
const wolf1 = new Creatures.Wolf()
wolf1.name = new ProperNoun("Innermost Wolf")
const wolf2 = new Creatures.Wolf()
wolf2.name = new ProperNoun("Inner Wolf")
this.combatants = [this.left, this.right, wolf1, wolf2]
this.left.perspective = POV.First

const fighter = new Creatures.Human(new ProperNoun('Fighter'), TheyPronouns, {
stats: {
Toughness: 40,
Power: 50,
Speed: 30,
Willpower: 40,
Charm: 20
}
})
const rogue = new Creatures.Human(new ProperNoun('Wizard'), MalePronouns, {
stats: {
Toughness: 25,
Power: 40,
Speed: 70,
Willpower: 50,
Charm: 80
}
})
const wizard = new Creatures.Human(new ProperNoun('Rogue'), FemalePronouns, {
stats: {
Toughness: 30,
Power: 20,
Speed: 50,
Willpower: 80,
Charm: 60
}
})

this.left = fighter
this.right = new Creatures.Withers()
this.combatants = [this.left, this.right, wizard, rogue]
console.log(this.left)
console.log(this.right)
}


+ 17
- 20
src/components/Combat.vue View File

@@ -1,17 +1,11 @@
<template>
<div class="combat-layout">
<div class="left-selector">
<button class="combatant-picker" @click="left = combatant" v-for="(combatant, index) in combatants.filter(c => c !== right && c !== left)" :key="'left' + index">
{{ combatant.name }}
</button>
<div id="left-stats">
<Statblock v-on:click.native="left = combatant" class="left-stats" :data-active="combatant === left" v-for="combatant in combatants.filter(c => c.side == Side.Heroes && c.vigors.Health > 0)" v-bind:key="combatant.name" :subject="combatant" />
</div>
<div class="right-selector">
<button class="combatant-picker" @click="right = combatant" v-for="(combatant, index) in combatants.filter(c => c !== left && c !== right)" :key="'right' + index">
{{ combatant.name }}
</button>
<div id="right-stats">
<Statblock v-on:click.native="right = combatant" class="right-stats" :data-active="combatant === right" v-for="combatant in combatants.filter(c => c.side == Side.Monsters && c.vigors.Health > 0)" v-bind:key="combatant.name" :subject="combatant" />
</div>
<Statblock class="left-stats" :subject="left" />
<Statblock class="right-stats" :subject="right" />
<div id="log">
</div>
<div class="left-actions">
@@ -42,6 +36,7 @@ import { Creature, POV } from '@/game/entity'
import { LogEntry } from '@/game/interface'
import Statblock from './Statblock.vue'
import ActionButton from './ActionButton.vue'
import { Side } from '@/game/combat'

@Component(
{
@@ -58,6 +53,8 @@ export default class Combat extends Vue {
@Prop()
combatants!: Array<Creature>

Side = Side

actionDescription = ''

constructor () {
@@ -121,14 +118,6 @@ export default class Combat extends Vue {
flex: 10;
}

.left-stats {
grid-area: 2 / 1 / 3 / 2
}

.right-stats {
grid-area: 2 / 3 / 3 / 4;
}

#log {
grid-area: main-row-start / main-col-start / main-row-end / main-col-end;
overflow-y: scroll;
@@ -146,12 +135,20 @@ export default class Combat extends Vue {
grid-area: 1 / 3 / 2 / 4;
}

#left-stats {
grid-area: 2 / 1 / 4 / 2
}

#right-stats {
grid-area: 2 / 3 / 4 / 4;
}

.left-actions {
grid-area: 4 / 1 / 6 / 2;
grid-area: 5 / 1 / 6 / 2;
}

.right-actions {
grid-area: 4 / 3 / 6 / 4;
grid-area: 5 / 3 / 6 / 4;
}

#action-desc {


+ 24
- 11
src/components/Statblock.vue View File

@@ -2,7 +2,7 @@
<div class="statblock">
<h2 v-if="subject.perspective === firstperson">You</h2>
<h2 v-if="subject.perspective !== firstperson">{{subject.name.all.capital}}</h2>
<div class="stat-line">
<div class="stat-line vigors">
<div :class="vigorClass(subject.vigors[vigor], subject.maxVigors[vigor])" v-for="vigor in Object.keys(subject.vigors)" v-bind:key="vigor">
<i :class="vigorIcons[vigor]" />
<div>{{subject.vigors[vigor]}}</div>
@@ -12,8 +12,7 @@
</div>
</div>
</div>
<br>
<div class="stat-line">
<div class="stat-line stats">
<div :class="statClass(subject.stats[stat], subject.baseStats[stat])" v-for="stat in Object.keys(subject.stats)" v-bind:key="stat">
<i :class="statIcons[stat]" />
<div>{{subject.stats[stat]}}</div>
@@ -23,8 +22,7 @@
</div>
</div>
</div>
<br>
<div class="stat-line">
<div class="stat-line vore-stats">
<div class="stat-entry" v-for="stat in Object.keys(subject.voreStats)" v-bind:key="stat">
<i :class="voreStatIcons[stat]" />
<div>{{subject.voreStats[stat]}}</div>
@@ -34,9 +32,7 @@
</div>
</div>
</div>
<br>
<div>Status: {{subject.status}}</div>
<ContainerView v-for="container in subject.containers" :key="container.name.toString()" :container="container" />
</div>
</template>

@@ -45,7 +41,7 @@ import { Component, Prop, Vue, Watch, Emit } from 'vue-property-decorator'
import { Creature, POV } from '@/game/entity'
import { Stats, Stat, StatIcons, StatDescs, Vigor, VigorIcons, VigorDescs, VoreStatDescs, VoreStatIcons } from '@/game/combat'
import ContainerView from './ContainerView.vue'
import tippy from 'tippy.js'
import tippy, { createSingleton } from 'tippy.js'
import 'tippy.js/dist/tippy.css'

@Component({
@@ -88,13 +84,14 @@ export default class Statblock extends Vue {
firstperson: POV = POV.First

mounted () {
this.$el.querySelectorAll(".stat-entry").forEach(elem => {
const tippyInstances = Array.from(this.$el.querySelectorAll(".stat-entry")).map(elem => {
const tooltip = elem.querySelector(".tooltip-template") as HTMLElement

tippy(elem, {
return tippy(elem, {
content: tooltip
})
})
createSingleton(tippyInstances, { delay: 500 })
}
}
</script>
@@ -102,7 +99,7 @@ export default class Statblock extends Vue {
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
h2 {
margin-bottom: 16pt;
margin-bottom: 8pt;
font-size: 200%;
}
ul {
@@ -118,6 +115,7 @@ a {
}
.statblock {
margin: 16px;
user-select: none;
}

.stat-line {
@@ -127,6 +125,11 @@ a {
flex-wrap: wrap;
}

.stats,
.vore-stats {
display: none;
}

.stat-entry {
position: relative;
font-size: 16pt;
@@ -149,6 +152,16 @@ a {
.stat-entry.buff {
color: green;
}

.statblock[data-active] {
background: #444;
border-radius: 4px;
}

.statblock[data-active] .stats,
.statblock[data-active] .vore-stats {
display: flex;
}
</style>

<style>


+ 7
- 3
src/game/combat.ts View File

@@ -197,13 +197,17 @@ export class UniformRandomDamageFormula implements DamageFormula {
}
}

export enum Side {
Heroes,
Monsters
}

/**
* A Combatant has a list of possible actions to take.
*
* This may be merged with [[Actionable]]
* A Combatant has a list of possible actions to take, as well as a side.
*/
export interface Combatant {
actions: Array<Action>;
side: Side;
}

/**


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

@@ -1,5 +1,7 @@
import { Wolf } from './creatures/wolf'
import { Player } from './creatures/player'
import { Cafat } from './creatures/cafat'
import { Human } from './creatures/human'
import { Withers } from './creatures/withers'

export { Wolf, Player, Cafat }
export { Wolf, Player, Cafat, Human, Withers }

+ 26
- 0
src/game/creatures/human.ts View File

@@ -0,0 +1,26 @@
import { Creature, POV, Entity } from '../entity'
import { Stat, Damage, DamageType, ConstantDamageFormula, Vigor, Stats } from '../combat'
import { MalePronouns, ImproperNoun, POVPair, POVPairArgs, Noun, Pronoun } from '../language'
import { LogLine, LogLines } from '../interface'
import { VoreType, Stomach, Bowels } from '../vore'
import { StatTest } from '../combat/tests'
import { AttackAction, TransferAction, FeedAction } from '../combat/actions'

export class Human extends Creature {
constructor (name: Noun, pronouns: Pronoun, options: {
stats?: Stats;
} = {}) {
let stats
if (options.stats === undefined) {
stats = { Toughness: 20, Power: 20, Speed: 20, Willpower: 20, Charm: 20 }
} else {
stats = options.stats
}
super(name, MalePronouns, stats, new Set([VoreType.Oral, VoreType.Anal]), new Set([VoreType.Oral, VoreType.Anal]), 25)
this.actions.push(new AttackAction(new ConstantDamageFormula(
new Damage(
{ amount: 20, target: Vigor.Health, type: DamageType.Slash }
)
)))
}
}

+ 39
- 0
src/game/creatures/withers.ts View File

@@ -0,0 +1,39 @@
import { Creature, POV, Entity } from '../entity'
import { Stat, Damage, DamageType, ConstantDamageFormula, Vigor, Side, PairAction, CombatTest } from '../combat'
import { MalePronouns, ImproperNoun, POVPair, POVPairArgs, ProperNoun, TheyPronouns } from '../language'
import { LogLine, LogLines, LogEntry } from '../interface'
import { VoreType, Stomach, Bowels } from '../vore'
import { StatTest } from '../combat/tests'
import { AttackAction, TransferAction, FeedAction } from '../combat/actions'

class BiteAction extends AttackAction {
constructor () {
super(new ConstantDamageFormula(new Damage({ amount: 50, type: DamageType.Slash, target: Vigor.Health })))
this.name = "Bite"
}
}

export class Withers extends Creature {
constructor () {
super(
new ProperNoun('Withers'),
TheyPronouns,
{ Toughness: 60, Power: 100, Speed: 40, Willpower: 60, Charm: 120 },
new Set(),
new Set([VoreType.Oral]),
5000)

this.actions.push(new BiteAction())

this.side = Side.Monsters

const stomach = new Stomach(this, 50, new Damage(
{ amount: 30, type: DamageType.Acid, target: Vigor.Health },
{ amount: 20, type: DamageType.Crush, target: Vigor.Stamina },
{ amount: 20, type: DamageType.Dominance, target: Vigor.Resolve }
))

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

+ 4
- 3
src/game/entity.ts View File

@@ -1,4 +1,4 @@
import { DamageType, Damage, Combatant, Stats, Action, Vigor, VoreStats, VoreStat, Stat } from './combat'
import { DamageType, Damage, Combatant, Stats, Action, Vigor, VoreStats, VoreStat, Stat, Side } from './combat'
import { Noun, Pronoun } from './language'
import { LogEntry, LogLine } from './interface'
import { Vore, Container, VoreType } from './vore'
@@ -38,6 +38,7 @@ export class Creature extends Vore implements Combatant {

baseStats: Stats
voreStats: VoreStats
side: Side

get disabled (): boolean {
return Object.values(this.vigors).some(val => val <= 0)
@@ -59,7 +60,7 @@ export class Creature extends Vore implements Combatant {
super()
const containers = this.containers
this.baseStats = Object.keys(Stat).reduce((base: any, key) => { base[key] = stats[key as Stat]; return base }, {})
this.side = Side.Heroes
this.voreStats = {
get [VoreStat.Bulk] () {
console.log(containers)
@@ -137,7 +138,7 @@ export class Creature extends Vore implements Combatant {
return "Broken"
}
if (this.containedIn !== null) {
return "Devoured"
return `Devoured by ${this.containedIn.owner.name}`
}

return "Normal"


Loading…
Cancel
Save