소스 검색

Add status effects to stomachs

This also restores preAction and preReceiveAction effects, which were
previouosly ignored. To support this, findResult and mapUntil were
added to the prototype for arrays. These allow for functions with
side effects to be searched or mapped over.
master
Fen Dweller 3 년 전
부모
커밋
c40614e3ab
6개의 변경된 파일126개의 추가작업 그리고 11개의 파일을 삭제
  1. +30
    -0
      src/game/combat.ts
  2. +11
    -4
      src/game/combat/effects.ts
  3. +4
    -1
      src/game/creature.ts
  4. +32
    -2
      src/game/creatures/monsters/werewolf.ts
  5. +12
    -4
      src/game/vore.ts
  6. +37
    -0
      src/main.ts

+ 30
- 0
src/game/combat.ts 파일 보기

@@ -481,6 +481,34 @@ export abstract class Action {
} }


try (user: Creature, targets: Array<Creature>): LogEntry { try (user: Creature, targets: Array<Creature>): LogEntry {
// Check if any pre-action effect will cancel this action.
const preActionResults = user.effects.mapUntil(effect => effect.preAction(user), result => result.prevented)
const preActionLogs = new LogLines(...preActionResults.map(result => result.log))

if (preActionResults.some(result => result.prevented)) {
return preActionLogs
}

// Check if any pre-receive-action effect will cancel this action.
const preReceiveActionResults = targets.mapUntil(target => {
const outcome = target.effects.mapUntil(effect => effect.preReceiveAction(user, target), result => result.prevented)

return outcome
}, results => results.some(result => result.prevented))

console.log(preReceiveActionResults)

const preReceiveActionLogs = new LogLines(...preReceiveActionResults.flatMap(
target => target.map(result => result.log)
))

if (preReceiveActionResults.some(results => results.some(result => result.prevented))) {
return new LogLines(
preActionLogs,
preReceiveActionLogs
)
}

const results = targets.map(target => { const results = targets.map(target => {
const failReason = this.tests.find(test => !test.test(user, target)) const failReason = this.tests.find(test => !test.test(user, target))
if (failReason !== undefined) { if (failReason !== undefined) {
@@ -499,6 +527,8 @@ export abstract class Action {
}) })


return new LogLines( return new LogLines(
preActionLogs,
preReceiveActionLogs,
...results.map(result => result.log), ...results.map(result => result.log),
this.executeAll( this.executeAll(
user, user,


+ 11
- 4
src/game/combat/effects.ts 파일 보기

@@ -161,10 +161,17 @@ export class UntouchableEffect extends StatusEffect {
super("Untouchable", "Cannot be attacked", "fas fa-times") super("Untouchable", "Cannot be attacked", "fas fa-times")
} }


preAttack (creature: Creature, attacker: Creature) {
return {
prevented: true,
log: new LogLine(`${creature.name.capital} cannot be attacked.`)
preReceiveAction (user: Creature, target: Creature) {
if (user !== target) {
return {
prevented: true,
log: new LogLine(`${target.name.capital} cannot be attacked.`)
}
} else {
return {
prevented: false,
log: nilLog
}
} }
} }
} }


+ 4
- 1
src/game/creature.ts 파일 보기

@@ -41,12 +41,14 @@ export class Creature extends Entity {
desc = "Some creature"; desc = "Some creature";


get effects (): Array<Effective> { get effects (): Array<Effective> {
return (this.statusEffects as Effective[]).concat(
const effects: Array<Effective> = (this.statusEffects as Effective[]).concat(
Object.values(this.equipment).filter(item => item !== undefined).flatMap( Object.values(this.equipment).filter(item => item !== undefined).flatMap(
item => (item as Equipment).effects item => (item as Equipment).effects
), ),
this.perks this.perks
) )

return effects
} }


statusEffects: Array<StatusEffect> = []; statusEffects: Array<StatusEffect> = [];
@@ -260,6 +262,7 @@ export class Creature extends Entity {
this.statusEffects.forEach(effect => { this.statusEffects.forEach(effect => {
results.push(effect) results.push(effect)
}) })

return results return results
} }




+ 32
- 2
src/game/creatures/monsters/werewolf.ts 파일 보기

@@ -1,8 +1,8 @@
import { VoreAI } from '@/game/ai' import { VoreAI } from '@/game/ai'
import { DamageType, Side, Stat, StatDamageFormula, Vigor } from '@/game/combat'
import { DamageType, Side, Stat, StatDamageFormula, StatusEffect, Vigor } from '@/game/combat'
import { DigestionPowerEffect } from '@/game/combat/effects' import { DigestionPowerEffect } from '@/game/combat/effects'
import { Creature } from '@/game/creature' import { Creature } from '@/game/creature'
import { LogEntry, LogLine } from '@/game/interface'
import { LogEntry, LogLine, nilLog } from '@/game/interface'
import { ImproperNoun, MalePronouns, ObjectPronouns, Preposition, Verb } from '@/game/language' import { ImproperNoun, MalePronouns, ObjectPronouns, Preposition, Verb } from '@/game/language'
import { anyVore, ConnectionDirection, Container, Stomach, Throat, transferDescription } from '@/game/vore' import { anyVore, ConnectionDirection, Container, Stomach, Throat, transferDescription } from '@/game/vore'
import * as Words from '@/game/words' import * as Words from '@/game/words'
@@ -40,6 +40,36 @@ export default class Werewolf extends Creature {
]) ])
) )


stomach.effects.push(new class extends StatusEffect {
constructor () {
super(
"Pinned",
"Prey sometimes can't move.",
"fas fa-sun"
)
}

onApply (creature: Creature): LogEntry {
return new LogLine(
`${stomach.owner.name.capital.possessive} ${stomach.name} is incredibly tight, gripping ${creature.name.objective} like a vice!`
)
}

preAction (creature: Creature): { prevented: boolean; log: LogEntry } {
if (Math.random() < 0.5) {
return {
prevented: true,
log: new LogLine(`${creature.name.capital} can't move!`)
}
} else {
return {
prevented: false,
log: nilLog
}
}
}
}())

this.addContainer(throat) this.addContainer(throat)
this.addContainer(stomach) this.addContainer(stomach)




+ 12
- 4
src/game/vore.ts 파일 보기

@@ -1,4 +1,4 @@
import { Damage, DamageType, Actionable, Action, Vigor, DamageInstance, DamageFormula, ConstantDamageFormula } from '@/game/combat'
import { Damage, DamageType, Actionable, Action, Vigor, DamageInstance, DamageFormula, ConstantDamageFormula, StatusEffect } from '@/game/combat'
import { LogLines, LogEntry, LogLine, nilLog, RandomEntry, FormatEntry, FormatOpt } from '@/game/interface' import { LogLines, LogEntry, LogLine, nilLog, RandomEntry, FormatEntry, FormatOpt } from '@/game/interface'
import { Noun, ImproperNoun, Verb, RandomWord, Word, Preposition, ToBe, Adjective } from '@/game/language' import { Noun, ImproperNoun, Verb, RandomWord, Word, Preposition, ToBe, Adjective } from '@/game/language'
import { RubAction, DevourAction, ReleaseAction, StruggleAction, TransferAction, StruggleMoveAction } from '@/game/combat/actions' import { RubAction, DevourAction, ReleaseAction, StruggleAction, TransferAction, StruggleMoveAction } from '@/game/combat/actions'
@@ -65,6 +65,8 @@ export interface Container extends Actionable {


connections: Array<Connection>; connections: Array<Connection>;


effects: Array<StatusEffect>;

wall: Wall | null; wall: Wall | null;
fluid: Fluid | null; fluid: Fluid | null;
gas: Gas | null; gas: Gas | null;
@@ -127,6 +129,8 @@ export abstract class DefaultContainer implements Container {


voreRelay = new VoreRelay() voreRelay = new VoreRelay()


effects: Array<StatusEffect> = []

consumeVerb = new Verb('devour') consumeVerb = new Verb('devour')
consumePreposition = new Preposition("into") consumePreposition = new Preposition("into")
releaseVerb = new Verb('release', 'releases', 'releasing', 'released') releaseVerb = new Verb('release', 'releases', 'releasing', 'released')
@@ -226,6 +230,8 @@ export abstract class DefaultContainer implements Container {
this.contents.push(prey) this.contents.push(prey)
prey.containedIn = this prey.containedIn = this


const effectResults = this.effects.map(effect => prey.applyEffect(effect))

const results = [ const results = [
this.voreRelay.dispatch("onEntered", this, { prey: prey }), this.voreRelay.dispatch("onEntered", this, { prey: prey }),
prey.voreRelay.dispatch("onEntered", this, { prey: prey }) prey.voreRelay.dispatch("onEntered", this, { prey: prey })
@@ -233,7 +239,7 @@ export abstract class DefaultContainer implements Container {


this.owner.effects.forEach(effect => results.push(effect.postEnter(this.owner, prey, this))) this.owner.effects.forEach(effect => results.push(effect.postEnter(this.owner, prey, this)))


return new LogLines(...results)
return new LogLines(...results, ...effectResults)
} }


exit (prey: Creature): LogEntry { exit (prey: Creature): LogEntry {
@@ -244,12 +250,14 @@ export abstract class DefaultContainer implements Container {
this.owner.containedIn.contents.push(prey) this.owner.containedIn.contents.push(prey)
} }


const effectResults = this.effects.map(effect => prey.removeEffect(effect))

const results = [ const results = [
this.voreRelay.dispatch("onExited", this, { prey: prey }), this.voreRelay.dispatch("onExited", this, { prey: prey }),
prey.voreRelay.dispatch("onExited", this, { prey: prey }) prey.voreRelay.dispatch("onExited", this, { prey: prey })
] ]


return new LogLines(...results)
return new LogLines(...results, ...effectResults)
} }


struggle (prey: Creature): LogEntry { struggle (prey: Creature): LogEntry {
@@ -297,7 +305,7 @@ export abstract class DefaultContainer implements Container {


tickLine (user: Creature, target: Creature, args: { damage: Damage }): LogEntry { tickLine (user: Creature, target: Creature, args: { damage: Damage }): LogEntry {
const options = [ const options = [
new LogLine(`${user.name.capital} ${Words.Churns.present}} ${target.name.objective} ${this.strugglePreposition} ${user.pronouns.possessive} ${this.name} for `, args.damage.renderShort(), `.`),
new LogLine(`${user.name.capital} ${Words.Churns.singular} ${target.name.objective} ${this.strugglePreposition} ${user.pronouns.possessive} ${this.name} for `, args.damage.renderShort(), `.`),
new LogLine(`${user.name.capital.possessive} ${this.name} ${this.name.conjugate(Words.Churns)}, ${Words.Churns.present} ${target.name.objective} for `, args.damage.renderShort(), `.`), new LogLine(`${user.name.capital.possessive} ${this.name} ${this.name.conjugate(Words.Churns)}, ${Words.Churns.present} ${target.name.objective} for `, args.damage.renderShort(), `.`),
new LogLine(`${target.name.capital} ${target.name.conjugate(Words.Struggle)} ${this.strugglePreposition} ${user.name.possessive} ${Words.Slick} ${this.name} as it ${Words.Churns.singular} ${target.pronouns.objective} for `, args.damage.renderShort(), `.`) new LogLine(`${target.name.capital} ${target.name.conjugate(Words.Struggle)} ${this.strugglePreposition} ${user.name.possessive} ${Words.Slick} ${this.name} as it ${Words.Churns.singular} ${target.pronouns.objective} for `, args.damage.renderShort(), `.`)
] ]


+ 37
- 0
src/main.ts 파일 보기

@@ -6,6 +6,8 @@ declare global {
joinGeneral (item: T, endItem: T|null): Array<T>; joinGeneral (item: T, endItem: T|null): Array<T>;
/* eslint-disable-next-line */ /* eslint-disable-next-line */
unique (predicate?: (elem: T) => any): Array<T>; unique (predicate?: (elem: T) => any): Array<T>;
findResult<U> (func: (elem: T) => U, predicate: (result: U) => boolean): U | undefined;
mapUntil<U> (func: (elem: T) => U, predicate: (result: U) => boolean): Array<U>;
} }
} }


@@ -33,6 +35,41 @@ Array.prototype.unique = function<T> (predicate?: (elem: T) => any): Array<T> {
return result return result
} }


/* eslint-disable-next-line */
Array.prototype.findResult = function<T,U> (func: (elem: T) => U, predicate: (result: U) => boolean): U | undefined {
for (let i = 0; i < this.length; i++) {
const elem: T = this[i]
const result: U = func(elem)
const decision = predicate(result)
if (decision) {
return result
}
}
return undefined
}

/**
* Maps over the array until the predicate is satisfied.
*/
/* eslint-disable-next-line */
Array.prototype.mapUntil = function<T,U> (func: (elem: T) => U, predicate: (result: U) => boolean): Array<U> {
const results: Array<U> = []

for (let i = 0; i < this.length; i++) {
const elem: T = this[i]
const result: U = func(elem)
const decision = predicate(result)

results.push(result)

if (decision) {
break
}
}

return results
}

Vue.config.productionTip = false Vue.config.productionTip = false


new Vue({ new Vue({


불러오는 중...
취소
저장