| @@ -10,6 +10,7 @@ import { Component, Vue, Prop } from 'vue-property-decorator' | |||
| import Combat from './components/Combat.vue' | |||
| import Header from './components/Header.vue' | |||
| import * as Creatures from '@/game/creatures' | |||
| import * as Items from '@/game/items' | |||
| import { Creature, POV } from '@/game/entity' | |||
| import { ProperNoun, TheyPronouns, FemalePronouns, MalePronouns, ImproperNoun } from '@/game/language' | |||
| @@ -35,6 +36,7 @@ export default class App extends Vue { | |||
| } | |||
| }) | |||
| fighter.title = "Lv. 6 Fighter" | |||
| fighter.items.push(Items.Sword) | |||
| const rogue = new Creatures.Human(new ProperNoun('Lidda'), FemalePronouns, { | |||
| stats: { | |||
| Toughness: 25, | |||
| @@ -45,6 +47,7 @@ export default class App extends Vue { | |||
| } | |||
| }) | |||
| rogue.title = "Lv. 5 Rogue" | |||
| rogue.items.push(Items.Dagger) | |||
| const wizard = new Creatures.Human(new ProperNoun('Mialee'), FemalePronouns, { | |||
| stats: { | |||
| Toughness: 30, | |||
| @@ -55,6 +58,7 @@ export default class App extends Vue { | |||
| } | |||
| }) | |||
| wizard.title = "Lv. 6 Wizard" | |||
| wizard.items.push(Items.Wand) | |||
| const cleric = new Creatures.Human(new ProperNoun('Jozan'), MalePronouns, { | |||
| stats: { | |||
| Toughness: 35, | |||
| @@ -64,7 +68,8 @@ export default class App extends Vue { | |||
| Charm: 50 | |||
| } | |||
| }) | |||
| cleric.title = "Lv. 5 Wizard" | |||
| cleric.title = "Lv. 5 Cleric" | |||
| cleric.items.push(Items.Mace) | |||
| this.left = fighter | |||
| this.right = new Creatures.Withers() | |||
| @@ -121,6 +121,11 @@ export class Damage { | |||
| return new Damage(...results) | |||
| } | |||
| // TODO make this combine damage instances when appropriate | |||
| combine (other: Damage): Damage { | |||
| return new Damage(...this.damages.concat(other.damages)) | |||
| } | |||
| toString (): string { | |||
| return this.damages.map(damage => damage.amount + " " + damage.type).join("/") | |||
| } | |||
| @@ -206,6 +211,44 @@ export class UniformRandomDamageFormula implements DamageFormula { | |||
| } | |||
| } | |||
| export class StatDamageFormula implements DamageFormula { | |||
| calc (user: Creature, target: Creature): Damage { | |||
| const instances: Array<DamageInstance> = this.factors.map(factor => ( | |||
| { | |||
| amount: factor.fraction * user.stats[factor.stat], | |||
| target: factor.target, | |||
| type: factor.type | |||
| } | |||
| )) | |||
| return new Damage(...instances) | |||
| } | |||
| describe (user: Creature, target: Creature): LogEntry { | |||
| return new LogLine( | |||
| this.explain(user), | |||
| `, for a total of `, | |||
| this.calc(user, target).renderShort() | |||
| ) | |||
| } | |||
| explain (user: Creature): LogEntry { | |||
| return new LogLine( | |||
| `Deal `, | |||
| ...this.factors.map(factor => new LogLine( | |||
| `${factor.fraction * 100}% of your `, | |||
| new PropElem(factor.stat), | |||
| ` as `, | |||
| new PropElem(factor.target) | |||
| )).joinGeneral(new LogLine(`, `), new LogLine(` and `)) | |||
| ) | |||
| } | |||
| constructor (private factors: Array<{ stat: Stat; fraction: number; type: DamageType; target: Vigor|Stat }>) { | |||
| } | |||
| } | |||
| export enum Side { | |||
| Heroes, | |||
| Monsters | |||
| @@ -2,6 +2,7 @@ import { DamageType, Damage, Combatant, Stats, Action, Vigor, VoreStats, VoreSta | |||
| import { Noun, Pronoun, Adjective, ImproperNoun, TextLike } from './language' | |||
| import { LogEntry, LogLine } from './interface' | |||
| import { Vore, VoreContainer, VoreType, Container } from './vore' | |||
| import { Item } from './items' | |||
| export enum POV {First, Third} | |||
| @@ -57,6 +58,8 @@ export class Creature extends Vore implements Combatant { | |||
| groupActions: Array<GroupAction> = [] | |||
| otherActions: Array<Action> = [] | |||
| items: Array<Item> = [] | |||
| get bulk (): number { | |||
| return this.voreStats.Mass + this.containers.reduce((total, conatiner) => { return total + conatiner.contents.reduce((total, prey) => total + prey.voreStats.Bulk, 0) }, 0) | |||
| } | |||
| @@ -156,7 +159,9 @@ export class Creature extends Vore implements Combatant { | |||
| let choices = this.actions.concat( | |||
| this.containers.flatMap(container => container.actions)).concat( | |||
| target.otherActions.concat( | |||
| this.otherContainers.flatMap(container => container.actions) | |||
| this.otherContainers.flatMap(container => container.actions).concat( | |||
| this.items.flatMap(item => item.actions) | |||
| ) | |||
| ) | |||
| ) | |||
| @@ -0,0 +1,54 @@ | |||
| import { TextLike, LiveText, DynText, Word, ImproperNoun } from './language' | |||
| import { Actionable, Action, DamageFormula, ConstantDamageFormula, Damage, DamageType, Vigor, StatDamageFormula, Stat } from './combat' | |||
| import { AttackAction } from './combat/actions' | |||
| export interface Item extends Actionable { | |||
| name: Word; | |||
| desc: TextLike; | |||
| } | |||
| export class Weapon implements Actionable { | |||
| actions: Array<Action> = [] | |||
| constructor (public name: Word, public desc: TextLike, damageFormula: DamageFormula) { | |||
| const attack = new AttackAction(damageFormula) | |||
| attack.desc = new DynText(`Attack with your `, this.name.all) | |||
| this.actions.push(attack) | |||
| } | |||
| } | |||
| export const Sword = new Weapon( | |||
| new ImproperNoun('sword', 'swords'), | |||
| 'An arming sword', | |||
| new StatDamageFormula([ | |||
| { fraction: 0.35, stat: Stat.Power, target: Vigor.Health, type: DamageType.Slash }, | |||
| { fraction: 0.25, stat: Stat.Power, target: Vigor.Health, type: DamageType.Pierce } | |||
| ]) | |||
| ) | |||
| export const Dagger = new Weapon( | |||
| new ImproperNoun('dagger', 'daggers'), | |||
| 'A pointy dagger', | |||
| new StatDamageFormula([ | |||
| { fraction: 0.50, stat: Stat.Speed, target: Vigor.Health, type: DamageType.Pierce }, | |||
| { fraction: 0.05, stat: Stat.Speed, target: Vigor.Health, type: DamageType.Slash } | |||
| ]) | |||
| ) | |||
| export const Wand = new Weapon( | |||
| new ImproperNoun('wand', 'wands'), | |||
| 'A magical wand', | |||
| new StatDamageFormula([ | |||
| { fraction: 0.25, stat: Stat.Charm, target: Vigor.Health, type: DamageType.Crush }, | |||
| { fraction: 0.25, stat: Stat.Willpower, target: Vigor.Health, type: DamageType.Crush } | |||
| ]) | |||
| ) | |||
| export const Mace = new Weapon( | |||
| new ImproperNoun('mace', 'maces'), | |||
| 'A heavy mace', | |||
| new StatDamageFormula([ | |||
| { fraction: 0.4, stat: Stat.Power, target: Vigor.Health, type: DamageType.Crush }, | |||
| { fraction: 0.2, stat: Stat.Power, target: Vigor.Health, type: DamageType.Pierce } | |||
| ]) | |||
| ) | |||
| @@ -1,6 +1,21 @@ | |||
| import Vue from 'vue' | |||
| import App from './App.vue' | |||
| declare global { | |||
| interface Array<T> { | |||
| joinGeneral (item: T, endItem: T|null): Array<T>; | |||
| } | |||
| } | |||
| /* eslint-disable-next-line */ | |||
| Array.prototype.joinGeneral = function (item, endItem = null) { | |||
| if (endItem === null) { | |||
| return this.slice(0, -1).flatMap(x => [x, item]).concat(this.slice(-1)) | |||
| } else { | |||
| return this.slice(0, -2).flatMap(x => [x, item]).concat(this.slice(-2, -1).flatMap(x => [x, endItem])).concat(this.slice(-1)) | |||
| } | |||
| } | |||
| Vue.config.productionTip = false | |||
| new Vue({ | |||