用python画lgx的图_[难度4]MOD教程之Python语言在Module System里的简单应用

好,从这里开始就假设你已经看过了简明 Python 教程的1~9章了。

看完以后,你再回过头看看Module System里各py文件的文件结构,就看那些以module_开头的,因为以header_开头的和以process_的基本都加了警告语:不要对这个文件进行编辑。事实上最好也不要去编辑这些文件,不然出了问题,麻烦就大了。

比如就看module_items.py吧,注意了,是看结构,不是看内容。看到什么了?一个大大的list(列表),这个list就是items。不论module_items.py里定义了多少item,不论有多少行,module_items.py文件里的内容不过就是一个list而已,另外就是头部的一些简单定义的语句,再往上就是“包含语句”,就是fromxxximport*语句了。Module System里那些以module_开头的文件的文件结构都与module_items.py里的类似,大同小异。主体部分都是一个大大的列表,然后这个list(列表)里面就是tuple(元组)或者是内层的list,文件结构非常之简单,没有什么神秘可言。了解到了这一点,你可能会感到很沮丧,原来所谓用Module System来制作MOD其实就是填一下或者修改一下各个list(列表)和tuple(元组)罢了,用的仅仅是Python语言里的冰山一角。做的事情就类似于把各种液体分装到不同的容器里,如此而已,而对容器里液体的使用完全轮不到你了,而且分装的规则也轮不到你来定。也就是说,MOD制作者做的不过是通过往这些list和tuple里填数据来向游戏引擎提供数据罢了,而对于这些数据的操作完全是由游戏引擎来完成的。MOD制作者的定位就是数据提供者,而不是数据使用者。而且游戏引擎对list和tuple里数据的使用方法是由游戏引擎决定的,什么样的使用方法就决定了这些list和tuple里的数据结构必须是什么样的,叫你填字符串的地方,你就不能填数值,叫你填数值的地方,你就不能填字符串,规定了list或者tuple的长度是多长,你少填多填都是错,MOD制作者是相当被动的。不过你至少能明白一点,使用Module System就是使用Python语言里的list和tuple,这就是为什么我说“你接触了Module System多长时间,你就接触了Python语言多长时间。”

说是接触了Python语言,但这个接触太浅了,浅到如果你不看简明 Python 教程里数据结构这一章,你基本就无法发现用Module System就是在用Python语言,而可能会认为Module System是M&B提供的一种特殊语言。

说实话,这篇教程的产生起源于一个事件。当时我要做的一个东西:负重影响技能。关键的地方在于header_operations.py里是没有获得物品重量的函数,当然我马上就想到slot,可以为物品定义一个新slot:slot_item_weight,通过

(item_set_slot,,,),

这个函数就可以一一往各个物品的这个slot里写入物品的重量了,然后

(item_get_slot,,,),

就可以取出相应slot里的值了,也就是取得物品的重量了。思路是对的,但新的问题来了,物品有400多种,一一这样往相应的slot里写入数值的话,就要写400多行

(item_set_slot,,,),

这样的代码。而且是完全手动一个一个地填写item_id和物品重量,稍不留神,就会填错。而且因为是手动,所以要仔细核查module_items.py里各物品的重量和这些item_set_slot函数中写入slot的值是否是一致的。

还是列举一部分这样写的代码吧。

(item_set_slot,"itm_no_item", slot_item_weight,150),

(item_set_slot,"itm_horse_meat", slot_item_weight,4000),

(item_set_slot,"itm_practice_sword", slot_item_weight,150),

(item_set_slot,"itm_heavy_practice_sword", slot_item_weight,625),

(item_set_slot,"itm_practice_axe", slot_item_weight,200),

(item_set_slot,"itm_arena_axe", slot_item_weight,150),

(item_set_slot,"itm_arena_sword", slot_item_weight,150),

………………

我曾经就这样写了400多行,并且一行一行地检查重量是否填错了,花了大约一下午的时间。这个方法又累又笨,还容易出错。

可以发现这里的重量都是扩大100倍的,原因很简单,这些地方只能填整数,不能填小数,另外重量1.25,又不可能就当成是1,那样几件装备累积下来的重量误差就太大了。所以定义的时候放大了100倍,使用的时候,把全部装备的重量一加,最后再除以100,得到的就是总负重的整数部分,这样就已经很精准了。

如果仅仅是为了以这样的方式获取物品的重量,可能我会认栽了,烦琐就烦琐了,反正以后不会再这样做了。而事实上,我想通过slot来获得东西远不止物品重量这么简单,我想获得的东西多着呢。自然每获得物品的一种属性都要这样写400多行代码,这种烦琐让人根本无法承受,迟早会让人精神崩溃的。而说什么Module System是最好的老师,其实也不完全对,对于上面这个问题,即使把Module System里各个文件翻来倒去地看上几百遍也没有用,你根本无法从中找到任何对这个问题有帮助的代码,一行都没有。所以我就去看了简明 Python 教程,看了也只是了解一些概念,依然解决不了问题。

还是先放出我最终写就的代码吧。defget_hrd_weight(y):

a = (y >> ibf_weight_bits) & ibf_armor_maskreturnint(25* a)defset_item_weight():

item_weight = []fori_iteminxrange(len(items)):

item_weight.append((item_set_slot, i_item, slot_item_weight, get_hrd_weight(items[i_item][6])))returnitem_weight[:]

("init_item_weight", set_item_weight()),

(call_script,"script_init_item_weight"),

这样使用:

前2个python函数放在module_scripts.py的头部,scripts这个大列表的上面,不要放到scripts这个列表里面了。

第3个是一条script,和添加其他script的方法一样。看起来和其他的script是不是差别很大。但不要惊讶,后面会说明的。

第4个就是call_script语句,就放在第一个script,也就是game_start里就行了,游戏开始时初始化一次物品的重量就可以了。

最后,slot_item_weight需要在module_constants.py里定义一下。

注:为什么get_hrd_weight里是int(25* a),是25而不是100。很简单,因为这里的a已经乘以过4,再乘25就是100倍了。

就是这4个部分,很简短吧。但你根本就无法想像这些代码从无到有的过程是多么地艰难,完全没有可以仿照的代码,也没有任何人可以教我。我走的弯路也多到你无法想像,这段代码至少重写了上百次,也测试了上百次,当然也失败了上百次。

看看我曾经写过的较“成功”的代码吧。defget_hrd_weight(y):

a = (y >> ibf_weight_bits) & ibf_armor_maskreturnint(25* a)

("get_total_equipment_weight",

[

(store_script_param_1,":troop_no"),

(assign,":total_weight",0),

(try_for_range,":cur_slot",0,8),#equipment slots(troop_get_inventory_slot,":cur_item",":troop_no",":cur_slot"),

(ge,":cur_item",0),

(assign,":cur_item_weight", get_hrd_weight(items[":cur_item"][6])),

(val_add,":total_weight",":cur_item_weight"),

(try_end),

(val_div,":total_weight",100),

(assign, reg0,":total_weight"),

]),

关键是看这一行:

(assign,":cur_item_weight", get_hrd_weight(items[":cur_item"][6])),

这个脚本就是获得某一个人身上8件装备的总重量,也就是负重。看起来一切都没有问题,上面这关键的一行似乎也完全合乎逻辑。但是这样做就把事情想简单了,一编译就报告类型错误,list里的元素必须为int类型。确实,这里":cur_item"是字符串类型,不是整型(int类型)。但是Module System里的局部变量或者全局变量肯定是带" "号的,脱去" "号就是错的,不脱就是字符串类型。

后来想到寄存器了,于是又改写了一下关键代码:

(assign, reg0,":cur_item"),

(assign,":cur_item_weight", get_hrd_weight(items[reg0][6])),

这样中转一下,依然没有用。

直接用函数的返回值理论上可行,但是[]里的必须是整型,字符串不行。reg0本质是整型,但是用在python函数里,它就不是寄存器了,而是一个常量。

看看header_common.py里的。

op_num_value_bits =24+32tag_register =1opmask_register = tag_register << op_num_value_bits

reg0 = opmask_register|0根据这4行就可以算出reg0在python函数里的值,是2^56,这显然并不代表任何物品的id。

这里其实是我误解了寄存器。这属于地址和地址里的值的问题,reg0通过计算得出的2^56,其实是这个寄存器的地址值,而不是地址里的值。header_operations.py里的assign函数并不是简单的赋值语句。

比如下面的

(assign, reg0,1),

并不是把1赋值给reg0,而是赋给reg0这个地址里面储存的值,而reg0的值始终都是2^56。

而get_hrd_weight(items[reg0][6])里,reg0代表的就是地址,也就是2^56,所以就行不通。

最终我还是回到了使用slot的方法上面来了。又是反复尝试,并反复读简明 Python 教程的数据结构这一章。终于获得了灵感,下面3个东西就是我代码的关键:1.append方法2.索引操作符3.切片操作符

再回头看一下我写的那个python函数吧。defset_item_weight():

item_weight = []fori_iteminxrange(len(items)):

item_weight.append((item_set_slot, i_item, slot_item_weight, get_hrd_weight(items[i_item][6])))returnitem_weight[:]

第1行就是定义函数名字和参数,函数名就是set_item_weight,参数无。

第2行就是新建一个空list - item_weight,通过把空list赋值给item_weight来完成的。

第3行就是循环语句,指定循环变量i_item的范围,len(items)就是取得items这个列表的长度,所以循环范围就是0到这个列表长度,这样所有的物品都包括进去了。

第4行就是循环体了,也是最关键的一行了。list的append方法不停地把(item_set_slot, i_item, slot_item_weight, get_hrd_weight(items[i_item][6]))这样的语句添加到item_weight这个list里。这样item_weight这个list里就包含了所有对item进行set_slot操作的400多行语句。

第5行就是设定返回值,这里用了切片操作符,item_weight[:]返回的就是整个list的拷贝,也就是400多行set_slot操作的语句,这同时也是这个函数的返回值。

然后就是下面的script了,它调用set_item_weight()函数,获得了这个函数的返回值,就是400多行set_slot操作的语句。

("init_item_weight", set_item_weight()),

最后在第一个script,也就是game_start里调用一下init_item_weight这个script,这就全部完成了。

(call_script,"script_init_item_weight"),

说一些上面那个循环过程吧。

当i_item为0的时候:

(item_set_slot, i_item, slot_item_weight, get_hrd_weight(items[i_item][6]))

就是

(item_set_slot,0, slot_item_weight, get_hrd_weight(items[0][6]))

这里的items[0][6]用的就是索引操作符,items[0][6]取出的就是items这个列表里第1个元素里的第7个元素。

注:这些id都是从0开始的。

相关代码,这就是items这个列表里的第1个元素:

["no_item","INVALID ITEM", [("practice_sword",0)], itp_type_one_handed_wpn|itp_primary|itp_secondary, itc_longsword,3,weight(1.5)|spd_rtng(103)|weapon_length(90)|swing_damage(16,blunt)|thrust_damage(10,blunt),imodbits_none],

items[0][6]就是weight(1.5)|spd_rtng(103)|weapon_length(90)|swing_damage(16,blunt)|thrust_damage(10,blunt)

再通过get_hrd_weight函数就可以从中把重量提取出来。提取出的是100倍的重量,就是150。

从而get_hrd_weight(items[0][6])返回的就是第0号(也就是第1个)物品的重量。

然后循环继续让下进行,append方法也不停地把这些set_slot语句添加到item_weight这个list里。并且添加的时候,每句,其实是每个tuple(元组)都会用逗号相互隔开。这和Module System语句的写法是一致。

item_weight[:]返回的是整个list的拷贝,也包括外面的[]号。

所以

("init_item_weight", set_item_weight()),

也就是

("init_item_weight", item_weight[:]),

所以这里的set_item_weight()就已经是有[]号的了,所以不需要,也不能再加[]号了。

这就是为什么

("init_item_weight", set_item_weight()),

这个script看起来那么奇怪,不像其他的script有[]号。

这也是为什么不能直接把set_item_weight()函数嵌入到game_start这个script里,因为script里的语句必须都是tuple,也就是()括起来的语句,不能是list。所以要通过init_item_weight这个script来中转一下。而每个script的第1个部分是script的名字,刚好第2个部分就是一个list。call_script的作用其实是从script的list里取出里面所有tuple,也就变相脱去script的list外面的[]号。脱list外面的[]号才是终极目的,如果能直接脱[]号,就直接用到game_start这个script里了,而不用这样麻烦了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值