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.
 
 
 
 
 

345 line
15 KiB

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