Syser 发表于 2023-7-31 15:12:29

获取地编玩家 ID 对应的世界玩家 ID

本帖最后由 Syser 于 2023-7-31 15:27 编辑

为方便描述,现定义如下概念:


[*]地编玩家 ID,在场景编辑器中使用的玩家索引,例如某个触发器中效果的目标(地编)玩家是“玩家1”。
[*]世界玩家 ID,除了场景编辑器的任何地方使用,例如 AI 脚本、XS 脚本。

在大厅中,玩家的座位号是定义 2,世界玩家 ID。这意味着房主永远是 1,第一个进入游戏的那个人可能是 2,在已满 8 人的游戏大厅中,这八个人的 世界玩家 ID 从上到下依次是 1~8。

而这八个人在开始游戏前,可以选择他们在场景中扮演的玩家,通过点击带颜色的按钮选择自己的玩家颜色编号,即定义 1,地编玩家 ID。例如房主选择了编号 2,颜色可能是红色。在进入游戏后,他将扮演场景中的玩家 2,颜色是红色。

在这种情况下,房主的 世界玩家 ID 是 1,但 地编玩家 ID 是 2。

在观战时按 Ctrl + Shift + F1,是切换到 世界玩家 ID 为 1 的玩家,而不是 地编玩家 ID 为 1 的玩家。这也是单机中无论自己是何种颜色编号,Ctrl + Shift + F1 始终能切换到自己的原因——自己始终是座位 1。

在地编中,地编玩家 ID 对应的颜色是可以修改的。例如,将玩家 3 颜色改成红色,那么红色就是编号 3,进入游戏后,就会看到他的颜色是红色,且玩家名称前有一个红色的 3。此时对他生效的触发,是玩家 3 的触发。在自定义场景中,如果只用 XS 脚本,或者只用场景触发器,是不会有任何问题的。但如果同时使用两者,甚者再加上 AI 脚本,世界玩家 ID(座位号)和地编玩家 ID(颜色编号)的不同会影响到场景逻辑。

解决思路:
地编中的资源(贡品)可以被 XS 函数读取,可以选取一个无用资源,通过触发器赋予八个玩家的值分别为 1~8。由于地编使用的是 地编玩家 ID,当我们在 XS 函数中顺序读取时使用的却是 世界玩家 ID,我们就可以依次记录当前 世界玩家 ID 对应的 地编玩家 ID。不过这种记录没有什么作用,因为记录是为了能在 XS 中索引到地编玩家,应该反过来记录“当前数字下标所代表的 地编玩家 ID 对应的 世界玩家 ID”。这样,每当我们想索引地编中的玩家时,就通过它获取在 XS 函数中可用的 世界玩家 ID。

思路已经明确,解决起来也不困难。在上文所述的触发器新建后,我们可以建立八个全局变量 p1, p2,..., p8,分别代指 地编玩家 ID 为 1~8 的玩家对应的 世界玩家 ID。在某个函数中初始化,另外要注意,由于场景触发器启动时间比 main() 更晚,故不可在 main 中使用这些变量,即不能在 main() 中索引地编玩家。这些变量的初始化,要在场景触发器为这八名玩家的资源(贡品)赋值后才能成功。

以下例程是可能的实现,其原理同上,只是在一些细则上有所不同:

[*]考虑到有遍历的需求,例程并不按八个全局变量的方式来实现,而是使用函数 p() 中的参数代表需要索引的地编玩家
[*]考虑到场景玩家不一定是 8 名,例程并没有固定玩家数量
/// @brief 获取地编玩家 ID 对应的世界玩家 ID。
/// 在调用本函数前,请在场景触发器中新建一个不循环触发,
/// 添加 8 个效果,分别设置玩家 1~8 的无用资源(例如 200 号资源)分别为 1~8,
/// 本函数会在被首次调用时初始化。
/// 此后,使用 p(1), p(2),而不是 1, 2。
/// 例如,设置地编中的玩家 3 的黄金为 999,应该调用:
/// xsSetPlayerAttribute(p(3), cAttributeGold, 999);
/// @param scenarioPlayerId 玩家在地编中的 ID,即他们在大厅中选择的颜色数字
/// @return 如果 scenarioPlayerId 有效,则返回对应玩家的世界 ID,否则返回 0。
/// 玩家的世界 ID 是他在大厅中的座位号,在 xs 内部,始终使用这个 ID 索引玩家。
int p(int scenarioPlayerId = 0) {
const int ResColor = 200;
static int stacked = 0;
int length = xsGetNumPlayers();
if (0 == stacked)
    for (i = 1; <= length)
      stacked = stacked + i * pow(10, xsPlayerAttribute(i, ResColor) - 1);

if (0 >= scenarioPlayerId || scenarioPlayerId > length)
    return (0);

scenarioPlayerId = pow(10, scenarioPlayerId - 1);
return ((stacked / scenarioPlayerId) % 10);
}

/// @brief 以场景玩家 ID 为序,打印他们对应的世界 ID。
void printPlayerWorldID() {
for (i = 1; <= xsGetNumPlayers())
    xsChatData("Scenario Player " + i + " World ID: " + p(i));
}

/// @brief 以世界玩家 ID 为序,打印他们对应的场景 ID。
/// 按照预期,打印的输出顺序应该和玩家们在大厅中选择的颜色数字相同
void printPlayerScenarioId() {
static int stacked = 0;
int length = xsGetNumPlayers();
if (0 == stacked)
    for (i = 1; <= length)
      stacked = stacked + i * pow(10, p(i) - 1);
int id = stacked;
for (i = 1; <= length) {
    xsChatData("World Player " + i + " Scenario ID:" + id % 10);
    id = id / 10;
}
}
有关 AI 脚本中的两者互换,可以参考:
newtonerdai, " [玩家编号-颜色校正模块] 在联机场景里终于可以正常使用数字AI啦!,"   Hawkaoe, Mar. 21, 2020. https://www.hawkaoe.net/bbs/thread-146068-1-1.html(访问时间 Jan. 1, 1970).
页: [1]
查看完整版本: 获取地编玩家 ID 对应的世界玩家 ID