自己写一个 LifeLine


发布于

|

分类

,

LifeLine》这个文字冒险游戏确实挺新颖的,当然,是对我们这一群习惯了GUI和CG的人来说。以前也有过类似形式的游戏,比如,逗B编程小王子就说,他们公司以前写过一个“短信游戏”,你发短信,告诉主人公下一步该怎么走怎么走……一条短信一块钱。

一块钱买了这个游戏,三天通关。在玩通关了之后,心血来潮:这东西的原理一定很简单!游戏肯定是以脚本方式工作的!脚本肯定是存在一个TXT里面或者数据库中!为啥?直觉。要相信一个程序猿的直觉。

那好,自己写一个出来玩玩。

拿到游戏脚本

由于下载不到ipa文件(iOS专用),只好转战Android平台,下载了apk。解压缩,在assets文件夹中找到了StoryData_en.txt等文件,打开看了以下,恩,确实是我们需要的游戏脚本。(点击查看 非官方中文版游戏脚本

iOS版有官方简体中文版,Android只有德文、英文、法语、日语、俄语,并没有中文。当然,国内汉化组很多……比对了国内汉化组和官方中文版,惊叹有些时候山寨的水平竟然能高于原著。

分析游戏脚本

脚本拿到了,就可以开启“上帝模式”了。本来想着是“只需要跳转跳转再跳转”就好了,应该很简单吧?结果发现,并没有第一想法那样简单。脚本里面使用了很多自定义语法,可能还得自己写一个脚本解释器才能让它工作。(后来才知道,这东西叫做“模板引擎”,和各种框架的V层异曲同工)

大致看了一下游戏脚本,里面的语法不太难,标签元素不太多,大致分为以下几种:

  1. 游戏区块定节类::: BlockName,其中BlockName是区块的名字。整个游戏是一个区块一个区块进行的,区块之间有跳转,每个区块有自己的名字。区块没有结束标志。

  2. 跳转类:[[BlockName]],其中BlockName是需要跳转到的区块的名字。

  3. 延时跳转类:[[delay XXX|BlockName]],其中XXX是需要延时的时间,单位为h(小时)、m(分钟)、s(秒)。比如,在出现“我们4小时后见”,后面会有一句[[delay 4h|SomeBlock]]。使用延时跳转后,会先出现一句“泰勒繁忙”,然后开始延时,游戏暂停,延时后再进行跳转,以正常速度进行游戏。

  4. 交互选择类:<<choice [[foo_1|bar_1]]>> | <<choice [[foo_2|bar_2]]>>。其中foo是提示语句,bar是跳转到的区块的名称。由于游戏中都是“二选一”,所以都按照二选一来处理就好了~记得中间有一个|哦~

  5. 屏幕显示类:[SomeSentence],其中SomeSentence都是英文,例如显示“泰勒繁忙”之类的提示语。这些提示语做了国际化处理,对应的翻译在另外的xml文件中。

  6. 变量赋值类:<<set $foo = bar>>,其中foo是变量,变量前要加上$bar是值。如果值为string,外面需要加上引号。这点和大多数编程语言都一样。

  7. 变量输出类:<<$foo>>,其中foo是变量。

  8. 判断类:<<if >><<elseif >><<else >><<endif >>,都能看懂吧?判断条都是$var op value的形式,例如$medicine gte 3(药品数量大于3片)。op我发现的有is(等于)、eq(等于)、gte(大于等于)。

  9. 静默类:<<silently>> <<endsilently>>,好象是为了防止输出用的吧?被这一队标签包裹的内容都不会产生输出。

  10. 注释:// Comment。都懂吧……正如游戏Script开头所说

    If you’re reading this, you’re in for spoilers but you probably don’t care! Thanks for playing, and have fun.

    你开心就好。

恩就这么多了。

实现

读脚本文件并进行标准化

大致思路如下:

  1. 去掉注释
  2. 去掉空行
  3. 去掉Choice__之间__的|
  4. 将每一个标签元素都自成一段

解析标签

由于每一个标签都是独立的一行,所以解析起来很简单了:大多数情况下只需要判断标签的前面半部分是什么就好了。

对于跳转什么的,可以拿Map,一下子就实现了,很好用,但是if、else、set这些东西呢?我的做法是将它们拿正则表达式或者纯的strReplace替换成Python的语法,然后使用eval(SomeStatement)获取结果。然后发现,if好像有嵌套……那就拿一个简单的栈来玩吧。

比如,处理if

def __do_if():
    # 全局变量表
    parameter = self.__parameter
    # 标签对我来说并没有用,扔掉
    script = script.replace('<<if', '')
    script = script.replace('<<elseif', '')
    script = script.replace('>>', '')
    # 将is、eq、gte等东西替换成Python的
    script = script.replace(' is ', ' == ')
    script = script.replace(' eq ', ' == ')
    script = script.replace(' gte ', ' >= ')
    # 用正则表达式,把模板里面带 $ 的变量替换为全局变量表里面的变量
    script = re.sub(r'\$(\S+)', r'parameter["\1"]', script)
    # 为了防止缩进,做一下Strip
    script = script.strip()
    # eval一下,拿到判断结果
    judgeResult = eval(script) 

    return judgeResult

延时跳转和本地化我没有做……一是当时没有发现(以为xml文件没有用,直接删掉了),二是,我懒……

但是为了获得一些类似的体验,我设置了几个“延时时间”,最起码能让哪些语句“一条一条地出现”而不是“哗啦一下子全部出现”。延时怎么做?time.sleep(SomeSeconds),注意单位是Seconds。

半成品

半成品我已经放到Github上好久了,请打开任意门~。

运行python3 main.py即可。一定是Python3啊~

啥?GUI?要什么自行车啊!

总结

模板引擎,简单实现嘛,又不用考虑效率神马的。

好像 3 Minute Games 并没有说不允许对它的游戏做逆向工程啊~更何况是“仅作为学习和研究使用”。

实现这个东西大概花了我两个下午和三个晚上。不过呢,人家可是公司啊!美工、剧本……还要求不能出BUG……点个暂顺手物质上支持一下也是应该的。

参考资料

  1. Python基础教程. [挪]Magnus Lie Hetland 著,司维 等译. 人民邮电出版社. 2014年6月

评论

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注