diff --git a/src/components/ActionButton.vue b/src/components/ActionButton.vue
index 541e2a2..bd1eeac 100644
--- a/src/components/ActionButton.vue
+++ b/src/components/ActionButton.vue
@@ -1,5 +1,5 @@
-
@@ -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)
+ }
+ }
}
@@ -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;
}
diff --git a/src/game/combat.ts b/src/game/combat.ts
index 743f7ec..5462881 100644
--- a/src/game/combat.ts
+++ b/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;
}
@@ -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 = []) {
+ abstract describe (user: Creature, target: Creature): LogEntry
+
+ constructor (public name: TextLike, public desc: TextLike, private conditions: Array = []) {
}
@@ -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}`)
+ }
}
diff --git a/src/game/creatures/cafat.ts b/src/game/creatures/cafat.ts
index b72a6c5..a083f28 100644
--- a/src/game/creatures/cafat.ts
+++ b/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 }
diff --git a/src/game/creatures/player.ts b/src/game/creatures/player.ts
index da25d80..ef95e79 100644
--- a/src/game/creatures/player.ts
+++ b/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 }))
diff --git a/src/game/creatures/wolf.ts b/src/game/creatures/wolf.ts
index b9fae97..54a10ae 100644
--- a/src/game/creatures/wolf.ts
+++ b/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"
}
diff --git a/src/game/interface.ts b/src/game/interface.ts
index ceee324..86a083c 100644
--- a/src/game/interface.ts
+++ b/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) {