crunch
Du kan inte välja fler än 25 ämnen Ämnen måste starta med en bokstav eller siffra, kan innehålla bindestreck ('-') och vara max 35 tecken långa.
 
 
 

621 lines
23 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 = 0.5 + 0.5 * attacker.health / attacker.maxHealth;
  24. let defenderPercent = 0.5 + 0.5 * defender.health / defender.maxHealth;
  25. if (attacker.stamina <= 0)
  26. attackerPercent /= 2;
  27. if (defender.stamina <= 0)
  28. defenderPercent /= 2;
  29. return doComp(attacker[stat] * attackerPercent, defender[stat] * defenderPercent);
  30. }
  31. function punchAttack(attacker) {
  32. return {
  33. name: "Punch",
  34. desc: "Punch a nerd",
  35. attack: function(defender) {
  36. return ["You punch " + defender.description("the") + " for " + attack(attacker, defender, attacker.str) + " damage"];
  37. },
  38. attackPlayer: function(defender) {
  39. return [attacker.description("The") + " punches you for " + attack(attacker, defender, attacker.str) + " damage"];
  40. }, requirements: [
  41. function(attacker, defender) { return isNormal(attacker) && isNormal(defender); }
  42. ],
  43. priority: 1,
  44. weight: function(attacker, defender) { return defender.health / defender.maxHealth; }
  45. };
  46. }
  47. function flankAttack(attacker) {
  48. return {
  49. name: "Flank",
  50. desc: "Be sneaky",
  51. attack: function(defender) {
  52. return ["You run around " + defender.description("the") + " and attack for " + attack(attacker, defender, attacker.dex) + " damage"];
  53. },
  54. attackPlayer: function(defender) {
  55. return [attacker.description("The") + " runs past you, then turns and hits you for " + attack(attacker, defender, attacker.str) + " damage"];
  56. }, requirements: [
  57. function(attacker, defender) { return isNormal(attacker) && isNormal(defender); }
  58. ],
  59. priority: 1,
  60. weight: function(attacker, defender) { return defender.health / defender.maxHealth; }
  61. };
  62. }
  63. function grapple(attacker, weightFactor = 1) {
  64. return {
  65. name: "Grapple",
  66. desc: "Try to grab your opponent",
  67. attack: function(defender) {
  68. let success = statHealthCheck(attacker, defender, "str");
  69. if (success) {
  70. attacker.changeStamina(-20);
  71. defender.changeStamina(-20);
  72. defender.flags.grappled = true;
  73. return ["You charge at " + defender.description("the") + ", tackling them and knocking them to the ground."];
  74. } else {
  75. attacker.changeStamina(-20);
  76. return ["You charge at " + defender.description("the") + ", but they dodge out of the way!"];
  77. }
  78. },
  79. attackPlayer: function(defender) {
  80. let success = statHealthCheck(attacker, defender, "str");
  81. if (success) {
  82. attacker.changeStamina(-20);
  83. defender.changeStamina(-20);
  84. defender.flags.grappled = true;
  85. return [attacker.description("The") + " lunges at you, pinning you to the floor!"];
  86. } else {
  87. return [attacker.description("The") + " tries to tackle you, but you deftly avoid them."];
  88. }
  89. },
  90. requirements: [
  91. function(attacker, defender) { return isNormal(attacker) && isNormal(defender); },
  92. function(attacker, defender) { return defender.prefs.grapple; }
  93. ],
  94. priority: 1,
  95. weight: function(attacker, defender) { return weightFactor - weightFactor * defender.health / defender.maxHealth; }
  96. };
  97. }
  98. function grappleSubdue(attacker) {
  99. return {
  100. name: "Subdue",
  101. desc: "Try to subdue your opponent",
  102. attack: function(defender) {
  103. attacker.changeStamina(-10);
  104. defender.changeStamina(-30);
  105. return ["You beat on " + defender.description("the") + " for " + attack(attacker, defender, attacker.str * 2) + " damage."];
  106. },
  107. attackPlayer: function(defender) {
  108. attacker.changeStamina(-10);
  109. defender.changeStamina(-30);
  110. return [attacker.description("The") + " beats you for " + attack(attacker, defender, attacker.str * 2) + " damage"];
  111. },
  112. requirements: [
  113. function(attacker, defender) {
  114. return isNormal(attacker) && isGrappled(defender);
  115. }
  116. ],
  117. priority: 1,
  118. weight: function(attacker, defender) { return defender.health / defender.maxHealth; }
  119. };
  120. }
  121. function grappleDevour(attacker) {
  122. return {
  123. name: "Devour",
  124. desc: "Try to consume your grappled opponent",
  125. attack: function(defender) {
  126. let success = statHealthCheck(attacker, defender, "str");
  127. if (success) {
  128. attacker.changeStamina(-10);
  129. defender.changeStamina(-25);
  130. attacker.stomach.feed(defender);
  131. defender.flags.grappled = false;
  132. changeMode("explore");
  133. attacker.cash += defender.cash;
  134. return ["You open your jaws wide, stuffing " + defender.description("the") + "'s head into your gullet and greedily wolfing them down. Delicious.", newline, "You hack up their wallet with $" + defender.cash + " inside a moment later. Nice!"];
  135. } else {
  136. attacker.changeStamina(-25);
  137. defender.changeStamina(-10);
  138. return ["Your jaws open wide, but " + defender.description("the") + " manages to avoid becoming " + attacker.species + " chow."];
  139. }
  140. },
  141. attackPlayer: function(defender) {
  142. let success = statHealthCheck(attacker, defender, "str");
  143. if(success) {
  144. defender.flags.grappled = false;
  145. changeMode("eaten");
  146. return [attacker.description("The") + " forces your head into their sloppy jaws, devouring you despite your frantic struggles. Glp."];
  147. } else {
  148. return [attacker.description("The") + " tries to swallow you down, but you manage to resist their hunger."];
  149. }
  150. }, requirements: [
  151. function(attacker, defender) { return isNormal(attacker) && isGrappled(defender) && defender.flags.shrunk != true; }
  152. ], conditions: [
  153. function(attacker, defender) { return defender.prefs.prey; },
  154. function(attacker, defender) { return defender.prefs.vore.oral > 0; }
  155. ],
  156. priority: 1,
  157. weight: function(attacker, defender) { return 1 - defender.health / defender.maxHealth; }
  158. };
  159. }
  160. function grappledDevour(attacker) {
  161. return {
  162. name: "Devour",
  163. desc: "Try to consume your grappling opponent",
  164. attackPlayer: function(defender) {
  165. let success = statHealthCheck(attacker, defender, "str");
  166. if(success) {
  167. defender.flags.grappled = false;
  168. changeMode("eaten");
  169. return [attacker.description("The") + " breaks your pin and crams your upper body into their maw, swallowing you down in seconds."];
  170. } else {
  171. attacker.changeStamina(-10);
  172. return [attacker.description("The") + " thrashes at you in an attempt to devour you, but you avoid their jaws."];
  173. }
  174. }, requirements: [
  175. function(attacker, defender) { return isGrappled(attacker) && isNormal(defender) && attacker.flags.shrunk != true; }
  176. ], conditions: [
  177. function(attacker, defender) { return defender.prefs.prey; },
  178. function(attacker, defender) { return defender.prefs.vore.oral > 0; }
  179. ],
  180. priority: 1,
  181. };
  182. }
  183. function grappleAnalVore(attacker) {
  184. return {
  185. name: "Anal Vore",
  186. desc: "Try to shove your opponent up your ass.",
  187. attack: function(defender) {
  188. let success = statHealthCheck(attacker, defender, "str");
  189. if (success) {
  190. attacker.changeStamina(-10);
  191. defender.changeStamina(-25);
  192. attacker.bowels.feed(defender);
  193. defender.flags.grappled = false;
  194. attacker.cash += defender.cash;
  195. changeMode("explore");
  196. 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.", newline, "You notice their wallet with $" + defender.cash + " lying on the ground. Score!"];
  197. } else {
  198. attacker.changeStamina(-25);
  199. defender.changeStamina(-10);
  200. return ["Your grasp and shove " + defender.description("the") + ", but they manage to avoid becoming " + attacker.species + " chow."];
  201. }
  202. }, requirements: [
  203. function(attacker, defender) { return isNormal(attacker) && isGrappled(defender) && defender.flags.shrunk != true ; }
  204. ], conditions: [
  205. function(attacker, defender) { return defender.prefs.prey; },
  206. function(attacker, defender) { return defender.prefs.vore.anal > 0; },
  207. function(attacker, defender) { return attacker.prefs.pred.anal; }
  208. ],
  209. priority: 1,
  210. };
  211. }
  212. function grappleCockVore(attacker) {
  213. return {
  214. name: "Cock Vore",
  215. desc: "Try to shove your opponent down your shaft.",
  216. attack: function(defender) {
  217. let success = statHealthCheck(attacker, defender, "str");
  218. if (success) {
  219. attacker.changeStamina(-10);
  220. defender.changeStamina(-25);
  221. attacker.balls.feed(defender);
  222. defender.flags.grappled = false;
  223. attacker.cash += defender.cash;
  224. changeMode("explore");
  225. return ["You gasp with pleasure as you shove " + defender.description("the") + "'s head into your throbbing shaft, quickly pulling them down into your churning balls.", newline, "You notice their wallet with $" + defender.cash + " lying on the ground. Score!"];
  226. } else {
  227. attacker.changeStamina(-25);
  228. defender.changeStamina(-10);
  229. return ["Your grasp and shove " + defender.description("the") + ", but they manage to avoid becoming " + attacker.species + " chow."];
  230. }
  231. }, requirements: [
  232. function(attacker, defender) { return isNormal(attacker) && isGrappled(defender) && defender.flags.shrunk != true ; }
  233. ], conditions: [
  234. function(attacker, defender) { return defender.prefs.prey; },
  235. function(attacker, defender) { return defender.prefs.vore.cock > 0; },
  236. function(attacker, defender) { return attacker.prefs.pred.cock; }
  237. ],
  238. priority: 1,
  239. };
  240. }
  241. function grappleUnbirth(attacker) {
  242. return {
  243. name: "Unbirth",
  244. desc: "Try to shove your opponent into your snatch.",
  245. attack: function(defender) {
  246. let success = statHealthCheck(attacker, defender, "str");
  247. if (success) {
  248. attacker.changeStamina(-10);
  249. defender.changeStamina(-25);
  250. attacker.womb.feed(defender);
  251. defender.flags.grappled = false;
  252. attacker.cash += defender.cash;
  253. changeMode("explore");
  254. return [defender.description("The") + " is powerless to resist as you stuff them head-first into your dripping sex. Ripples of pleasure roll up your spine as rhythmic clenches pull them deeper and deeper, eventually imprisoning your prey into your hot, tight womb.", newline, "You notice their wallet with $" + defender.cash + " lying on the ground. Score!"];
  255. } else {
  256. attacker.changeStamina(-25);
  257. defender.changeStamina(-10);
  258. return ["Your grasp and shove " + defender.description("the") + ", but they manage to avoid becoming a " + attacker.species + " toy."];
  259. }
  260. }, requirements: [
  261. function(attacker, defender) { return isNormal(attacker) && isGrappled(defender) && defender.flags.shrunk != true ; }
  262. ], conditions: [
  263. function(attacker, defender) { return defender.prefs.prey; },
  264. function(attacker, defender) { return defender.prefs.vore.unbirth > 0; },
  265. function(attacker, defender) { return attacker.prefs.pred.unbirth; }
  266. ],
  267. priority: 1,
  268. };
  269. }
  270. function grappleBreastVore(attacker) {
  271. return {
  272. name: "Breast Vore",
  273. desc: "Try to shove your opponent into your breasts.",
  274. attack: function(defender) {
  275. let success = statHealthCheck(attacker, defender, "str");
  276. if (success) {
  277. attacker.changeStamina(-10);
  278. defender.changeStamina(-25);
  279. attacker.breasts.feed(defender);
  280. defender.flags.grappled = false;
  281. attacker.cash += defender.cash;
  282. changeMode("explore");
  283. return ["You grin and ram " + defender.description("the") + " against your bare chest, enveloping their head in your greedy breast! Before they can recover from the surprise, you swiftly stuff the rest of their squirming body inside.", newline, "You notice their wallet with $" + defender.cash + " lying on the ground. Score!"];
  284. } else {
  285. attacker.changeStamina(-25);
  286. defender.changeStamina(-10);
  287. return ["Your grasp and shove " + defender.description("the") + ", but they manage to avoid becoming " + attacker.species + " chow."];
  288. }
  289. }, requirements: [
  290. function(attacker, defender) { return isNormal(attacker) && isGrappled(defender) && defender.flags.shrunk != true ; }
  291. ], conditions: [
  292. function(attacker, defender) { return defender.prefs.prey; },
  293. function(attacker, defender) { return defender.prefs.vore.breast > 0; },
  294. function(attacker, defender) { return attacker.prefs.pred.breast; }
  295. ],
  296. priority: 1,
  297. };
  298. }
  299. function grappleRelease(attacker) {
  300. return {
  301. name: "Release",
  302. desc: "Release your opponent",
  303. attack: function(defender) {
  304. defender.flags.grappled = false;
  305. defender.changeStamina(-15);
  306. return ["You throw " + defender.description("the") + " back, dealing " + attack(attacker, defender, attacker.str*1.5) + " damage"];
  307. }, requirements: [
  308. function(attacker, defender) { return isNormal(attacker) && isGrappled(defender); }
  309. ],
  310. priority: 1,
  311. };
  312. }
  313. function grappledStruggle(attacker) {
  314. return {
  315. name: "Struggle",
  316. desc: "Try to break your opponent's pin",
  317. attack: function(defender) {
  318. let success = statHealthCheck(attacker, defender, "str");
  319. if (success) {
  320. attacker.changeStamina(-5);
  321. defender.changeStamina(-10);
  322. attacker.flags.grappled = false;
  323. return ["You struggle and shove " + defender.description("the") + " off of you."];
  324. } else {
  325. attacker.changeStamina(-10);
  326. defender.changeStamina(-5);
  327. return ["You struggle, but to no avail."];
  328. }
  329. },
  330. attackPlayer: function(defender) {
  331. let success = statHealthCheck(attacker, defender, "str");
  332. if (success) {
  333. attacker.changeStamina(-5);
  334. defender.changeStamina(-10);
  335. attacker.flags.grappled = false;
  336. return ["Your prey shoves you back, breaking your grapple!"];
  337. } else {
  338. attacker.changeStamina(-10);
  339. defender.changeStamina(-5);
  340. return ["Your prey squirms, but remains pinned."];
  341. }
  342. },
  343. requirements: [
  344. function(attacker, defender) { return isGrappled(attacker) && isNormalSize(attacker) && isNormal(defender); }
  345. ],
  346. priority: 1,
  347. };
  348. }
  349. function grappledReverse(attacker) {
  350. return {
  351. name: "Reversal",
  352. desc: "Try to pin your grappler. Less likely to work than struggling.",
  353. attack: function(defender) {
  354. let success = statHealthCheck(attacker, defender, "str");
  355. if (success) {
  356. attacker.changeStamina(-5);
  357. defender.changeStamina(-15);
  358. attacker.flags.grappled = false;
  359. defender.flags.grappled = true;
  360. return ["You surprise " + defender.description("the") + " with a burst of strength, flipping them over and pinning them."];
  361. } else {
  362. attacker.changeStamina(-15);
  363. return ["You try to throw your opponent off of you, but fail."];
  364. }
  365. },
  366. attackPlayer: function(defender) {
  367. let success = statHealthCheck(attacker, defender, "str");
  368. if (success) {
  369. attacker.changeStamina(-5);
  370. defender.changeStamina(-15);
  371. attacker.flags.grappled = false;
  372. defender.flags.grappled = true;
  373. return ["Your prey suddenly grabs hold and flips you over, pinning you!"];
  374. } else {
  375. attacker.changeStamina(-15);
  376. return ["Your prey tries to grab at you, but you keep them under control."];
  377. }
  378. },
  379. conditions: [
  380. function(attacker, defender) { return defender.flags.grapple; }
  381. ],
  382. requirements: [
  383. function(attacker, defender) { return isGrappled(attacker) && isNormalSize(attacker) && isNormal(defender); },
  384. function(attacker, defender) { return attacker.flags.grappled; }
  385. ],
  386. priority: 1,
  387. };
  388. }
  389. function shrunkGrapple(attacker) {
  390. return {
  391. name: "Grab",
  392. desc: "Grab this fun-sized snack",
  393. attack: function(defender) {
  394. let success = statCheck(attacker, defender, "dex") || statCheck(attacker, defender, "dex");
  395. if (success) {
  396. defender.changeStamina(-25);
  397. defender.flags.grappled = true;
  398. return ["You snatch up " + defender.description("the")];
  399. } else {
  400. return ["You try to grab " + defender.description("the") + ", but they elude your grasp."];
  401. }
  402. },
  403. requirements: [
  404. function(attacker, defender) {
  405. return isNormal(attacker) && defender.flags.grappled != true && defender.flags.shrunk == true;
  406. }
  407. ],
  408. priority: 2
  409. };
  410. }
  411. function shrunkSwallow(attacker) {
  412. return {
  413. name: "Swallow",
  414. desc: "Swallow your prey",
  415. attack: function(defender) {
  416. defender.changeStamina(-50);
  417. changeMode("explore");
  418. attacker.stomach.feed(defender);
  419. return ["With a light swallow, " + defender.description("the") + " is dragged down to your sloppy guts."];
  420. },
  421. requirements: [
  422. function(attacker, defender) {
  423. return isNormal(attacker) && defender.flags.grappled == true && defender.flags.shrunk == true;
  424. }
  425. ], conditions: [
  426. function(attacker, defender) { return defender.prefs.prey; },
  427. function(attacker, defender) { return defender.prefs.vore.oral > 0; }
  428. ],
  429. priority: 2
  430. };
  431. }
  432. function shrunkStomp(attacker) {
  433. return {
  434. name: "Stomp",
  435. desc: "Stomp on your shrunken prey",
  436. attack: function(defender) {
  437. let success = statCheck(attacker, defender, "dex") || statCheck(attacker, defender, "dex") || defender.stamina == 0;
  438. defender.stamina = 0;
  439. defender.health = Math.max(0, defender.health - 50);
  440. return ["Your paw comes crashing down on " + defender.description("the") + ", burying them under your heavy toes and pinning them down hard."];
  441. },
  442. requirements: [
  443. function(attacker, defender) {
  444. return isNormal(attacker) && defender.flags.grappled != true && defender.flags.shrunk == true;
  445. }
  446. ]
  447. };
  448. }
  449. function flee(attacker) {
  450. return {
  451. name: "Flee",
  452. desc: "Try to run away",
  453. attack: function(defender) {
  454. let success = statCheck(attacker, defender, "dex");
  455. if (success) {
  456. attacker.changeStamina(-25);
  457. attacker.clear();
  458. changeMode("explore");
  459. return ["You successfully run away."];
  460. } else {
  461. attacker.changeStamina(-25);
  462. defender.changeStamina(-25);
  463. return ["You can't escape!"];
  464. }
  465. },
  466. requirements: [
  467. function(attacker, defender) { return isNormal(attacker) && !attacker.isGrappling; }
  468. ]
  469. };
  470. }
  471. function pass(attacker) {
  472. return {
  473. name: "Pass",
  474. desc: "You can't do anything!",
  475. attack: function(defender) {
  476. return ["You do nothing."];
  477. },
  478. attackPlayer: function(defender) {
  479. return [attacker.description("The") + " does nothing."];
  480. },
  481. priority: 0,
  482. };
  483. }
  484. function devourPlayer(attacker) {
  485. return {
  486. requirements: [
  487. function(attacker, defender) { return attacker.leering == true; }
  488. ],
  489. attackPlayer: function(defender) {
  490. attacker.flags.voreType = "oral";
  491. changeMode("eaten");
  492. return ["The voracious " + attacker.description() + " pins you down, his slimy maw spreading wide and engulfing your upper body with ease. He swallows and shoves you deeper, cramming your succulent frame into churning, crushing depths in seconds. A lazy, drawn-out <i>belch</i> escapes his gullet, his hunger briefly sated...and your existence now in inescapable peril."];
  493. }, conditions: [
  494. function(attacker, defender) { return defender.prefs.prey; },
  495. function(attacker, defender) { return defender.prefs.vore.oral > 0; }
  496. ],
  497. priority: 1,
  498. weight: function(attacker, defender) { return defender.prefs.vore.oral; }
  499. };
  500. }
  501. function devourPlayerAnal(attacker) {
  502. return {
  503. requirements: [
  504. function(attacker, defender) { return attacker.leering == true; }
  505. ],
  506. attackPlayer: function(defender) {
  507. attacker.flags.voreType = "anal";
  508. changeMode("eaten");
  509. return ["Fen grabs you and shoves you against the wall, turning around and slamming his ass against your face. Your entire head slips into his bowels with a wet <i>shlllrp</i>; there he holds you for a long minute, clenching and squeezing on his latest toy.",newline,"After what seems an eternity, his depths begin to pull...and within seconds, you're gone, dragged up his ass and imprisoned in his intestines. He moans softly, panting and curling his toes in delight."];
  510. }, conditions: [
  511. function(attacker, defender) { return defender.prefs.prey; },
  512. function(attacker, defender) { return defender.prefs.vore.anal > 0; }
  513. ],
  514. priority: 1,
  515. weight: function(attacker, defender) { return defender.prefs.vore.anal;}
  516. };
  517. }
  518. function leer(attacker) {
  519. return {
  520. name: "Leer",
  521. desc: "Leer at something",
  522. attackPlayer: function(defender) {
  523. attacker.leering = true;
  524. defender.changeStamina(-100);
  525. return [attacker.description("The") + " leers at you."];
  526. },
  527. requirements: [
  528. function(attacker, defender) { return attacker.leering != true && attacker.flags.grappled != true; }
  529. ],
  530. priority: 1,
  531. };
  532. }
  533. function poke(attacker) {
  534. return {
  535. name: "Poke",
  536. desc: "Poke a nerd",
  537. attackPlayer: function(defender) {
  538. return [attacker.description("The") + " pokes you on the snout for " + attack(attacker, defender, 1e12) + " damage"];
  539. },
  540. priority: 1,
  541. };
  542. }
  543. function digestPlayerStomach(predator,damage=20) {
  544. return {
  545. digest: function(player) {
  546. player.changeStamina(-25);
  547. attack(predator, player, damage);
  548. return [predator.description("The") + "'s stomach grinds over your body, swiftly digesting you."];
  549. },
  550. priority: 1,
  551. gameover: function() { return "Digested by " + predator.description("a"); }
  552. };
  553. }
  554. function instakillPlayerStomach(predator) {
  555. return {
  556. digest: function(player) {
  557. player.health = -100;
  558. return ["The stomach walls churn, clench, and swiftly crush you into nothingness."];
  559. },
  560. priority: 1,
  561. weight: function(attacker, defender) { return 1; },
  562. requirements: [
  563. function(attacker, defender) {
  564. return attacker.flags.voreType == "oral";
  565. }
  566. ],
  567. gameover: function() { return "Crushed by Fen's stomach"; }
  568. };
  569. }
  570. function instakillPlayerBowels(predator) {
  571. return {
  572. digest: function(player) {
  573. player.health = -100;
  574. return ["Fen's intestines clench, and clench and <i>clench</i> - and in seconds, you're gone, just another victim of the beast's ravenous body."];
  575. },
  576. priority: 1,
  577. weight: function(attacker, defender) { return 1; },
  578. requirements: [
  579. function(attacker, defender) {
  580. return attacker.flags.voreType == "anal";
  581. }
  582. ],
  583. gameover: function() { return "Absorbed into Fen's bowels"; }
  584. };
  585. }