Feast 2.0!
25개 이상의 토픽을 선택하실 수 없습니다. Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

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