Feast 2.0!
25'ten fazla konu seçemezsiniz Konular bir harf veya rakamla başlamalı, kısa çizgiler ('-') içerebilir ve en fazla 35 karakter uzunluğunda olabilir.
 
 
 
 
 

336 satır
15 KiB

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