iMagicBae

Level/Gmap Screenshot Tool

Jan 28th, 2022 (edited)
348
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. // Scripted by iMagic
  2. function onActionServerside(temp.action) {
  3.   switch (temp.action) {
  4.     case "warpMe":
  5.       temp.coords = params[1];
  6.       player.setLevel2(temp.coords[0], temp.coords[1], temp.coords[2]);
  7.       player.triggerClient("weapon", this.name, "finishedWarping");
  8.     break;
  9.   }
  10. }
  11.  
  12. //#CLIENTSIDE
  13. function onActionClientside(temp.action) {
  14.   this.trigger("onActionClientsideDone");
  15. }
  16.  
  17. function onPlayerChats() {
  18.   temp.tokens = player.chat.tokenize(" ");
  19.   switch (temp.tokens[0]) {
  20.     case "/sc":
  21.       if (temp.tokens[1] && temp.tokens[2]) {
  22.         temp.data = {temp.tokens[1], temp.tokens[2]};
  23.       } else {
  24.         temp.data = temp.tokens[1];
  25.       }
  26.  
  27.       generateLevelScreenshot(temp.data);
  28.     break;
  29.   }
  30. }
  31.  
  32. function generateLevelScreenshot(temp.data, temp.returnB64) {
  33.     // The coordinates the player started at
  34.   temp.coords = {player.x, player.y};
  35.   temp.sc.level_width = player.level.width;
  36.   temp.sc.level_height = player.level.height;
  37.     // The amount of screenshots/steps it will take to
  38.     // collect all images for the final render
  39.   temp.sc.screenshots = {player.level.width / 32, player.level.height / 32};
  40.  
  41.   /* DETERMINE SCALE FROM DIMENSIONS */
  42.   if (temp.data.size() == 2) {
  43.     temp.sc.pixel_width = constrain(32, 2048, temp.data[0]);
  44.     temp.sc.pixel_height = constrain(32, 2048, temp.data[1]);
  45.      
  46.     temp.sc.scale_width = 2048 / temp.sc.pixel_width;
  47.     temp.sc.scale_height = 2048 / temp.sc.pixel_height;
  48.   } else {
  49.     /* DETERMINE DIMENSIONS FROM SCALE */
  50.    
  51.       // Ensure the scale is a valid decimal percentage
  52.     temp.scale = temp.data == NULL || !(temp.data in |0.02, 1|) ? 1 : temp.data;
  53.    
  54.       // Calculate the width/height of the final render given the scale
  55.     temp.sc.pixel_width = (temp.sc.level_width * 16) * temp.scale;
  56.     temp.sc.pixel_height = (temp.sc.level_height * 16) * temp.scale;
  57.     /* The script can only produce images smaller than 2048x2048
  58.        We must ensure the final dimensions are not larger than this
  59.        otherwise we will end up with an incomplete screenshot.
  60.  
  61.        We'll need to igure out how much we need to downscale the canvas
  62.        so that both dimensions are under 2048px while still keeping the
  63.        same aspect ratio.
  64.     */
  65.     temp.sc.scale_width = 2048 / temp.sc.pixel_width;
  66.     temp.sc.scale_height = 2048 / temp.sc.pixel_height;
  67.    
  68.       // If either of these measurement is less than 1 it means
  69.       // at least one of them needs to be shrinked...
  70.       // and to keep aspect-ratio we should shrink both.
  71.     if (temp.sc.scale_width < 1 || temp.sc.scale_height < 1) {
  72.       temp.min = min(temp.sc.scale_width, temp.sc.scale_height);
  73.         // Make the scale factor the same to preserve the aspect ratio  
  74.       temp.sc.scale_width = temp.sc.scale_height = temp.min;
  75.  
  76.       temp.sc.pixel_width *= temp.min;
  77.       temp.sc.pixel_height *= temp.min;
  78.     }
  79.   }
  80.  
  81.   /* Recalculate the final dimensions again but ensure
  82.      that the width and the height are integer values.
  83.      Skipping this step may lead to weird 1px gaps
  84.      on the final render.
  85.   */
  86.  
  87.   temp.sc.pixel_width -= temp.sc.pixel_width % temp.sc.screenshots[0];
  88.   temp.sc.pixel_height -= temp.sc.pixel_height % temp.sc.screenshots[1];
  89.  
  90.     // Recalculate the scales now that the width/height are integers
  91.   temp.sc.scale_width = 2048 / temp.sc.pixel_width;
  92.   temp.sc.scale_height = 2048 / temp.sc.pixel_height;
  93.  
  94.     // Create the GUI object that will generate the final image
  95.   this.canvas = new GuiDrawingPanel();
  96.   this.canvas.resize(0, 0, temp.sc.pixel_width, temp.sc.pixel_height);
  97.   this.canvas.visible = true;
  98.  
  99.   this.screenshotsTaken = {};
  100.  
  101.   if (player.gmap != NULL) {
  102.     /* This boolean signals to the script that the
  103.        player should be warped serverside so that we
  104.        ensure the tiles and npcs load properly. Without
  105.        this there will be black patches on the final render
  106.        that occur whenever the player warps from, say, x: 700
  107.        back to 0.
  108.     */
  109.     temp.isLargeGmap = temp.sc.screenshots[0] >= 10;
  110.   }
  111.  
  112.   for (temp.y = 0; temp.y < temp.sc.level_height; temp.y += 32) {
  113.     for (temp.x = 0; temp.x < temp.sc.level_width; temp.x += 32) {
  114.       /* If the client's screen were 1024x1024 pixels
  115.          we could take one screenshot of the entire level
  116.          and call it a day. However, this is not the case
  117.          for most displays, so instead we go in increments
  118.          of 32 tiles instead of 64 taking screenshots of
  119.          512x512, effectively half the level
  120.  
  121.          NOTE
  122.          Using makeScreenshot2() for a resolution greater
  123.          than the client's screen WILL crash the client.
  124.          That is also another reason why we do 1 entire
  125.          screenshot per level
  126.       */
  127.       onScreenshotLevel(temp.x, temp.y, {temp.sc.pixel_width, temp.sc.pixel_height}, temp.sc.screenshots, temp.isLargeGmap);
  128.         // Wait for a signal to keep taking the other screenshots
  129.       waitfor(this, "ContinueScreenshot", 10);
  130.     }
  131.   }
  132.  
  133.   /* The screenshots NEED to be saved on the
  134.    * scriptfiles folder in order to be read
  135.    * later on to extract the base64. Storing
  136.    * the screenshot on the root graal
  137.    * directory will make the file unreadable
  138.    * by script.
  139.   */
  140.   temp.sc.fileName = extractFilebase(player.gmap == NULL ? player.level.name : player.gmap);
  141.   temp.sc.fileName = "scriptfiles/" @ temp.sc.fileName @ ".png";
  142.     // Save the final render
  143.   this.canvas.saveimage(temp.sc.fileName);
  144.   this.canvas.destroy();
  145.  
  146.   if (temp.returnB64) {
  147.       // Load the raw image data string and extract the base64
  148.     temp.data.loadstring(temp.sc.fileName);
  149.     temp.b64 = base64encode(temp.data);
  150.     this.screenshots.add(temp.sc.fileName);
  151.   }
  152.  
  153.     // Let's delete the screenshots we generated earlier
  154.   for (temp.screenshot : this.screenshotsTaken) {
  155.     deletefile(temp.screenshot);
  156.   }
  157.  
  158.   // Uncomment this for fun stats
  159.   //echo(temp.sc.savejsontostring(1));
  160.   this.screenshots = NULL;
  161.  
  162.    // Reset the player back to their original position
  163.   player.x = temp.coords[0];
  164.   player.y = temp.coords[1];
  165.   return temp.b64 == NULL ? temp.sc.fileName : temp.b64;
  166. }
  167.  
  168. function onScreenshotLevel(temp.x, temp.y, temp.finalDimensions, temp.screenshots, temp.gmapWarping) {
  169.   if (temp.gmapWarping) {
  170.     triggerServer("weapon", this.name, "warpMe", {player.gmap, temp.x, temp.y});
  171.     waitfor(this, "onActionClientsideDone", 10);
  172.   } else {
  173.       // Make sure all of the NPCs are loaded in the sector being screenshotted by warping the player there.
  174.     player.x = temp.x;
  175.     player.y = temp.y;
  176.  
  177.     /* Give time for things to load around the player, removing this sleep
  178.      * might cause the level not to load properly and chunks of it
  179.      * might be missing on the final screenshot. It can also cause
  180.      * the client to crash
  181.     */
  182.     waitfor(this, "Nothing", 0.1);
  183.   }
  184.  
  185.   temp.resolution = {512, 512};
  186.   temp.scale = {temp.finalDimensions[0] / (temp.resolution[0] * temp.screenshots[0]), temp.finalDimensions[1] / (temp.resolution[1] * temp.screenshots[1])};
  187.     // Coordinates for this screenshot on the overall image
  188.   temp.ss.x = (temp.x * 16) * temp.scale[0];
  189.   temp.ss.y = (temp.y * 16) * temp.scale[1];
  190.   temp.ss.width = temp.resolution[0] * temp.scale[0];
  191.   temp.ss.height = temp.resolution[1] * temp.scale[1];
  192.  
  193.     // Create a unique temporal name for this screenshot.
  194.   temp.fileName = format("temp_generate_%s_%s.png", temp.x, temp.y);
  195.     // Take the screenshot of the level
  196.   makeScreenshot2(temp.fileName, 0x4, temp.x, temp.y, 0, temp.resolution[0], temp.resolution[1]);
  197.     // Add the screenshot we just took to the overall image being created
  198.   this.canvas.drawimagestretched(
  199.       // Insert the stretched-down image to a specific part of the overall image.
  200.     temp.ss.x, // X coordinate this image is being inserted into the final image
  201.     temp.ss.y, // Y coordinate this image is being inserted into the final image
  202.     temp.ss.width, // Width of this image in the overall image
  203.     temp.ss.height, // Height of this image in the overall image
  204.       // Select which part of this screenshot we just took we want to add to the overall image (we want to add the whole screenshot)
  205.     temp.fileName, 0, 0, temp.resolution[0], temp.resolution[1]
  206.   );
  207.  
  208.     // Add the name of this screenshot to an array to be deleted later on.
  209.   this.screenshotsTaken.add(temp.fileName);
  210.     // Trigger the loop to keep going.
  211.   this.trigger("ContinueScreenshot");
  212. }
  213.  
  214. function constrain(temp.min, temp.max, temp.number) {
  215.   return max(temp.min, min(temp.max, temp.number));
  216. }
  217.  
Add Comment
Please, Sign In to add comment