Billabob

recursivepunching2

Oct 6th, 2024
81
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. let monsterhealth=150
  2.  
  3. //modes are as follows:
  4. //0: standard, respecting custom values
  5. //1: shotgun
  6. //2: SSG
  7. //3: 40 BFG tracers
  8. //4: 40 BFG tracers + BFG ball
  9. //5: 1 BFG tracer
  10. let mode=0
  11. let rngmode=0                   //0: vanilla rng; 1: boom rng; 2: perfect ideal distribution of rolls
  12. let cumprint=true               //true if you want the final output to also print cumulative values
  13. let rollprint=true              //true if you want the roll table to be printed at the end (normalised so the sum of all rolls is 100)
  14. let roundoutput=true            //round output to 5 sigfig unless it would lose integer precision
  15.  
  16. //ignored on modes >0
  17. //ex. 20 and 10 for punching, 5 and 3 for pistol
  18. let basedmg=20
  19. let range=8
  20.  
  21. let consistentsplash = false    //false if you want to use the range of 110-120 or whatever it is lol
  22. let splash=98                   //splash damage, set to 0 if not using rockets - also minimum splash if consistentsplash=false
  23. let maxsplash=117               //max splash damage for range
  24. //keep in mind above 2 values are also used for rocket mode
  25. let rocketmode = true           //rocket mode: get accurate range assuming enemy is walking towards you at "monsterspeed" units every "monsterrate" tics
  26. let monsterspeed = 10           //units moved per movement
  27. let monsterrate = 2             //tics per movement (1 will break my code just adjust the splash values and use the regular consistentsplash function for that)
  28.  
  29. //don't touch this
  30. let t=Date.now()
  31. let dmg={0:1}
  32. let outp={}
  33.  
  34. let PRNG =  [0,   8, 109, 220, 222, 241, 149, 107,  75, 248, 254, 140,  16,  66,
  35.             74,  21, 211,  47,  80, 242, 154,  27, 205, 128, 161,  89,  77,  36,
  36.             95, 110,  85,  48, 212, 140, 211, 249,  22,  79, 200,  50,  28, 188,
  37.             52, 140, 202, 120,  68, 145,  62,  70, 184, 190,  91, 197, 152, 224,
  38.             149, 104,  25, 178, 252, 182, 202, 182, 141, 197,   4,  81, 181, 242,
  39.             145,  42,  39, 227, 156, 198, 225, 193, 219,  93, 122, 175, 249,   0,
  40.             175, 143,  70, 239,  46, 246, 163,  53, 163, 109, 168, 135,   2, 235,
  41.             25,  92,  20, 145, 138,  77,  69, 166,  78, 176, 173, 212, 166, 113,
  42.             94, 161,  41,  50, 239,  49, 111, 164,  70,  60,   2,  37, 171,  75,
  43.             136, 156,  11,  56,  42, 146, 138, 229,  73, 146,  77,  61,  98, 196,
  44.             135, 106,  63, 197, 195,  86,  96, 203, 113, 101, 170, 247, 181, 113,
  45.             80, 250, 108,   7, 255, 237, 129, 226,  79, 107, 112, 166, 103, 241,
  46.             24, 223, 239, 120, 198,  58,  60,  82, 128,   3, 184,  66, 143, 224,
  47.             145, 224,  81, 206, 163,  45,  63,  90, 168, 114,  59,  33, 159,  95,
  48.             28, 139, 123,  98, 125, 196,  15,  70, 194, 253,  54,  14, 109, 226,
  49.             71,  17, 161,  93, 186,  87, 244, 138,  20,  52, 123, 251,  26,  36,
  50.             17,  46,  52, 231, 232,  76,  31, 221,  84,  37, 216, 165, 212, 106,
  51.             197, 242,  98,  43,  39, 175, 254, 145, 190,  84, 118, 222, 187, 136,
  52.             120, 163, 236, 249]
  53.  
  54. //to obtain these tables I ignored the initial PRNG seed and simply ran every possible RNG value
  55. //due to the way RNG is initialised the first RNG call will always return an odd number, and then it alternates between even & odd on each call
  56. //every RNG value is still equally likely (occurs exactly 2 times) and the right-shift in the P_Random() function ensures this parity does not affect the actual RNG output
  57.  
  58. let boomSSGTable = {
  59.     "110": 176, "115": 2064, "120": 11376, "125": 51712, "130": 199328, "135": 652192, "140": 1880032, "145": 4845120,
  60.     "150": 11218000, "155": 23614848, "160": 45180272, "165": 79390848, "170": 128525984, "175": 192190944, "180": 265873680, "185": 341163616, "190": 406654528, "195": 450420768,
  61.     "200": 464287024, "205": 445193456, "210": 397141408, "215": 329356672, "220": 253760720, "225": 181330992, "230": 119820688, "235": 73163136, "240": 41187680, "245": 21205728,
  62.     "250": 9948144, "255": 4273600, "260": 1639664, "265": 561552, "270": 168112, "275": 42448, "280": 9088, "285": 1440, "290": 240, "295": 16}
  63.  
  64. let boomSGTable = {
  65.     "35": 2073536, "40": 14340784, "45": 56911200, "50": 155151216, "55": 322040896, "60": 528369152, "65": 704610176, "70": 770981232,
  66.     "75": 696387616, "80": 516129200, "85": 310947472, "90": 148070896, "95": 53655552, "100": 13387376, "105": 1910992
  67. }
  68.  
  69. let boomBFGTable = {
  70.     "2424": 512, "2425": 512, "2426": 1024, "2427": 512, "2429": 1024, "2430": 512, "2432": 512, "2433": 512, "2434": 1024, "2435": 512, "2436": 512, "2437": 512, "2438": 512,
  71.     "2439": 1024, "2440": 1024, "2444": 2048, "2445": 1536, "2446": 1024, "2447": 2560, "2448": 2560, "2449": 512, "2450": 2048, "2451": 3072, "2452": 1024, "2453": 1024,
  72.     "2454": 1536, "2455": 2048, "2456": 1024, "2457": 2048, "2458": 2048, "2459": 3584, "2460": 2048, "2461": 6144, "2462": 3584, "2463": 5120, "2464": 4096, "2465": 3584,
  73.     "2466": 4096, "2467": 1536, "2468": 2560, "2469": 5120, "2470": 4096, "2471": 4608, "2472": 6656, "2473": 7168, "2474": 9728, "2475": 7168, "2476": 7680, "2477": 8192,
  74.     "2478": 8704, "2479": 11776, "2480": 8192, "2481": 11776, "2482": 12800, "2483": 14848, "2484": 16384, "2485": 17408, "2486": 16896, "2487": 16896, "2488": 19456, "2489": 24064,
  75.     "2490": 32256, "2491": 25600, "2492": 30208, "2493": 30208, "2494": 32768, "2495": 44032, "2496": 41984, "2497": 35328, "2498": 45568, "2499": 40448, "2500": 55296,
  76.     "2501": 57344, "2502": 52224, "2503": 69120, "2504": 66560, "2505": 70656, "2506": 72704, "2507": 75776, "2508": 79872, "2509": 99328, "2510": 87552, "2511": 89088,
  77.     "2512": 110080, "2513": 113152, "2514": 116224, "2515": 124928, "2516": 114176, "2517": 145920, "2518": 151552, "2519": 162304, "2520": 159232, "2521": 178176, "2522": 178176,
  78.     "2523": 192512, "2524": 214016, "2525": 208896, "2526": 225792, "2527": 249856, "2528": 270336, "2529": 281600, "2530": 294912, "2531": 312832, "2532": 320000, "2533": 353792,
  79.     "2534": 370176, "2535": 400896, "2536": 408576, "2537": 441344, "2538": 489472, "2539": 506880, "2540": 521728, "2541": 521728, "2542": 577536, "2543": 620544, "2544": 621056,
  80.     "2545": 668160, "2546": 682496, "2547": 744448, "2548": 745472, "2549": 804864, "2550": 866304, "2551": 883712, "2552": 935424, "2553": 1009664, "2554": 1028096, "2555": 1052672,
  81.     "2556": 1156096, "2557": 1189376, "2558": 1284608, "2559": 1367040, "2560": 1385984, "2561": 1436672, "2562": 1489920, "2563": 1567232, "2564": 1658880, "2565": 1742336,
  82.     "2566": 1846784, "2567": 1867264, "2568": 1904640, "2569": 2065920, "2570": 2110976, "2571": 2196992, "2572": 2334208, "2573": 2420224, "2574": 2513920, "2575": 2610688,
  83.     "2576": 2689024, "2577": 2847744, "2578": 2933248, "2579": 2958336, "2580": 3162624, "2581": 3279872, "2582": 3418112, "2583": 3598848, "2584": 3646464, "2585": 3813376,
  84.     "2586": 3947520, "2587": 3986432, "2588": 4207104, "2589": 4381696, "2590": 4524032, "2591": 4719104, "2592": 4874752, "2593": 4991488, "2594": 5201920, "2595": 5430784,
  85.     "2596": 5475328, "2597": 5699584, "2598": 6007808, "2599": 6183936, "2600": 6340096, "2601": 6579200, "2602": 6730240, "2603": 6887424, "2604": 7132160, "2605": 7310336,
  86.     "2606": 7579648, "2607": 7803904, "2608": 8019456, "2609": 8277504, "2610": 8496640, "2611": 8752640, "2612": 9040384, "2613": 9338880, "2614": 9482240, "2615": 9814016,
  87.     "2616": 10065408, "2617": 10273280, "2618": 10417152, "2619": 10839552, "2620": 11064832, "2621": 11408896, "2622": 11653120, "2623": 11939328, "2624": 12240384,
  88.     "2625": 12516864, "2626": 12687360, "2627": 13039616, "2628": 13487616, "2629": 13663232, "2630": 14251008, "2631": 14319104, "2632": 14617600, "2633": 14903808,
  89.     "2634": 15025664, "2635": 15385600, "2636": 15914496, "2637": 16132096, "2638": 16487936, "2639": 16854016, "2640": 17240576, "2641": 17546752, "2642": 17884160,
  90.     "2643": 18251264, "2644": 18478592, "2645": 18871296, "2646": 19109376, "2647": 19536384, "2648": 19769344, "2649": 19960832, "2650": 20655616, "2651": 20644864,
  91.     "2652": 21296640, "2653": 21296128, "2654": 21764096, "2655": 21972480, "2656": 22325248, "2657": 22724608, "2658": 22777344, "2659": 23212544, "2660": 23724544,
  92.     "2661": 23855616, "2662": 24286720, "2663": 24343040, "2664": 24859648, "2665": 24974336, "2666": 25187840, "2667": 25572864, "2668": 26062336, "2669": 26275840,
  93.     "2670": 26450432, "2671": 26634752, "2672": 27142144, "2673": 27262464, "2674": 27417088, "2675": 27637248, "2676": 27896832, "2677": 27995136, "2678": 27977728,
  94.     "2679": 28504576, "2680": 28476928, "2681": 28703232, "2682": 28879360, "2683": 29074944, "2684": 29399552, "2685": 29598208, "2686": 29593088, "2687": 29704704,
  95.     "2688": 29877248, "2689": 30120448, "2690": 29886976, "2691": 29805056, "2692": 30177280, "2693": 30393856, "2694": 30406144, "2695": 30354432, "2696": 30529024,
  96.     "2697": 30382592, "2698": 30438400, "2699": 30469632, "2700": 30327808, "2701": 30467584, "2702": 30281216, "2703": 30386688, "2704": 30402048, "2705": 30545920,
  97.     "2706": 30337024, "2707": 30216704, "2708": 30140416, "2709": 30234112, "2710": 29915648, "2711": 29591552, "2712": 29582336, "2713": 29927936, "2714": 29413888,
  98.     "2715": 29496832, "2716": 29313536, "2717": 29304832, "2718": 28797952, "2719": 28536320, "2720": 28594176, "2721": 28642304, "2722": 28003328, "2723": 27817472,
  99.     "2724": 27913728, "2725": 27444736, "2726": 27698688, "2727": 27238912, "2728": 26849792, "2729": 26727936, "2730": 26294272, "2731": 26219008, "2732": 25915904,
  100.     "2733": 25805312, "2734": 25320448, "2735": 25065984, "2736": 24713216, "2737": 24619520, "2738": 24032256, "2739": 23987200, "2740": 23721472, "2741": 23299072,
  101.     "2742": 23172608, "2743": 22827520, "2744": 22464000, "2745": 22239232, "2746": 21722112, "2747": 21357568, "2748": 21308416, "2749": 20881920, "2750": 20679680,
  102.     "2751": 20465152, "2752": 20111360, "2753": 19546624, "2754": 19423232, "2755": 18901504, "2756": 18633728, "2757": 18236928, "2758": 17902592, "2759": 17644032,
  103.     "2760": 17439232, "2761": 17047040, "2762": 16723456, "2763": 16229376, "2764": 15942656, "2765": 15698944, "2766": 15347200, "2767": 15187968, "2768": 14638592,
  104.     "2769": 14430720, "2770": 14025216, "2771": 13698560, "2772": 13553664, "2773": 13282816, "2774": 12925952, "2775": 12552704, "2776": 12151808, "2777": 11855872,
  105.     "2778": 11570688, "2779": 11356672, "2780": 11085824, "2781": 10804224, "2782": 10598912, "2783": 10215936, "2784": 10223616, "2785": 9739776, "2786": 9531904, "2787": 9210880,
  106.     "2788": 8980480, "2789": 8622080, "2790": 8477184, "2791": 8295936, "2792": 8024064, "2793": 7700992, "2794": 7607296, "2795": 7267840, "2796": 7049216, "2797": 6899200,
  107.     "2798": 6696448, "2799": 6489600, "2800": 6222336, "2801": 6163456, "2802": 5800448, "2803": 5717504, "2804": 5500416, "2805": 5327360, "2806": 5166080, "2807": 5027840,
  108.     "2808": 4759552, "2809": 4653568, "2810": 4371968, "2811": 4321792, "2812": 4168192, "2813": 4060672, "2814": 3929088, "2815": 3723776, "2816": 3572224, "2817": 3402752,
  109.     "2818": 3384320, "2819": 3228672, "2820": 3206656, "2821": 2969600, "2822": 2973184, "2823": 2816000, "2824": 2666496, "2825": 2620928, "2826": 2444800, "2827": 2257920,
  110.     "2828": 2290688, "2829": 2175488, "2830": 2101760, "2831": 2023936, "2832": 1903616, "2833": 1852928, "2834": 1767936, "2835": 1691648, "2836": 1665536, "2837": 1523712,
  111.     "2838": 1478144, "2839": 1421824, "2840": 1389056, "2841": 1293312, "2842": 1266688, "2843": 1233408, "2844": 1144320, "2845": 1081856, "2846": 1032192, "2847": 1008640,
  112.     "2848": 978944, "2849": 884224, "2850": 830976, "2851": 785408, "2852": 765952, "2853": 769024, "2854": 692736, "2855": 689664, "2856": 630784, "2857": 625152, "2858": 601088,
  113.     "2859": 553984, "2860": 538624, "2861": 498688, "2862": 473088, "2863": 433152, "2864": 436224, "2865": 390656, "2866": 377856, "2867": 363008, "2868": 335360, "2869": 324608,
  114.     "2870": 299008, "2871": 282112, "2872": 260096, "2873": 264704, "2874": 227840, "2875": 212992, "2876": 215040, "2877": 202240, "2878": 179712, "2879": 169984, "2880": 168448,
  115.     "2881": 167936, "2882": 147968, "2883": 123904, "2884": 118784, "2885": 130560, "2886": 120320, "2887": 102912, "2888": 102400, "2889": 82432, "2890": 88064, "2891": 76800,
  116.     "2892": 74240, "2893": 69120, "2894": 76288, "2895": 67584, "2896": 58880, "2897": 45056, "2898": 48128, "2899": 49664, "2900": 45568, "2901": 44032, "2902": 41984,
  117.     "2903": 35328, "2904": 34304, "2905": 31744, "2906": 25600, "2907": 31232, "2908": 22016, "2909": 20992, "2910": 23552, "2911": 20480, "2912": 15360, "2913": 15872,
  118.     "2914": 14848, "2915": 11776, "2916": 13312, "2917": 16896, "2918": 10752, "2919": 13824, "2920": 8704, "2921": 8704, "2922": 6656, "2923": 6144, "2924": 6656, "2925": 4096,
  119.     "2926": 3072, "2927": 7680, "2928": 2560, "2929": 7168, "2930": 3072, "2931": 5632, "2932": 5120, "2933": 4096, "2934": 1024, "2935": 5120, "2936": 6144, "2937": 1536,
  120.     "2938": 1536, "2939": 2048, "2940": 2048, "2941": 2048, "2942": 2048, "2943": 3584, "2944": 1536, "2945": 3584, "2946": 512, "2947": 2048, "2948": 2560, "2949": 2048,
  121.     "2950": 1024, "2951": 2048, "2952": 1024, "2953": 2048, "2955": 1024, "2956": 1536, "2957": 512, "2958": 2560, "2959": 1536, "2960": 1024, "2961": 1024, "2962": 1536,
  122.     "2963": 512, "2964": 512, "2966": 512, "2967": 512, "2973": 512, "2974": 512, "2976": 512
  123. }
  124.  
  125. let boomTracerTable = {"26": 1024, "27": 2560, "28": 3584, "29": 5120, "30": 10240, "31": 20480, "32": 22528, "33": 61952, "34": 76800, "35": 152576, "36": 243712, "37": 387584,
  126.     "38": 577024, "39": 922624, "40": 1404928, "41": 2021376, "42": 2871296, "43": 4070912, "44": 5553664, "45": 7546368, "46": 10203648, "47": 13490688, "48": 17437184,
  127.     "49": 22211072, "50": 28212224, "51": 34910208, "52": 43022336, "53": 52021248, "54": 61858816, "55": 72724480, "56": 84642304, "57": 96925184, "58": 109870080, "59": 122261504,
  128.     "60": 135552000, "61": 147715072, "62": 158703104, "63": 169087488, "64": 177570304, "65": 183586304, "66": 188894208, "67": 191238656, "68": 190485504, "69": 188260864,
  129.     "70": 183988736, "71": 177042944, "72": 168504832, "73": 159318528, "74": 147393024, "75": 135540736, "76": 122732032, "77": 109907456, "78": 97223168, "79": 84743680,
  130.     "80": 72975360, "81": 61939200, "82": 51587584, "83": 42720768, "84": 35087872, "85": 28123136, "86": 22316032, "87": 17399808, "88": 13352448, "89": 10170368, "90": 7585280,
  131.     "91": 5694464, "92": 4035072, "93": 2814976, "94": 1938944, "95": 1408512, "96": 942592, "97": 624640, "98": 373248, "99": 274432, "100": 154112, "101": 87040, "102": 50176,
  132.     "103": 29696, "104": 18432, "105": 12288, "106": 6144, "107": 4096, "108": 1024, "109": 2048, "110": 1024, "111": 512
  133. }
  134.  
  135. let rngindex=0;
  136. function M_Random(){
  137.     rngindex=(rngindex+1)%256
  138.     return PRNG[rngindex]
  139. }
  140.  
  141. function normalise(obj){ //weighted so a perfect range would be all 1s, for the sake of multiplying out probabilities later - sum equals the length of the table
  142.     let objk=Object.keys(obj)
  143.     let sum=0
  144.     for(let i=0;i<objk.length;i++){
  145.         sum+=obj[objk[i]]
  146.     }
  147.     for(let i=0;i<objk.length;i++){
  148.         obj[objk[i]]/=sum/objk.length
  149.         //for the sake of sanity
  150.         if(obj[objk[i]]==0){
  151.             delete obj[objk[i]]
  152.         }
  153.     }
  154.     return obj
  155. }
  156.  
  157. function calc40Tracers(){
  158.     if(rngmode==0){ //vanilla
  159.         for(i=0;i<=255;i++){
  160.             rngindex=i;
  161.             let sum=0;
  162.             //rng calls per tracer are as follows:
  163.             //1: P_SpawnMobj rolls for random player target
  164.             //16: 15 rolls(!!!) for damage sum - 1-8 dmg per roll
  165.             //enemy is damaged
  166.             //if the enemy dies: 1 extra call for tic delay on death animation, another call if it is 64 units above you & dmg is under 40???? does RNG get called if the checks fail?
  167.             //if the enemy dies: P_DamageMobj RETURNS now
  168.             //if the enemy survives:
  169.             //17: P_DamageMobj rolls for pain chance
  170.            
  171.             for(j=0;j<40;j++){
  172.                 M_Random()
  173.                 let tsum=0;
  174.                 for(k=0;k<15;k++){
  175.                     tsum+=(M_Random()%8)+1
  176.                 }
  177.                 sum+=tsum
  178.                 M_Random()
  179.             }
  180.             rolls[sum] = (rolls[sum] ?? 0) + 1
  181.         }
  182.     }else if(rngmode==1){//boom
  183.         //just pre-bake all tracer rolls
  184.         rolls=normalise(boomBFGTable)
  185.     }else{//ideal
  186.         let tracerRolls={0:1}
  187.         for(i=0;i<15;i++){
  188.             let newdmg={}
  189.             let rolkeys=Object.keys(tracerRolls);
  190.             for(l=0;l<rolkeys.length;l++){
  191.                 for(j=1;j<=8;j++){
  192.                     let df=j+Number(rolkeys[l])
  193.                     newdmg[df] = (newdmg[df]??0)+tracerRolls[rolkeys[l]]
  194.                 }
  195.             }
  196.             tracerRolls=newdmg
  197.         }
  198.         normalise(tracerRolls)
  199.         let trollkeys = Object.keys(tracerRolls)
  200.        
  201.         let bfgrolls={0:1}
  202.         for(i=0;i<40;i++){
  203.             let newdmg={}
  204.             let rolkeys=Object.keys(bfgrolls)
  205.             for(l=0;l<rolkeys.length;l++){
  206.                 for(j=0;j<trollkeys.length;j++){
  207.                     let dfds = Number(rolkeys[l])+Number(trollkeys[j])
  208.                     newdmg[dfds] = (newdmg[dfds]??0) + (bfgrolls[rolkeys[l]]*tracerRolls[trollkeys[j]])
  209.                 }
  210.             }
  211.             bfgrolls=newdmg
  212.         }
  213.         normalise(bfgrolls)
  214.         rolls=bfgrolls
  215.     }
  216. }
  217.  
  218. let rolls = {}
  219. switch (mode){
  220.     case 0:
  221.         if(rngmode==0){ //vanilla - build table of expected chances of damage values (weighted so a perfect range would be all 1s)
  222.             for(i=0;i<PRNG.length;i++){
  223.                 let roll = ((PRNG[i]%range)+1)*basedmg+splash
  224.                 rolls[roll] = (rolls[roll]??0)+/*(range/256)*/range
  225.             }
  226.         }else if(rngmode==1){//boom
  227.             for(i=0;i<256;i++){ //just iterate 1-256 to match boom expected output
  228.                 let roll = ((i%range)+1)*basedmg+splash
  229.                 rolls[roll] = (rolls[roll]??0)+/*(range/256)*/range
  230.             }
  231.         }else{ //assume perfect range
  232.             for(let j=basedmg+splash;j<=(basedmg*range)+splash;j+=basedmg){
  233.                 rolls[j]=1
  234.             }
  235.         }
  236.         if(rocketmode){
  237.             //first pass under normal conditions...
  238.             let splashdiff = maxsplash-splash
  239.             let newdmg={}
  240.             let rolkeys=Object.keys(rolls)
  241.             console.log(rolls)
  242.             for(j=0;j<rolkeys.length;j++){
  243.                 for(k=0;k<=splashdiff;k++){
  244.                     let df=Number(rolkeys[j])+k
  245.                     newdmg[df] = (newdmg[df]??0)+rolls[rolkeys[j]]
  246.                 }
  247.             }
  248.             //now another pass with extra damage from the enemy walking into the rocket
  249.             let cappeddmg = 128-splash
  250.             for(j=0;j<rolkeys.length;j++){
  251.                 for(k=0;k<=splashdiff;k++){
  252.                     let df=Number(rolkeys[j])+Math.min(k+monsterspeed,cappeddmg)
  253.                     newdmg[df] = (newdmg[df]??0)+(rolls[rolkeys[j]]/(monsterrate-1))
  254.                 }
  255.             }
  256.             console.log(newdmg)
  257.             rolls=newdmg
  258.         }else{
  259.             if(!consistentsplash){ //splash damage range - iterate through and spread out odds evenly across range
  260.                 let splashdiff = maxsplash-splash
  261.                 let newdmg={}
  262.                 let rolkeys=Object.keys(rolls)
  263.                 console.log(rolls)
  264.                 for(j=0;j<rolkeys.length;j++){
  265.                     for(k=0;k<=splashdiff;k++){
  266.                         let df=k+Number(rolkeys[j])
  267.                         newdmg[df] = (newdmg[df]??0)+rolls[rolkeys[j]]
  268.                     }
  269.                 }
  270.                 console.log(newdmg)
  271.                 rolls=newdmg
  272.             }
  273.         }
  274.         break;
  275.     case 1: //shotgun
  276.         if(rngmode==0){ //vanilla
  277.             for(i=0;i<=255;i++){
  278.                 rngindex=i;
  279.                 let sum=0;
  280.                 //rng calls are as follows (i think???):
  281.                 //1: P_GunShot rolls for damage
  282.                 //3: P_GunShot rolls twice for horizontal offset
  283.                 //5: P_SpawnBlood rolls twice for Z offset
  284.                 //6: P_SpawnMobj might roll when blood is spawned? to pick a random player target?
  285.                 //7: P_SpawnBlood rolls for animation tic delay
  286.                 //enemy is damaged
  287.                 //if the enemy dies: 1 extra call for tic delay on death animation, another call if it is 64 units above you & dmg is under 40????
  288.                 // * -- does RNG get called if the height and damage checks fail?? sometimes this extra RNG call doesn't happen after a death?
  289.                 //if the enemy dies: P_DamageMobj RETURNS now
  290.                 //if the enemy survives:
  291.                 //8: P_DamageMobj rolls for pain chance
  292.                
  293.                 for(j=0;j<7;j++){
  294.                     sum+=((M_Random()%3)+1)*5
  295.                     for(d=0;d<7;d++){M_Random()}
  296.                 }
  297.                 rolls[sum] = (rolls[sum] ?? 0) + 1
  298.             }
  299.         }else if(rngmode==1){
  300.             //just use pre-baked SG roll table
  301.             rolls = boomSGTable
  302.         }else{//ideal
  303.             let sgrolls={0:1}
  304.             for(i=0;i<7;i++){
  305.                 let newdmg={}
  306.                 let rolkeys=Object.keys(sgrolls)
  307.                 for(j=0;j<rolkeys.length;j++){
  308.                     for(k=5;k<=15;k+=5){
  309.                         let df=k+Number(rolkeys[j])
  310.                         newdmg[df] = (newdmg[df]??0)+sgrolls[rolkeys[j]]
  311.                     }
  312.                 }
  313.                 console.log(newdmg)
  314.                 sgrolls=newdmg
  315.             }
  316.             rolls=sgrolls
  317.         }
  318.         break;
  319.     case 2: //SSG
  320.         if(rngmode==0){//vanilla
  321.             for(i=0;i<=255;i++){
  322.                 rngindex=i;
  323.                 let sum=0;
  324.                 //rng calls are as follows (i think???):
  325.                 //1: A_FireShotgun2 rolls for damage
  326.                 //3: A_FireShotgun2 rolls twice for horizontal offset
  327.                 //5: A_FireShotgun2 rolls twice for vertical offset
  328.                 //7: P_SpawnBlood rolls twice for Z offset
  329.                 //8: P_SpawnMobj might roll when blood is spawned? to pick a random player target?
  330.                 //9: P_SpawnBlood rolls for animation tic delay
  331.                 //enemy is damaged
  332.                 //if the enemy dies: 1 extra call for tic delay on death animation, another call if it is 64 units above you & dmg is under 40????
  333.                 // * -- does RNG get called if the checks fail?
  334.                 //if the enemy dies: P_DamageMobj RETURNS now
  335.                 //if the enemy survives:
  336.                 //10: P_DamageMobj rolls for pain chance
  337.                
  338.                 for(j=0;j<20;j++){
  339.                     sum+=((M_Random()%3)+1)*5
  340.                     for(d=0;d<9;d++){M_Random()}
  341.                 }
  342.                 rolls[sum] = (rolls[sum] ?? 0) + 1
  343.             }
  344.         }else if(rngmode==1){//boom
  345.             rolls=boomSSGTable
  346.         }else{
  347.             let sgrolls={0:1}
  348.             for(i=0;i<20;i++){
  349.                 let newdmg={}
  350.                 let rolkeys=Object.keys(sgrolls)
  351.                 for(j=0;j<rolkeys.length;j++){
  352.                     for(k=5;k<=15;k+=5){
  353.                         let df=k+Number(rolkeys[j])
  354.                         newdmg[df] = (newdmg[df]??0)+sgrolls[rolkeys[j]]
  355.                     }
  356.                 }
  357.                 console.log(newdmg)
  358.                 sgrolls=newdmg
  359.             }
  360.             rolls=sgrolls
  361.         }
  362.         break;
  363.     case 3:
  364.         calc40Tracers()
  365.         break;
  366.     case 4:
  367.         calc40Tracers()
  368.         console.log(rolls)
  369.         //now do 100-800 from the ball on top of the table
  370.         if(rngmode==0){//vanilla
  371.             let newrolls={}
  372.             let brollkeys=Object.keys(rolls)
  373.             for(j=0;j<brollkeys.length;j++){
  374.                 for(i=0;i<PRNG.length;i++){
  375.                     let roll = Number(brollkeys[j]) + (((PRNG[i]%8)+1)*100)
  376.                     newrolls[roll] = (newrolls[roll]??0)+rolls[brollkeys[j]]
  377.                 }
  378.             }
  379.             rolls=newrolls
  380.         }else{//boom matches ideal distribution here so can use the same code
  381.             let newrolls={}
  382.             let brollkeys=Object.keys(rolls)
  383.             for(j=0;j<brollkeys.length;j++){
  384.                 for(i=100;i<=800;i+=100){
  385.                     let roll = Number(brollkeys[j]) + i
  386.                     newrolls[roll] = (newrolls[roll]??0)+rolls[brollkeys[j]]
  387.                 }
  388.             }
  389.             rolls=newrolls
  390.         }
  391.         break;
  392.     case 5:
  393.         if(rngmode==0){//vanilla
  394.             for(i=0;i<=255;i++){
  395.                 rngindex=i;
  396.                 let sum=0;
  397.                 for(k=0;k<15;k++){
  398.                     sum+=(M_Random()%8)+1
  399.                 }
  400.                 rolls[sum] = (rolls[sum] ?? 0) + 1
  401.             }
  402.         }else if(rngmode==1){//boom
  403.             //just use pre-baked tracer roll table
  404.             rolls = boomTracerTable
  405.         }else{//ideal
  406.             let tracerRolls={0:1}
  407.             for(i=0;i<15;i++){
  408.                 let newdmg={}
  409.                 let rolkeys=Object.keys(tracerRolls);
  410.                 for(l=0;l<rolkeys.length;l++){
  411.                     for(j=1;j<=8;j++){
  412.                         let df=j+Number(rolkeys[l])
  413.                         newdmg[df] = (newdmg[df]??0)+tracerRolls[rolkeys[l]]
  414.                     }
  415.                 }
  416.                 tracerRolls=newdmg
  417.             }
  418.             rolls=tracerRolls
  419.         }
  420.         break;
  421. }
  422. console.log(JSON.stringify(rolls))
  423. normalise(rolls)
  424. console.log("Roll table:\n---")
  425. console.log(rolls)
  426. console.log("---")
  427. let rollkeys=Object.keys(rolls)
  428.  
  429. //repeatedly apply roll table over itself and record probabilities
  430. //TODO: support e3m8 style mixed damage cases, like 3 rockets + indeterminate cells against a mastermind
  431. //maybe this could all use bigints to avoid loss of precision, or some other format like tracking quotients as fractions
  432. for(hit=1;hit<Math.ceil(monsterhealth/(rollkeys[0]))+1;hit++){
  433.     let newdmg={}
  434.     let dmgkeys=Object.keys(dmg)
  435.     for(i=0;i<dmgkeys.length;i++){
  436.         for(let j=0;j<rollkeys.length;j++){
  437.             let dfds=Number(rollkeys[j])+Number(dmgkeys[i])
  438.             newdmg[dfds] = (newdmg[dfds] ?? 0) + (dmg[dmgkeys[i]]*rolls[rollkeys[j]])/rollkeys.length
  439.         }
  440.     }
  441.     //check if any rolls result in death
  442.     let ndmgkeys=Object.keys(newdmg)
  443.     let totdead=0
  444.     for(i=0;i<ndmgkeys.length;i++){
  445.         if(ndmgkeys[i]>=monsterhealth){
  446.             totdead+=newdmg[ndmgkeys[i]]
  447.             delete newdmg[ndmgkeys[i]]
  448.         }
  449.     }
  450.     if(totdead>0){outp[hit]=(100*totdead)} // /(rollkeys.length**hit)}
  451.     dmg=newdmg
  452. }
  453.  
  454. function sround(x, n=5){
  455.     //rounds to n sig figs unless it would lose integer precision
  456.     if(x<10**n){
  457.         return parseFloat(x.toPrecision(n))
  458.     }else{
  459.         return Math.round(x)
  460.     }
  461. }
  462.  
  463. if(rollprint){
  464.     let sum=0
  465.     let avgsum=0
  466.     for(let i=0;i<rollkeys.length;i++){
  467.         sum+=rolls[rollkeys[i]]
  468.         avgsum+=(rolls[rollkeys[i]]*rollkeys[i])
  469.     }
  470.     console.log("Average damage: "+avgsum/rollkeys.length)
  471.     for(let i=0;i<rollkeys.length;i++){
  472.         rolls[rollkeys[i]]/=sum/100
  473.     }
  474.     console.log(rollprint)
  475.     let cum=0
  476.     let str="Damage Chance (%)  Cumulative  Expected"
  477.     for(i=0;i<rollkeys.length;i++){
  478.         cum+=rolls[rollkeys[i]]
  479.         if(roundoutput){
  480.             str=str+"\n"+rollkeys[i]+"  "+sround(rolls[rollkeys[i]])+"  "+sround(cum)+" "+sround(100/cum)
  481.         }else{
  482.             str=str+"\n"+rollkeys[i]+"  "+rolls[rollkeys[i]]+"  "+cum+" "+(100/cum)
  483.         }
  484.     }
  485.     console.log(str)
  486. }
  487.  
  488. let avgsum=0
  489. for(let i=0;i<Object.keys(outp).length;i++){
  490.     let x=Object.keys(outp)[i]
  491.     avgsum+=(Number(x)*outp[x])
  492. }
  493. console.log("Average hits: "+avgsum/100)
  494.  
  495. //print it to easily paste into excel
  496. let str="Shots  Chance"
  497. if(cumprint){str="Shots Chance (%)  Cumulative  Expected"}
  498. let cum=0
  499. for(i=0;i<Object.keys(outp).length;i++){
  500.     let x=Object.keys(outp)[i]
  501.     cum+=outp[x]
  502.     if(roundoutput){
  503.         str=str+"\n"+x+"    "+sround(outp[x])
  504.         if(cumprint){str=str+"  "+sround(cum)+" "+sround(100/cum)}
  505.     }else{
  506.         str=str+"\n"+x+"    "+outp[x]
  507.         if(cumprint){str=str+"  "+cum+" "+(100/cum)}
  508.     }
  509. }
  510. console.log(str)
  511. console.log("Run in "+(Date.now()-t)+"ms")
  512.  
Add Comment
Please, Sign In to add comment