Просмотр исходного кода

Add action descrioptions

master
Fen Dweller 5 лет назад
Родитель
Сommit
fb7d10b39d
7 измененных файлов: 174 добавлений и 46 удалений
  1. +7
    -2
      src/components/ActionButton.vue
  2. +25
    -5
      src/components/Combat.vue
  3. +88
    -24
      src/game/combat.ts
  4. +15
    -9
      src/game/creatures/cafat.ts
  5. +2
    -2
      src/game/creatures/player.ts
  6. +3
    -3
      src/game/creatures/wolf.ts
  7. +34
    -1
      src/game/interface.ts

+ 7
- 2
src/components/ActionButton.vue Просмотреть файл

@@ -1,5 +1,5 @@
<template>
<button class="action-button" @click="execute">
<button @mouseover="describe" class="action-button" @click="execute">
<div class="action-title">{{ action.name }}</div>
<div class="action-desc">{{ action.desc }}</div>
</button>
@@ -26,6 +26,11 @@ export default class ActionButton extends Vue {
execute () {
this.$emit('executed', this.action.execute(this.user, this.target))
}

@Emit("describe")
describe () {
this.$emit('described', this.action.describe(this.user, this.target))
}
}

</script>
@@ -35,7 +40,7 @@ export default class ActionButton extends Vue {
.action-button {
width: 100pt;
background: #333;
}
}q

.action-button:hover {
background: #555;


+ 25
- 5
src/components/Combat.vue Просмотреть файл

@@ -17,20 +17,22 @@
<div class="left-actions">
<div class="vert-display">
<h2>Moves</h2>
<ActionButton @executed="executedLeft" v-for="action in left.validActions(right)" :key="'left' + action.name" :action="action" :user="left" :target="right" />
<ActionButton @described="described" @executed="executedLeft" v-for="action in left.validActions(right)" :key="'left' + action.name" :action="action" :user="left" :target="right" />
<h2>Self-moves</h2>
<ActionButton @executed="executedLeft" v-for="action in left.validActions(left)" :key="'left' + action.name" :action="action" :user="left" :target="right" />
<ActionButton @described="described" @executed="executedLeft" v-for="action in left.validActions(left)" :key="'left' + action.name" :action="action" :user="left" :target="right" />
</div>
<div>{{actionDescription}}</div>
</div>
<div class="right-actions">
<div class="vert-display">
<h2>Moves</h2>
<ActionButton @executed="executedRight" v-for="action in right.validActions(left)" :key="'right' + action.name" :action="action" :user="right" :target="left" />
<ActionButton @described="described" @executed="executedRight" v-for="action in right.validActions(left)" :key="'right' + action.name" :action="action" :user="right" :target="left" />
<h2>Self-moves</h2>
<ActionButton @executed="executedRight" v-for="action in right.validActions(right)" :key="'right' + action.name" :action="action" :user="right" :target="left" />
<ActionButton @described="described" @executed="executedRight" v-for="action in right.validActions(right)" :key="'right' + action.name" :action="action" :user="right" :target="left" />
</div>
</div>
<div id="action-desc">
</div>
</div>
</template>

@@ -91,6 +93,20 @@ export default class Combat extends Vue {
log.scrollTo({ top: 10000000000, left: 0 })
}
}

@Emit("described")
described (entry: LogEntry) {
const actionDesc = document.querySelector("#action-desc")

if (actionDesc !== null) {
const holder = document.createElement("div")
entry.render().forEach(element => {
holder.appendChild(element)
})
actionDesc.innerHTML = ''
actionDesc.appendChild(holder)
}
}
}
</script>

@@ -115,7 +131,7 @@ export default class Combat extends Vue {

#log {
grid-area: main-row-start / main-col-start / main-row-end / main-col-end;
overflow-y: auto;
overflow-y: scroll;
font-size: 16pt;
width: 100%;
max-height: 100%;
@@ -138,6 +154,10 @@ export default class Combat extends Vue {
grid-area: 4 / 3 / 6 / 4;
}

#action-desc {
grid-area: main-row-end / main-col-start / 6 / main-col-end
}

h3 {
margin: 40px 0 0;
}


+ 88
- 24
src/game/combat.ts Просмотреть файл

@@ -25,15 +25,15 @@ export enum Vigor {
}

export const VigorIcons: {[key in Vigor]: string} = {
[Vigor.Health]: "fas fa-heart",
[Vigor.Stamina]: "fas fa-bolt",
[Vigor.Resolve]: "fas fa-brain"
Health: "fas fa-heart",
Stamina: "fas fa-bolt",
Resolve: "fas fa-brain"
}

export const VigorDescs: {[key in Vigor]: string} = {
[Vigor.Health]: "How much damage you can take",
[Vigor.Stamina]: "How much energy you have",
[Vigor.Resolve]: "How much dominance you can resist"
Health: "How much damage you can take",
Stamina: "How much energy you have",
Resolve: "How much dominance you can resist"
}

export type Vigors = {[key in Vigor]: number}
@@ -49,19 +49,19 @@ export enum Stat {
export type Stats = {[key in Stat]: number}

export const StatIcons: {[key in Stat]: string} = {
[Stat.Toughness]: 'fas fa-heartbeat',
[Stat.Power]: 'fas fa-fist-raised',
[Stat.Speed]: 'fas fa-feather',
[Stat.Willpower]: 'fas fa-book',
[Stat.Charm]: 'fas fa-comments'
Toughness: 'fas fa-heartbeat',
Power: 'fas fa-fist-raised',
Speed: 'fas fa-feather',
Willpower: 'fas fa-book',
Charm: 'fas fa-comments'
}

export const StatDescs: {[key in Stat]: string} = {
[Stat.Toughness]: 'Your physical resistance',
[Stat.Power]: 'Your physical power',
[Stat.Speed]: 'How quickly you can act',
[Stat.Willpower]: 'Your mental resistance',
[Stat.Charm]: 'Your mental power'
Toughness: 'Your physical resistance',
Power: 'Your physical power',
Speed: 'How quickly you can act',
Willpower: 'Your mental resistance',
Charm: 'Your mental power'
}

export interface CombatTest {
@@ -129,7 +129,7 @@ export class StatVigorTest extends RandomTest {
result = 'You have ' + delta + ' more ' + this.stat + ' than you foe.'
}

result += ' Your odds of success are ' + (100 * this.odds(user, target)) + '%'
result += ' Your odds of success are ' + (100 * this.odds(user, target)).toFixed(1) + '%'

return new LogLines(result)
}
@@ -159,7 +159,7 @@ export class StatTest extends RandomTest {
result = 'You have ' + delta + ' more ' + this.stat + ' than you foe.'
}

result += ' Your odds of success are ' + (100 * this.odds(user, target)) + '%'
result += ' Your odds of success are ' + (100 * this.odds(user, target)).toFixed(1) + '%'

return new LogLines(result)
}
@@ -221,6 +221,39 @@ export class Damage {
}
}

export interface DamageFormula {
calc (user: Creature, target: Creature): Damage;
describe (user: Creature, target: Creature): LogEntry;
}

export class ConstantDamageFormula implements DamageFormula {
calc (user: Creature, target: Creature): Damage {
return this.damage
}

constructor (private damage: Damage) {

}

describe (user: Creature, target: Creature): LogEntry {
return new LogLine('Deal ', this.damage.renderShort(), ' damage')
}
}

export class UniformRandomDamageFormula implements DamageFormula {
calc (user: Creature, target: Creature): Damage {
return this.damage.scale(Math.random() * this.variance * 2 - this.variance + 1)
}

constructor (private damage: Damage, private variance: number) {

}

describe (user: Creature, target: Creature): LogEntry {
return new LogLine('Deal between ', this.damage.scale(1 - this.variance).renderShort(), ' and ', this.damage.scale(1 + this.variance).renderShort(), ' damage.')
}
}

export interface Combatant {
actions: Array<Action>;
}
@@ -232,7 +265,9 @@ export abstract class Action {

abstract execute(user: Creature, target: Creature): LogEntry

constructor (public name: TextLike, public desc: string, private conditions: Array<Condition> = []) {
abstract describe (user: Creature, target: Creature): LogEntry

constructor (public name: TextLike, public desc: TextLike, private conditions: Array<Condition> = []) {

}

@@ -328,20 +363,25 @@ export class AttackAction extends TogetherAction {
[[POV.Third, POV.Third], (user, target) => new LogLines(`${user.name.capital} misses ${target.name}`)]
])

constructor (protected damage: Damage) {
constructor (protected damage: DamageFormula) {
super('Attack', 'Attack the enemy', [new CapableCondition()])
this.test = new StatTest(Stat.Power)
}

execute (user: Creature, target: Creature): LogEntry {
if (this.test.test(user, target)) {
const targetResult = target.takeDamage(this.damage)
const ownResult = this.successLines.run(user, target, { damage: this.damage })
const damage = this.damage.calc(user, target)
const targetResult = target.takeDamage(damage)
const ownResult = this.successLines.run(user, target, { damage: damage })
return new CompositeLog(ownResult, targetResult)
} else {
return this.failLines.run(user, target)
}
}

describe (user: Creature, target: Creature): LogEntry {
return new LogLine(`Attack ${target.name}. `, this.damage.describe(user, target), '. ', this.test.explain(user, target))
}
}

export class DevourAction extends TogetherAction {
@@ -377,6 +417,10 @@ export class DevourAction extends TogetherAction {
return this.failLines.run(user, target)
}
}

describe (user: Creature, target: Creature): LogEntry {
return new LogLine(`Try to consume your opponent, sending them to your ${this.container.name}. `, this.test.explain(user, target))
}
}

export class FeedAction extends TogetherAction {
@@ -409,6 +453,10 @@ export class FeedAction extends TogetherAction {
execute (user: Creature, target: Creature): LogEntry {
return this.container.consume(user)
}

describe (user: Creature, target: Creature): LogEntry {
return new LogLine(`Your willpower is drained, and you really feel like shoving yourself into ${target.name}'s ${this.container.name}...`)
}
}

export class StruggleAction extends PairAction {
@@ -444,6 +492,10 @@ export class StruggleAction extends PairAction {
return new LogLines("Vore's bugged!")
}
}

describe (user: Creature, target: Creature): LogEntry {
return new LogLine(`Try to escape from ${target.name}'s ${this.container.name}. `, this.test.explain(user, target))
}
}

export abstract class EatenAction extends PairAction {
@@ -458,7 +510,7 @@ export abstract class EatenAction extends PairAction {
}

constructor (public container: Container, name: TextLike, desc: string) {
super(new DynText(name, ' (', new LiveText(container, x => x.name.all), ')'), 'Do something to your prey!', [new CapableCondition()])
super(new DynText(name, ' (', new LiveText(container, x => x.name.all), ')'), desc, [new CapableCondition()])
}
}
export class DigestAction extends SelfAction {
@@ -480,6 +532,10 @@ export class DigestAction extends SelfAction {
const results = this.container.tick(60)
return new CompositeLog(results)
}

describe (user: Creature, target: Creature): LogEntry {
return new LogLine(`Digest everyone inside of your ${this.container.name}.`)
}
}

export class ReleaseAction extends PairAction {
@@ -498,6 +554,10 @@ export class ReleaseAction extends PairAction {
execute (user: Creature, target: Creature): LogEntry {
return this.container.release(target)
}

describe (user: Creature, target: Creature): LogEntry {
return new LogLine(`Release ${target.name} from your ${this.container.name}.`)
}
}

export class TransferAction extends PairAction {
@@ -516,7 +576,7 @@ export class TransferAction extends PairAction {
}

constructor (protected from: Container, protected to: Container) {
super('Transfer', `Shove your prey from your ${from.name} to your ${to.name}`, [new CapableCondition()])
super('Transfer', `Move from your ${from.name} to your ${to.name}`, [new CapableCondition()])
}

execute (user: Creature, target: Creature): LogEntry {
@@ -524,4 +584,8 @@ export class TransferAction extends PairAction {
this.to.consume(target)
return this.lines.run(user, target, { from: this.from, to: this.to })
}

describe (user: Creature, target: Creature): LogEntry {
return new LogLine(`Push ${target.name} from your ${this.from.name} to your ${this.to.name}`)
}
}

+ 15
- 9
src/game/creatures/cafat.ts Просмотреть файл

@@ -1,5 +1,5 @@
import { Creature, POV, Entity } from '../entity'
import { Stat, Damage, DamageType, TransferAction, Vigor, StatTest, FeedAction, DigestAction, EatenAction, AttackAction } from '../combat'
import { Stat, Damage, DamageType, TransferAction, Vigor, StatTest, FeedAction, DigestAction, EatenAction, AttackAction, DamageFormula, ConstantDamageFormula } from '../combat'
import { ProperNoun, TheyPronouns, ImproperNoun, POVPair, FemalePronouns, POVPairArgs } from '../language'
import { VoreType, Stomach, InnerStomach, Container, Bowels } from '../vore'
import { LogLine, LogLines, LogEntry, FAElem, CompositeLog, ImgElem } from '../interface'
@@ -21,15 +21,17 @@ class BellyCrushAction extends AttackAction {
), new ImgElem('./media/cafat/images/belly-crush.webp'))]
])

constructor (private _damage: Damage) {
super(_damage)
constructor (_damage: Damage) {
super({
calc (user, target) { return _damage.scale(user.bulk / 25) },
describe (user, target) { return new LogLine('Deal ', _damage.scale(user.bulk / 25).renderShort(), ` with your ${user.bulk} `, new FAElem('fas fa-weight-hanging')) }
})
this.name = 'Belly Crush'
this.desc = 'Deal damage proportional to your bulk'
this.desc = 'Use your weight!'
}

execute (user: Creature, target: Creature): LogEntry {
this.damage = this._damage.scale(user.bulk / 25 + 1)
return super.execute(user, target)
describe (user: Creature, target: Creature): LogEntry {
return new LogLine(`Crush ${target.name} under your gut. `, this.damage.describe(user, target))
}
}

@@ -50,7 +52,7 @@ class BelchAction extends AttackAction {
])

constructor (damage: Damage) {
super(damage)
super(new ConstantDamageFormula(damage))
this.name = 'Belch'
this.desc = 'Drain your foe\'s willpower with a solid BELCH'
}
@@ -75,6 +77,10 @@ class CrushAction extends EatenAction {
target.takeDamage(this.damage)
return this.lines.run(user, target)
}

describe (user: Creature, target: Creature): LogEntry {
return new LogLine(`Crush ${target.name} in your ${this.container.name} for massive, unavoidable damage.`)
}
}

export class Cafat extends Creature {
@@ -132,7 +138,7 @@ export class Cafat extends Creature {
this.actions.push(transfer)
this.actions.push(new TransferAction(lowerStomach, stomach))

this.actions.push(new AttackAction(new Damage({ amount: 40, type: DamageType.Crush, target: Vigor.Health })))
this.actions.push(new AttackAction(new ConstantDamageFormula(new Damage({ amount: 40, type: DamageType.Crush, target: Vigor.Health }))))
this.actions.push(new BellyCrushAction(new Damage({ amount: 10, type: DamageType.Crush, target: Vigor.Health }, { amount: 10, type: DamageType.Dominance, target: Vigor.Resolve })))
this.actions.push(new BelchAction(new Damage(
{ amount: 100, target: Vigor.Resolve, type: DamageType.Acid }


+ 2
- 2
src/game/creatures/player.ts Просмотреть файл

@@ -1,13 +1,13 @@
import { Creature, POV } from '../entity'
import { ProperNoun, TheyPronouns } from '../language'
import { Stat, Damage, AttackAction, DamageType, Vigor } from '../combat'
import { Stat, Damage, AttackAction, DamageType, Vigor, ConstantDamageFormula } from '../combat'
import { Stomach, Bowels, VoreType } from '../vore'

export class Player extends Creature {
constructor () {
super(new ProperNoun('The Dude'), TheyPronouns, { Toughness: 20, Power: 20, Speed: 20, Willpower: 20, Charm: 20 }, new Set([VoreType.Oral, VoreType.Anal]), new Set([VoreType.Oral, VoreType.Anal]), 50)

this.actions.push(new AttackAction(new Damage({ type: DamageType.Pierce, amount: 20, target: Vigor.Health }, { type: DamageType.Pierce, amount: 20, target: Vigor.Stamina })))
this.actions.push(new AttackAction(new ConstantDamageFormula(new Damage({ type: DamageType.Pierce, amount: 20, target: Vigor.Health }, { type: DamageType.Pierce, amount: 20, target: Vigor.Stamina }))))

const stomach = new Stomach(this, 100, new Damage({ amount: 20, type: DamageType.Acid, target: Vigor.Health }, { amount: 10, type: DamageType.Crush, target: Vigor.Health }))



+ 3
- 3
src/game/creatures/wolf.ts Просмотреть файл

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

class BiteAction extends AttackAction {
constructor () {
super(new Damage({ amount: 10, type: DamageType.Slash, target: Vigor.Health }))
super(new ConstantDamageFormula(new Damage({ amount: 10, type: DamageType.Slash, target: Vigor.Health })))
this.name = "Bite"
}
}
@@ -34,7 +34,7 @@ class HypnoAction extends AttackAction {
])

constructor () {
super(new Damage({ amount: 30, type: DamageType.Dominance, target: Vigor.Resolve }))
super(new ConstantDamageFormula(new Damage({ amount: 30, type: DamageType.Dominance, target: Vigor.Resolve })))
this.test = new StatTest(Stat.Willpower)
this.name = "Hypnotize"
}


+ 34
- 1
src/game/interface.ts Просмотреть файл

@@ -1,3 +1,5 @@
import { Stat, Vigor } from './combat'

export interface LogEntry {
render: () => HTMLElement[];
}
@@ -62,7 +64,7 @@ export class LogLine implements LogEntry {
}

render (): HTMLElement[] {
const div = document.createElement("div")
const div = document.createElement("span")

this.parts.forEach(part => {
if (typeof part === "string") {
@@ -92,6 +94,37 @@ export class FAElem implements LogEntry {
}
}

export class PropElem implements LogEntry {
constructor (private value: number, private prop: Stat | Vigor) {

}

render (): HTMLElement[] {
let cls: string
switch (this.prop) {
case Vigor.Health: cls = "fas fa-heart"; break
case Vigor.Stamina: cls = "fas fa-bolt"; break
case Vigor.Resolve: cls = "fas fa-brain"; break
case Stat.Toughness: cls = "fas fa-heartbeat"; break
case Stat.Power: cls = "fas fa-fist-raised"; break
case Stat.Speed: cls = "fas fa-weather"; break
case Stat.Willpower: cls = "fas fa-book"; break
case Stat.Charm: cls = "fas fa-comments"; break
}

const span = document.createElement("span")
span.classList.add("stat-entry")
span.textContent = this.value.toString()
new FAElem(cls).render().forEach(elem => {
span.appendChild(elem)
})
span.dataset.tooltip = this.prop
span.dataset["tooltip-full"] = this.prop

return [span]
}
}

export class ImgElem implements LogEntry {
constructor (private url: string) {



Загрузка…
Отмена
Сохранить