| @@ -360,11 +360,17 @@ export abstract class StatusEffect implements VisibleStatus { | |||||
| get bottomRight () { return '' } | get bottomRight () { return '' } | ||||
| onApply (creature: Creature): LogEntry { return nilLog } | onApply (creature: Creature): LogEntry { return nilLog } | ||||
| onRemove (creature: Creature): LogEntry { return nilLog } | onRemove (creature: Creature): LogEntry { return nilLog } | ||||
| preAction (creature: Creature): { prevented: boolean; log: LogEntry } { | preAction (creature: Creature): { prevented: boolean; log: LogEntry } { | ||||
| return { | return { | ||||
| prevented: false, | prevented: false, | ||||
| log: nilLog | log: nilLog | ||||
| } | } | ||||
| } | } | ||||
| preDamage (creature: Creature, damage: Damage): Damage { | |||||
| return damage | |||||
| } | |||||
| } | } | ||||
| @@ -11,7 +11,7 @@ export class AttackAction extends Action { | |||||
| protected successLine: PairLineArgs<Creature, { damage: Damage }> = (user, target, args) => new LogLine( | protected successLine: PairLineArgs<Creature, { damage: Damage }> = (user, target, args) => new LogLine( | ||||
| `${user.name.capital} ${user.name.conjugate(this.verb)} ${target.name.objective} for `, | `${user.name.capital} ${user.name.conjugate(this.verb)} ${target.name.objective} for `, | ||||
| args.damage.renderShort() | |||||
| target.effectiveDamage(args.damage).renderShort() | |||||
| ) | ) | ||||
| protected failLine: PairLine<Creature> = (user, target) => new LogLine( | protected failLine: PairLine<Creature> = (user, target) => new LogLine( | ||||
| @@ -1,5 +1,5 @@ | |||||
| import { StatusEffect, Damage } from '../combat' | |||||
| import { DynText, LiveText, ToBe } from '../language' | |||||
| import { StatusEffect, Damage, DamageType } from '../combat' | |||||
| import { DynText, LiveText, ToBe, Verb } from '../language' | |||||
| import { Creature } from '../entity' | import { Creature } from '../entity' | ||||
| import { LogLine, LogEntry, LogLines, FAElem } from '../interface' | import { LogLine, LogEntry, LogLines, FAElem } from '../interface' | ||||
| @@ -56,3 +56,21 @@ export class StunEffect extends StatusEffect { | |||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| export class ShieldEffect extends StatusEffect { | |||||
| constructor (private damageTypes: DamageType[], private amount: number) { | |||||
| super('Shield', 'Block a fraction of incoming damage', 'fas fa-shield-alt') | |||||
| } | |||||
| onApply (creature: Creature) { | |||||
| return new LogLine(`${creature.name.capital} ${creature.name.conjugate(new Verb('gain'))} a shield!`) | |||||
| } | |||||
| onRemove (creature: Creature) { | |||||
| return new LogLine(`${creature.name.capital} ${creature.name.conjugate(new Verb('lose'))} ${creature.pronouns.possessive} shield!`) | |||||
| } | |||||
| preDamage (creature: Creature, damage: Damage) { | |||||
| return damage.scale(this.amount) | |||||
| } | |||||
| } | |||||
| @@ -10,7 +10,7 @@ import * as Words from '../words' | |||||
| class BellyCrushAction extends AttackAction { | class BellyCrushAction extends AttackAction { | ||||
| successLine: PairLineArgs<Creature, { damage: Damage }> = (user, target, args) => new LogLines(new LogLine( | successLine: PairLineArgs<Creature, { damage: Damage }> = (user, target, args) => new LogLines(new LogLine( | ||||
| `${user.name.capital} ${user.name.conjugate(new Verb('crush', 'crushes'))} on ${target.name.objective} with ${user.pronouns.possessive} belly for `, | `${user.name.capital} ${user.name.conjugate(new Verb('crush', 'crushes'))} on ${target.name.objective} with ${user.pronouns.possessive} belly for `, | ||||
| args.damage.renderShort() | |||||
| target.effectiveDamage(args.damage).renderShort() | |||||
| ), new ImgElem('./media/cafat/images/belly-crush.webp')) | ), new ImgElem('./media/cafat/images/belly-crush.webp')) | ||||
| constructor (_damage: Damage) { | constructor (_damage: Damage) { | ||||
| @@ -32,7 +32,7 @@ class BelchAction extends AttackAction { | |||||
| successLine: PairLineArgs<Creature, { damage: Damage }> = (user, target, args) => new LogLines( | successLine: PairLineArgs<Creature, { damage: Damage }> = (user, target, args) => new LogLines( | ||||
| new LogLine( | new LogLine( | ||||
| `${user.name.capital} ${user.name.conjugate(new Verb('belch', 'belches'))} on ${target.name.objective} for `, | `${user.name.capital} ${user.name.conjugate(new Verb('belch', 'belches'))} on ${target.name.objective} for `, | ||||
| args.damage.renderShort() | |||||
| target.effectiveDamage(args.damage).renderShort() | |||||
| ), | ), | ||||
| new ImgElem('./media/cafat/images/belch.webp') | new ImgElem('./media/cafat/images/belch.webp') | ||||
| ) | ) | ||||
| @@ -5,7 +5,7 @@ import { LogLine, LogLines, LogEntry, Newline } from '../interface' | |||||
| import { VoreType, Stomach, VoreContainer, Vore, NormalContainer, Container, InnerStomach } from '../vore' | import { VoreType, Stomach, VoreContainer, Vore, NormalContainer, Container, InnerStomach } from '../vore' | ||||
| import { AttackAction, FeedAction, TransferAction, EatenAction } from '../combat/actions' | import { AttackAction, FeedAction, TransferAction, EatenAction } from '../combat/actions' | ||||
| import { TogetherCondition, ContainsCondition, EnemyCondition, AllyCondition, PairCondition, CapableCondition } from '../combat/conditions' | import { TogetherCondition, ContainsCondition, EnemyCondition, AllyCondition, PairCondition, CapableCondition } from '../combat/conditions' | ||||
| import { InstantKillEffect } from '../combat/effects' | |||||
| import { InstantKillEffect, ShieldEffect } from '../combat/effects' | |||||
| import * as Words from '../words' | import * as Words from '../words' | ||||
| import { StatVigorTest } from '../combat/tests' | import { StatVigorTest } from '../combat/tests' | ||||
| @@ -26,6 +26,8 @@ class LevelDrain extends Action { | |||||
| } | } | ||||
| })) | })) | ||||
| // TODO make this respect resistances | |||||
| user.takeDamage(heal) | user.takeDamage(heal) | ||||
| const targetResult = target.takeDamage(damage) | const targetResult = target.takeDamage(damage) | ||||
| @@ -238,6 +240,10 @@ class StompAllyAction extends Action { | |||||
| return new LogLines( | return new LogLines( | ||||
| this.line(user, target), | this.line(user, target), | ||||
| target.applyEffect(new InstantKillEffect()), | target.applyEffect(new InstantKillEffect()), | ||||
| user.applyEffect(new ShieldEffect( | |||||
| [DamageType.Crush, DamageType.Slash, DamageType.Pierce], | |||||
| 0.5 | |||||
| )), | |||||
| new LogLine(`${user.name.capital} absorbs ${target.pronouns.possessive} power, gaining `, heal.renderShort()) | new LogLine(`${user.name.capital} absorbs ${target.pronouns.possessive} power, gaining `, heal.renderShort()) | ||||
| ) | ) | ||||
| } | } | ||||
| @@ -139,10 +139,21 @@ export class Creature extends Vore implements Combatant { | |||||
| return this.name.toString() | return this.name.toString() | ||||
| } | } | ||||
| /** | |||||
| * Determines how much damage an attack would do | |||||
| */ | |||||
| effectiveDamage (damage: Damage): Damage { | |||||
| return this.effects.reduce((modifiedDamage: Damage, effect: StatusEffect) => { | |||||
| return effect.preDamage(this, modifiedDamage) | |||||
| }, damage) | |||||
| } | |||||
| takeDamage (damage: Damage): LogEntry { | takeDamage (damage: Damage): LogEntry { | ||||
| // first, we record health to decide if the entity just died | // first, we record health to decide if the entity just died | ||||
| const startHealth = this.vigors.Health | const startHealth = this.vigors.Health | ||||
| damage = this.effectiveDamage(damage) | |||||
| damage.damages.forEach(instance => { | damage.damages.forEach(instance => { | ||||
| const factor = instance.type === DamageType.Heal ? -1 : 1 | const factor = instance.type === DamageType.Heal ? -1 : 1 | ||||
| const effectiveResistance: number|undefined = this.resistances.get(instance.type) | const effectiveResistance: number|undefined = this.resistances.get(instance.type) | ||||
| @@ -44,6 +44,7 @@ export abstract class Vore implements Mortal { | |||||
| abstract maxVigors: {[key in Vigor]: number}; | abstract maxVigors: {[key in Vigor]: number}; | ||||
| abstract disabled: boolean; | abstract disabled: boolean; | ||||
| abstract resistances: Map<DamageType, number>; | abstract resistances: Map<DamageType, number>; | ||||
| abstract effectiveDamage (damage: Damage): Damage; | |||||
| abstract takeDamage (damage: Damage): LogEntry; | abstract takeDamage (damage: Damage): LogEntry; | ||||
| abstract stats: Stats; | abstract stats: Stats; | ||||
| abstract baseStats: Stats; | abstract baseStats: Stats; | ||||
| @@ -287,7 +288,7 @@ export class Stomach extends NormalVoreContainer { | |||||
| this.owner.takeDamage(heal) | this.owner.takeDamage(heal) | ||||
| return new LogLines( | return new LogLines( | ||||
| super.digest(preys), | super.digest(preys), | ||||
| new LogLine(`${this.owner.name.capital} heals for `, heal.renderShort()) | |||||
| new LogLine(`${this.owner.name.capital} heals for `, this.owner.effectiveDamage(heal).renderShort()) | |||||
| ) | ) | ||||
| } | } | ||||
| } | } | ||||