实时 XML

实时 XML

一个用于在游戏运行时修改Python对象(源自XML调谐文件)的模组。无需创建覆盖模组来启用/禁用自主行为,该模组可动态调整这些及众多其他设置。从此告别过时

实用工具

实时 XML 调音编辑器

这个模组是为那些想在不编辑 XML 文件的情况下调整数值的玩家准备的。它是覆盖文件的更优替代方案。

我们经常看到一些三个月前的 XML 调整覆盖模组,与当前的 XML 文件对比时,明显可以看出 EA 在 XML 文件中添加了许多新内容(修复、增益、特性、商品等),而这些内容在模组中是缺失的。

它们经常在 EA 发布新的 TS4 版本时失效。

作为一个普通用户,当其他模组需要此模组时,您会想要安装它。

该模组本身完全不修改游戏玩法。

当然,你可以安装它,并从中受益(或受苦),如果你未能移除包含的示例配置文件,它们将导致游戏变化。

该模组需要 S4CL,可从以下网址下载:

现在包含了一个帮助创建配置文件的命令。

现实世界示例

我们该如何实现一个简单的需求,即禁用某个交互的自主行为?

复制 XML 调谐

不要这样做! 遗憾的是,已经有很多模组是以这种方式创建的。

简单的方法就是复制调校(例如 stereo_Dance_LocalCultureSkill)并在其中添加一行 <T n="allow_autonomous">False</T>。

然后将 XML 添加到一个新的 .package 文件中,就完成了。

为此,我们对 10 个相似的调谐进行复制操作,复制所有 10 个文件,编辑它们并将它们添加到 .package 中。

然后我们向 EA 祈祷,希望他们永远不要修改这些调校,尽管我们知道我们做错了。

如果你是这样创建你的调校模组的,可以考虑使用这个模组。

图片中我们只看到缺失了三行,实际上改装调校中共缺失了 200 行。

“实时编辑” XML

我们需要收集调校数据,为此模组我们需要名称。

支持通配符,便于匹配相似的调谐(例如 stereo_Dance_*)。

要将 allow_autonomous 设置为 False,技术上需要修改已加载调校的属性。

通过此模组,我们将一个变量定义为 False 并调整了调谐参数。

它看起来有点像一种基础语言,实际上也确实如此:

  • 赋值:值 = False
  • setattr: 调校, allow_autonomous, 值

这段代码在加载地块后立即执行,并动态修改已加载的调谐参数。

其他现实世界的例子

可能存在一份很长的列表,其中包含复制的 XML 文件,这些文件现在缺少大量数值。

有些测试你只想禁用它们,每次都让它们失败。

例如,要禁用广播员,通常可以通过在调整中将 allow_sims 设置为 False 来实现:

  • assign: value = False
  • setattr:调整,allow_sims,值

存在 max_value_tuning、max_duration 等高值参数,以避免交互的快速重复执行。

或者它们的数值可能较低,以避免效果持续应有的时间,反之亦然。

修复它其实很简单:

  • 赋值:值 = 3660
  • cwo: var = _temporary_commodity_info, max_duration, value # 可选地,如果我们需要修改一个冻结集合
  • setattr: 调谐, max_value_tuning, 值

这些都是简单的例子,该模组可能还不支持编辑调校的所有选项。

特别是更复杂的测试可能难以通过此模组实现。

目前支持的功能包括位置测试、半径大小调整、创建集合和不可变集合。此外,还可以进行简单的条件检查和循环操作。

代码可能如下所示:

  • frozenset: foo = 1, 2, 3
  • foreach: 元组, 条
  • foreach: 元素, 元组
  • setattr: 元素, 属性, foo

安全

本模组使用 literal_eval() 读取并解析配置文件为 Python dict() 元素。

元素应被正确处理为变量,且绝不应被执行。

请告诉我你是否能识别安全问题,没有人想运行一个带有代码注入或 shell 漏洞的模组。

目前我建议你在保存到 mod_data/live_xml/ 之前先检查一下文件。

我认为这个模组是安全的,否则我不会发布它。

轻度警告 - 游戏机制已变更

mod_data/*.dict 文件用于调整游戏参数。它们以多种方式改变游戏机制和判定(类似于带有 XML 的 .package 文件)。

请检查 mod_data 中的文件,并移除您不想使用的文件。广播过滤会影响多个模拟市民。修改后的年龄/种族/性别检查可能会允许模拟市民执行原本不打算对他们开放的任务。修改后的范围检查可能会降低或提高模拟市民的隐私需求。

包含的文件不应过多影响游戏玩法,但全面测试存在难度。

作弊指令

要创建新的配置文件,最好清理 mod_data/live_xml 目录以便仅处理新文件。

要重新加载文件,可以使用 o19.live_xml.reload,它会将所有内容记录到 mod_logs/LiveXML_Messages.log 中。

日志记录较为详细,因此您可能会看到比预期更多的信息。

模组冲突

本模组使用了作弊命令 inspect,并与 scumbumbo 的 inspector.py 等类似模组冲突。

合并

大多数人不会合并脚本模组。

此模组可通过 ZIP 程序与其他模组合并。

文件名可被重命名,它不用于引用任何内容。

未来

可以利用 Snippet Tuning XML 格式来添加 JSON 数据(或在 XML 标签内),这样模组创作者仍然可以交付一个 .package 文件,而不是需要存储在 mod_data 中的文件。

对于简单且常见的属性,可以为所有对象添加饼图菜单交互。

这很容易实现,但我不确定我们是否真的需要这个功能。

创建自定义设置

请使用现已独立为单独模组的“调谐检查器”。无论是“实时 XML”还是“修补 XML”都可能需要检查器,因此将其作为一个独立模组是合理的。

配置文件结构

所有需要替换的内容都已放入圆括号 (...) 中。括号及其之间的所有内容都必须被替换。

结构:

{
'(自定义唯一名称)': {
  'manager_imp': '(通常为大写)',  # 可选参数
  'tunings_imp': ['(调谐名称,支持前导和结尾通配符 *)', '(另一个调谐名称)'],  # 可选参数
  'manager': '(所需管理器,通常为大写)',  # core/sims4/resources.py 或在 tuning i="..."
  'tunings': ['(调音名称,支持前导和结尾的通配符 *)', '(另一个调音名称)'],  # 你可以在这里添加调音 ID 作为注释
  'items': ['(在此处定义调谐的几个属性以便获取)'],
  '流程': [
    '(第一个命令)',
    '(第二个命令)',  # 以此类推。
    '(循环或条件命令)',
    '(内部循环命令或当条件为真时执行的进程)', # 使用 4 个空格或 TAB 键来缩进循环内的命令。
    ],
  'command': '(调试命令)',  # 运行命令
  'command_parameters': '(调试命令参数)',  # 运行命令
  }
}

引用调谐

要访问增益或特性的 ID 或调整以将其添加到阻止或允许列表中,可以从以下配置开始:

'manager_imp': '...', 和 'tunings_imp': ['...', '...', ] - 这将添加到变量中:

  • tunings_imp_tunings = tuple(<类 ...>, )
  • tunings_imp_ids = tuple(123, )

之后可以将它们作为普通变量访问,并将调谐类列表添加到 EA 的允许列表中:

新增:new_whitelist_traits = whitelist_traits, tunings_imp_tunings,

命令

所有以 * 结尾的字符串都会被添加至/读取自本地数据存储。因此 var* 与 var 并不相同。请勿混淆。

'manager': '...',以及 'tunings': ['...', '...', ] - 'tuning*' = get_tuning(调谐名称, 管理器名称)

如果调谐匹配多个调谐,则所有调谐将依次循环处理。

在流程中加入了业务逻辑。

'items': ['...', '...', ] - 所有这些物品将被添加到全局变量中。物品 'foo' 将被添加到全局变量 'foo*' 中,其值为 tuning.foo。物品 'foo.bar' 将如上添加 'foo*',并添加 'bar*',其值为 tuning.foo.bar。也可以使用 getattr: 命令。

  • 赋值:var* = value - 值 'value' 可以是 'None'、'True'、'False'、整数、浮点数或字符串。该值将被赋给 'var*'。'value' 是字符串且不引用变量。

  • 获取属性:var* = obj*, prop - 将 'obj*' 的属性 'prop' 赋给 'var*'。'prop' 是字符串且不引用变量。

  • 设置属性:obj*, prop, value* - 将 'obj*' 的属性 'prop' 设为 'value*'。'prop' 是字符串,而 'value*' 引用先前已赋值的变量。

  • 类定义:var* = string - 将类 'string' 赋给 'var*'。这不是字符串赋值,该类必须存在。

  • 类字符串:var* = string - 将 'string' 作为字符串赋给 'var*'。仅当无法加载类时应使用此方式(例如包装类)。

  • 冻结集合:var* = - 创建空冻结集合。

  • 冻结集合:var* = value, value2, ... - 创建包含 1-n 个元素的 frozenset()。有效值参见 '赋值:' 说明。

  • 集合:var* = value, value2, ... - 创建包含 1-n 个元素的 set()。有效值参见 '赋值:' 说明。

  • 元组:var* = value, value2, ... - 创建包含 1-n 个元素的 tuple()。有效值参见 '赋值:' 说明。

  • 列表:var* = value, value2, ... - 创建包含 1-n 个元素的 list()。有效值参见 '赋值:' 说明。

对于冻结集合/集合/元组/列表,可用 '**var**' 代替 value, value2,...。此时将直接赋值 'var*' 的值。特别适用于将类/调谐赋值给元组。

  • 克隆对象:var* = obj*, key, value* - 用 'ImmutableSlots' 的 'key' 克隆 'obj*' 对象到新 'value*',并存入 'var*'

  • 打印:var* - 输出变量值用于调试。

  • 打印成员:var* - 输出变量值及其所有成员用于调试。

简单的数学函数。希望不需要用到 sin/cos/tan/sqrt/pow()。

  • mul: var* = var1*, var2* - 将 'var1*' * 'var2*' 赋值给 'var*'。
  • div: var* = var1*, var2* - 将 'var1*' / 'var2*' 赋值给 'var*'。
  • add: var* = var1*, var2* - 将 'var1*' + 'var2*' 赋值给 'var*'。
  • sub: var* = var1*, var2* - 将 'var1*' - 'var2*' 赋值给 'var*'。
  • addut: var* = var1*, var2* - 将 'var1*' + 'var2*' 赋值给 'var*'。将 var* 转换为具有唯一值的元组。- tuple(set(var*))
  • addul: var* = var1*, var2* - 将 'var1*' + 'var2*' 赋值给 'var*'。将 var* 转换为具有唯一值的列表。- list(set(var*))
  • addu? 命令会打乱元素的顺序。这应该不是问题。

待办事项

替换功能

  • --add: var*, value, value2, ... - 将 'value','value2',... 追加到 '*var' 中。
  • --del: var*, value, value2, ... - 从 '*var' 中移除 'value','value2',...。
  • --sub: var*, value1, value2 - 在 '*var' 中用 'value2' 替换 'value1'。

对于所有三个命令,可以指定 '**var**'。在这种情况下,将使用 'var*' 的值。

循环、条件判断等

允许创建更复杂的配置。

  • if: var* - 检查 'var*' 是否已设置。
  • ifnot: var* - 检查 'var*' 是否未设置。
  • ifis: var1*, var2* - 检查 'var1*' 是否等于 'var2*'。
  • ifnotis: var1*, var2* - 检查 'var1*' 是否不等于 'var2*'。
  • ifin: var1*, var2* - 检查 'var1*' 是否在 'var2*' 中。'var2*' 应为集合/列表/元组。
  • ifnotin: var1*, var2* - 检查 'var1*' 是否不在 'var2*' 中。
  • isinstance: var*, class* - 检查 'var*' 是否为 'class*' 的实例。
  • isinstancestr: var*, classstr* - 检查 'type(var*)' 是否与 'classstr*' 相同。
  • isinstancetypestr: var*, class* - 检查 'var*: type(var*)' 是否与 'classstr*' 相同。
  • foreach: var*, vars* - 遍历 'vars*' 中的所有元素并将其赋值给 'var*'。

以下所有命令必须缩进 1 个 TAB 或 4 个空格字符。

检查和循环可以嵌套,下一级需要使用另一个 TAB 或 4 个空格字符进行缩进。

没有其他可用的选项,一旦意图被移除,'if' 部分就结束了。

执行命令

交互允许指定 <L n="basic_extras"><V t="do_command"> 在执行交互时调用 Python 命令。

与过程命令一样,需要一个管理器(强烈建议使用 INTERACTION,这是默认选项),并且需要调整调谐参数。

以下参数被替代使用:

  • 'manager': INTERACTION, - 如未指定,默认为 INTERACTION。
  • 'tunings': ['...', '...', ] - 必需。请参考上文
  • 'command': '...' - 必需。要执行的命令。可通过调试控制台(Shift+Ctrl+C)调用以进行测试。
  • 'command_parameters: '...' - 如需指定参数则为必需。模组会将调谐名称或 ID 以 '+' 作为分隔符添加到 command_parameters 前。多个参数可通过 '+' 连接并作为一个字符串指定,例如 'buff_add_123+trait_add_123'。
  • 'command_timing': '...' - 可选。可以是 at_end(默认)或 at_beginning。
  • 'command_offset_time': '...' - 可选。command_timing 必须为 at_beginning,且命令会延迟一段时间调用。由于需要消耗大量 CPU 资源,此选项非必需。
  • 'command_xevt_id': '...' - 可选。可指定动画剪辑头中定义的 xevt_id,以便在动画的特定时间点执行命令。

LiveXML 本身并不包含需要调用的命令。

它支持需要更复杂操作的模组创作者使用 Python 编写这些操作,并在交互中调用它们。

LiveXML 未来可能实现的功能:

  • 添加/移除增益和特征
  • 更换服装
  • 穿戴/脱下帽子、手套、袜子和鞋子
  • 添加/移除体毛
  • 模拟市民年龄增长/减少
  • 调整体型/胖瘦/身体滑块

同样在 <V n="outcome" t="single"><U n="single"><U n="actions"><L n="basic_extras"> 中,可以添加 do_command - 但此模组目前尚未支持该功能。

附录

游戏兼容性

该模组已与《模拟人生 4》1.103.315 版本(2023 年 12 月)、S4CL 3.0(2023 年 12 月)、TS4Lib 0.1.8(2023 年 9 月)进行过兼容性测试。

预计将与即将发布的多个 TS4、S4CL 和 TS4Lib 版本兼容。

依赖项

  • 模拟人生 4 TS4-库 (NexusMods) 或 TS4-库 (GitHub)
  • S4CL (NexusMods) 或 S4CL (GitHub)

如果未安装,请下载并安装 TS4 及以下模组。

全部免费提供。

安装

定位包含 Mods 文件夹的本地化 The Sims 4 文件夹。将 ZIP 文件解压到此 The Sims 4 文件夹中。

这将创建以下目录/文件:

  • Mods/_o19_/$mod_name.ts4script
  • [i]Mods/_o19_/$mod_name.package
  • [i]mod_data/$mod_name/*

和/或

  • mod_documentation/$mod_name/*

mod_logs/$mod_name.txt 将在数据被记录时自动创建。

手动安装

如果你不想将 ZIP 文件解压到 The Sims 4 文件夹中,你可能需要阅读以下内容。

ZIP 文件 /mod_data 中的文件通常是必需的,应解压到 The Sims 4/mod_data 目录下。

ZIP 文件 /mod_documentation 中的文件供您阅读,使用此模组时不需要这些文件。

[i]Mods/_o19_/*.ts4script 文件可以存放在 Mods 中的任意文件夹内,或直接放在 Mods 目录下。我强烈建议将其存放在 [i]_o19_ 文件夹中,这样你就知道是谁创建了它。

使用情况跟踪 / 隐私

该模组不会向任何追踪服务器发送数据。

代码为开源且未经混淆,可供审查。

日志文件(mod_logs 文件夹)中的某些日志条目可能包含本地用户名,尤其是当文件未找到时(WARN、ERROR)。