screeps查找房间

screeps游戏中想查找某个合适的房间,比如想要房间中元素矿的含量大于45000。但是点开房间看又不方便,于是本脚本应运而生。

打开screeps官网(建议使用新版地图),控制台粘贴源码,然后调用适当的方法。

方法速览

  1. 查看E5S15周围5格房间
helper.search('E5S15', 5).then(() => helper.log().draw())
  1. 查看E5S15到E10S15之间的房间
helper.search('E5S15', 'E10S15').then(() => helper.log().draw())
  1. 查看E5S15周围T矿大于45000的房间
helper.search('E5S15', 5).then(() => helper.filter(data => data.T >= 45000).log().draw())

源码

/**
 * 寻找合适的房间
 *
 * 1. helper.search(roomName, roomNameOrRange)得到房间数据
 * @param {String} roomName 起始房间
 * @param {String|Number} roomNameOrRange 目标房间或者范围,默认为5。如果传入房间名,则搜索由roomName和roomNameOrRange组成的矩形区域内的房间;
 * 否则搜索以roomName为中心,roomNameOrRange为半径的范围内的房间
 * 2. helper.log()打印房间数据
 * 3. helper.draw()在地图上绘制信息,注意,在新版地图才能使用该api
 *
 * 如果helper配置了myName,则会过滤掉别人的房间或者外矿,如helper.setConfig({myName: 'ganyu'}).search('E13S7').then(() => helper.filter((data) => data.T >= 45000).log().draw())
 */

const utils = {
  /**
   * 验证房间名是否合法
   * @param {String} roomName 房间名
   * @returns {Boolean}
   */
  validateRoomName(roomName) {
    return /^[WE]\d+[NS]\d+$/.test(roomName);
  },

  /**
   * 判断一个房间是否过道房间,如果房间名中带有0则是过道
   * @param {String} roomName 房间名
   * @returns {Boolean}
   */
  isHighWayRoom(roomName) {
    return roomName.includes("0");
  },

  /**
   * 判断一个房间是否是九房
   * @param {String} roomName 房间名
   * @returns {Boolean}
   */
  isCenterNineRoom(roomName) {
    const [_, x, y] = roomName
      .match(/[EW](\d+)[SN](\d+)/)
      .map((num) => Number(num) % 10);
    return x >= 4 && x <= 6 && y >= 4 && y <= 6;
  },

  /**
   * 将房间名转为坐标,参考点E0S0 -> {x: 0, y: 0}
   * @param {String} roomName 房间名
   * @returns {{x: number, y: number}} {x, y}
   */
  roomNameToXY(roomName) {
    // 解析房间名
    const parse = roomName.match(/([EW])(\d+)([NS])(\d+)/);
    const x = parse[1] === "W" ? ~parseInt(parse[2]) : parseInt(parse[2]);
    const y = parse[3] === "N" ? ~parseInt(parse[4]) : parseInt(parse[4]);
    return {
      x,
      y,
    };
  },

  /**
   * 将坐标转为房间名
   * @param {Number} x x坐标
   * @param {Number} y y坐标
   * @returns {String} 房间名
   */
  xyToRoomName(x, y) {
    return (x < 0 ? "W" + ~x : "E" + x) + (y < 0 ? "N" + ~y : "S" + y);
  },

  /**
   * 计算两个房间的距离
   * @param roomName1 房间名1
   * @param roomName2 房间名2
   * @returns {Number} 距离
   */
  calcRoomDistance(roomName1, roomName2) {
    const { x: x1, y: y1 } = roomNameToXY(roomName1);
    const { x: x2, y: y2 } = roomNameToXY(roomName2);
    return Math.max(Math.abs(x1 - x2), Math.abs(y1 - y2));
  },
};

const helper = {
  baseURL: "https://screeps.com",
  myName: "",
  roomCache: {},
  searchResult: {},
  token: "",

  setConfig(config) {
    Object.keys(config).forEach((key) => (this[key] = config[key]));
    return this;
  },

  /**
   *
   * @param {String} roomName
   * @param {String | Number} roomNameOrRange
   * @param {*} config
   */
  async search(roomName, roomNameOrRange = 5, config = {}) {
    if (typeof roomName !== "string" || !utils.validateRoomName(roomName)) {
      throw `参数${roomName}不合法,请传入正确的房间名`;
    }
    let roomNames = [];
    const { x: x1, y: y1 } = utils.roomNameToXY(roomName);
    if (typeof roomNameOrRange === "string") {
      if (!utils.validateRoomName(roomNameOrRange)) {
        throw `参数${roomNameOrRange}不合法,请传入正确的房间名`;
      }
      const { x: x2, y: y2 } = utils.roomNameToXY(roomNameOrRange);
      for (let x = Math.min(x1, x2); x <= Math.max(x1, x2); x++) {
        for (let y = Math.min(y1, y2); y <= Math.max(y1, y2); y++) {
          roomNames.push(utils.xyToRoomName(x, y));
        }
      }
    } else if (typeof roomNameOrRange === "number" && roomNameOrRange >= 0) {
      roomNameOrRange = Math.ceil(roomNameOrRange);
      for (let dx = -roomNameOrRange; dx <= roomNameOrRange; dx++) {
        for (let dy = -roomNameOrRange; dy <= roomNameOrRange; dy++) {
          roomNames.push(utils.xyToRoomName(x1 + dx, y1 + dy));
        }
      }
    } else {
      throw `参数${roomNameOrRange}不合法,请传入正确的房间名或者正整数`;
    }
    roomNames = roomNames.filter(
      (r) => !utils.isCenterNineRoom(r) && !utils.isHighWayRoom(r)
    );
    if (!roomNames.length) return;

    this.searchResult = {};
    const shard = this.getShard(config.shard);
    if (!this.roomCache[shard]) this.roomCache[shard] = {};
    const shardCache = this.roomCache[shard];

    await Promise.all(
      roomNames.map(async (r) => {
        if (shardCache[r]) {
          this.searchResult[r] = shardCache[r];
          return;
        }
        const data = await this.getRoomObjects(r, shard);
        if (!data.ok) return;
        const { objects, users } = data;
        if (
          Object.keys(users).find(
            (id) =>
              this.myName &&
              users[id].username !== this.myName &&
              !["Invader", "Source Keeper"].includes(users[id].username)
          )
        ) {
          return;
        }
        this.searchResult[r] = {};
        const minerals = objects.filter((o) => o.type === "mineral");
        for (const mineral of minerals) {
          this.searchResult[r][mineral.mineralType] = mineral.mineralAmount;
        }
        shardCache[r] = this.searchResult[r];
      })
    );
    return this;
  },

  /**
   * 过滤
   *
   */
  filter(fn) {
    if (typeof fn !== "function") return this;
    Object.keys(this.searchResult).forEach((r) => {
      if (!fn(this.searchResult[r])) {
        delete this.searchResult[r];
      }
    });
    return this;
  },

  /**
   * 打印结果
   */
  log() {
    console.log(this.searchResult);
    return this;
  },

  /**
   * 绘制结果
   */
  draw(
    fn = (data) =>
      Object.entries(data)
        .map((v) => v.join(" "))
        .join("\\n")
  ) {
    if (!window.location.href.includes("map2")) {
      console.log(
        `%c请在新地图中使用draw方法:${window.location.href.replace(
          "map",
          "map2"
        )}`,
        "color:red;"
      );
      return this;
    }
    const colors = ["#ffffff", "#d7efac", "#ff96c7", "#ae47b5", "#bb5626"];
    const getColor = (amount) => {
      if (amount < 1e4) return colors[0];
      return colors[Math.min(Math.floor(amount / 2e4) + 1, colors.length)];
    };
    const initExpression =
      "const R=(r)=>new RoomPosition(0,10,r);const A=(text,r,config)=>[text,R(r),{fontSize:7,align:'left',...config}];";
    let expression = initExpression;
    let first = true;
    for (const r in this.searchResult) {
      if (expression.length > 800) {
        this.submit(expression);
        expression = initExpression;
        first = true;
      }
      const text = fn(this.searchResult[r], r);
      if (first) {
        expression += `Game.map.visual.text(...A("${text}","${r}",{color:"${getColor(
          this.searchResult[r].T || 0
        )}"}))`;
        first = false;
      } else {
        expression += `.text(...A("${text}","${r}",{color:"${getColor(
          this.searchResult[r].T || 0
        )}"}))`;
      }
    }
    console.log(expression);
    this.submit(expression);
    return this;
  },

  /**
   * 获取当前所在shard
   * @param {'shard0' | 'shard1' | 'shard2' | 'shard3' | 'shardSeason'} shard 分片
   * @returns {'shard0' | 'shard1' | 'shard2' | 'shard3' | 'shardSeason'}
   */
  getShard(shard) {
    if (["shard0", "shard1", "shard2", "shard3", "shardSeason"].includes(shard))
      return shard;
    return window.location.href.match(/(shard\w+)/)[1] || "shardSeason";
  },

  /**
   * 构造url前缀
   */
  buildBaseURL() {
    if (window.location.href.includes("season"))
      return this.baseURL + "/season";
    return this.baseURL;
  },

  /**
   * 获取房间对象
   * @param {String} roomName 房间名
   * @param {'shard0' | 'shard1' | 'shard2' | 'shard3' | 'shardSeason'} shard 分片
   */
  async getRoomObjects(roomName, shard = this.getShard()) {
    const response = await fetch(
      `${this.buildBaseURL()}/api/game/room-objects?room=${roomName}&shard=${shard}`,
      {
        method: "GET",
        headers: {
          "Content-Type": "application/json",
        },
      }
    );
    return response.json();
  },

  /**
   * 提交代码
   * @param {String} expression 代码语句
   * @param {'shard0' | 'shard1' | 'shard2' | 'shard3' | 'shardSeason'} shard 分片
   */
  async submit(expression, shard = this.getShard()) {
    const response = await fetch(`${this.buildBaseURL()}/api/user/console`, {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
        "x-token": this.token || localStorage.getItem("auth").slice(1, -1),
      },
      body: JSON.stringify({
        expression,
        shard,
      }),
    });
    return response.json();
  },
};
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容