城市一夜


关于本教程

本教程是免费且开源的,所有代码均使用 MIT 许可证 - 因此您可以随意使用。我希望您喜欢本教程,并制作出色的游戏!

如果您喜欢这个教程并希望我继续写作,请考虑支持我的 Patreon

动手 Rust 编程


游戏的下一关是一个黑暗精灵城市。设计文档在细节上有些简略,但这是我们所知道的:

  • 它最终通向一个前往深渊的传送门。
  • 黑暗精灵天生好斗,尔虞我诈,应该表现得像那样。
  • 黑暗精灵城市出奇地像城市,只是位于地下深处。
  • 光照将非常重要。

生成一个基础城市

map_builders/mod.rs 中的 level_builder 函数控制为给定关卡调用哪个地图算法。为新的地图类型添加一个占位符条目:

#![allow(unused)]
fn main() {
pub fn level_builder(new_depth: i32, width: i32, height: i32) -> BuilderChain {
    rltk::console::log(format!("Depth: {}", new_depth));
    match new_depth {
        1 => town_builder(new_depth, width, height),
        2 => forest_builder(new_depth, width, height),
        3 => limestone_cavern_builder(new_depth, width, height),
        4 => limestone_deep_cavern_builder(new_depth, width, height),
        5 => limestone_transition_builder(new_depth, width, height),
        6 => dwarf_fort_builder(new_depth, width, height),
        7 => mushroom_entrance(new_depth, width, height),
        8 => mushroom_builder(new_depth, width, height),
        9 => mushroom_exit(new_depth, width, height),
        10 => dark_elf_city(new_depth, width, height),
        _ => random_builder(new_depth, width, height)
    }
}
}

在同一文件的顶部,为新的 builder 模块添加导入:

#![allow(unused)]
fn main() {
mod dark_elves;
use dark_elves::*;
}

并创建新的 map_builders/dark_elves.rs 文件,并在其中放置一个占位符 builder:

#![allow(unused)]
fn main() {
use super::{BuilderChain, XStart, YStart, AreaStartingPosition,
    CullUnreachable, VoronoiSpawning,
    AreaEndingPosition, XEnd, YEnd, BspInteriorBuilder };

pub fn dark_elf_city(new_depth: i32, width: i32, height: i32) -> BuilderChain {
    println!("Dark elf builder"); // 黑暗精灵生成器
    let mut chain = BuilderChain::new(new_depth, width, height, "Dark Elven City");
    chain.start_with(BspInteriorBuilder::new());
    chain.with(AreaStartingPosition::new(XStart::CENTER, YStart::CENTER));
    chain.with(CullUnreachable::new());
    chain.with(AreaStartingPosition::new(XStart::RIGHT, YStart::CENTER));
    chain.with(AreaEndingPosition::new(XEnd::LEFT, YEnd::CENTER));
    chain.with(VoronoiSpawning::new());
    chain
}
}

这创建了一个完全不像城市的地图(只是一个 bsp 内部地图)——但这是一个好的开始。我选择这个作为基础 builder,因为它不浪费任何空间。我喜欢想象这座城市是一个巨大的相互连接的房间组成的迷宫,较贫穷的精灵住在危险的地方(顶部)。因此,我们将在这一层填充相对“正常”的黑暗精灵和他们的奴隶。

添加一些黑暗精灵

如果我们只是想在到处放置黑暗精灵,那么只需在 spawns.jsonspawn_table 部分添加一行即可:

{ "name": "Dark Elf", "weight": 10, "min_depth": 10, "max_depth": 11 }

那样太无聊了,所以我们不那样做。我们的黑暗精灵分为 Arbat 氏族Barbo 氏族Cirro 氏族(A、B、C,明白了吗?)。由于 YALA 护身符的深渊影响,他们内部充满了可怕的争斗和战争!我们稍后会担心区分氏族,现在让我们先创建一些条目,提供三组互相憎恨的黑暗精灵。

spawns.jsonfactions 部分,创建三个新的派系:

{ "name" : "DarkElfA", "responses" : { "Default" : "attack", "DarkElfA" : "ignore", "DarkElfB" : "attack", "DarkElfC" : "attack" } },
{ "name" : "DarkElfB", "responses" : { "Default" : "attack", "DarkElfB" : "ignore", "DarkElfA" : "attack", "DarkElfC" : "attack" } },
{ "name" : "DarkElfC", "responses" : { "Default" : "attack", "DarkElfC" : "ignore", "DarkElfA" : "attack", "DarkElfB" : "attack" } }

请注意他们如何忽略自己的氏族,并攻击其他氏族。这是制造战区的关键!我们的派系系统已经支持交战团体——我们只是没有广泛使用它。现在找到 mobs 部分,并将“Dark Elf”复制三次——每个派系一次:

{
    "name" : "Arbat Dark Elf",
    "renderable": {
        "glyph" : "e",
        "fg" : "#FF0000",
        "bg" : "#000000",
        "order" : 1
    },
    "blocks_tile" : true,
    "vision_range" : 8,
    "movement" : "random_waypoint",
    "attributes" : {},
    "equipped" : [ "Hand Crossbow", "Scimitar", "Buckler", "Drow Chain", "Drow Leggings", "Drow Boots" ],
    "faction" : "DarkElfA",
    "gold" : "3d6",
    "level" : 6
},

{
    "name" : "Barbo Dark Elf",
    "renderable": {
        "glyph" : "e",
        "fg" : "#FF0000",
        "bg" : "#000000",
        "order" : 1
    },
    "blocks_tile" : true,
    "vision_range" : 8,
    "movement" : "random_waypoint",
    "attributes" : {},
    "equipped" : [ "Hand Crossbow", "Scimitar", "Buckler", "Drow Chain", "Drow Leggings", "Drow Boots" ],
    "faction" : "DarkElfB",
    "gold" : "3d6",
    "level" : 6
},

{
    "name" : "Cirro Dark Elf",
    "renderable": {
        "glyph" : "e",
        "fg" : "#FF0000",
        "bg" : "#000000",
        "order" : 1
    },
    "blocks_tile" : true,
    "vision_range" : 8,
    "movement" : "random_waypoint",
    "attributes" : {},
    "equipped" : [ "Hand Crossbow", "Scimitar", "Buckler", "Drow Chain", "Drow Leggings", "Drow Boots" ],
    "faction" : "DarkElfC",
    "gold" : "3d6",
    "level" : 6
},

在 spawn 表中,我们希望它们出现在 10 级:

{ "name" : "Arbat Dark Elf", "weight": 10, "min_depth": 10, "max_depth": 11 },
{ "name" : "Barbo Dark Elf", "weight": 10, "min_depth": 10, "max_depth": 11 },
{ "name" : "Cirro Dark Elf", "weight": 10, "min_depth": 10, "max_depth": 11 }

如果你现在 cargo run,并作弊下到 10 层(我建议使用上帝模式和传送)——你会发现自己身处三个氏族之间的战区之中。到处都是战斗,他们只会在互相残杀的间隙停下来谋杀玩家。这里有大量的混乱——混沌之神会感到自豪的。

氏族区分

让所有氏族都一模一样有点无聊。基本的“Dark Elf”可以保持不变,但让我们添加一些特色,使氏族 感觉 与众不同。

Arbat 氏族

我们将首先使 Arbat 氏族变成不同的颜色——更浅的红色。将他们的 Dark Elf 的 “fg” 属性替换为 #FFAAAA ——一种粉红色。我们也将拿走他们的弩。他们是一个以近战为主的氏族。将 Scimitar 替换为 Scimitar +1。修改后的 Arbat Dark Elf 看起来像这样:

{
    "name" : "Arbat Dark Elf",
    "renderable": {
        "glyph" : "e",
        "fg" : "#FFAAAA",
        "bg" : "#000000",
        "order" : 1
    },
    "blocks_tile" : true,
    "vision_range" : 8,
    "movement" : "random_waypoint",
    "attributes" : {},
    "equipped" : [ "Scimitar +1", "Buckler", "Drow Chain", "Drow Leggings", "Drow Boots" ],
    "faction" : "DarkElfA",
    "gold" : "3d6",
    "level" : 6
},

我们也给他们一些首领——更强大的战士:

{
    "name" : "Arbat Dark Elf Leader",
    "renderable": {
        "glyph" : "E",
        "fg" : "#FFAAAA",
        "bg" : "#000000",
        "order" : 1
    },
    "blocks_tile" : true,
    "vision_range" : 8,
    "movement" : "random_waypoint",
    "attributes" : {},
    "equipped" : [ "Scimitar +2", "Buckler +1", "Drow Chain", "Drow Leggings", "Drow Boots" ],
    "faction" : "DarkElfA",
    "gold" : "3d6",
    "level" : 7
},

他们也应该得到一些兽人奴隶:

{
    "name" : "Arbat Orc Slave",
    "renderable": {
        "glyph" : "o",
        "fg" : "#FFAAAA",
        "bg" : "#000000",
        "order" : 1
    },
    "blocks_tile" : true,
    "vision_range" : 8,
    "movement" : "static",
    "attributes" : {},
    "faction" : "DarkElfA",
    "gold" : "1d8"
},

最后,将这些放入 spawn 表:

{ "name" : "Arbat Dark Elf", "weight": 10, "min_depth": 10, "max_depth": 11 },
{ "name" : "Arbat Dark Elf Leader", "weight": 7, "min_depth": 10, "max_depth": 11 },
{ "name" : "Arbat Orc Slave", "weight": 14, "min_depth": 10, "max_depth": 11 },

他们可能会后悔专注于近战,但我们不太关心他们的健康!

Barbo 氏族

相反,我们将使 Barbo 氏族非常侧重于远程武器——并且更加稀有,因为那样非常危险。我们还将给他们一把匕首而不是弯刀,并将他们的颜色改为橙色:

{
    "name" : "Barbo Dark Elf",
    "renderable": {
        "glyph" : "e",
        "fg" : "#FF9900",
        "bg" : "#000000",
        "order" : 1
    },
    "blocks_tile" : true,
    "vision_range" : 8,
    "movement" : "random_waypoint",
    "attributes" : {},
    "equipped" : [ "Hand Crossbow +1", "Dagger", "Buckler", "Drow Chain", "Drow Leggings", "Drow Boots" ],
    "faction" : "DarkElfB",
    "gold" : "3d6",
    "level" : 6
},

他们也得到一些奴隶——这次是地精,带有远程武器:

{
    "name" : "Barbo Goblin Archer",
    "renderable": {
        "glyph" : "g",
        "fg" : "#FF9900",
        "bg" : "#000000",
        "order" : 1
    },
    "blocks_tile" : true,
    "vision_range" : 8,
    "movement" : "static",
    "attributes" : {},
    "faction" : "Cave Goblins",
    "gold" : "1d6",
    "equipped" : [ "Shortbow", "Leather Armor", "Leather Boots" ]
},

最后,更新 spawns 表以包含它们:

{ "name" : "Barbo Dark Elf", "weight": 9, "min_depth": 10, "max_depth": 11 },
{ "name" : "Barbo Goblin Archer", "weight": 13, "min_depth": 10, "max_depth": 11 },

Cirro 氏族

我们将使 Cirro 氏族强大而稀有。基本的 Cirro Dark Elf 看起来像这样:

{
    "name" : "Cirro Dark Elf",
    "renderable": {
        "glyph" : "e",
        "fg" : "#FF00FF",
        "bg" : "#000000",
        "order" : 1
    },
    "blocks_tile" : true,
    "vision_range" : 8,
    "movement" : "random_waypoint",
    "attributes" : {},
    "equipped" : [ "Hand Crossbow", "Scimitar", "Buckler", "Drow Chain", "Drow Leggings", "Drow Boots" ],
    "faction" : "DarkElfC",
    "gold" : "3d6",
    "level" : 7
},

我们还将给他们首领——可以网住你的女祭司:

{
        "name" : "Cirro Dark Priestess",
        "renderable": {
            "glyph" : "E",
            "fg" : "#FF00FF",
            "bg" : "#000000",
            "order" : 1
        },
        "blocks_tile" : true,
        "vision_range" : 8,
        "movement" : "random_waypoint",
        "attributes" : {},
        "equipped" : [ "Hand Crossbow", "Scimitar", "Buckler", "Drow Chain", "Drow Leggings", "Drow Boots" ],
        "faction" : "DarkElfC",
        "gold" : "3d6",
        "level" : 8,
        "abilities" : [
            { "spell" : "Web", "chance" : 0.2, "range" : 6.0, "min_range" : 3.0 }
        ]
    },

我们不给他们奴隶,而是给他们蜘蛛:

{
    "name" : "Cirro Spider",
    "level" : 3,
    "attributes" : {},
    "renderable": {
        "glyph" : "s",
        "fg" : "#FF00FF",
        "bg" : "#000000",
        "order" : 1
    },
    "blocks_tile" : true,
    "vision_range" : 6,
    "movement" : "static",
    "natural" : {
        "armor_class" : 12,
        "attacks" : [
            { "name" : "bite", "hit_bonus" : 1, "damage" : "1d12" }
        ]
    },
    "abilities" : [
        { "spell" : "Web", "chance" : 0.2, "range" : 6.0, "min_range" : 3.0 }
    ],
    "faction" : "DarkElfC"
},

这也需要更新 spawn 表:

{ "name" : "Cirro Dark Elf", "weight": 7, "min_depth": 10, "max_depth": 11 },
{ "name" : "Cirro Dark Priestess", "weight": 6, "min_depth": 10, "max_depth": 11 },
{ "name" : "Cirro Spider", "weight": 10, "min_depth": 10, "max_depth": 11 }

如果你现在 cargo run 该项目,你会发现黑暗精灵正在互相残杀——并且呈现出良好的多样性。

总结

这是一个简短的章节:因为大多数先决条件已经编写完成。这对整个引擎来说是一个好兆头:我们现在可以构建非常不同风格的关卡,而无需编写太多新代码。在下一章中,我们将进一步深入黑暗精灵城市——尝试创建一个更开放的城市关卡。混乱将继续!


本章的源代码可以在这里找到

在您的浏览器中使用 web assembly 运行本章的示例 (需要 WebGL2)

版权 (C) 2019, Herbert Wolverson.