|
- import { Creature, POV } from './entity'
- import { POVActionPicker } from './language'
- import { Container } from './vore'
- import { LogEntry, LogLines, CompositeLog } from './interface'
-
- export enum DamageType {Pierce, Slash, Crush, Acid}
-
- export interface DamageInstance {
- type: DamageType;
- amount: number;
- }
-
- export class Damage {
- readonly damages: DamageInstance[]
-
- constructor (...damages: DamageInstance[]) {
- this.damages = damages
- }
-
- scale (factor: number): Damage {
- const results: Array<DamageInstance> = []
-
- this.damages.forEach(damage => {
- results.push({
- type: damage.type,
- amount: damage.amount * factor
- })
- })
-
- return new Damage(...results)
- }
- }
-
- export enum Stat {
- STR = 'Strength',
- DEX = 'Dexterity',
- CON = 'Constitution'
- }
-
- export type Stats = {[key in Stat]: number}
-
- export enum State {
- Normal = 'Normal',
- Grappled = 'Grappled',
- Grappling = 'Grappling',
- Eaten = 'Eaten'
- }
-
- export interface Combatant {
- actions: Array<Action>;
- }
-
- export abstract class Action {
- public name: string;
- allowed (user: Creature, target: Creature) {
- return this.userStates.has(user.state) && this.targetStates.has(target.state)
- }
-
- abstract execute(user: Creature, target: Creature): LogEntry
-
- constructor (name: string, public userStates: Set<State>, public targetStates: Set<State>) {
- this.name = name
- }
-
- toString (): string {
- return this.name
- }
- }
-
- abstract class SelfAction extends Action {
- allowed (user: Creature, target: Creature) {
- if (user === target) {
- return super.allowed(user, target)
- } else {
- return false
- }
- }
- }
-
- abstract class PairAction extends Action {
- allowed (user: Creature, target: Creature) {
- if (user !== target) {
- return super.allowed(user, target)
- } else {
- return false
- }
- }
- }
-
- export class AttackAction extends PairAction {
- protected lines: POVActionPicker = {
- [POV.First]: {
- [POV.First]: (user, target) => new LogLines('You bite...yourself?'),
- [POV.Third]: (user, target) => new LogLines('You bite ' + target.name)
- },
- [POV.Third]: {
- [POV.First]: (user, target) => new LogLines(user.name.capital + ' bites you'),
- [POV.Third]: (user, target) => new LogLines(user.name.capital + ' bites ' + target.name)
- }
- }
-
- constructor (protected damage: Damage) {
- super('Attack', new Set([State.Normal]), new Set([State.Normal]))
- }
-
- execute (user: Creature, target: Creature): LogEntry {
- target.takeDamage(this.damage)
- return this.lines[user.perspective][target.perspective](user, target)
- }
- }
-
- export class DevourAction extends PairAction {
- protected lines: POVActionPicker = {
- [POV.First]: {
- [POV.First]: (user, target) => new LogLines('You devour...yourself?'),
- [POV.Third]: (user, target) => new LogLines('You devour ' + target.name)
- },
- [POV.Third]: {
- [POV.First]: (user, target) => new LogLines(user.name.capital + ' devours you'),
- [POV.Third]: (user, target) => new LogLines(user.name.capital + ' devours ' + target.name)
- }
- }
-
- constructor (protected container: Container) {
- super('Devour', new Set([State.Normal]), new Set([State.Normal]))
- }
-
- execute (user: Creature, target: Creature): LogEntry {
- target.state = State.Eaten
- return new CompositeLog(this.lines[user.perspective][target.perspective](user, target), this.container.consume(target))
- }
- }
-
- export class StruggleAction extends PairAction {
- protected lines: POVActionPicker = {
- [POV.First]: {
- [POV.First]: (user, target) => new LogLines('You escape from...yourself?'),
- [POV.Third]: (user, target) => new LogLines('You escape from ' + target.name)
- },
- [POV.Third]: {
- [POV.First]: (user, target) => new LogLines(user.name.capital + ' escapes from you'),
- [POV.Third]: (user, target) => new LogLines(user.name.capital + ' escapes from ' + target.name)
- }
- }
-
- constructor () {
- super('Struggle', new Set([State.Eaten]), new Set([State.Normal]))
- }
-
- execute (user: Creature, target: Creature): LogEntry {
- if (user.containedIn) {
- user.state = State.Normal
- return new CompositeLog(this.lines[user.perspective][target.perspective](user, target), user.containedIn.release(user))
- } else { return new LogLines("The prey wasn't actually eaten...") }
- }
- }
-
- export class DigestAction extends SelfAction {
- protected lines: POVActionPicker = {
- [POV.First]: {
- [POV.First]: (user, target) => new LogLines('You rub your stomach'),
- [POV.Third]: (user, target) => new LogLines("You can't digest for other people...")
- },
- [POV.Third]: {
- [POV.First]: (user, target) => new LogLines("Other people can't digest for you..."),
- [POV.Third]: (user, target) => new LogLines(user.name.capital + ' rubs ' + user.pronouns.possessive + ' gut.')
- }
- }
-
- allowed (user: Creature, target: Creature) {
- if (Array.from(user.containers).some(container => {
- return container.contents.size > 0
- })) {
- return super.allowed(user, target)
- } else {
- return false
- }
- }
-
- constructor () {
- super('Digest', new Set([State.Normal]), new Set([State.Normal]))
- }
-
- execute (user: Creature, target: Creature): LogEntry {
- const results = new CompositeLog(...Array.from(user.containers).map(container => container.tick(60)))
- return new CompositeLog(this.lines[user.perspective][target.perspective](user, target), results)
- }
- }
-
- export interface CombatTest {
- test: (user: Creature, target: Creature) => boolean;
- odds: (user: Creature, target: Creature) => number;
- explain: (user: Creature, target: Creature) => LogEntry;
- }
-
- function logistic (x0: number, L: number, k: number): (x: number) => number {
- return (x: number) => {
- return L / (1 + Math.exp(-k * (x - x0)))
- }
- }
-
- abstract class RandomTest implements CombatTest {
- test (user: Creature, target: Creature): boolean {
- return Math.random() < this.odds(user, target)
- }
-
- abstract odds(user: Creature, target: Creature): number
- abstract explain(user: Creature, target: Creature): LogEntry
- }
-
- export class StatTest extends RandomTest {
- private f: (x: number) => number
-
- constructor (public readonly stat: Stat, k = 0.1) {
- super()
- this.f = logistic(0, 1, k)
- }
-
- odds (user: Creature, target: Creature): number {
- return this.f(user.stats[this.stat] - target.stats[this.stat])
- }
-
- explain (user: Creature, target: Creature): LogEntry {
- const delta: number = user.stats[this.stat] - target.stats[this.stat]
- let result: string
-
- if (delta === 0) {
- result = 'You and the target have the same ' + this.stat + '.'
- } else if (delta < 0) {
- result = 'You have ' + delta + ' less ' + this.stat + ' than your foe.'
- } else {
- result = 'You have ' + delta + ' more ' + this.stat + ' than you foe.'
- }
-
- result += ' Your odds of success are ' + (100 * this.odds(user, target)) + '%'
-
- return new LogLines(result)
- }
- }
-
- export class ChanceTest extends RandomTest {
- constructor (public readonly chance: number) {
- super()
- }
-
- odds (user: Creature, target: Creature): number {
- return this.chance
- }
-
- explain (user: Creature, target: Creature): LogEntry {
- return new LogLines('You have a flat ' + (100 * this.chance) + '% chance.')
- }
- }
|