文章目录

  • 前言
  • 一、进制转换
    • 将二进制、八进制、十六进制转换为十进制
    • 将二进制、八进制、十六进制转换为十进制
  • 二、获取数据修改关卡
    • 小技巧进制转换
  • 三、获取数据修改阳光
  • 四、通过编码修改关卡和金钱
    • 修改关卡
    • 十进制转十六进制,小于16的十进制前面补一个0
    • 修改金钱
    • Java16进制的高低字节序转化
  • 五、完整代码

前言

植物大战僵尸作为一个集趣味性和益智性为一身的经典塔防类游戏,在推出之初便收获了广泛的关注及好评。推出十年间,不断有围绕该游戏不同的攻略,不同的二创、三创作品。使得人们对该游戏的热度不减。然而,作为经典单机游戏,它的有趣性不仅体现在于其本身画面设计精美、游戏数据设计合理,还在与其有强大的可再修改性。通过修改当中完全开源的内容,玩家可以自行升高或降低难度,自行调节关卡进度,自行查看场地数据,达到了十足的可玩性。本文从最基本的关卡和金币修改出发,探究植物大战僵尸游戏中蕴藏的数据原理。


一、进制转换

不同进制之间的转换在编程中经常会用到,尤其是C语言。

将二进制、八进制、十六进制转换为十进制

二进制、八进制和十六进制向十进制转换都非常容易,就是“按权相加”。所谓“权”,也即“位权”。

假设当前数字是N进制,那么:

对于整数部分,从右往左看,第i位的位权等于Ni-1

对于小数部分,恰好相反,要从左往右看,第j位的位权为N-j。

更加通俗的理解是,假设一个多位数(由多个数字组成的数)某位上的数字是1,那么它所表示的数值大小就是该位的位权。

例如,将八进制数字53627转换成十进制:

53627 = 5×84 + 3×83 + 6×82 + 2×81 + 7×80 = 22423(十进制)

从右往左看,第1位的位权为 80=1,第2位的位权为 81=8,第3位的位权为 82=64,第4位的位权为 83=512,第5位的位权为 84=4096 …… 第n位的位权就为 8n-1。将各个位的数字乘以位权,然后再相加,就得到了十进制形式。

注意,这里我们需要以十进制形式来表示位权。

再如,将十六进制数字9FA8C转换成十进制:

9FA8C = 9×164 + 15×163 + 10×162 + 8×161 + 12×160 = 653964(十进制)

从右往左看,第1位的位权为160=1,第2位的位权为 161=16,第3位的位权为 162=256,第4位的位权为 163=4096,第5位的位权为 164=65536 …… 第n位的位权就为16n-1。将各个位的数字乘以位权,然后再相加,就得到了十进制形式。

将二进制数字转换成十进制也是类似的道理:

11010 = 1×24 + 1×23 + 0×22 + 1×21 + 0×20 = 26(十进制)

从右往左看,第1位的位权为20=1,第2位的位权为21=2,第3位的位权为22=4,第4位的位权为23=8,第5位的位权为24=16 …… 第n位的位权就为2n-1。将各个位的数字乘以位权,然后再相加,就得到了十进制形式。

将二进制、八进制、十六进制转换为十进制

将十进制转换为其它进制时比较复杂,整数部分和小数部分的算法不一样。

  1. 整数部分
    十进制整数转换为 N 进制整数采用“除 N 取余,逆序排列”法。具体做法是:
    将 N 作为除数,用十进制整数除以 N,可以得到一个商和余数;
    保留余数,用商继续除以 N,又得到一个新的商和余数;
    仍然保留余数,用商继续除以 N,还会得到一个新的商和余数;

如此反复进行,每次都保留余数,用商接着除以 N,直到商为 0 时为止。

把先得到的余数作为 N 进制数的低位数字,后得到的余数作为 N 进制数的高位数字,依次排列起来,就得到了 N 进制数字。
例如 将十进制数字 36926 转换成八进制:

从图中得知,十进制数字 36926 转换成八进制的结果为 110076。将例如 将十进制数字 42 转换成二进制:

从图中得知,十进制数字 42 转换成二进制的结果为 101010。

二、获取数据修改关卡

用户玩游戏的数据一般 会存放到本地 通过百度可以发现植物大战僵尸的存放路径是(C:\ProgramData\PopCap Games\PlantsVsZombies\userdata)
当你通关几局游戏时你会发现 userdata文件里有几个文件,有.dat后缀的文件打开后都是特殊符号,任务文档有提示说有十六进制的转换
这里 作者使用的是 Hex Editor Neo 也可以使用Notepad++ 安装插件,或者 Sublime Text 3

为了了解通关后用户数据的变动,先用 Hex Editor Neo 打开需要观察的用户数据文件

经过作者的不断尝试最终发现

关卡 10进制 16进制
1-1 01 01
1-2 02 02
2-1 11 0b
3-1 21 15
5-1 41 29
最终关 5-10 50 32

小技巧进制转换

作者也是经过不断的学习掌握的进制转换
在这个教大家个小技巧
可以通过电脑自带的计算机 切换成 程序员模式

三、获取数据修改阳光

通过修改关卡 到 5-2 (42)2a 的时候商店已经开启了
再打打游戏收集一些钱(捡钱好难)当然 你也可以直接通过更改第一行 08的位置

作者通过 收集金币 收集到的金币数量是 230个 然后通过转换成16进制 e6 通过仔细查找 未查找到数据
通过作者不断地尝试发现 将23 转换成16进制 得到数据 17 成功查找到数据。那么钱数为十六进制转十进制后加一个0
经过尝试
作者 发现如果进制转换成 后二位的化 只需要更改成08 位置的数据就可以了但是地址08能存储的金币最大值是2550枚(ff的10倍),商店里有很多商品的价格都超过了2550,所以应该是使用了多位地址存储金币数量;

四、通过编码修改关卡和金钱

修改关卡

  // 修改关卡public void update() {Scanner input = new Scanner(System.in);System.err.println("格式 1-1 输入1 2-3 输入13 5-2 输入 42");System.out.println("请输入您现在的关卡");tempNum = decToHexStringDiv(Integer.parseInt(input.next()));System.out.println("您想更改的关卡:");tempNum2 = decToHexStringDiv(Integer.parseInt(input.next()));System.err.println("16进制当前关卡:" + tempNum);System.err.println("16进制更改的关卡:" + tempNum2);if (Integer.parseInt(tempNum2, 16) > 50) {System.out.println("对不起您输入的关数不存在");System.exit(0);}pattern = new String[][] {{tempNum, tempNum2}};}

十进制转十六进制,小于16的十进制前面补一个0

  /** 十进制转十六进制,小于16的十进制前面补一个0 */public static String decToHexStringDiv(int n) {int flag = n;StringBuffer s = new StringBuffer();String a;char[] b = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};while (n != 0) {s = s.append(b[n % 16]);n = n / 16;}a = s.reverse().toString();if (flag < 16) {a = 0 + a;}return a;}

当然也可 通过java 自带的方法进行修改

修改金钱

通过观察金钱的数据

发现金钱使用了多位地址存储金币数量; 这四个位置就是存储
例子
1a 0a 6666 存储位置 是在 08(0a) 09(1a) 通过规律发现 采用的 是高低字节来存储金币数量

// 修改金钱private void updateMoney() {Scanner input = new Scanner(System.in);System.out.println("请输入您当前拥有的金币:");tempNum = decToHexStringDiv(Integer.parseInt(input.next()));System.out.println("您想更改的金币数量:");tempNum2 = decToHexStringDiv(Integer.parseInt(input.next()));System.out.println("16现在金币:" + tempNum);System.out.println("16想去金币:" + tempNum2);// 地址08能存储的金币最大值是2550枚(ff的10倍),商店里有很多商品的价格都超过了2550,所以应该是使用了多位地址存储金币数量;if (Integer.parseInt(tempNum, 16) > 255) {System.out.println("输入的字节大于255");System.out.println("要查找的字符:" + reverseHex(tempNum));tempNum = reverseHex(tempNum);}if (Integer.parseInt(tempNum2, 16) > 255) {for (int i = 0; i <= tempNum2.length() - tempNum.length(); i++) {// 计算 要更改的数据长度tempNum += "0";}System.err.println("16进制当前金币:" + tempNum);System.err.println("16进制更改的金币:" + tempNum2);tempNum2 = reverseHex(tempNum2);System.err.println("16进制低字节转换:" + tempNum2);}pattern = new String[][] {{tempNum, tempNum2}};}

Java16进制的高低字节序转化

  //  Java16进制的高低字节序转化public static String reverseHex(String hex) {char[] charArray = hex.toCharArray();int length = charArray.length;int times = length / 2;for (int c1i = 0; c1i < times; c1i += 2) {int c2i = c1i + 1;char c1 = charArray[c1i];char c2 = charArray[c2i];int c3i = length - c1i - 2;int c4i = length - c1i - 1;charArray[c1i] = charArray[c3i];charArray[c2i] = charArray[c4i];charArray[c3i] = c1;charArray[c4i] = c2;}return new String(charArray);}

五、完整代码

package com.ghcare.demo;/*** RO Utility Mainly used for: 1.Double Open client 2.Open Unlimited View* 这是个样本程序,是我针对游戏修改写的。主要作用是将游戏文件用16进制打开,然后 修改相关的部分,然后保存。** @author Ciro Deng(cdtdx@sohu.com)* @version 1.0*/
import java.io.*;
import java.util.Scanner;/*** RO Utility Mainly used for: 1.Double Open client 2.Open Unlimited View** @author Ciro Deng(cdtdx@sohu.com)* @version 1.0*/
public class MainTool {private final String RO_HOME ="C:\\ProgramData\\PopCap Games\\PlantsVsZombies\\userdata\\"; // 修改文件的路径private final String FILE = "user1"; // 修改文件的主文件名private final String BAK_FILE = FILE + "_BAK.dat"; // 修改文件的备份扩展名private final String PATCH_FILE = FILE + ".dat"; // 修改文件的扩展名/*** 进行16进制替换的规则定义 Pattern Array Example: pattern[0][0] = "Original Hex String"; 原16进制字符串* pattern[0][1] = "New Hex String"; 要替换的16进制字符串*//*** 更改替换关卡** @param*/String tempNum = null;String tempNum2 = null;private String[][] pattern = {{"ff00", "0a1a"}};/*** Main方法 main** @param args* @throws IOException*/public static void main(String[] args) throws IOException {MainTool tool = new MainTool();tool.patch();}//  Java16进制的高低字节序转化public static String reverseHex(String hex) {char[] charArray = hex.toCharArray();int length = charArray.length;int times = length / 2;for (int c1i = 0; c1i < times; c1i += 2) {int c2i = c1i + 1;char c1 = charArray[c1i];char c2 = charArray[c2i];int c3i = length - c1i - 2;int c4i = length - c1i - 1;charArray[c1i] = charArray[c3i];charArray[c2i] = charArray[c4i];charArray[c3i] = c1;charArray[c4i] = c2;}return new String(charArray);}/** 十进制转十六进制,小于16的十进制前面补一个0 */public static String decToHexStringDiv(int n) {int flag = n;StringBuffer s = new StringBuffer();String a;char[] b = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};while (n != 0) {s = s.append(b[n % 16]);n = n / 16;}a = s.reverse().toString();if (flag < 16) {a = 0 + a;}return a;}/** 备份文件恢复 ture the backup file into real file */public void restore() {if (isExistBackup()) {new File(RO_HOME + PATCH_FILE).delete();new File(RO_HOME + BAK_FILE).renameTo(new File(RO_HOME + PATCH_FILE));System.out.println("[----------------Restore file OK!--------------------]");} else {System.out.println("Backup file does not exist!");System.exit(0);}}public void init() { // 初始化操作if (new File(RO_HOME + PATCH_FILE).exists()) {System.out.println("[-------------初始化原始文件OK!-----------]");} else {System.out.println("文件不存在!请先恢复它!");}// 备份原始文件if (!isExistBackup()) {new File(RO_HOME + PATCH_FILE).renameTo(new File(RO_HOME + BAK_FILE));}System.out.println("[---------------请选择您的行动------------]");System.out.println("1:修改双打开和查看模式!");System.out.println("2:恢复原来的模式!");System.out.println("请输入1或2,然后输入:");}public void success() { // 成功操作提示System.out.println();System.out.println("[-------------Patch file OK! Have fun with RO!-------]");System.out.println("[-------------补丁文件 OK !和RO玩得开心!-------]");}/*** 进行16进制替换 replace input Hex String with defined pattern 将输入的十六进制字符串替换为已定义的模式** @param original* @return*/public String replace(String original) {for (int i = 0; i < pattern.length; i++) {original = original.replaceAll(pattern[i][0].toLowerCase(), pattern[i][1].toLowerCase());if (original.contains(pattern[i][0].toLowerCase())) {System.out.println("替换成功");}}return original;}/*** 将文件读取为16进制String Read original File and transfer it into Hex String 读取原始文件并将其转换为十六进制字符串** @return* @throws IOException*/public String readOriginal2Hex() throws IOException {FileInputStream fin = new FileInputStream(new File(RO_HOME + BAK_FILE));StringWriter sw = new StringWriter();int len = 1;byte[] temp = new byte[len];/*16进制转化模块*/for (; (fin.read(temp, 0, len)) != -1; ) {if (temp[0] > 0xf && temp[0] <= 0xff) {sw.write(Integer.toHexString(temp[0]));} else if (temp[0] >= 0x0 && temp[0] <= 0xf) { // 对于只有1位的16进制数前边补“0”sw.write("0" + Integer.toHexString(temp[0]));} else { // 对于int<0的位转化为16进制的特殊处理,因为Java没有Unsigned int,所以这个int可能为负数sw.write(Integer.toHexString(temp[0]).substring(6));}}return sw.toString();}/*** 将替换后的16进制字符串写回文件 write replaced original String to file** @param replaced* @throws NumberFormatException* @throws IOException*/public void writeNew2Binary(String replaced) throws NumberFormatException, IOException {FileOutputStream fout = new FileOutputStream(RO_HOME + PATCH_FILE);for (int i = 0; i < replaced.length(); i = i + 2) {fout.write(Integer.parseInt(replaced.substring(i, i + 2), 16));}}/*** test direct output string to file** @param temp* @throws IOException*/public void writeTest(String temp) throws IOException {FileOutputStream fout = new FileOutputStream(RO_HOME + "test.txt");for (int i = 0; i < temp.length(); i++) {fout.write(temp.charAt(i));}}/*** check if the backup file exists 检查备份文件是否存在** @return*/public boolean isExistBackup() {return new File(RO_HOME + BAK_FILE).exists();}/*** 主要操作方法,组织工作流程 Main process method** @throws IOException*/public void patch() throws IOException {// initinit();// 输入参数:// 1:进行查找替换// 2:将备份文件恢复String input = new BufferedReader(new InputStreamReader(System.in)).readLine();if (input.equals("1")) {System.out.println("输入1更改关卡,输入2更改金币,输入其他数字进行手动更改");String selectOption = new BufferedReader(new InputStreamReader(System.in)).readLine();if (selectOption.equals("1")) {update();} else if (selectOption.equals("2")) {updateMoney();// TODO 修改阳光} else {// 手动模式更}String temp = null;temp = readOriginal2Hex();temp = replace(temp);writeNew2Binary(temp);success();} else if (input.equals("2")) {restore();} else {System.out.println("Bad input parameter!");System.exit(0);}}// 修改金钱private void updateMoney() {Scanner input = new Scanner(System.in);System.out.println("请输入您当前拥有的金币(自动加0):");tempNum = decToHexStringDiv(Integer.parseInt(input.next()));System.out.println("您想更改的金币数量(自动加零):");tempNum2 = decToHexStringDiv(Integer.parseInt(input.next()));System.out.println("16现在金币:" + tempNum);System.out.println("16想去金币:" + tempNum2);// 地址08能存储的金币最大值是2550枚(ff的10倍),商店里有很多商品的价格都超过了2550,所以应该是使用了多位地址存储金币数量;if (Integer.parseInt(tempNum, 16) > 255) {System.out.println("输入的字节大于255");System.out.println("要查找的字符:" + reverseHex(tempNum));tempNum = reverseHex(tempNum);}if (Integer.parseInt(tempNum2, 16) > 255) {for (int i = 0; i <= tempNum2.length() - tempNum.length(); i++) {// 计算 要更改的数据长度tempNum += "0";}System.err.println("16进制当前金币:" + tempNum);System.err.println("16进制更改的金币:" + tempNum2);tempNum2 = reverseHex(tempNum2);System.err.println("16进制低字节转换:" + tempNum2);}pattern = new String[][] {{tempNum, tempNum2}};}// 修改关卡public void update() {Scanner input = new Scanner(System.in);System.out.println("格式 1-1 输入1 2-3 输入13 5-2 输入 42");System.out.println("请输入您现在的关卡");tempNum = decToHexStringDiv(Integer.parseInt(input.next()));System.out.println("您想更改的关卡:");tempNum2 = decToHexStringDiv(Integer.parseInt(input.next()));System.err.println("16进制当前关卡:" + tempNum);System.err.println("16进制更改的关卡:" + tempNum2);if (Integer.parseInt(tempNum2, 16) > 50) {System.out.println("对不起您输入的关数不存在");System.exit(0);}pattern = new String[][] {{tempNum, tempNum2}};}
}

参考:https://blog.csdn.net/weixin_42326020/article/details/114111040
参考:https://blog.csdn.net/liubing8609/article/details/82695059

植物大战僵尸 进制转换 使用JAVA 更改关卡 和金钱相关推荐

  1. java函数实现进制转换与java实现八进制到十进制的转换(百练OJ:2735:八进制到十进制)

    java进制转换函数介绍:Java二进制.八进制.十进制.十六进制相互转换: 二进制转十进制 Integer.parseInt("0110", 2); 八进制转十进制 Intege ...

  2. 最清晰的进制转换讲解 - java实现

    子曰:知之为不知,不知为不知,太菜也! 进制转换在平时的算法练习题或者项目中都会遇到,下面我们来看一下使用java如何进行进制互相转换. 文章目录 1. 使用内置函数进行进制转换 十进制转换其他进制 ...

  3. 进制转换和java基础语法

    标题1.软件版本升级的原因 软件版本升级的原因:1.功能增加 2.功能优化 3.修改bug 注意:一般情况,新的版本的软件中的体积会越来越大,新的方法不会覆盖原来的方法 标题2.进制转换 2.1什么是 ...

  4. java 进制转换工具_进制转换工具(JAVA)

    /**进制转换工具,支持2-36任意进制间相互转换 */ public class HexConverter { public static String Quotient = null; publi ...

  5. java进制转换界面,java进制转换器 图形用户界面 十进制及其相反数诀别转化为二,四,八,十六进制...

    java进制转换器 图形用户界面 十进制及其相反数分别转化为二,四,八,十六进制 package com.rgy.Test; import java.awt.Color; import java.aw ...

  6. java:数据结构(二)栈的应用(进制转换全收集)

    说到进制转换,java已经封装了基本的方法,在竞赛中使用封装的方法自然能节省大量时间 另一位仁兄介绍的封装好的方法: https://blog.csdn.net/m0_37961948/article ...

  7. Java中四种进制转换

    进制介绍 举例: 不同进制转换运算 Java中二进制使用0b(0B)前缀来表示,不足一字节需高位补零,凑成8位. Java中八进制使用0前缀来表示. Java中十六进制使用0x(0X)来表示,a~e( ...

  8. JAVA之旅(一)——基本常识,JAVA概念,开发工具,关键字/标识符,变量/常量,进制/进制转换,运算符,三元运算

    JAVA之旅(一)--基本常识,JAVA概念,开发工具,关键字/标识符,变量/常量,进制/进制转换,运算符,三元运算 Android老鸟重新学一遍JAVA是什么感觉?枯燥啊,乏味啊,而且归纳写博客,都 ...

  9. JAVA之旅(一)——基本常识,JAVA概念,开发工具,关键字/标识符,变量/常量,进制/进制转换,运算符,三元运算...

    JAVA之旅(一)--基本常识,JAVA概念,开发工具,关键字/标识符,变量/常量,进制/进制转换,运算符,三元运算 Android老鸟重新学一遍JAVA是什么感觉?枯燥啊,乏味啊,而且归纳写博客,都 ...

最新文章

  1. SetTimer的使用问题
  2. 省带宽、耗电小,腾讯游戏学院专家解析手游渲染架构
  3. unity创建和销毁对象_如何创建和销毁对象
  4. android fileinputstream 获取file,Android FileInputStream没有读取文件
  5. 最小延迟调度问题——贪心算法(C++实现)
  6. linux关闭计算机的命令是,关闭Linux计算机的正确命令和方法
  7. 知识点 - DataList中CheckBox实现单选
  8. 声音加速_听,这是加速的声音
  9. 数据清洗挑战Day1 | 手把手教你处理数据集中的缺失值
  10. 占位智能家居市场,施耐德电气仅靠一个Wiser系统?
  11. 树莓派 USB摄像头 实现网络监控
  12. css html5布局方式_创建新HTML5&CSS3单页布局– Rock门户
  13. 电脑便签损坏怎么修复
  14. python模拟app发送请求失败_如何使用Tensorflow解决Python Chatbot App的运行会话失败问题...
  15. 字母异位词分组-LeetCode49
  16. 运放指标-压摆率SR
  17. QT 无边窗口圆角实现
  18. java设计奥运图标_15组优秀的奥运会、体育运动类图标系列网页素材
  19. 《文明之光 第二册》一一 10.4 罗卡尔角的夕阳—— 葡、西的殖民时代(4)
  20. 西工大机考《大学英语2》大作业网考

热门文章

  1. 智和网管工业交换机网管方案
  2. matlab多项式计算(求根、由根创建、导数、积分)
  3. 22-2-21 ETH币价今日价格分析
  4. Linux各目录及每个目录的详细介绍
  5. 为什么前端UI设计师给750PX的2倍设计稿?
  6. 一文归纳Ai调参炼丹之法
  7. 随机Tiled Map的生成
  8. Xmanager 5 (Windows 远程Centos工具 )
  9. 【V-REP自学笔记(八)】控制youBot抓取和移动物体
  10. 携程再爆大数据杀熟,携程致歉信:程序 bug 已紧急修复,将赔偿用户