big steppy
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.
 
 
 

751 lines
19 KiB

  1. var things =
  2. {
  3. "Container": Container,
  4. "Person": Person,
  5. "Empty Car": EmptyCar,
  6. "Car": Car,
  7. "Bus": Bus,
  8. "Tram": Tram,
  9. "Motorcycle": Motorcycle,
  10. "House": House,
  11. "Small Skyscraper": SmallSkyscraper,
  12. "Train": Train,
  13. "Train Car": TrainCar,
  14. "Parking Garage": ParkingGarage,
  15. "Overpass": Overpass,
  16. };
  17. var areas =
  18. {
  19. "Container": 0,
  20. "Person": 1,
  21. "Car": 4,
  22. "Bus": 12,
  23. "Tram": 20,
  24. "Motorcycle": 2,
  25. "House": 1000,
  26. "Small Skyscraper": 10000,
  27. "Train": 500,
  28. "TrainCar": 500,
  29. "Parking Garage": 5000,
  30. "Overpass": 10000,
  31. };
  32. var masses =
  33. {
  34. "Container": 0,
  35. "Person": 80,
  36. "Car": 1000,
  37. "Bus": 5000,
  38. "Tram": 10000,
  39. "Motorcycle": 200,
  40. "House": 10000,
  41. "Small Skyscraper": 100000,
  42. "Train": 5000,
  43. "Train Car": 5000,
  44. "Parking Garage": 100000,
  45. "Overpass": 100000,
  46. };
  47. // general logic: each step fills in a fraction of the remaining space
  48. function fill_area2(area, weights = {"Person": 0.1, "Car": 0.05, "House": 0.1})
  49. {
  50. result = [];
  51. candidates = [];
  52. for (var key in weights) {
  53. if (weights.hasOwnProperty(key)) {
  54. candidates.push({"name": key, "area": areas[key], "weight": weights[key]});
  55. }
  56. }
  57. candidates = candidates.sort(function (x,y) {
  58. return x.area > y.area
  59. });
  60. while(candidates.length > 0) {
  61. var candidate = candidates.pop();
  62. if (candidate.area > area)
  63. continue;
  64. var max = Math.floor(area / candidate.area);
  65. var limit = Math.min(max, 100);
  66. var count = 0;
  67. // for small amounts, actually do the randomness
  68. // the first few ones get a better shot
  69. while (limit > 0) {
  70. if (limit <= 3)
  71. count += Math.random() < (1 - Math.pow((1 - candidate.weight),2)) ? 1 : 0;
  72. else
  73. count += Math.random() < candidate.weight ? 1 : 0;
  74. --limit;
  75. }
  76. if (limit < max) {
  77. count += Math.round((max-limit) * candidate.weight);
  78. }
  79. area -= count * candidate.area;
  80. if (count > 0)
  81. result.push(new things[candidate.name](count));
  82. }
  83. if (result.length > 1) {
  84. return new Container(result);
  85. } else if (result.length == 1) {
  86. return result[0];
  87. } else {
  88. return new Person(1);
  89. }
  90. }
  91. function fill_area(area, weights = {"Person": 0.1})
  92. {
  93. var testRadius = Math.sqrt(area / Math.PI);
  94. result = []
  95. for (var key in weights) {
  96. if (weights.hasOwnProperty(key)) {
  97. var objArea = areas[key];
  98. var objRadius = Math.sqrt(objArea / Math.PI);
  99. var newRadius = testRadius - objRadius;
  100. if (newRadius > 0) {
  101. var newArea = newRadius * newRadius * Math.PI;
  102. var numObjs = weights[key] * newArea;
  103. if (numObjs < 1) {
  104. numObjs = Math.random() > numObjs ? 0 : 1;
  105. }
  106. else {
  107. numObjs = Math.round(numObjs);
  108. }
  109. if (numObjs > 0)
  110. result.push(new things[key](numObjs));
  111. else {
  112. // try again with a better chance for just one
  113. }
  114. }
  115. }
  116. }
  117. if (result.length > 1)
  118. return new Container(result);
  119. else if (result.length == 1)
  120. return result[0];
  121. else
  122. return new Person();
  123. }
  124. // describes everything in the container
  125. function describe_all(contents,verbose=true) {
  126. var things = [];
  127. for (var key in contents) {
  128. if (contents.hasOwnProperty(key)) {
  129. things.push(contents[key].describe(verbose));
  130. }
  131. }
  132. return merge_things(things);
  133. }
  134. function random_desc(list, odds=1) {
  135. if (Math.random() < odds)
  136. return list[Math.floor(Math.random() * list.length)];
  137. else
  138. return "";
  139. }
  140. // combine strings into a list with proper grammar
  141. function merge_things(list,semicolons=false) {
  142. if (list.length == 0) {
  143. return "";
  144. } else if (list.length == 1) {
  145. return list[0];
  146. } else if (list.length == 2) {
  147. return list[0] + " and " + list[1];
  148. } else {
  149. result = "";
  150. list.slice(0,list.length-1).forEach(function(term) {
  151. result += term + ", ";
  152. })
  153. result += "and " + list[list.length-1]
  154. return result;
  155. }
  156. }
  157. // combine the adjectives for something into a single string
  158. function merge_desc(list) {
  159. result = ""
  160. list.forEach(function(term) {
  161. if (term != "")
  162. result += term + " ";
  163. });
  164. // knock off the last space
  165. if (result.length > 0) {
  166. result = result.substring(0, result.length - 1);
  167. }
  168. return result;
  169. }
  170. // maybe make this something that approximates a
  171. // normal distribution; doing this 15,000,000 times is bad...
  172. // solution: only a few are random lul
  173. function distribution(min, max, samples) {
  174. var result = 0;
  175. var limit = Math.min(100,samples)
  176. for (var i = 0; i < limit; i++) {
  177. result += Math.floor(Math.random() * (max - min + 1) + min);
  178. }
  179. if (limit < samples) {
  180. result += Math.round((max - min) * (samples - limit));
  181. }
  182. return result;
  183. }
  184. /* default actions */
  185. function defaultStomp(thing) {
  186. return function (height=10) { return "You crush " + thing.describe() + " underfoot."};
  187. }
  188. function defaultKick(thing) {
  189. return function(height=10) { return "You punt " + thing.describe() + ", destroying " + (thing.count > 1 ? "them" : "it") + "."; }
  190. }
  191. function defaultEat(thing) {
  192. return function(height=10) { return "You scoop up " + thing.describe() + " and swallow " + (thing.count > 1 ? "them" : "it") + " whole."; }
  193. }
  194. function defaultAnalVore(thing) {
  195. return function(height=10) { return "You sit yourself down on " + thing.describe() + ". " + (thing.count > 1 ? "They slide" : "It slides") + " inside with ease."; }
  196. }
  197. function defaultButtcrush(thing) {
  198. return function(height=10) { return "Your heavy ass obliterates " + thing.describe() + ". "; }
  199. }
  200. function defaultArea(thing) {
  201. return areas[thing.name];
  202. }
  203. function defaultMass(thing) {
  204. return masses[thing.name];
  205. }
  206. function defaultMerge(thing) {
  207. return function(container) {
  208. var newCount = this.count + container.count;
  209. var newThing = new things[thing.name](newCount);
  210. newThing.contents = {};
  211. for (var key in this.contents) {
  212. if (this.contents.hasOwnProperty(key)) {
  213. newThing.contents[key] = this.contents[key];
  214. }
  215. }
  216. for (var key in container.contents) {
  217. if (container.contents.hasOwnProperty(key)) {
  218. if (this.contents.hasOwnProperty(key)) {
  219. newThing.contents[key] = this.contents[key].merge(container.contents[key]);
  220. } else {;
  221. newThing.contents[key] = container.contents[key];
  222. }
  223. }
  224. }
  225. return newThing;
  226. }
  227. }
  228. function defaultSum(thing) {
  229. return function() {
  230. var counts = {}
  231. if (thing.name != "Container")
  232. counts[thing.name] = thing.count;
  233. for (var key in thing.contents) {
  234. if (thing.contents.hasOwnProperty(key)) {
  235. subcount = thing.contents[key].sum();
  236. for (var subkey in subcount) {
  237. if (!counts.hasOwnProperty(subkey)) {
  238. counts[subkey] = 0;
  239. }
  240. counts[subkey] += subcount[subkey];
  241. }
  242. }
  243. }
  244. return counts;
  245. }
  246. }
  247. function defaultSumProperty(thing) {
  248. return function(prop) {
  249. var total = 0;
  250. total += thing[prop] * thing.count;
  251. for (var key in thing.contents) {
  252. if (thing.contents.hasOwnProperty(key)) {
  253. total += thing.contents[key].sum_property(prop);
  254. }
  255. }
  256. return total;
  257. }
  258. }
  259. function DefaultEntity() {
  260. this.stomp = defaultStomp;
  261. this.eat = defaultEat;
  262. this.kick = defaultKick;
  263. this.anal_vore = defaultAnalVore;
  264. this.buttcrush = defaultButtcrush;
  265. this.sum = defaultSum;
  266. this.area = defaultArea;
  267. this.mass = defaultMass;
  268. this.sum_property = defaultSumProperty;
  269. this.merge = defaultMerge;
  270. return this;
  271. }
  272. // god I love reinventing the wheel
  273. function copy_defaults(self,proto) {
  274. for (var key in proto) {
  275. if (proto.hasOwnProperty(key)) {
  276. self[key] = proto[key](self)
  277. }
  278. }
  279. }
  280. function Container(contents = []) {
  281. this.name = "Container";
  282. copy_defaults(this,new DefaultEntity());
  283. this.count = 0;
  284. this.contents = {}
  285. for (var i=0; i < contents.length; i++) {
  286. this.contents[contents[i].name] = contents[i];
  287. }
  288. for (var key in this.contents) {
  289. if (this.contents.hasOwnProperty(key)) {
  290. this.count += this.contents[key].count;
  291. }
  292. }
  293. this.describe = function(verbose = true) {
  294. return describe_all(this.contents,false)
  295. }
  296. this.eat = function() {
  297. var line = containerEat(this);
  298. if (line == "")
  299. return defaultEat(this)();
  300. else
  301. return line;
  302. };
  303. return this;
  304. }
  305. function Person(count = 1) {
  306. this.name = "Person";
  307. copy_defaults(this,new DefaultEntity());
  308. this.count = count;
  309. this.contents = {};
  310. this.describeOne = function (verbose=true) {
  311. body = random_desc(["skinny","fat","tall","short","stocky","spindly"], (verbose ? 0.6 : 0));
  312. sex = random_desc(["male", "female"], (verbose ? 1 : 0));
  313. species = random_desc(["wolf","cat","dog","squirrel","horse","hyena","fox","jackal","crux","sergal"]);
  314. return "a " + merge_desc([body,sex,species]);
  315. }
  316. this.describe = function(verbose=true) {
  317. if (verbose) {
  318. if (count <= 3) {
  319. list = [];
  320. for (var i = 0; i < count; i++) {
  321. list.push(this.describeOne(this.count <= 2));
  322. }
  323. return merge_things(list);
  324. } else {
  325. return this.count + " people"
  326. }
  327. } else {
  328. return this.count + " " + (this.count > 1 ? "people" : "person");
  329. }
  330. }
  331. this.stomp = function() {
  332. var line = personStomp(this);
  333. if (line == "")
  334. return defaultStomp(this)();
  335. else
  336. return line;
  337. };
  338. this.eat = function() {
  339. var line = personEat(this);
  340. if (line == "")
  341. return defaultEat(this)();
  342. else
  343. return line;
  344. };
  345. return this;
  346. }
  347. function EmptyCar(count = 1) {
  348. this.name = "Car";
  349. copy_defaults(this,new DefaultEntity());
  350. this.count = count;
  351. this.contents = {};
  352. this.describeOne = function(verbose=true) {
  353. color = random_desc(["black","black","gray","gray","blue","red","tan","white","white"]);
  354. adjective = random_desc(["rusty","brand-new"],0.3);
  355. type = random_desc(["SUV","coupe","sedan","truck","van","convertible"]);
  356. return "a parked " + merge_desc([adjective,color,type]);
  357. }
  358. this.describe = function(verbose = true) {
  359. if (verbose) {
  360. if (this.count <= 3) {
  361. list = [];
  362. for (var i = 0; i < this.count; i++) {
  363. list.push(this.describeOne());
  364. }
  365. return merge_things(list);
  366. } else {
  367. return this.count + " parked cars";
  368. }
  369. } else {
  370. return this.count + " parked " + (this.count > 1 ? "cars" : "car");
  371. }
  372. }
  373. }
  374. function Car(count = 1) {
  375. this.name = "Car";
  376. copy_defaults(this,new DefaultEntity());
  377. this.count = count;
  378. this.contents = {};
  379. var amount = distribution(2,5,count);
  380. this.contents.person = new Person(amount);
  381. this.describeOne = function(verbose=true) {
  382. color = random_desc(["black","black","gray","gray","blue","red","tan","white","white"], (verbose ? 1 : 0));
  383. adjective = random_desc(["rusty","brand-new"], (verbose ? 0.3 : 0));
  384. type = random_desc(["SUV","coupe","sedan","truck","van","convertible"]);
  385. return "a " + merge_desc([adjective,color,type]);
  386. }
  387. this.describe = function(verbose = true) {
  388. if (verbose) {
  389. if (this.count <= 3) {
  390. list = [];
  391. for (var i = 0; i < this.count; i++) {
  392. list.push(this.describeOne(this.count < 2));
  393. }
  394. return merge_things(list) + " with " + this.contents.person.describe(false) + " inside";
  395. } else {
  396. return this.count + " cars with " + this.contents.person.describe(false) + " inside";
  397. }
  398. } else {
  399. return this.count + " " + (this.count > 1 ? "cars" : "car");
  400. }
  401. }
  402. }
  403. function Bus(count = 1) {
  404. this.name = "Bus";
  405. copy_defaults(this,new DefaultEntity());
  406. this.count = count;
  407. this.contents = {};
  408. var amount = distribution(10,35,count);
  409. this.contents.person = new Person(amount);
  410. this.describeOne = function(verbose=true) {
  411. adjective = random_desc(["rusty","brand-new"], (verbose ? 0.3 : 0));
  412. color = random_desc(["black","tan","gray"], (verbose ? 1 : 0));
  413. type = random_desc(["bus","double-decker bus","articulating bus"]);
  414. return "a " + merge_desc([adjective,color,type]);
  415. }
  416. this.describe = function(verbose = true) {
  417. if (this.count <= 3) {
  418. list = [];
  419. for (var i = 0; i < this.count; i++) {
  420. list.push(this.describeOne(this.count < 2));
  421. }
  422. return merge_things(list) + " with " + this.contents.person.describe() + " inside";
  423. } else {
  424. return this.count + " buses with " + this.contents.person.describe() + " inside";
  425. }
  426. }
  427. }
  428. function Tram(count = 1) {
  429. this.name = "Tram";
  430. copy_defaults(this,new DefaultEntity());
  431. this.count = count;
  432. this.contents = {};
  433. var amount = distribution(40,60,count);
  434. this.contents.person = new Person(amount);
  435. this.describeOne = function(verbose=true) {
  436. adjective = random_desc(["rusty","weathered"], (verbose ? 0.3 : 0));
  437. color = random_desc(["blue","brown","gray"], (verbose ? 1 : 0));
  438. type = random_desc(["tram"]);
  439. return "a " + merge_desc([adjective,color,type]);
  440. }
  441. this.describe = function(verbose = true) {
  442. if (this.count <= 3) {
  443. list = [];
  444. for (var i = 0; i < this.count; i++) {
  445. list.push(this.describeOne(this.count < 2));
  446. }
  447. return merge_things(list) + " with " + this.contents.person.describe() + " inside";
  448. } else {
  449. return this.count + " trams with " + this.contents.person.describe() + " inside";
  450. }
  451. }
  452. this.anal_vore = function() {
  453. return "You slide " + this.describe() + " up your tight ass";
  454. }
  455. }
  456. function Motorcycle(count = 1) {
  457. this.name = "Motorcycle";
  458. copy_defaults(this,new DefaultEntity());
  459. this.count = count;
  460. this.contents = {};
  461. var amount = distribution(1,2,count);
  462. this.contents.person = new Person(amount);
  463. }
  464. function Train(count = 1) {
  465. this.name = "Train";
  466. copy_defaults(this,new DefaultEntity());
  467. this.count = count;
  468. this.contents = {};
  469. var amount = distribution(1,4,count);
  470. this.contents.person = new Person(amount);
  471. amount = distribution(1,10,count);
  472. this.contents.traincar = new TrainCar(amount);
  473. this.describeOne = function(verbose=true) {
  474. adjective = random_desc(["rusty","brand-new"], (verbose ? 0.3 : 0));
  475. color = random_desc(["black","tan","gray"], (verbose ? 1 : 0));
  476. type = random_desc(["train","passenger train","freight train"]);
  477. return "a " + merge_desc([adjective,color,type]);
  478. }
  479. this.describe = function(verbose = true) {
  480. if (this.count <= 3) {
  481. list = [];
  482. for (var i = 0; i < this.count; i++) {
  483. list.push(this.describeOne(this.count < 2));
  484. }
  485. return merge_things(list) + " with " + this.contents.person.describe() + " in the engine and " + this.contents.traincar.describe() + " attached";
  486. } else {
  487. return this.count + " trains with " + this.contents.person.describe() + " in the engine and " + this.contents.traincar.describe() + " attached";
  488. }
  489. }
  490. this.anal_vore = function() {
  491. var cars = (this.contents.traincar.count == 1 ? this.contents.traincar.describe() + " follows it inside" : this.contents.traincar.describe() + " are pulled slowly inside");
  492. return "You snatch up " + this.describeOne() + " and stuff it into your pucker, moaning as " + cars;
  493. }
  494. }
  495. function TrainCar(count = 1) {
  496. this.name = "Train Car";
  497. copy_defaults(this,new DefaultEntity());
  498. this.count = count;
  499. this.contents = {};
  500. var amount = distribution(10,40,count);
  501. this.contents.person = new Person(amount);
  502. this.describeOne = function(verbose=true) {
  503. adjective = random_desc(["rusty","brand-new"], (verbose ? 0.3 : 0));
  504. color = random_desc(["black","tan","gray"], (verbose ? 1 : 0));
  505. type = random_desc(["train car","passenger train car","freight train car"]);
  506. return "a " + merge_desc([adjective,color,type]);
  507. }
  508. this.describe = function(verbose = true) {
  509. if (this.count <= 3) {
  510. list = [];
  511. for (var i = 0; i < this.count; i++) {
  512. list.push(this.describeOne(this.count < 2));
  513. }
  514. return merge_things(list) + " with " + describe_all(this.contents) + " inside";
  515. } else {
  516. return this.count + " train cars with " + describe_all(this.contents) + " inside";
  517. }
  518. }
  519. }
  520. function House(count = 1) {
  521. this.name = "House";
  522. copy_defaults(this,new DefaultEntity());
  523. this.count = count;
  524. this.contents = {};
  525. var amount = distribution(0,8,count);
  526. this.contents.person = new Person(amount);
  527. amount = distribution(0,2,count);
  528. this.contents.emptycar = new EmptyCar(amount);
  529. this.describeOne = function(verbose=true) {
  530. size = random_desc(["little","two-story","large"], (verbose ? 0.5 : 0));
  531. color = random_desc(["blue","white","gray","tan","green"], (verbose ? 0.5 : 0));
  532. name = random_desc(["house","house","house","house","house","trailer"], 1);
  533. return "a " + merge_desc([size,color,name]);
  534. }
  535. this.describe = function(verbose = true) {
  536. if (this.count <= 3) {
  537. list = [];
  538. for (var i = 0; i < this.count; i++) {
  539. list.push(this.describeOne(this.count < 2));
  540. }
  541. return merge_things(list) + " with " + this.contents.person.describe() + " inside";
  542. } else {
  543. return this.count + " homes with " + this.contents.person.describe() + " inside";
  544. }
  545. }
  546. }
  547. function SmallSkyscraper(count = 1) {
  548. this.name = "Small Skyscraper";
  549. copy_defaults(this,new DefaultEntity());
  550. this.count = count;
  551. this.contents = {};
  552. var amount = distribution(50,500,count);
  553. this.contents.person = new Person(amount);
  554. amount = distribution(10,50,count);
  555. this.contents.emptycar = new EmptyCar(amount);
  556. this.describeOne = function(verbose=true) {
  557. color = random_desc(["blue","white","gray","tan","green"], (verbose ? 0.5 : 0));
  558. name = random_desc(["skyscraper","office tower","office building"], 1);
  559. return "a " + merge_desc([color,name]);
  560. }
  561. this.describe = function(verbose = true) {
  562. if (this.count <= 3) {
  563. list = [];
  564. for (var i = 0; i < this.count; i++) {
  565. list.push(this.describeOne(this.count < 2));
  566. }
  567. return merge_things(list) + " with " + describe_all(this.contents,false) + " inside";
  568. } else {
  569. return this.count + " small skyscrapers with " + describe_all(this.contents,false) + " inside";
  570. }
  571. }
  572. this.anal_vore = function(height=10) {
  573. var line = skyscraperAnalVore(this,height);
  574. if (line == "")
  575. return defaultAnalVore(this)();
  576. else
  577. return line;
  578. };
  579. }
  580. function ParkingGarage(count = 1) {
  581. this.name = "Parking Garage";
  582. copy_defaults(this,new DefaultEntity());
  583. this.count = count;
  584. this.contents = {};
  585. var amount = distribution(10,200,count);
  586. this.contents.person = new Person(amount);
  587. amount = distribution(30,100,count);
  588. this.contents.emptycar = new EmptyCar(amount);
  589. amount = distribution(5,20,count);
  590. this.contents.car = new Car(amount);
  591. this.describeOne = function(verbose=true) {
  592. return "a parking garage";
  593. }
  594. this.describe = function(verbose = true) {
  595. return (this.count == 1 ? "a parking garage" : this.count + " parking garages") + " with " + describe_all(this.contents) + " inside";
  596. }
  597. }
  598. function Overpass(count = 1) {
  599. this.name = "Overpass";
  600. copy_defaults(this,new DefaultEntity());
  601. this.count = count;
  602. this.contents = {};
  603. var amount = distribution(0,20,count);
  604. this.contents.person = new Person(amount);
  605. amount = distribution(25,100,count);
  606. this.contents.car = new Car(amount);
  607. }