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.
 
 
 
 
 

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