Feast 2.0!
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

365 line
16 KiB

  1. import { Entity, Mortal, POV, Creature } from './entity'
  2. import { Damage, DamageType, Stats, Actionable, Action, Vigor, VoreStats } from './combat'
  3. import { LogLines, LogEntry, CompositeLog, LogLine } from './interface'
  4. import { Noun, Pronoun, POVPair, POVPairArgs, ImproperNoun, POVSolo, TextLike, Verb, Word } from './language'
  5. import { DigestAction, DevourAction, ReleaseAction, StruggleAction } from './combat/actions'
  6. import * as Words from './words'
  7. export enum VoreType {
  8. Oral = "Oral Vore",
  9. Anal = "Anal Vore",
  10. Cock = "Cock Vore",
  11. Unbirth = "Unbirthing"
  12. }
  13. export abstract class Vore implements Mortal {
  14. abstract name: Noun;
  15. abstract title: TextLike;
  16. abstract desc: TextLike;
  17. abstract kind: Noun;
  18. abstract pronouns: Pronoun;
  19. abstract perspective: POV;
  20. abstract vigors: {[key in Vigor]: number};
  21. abstract maxVigors: {[key in Vigor]: number};
  22. abstract disabled: boolean;
  23. abstract resistances: Map<DamageType, number>;
  24. abstract takeDamage (damage: Damage): LogEntry;
  25. abstract stats: Stats;
  26. abstract baseStats: Stats;
  27. abstract status: string;
  28. digested = false;
  29. abstract preyPrefs: Set<VoreType>;
  30. abstract voreStats: VoreStats;
  31. abstract containedIn: Container | null;
  32. abstract predPrefs: Set<VoreType>;
  33. abstract containers: Array<VoreContainer>;
  34. abstract otherContainers: Array<Container>;
  35. destroy (): LogEntry {
  36. const lines = new POVSolo<Vore>([
  37. [[POV.First], (target: Vore) => new LogLine('You die!')],
  38. [[POV.Third], (target: Vore) => new LogLine(`${target.name.capital} dies!`)]
  39. ])
  40. this.containers.map(container => {
  41. container.contents.map(prey => {
  42. prey.containedIn = this.containedIn
  43. if (this.containedIn !== null) {
  44. this.containedIn.contents.push(prey)
  45. }
  46. })
  47. })
  48. return lines.run(this)
  49. }
  50. }
  51. export interface Container extends Actionable {
  52. name: Noun;
  53. owner: Vore;
  54. voreTypes: Set<VoreType>;
  55. contents: Array<Vore>;
  56. capacity: number;
  57. fullness: number;
  58. canTake: (prey: Vore) => boolean;
  59. consume: (prey: Vore) => LogEntry;
  60. release: (prey: Vore) => LogEntry;
  61. struggle: (prey: Vore) => LogEntry;
  62. describe: () => LogEntry;
  63. consumeVerb: Verb;
  64. releaseVerb: Verb;
  65. struggleVerb: Verb;
  66. }
  67. export abstract class NormalContainer implements Container {
  68. public name: Noun
  69. contents: Array<Vore> = []
  70. actions: Array<Action> = []
  71. abstract consumeLines: POVPair<Vore, Vore>
  72. abstract releaseLines: POVPair<Vore, Vore>
  73. abstract struggleLines: POVPair<Vore, Vore>
  74. abstract consumeVerb: Verb
  75. abstract releaseVerb: Verb
  76. abstract struggleVerb: Verb
  77. get fullness (): number {
  78. return Array.from(this.contents.values()).reduce((total: number, prey: Vore) => total + prey.voreStats.Bulk, 0)
  79. }
  80. canTake (prey: Vore): boolean {
  81. const fits = this.capacity - this.fullness >= prey.voreStats.Bulk
  82. const permitted = Array.from(this.voreTypes).every(voreType => {
  83. return prey.preyPrefs.has(voreType)
  84. })
  85. return fits && permitted
  86. }
  87. consume (prey: Vore): LogEntry {
  88. this.contents.push(prey)
  89. prey.containedIn = this
  90. return this.consumeLines.run(this.owner, prey)
  91. }
  92. release (prey: Vore): LogEntry {
  93. prey.containedIn = this.owner.containedIn
  94. this.contents = this.contents.filter(victim => victim !== prey)
  95. if (this.owner.containedIn !== null) {
  96. this.owner.containedIn.contents.push(prey)
  97. }
  98. return this.releaseLines.run(this.owner, prey)
  99. }
  100. struggle (prey: Vore): LogEntry {
  101. return this.struggleLines.run(prey, this.owner)
  102. }
  103. describe (): LogEntry {
  104. const lines: Array<string> = []
  105. this.contents.forEach(prey => {
  106. lines.push(prey.toString())
  107. })
  108. return new LogLine(...lines)
  109. }
  110. constructor (name: Noun, public owner: Vore, public voreTypes: Set<VoreType>, public capacity: number) {
  111. this.name = name.all
  112. this.actions.push(new DevourAction(this))
  113. this.actions.push(new ReleaseAction(this))
  114. this.actions.push(new StruggleAction(this))
  115. }
  116. }
  117. export interface VoreContainer extends Container {
  118. digested: Array<Vore>;
  119. tick: (dt: number) => LogEntry;
  120. digest: (prey: Vore) => LogEntry;
  121. absorb: (prey: Vore) => LogEntry;
  122. dispose: (preys: Vore[]) => LogEntry;
  123. }
  124. export abstract class NormalVoreContainer extends NormalContainer implements VoreContainer {
  125. digested: Array<Vore> = []
  126. abstract tickLines: POVPairArgs<Vore, Vore, { damage: Damage }>
  127. abstract digestLines: POVPair<Vore, Vore>
  128. abstract absorbLines: POVPair<Vore, Vore>
  129. abstract disposeLines: POVPair<Vore, Vore>
  130. consumeVerb = new Verb('devour')
  131. releaseVerb = new Verb('release', 'releases', 'releasing', 'released')
  132. struggleVerb = new Verb('struggle', 'struggles', 'struggling', 'struggled')
  133. tick (dt: number): LogEntry {
  134. const justDigested: Array<Vore> = []
  135. const scaled = this.damage.scale(dt / 60)
  136. const damageResults: Array<LogEntry> = []
  137. this.contents.forEach(prey => {
  138. damageResults.push(prey.takeDamage(scaled))
  139. if (prey.vigors[Vigor.Health] <= 0) {
  140. prey.digested = true
  141. this.digested.push(prey)
  142. justDigested.push(prey)
  143. }
  144. })
  145. const tickedEntries = new LogLines(...this.contents.map(prey => this.tickLines.run(this.owner, prey, { damage: scaled })))
  146. const digestedEntries = new LogLines(...justDigested.map(prey => this.digest(prey)))
  147. this.contents = this.contents.filter(prey => {
  148. return prey.vigors[Vigor.Health] > 0
  149. })
  150. return new LogLines(tickedEntries, new LogLines(...damageResults), digestedEntries)
  151. }
  152. digest (prey: Vore): LogEntry {
  153. return this.digestLines.run(this.owner, prey)
  154. }
  155. absorb (prey: Vore): LogEntry {
  156. return this.absorbLines.run(this.owner, prey)
  157. }
  158. dispose (preys: Vore[]): LogEntry {
  159. return new CompositeLog(...preys.map(prey => this.disposeLines.run(this.owner, prey)))
  160. }
  161. constructor (name: Noun, owner: Vore, voreTypes: Set<VoreType>, capacity: number, private damage: Damage) {
  162. super(name, owner, voreTypes, capacity)
  163. this.name = name
  164. this.actions.push(new DigestAction(this))
  165. }
  166. }
  167. abstract class InnerContainer extends NormalVoreContainer {
  168. release (prey: Vore): LogEntry {
  169. prey.containedIn = this.escape
  170. this.contents = this.contents.filter(victim => victim !== prey)
  171. return this.releaseLines.run(this.owner, prey)
  172. }
  173. constructor (name: Noun, owner: Vore, voreTypes: Set<VoreType>, capacity: number, damage: Damage, private escape: VoreContainer) {
  174. super(name, owner, voreTypes, capacity, damage)
  175. this.actions = []
  176. this.actions.push(new DigestAction(this))
  177. this.actions.push(new StruggleAction(this))
  178. }
  179. }
  180. export class Stomach extends NormalVoreContainer {
  181. constructor (owner: Vore, capacity: number, damage: Damage) {
  182. super(new ImproperNoun('stomach', 'stomachs').all, owner, new Set([VoreType.Oral]), capacity, damage)
  183. }
  184. consumeLines = new POVPair<Vore, Vore>([
  185. [[POV.First, POV.Third], (user, target) => new LogLine(`You devour ${target.name}`)],
  186. [[POV.Third, POV.First], (user, target) => new LogLine(`${user.name.capital} munches you`)],
  187. [[POV.Third, POV.Third], (user, target) => new LogLine(`${user.name.capital} munches ${target.name}`)]
  188. ])
  189. releaseLines = new POVPair<Vore, Vore>([
  190. [[POV.First, POV.Third], (user, target) => new LogLine(`You hork up ${target.name}`)],
  191. [[POV.Third, POV.First], (user, target) => new LogLine(`${user.name.capital} horks you up`)],
  192. [[POV.Third, POV.Third], (user, target) => new LogLine(`${user.name.capital} horks up ${target.name}`)]
  193. ])
  194. struggleLines = new POVPair<Vore, Vore>([
  195. [[POV.First, POV.Third], (user, target) => new LogLine(`You claw your way out of ${target.name}`)],
  196. [[POV.Third, POV.First], (user, target) => new LogLine(`${user.name.capital} forces ${user.pronouns.possessive} way up your throat!`)],
  197. [[POV.Third, POV.Third], (user, target) => new LogLine(`${user.name.capital} escapes from the gut of ${target.name}`)]
  198. ])
  199. tickLines = new POVPairArgs<Vore, Vore, { damage: Damage }>([
  200. [[POV.First, POV.Third], (user, target, args) => new LogLine(`Your stomach ${Words.Churns.singular} ${target.name} for `, args.damage.renderShort())],
  201. [[POV.Third, POV.First], (user, target, args) => new LogLine(`${user.name.capital}'s stomach ${Words.Churns.singular} you for `, args.damage.renderShort())],
  202. [[POV.Third, POV.Third], (user, target, args) => new LogLine(`${user.name.capital} ${Words.Churns.singular} ${target.name} for `, args.damage.renderShort())]
  203. ])
  204. digestLines = new POVPair<Vore, Vore>([
  205. [[POV.First, POV.Third], (user, target) => new LogLine(`Your stomach ${Words.Digests.singular} ${target.name}`)],
  206. [[POV.Third, POV.First], (user, target) => new LogLine(`${user.name.capital}'s stomach ${Words.Digests.singular} you`)],
  207. [[POV.Third, POV.Third], (user, target) => new LogLine(`${target.name.capital}'s ${Words.Struggles.present} fades as the ${target.kind.all} is ${Words.Digests.past} by the ${user.kind.all}'s ${this.name}.`)]
  208. ])
  209. absorbLines = new POVPair<Vore, Vore>([
  210. [[POV.First, POV.Third], (user, target) => new LogLine(`Your guts completely absorb ${target.name}`)],
  211. [[POV.Third, POV.First], (user, target) => new LogLine(`${user.name.capital}'s guts soak you up like water in a sponge`)],
  212. [[POV.Third, POV.Third], (user, target) => new LogLine(`${user.name.capital} finishes absorbing the remains of ${target.name}`)]
  213. ])
  214. disposeLines = new POVPair<Vore, Vore>([
  215. [[POV.First, POV.Third], (user, target) => new LogLine(`Your guts completely absorb ${target.name}`)],
  216. [[POV.Third, POV.First], (user, target) => new LogLine(`${user.name.capital}'s guts soak you up like water in a sponge`)],
  217. [[POV.Third, POV.Third], (user, target) => new LogLine(`${user.name.capital} finishes absorbing the remains of ${target.name}`)]
  218. ])
  219. }
  220. export class InnerStomach extends InnerContainer {
  221. constructor (owner: Vore, capacity: number, damage: Damage, escape: VoreContainer) {
  222. super(new ImproperNoun('inner stomach', 'inner stomachs').all, owner, new Set([VoreType.Oral]), capacity, damage, escape)
  223. }
  224. consumeLines = new POVPair([
  225. [[POV.First, POV.Third], (user, target) => new LogLine(`You devour ${target.name}`)],
  226. [[POV.Third, POV.First], (user, target) => new LogLine(`${user.name.capital} munches you`)],
  227. [[POV.Third, POV.Third], (user, target) => new LogLine(`${user.name.capital} munches ${target.name.capital}`)]
  228. ])
  229. releaseLines = new POVPair([
  230. [[POV.First, POV.Third], (user, target) => new LogLine(`You hork up ${target.name}`)],
  231. [[POV.Third, POV.First], (user, target) => new LogLine(`${user.name.capital} horks you up`)],
  232. [[POV.Third, POV.Third], (user, target) => new LogLine(`${user.name.capital} horks up ${target.name.capital}`)]
  233. ])
  234. struggleLines = new POVPair([
  235. [[POV.First, POV.Third], (user, target) => new LogLine(`You claw your way out of ${target.name}`)],
  236. [[POV.Third, POV.First], (user, target) => new LogLine(`${user.name.capital} forces ${user.pronouns.possessive} way up your throat!`)],
  237. [[POV.Third, POV.Third], (user, target) => new LogLine(`${user.name.capital} escapes from the gut of ${target.name}`)]
  238. ])
  239. tickLines = new POVPairArgs<Vore, Vore, { damage: Damage }>([
  240. [[POV.First, POV.Third], (user, target, args) => new LogLine(`Your stomach gurgles ${target.name} for `, args.damage.renderShort())],
  241. [[POV.Third, POV.First], (user, target, args) => new LogLine(`${user.name.capital}'s stomach churns you for `, args.damage.renderShort())],
  242. [[POV.Third, POV.Third], (user, target, args) => new LogLine(`${user.name.capital} churns ${target.name} for `, args.damage.renderShort())]
  243. ])
  244. digestLines = new POVPair([
  245. [[POV.First, POV.Third], (user, target) => new LogLine(`Your stomach overwhelms ${target.name}`)],
  246. [[POV.Third, POV.First], (user, target) => new LogLine(`${user.name.capital}'s stomach finishes you off`)],
  247. [[POV.Third, POV.Third], (user, target) => new LogLine(`${target.name.capital}'s squirms fade, overwhelmed by the stomach of ${user.name}`)]
  248. ])
  249. absorbLines = new POVPair([
  250. [[POV.First, POV.Third], (user, target) => new LogLine(`Your guts completely absorb ${target.name}`)],
  251. [[POV.Third, POV.First], (user, target) => new LogLine(`${user.name.capital}'s guts soak you up like water in a sponge`)],
  252. [[POV.Third, POV.Third], (user, target) => new LogLine(`${user.name.capital} finishes absorbing the remains of ${target.name}`)]
  253. ])
  254. disposeLines = new POVPair([
  255. [[POV.First, POV.Third], (user, target) => new LogLine(`Your guts completely absorb ${target.name}`)],
  256. [[POV.Third, POV.First], (user, target) => new LogLine(`${user.name.capital}'s guts soak you up like water in a sponge`)],
  257. [[POV.Third, POV.Third], (user, target) => new LogLine(`${user.name.capital} finishes absorbing the remains of ${target.name}`)]
  258. ])
  259. }
  260. export class Bowels extends NormalVoreContainer {
  261. constructor (owner: Vore, capacity: number, damage: Damage) {
  262. super(new ImproperNoun('bowel', 'bowels').plural, owner, new Set([VoreType.Anal]), capacity, damage)
  263. }
  264. consumeLines = new POVPair([
  265. [[POV.First, POV.Third], (user, target) => new LogLine(`You force ${target.name} into your bowels`)],
  266. [[POV.Third, POV.First], (user, target) => new LogLine(`${user.name.capital} works you into ${user.pronouns.possessive} ass`)],
  267. [[POV.Third, POV.Third], (user, target) => new LogLine(`${user.name.capital} anal-vores ${target.name.capital}`)]
  268. ])
  269. releaseLines = new POVPair([
  270. [[POV.First, POV.Third], (user, target) => new LogLine(`You let out ${target.name}`)],
  271. [[POV.Third, POV.First], (user, target) => new LogLine(`${user.name.capital} lets you out `)],
  272. [[POV.Third, POV.Third], (user, target) => new LogLine(`${user.name.capital} lets out ${target.name.capital}`)]
  273. ])
  274. struggleLines = new POVPair([
  275. [[POV.First, POV.Third], (user, target) => new LogLine(`You claw your way out of ${target.name}`)],
  276. [[POV.Third, POV.First], (user, target) => new LogLine(`${user.name.capital} forces ${user.pronouns.possessive} way out your rump!`)],
  277. [[POV.Third, POV.Third], (user, target) => new LogLine(`${user.name.capital} escapes from the bowels of ${target.name}`)]
  278. ])
  279. tickLines = new POVPairArgs<Vore, Vore, { damage: Damage }>([
  280. [[POV.First, POV.Third], (user, target, args) => new LogLine(`Your bowels gurgle ${target.name} for `, args.damage.renderShort())],
  281. [[POV.Third, POV.First], (user, target, args) => new LogLine(`${user.name.capital}'s bowels churn you for `, args.damage.renderShort())],
  282. [[POV.Third, POV.Third], (user, target, args) => new LogLine(`${target.name.capital} churns ${user.name} for `, args.damage.renderShort())]
  283. ])
  284. digestLines = new POVPair([
  285. [[POV.First, POV.Third], (user, target) => new LogLine(`Your bowels overwhelm ${target.name}`)],
  286. [[POV.Third, POV.First], (user, target) => new LogLine(`${user.name.capital}'s bowels finish you off`)],
  287. [[POV.Third, POV.Third], (user, target) => new LogLine(`${target.name.capital}'s squirms fade, overwhelmed by the bowels of ${user.name}`)]
  288. ])
  289. absorbLines = new POVPair([
  290. [[POV.First, POV.Third], (user, target) => new LogLine(`Your guts completely absorb ${target.name}`)],
  291. [[POV.Third, POV.First], (user, target) => new LogLine(`${user.name.capital}'s guts soak you up like water in a sponge`)],
  292. [[POV.Third, POV.Third], (user, target) => new LogLine(`${user.name.capital} finishes absorbing the remains of ${target.name}`)]
  293. ])
  294. disposeLines = new POVPair([
  295. [[POV.First, POV.Third], (user, target) => new LogLine(`Your guts completely absorb ${target.name}`)],
  296. [[POV.Third, POV.First], (user, target) => new LogLine(`${user.name.capital}'s guts soak you up like water in a sponge`)],
  297. [[POV.Third, POV.Third], (user, target) => new LogLine(`${user.name.capital} finishes absorbing the remains of ${target.name}`)]
  298. ])
  299. }