Procházet zdrojové kódy

Combine Mortal and Creature; add stat-changing effects; add more potions

master
Fen Dweller před 5 roky
rodič
revize
0ad5a482c9
7 změnil soubory, kde provedl 219 přidání a 168 odebrání
  1. +7
    -0
      src/game/combat.ts
  2. +16
    -1
      src/game/combat/effects.ts
  3. +105
    -19
      src/game/creature.ts
  4. +0
    -102
      src/game/entity.ts
  5. +73
    -46
      src/game/items.ts
  6. +2
    -0
      src/game/maps/town.ts
  7. +16
    -0
      src/main.ts

+ 7
- 0
src/game/combat.ts Zobrazit soubor

@@ -577,6 +577,13 @@ export class Effective {
modDigestionDamage (predator: Creature, prey: Creature, container: VoreContainer, damage: Damage): Damage {
return damage
}

/**
* Affects a stat
*/
modStat (creature: Creature, stat: Stat, current: number): number {
return current
}
}
/**
* A displayable status effect


+ 16
- 1
src/game/combat/effects.ts Zobrazit soubor

@@ -1,4 +1,4 @@
import { StatusEffect, Damage, DamageType, Action, Condition, Vigor } from '../combat'
import { StatusEffect, Damage, DamageType, Action, Condition, Vigor, Stat } from '../combat'
import { DynText, LiveText, ToBe, Verb } from '../language'
import { Creature } from "../creature"
import { LogLine, LogEntry, LogLines, FAElem, nilLog } from '../interface'
@@ -194,3 +194,18 @@ export class DigestionPowerEffect extends StatusEffect {
return damage.scale(this.factor)
}
}

export class StatEffect extends StatusEffect {
constructor (private stat: Stat, private amount: number, private factor: number) {
super('Stat boosted', 'This creature has modified stats', 'fas fa-user-plus')
}

modStat (creature: Creature, stat: Stat, current: number): number {
console.log(stat, this.stat)
if (stat === this.stat) {
return current * this.factor + this.amount
} else {
return current
}
}
}

+ 105
- 19
src/game/creature.ts Zobrazit soubor

@@ -1,14 +1,33 @@
import { Damage, Combatant, Stats, Action, Vigor, Side, GroupAction, VisibleStatus, ImplicitStatus, StatusEffect, DamageType, Effective, VoreStat, VoreStats } from './combat'
import { Damage, Combatant, Stats, Action, Vigor, Side, GroupAction, VisibleStatus, ImplicitStatus, StatusEffect, DamageType, Effective, VoreStat, VoreStats, DamageInstance, Stat, Vigors } from './combat'
import { Noun, Pronoun, SoloLine, Verb } from './language'
import { LogEntry, LogLines, LogLine } from './interface'
import { VoreContainer, VoreType, Container } from './vore'
import { Item, EquipmentSlot, Equipment, ItemKind, Currency } from './items'
import { PassAction } from './combat/actions'
import { AI } from './ai'
import { Mortal } from './entity'
import { Entity, Resistances } from './entity'
import { Perk } from './combat/perks'

export class Creature extends Mortal {
export class Creature extends Entity {
baseResistances: Resistances

stats: Stats = (Object.keys(Stat) as Array<Stat>).reduce((result: Partial<Stats>, stat: Stat) => {
Object.defineProperty(result, stat, {
get: () => this.effects.reduce((total, effect) => effect.modStat(this, stat, total), this.baseStats[stat]),
set: (value: number) => { this.baseStats[stat] = value },
enumerable: true
})
return result
}, {}) as Stats

vigors: {[key in Vigor]: number} = {
[Vigor.Health]: 100,
[Vigor.Stamina]: 100,
[Vigor.Resolve]: 100
}

destroyed = false;

containers: Array<VoreContainer> = []
otherContainers: Array<Container> = []

@@ -41,8 +60,16 @@ export class Creature extends Mortal {
equipment: {[key in EquipmentSlot]?: Equipment } = {}
ai: AI|null = null

constructor (name: Noun, kind: Noun, pronouns: Pronoun, stats: Stats, public preyPrefs: Set<VoreType>, public predPrefs: Set<VoreType>, private baseMass: number) {
super(name, kind, pronouns, stats)
constructor (name: Noun, kind: Noun, pronouns: Pronoun, public baseStats: Stats, public preyPrefs: Set<VoreType>, public predPrefs: Set<VoreType>, private baseMass: number) {
super(name, kind, pronouns)

/* eslint-disable-next-line */
this.baseResistances = Object.keys(DamageType).reduce((resist: any, key) => { resist[key] = 1; return resist }, {})
Object.entries(this.maxVigors).forEach(([key, val]) => {
this.vigors[key as Vigor] = val
})

console.log(this)

this.actions.push(new PassAction())
this.side = Side.Heroes
@@ -96,26 +123,85 @@ export class Creature extends Mortal {
}
}

applyEffect (effect: StatusEffect): LogEntry {
this.statusEffects.push(effect)
return effect.onApply(this)
resistanceTo (damageType: DamageType): number {
return this.baseResistances[damageType]
}

get maxVigors (): Readonly<Vigors> {
return {
Health: this.stats.Toughness * 10 + this.stats.Power * 5,
Resolve: this.stats.Willpower * 10 + this.stats.Charm * 5,
Stamina: this.stats.Agility * 5 + this.stats.Reflexes * 5
}
}

get disabled (): boolean {
return Object.values(this.vigors).some(val => val <= 0)
}

/**
* Determines how much damage an attack would do
*/
effectiveDamage (damage: Damage): Damage {
const preDamage = this.effects.reduce((modifiedDamage: Damage, effect: Effective) => {
return effect.preDamage(this, modifiedDamage)
}, damage)
return super.effectiveDamage(preDamage)
const newDamages: DamageInstance[] = []
damage.damages.forEach(instance => {
const factor = instance.type === DamageType.Heal ? -1 : 1
const baseResistance: number = this.resistanceTo(instance.type)
const resistance = baseResistance * factor
newDamages.push({
amount: instance.amount * resistance,
target: instance.target,
type: instance.type
})
})

return new Damage(...newDamages)
}

takeDamage (damage: Damage): LogEntry {
// first, we record health to decide if the entity just died
const startHealth = this.vigors.Health

damage = this.effectiveDamage(damage)

damage.damages.forEach(instance => {
if (instance.target in Vigor) {
// just deal damage
this.vigors[instance.target as Vigor] -= instance.amount
} else if (instance.target in Stat) {
// drain the stats, then deal damage to match
const startVigors = this.maxVigors
this.stats[instance.target as Stat] -= instance.amount
const endVigors = this.maxVigors

Object.keys(Vigor).map(vigor => {
this.vigors[vigor as Vigor] -= startVigors[vigor as Vigor] - endVigors[vigor as Vigor]
})
}
})

Object.keys(Vigor).forEach(vigorStr => {
const vigor = vigorStr as Vigor
if (this.vigors[vigor] > this.maxVigors[vigor]) {
this.vigors[vigor] = this.maxVigors[vigor]
}
})

if (this.vigors.Health <= -this.maxVigors.Health) {
this.destroyed = true
}

if (this.vigors.Health <= 0 && startHealth > 0) {
return this.destroy()
} else {
return new LogLine()
}
}

resistanceTo (damageType: DamageType) {
const base = super.resistanceTo(damageType)
toString (): string {
return this.name.toString()
}

const modified = this.effects.reduce((resist, effect) => effect.modResistance(damageType, resist), base)
return modified
applyEffect (effect: StatusEffect): LogEntry {
this.statusEffects.push(effect)
return effect.onApply(this)
}

executeAction (action: Action, target: Creature): LogEntry {


+ 0
- 102
src/game/entity.ts Zobrazit soubor

@@ -35,105 +35,3 @@ export abstract class Entity {
}

export type Resistances = {[key in DamageType]: number}

export abstract class Mortal extends Entity {
baseResistances: Resistances

stats: Stats;
vigors: {[key in Vigor]: number} = {
[Vigor.Health]: 100,
[Vigor.Stamina]: 100,
[Vigor.Resolve]: 100
}

destroyed = false;

constructor (name: Noun, kind: Noun, pronouns: Pronoun, public baseStats: Stats) {
super(name, kind, pronouns)
/* eslint-disable-next-line */
this.stats = Object.keys(Stat).reduce((base: any, key) => { base[key] = baseStats[key as Stat]; return base }, {})
/* eslint-disable-next-line */
this.baseResistances = Object.keys(DamageType).reduce((resist: any, key) => { resist[key] = 1; return resist }, {})
Object.entries(this.maxVigors).forEach(([key, val]) => {
this.vigors[key as Vigor] = val
})
}

resistanceTo (damageType: DamageType): number {
return this.baseResistances[damageType]
}

get maxVigors (): Readonly<Vigors> {
return {
Health: this.stats.Toughness * 10 + this.stats.Power * 5,
Resolve: this.stats.Willpower * 10 + this.stats.Charm * 5,
Stamina: this.stats.Agility * 5 + this.stats.Reflexes * 5
}
}

get disabled (): boolean {
return Object.values(this.vigors).some(val => val <= 0)
}

effectiveDamage (damage: Damage): Damage {
const newDamages: DamageInstance[] = []
damage.damages.forEach(instance => {
const factor = instance.type === DamageType.Heal ? -1 : 1
const baseResistance: number = this.resistanceTo(instance.type)
const resistance = baseResistance * factor
newDamages.push({
amount: instance.amount * resistance,
target: instance.target,
type: instance.type
})
})

return new Damage(...newDamages)
}

takeDamage (damage: Damage): LogEntry {
// first, we record health to decide if the entity just died
const startHealth = this.vigors.Health

damage = this.effectiveDamage(damage)

damage.damages.forEach(instance => {
if (instance.target in Vigor) {
// just deal damage
this.vigors[instance.target as Vigor] -= instance.amount
} else if (instance.target in Stat) {
// drain the stats, then deal damage to match
const startVigors = this.maxVigors
this.stats[instance.target as Stat] -= instance.amount
const endVigors = this.maxVigors

Object.keys(Vigor).map(vigor => {
this.vigors[vigor as Vigor] -= startVigors[vigor as Vigor] - endVigors[vigor as Vigor]
})
}
})

Object.keys(Vigor).forEach(vigorStr => {
const vigor = vigorStr as Vigor
if (this.vigors[vigor] > this.maxVigors[vigor]) {
this.vigors[vigor] = this.maxVigors[vigor]
}
})

if (this.vigors.Health <= -this.maxVigors.Health) {
this.destroyed = true
}

if (this.vigors.Health <= 0 && startHealth > 0) {
return this.destroy()
} else {
return new LogLine()
}
}

toString (): string {
return this.name.toString()
}

abstract destroy (): LogEntry;
}

+ 73
- 46
src/game/items.ts Zobrazit soubor

@@ -1,9 +1,9 @@
import { TextLike, LiveText, DynText, Word, ImproperNoun, Verb, Noun } from './language'
import { Actionable, Action, DamageFormula, ConstantDamageFormula, Damage, DamageType, Vigor, StatDamageFormula, Stat, Effective, CompositionAction, Condition, CompositeDamageFormula } from './combat'
import { Actionable, Action, DamageFormula, ConstantDamageFormula, Damage, DamageType, Vigor, StatDamageFormula, Stat, Effective, CompositionAction, Condition, CompositeDamageFormula, Consequence, StatusEffect } from './combat'
import { AttackAction } from './combat/actions'
import { Resistances } from './entity'
import { DamageTypeResistanceEffect, DigestionPowerEffect } from './combat/effects'
import { DamageConsequence, LogConsequence, HealingConsequence, StatusConsequence } from './combat/consequences'
import { DamageTypeResistanceEffect, DigestionPowerEffect, SizeEffect, StatEffect } from './combat/effects'
import { DamageConsequence, LogConsequence, HealingConsequence, StatusConsequence, ArbitraryConsequence } from './combat/consequences'
import { SoloCondition } from './combat/conditions'
import { LogLine, LogEntry } from './interface'
import { Creature } from './creature'
@@ -192,67 +192,94 @@ export class Consumable extends Item {
}
}

export class HealthPotion extends Consumable {
constructor () {
export abstract class Potion extends Consumable {
constructor (name: ImproperNoun, desc: string, consequences: Array<Consequence>) {
super(
new ImproperNoun("health potion"),
"Restores all of your vigors",
desc,
new CompositionAction(
"Drink Potion",
"Heals your vigors",
"Drink " + name,
desc,
{
conditions: [
new SoloCondition()
],
consequences: [
consequences: ([
new LogConsequence(
(user, target) => new LogLine(`${user.name.capital} ${user.name.conjugate(new Verb('drink'))} a potion.`)
),
new HealingConsequence(
new CompositeDamageFormula([
new ConstantDamageFormula(
new Damage(
{ amount: 100, target: Vigor.Health, type: DamageType.Heal },
{ amount: 100, target: Vigor.Stamina, type: DamageType.Heal },
{ amount: 100, target: Vigor.Resolve, type: DamageType.Heal }
)
),
new StatDamageFormula([
{ fraction: 2, stat: Stat.Toughness, target: Vigor.Health, type: DamageType.Heal },
{ fraction: 2, stat: Stat.Agility, target: Vigor.Stamina, type: DamageType.Heal },
{ fraction: 2, stat: Stat.Willpower, target: Vigor.Resolve, type: DamageType.Heal }
])
])
(user, target) => new LogLine(`${user.name.capital} ${user.name.conjugate(new Verb('drink'))} a ${name}.`)
)
]
] as Consequence[]).concat(consequences)
}
)
)
}
}

export class AcidPotion extends Consumable {
export class HealthPotion extends Potion {
constructor () {
super(
new ImproperNoun("Acid Potion"),
"Boosts your digestive power and causes a burst of damage to your prey",
new CompositionAction(
"Drink Potion",
"Speed up your digestion",
{
conditions: [
new SoloCondition()
],
consequences: [
new LogConsequence(
(user, target) => new LogLine(`${user.name.capital} ${user.name.conjugate(new Verb('drink'))} an acid potion.`)
new ImproperNoun("Health Potion"),
"Heals your vigors",
[
new HealingConsequence(
new CompositeDamageFormula([
new ConstantDamageFormula(
new Damage(
{ amount: 100, target: Vigor.Health, type: DamageType.Heal },
{ amount: 100, target: Vigor.Stamina, type: DamageType.Heal },
{ amount: 100, target: Vigor.Resolve, type: DamageType.Heal }
)
),
new StatusConsequence(
(user, target) => new DigestionPowerEffect(2)
)
]
}
)
new StatDamageFormula([
{ fraction: 2, stat: Stat.Toughness, target: Vigor.Health, type: DamageType.Heal },
{ fraction: 2, stat: Stat.Agility, target: Vigor.Stamina, type: DamageType.Heal },
{ fraction: 2, stat: Stat.Willpower, target: Vigor.Resolve, type: DamageType.Heal }
])
])
)
]
)
}
}

export class StrengthPotion extends Potion {
constructor () {
super(
new ImproperNoun("Strength Potion"),
"Power up!",
[
new StatusConsequence(
(user, target) => new StatEffect(Stat.Power, 0, 1.5)
)
]
)
}
}

export class AcidPotion extends Potion {
constructor () {
super(
new ImproperNoun("Acid Potion"),
"Boosts your digestive power",
[
new StatusConsequence(
(user, target) => new DigestionPowerEffect(2)
)
]
)
}
}

export class ShrinkPotion extends Potion {
constructor () {
super(
new ImproperNoun("Shrink Potion"),
"Drink me!",
[
new StatusConsequence(
(user, target) => new SizeEffect(0.5)
)
]
)
}
}

+ 2
- 0
src/game/maps/town.ts Zobrazit soubor

@@ -251,6 +251,8 @@ export const Town = (): Place => {
(world, executor) => {
executor.items.push(new Items.HealthPotion())
executor.items.push(new Items.AcidPotion())
executor.items.push(new Items.ShrinkPotion())
executor.items.push(new Items.StrengthPotion())
return new LogLine("You grab some potions")
}
)


+ 16
- 0
src/main.ts Zobrazit soubor

@@ -4,6 +4,8 @@ import App from './App.vue'
declare global {
interface Array<T> {
joinGeneral (item: T, endItem: T|null): Array<T>;
/* eslint-disable-next-line */
unique (predicate: (elem: T) => any): Array<T>;
}
}

@@ -16,6 +18,20 @@ Array.prototype.joinGeneral = function (item, endItem = null) {
}
}

/* eslint-disable-next-line */
Array.prototype.unique = function<T> (predicate: (elem: T) => any): Array<T> {
const set = new Set()
const result: Array<T> = [] as T[]
this.forEach(elem => {
const predResult = predicate(elem)
if (!set.has(predResult)) {
set.add(predResult)
result.push(elem)
}
})
return result
}

Vue.config.productionTip = false

new Vue({


Načítá se…
Zrušit
Uložit