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.
 
 
 
 
 

241 lines
9.4 KiB

  1. import { Entity, Mortal, POV } from './entity'
  2. import { Damage, Actionable, Action, DevourAction, FeedAction, DigestAction, ReleaseAction, StruggleAction, Vigor } from './combat'
  3. import { LogLines, LogEntry, CompositeLog } from './interface'
  4. import { POVSolo, POVPair, POVPairArgs } from './language'
  5. export enum VoreType {
  6. Oral = "Oral Vore",
  7. Anal = "Anal Vore",
  8. Cock = "Cock Vore",
  9. Unbirth = "Unbirthing"
  10. }
  11. export interface Prey extends Mortal {
  12. preyPrefs: Set<VoreType>;
  13. bulk: number;
  14. containedIn: Container | null;
  15. }
  16. export interface Pred extends Entity {
  17. predPrefs: Set<VoreType>;
  18. containers: Array<Container>;
  19. }
  20. export interface Container extends Actionable {
  21. name: string;
  22. owner: Pred;
  23. voreTypes: Set<VoreType>;
  24. contents: Array<Prey>;
  25. capacity: number;
  26. fullness: number;
  27. canTake: (prey: Prey) => boolean;
  28. consume: (prey: Prey) => LogEntry;
  29. release: (prey: Prey) => LogEntry;
  30. struggle: (prey: Prey) => LogEntry;
  31. tick: (dt: number) => LogEntry;
  32. describe: () => LogEntry;
  33. digest: (prey: Prey) => LogEntry;
  34. absorb: (prey: Prey) => LogEntry;
  35. dispose: (preys: Prey[]) => LogEntry;
  36. actions: Array<Action>;
  37. }
  38. abstract class NormalContainer implements Container {
  39. contents: Array<Prey>
  40. abstract consumeLines: POVPair<Pred, Prey>
  41. abstract releaseLines: POVPair<Pred, Prey>
  42. abstract struggleLines: POVPair<Prey, Pred>
  43. abstract tickLines: POVSolo<Pred>
  44. abstract digestLines: POVPair<Pred, Prey>
  45. abstract absorbLines: POVPair<Pred, Prey>
  46. abstract disposeLines: POVPair<Pred, Prey>
  47. get fullness (): number {
  48. return Array.from(this.contents.values()).reduce((total: number, prey: Prey) => total + prey.bulk, 0)
  49. }
  50. canTake (prey: Prey): boolean {
  51. const fits = this.capacity - this.fullness >= prey.bulk
  52. const permitted = Array.from(this.voreTypes).every(voreType => {
  53. return prey.preyPrefs.has(voreType)
  54. })
  55. return fits && permitted
  56. }
  57. consume (prey: Prey): LogEntry {
  58. this.contents.push(prey)
  59. prey.containedIn = this
  60. return this.consumeLines.run(this.owner, prey)
  61. }
  62. release (prey: Prey): LogEntry {
  63. prey.containedIn = null
  64. this.contents = this.contents.filter(victim => victim !== prey)
  65. return this.releaseLines.run(this.owner, prey)
  66. }
  67. struggle (prey: Prey): LogEntry {
  68. return this.struggleLines.run(prey, this.owner)
  69. }
  70. tick (dt: number): LogEntry {
  71. const digested: Array<Prey> = []
  72. const absorbed: Array<Prey> = []
  73. this.contents.forEach(prey => {
  74. const start = prey.vigors[Vigor.Health]
  75. prey.takeDamage(this.damage.scale(dt / 3600))
  76. const end = prey.vigors[Vigor.Health]
  77. if (start > 0 && end <= 0) {
  78. digested.push(prey)
  79. }
  80. if (start > -100 && end <= -100) {
  81. absorbed.push(prey)
  82. }
  83. })
  84. const digestedEntries = new CompositeLog(...digested.map(prey => this.digest(prey)))
  85. const absorbedEntries = new CompositeLog(...absorbed.map(prey => this.absorb(prey)))
  86. this.contents = this.contents.filter(prey => {
  87. return prey.vigors[Vigor.Health] > -100
  88. })
  89. return new CompositeLog(this.tickLines.run(this.owner), digestedEntries, absorbedEntries)
  90. }
  91. describe (): LogEntry {
  92. const lines: Array<string> = []
  93. this.contents.forEach(prey => {
  94. lines.push(prey.toString())
  95. })
  96. return new LogLines(...lines)
  97. }
  98. digest (prey: Prey): LogEntry {
  99. return this.digestLines.run(this.owner, prey)
  100. }
  101. absorb (prey: Prey): LogEntry {
  102. return this.absorbLines.run(this.owner, prey)
  103. }
  104. dispose (preys: Prey[]): LogEntry {
  105. return new CompositeLog(...preys.map(prey => this.disposeLines.run(this.owner, prey)))
  106. }
  107. actions: Array<Action>
  108. constructor (public name: string, public owner: Pred, public voreTypes: Set<VoreType>, public capacity: number, private damage: Damage) {
  109. this.contents = []
  110. this.actions = []
  111. this.actions.push(new DevourAction(this))
  112. this.actions.push(new DigestAction(this))
  113. this.actions.push(new ReleaseAction(this))
  114. this.actions.push(new StruggleAction(this))
  115. }
  116. }
  117. export class Stomach extends NormalContainer {
  118. constructor (owner: Pred, capacity: number, damage: Damage) {
  119. super('Stomach', owner, new Set([VoreType.Oral]), capacity, damage)
  120. }
  121. consumeLines = new POVPair([
  122. [[POV.First, POV.Third], (user, target) => new LogLines(`You devour ${target.name}`)],
  123. [[POV.Third, POV.First], (user, target) => new LogLines(`${user.name.capital} munches you`)],
  124. [[POV.Third, POV.Third], (user, target) => new LogLines(`${user.name.capital} munches ${target.name.capital}`)]
  125. ])
  126. releaseLines = new POVPair([
  127. [[POV.First, POV.Third], (user, target) => new LogLines(`You hork up ${target.name}`)],
  128. [[POV.Third, POV.First], (user, target) => new LogLines(`${user.name.capital} horks you up`)],
  129. [[POV.Third, POV.Third], (user, target) => new LogLines(`${user.name.capital} horks up ${target.name.capital}`)]
  130. ])
  131. struggleLines = new POVPair([
  132. [[POV.First, POV.Third], (user, target) => new LogLines(`You claw your way out of ${target.name}`)],
  133. [[POV.Third, POV.First], (user, target) => new LogLines(`${user.name.capital} forces ${user.pronouns.possessive} way up your throat!`)],
  134. [[POV.Third, POV.Third], (user, target) => new LogLines(`${user.name.capital} escapes from the gut of ${target.name}`)]
  135. ])
  136. tickLines = new POVSolo([
  137. [[POV.First], (user) => new LogLines(`Your stomach gurgles and churns`)],
  138. [[POV.Third], (user) => new LogLines(`${user.name.capital}'s gut snarls and gurgles`)]
  139. ])
  140. digestLines = new POVPair([
  141. [[POV.First, POV.Third], (user, target) => new LogLines(`Your stomach overwhelms ${target.name}`)],
  142. [[POV.Third, POV.First], (user, target) => new LogLines(`${user.name.capital}'s stomach finishes you off`)],
  143. [[POV.Third, POV.Third], (user, target) => new LogLines(`${target.name.capital}'s squirms fade, overwhelmed by the stomach of ${user.name}`)]
  144. ])
  145. absorbLines = new POVPair([
  146. [[POV.First, POV.Third], (user, target) => new LogLines(`Your guts completely absorb ${target.name}`)],
  147. [[POV.Third, POV.First], (user, target) => new LogLines(`${user.name.capital}'s guts soak you up like water in a sponge`)],
  148. [[POV.Third, POV.Third], (user, target) => new LogLines(`${user.name.capital} finishes absorbing the remains of ${target.name}`)]
  149. ])
  150. disposeLines = new POVPair([
  151. [[POV.First, POV.Third], (user, target) => new LogLines(`Your guts completely absorb ${target.name}`)],
  152. [[POV.Third, POV.First], (user, target) => new LogLines(`${user.name.capital}'s guts soak you up like water in a sponge`)],
  153. [[POV.Third, POV.Third], (user, target) => new LogLines(`${user.name.capital} finishes absorbing the remains of ${target.name}`)]
  154. ])
  155. }
  156. export class Bowels extends NormalContainer {
  157. constructor (owner: Pred, capacity: number, damage: Damage) {
  158. super('Bowels', owner, new Set([VoreType.Anal]), capacity, damage)
  159. }
  160. consumeLines = new POVPair([
  161. [[POV.First, POV.Third], (user, target) => new LogLines(`You devour ${target.name}`)],
  162. [[POV.Third, POV.First], (user, target) => new LogLines(`${user.name.capital} munches you`)],
  163. [[POV.Third, POV.Third], (user, target) => new LogLines(`${user.name.capital} munches ${target.name.capital}`)]
  164. ])
  165. releaseLines = new POVPair([
  166. [[POV.First, POV.Third], (user, target) => new LogLines(`You hork up ${target.name}`)],
  167. [[POV.Third, POV.First], (user, target) => new LogLines(`${user.name.capital} horks you up`)],
  168. [[POV.Third, POV.Third], (user, target) => new LogLines(`${user.name.capital} horks up ${target.name.capital}`)]
  169. ])
  170. struggleLines = new POVPair([
  171. [[POV.First, POV.Third], (user, target) => new LogLines(`You claw your way out of ${target.name}`)],
  172. [[POV.Third, POV.First], (user, target) => new LogLines(`${user.name.capital} forces ${user.pronouns.possessive} way up your throat!`)],
  173. [[POV.Third, POV.Third], (user, target) => new LogLines(`${user.name.capital} escapes from the gut of ${target.name}`)]
  174. ])
  175. tickLines = new POVSolo([
  176. [[POV.First], (user) => new LogLines(`Your stomach gurgles and churns!`)],
  177. [[POV.Third], (user) => new LogLines(`${user.name.capital}'s gut snarls and gurgles`)]
  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. }