找回密码
 立即注册

QQ登录

只需一步,快速开始

查看: 890|回复: 0

[教程] 【功能组件】随机游戏核心

[复制链接]
发表于 2026-4-24 23:45:08 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

×
本帖最后由 Maz马 于 2026-8-17 11:20 编辑

全随机游戏就是肉鸽Rogue了...

26/8/17 重构了类和变量的命名风格,配置方式,使用方式。
让内部黑箱工厂化,不再用调用式配置。
支持节点、单元检索生成。


组件可以对接的功能
1随机取名,随机属性的角色
2随机取名,随机属性的道具
3随机地点,发生随机事件
4随机房间,放置随机家具
...
组件本身是定义了随机组装的规则而不是生产具体的对象
所以这是一个核心

                               
登录/注册后可看大图


在使用上需要自行设计接收者,可以是类,也可以是函数
这里主要还是提供一个“心脏”提供一个思路

如果只想使用,完全不关心内部
你只要知道,在配置好树图后,系统提交给你的是
一枚 随机种子

种子是一个RL_Seed对象,内部属性是:
units 单元列表
nodes 节点列表

votes 投票字典
以及name,rare等对以上三个属性封装翻译处理后转发的getattr方法

系统不知道你的节点内容,单元内容,给哪个tag投票
只根据你设计的路径图进行收集组装。

过去的思路是写表,在结构简单时还行
随机创建角色
但一旦可能性变多,分支复用,写表穷举就会十分臃肿
然后就演变成以下

第一部分:RL系统(直接复制为一个文件)
[RenPy] 纯文本查看 复制代码
# ---------- 节点随机核心 ----------
init python:
    """
    Roguelike Core(Ren'py 8.3.6)
    Ver 1.0
    Author: Maz
    
    核心设计
    有向无环图、双树嵌套、投票字典
    RL_Node ➡ RL_Node     RL_Node ➡ RL_Unit
            |                      |
            ➡ RL_Node             ➡ RL_Unit
            |                      |
            ➡ RL_Node             ➡ RL_Unit
    从节点树随机选择路径到达叶子节点结束 ,从每个节点的局部单元树随机获取数据,用词条投票的模式整合成 随机种子字典
    """
# 随机种子注册表单
default _RL_SYS = {}
init python:
    import random
    class RL_Unit:
        def __init__(self,name,roll=None,node=None,vote=None):
            self.name = name               # 文本
            self.roll = roll               # 权重 数值越大概率越大,正数非0
            self.node = node               # 所属节点(NODE对象)
            
            self.vote = vote               # 投票字典/词缀系统 {tags:value}
    class RL_Node:
        def __init__(self,name,roll=None,units=None,parents=None,childs=None):
            self.name = name               # 文本
            self.roll = roll               # 权重 数值越大概率越大,正数非0
            self.units = units or []       # 关联单元(UNIT对象池)
                                                          
            self.parents = parents or []   # 父级节点(NODE对象池)
            self.childs = childs or []     # 子级节点(NODE对象池)
    class RL_Seed:
        def __init__(self,units,nodes,votes):
            self.units = units             # 单元列表
            self.nodes = nodes             # 节点列表
            self.votes = votes             # 投票字典
        
        # 转发给外部的方法,可在这一层同一封装特殊处理,比如先翻译再组合字段
        def __getattr__(self, attr):
            if attr == "name":
                return "".join(renpy.translate_string(u) for u in self.units)
            if attr == "type":
                return renpy.translate_string(self.nodes[0])
            if attr == "race":
                return renpy.translate_string(self.nodes[1])
            if attr == "rare":
                return "".join(renpy.translate_string(u) for u in self.nodes[2:])
            raise AttributeError(attr)
    class RL_Tree:
        def __init__(self,tree,registry):
            self.tree = tree               # 树图名称
            self.registry = registry       # 注册表单 {树名:{名称:种子字典}}
            
            self.nodepool = {}             # 节点表单 {node_name:node obj}
            self.unitpool = {}             # 单元表单 {node_name:unit obj}

            self.cache_roots = []          # 所有根   [n,n...]
            self.cache_paths = []          # 所有路径 [(n,n),(n,n)...]
            self.cache_units = {}          # 单元索引 {u:[n...]}
            self.cache_nodes = {}          # 节点索引 {n:[[n...],[n...]...]}
        
        # 节点 创建/关联(_roll 必填,配置表已保证合法)
        def _set_node(self,node_name,roll):
            if node_name not in self.nodepool:
                self.nodepool[node_name] = RL_Node(node_name,roll=roll)
        def _link_node(self,node_name,node_names):
            for k in node_names:
                if k not in self.nodepool:
                    continue
                if self.nodepool[node_name] not in self.nodepool[k].parents:
                    self.nodepool[k].parents.append(self.nodepool[node_name])
                if self.nodepool[k] not in self.nodepool[node_name].childs:
                    self.nodepool[node_name].childs.append(self.nodepool[k])
        # 单元 创建/关联(_unit_names 标准化:[名字,权重,投票] 三元素,不允许多种格式)
        def _set_unit(self,node_name,unit_names):
            if node_name not in self.nodepool:
                print(f"!!!节点'{node_name}'不存在,请先创建节点")
                return
            if node_name not in self.unitpool:
                self.unitpool[node_name] = []
            exist = {unit.name for unit in self.unitpool[node_name]}
            for i in unit_names:
                _name = i[0]
                _roll = i[1]
                _vote = i[2]
                if _name not in exist:
                    self.unitpool[node_name].append(RL_Unit(_name,roll=_roll,vote=_vote))
        def _link_unit(self):
            # 关联所有节点和节点下所有单元
            for k in self.nodepool:
                if k not in self.unitpool:
                    print(f"??? 节点'{k}'下没有任何关联单元")
            for k in self.unitpool:
                self.nodepool[k].units = self.unitpool[k]
                for unit in self.unitpool[k]:
                    unit.node = self.nodepool[k]
        
        # 遍历树图 深度优先搜索
        def _path_update(self):
            # 清空缓存
            self.cache_roots = []
            self.cache_paths = []
            self.cache_units = {}
            self.cache_nodes = {}
            # 锁定根,缓存根,DFS
            for node in self.nodepool.values():
                if not node.parents:
                    self.cache_roots.append(node.name)
                    self._path_dfs(node,[],self.cache_paths,{})
            # 缓存索引 
            for path in self.cache_paths:
                for k in path:
                    if k not in self.cache_nodes:
                        self.cache_nodes[k] = []
                    self.cache_nodes[k].append(path)
            # 单元索引:单元名 → 含它的节点列表(跨节点重名累积)
            for node,units in self.unitpool.items():
                for unit in units:
                    if unit.name not in self.cache_units:
                        self.cache_units[unit.name] = []
                    self.cache_units[unit.name].append(node)
        # DFS 从指定节点出发 检测环 并 收集路径
        def _path_dfs(self,node,path,paths,visit):
            if node.name in visit:
                loop = path[visit[node.name]:] + [node.name]
                print(f"!!!检测到环: {' → '.join(loop)}")
                return
            visit[node.name] = len(path)
            # 每次加入节点
            path.append(node.name)
            # 开始递归
            # path [1]➡[1,2]➡[1,2,3]
            # paths []
            if node.childs:
                for child in node.childs:
                    self._path_dfs(child,path,paths,visit.copy())
            # 递归到最深处后把路径加入路径列表
            # path [1,2,3]
            # paths [(1,2,3)]
            else:
                paths.append(tuple(path))
            # 弹出最后一个节点,检查2除了3能不能继续,逐级回退检查
            # paths [(1,2,3)]
            path.pop()
        # 打印缓存
        def _path_check(self):
            print(f"@ {self.tree}")
            print("-" * 60)
            if not self.cache_roots:
                print("!!! 未找到根节点  !!!")
            else:
                for i in self.cache_roots:
                    print(f"根节点: {i}")
                print(f"共 {len(self.nodepool)} 个节点,{len(self.cache_roots)} 个根节点")
            if not self.cache_paths:
                print("!!! 未找到路径  !!!")
                print("请先刷新树图缓存")
                print("如果已经缓存,则节点未进行连接")
            else:
                print(f"共 {len(self.cache_paths)} 条路径")
                print("")
                for i,p in enumerate(self.cache_paths,1):
                    print(f"{i:3}. {' → '.join(p)}")

        # 节点筛选路径
        def _random_paths(self,must_nodes,skip_nodes):
            paths = set(self.cache_paths)
            # 候选 路径交集
            if must_nodes:
                path_sets = [set(self.cache_nodes.get(node, [])) for node in must_nodes]
                paths = set.intersection(*path_sets)
            # 排除 路径并集
            if skip_nodes:
                skip_sets = [set(self.cache_nodes.get(node, [])) for node in skip_nodes]
                paths = paths - set.union(*skip_sets)
            return paths
        # 单元筛选路径 返回可用路径
        def _random_units(self,must_units,skip_units):
            paths = set(self.cache_paths)
            musts = {} # 必须单元节点表 {单元:[落点...]}
            skips = {} # 回避单元节点表 {节点:{单元...}}
            # 必须单元
            if must_units:
                # 读取预存的单元索引,单元名映射节点,映射表的设计是同一单元名:[节点名,节点名...]
                # 因为在设计上是字段检索,因此单元唯一,但不同节点下的不同单元的单元名的映射并不唯一
                for _u in must_units:
                    musts[_u] = self.cache_units.get(_u, [])
                
                # 节点可能持有同一单元名,把所有节点组合穷举,找出能同时锁定单元的路径
                unit_paths = set()
                must_units = {u:set() for u in musts}
                # 穷举节点组合
                combos = [[]]
                for unit in musts:
                    new_combos = []
                    for combo in combos:
                        for node in musts[unit]:
                            new_combos.append(combo + [node])
                    combos = new_combos
                # 筛选可用组合
                for combo in combos:
                    # 同一个节点被两个单元锁定,抛弃
                    if len(set(combo)) != len(combo):      
                        continue
                    # 无路径同时锁定全部的单元,抛弃
                    combo_paths = set(self.cache_nodes.get(combo[0], []))
                    for node in combo[1:]:
                        combo_paths &= set(self.cache_nodes.get(node, []))
                    if not combo_paths:
                        continue
                    # 累加组装 paths,musts
                    unit_paths |= combo_paths 
                    for unit,node in zip(musts,combo):
                        must_units[unit].add(node)
                # 整理最终计算值
                paths = unit_paths
                musts = {u:sorted(nodes) for u,nodes in must_units.items() if nodes}

            # 回避单元
            if skip_units:
                # 因为逻辑上需要节点反查单元,所以直接反向建表
                for _u in skip_units:
                    for node in self.cache_units.get(_u, []):
                        skips.setdefault(node,set()).add(_u)
                # 筛选可用组合
                for node,names in list(skips.items()):
                    # 路径存在全为回避单元的节点,抛弃
                    if all(k.name in names for k in self.nodepool[node].units):
                        paths -= set(self.cache_nodes.get(node,[]))
                # 无需整理skips,因为是查找制,避免多余遍历

            return paths,musts,skips
        # 加权游走:先按节点权重随机选路径,再为必须单元分配落点,最后按绑定回填单元
        def _random_walk(self,paths,must_units,skip_units):
            if not paths:
                return [],[]
            # 按节点权重筛选路径
            idx = 0
            select_nodes = []
            while paths:
                # 获取当前索引节点
                current_names = list(set([path[idx] for path in paths]))
                weight = [self.nodepool[name].roll for name in current_names]
                select = random.choices(current_names,weights=weight,k=1)[0]
                # 从节点池放进结果集
                select_nodes.append(self.nodepool[select])
                # 排除不匹配的路径,并前进一步
                paths = [path for path in paths if path[idx] == select]
                idx += 1
                # 如果只剩一条路径,直接走完剩余部分,如果路径恰好结束,那么直接结束
                # 不存在索引越位,因为节点不可能既为路径,又为末端
                if len(paths) == 1:
                    path = paths[0]
                    for i in range(idx,len(path)):
                        select_nodes.append(self.nodepool[path[i]])
                    break
            # 为必须单元分配落点
            path_names = [n.name for n in select_nodes]
            assign = {}
            if must_units:
                occupied = set()
                # 可用落点少的先分(唯一落点优先,防止同节点挤占)
                order = []
                for unit in must_units:
                    count = 0
                    for n in must_units[unit]:
                        if n in path_names:
                            count += 1
                    order.append((count, unit))
                order.sort()
                for _, unit in order:
                    avail = [n for n in must_units[unit] if n in path_names and n not in occupied]
                    if not avail:
                        continue
                    pick = random.choice(avail)
                    assign[pick] = unit
                    occupied.add(pick)
            # 去除回避单元,按分配回填单元,其余按权重随机
            select_units = []
            for node_obj in select_nodes:
                if not node_obj.units:
                    continue
                cand = [u for u in node_obj.units if u.name not in skip_units.get(node_obj.name,())]
                if node_obj.name in assign:
                    unit = [u for u in cand if u.name == assign[node_obj.name]]
                    if unit:
                        select_units.append(unit[0])
                        continue
                weight_units = [u.roll for u in cand]
                select_units.append(random.choices(cand,weights=weight_units,k=1)[0])
            return select_units,select_nodes
        # 由路径集生成种子
        def _random_seed(self,paths,must_units,skip_units):
            _units,_nodes = self._random_walk(paths,must_units,skip_units)
            votes = {}
            for unit in _units:
                if not unit.vote:
                    continue
                for k,v in unit.vote.items():
                    votes[k] = votes.get(k,0) + v
            # 组装种子实例
            return RL_Seed([unit.name for unit in _units],[node.name for node in _nodes],votes)
              
        # 对外接口
        def obj_random(self,id,must_nodes=None,skip_nodes=None,must_units=None,skip_units=None):
            if must_nodes and not all(node_name in self.nodepool for node_name in must_nodes):
                print(f"???存在未注册节点,随机游走返回空种子")
            if must_units and not all(u in self.cache_units for u in must_units):
                print(f"???存在未注册单元,随机游走返回空种子")
            # 无约束随机根节点生成
            if not must_nodes and not must_units:
                must_nodes = [random.choice(self.cache_roots)]
            # 单元域:可用路径 + 强选单元 + 回避单元(收在 _random_units 内)
            paths, musts, skips = self._random_units(must_units, skip_units)
            # 节点域与单元域路径取交集
            final_paths = self._random_paths(must_nodes, skip_nodes) & paths
            if not final_paths:
                print("!!!约束无法满足,无可用路径")
                return
            # 存储 随机种子 到 随机种子注册表单
            self.registry[self.tree][id] = self._random_seed(final_paths,musts,skips)
            return self.registry[self.tree].get(id)
        def obj_get(self,id):
            # 已存在直接返回,未创建则随机生成一个
            if id not in self.registry[self.tree]:
                return self.obj_random(id)
            return self.registry[self.tree].get(id)
        def obj_del(self,id):
            if id not in self.registry[self.tree]:
                print("!!!未记录的id,删除无效")
                return
            return self.registry[self.tree].pop(id)
        def obj_list(self):
            seeds = list(self.registry[self.tree].keys())
            print(f"树图 '{self.tree}' 中共有 {len(seeds)} 个随机种子: {seeds}")
            return seeds
        
    # 树图管理器
    class RL_Manager:
        def __init__(self):
            self.registry = store._RL_SYS.setdefault("_registry_",{}) # 注册表单 {tree_name:{_id:_seed}}(硬绑定全局注册表)
            self.treeform = store._RL_SYS.setdefault("_treepool_",{}) # 树图表单 {tree_name:tree obj}   (硬绑定全局注册表)
            
            self.tree_name_cache = None                           # 树图名缓存

        # 接入树图 间接操作注册表单
        def api_tree(self,tree):
            if tree not in self.registry:
                self.registry[tree] = {}
                self.treeform[tree] = RL_Tree(tree,self.registry)
                self.tree_name_cache = tree
            if tree != self.tree_name_cache:
                print(f"\n※ 已接入随机树: {tree}")
                self.tree_name_cache = tree
            return self.treeform[tree]
        
        # 校验刷新
        def api_update(self,tree=None,check=False):
            if tree:
                self.api_tree(tree)._path_update()
            else:
                for name in self.treeform:
                    self.api_tree(name)._path_update()
            if check:
                self.api_check(tree)
        # 打印报告
        def api_check(self,tree=None):
            # 打印标题
            print("\n\n\n")
            print("RL SYS")
            print("=" * 60)
            print("!!!    注意核对根节点    !!!")
            print("!!!根节点不正常则环位于根 !!!")
            print("")
            print("!!!     注意核对路径     !!!")
            print("!!!不检测*跳过节点*的逆向环!!!")
            print("")
            if tree:
                self.api_tree(tree)._path_check()
            else:
                for name in self.treeform:
                    self.api_tree(name)._path_check()
            print("=" * 60)
            print("\n\n\n")

        # 对外接口
        def obj_random(self,tree,id,must_nodes=None,skip_nodes=None,must_units=None,skip_units=None):
            return self.api_tree(tree).obj_random(id,must_nodes,skip_nodes,must_units,skip_units)
        def obj_get(self,tree,id):
            return self.api_tree(tree).obj_get(id)
        def obj_del(self,tree,id):
            return self.api_tree(tree).obj_del(id)
        def obj_list(self,tree=None):
            if tree:
                self.api_tree(tree).obj_list()
            else:
                for name in self.registry:
                    self.api_tree(name).obj_list()
            return 
        
    # 遍历配置表建树工厂
    def init_RLSYS():
        store.RL = RL_Manager()
        for cfg in RL_TREES:
            t = store.RL.api_tree(cfg["tree"])
            for name,roll in cfg["node"].items():
                t._set_node(name,roll=roll)
            for parent,childs in cfg["link"].items():
                t._link_node(parent,node_names=childs)
            for node,units in cfg["unit"].items():
                t._set_unit(node,units)
            t._link_unit()
        # 完成树图注册,检测结构健康,顺遍在控制台输出
        store.RL.api_update(check=True)
    # 在renpy完全启动后再初始化树图
    config.start_callbacks += [init_RLSYS]


第二部分 配置树图:

[RenPy] 纯文本查看 复制代码
# 树图配置表
#   tree  树名 内部标识
#   link  字典 {父节点:[子节点...]}
#   node  字典 {节点名:权重},权重数值越大概率越大,默认1(均等)
#   unit  字典 {节点:[[名字,权重,投票],...]},每个单元固定三元素
define RL_TREE_1 = {
    "tree": "中文姓名",
    "link":{
        _("角色"):[_("男"),_("女")],
        _("男"):[_("男前名")],_("男前名"):[_("男中名")],_("男中名"):[_("男后名")],
        _("女"):[_("女前名")],_("女前名"):[_("女中名")],_("女中名"):[_("女后名")],
    },
    "node":{
        _("角色"):1,_("男"):4,_("女"):7,
        _("男前名"):1,_("男中名"):1,_("男后名"):1,
        _("女前名"):1,_("女中名"):1,_("女后名"):1,
    },
    "unit":{
        _("男前名"):[
            [_("赵"),1,{}],[_("钱"),1,{}],[_("孙"),1,{}],[_("李"),1,{}],[_("周"),1,{}],[_("吴"),1,{}],[_("郑"),1,{}],[_("王"),1,{}],
            [_("冯"),1,{}],[_("陈"),1,{}],[_("蒋"),1,{}],[_("沈"),1,{}],[_("韩"),1,{}],[_("杨"),1,{}],[_("朱"),1,{}],[_("秦"),1,{}],
            [_("许"),1,{}],[_("何"),1,{}],[_("吕"),1,{}],[_("张"),1,{}],[_("孔"),1,{}],[_("曹"),1,{}],[_("魏"),1,{}],[_("陶"),1,{}],
            [_("姜"),1,{}],[_("韦"),1,{}],[_("马"),1,{}],[_("袁"),1,{}],[_("柳"),1,{}],[_("史"),1,{}],[_("姚"),1,{}],[_("汪"),1,{}],
            [_("朱"),1,{}],[_("董"),1,{}],[_("梁"),1,{}],[_("杜"),1,{}],[_("阮"),1,{}],[_("蓝"),1,{}],[_("贾"),1,{}],[_("童"),1,{}],
            [_("武"),1,{}],[_("司马"),1,{}],[_("上官"),1,{}],[_("欧阳"),1,{}],[_("夏侯"),1,{}],[_("诸葛"),1,{}],[_("东方"),1,{}],
            [_("公孙"),1,{}],[_("宇文"),1,{}],[_("长孙"),1,{}],[_("慕容"),1,{}],
        ],
        _("女前名"):[
            [_("赵"),1,{}],[_("钱"),1,{}],[_("孙"),1,{}],[_("李"),1,{}],[_("周"),1,{}],[_("吴"),1,{}],[_("郑"),1,{}],[_("王"),1,{}],
            [_("冯"),1,{}],[_("陈"),1,{}],[_("蒋"),1,{}],[_("沈"),1,{}],[_("韩"),1,{}],[_("杨"),1,{}],[_("朱"),1,{}],[_("秦"),1,{}],
            [_("许"),1,{}],[_("何"),1,{}],[_("吕"),1,{}],[_("张"),1,{}],[_("孔"),1,{}],[_("曹"),1,{}],[_("魏"),1,{}],[_("陶"),1,{}],
            [_("姜"),1,{}],[_("韦"),1,{}],[_("马"),1,{}],[_("袁"),1,{}],[_("柳"),1,{}],[_("史"),1,{}],[_("姚"),1,{}],[_("汪"),1,{}],
            [_("朱"),1,{}],[_("董"),1,{}],[_("梁"),1,{}],[_("杜"),1,{}],[_("阮"),1,{}],[_("蓝"),1,{}],[_("贾"),1,{}],[_("童"),1,{}],
            [_("武"),1,{}],[_("司马"),1,{}],[_("上官"),1,{}],[_("欧阳"),1,{}],[_("夏侯"),1,{}],[_("诸葛"),1,{}],[_("东方"),1,{}],
            [_("公孙"),1,{}],[_("宇文"),1,{}],[_("长孙"),1,{}],[_("慕容"),1,{}],
        ],
        _("男中名"):[
            [_("伯"),1,{}],[_("仲"),1,{}],[_("叔"),1,{}],[_("季"),1,{}],[_("子"),1,{}],[_("作"),1,{}],[_("文"),1,{}],[_("武"),1,{}],
            [_("元"),1,{}],[_("宇"),1,{}],[_("冠"),1,{}],[_("世"),1,{}],[_("震"),1,{}],[_("晓"),1,{}],[_("克"),1,{}],[_("轩"),1,{}],
            [_("昂"),1,{}],[_("光"),1,{}],[_("修"),1,{}],[_("柯"),1,{}],[_("云"),1,{}],[_(""),15,{}],
        ],
        _("女中名"):[
            [_("温"),1,{}],[_("婉"),1,{}],[_("绮"),1,{}],[_("诗"),1,{}],[_("润"),1,{}],[_("涵"),1,{}],[_("曼"),1,{}],[_("玉"),1,{}],
            [_("元"),1,{}],[_("语"),1,{}],[_("言"),1,{}],[_("怜"),1,{}],[_("惜"),1,{}],[_("清"),1,{}],[_("雨"),1,{}],[_("文"),1,{}],
            [_("汶"),1,{}],[_("嫣"),1,{}],[_("芷"),1,{}],[_("初"),1,{}],[_("乐"),1,{}],[_(""),15,{}],
        ],
        _("男后名"):[
            [_("杰"),1,{}],[_("和"),1,{}],[_("祖"),1,{}],[_("雄"),1,{}],[_("长"),1,{}],[_("德"),1,{}],[_("儒"),1,{}],
            [_("冲"),1,{}],[_("高"),1,{}],[_("龙"),1,{}],[_("炎"),1,{}],[_("霖"),1,{}],[_("彻"),1,{}],[_("南"),1,{}],
            [_("爽"),1,{}],[_("过"),1,{}],[_("谋"),1,{}],[_("晏"),1,{}],[_("天"),1,{}],[_("农"),1,{}],[_("坤"),1,{}],
        ],
        _("女后名"):[
            [_("柔"),1,{}],[_("珊"),1,{}],[_("怡"),1,{}],[_("容"),1,{}],[_("婷"),1,{}],[_("梦"),1,{}],[_("卿"),1,{}],[_("岚"),1,{}],
            [_("清"),1,{}],[_("琴"),1,{}],[_("瑶"),1,{}],[_("璇"),1,{}],[_("萱"),1,{}],[_("琪"),1,{}],[_("晴"),1,{}],[_("彤"),1,{}],
            [_("若"),1,{}],[_("凤"),1,{}],[_("稚"),1,{}],[_("乐"),1,{}],[_("然"),1,{}],
        ],
    },}
define RL_TREE_2 = {
    "tree": "修仙道具",
    "link":{
        _("道具"):[_("秘籍"),_("药品")],

        _("秘籍"):[_("天阶"),_("地阶"),_("玄阶"),_("黄阶")],
        _("天阶"):[_("极品"),_("上品"),_("中品"),_("下品")],_("地阶"):[_("极品"),_("上品"),_("中品"),_("下品")],
        _("玄阶"):[_("极品"),_("上品"),_("中品"),_("下品")],_("黄阶"):[_("极品"),_("上品"),_("中品"),_("下品")],
        _("极品"):[_("功法"),_("身法"),_("武技"),_("战技")],_("上品"):[_("功法"),_("身法"),_("武技"),_("战技")],
        _("中品"):[_("功法"),_("身法"),_("武技"),_("战技")],_("下品"):[_("功法"),_("身法"),_("武技"),_("战技")],

        _("药品"):[_("九品"),_("六品"),_("三品"),_("一品")],
        _("九品"):[_("丹药"),_("灵植")],_("六品"):[_("丹药"),_("灵植")],
        _("三品"):[_("丹药"),_("灵植")],_("一品"):[_("丹药"),_("灵植")],
    },
    "node":{
        _("道具"):1,
        _("秘籍"):1,
        _("天阶"):0.05,_("地阶"):0.15,_("玄阶"):0.30,_("黄阶"):0.50,
        _("极品"):0.10,_("上品"):0.20,_("中品"):0.30,_("下品"):0.40,
        _("功法"):1.0,_("身法"):1.0,_("武技"):1.0,_("战技"):1.0,

        _("药品"):1,
        _("九品"):0.35,_("六品"):0.30,_("三品"):0.20,_("一品"):0.15,
        _("丹药"):0.50,_("灵植"):0.50,
    },
    "unit":{
        _("天阶"):[[_("大天衍"),1,{}],[_("无极"),1,{}],[_("万象"),1,{}],[_("九转"),1,{}],[_("四象"),1,{}]],
        _("地阶"):[[_("阴阳"),1,{}],[_("梵天"),1,{}],[_("破军"),1,{}],[_("八荒"),1,{}],[_("天罡"),1,{}]],
        _("玄阶"):[[_("两仪"),1,{}],[_("星月"),1,{}],[_("七杀"),1,{}],[_("镇狱"),1,{}],[_("地煞"),1,{}]],
        _("黄阶"):[[_("孤峰"),1,{}],[_("崩山"),1,{}],[_("沧澜"),1,{}],[_("断水"),1,{}],[_("叠浪"),1,{}]],
        _("极品"):[[_("真龙"),1,{}],[_("白虎"),1,{}],[_("朱雀"),1,{}],[_("真武"),1,{}],[_("鲲鹏"),1,{}]],
        _("上品"):[[_("赤炎"),1,{}],[_("寒霜"),1,{}],[_("风雷"),1,{}],[_("穿云"),1,{}],[_("金刚"),1,{}]],
        _("中品"):[[_("游龙"),1,{}],[_("伏虎"),1,{}],[_("贪狼"),1,{}],[_("长春"),1,{}],[_("灵蛇"),1,{}]],
        _("下品"):[[_("一"),1,{}],[_("九"),1,{}],[_("十三"),1,{}],[_("二十四"),1,{}],[_("三十六"),1,{}],[_(""),1,{}]],
        _("功法"):[[_("法"),1,{}],[_("经"),1,{}],[_("诀"),1,{}],[_("功"),1,{}],[_("残篇"),1,{}]],
        _("身法"):[[_("变"),1,{}],[_("纵"),1,{}],[_("步"),1,{}],[_("遁"),1,{}],[_("闪"),1,{}]],
        _("武技"):[[_("破"),1,{}],[_("斩"),1,{}],[_("掌"),1,{}],[_("拳"),1,{}],[_("指"),1,{}]],
        _("战技"):[[_("术"),1,{}],[_("击"),1,{}],[_("剑"),1,{}],[_("刀"),1,{}],[_("枪"),1,{}]],

        _("九品"):[[_("还魂"),1,{}],[_("断肠"),1,{}],[_("妖"),1,{}],[_("仙"),1,{}],[_("异域"),1,{}]],
        _("六品"):[[_("金"),1,{}],[_("银"),1,{}],[_("暴血"),1,{}],[_("聚灵"),1,{}],[_("破境"),1,{}]],
        _("三品"):[[_("固本"),1,{}],[_("培元"),1,{}],[_("金创"),1,{}],[_("提气"),1,{}],[_("补神"),1,{}]],
        _("一品"):[[_("练气"),1,{}],[_("筑基"),1,{}],[_("回气"),1,{}],[_("益血"),1,{}],[_("治愈"),1,{}]],
        _("丹药"):[[_("丹"),1,{}],[_("散"),1,{}],[_("汤"),1,{}],[_("液"),1,{}],[_("药"),1,{}]],
        _("灵植"):[[_("叶"),1,{}],[_("花"),1,{}],[_("草"),1,{}],[_("果"),1,{}],[_("根"),1,{}]],
    },}
define RL_TREE_3 = {
    "tree": "西幻姓名",
    "link":{
        _("角色"):[_("男"),_("女")],
        _("男"):[_("男前名")],_("女"):[_("女前名")],
        _("男前名"):[_("男中名")],_("女前名"):[_("女中名")],
        _("男中名"):[_("男后名")],_("女中名"):[_("女后名")],
    },
    "node":{
        _("角色"):1,
        _("男"):4,_("女"):7,
        _("男前名"):1,_("女前名"):1,
        _("男中名"):1,_("女中名"):1,
        _("男后名"):1,_("女后名"):1,
    },
    "unit":{
        _("男前名"):[
            [_("诺亚"),1,{}],[_("利亚姆"),1,{}],[_("奥利弗"),1,{}],[_("以利亚"),1,{}],[_("卢卡斯"),1,{}],
            [_("马特奥"),1,{}],[_("利维"),1,{}],[_("阿瑟"),1,{}],[_("西奥多"),1,{}],[_("亨利"),1,{}],
            [_("亚历山大"),1,{}],[_("塞巴斯蒂安"),1,{}],[_("本杰明"),1,{}],[_("伊桑"),1,{}],[_("丹尼尔"),1,{}],
        ],
        _("女前名"):[
            [_("艾玛"),1,{}],[_("奥利维亚"),1,{}],[_("夏洛特"),1,{}],[_("索菲亚"),1,{}],[_("阿米莉亚"),1,{}],
            [_("伊莎贝拉"),1,{}],[_("米娅"),1,{}],[_("埃维莉娜"),1,{}],[_("卢娜"),1,{}],[_("艾娃"),1,{}],
            [_("哈珀"),1,{}],[_("吉安娜"),1,{}],[_("伊丽莎白"),1,{}],[_("埃莉诺"),1,{}],[_("斯卡利特"),1,{}],
        ],
        _("男中名"):[[_("."),1,{}]],
        _("女中名"):[[_("."),1,{}]],
        _("男后名"):[
            [_("史密斯"),1,{}],[_("约翰逊"),1,{}],[_("威廉姆斯"),1,{}],[_("布朗"),1,{}],[_("琼斯"),1,{}],
            [_("米勒"),1,{}],[_("戴维斯"),1,{}],[_("加西亚"),1,{}],[_("罗德里格斯"),1,{}],[_("威尔逊"),1,{}],
            [_("马丁内斯"),1,{}],[_("安德森"),1,{}],[_("泰勒"),1,{}],[_("托马斯"),1,{}],[_("杰克逊"),1,{}],
        ],
        _("女后名"):[
            [_("史密斯"),1,{}],[_("约翰逊"),1,{}],[_("威廉姆斯"),1,{}],[_("布朗"),1,{}],[_("琼斯"),1,{}],
            [_("米勒"),1,{}],[_("戴维斯"),1,{}],[_("加西亚"),1,{}],[_("罗德里格斯"),1,{}],[_("威尔逊"),1,{}],
            [_("马丁内斯"),1,{}],[_("安德森"),1,{}],[_("泰勒"),1,{}],[_("托马斯"),1,{}],[_("杰克逊"),1,{}],
        ],
    },}

# 树图放进工厂列表中
define RL_TREES = [RL_TREE_1, RL_TREE_2, RL_TREE_3]


第三部分 使用方式:
[RenPy] 纯文本查看 复制代码
# ---------- 最小使用流程示例 ----------
init python:
    # 自定义复杂类
    class Char:
        def __init__(self,seed=None,name=None,rare=None,type=None,race=None):
            self.seed = seed               # 存储种子实例

            # 显式给的名字 会 覆盖种子兜底属性
            if name is not None:
                self.name = name           
            if type is not None:
                self.type = type
            if race is not None:
                self.race = race
            if rare is not None:
                self.rare = rare

        # 用getattr方法,让种子数据只作为兜底,实例上不存在属性时,使用种子的属性
        def __getattr__(self,attr):
            if attr in ("name","type","race","rare") and self.seed is not None:
                seed = self.__dict__.get("seed")   # 直接查实例字典,防递归
                if seed is not None:
                    return getattr(seed,attr)
            raise AttributeError(attr)

    # 原生角色类  用 猴子补丁 注入种子兜底方法
    def adv_getattr(self,attr):
        if attr in ("type","race","rare"):
            seed = self.__dict__.get("seed")   # 直接查实例字典,防递归
            if seed is not None:
                return getattr(seed,attr)
        raise AttributeError(attr)
    # 注入
    ADVCharacter.seed = None                   # 类属性兜底,未注入时不炸
    ADVCharacter.__getattr__ = adv_getattr

# 原生角色的使用方法 动态转发name实际数据
default testchar = Character("testchar.seed.name",dynamic=True)
label RLSYS_test:
    "随机种子为实例,系统只负责生成和存储,不负责你如何使用"
    "种子本身带了封装好的返回属性函数"
    
    "用 RL.obj_random('树图名称','任意字符ID') 取得随机种子"
    $ RL.obj_random('中文姓名','随机1')

    # 用 RL.obj_random('树图名称','任意字符ID',[必须节点名],[回避节点名],[必须单元名],[回避单元名]) 取得定向随机种子
    $ RL.obj_random('中文姓名','随机1',['男'],[],['龙'],[])

    "使用不同树图,可用同样的字符ID"
    $ RL.obj_random('修仙道具','随机1')

    "使用相同树图,同字符ID会覆盖原来的种子"
    $ RL.obj_random('修仙道具','随机1')

    "用 RL.obj_get(树图名称,字符ID) 获取已创建的种子"
    $ RL.obj_get('修仙道具','随机1')

    "用 RL.obj_del(树图名称,字符ID) 删除已创建的种子"
    $ RL.obj_del('修仙道具','随机1')

    "用 RL.obj_list() 查看所有已创建的随机种子ID"
    $ RL.obj_list()
    
    $ testchar.seed = RL.obj_get('中文姓名','随机2')
    testchar "这个原生角色的name使用了动态延迟求值的方法,因此可以直接接入种子的name的getattr方法"
    "同时,别的数据也可以调用到种子方法或数据,[testchar.race]"

    # 可以重复覆盖,但注意用字符串引号多包裹一层,因为dynamic=True外层会从字面量转为表达式
    $ testchar.name = "'我改名了'"
    testchar "种子在使用上只是兜底"
    ""
    return


注意,此处只是数据实现,假设要批量的,自动管理多角色、多道具
请查阅 多数据管理技巧

#点击头像 查看我写的更多屎
粉身碎骨浑不怕,要留答辩在人间




您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|RenPy中文空间 ( 苏ICP备17067825号 )

GMT+8, 2026-9-7 23:15 , Processed in 0.025692 second(s), 8 queries , Redis On.

Powered by Discuz! X3.5

© 2001-2026 Discuz! Team.

快速回复 返回顶部 返回列表