crunch
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.
 
 
 

387 line
13 KiB

  1. "use strict";
  2. function attack(attacker, defender, baseDamage) {
  3. let damage = Math.round((Math.random() * 0.5 - 0.25 + 1) * baseDamage);
  4. defender.health -= damage;
  5. return damage;
  6. }
  7. function isNormal(entity) {
  8. return entity.flags.grappled != true && entity.flags.shrunk != true;
  9. }
  10. function isNormalSize(entity) {
  11. return entity.flags.shrunk != true;
  12. }
  13. function isGrappled(entity) {
  14. return entity.flags.grappled == true;
  15. }
  16. function doComp(attackStat, defendStat) {
  17. return Math.random() * attackStat > Math.random() * defendStat;
  18. }
  19. function statCheck(attacker, defender, stat) {
  20. return doComp(attacker[stat], defender[stat]);
  21. }
  22. function statHealthCheck(attacker, defender, stat) {
  23. let attackerPercent = attacker.health / attacker.maxHealth;
  24. let defenderPercent = defender.health / defender.maxHealth;
  25. return doComp(attacker[stat] * attackerPercent, defender[stat] * defenderPercent);
  26. }
  27. function punchAttack(attacker) {
  28. return {
  29. name: "Punch",
  30. desc: "Punch a nerd",
  31. attack: function(defender) {
  32. return "You punch " + defender.description("the") + " for " + attack(attacker, defender, attacker.str) + " damage";
  33. },
  34. attackPlayer: function(defender) {
  35. return attacker.description("The") + " punches you for " + attack(attacker, defender, attacker.str) + " damage";
  36. }, requirements: [
  37. function(attacker, defender) { return isNormal(attacker) && isNormal(defender); }
  38. ],
  39. priority: 1,
  40. weight: function(attacker, defender) { return defender.health / defender.maxHealth; }
  41. };
  42. }
  43. function flankAttack(attacker) {
  44. return {
  45. name: "Flank",
  46. desc: "Be sneaky",
  47. attack: function(defender) {
  48. return "You run around " + defender.description("the") + " and attack for " + attack(attacker, defender, attacker.dex) + " damage";
  49. },
  50. attackPlayer: function(defender) {
  51. return attacker.description("The") + " runs past you, then turns and hits you for " + attack(attacker, defender, attacker.str) + " damage";
  52. }, requirements: [
  53. function(attacker, defender) { return isNormal(attacker) && isNormal(defender); }
  54. ],
  55. priority: 1,
  56. weight: function(attacker, defender) { return defender.health / defender.maxHealth; }
  57. };
  58. }
  59. function grapple(attacker) {
  60. return {
  61. name: "Grapple",
  62. desc: "Try to grab your opponent",
  63. attack: function(defender) {
  64. let success = statHealthCheck(attacker, defender, "str");
  65. if (success) {
  66. defender.flags.grappled = true;
  67. return "You charge at " + defender.description("the") + ", tackling them and knocking them to the ground.";
  68. } else {
  69. return "You charge at " + defender.description("the") + ", but they dodge out of the way!";
  70. }
  71. },
  72. attackPlayer: function(defender) {
  73. let success = Math.random() < 0.5;
  74. if (success) {
  75. defender.flags.grappled = true;
  76. return attacker.description("The") + " lunges at you, pinning you to the floor!";
  77. } else {
  78. return attacker.description("The") + " tries to tackle you, but you deftly avoid them.";
  79. }
  80. },
  81. requirements: [
  82. function(attacker, defender) { return isNormal(attacker) && isNormal(defender); }
  83. ],
  84. priority: 1,
  85. weight: function(attacker, defender) { return 1 - defender.health / defender.maxHealth; }
  86. };
  87. }
  88. function grappleDevour(attacker) {
  89. return {
  90. name: "Devour",
  91. desc: "Try to consume your grappled opponent",
  92. attack: function(defender) {
  93. let success = statHealthCheck(attacker, defender, "str");
  94. if (success) {
  95. attacker.stomach.feed(defender);
  96. defender.flags.grappled = false;
  97. changeMode("explore");
  98. attacker.cash += defender.cash;
  99. return "You open your jaws wide, stuffing " + defender.description("the") + "'s head into your gullet and greedily wolfing them down. Delicious. You hack up their wallet with $" + defender.cash + " inside a moment later. Nice!";
  100. } else {
  101. return "Your jaws open wide, but " + defender.description("the") + " manages to avoid becoming " + attacker.species + " chow.";
  102. }
  103. },
  104. attackPlayer: function(defender) {
  105. let success = statHealthCheck(attacker, defender, "str");
  106. if(success) {
  107. defender.flags.grappled = false;
  108. changeMode("eaten");
  109. return attacker.description("The") + " forces your head into their sloppy jaws, devouring you despite your frantic struggles. Glp.";
  110. } else {
  111. return attacker.description("The") + " tries to swallow you down, but you manage to resist their hunger.";
  112. }
  113. }, requirements: [
  114. function(attacker, defender) { return isNormal(attacker) && isGrappled(defender) && defender.flags.shrunk != true; }
  115. ], conditions: [
  116. function(attacker, defender) { return defender.prefs.prey; }
  117. ],
  118. priority: 1,
  119. };
  120. }
  121. function grappleAnalVore(attacker) {
  122. return {
  123. name: "Anal Vore",
  124. desc: "Try to shove your opponent up your ass.",
  125. attack: function(defender) {
  126. let success = statHealthCheck(attacker, defender, "str");
  127. if (success) {
  128. attacker.butt.feed(defender);
  129. defender.flags.grappled = false;
  130. attacker.cash += defender.cash;
  131. changeMode("explore");
  132. return "You shove " + defender.description("the") + " between your cheeks. Their head slips into your ass with a wet <i>shlk</i>, and the rest of their body follows suit. You moan and gasp, working them deeper and deeper...and noticing their wallet with $" + defender.cash + " on the ground. Score!";
  133. } else {
  134. return "Your grasp and shove " + defender.description("the") + ", but they manage to avoid becoming " + attacker.species + " chow.";
  135. }
  136. }, requirements: [
  137. function(attacker, defender) { return isNormal(attacker) && isGrappled(defender) && defender.flags.shrunk != true ; }
  138. ], conditions: [
  139. function(attacker, defender) { return defender.prefs.prey && defender.prefs.analVore; }
  140. ],
  141. priority: 1,
  142. };
  143. }
  144. function grappleRelease(attacker) {
  145. return {
  146. name: "Release",
  147. desc: "Release your opponent",
  148. attack: function(defender) {
  149. defender.flags.grappled = false;
  150. return "You throw " + defender.description("the") + " back, dealing " + attack(attacker, defender, attacker.str*1.5) + " damage";
  151. }, requirements: [
  152. function(attacker, defender) { return isNormal(attacker) && isGrappled(defender); }
  153. ],
  154. priority: 1,
  155. };
  156. }
  157. function grappledStruggle(attacker) {
  158. return {
  159. name: "Struggle",
  160. desc: "Try to break your opponent's pin",
  161. attack: function(defender) {
  162. let success = statHealthCheck(attacker, defender, "str");
  163. if (success) {
  164. attacker.flags.grappled = false;
  165. return "You struggle and shove " + defender.description("the") + " off of you.";
  166. } else {
  167. return "You struggle, but to no avail.";
  168. }
  169. },
  170. attackPlayer: function(defender) {
  171. let success = statHealthCheck(attacker, defender, "str");
  172. if (success) {
  173. attacker.flags.grappled = false;
  174. return "Your prey shoves you back, breaking your grapple!";
  175. } else {
  176. return "Your prey squirms, but remains pinned.";
  177. }
  178. },
  179. requirements: [
  180. function(attacker, defender) { return isGrappled(attacker) && isNormalSize(attacker) && isNormal(defender); }
  181. ],
  182. priority: 1,
  183. };
  184. }
  185. function grappledReverse(attacker) {
  186. return {
  187. name: "Reversal",
  188. desc: "Try to pin your grappler. Less likely to work than struggling.",
  189. attack: function(defender) {
  190. let success = statHealthCheck(attacker, defender, "str");
  191. if (success) {
  192. attacker.flags.grappled = false;
  193. defender.flags.grappled = true;
  194. return "You surprise " + defender.description("the") + " with a burst of strength, flipping them over and pinning them.";
  195. } else {
  196. return "You try to throw your opponent off of you, but fail.";
  197. }
  198. },
  199. attackPlayer: function(defender) {
  200. let success = statHealthCheck(attacker, defender, "str");
  201. if (success) {
  202. attacker.flags.grappled = false;
  203. defender.flags.grappled = true;
  204. return "Your prey suddenly grabs hold and flips you over, pinning you!";
  205. } else {
  206. return "Your prey tries to grab at you, but you keep them under control.";
  207. }
  208. },
  209. requirements: [
  210. function(attacker, defender) { return isGrappled(attacker) && isNormalSize(attacker) && isNormal(defender); }
  211. ],
  212. priority: 1,
  213. };
  214. }
  215. function shrunkGrapple(attacker) {
  216. return {
  217. name: "Grab",
  218. desc: "Grab this fun-sized snack",
  219. attack: function(defender) {
  220. let success = statCheck(attacker, defender, "dex") || statCheck(attacker, defender, "dex");
  221. if (success) {
  222. defender.flags.grappled = true;
  223. return "You snatch up " + defender.description("the");
  224. } else {
  225. return "You try to grab " + defender.description("the") + ", but they elude your grasp.";
  226. }
  227. },
  228. requirements: [
  229. function(attacker, defender) {
  230. return isNormal(attacker) && defender.flags.grappled != true && defender.flags.shrunk == true;
  231. }
  232. ],
  233. priority: 2
  234. };
  235. }
  236. function shrunkSwallow(attacker) {
  237. return {
  238. name: "Swallow",
  239. desc: "Swallow your prey",
  240. attack: function(defender) {
  241. changeMode("explore");
  242. attacker.stomach.feed(defender);
  243. return "With a light swallow, " + defender.description("the") + " is dragged down to your sloppy guts.";
  244. },
  245. requirements: [
  246. function(attacker, defender) {
  247. return isNormal(attacker) && defender.flags.grappled == true && defender.flags.shrunk == true;
  248. }
  249. ],
  250. priority: 2
  251. };
  252. }
  253. function shrunkStomp(attacker) {
  254. return {
  255. name: "Stomp",
  256. desc: "Stomp on your shrunken prey",
  257. attack: function(defender) {
  258. let success = statCheck(attacker, defender, "dex") || statCheck(attacker, defender, "dex") || defender.stamina == 0;
  259. defender.stamina = 0;
  260. defender.health = Math.max(0, defender.health - 50);
  261. return "Your paw comes crashing down on " + defender.description("the") + ", burying them under your heavy toes and pinning them down hard.";
  262. },
  263. requirements: [
  264. function(attacker, defender) {
  265. return isNormal(attacker) && defender.flags.grappled != true && defender.flags.shrunk == true;
  266. }
  267. ]
  268. };
  269. }
  270. function flee(attacker) {
  271. return {
  272. name: "Flee",
  273. desc: "Try to run away",
  274. attack: function(defender) {
  275. let success = statCheck(attacker, defender, "dex");
  276. if (success) {
  277. attacker.clear();
  278. changeMode("explore");
  279. return "You successfully run away.";
  280. } else {
  281. return "You can't escape!";
  282. }
  283. },
  284. requirements: [
  285. function(attacker, defender) { return isNormal(attacker) && !attacker.isGrappling; }
  286. ]
  287. };
  288. }
  289. function pass(attacker) {
  290. return {
  291. name: "Pass",
  292. desc: "You can't do anything!",
  293. attack: function(defender) {
  294. return "You do nothing.";
  295. },
  296. attackPlayer: function(defender) {
  297. return attacker.description("The") + " does nothing.";
  298. },
  299. priority: 0,
  300. };
  301. }
  302. function devourPlayer(attacker) {
  303. return {
  304. name: "Devours YOU!",
  305. desc: "You won't see this",
  306. conditions: [
  307. function(attacker, defender) { return defender.prefs.prey; }
  308. ],
  309. requirements: [
  310. function(attacker, defender) { return attacker.leering == true; }
  311. ],
  312. attackPlayer: function(defender) {
  313. changeMode("eaten");
  314. return "The voracious " + attacker.description() + " pins you down and devours you in seconds.";
  315. },
  316. priority: 1,
  317. };
  318. }
  319. function leer(attacker) {
  320. return {
  321. name: "Leer",
  322. desc: "Leer at something",
  323. attackPlayer: function(defender) {
  324. attacker.leering = true;
  325. return attacker.description("The") + " leers at you.";
  326. },
  327. requirements: [
  328. function(attacker, defender) { return attacker.leering != true && attacker.flags.grappled != true; }
  329. ],
  330. priority: 1,
  331. };
  332. }
  333. function poke(attacker) {
  334. return {
  335. name: "Poke",
  336. desc: "Poke a nerd",
  337. attackPlayer: function(defender) {
  338. return attacker.description("The") + " pokes you on the snout for " + attack(attacker, defender, 1e12) + " damage";
  339. },
  340. priority: 1,
  341. };
  342. }
  343. function digestPlayerStomach(predator,damage=20) {
  344. return {
  345. digest: function(player) {
  346. attack(predator, player, damage);
  347. return predator.description("The") + "'s stomach grinds over your body, swiftly digesting you.";
  348. },
  349. priority: 1,
  350. };
  351. }
  352. function instakillPlayerStomach(pedator) {
  353. return {
  354. digest: function(player) {
  355. player.health = -100;
  356. return "The stomach walls churn, clench, and swiftly crush you into nothingnes.";
  357. },
  358. priority: 1,
  359. weight: function(attacker, defender) { return 1/3; },
  360. };
  361. }