蘑菇森林
关于本教程
本教程是免费且开源的,所有代码均使用 MIT 许可证 - 因此您可以随意使用它。 我希望您会喜欢本教程,并制作出色的游戏!
如果您喜欢这个教程并希望我继续写作,请考虑支持 我的 Patreon。
设计文档指出,一旦你征服了堡垒中的巨龙,你将进入一片广阔的蘑菇森林。这是一个有趣的过渡:我们之前做过森林,但我们想让蘑菇森林与深入树林关卡有所不同。在这个关卡中,我们也希望在堡垒和森林之间进行过渡 - 所以我们需要另一种分层方法。
我们将从在 map_builder/mod.rs
中的关卡构建器中添加一个新函数开始:
#![allow(unused)] fn main() { mod mushroom_forest; use mushroom_forest::*; ... pub fn level_builder(new_depth: i32, rng: &mut rltk::RandomNumberGenerator, width: i32, height: i32) -> BuilderChain { rltk::console::log(format!("Depth: {}", new_depth)); match new_depth { 1 => town_builder(new_depth, rng, width, height), 2 => forest_builder(new_depth, rng, width, height), 3 => limestone_cavern_builder(new_depth, rng, width, height), 4 => limestone_deep_cavern_builder(new_depth, rng, width, height), 5 => limestone_transition_builder(new_depth, rng, width, height), 6 => dwarf_fort_builder(new_depth, rng, width, height), 7 => mushroom_entrance(new_depth, rng, width, height), _ => random_builder(new_depth, rng, width, height) } } }
现在我们将创建一个新文件,map_builder/mushroom_forest.rs
:
#![allow(unused)] fn main() { use super::{BuilderChain, XStart, YStart, AreaStartingPosition, CullUnreachable, VoronoiSpawning, AreaEndingPosition, XEnd, YEnd, CellularAutomataBuilder, PrefabBuilder, WaveformCollapseBuilder}; use crate::map_builders::prefab_builder::prefab_sections::UNDERGROUND_FORT; pub fn mushroom_entrance(new_depth: i32, _rng: &mut rltk::RandomNumberGenerator, width: i32, height: i32) -> BuilderChain { let mut chain = BuilderChain::new(new_depth, width, height, "Into The Mushroom Grove"); chain.start_with(CellularAutomataBuilder::new()); chain.with(WaveformCollapseBuilder::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.with(PrefabBuilder::sectional(UNDERGROUND_FORT)); chain } }
这应该看起来很熟悉:我们再次使用了细胞自动机 - 但将其与一些波形坍缩混合在一起,然后在上面添加了一个堡垒边缘。这为一个森林模板提供了一个相当不错的开始,尽管它需要视觉效果(和人口):
蘑菇林的 themed
我们之前使用过分割主题(用于进入堡垒),所以我们将会打开 map/themes.rs
并添加另一个主题应该不足为奇! 在这种情况下,我们希望堡垒主题应用于地图东部的防御工事,而新的蘑菇林外观应用于其余部分。
我们可以更新 tile_glyph
看起来像这样:
#![allow(unused)] fn main() { pub fn tile_glyph(idx: usize, map : &Map) -> (rltk::FontCharType, RGB, RGB) { let (glyph, mut fg, mut bg) = match map.depth { 7 => { let x = idx as i32 % map.width; if x > map.width-16 { get_tile_glyph_default(idx, map) } else { get_mushroom_glyph(idx, map) } } 5 => { let x = idx as i32 % map.width; if x < map.width/2 { get_limestone_cavern_glyph(idx, map) } else { get_tile_glyph_default(idx, map) } } 4 => get_limestone_cavern_glyph(idx, map), 3 => get_limestone_cavern_glyph(idx, map), 2 => get_forest_glyph(idx, map), _ => get_tile_glyph_default(idx, map) }; ... }
get_mushroom_glyph
函数基本上与 get_forest_glyph
相同,但更改为更像游戏 Dwarf Fortress 中的蘑菇林外观(耶,胖矮帽!):
#![allow(unused)] fn main() { fn get_mushroom_glyph(idx:usize, map: &Map) -> (rltk::FontCharType, RGB, RGB) { let glyph; let fg; let bg = RGB::from_f32(0., 0., 0.); match map.tiles[idx] { TileType::Wall => { glyph = rltk::to_cp437('♠'); fg = RGB::from_f32(1.0, 0.0, 1.0); } TileType::Bridge => { glyph = rltk::to_cp437('.'); fg = RGB::named(rltk::GREEN); } TileType::Road => { glyph = rltk::to_cp437('≡'); fg = RGB::named(rltk::CHOCOLATE); } TileType::Grass => { glyph = rltk::to_cp437('"'); fg = RGB::named(rltk::GREEN); } TileType::ShallowWater => { glyph = rltk::to_cp437('~'); fg = RGB::named(rltk::CYAN); } TileType::DeepWater => { glyph = rltk::to_cp437('~'); fg = RGB::named(rltk::BLUE); } TileType::Gravel => { glyph = rltk::to_cp437(';'); fg = RGB::from_f32(0.5, 0.5, 0.5); } TileType::DownStairs => { glyph = rltk::to_cp437('>'); fg = RGB::from_f32(0., 1.0, 1.0); } TileType::UpStairs => { glyph = rltk::to_cp437('<'); fg = RGB::from_f32(0., 1.0, 1.0); } _ => { glyph = rltk::to_cp437('"'); fg = RGB::from_f32(0.0, 0.6, 0.0); } } (glyph, fg, bg) } }
这给出了一个略显迷幻但非常漂亮的世界观:
填充蘑菇林
我首先编辑了 spawns.json
,从这个关卡中移除了 dragon wyrmlings
;蜥蜴人和巨型蜥蜴可以留下,但我们现在要将重心从蜥蜴人身上移开!你期望在一个神秘的地下蘑菇森林中找到什么?由于它们在现实生活中不存在,这是一个有点开放式的问题!我想关注一些自然危害,一种新型怪物,以及更多战利品。玩家刚刚完成了一场重要的 Boss 战,所以最好稍微降低一下装备等级,给他们一些时间来恢复。
自然危害
让我们从添加一些危害开始。蘑菇经常释放孢子,孢子对玩家(以及任何其他触发孢子的人!)产生有趣的影响是一个常见的主题。实际上,这些是道具还是 NPC 是一个有趣的问题;它们像 NPC 一样对发现玩家做出反应,但实际上并不移动或做太多其他事情,只是触发效果 - 更像道具(但与道具不同,您不必站在它们上面才能生效)。
爆炸火帽蘑菇
让我们从添加一个爆炸蘑菇开始。在 spawns.json
中(在怪物部分):
{
"name": "Firecap Mushroom",
"renderable": {
"glyph": "♠",
"fg": "#FFAA50",
"bg": "#000000",
"order": 1
},
"blocks_tile": true,
"vision_range": 3,
"movement": "static",
"attributes": {},
"faction": "Fungi",
"level": 1,
"abilities": [
{ "spell": "Explode", "chance": 1.0, "range": 3.0, "min_range": 0.0 }
]
}
所以我们给了它一个漂亮的蘑菇字形,并使其呈橙色(这似乎很合适)。它具有较短的视觉范围,因为我从未将真菌想象成具有最好的视力(甚至眼睛,真的)。它属于一个尚未存在的派系 Fungi
,并具有一个尚未存在的 Explode
咒语能力!
让我们继续将其添加到派系表中:
{ "name": "Fungi", "responses": { "Default": "attack", "Fungi": "ignore" } }
我们还将开始定义 Explode
能力。在 spawns.json
的咒语部分:
{
"name": "Explode",
"mana_cost": 1,
"effects": {
"ranged": "3",
"damage": "20",
"area_of_effect": "3",
"particle": "▒;#FFAA50;400.0",
"single_activation": "1"
}
}
几乎所有这些都是我们已经构建到效果系统中的东西:它具有 3 的范围,效果区域和 single_activation
。我们以前只对陷阱道具使用过这个标签,但它传达了信息 - 蘑菇只能爆炸一次,并且会在这个过程中被摧毁。我们已经支持在 raws/rawmaster.rs
中附加标签 - 所以那里不需要做任何事情。 我们确实需要扩展效果系统以允许自毁序列运行。在 effects/triggers.rs
中,我们需要扩展 spell_trigger
以支持自毁:
#![allow(unused)] fn main() { pub fn spell_trigger(creator : Option<Entity>, spell: Entity, targets : &Targets, ecs: &mut World) { let mut self_destruct = false; if let Some(template) = ecs.read_storage::<SpellTemplate>().get(spell) { let mut pools = ecs.write_storage::<Pools>(); if let Some(caster) = creator { if let Some(pool) = pools.get_mut(caster) { if template.mana_cost <= pool.mana.current { pool.mana.current -= template.mana_cost; } } } if let Some(_destruct) = ecs.read_storage::<SingleActivation>().get(spell) { self_destruct = true; } } event_trigger(creator, spell, targets, ecs); if self_destruct && creator.is_some() { ecs.entities().delete(creator.unwrap()).expect("Unable to delete owner"); } } }
所以这几乎是之前的代码,但增加了一个额外的检查,以查看咒语是否删除了施法者 - 如果是,它会在爆炸发生后立即删除施法者。
我们还应该使它们在蘑菇关卡中生成。在 spawns.json
的生成表部分:
{ "name" : "Firecap Mushroom", "weight" : 10, "min_depth" : 7, "max_depth" : 9 },
如果您现在 cargo run
,蘑菇会在您靠近时引爆:
有一个小问题,蜥蜴人和真菌正在战斗,这没有多大意义。所以我们将更新他们的派系以防止这种情况发生:
{ "name" : "Wyrm", "responses": { "Default" : "attack", "Wyrm" : "ignore", "Fungi" : "ignore" }},
{ "name" : "Fungi", "responses": { "Default" : "attack", "Fungi" : "ignore", "Wyrm" : "ignore" }}
如果真菌在死亡时爆炸也会非常好;如果你有一些在一起,这可能会产生非常有趣的连锁反应(并且可以扩展到另一个关卡的爆炸桶!)。我们将向蘑菇添加一个注释:
{
"name" : "Firecap Mushroom",
"renderable": {
"glyph" : "♠",
"fg" : "#FFAA50",
"bg" : "#000000",
"order" : 1
},
"blocks_tile" : true,
"vision_range" : 3,
"movement" : "static",
"attributes" : {},
"faction" : "Fungi",
"level" : 1
"abilities" : [
{ "spell" : "Explode", "chance" : 1.0, "range" : 3.0, "min_range" : 0.0 }
],
"on_death" : [
{ "spell" : "Explode", "chance" : 1.0, "range" : 0.0, "min_range" : 0.0 }
]
}
所以,现在我们有一个 on_death
触发器需要实现。我们将从 raws/mob_structs.rs
开始,以支持此 JSON 标签。我们正在重复使用 spell 标签,即使范围没有意义 - 只是为了帮助保持一致性。所以我们只需要在原始结构中添加一行:
#![allow(unused)] fn main() { #[derive(Deserialize, Debug)] pub struct Mob { pub name : String, pub renderable : Option<Renderable>, pub blocks_tile : bool, pub vision_range : i32, pub movement : String, pub quips : Option<Vec<String>>, pub attributes : MobAttributes, pub skills : Option<HashMap<String, i32>>, pub level : Option<i32>, pub hp : Option<i32>, pub mana : Option<i32>, pub equipped : Option<Vec<String>>, pub natural : Option<MobNatural>, pub loot_table : Option<String>, pub light : Option<MobLight>, pub faction : Option<String>, pub gold : Option<String>, pub vendor : Option<Vec<String>>, pub abilities : Option<Vec<MobAbility>>, pub on_death : Option<Vec<MobAbility>> } }
我们还需要一个新的组件来存储 on_death
事件触发器。我们可以重复使用一些 SpecialAbilities
代码来保持简单。在 components.rs
中(并注册在 main.rs
和 saveload_system.rs
中):
#![allow(unused)] fn main() { #[derive(Component, Debug, Serialize, Deserialize, Clone)] pub struct OnDeath { pub abilities : Vec<SpecialAbility> } }
然后我们在 raws/rawmaster.rs
的 spawn_named_mob
中添加一些代码来实例化它。它就像特殊能力代码一样 - 所以把它放在旁边:
#![allow(unused)] fn main() { if let Some(ability_list) = &mob_template.on_death { let mut a = OnDeath{ abilities : Vec::new() }; for ability in ability_list.iter() { a.abilities.push( SpecialAbility{ chance : ability.chance, spell : ability.spell.clone(), range : ability.range, min_range : ability.min_range } ); } eb = eb.with(a); } }
最后,我们需要使 on_death
事件实际触发。如果您将此添加到 damage_system.rs
中 delete_the_dead
函数的末尾(在最终实体删除之前),您将获得一个很好的错峰效果,并在蘑菇被杀死之后立即爆炸:
#![allow(unused)] fn main() { // Fire death events // 触发死亡事件 use crate::effects::*; use crate::Map; use crate::components::{OnDeath, AreaOfEffect}; for victim in dead.iter() { let death_effects = ecs.read_storage::<OnDeath>(); if let Some(death_effect) = death_effects.get(*victim) { let mut rng = ecs.fetch_mut::<rltk::RandomNumberGenerator>(); for effect in death_effect.abilities.iter() { if rng.roll_dice(1,100) <= (effect.chance * 100.0) as i32 { let map = ecs.fetch::<Map>(); if let Some(pos) = ecs.read_storage::<Position>().get(*victim) { let spell_entity = crate::raws::find_spell_entity(ecs, &effect.spell).unwrap(); let tile_idx = map.xy_idx(pos.x, pos.y); let target = if let Some(aoe) = ecs.read_storage::<AreaOfEffect>().get(spell_entity) { Targets::Tiles { tiles : aoe_tiles(&map, rltk::Point::new(pos.x, pos.y), aoe.radius) } } else { Targets::Tile{ tile_idx : tile_idx as i32 } }; add_effect( None, EffectType::SpellUse{ spell: crate::raws::find_spell_entity( ecs, &effect.spell ).unwrap() }, target ); } } } } } }
还有一个明显的问题;当蘑菇爆炸时,爆炸中心位于玩家而不是蘑菇身上。让我们创建一个新组件来表示覆盖咒语目标始终以自身为目标。在 components.rs
中(并且,像往常一样,注册在 main.rs
和 saveload_system.rs
中):
#![allow(unused)] fn main() { #[derive(Component, Debug, Serialize, Deserialize, Clone)] pub struct AlwaysTargetsSelf {} }
我们将把它添加到 raws/rawmaster.rs
中的 effects
宏中:
#![allow(unused)] fn main() { "target_self" => $eb = $eb.with( AlwaysTargetsSelf{} ), }
我们应该将其应用于 spawns.json
中的 Explode
能力:
{
"name": "Explode",
"mana_cost": 1,
"effects": {
"ranged": "3",
"damage": "20",
"area_of_effect": "3",
"particle": "▒;#FFAA50;400.0",
"single_activation": "1",
"target_self": "1"
}
}
最后,我们需要修改 effects/triggers.rs
中的 spell_trigger
以能够修改目标选择:
#![allow(unused)] fn main() { pub fn spell_trigger(creator : Option<Entity>, spell: Entity, targets : &Targets, ecs: &mut World) { let mut targeting = targets.clone(); let mut self_destruct = false; if let Some(template) = ecs.read_storage::<SpellTemplate>().get(spell) { let mut pools = ecs.write_storage::<Pools>(); if let Some(caster) = creator { if let Some(pool) = pools.get_mut(caster) { if template.mana_cost <= pool.mana.current { pool.mana.current -= template.mana_cost; } } // Handle self-targeting override // 处理自目标覆盖 if ecs.read_storage::<AlwaysTargetsSelf>().get(spell).is_some() { if let Some(pos) = ecs.read_storage::<Position>().get(caster) { let map = ecs.fetch::<Map>(); targeting = if let Some(aoe) = ecs.read_storage::<AreaOfEffect>().get(spell) { Targets::Tiles { tiles : aoe_tiles(&map, rltk::Point::new(pos.x, pos.y), aoe.radius) } } else { Targets::Tile{ tile_idx : map.xy_idx(pos.x, pos.y) as i32 } } } } } if let Some(_destruct) = ecs.read_storage::<SingleActivation>().get(spell) { self_destruct = true; } } event_trigger(creator, spell, &targeting, ecs); if self_destruct && creator.is_some() { ecs.entities().delete(creator.unwrap()).expect("Unable to delete owner"); } } }
为了演示我们刚刚创建的怪物,我将蘑菇的生成密度提高到 300 - 并将爆炸半径更改为 6。开始了:
改回设置可能是一个好主意!现在真的很想制作一个连锁蘑菇关卡,以便在整个关卡中产生多米诺骨牌般的爆炸涟漪 - 但这可能更适合观看而不是玩!
混乱蘑菇
另一个明显的效果是孢子播撒混乱的蘑菇。我们拥有实现它们所需的一切!
在 spawns.json
的怪物部分,我们定义了基本蘑菇:
{
"name": "Sporecap Mushroom",
"renderable": {
"glyph": "♠",
"fg": "#00AAFF",
"bg": "#000000",
"order": 1
},
"blocks_tile": true,
"vision_range": 3,
"movement": "static",
"attributes": {},
"faction": "Fungi",
"level": 1,
"abilities": [
{ "spell": "ConfusionCloud", "chance": 1.0, "range": 3.0, "min_range": 0.0 }
],
"on_death": [
{ "spell": "ConfusionCloud", "chance": 1.0, "range": 0.0, "min_range": 0.0 }
]
}
在生成权重中,我们使它们在真菌林中很常见:
{ "name" : "Sporecap Mushroom", "weight" : 10, "min_depth" : 7, "max_depth" : 9 },
我们可以将咒语定义如下:
{
"name": "ConfusionCloud",
"mana_cost": 1,
"effects": {
"ranged": "3",
"confusion": "4",
"area_of_effect": "3",
"particle": "?;#FFFF00;400.0",
"single_activation": "1",
"target_self": "1"
}
}
无需其他代码!如果您现在 cargo run
,您将获得蓝色蘑菇爆炸,带来令人困惑的美好:
毒气蘑菇
我们将添加另一种蘑菇类型:一种传播有毒孢子的死帽蘑菇! 再次,我们拥有实现此目的所需的一切。我们将蘑菇定义为 spawns.json
中的怪物:
{
"name": "Deathcap Mushroom",
"renderable": {
"glyph": "♠",
"fg": "#55FF55",
"bg": "#000000",
"order": 1
},
"blocks_tile": true,
"vision_range": 3,
"movement": "static",
"attributes": {},
"faction": "Fungi",
"level": 1,
"abilities": [
{ "spell": "PoisonCloud", "chance": 1.0, "range": 3.0, "min_range": 0.0 }
],
"on_death": [
{ "spell": "PoisonCloud", "chance": 1.0, "range": 0.0, "min_range": 0.0 }
]
}
使其生成:
{ "name" : "Deathcap Mushroom", "weight" : 7, "min_depth" : 7, "max_depth" : 9 },
并定义咒语效果:
{
"name": "PoisonCloud",
"mana_cost": 1,
"effects": {
"ranged": "3",
"damage_over_time": "4",
"area_of_effect": "3",
"particle": "*;#00FF00;400.0",
"single_activation": "1",
"target_self": "1"
}
}
瞧 - 你有了有毒蘑菇孢子云。
真菌林怪物
我们不只是想让玩家浑身沾满孢子。还有一些蜥蜴人需要担心,但一些怪物也居住在树林中是有道理的。我想到了几个:真菌人,你可以与他们战斗 - 并吃掉他们的尸体,还有一种整天漫游啃食真菌(或玩家)的野兽。我们还可以引入“孢子僵尸” - 大脑被真菌控制的人,他们只寻求杀死真菌的敌人(有一些令人不安的寄生虫以类似的方式接管宿主,所以这并不像听起来那么不切实际!)。
真菌人
让我们从真菌人开始。在 spawns.json
中,我们可以将它们定义为常规的敌人类别:
{
"name": "Fungus Man",
"renderable": {
"glyph": "f",
"fg": "#FF0000",
"bg": "#000000",
"order": 1
},
"blocks_tile": true,
"vision_range": 8,
"movement": "random_waypoint",
"attributes": {},
"faction": "Fungi",
"gold": "2d8",
"level": 4,
"loot_table": "Animal"
}
我们还将使它们生成:
{ "name" : "Fungus Man", "weight" : 8, "min_depth" : 7, "max_depth" : 9 },
这增加了真菌人,他们掉落肉。你可能不想过多考虑味道。
孢子僵尸
同样,我们将从基本的怪物定义开始。我们不能让所有东西都做时髦的事情 - 那会压倒玩家:
{
"name": "Spore Zombie",
"renderable": {
"glyph": "z",
"fg": "#FF0000",
"bg": "#000000",
"order": 1
},
"blocks_tile": true,
"vision_range": 8,
"movement": "random_waypoint",
"attributes": {},
"faction": "Fungi",
"gold": "2d8",
"level": 5
}
我们还需要使它们生成:
{ "name" : "Spore Zombie", "weight" : 7, "min_depth" : 7, "max_depth" : 9 },
真菌野兽
我们将野兽模仿其他动物,但将它们放入 “Fungi” 派系:
{
"name": "Fungal Beast",
"renderable": {
"glyph": "F",
"fg": "#995555",
"bg": "#000000",
"order": 1
},
"blocks_tile": true,
"vision_range": 6,
"movement": "random",
"attributes": {},
"natural": {
"armor_class": 11,
"attacks": [{ "name": "bite", "hit_bonus": 0, "damage": "1d4" }]
},
"faction": "Fungi"
}
我们还需要使它们生成:
{ "name" : "Fungal Beast", "weight" : 9, "min_depth" : 7, "max_depth" : 9 },
如果您现在 cargo run
,您将拥有一个充满生机和会爆炸事物的关卡!
一些物品
作为对永久吸入毒气、被僵尸啃咬和被野兽嚼碎的奖励,现在是时候向树林引入一些新物品了! 让我们考虑玩家可能会遇到的一些新物品。
一个简单的提升是一把更好的长剑:
{
"name" : "Longsword +2",
"renderable": {
"glyph" : "/",
"fg" : "#FFAAFF",
"bg" : "#000000",
"order" : 2
},
"weapon" : {
"range" : "melee",
"attribute" : "might",
"base_damage" : "1d8+2",
"hit_bonus" : 2
},
"weight_lbs" : 1.0,
"base_value" : 100.0,
"initiative_penalty" : 0,
"vendor_category" : "weapon",
"magic" : { "class" : "common", "naming" : "Unidentified Longsword" }
},
当然,将其添加到生成列表:
{ "name" : "Longsword +2", "weight" : 1, "min_depth" : 7, "max_depth" : 100 },
另一个简单的物品是一件魔法胸甲:
{
"name" : "Breastplate +1",
"renderable": {
"glyph" : "[",
"fg" : "#00FF00",
"bg" : "#000000",
"order" : 2
},
"wearable" : {
"slot" : "Torso",
"armor_class" : 4.0
},
"weight_lbs" : 20.0,
"base_value" : 200.0,
"initiative_penalty" : 1.0,
"vendor_category" : "armor",
"magic" : { "class" : "common", "naming" : "Unidentified Breastplate" }
},
同样,它也需要可生成:
{ "name" : "Breastplate +1", "weight" : 1, "min_depth" : 7, "max_depth" : 100 },
同样,很容易采用基本的塔盾并提供改进的版本:
{
"name" : "Tower Shield +1",
"renderable": {
"glyph" : "[",
"fg" : "#00FFFF",
"bg" : "#000000",
"order" : 2
},
"wearable" : {
"slot" : "Shield",
"armor_class" : 3.0
},
"weight_lbs" : 45.0,
"base_value" : 30.0,
"initiative_penalty" : 0.0,
"vendor_category" : "armor"
},
当然,它也需要一些生成数据:
{ "name" : "Tower Shield +1", "weight" : 1, "min_depth" : 7, "max_depth" : 100 },
我们还应该考虑填充一些未使用的装备槽位。我们有很多以躯干为中心的物品,而很少有物品可以填充其他槽位。 为了完整性,我们应该添加一些!
头部物品
目前,我们只有一个头部物品:锁子头盔。为我们目前使用的主要盔甲类别中的每一个都配备一个头部物品是有道理的:布甲,皮甲,锁甲(我们有这个!),板甲。
物品定义是:
{
"name" : "Cloth Cap",
"renderable": {
"glyph" : "[",
"fg" : "#00FF00",
"bg" : "#000000",
"order" : 2
},
"wearable" : {
"slot" : "Head",
"armor_class" : 0.2
},
"weight_lbs" : 0.25,
"base_value" : 5.0,
"initiative_penalty" : 0.1,
"vendor_category" : "armor"
},
{
"name" : "Leather Cap",
"renderable": {
"glyph" : "[",
"fg" : "#00FF00",
"bg" : "#000000",
"order" : 2
},
"wearable" : {
"slot" : "Head",
"armor_class" : 0.4
},
"weight_lbs" : 0.5,
"base_value" : 10.0,
"initiative_penalty" : 0.2,
"vendor_category" : "armor"
},
{
"name" : "Chain Coif",
"renderable": {
"glyph" : "[",
"fg" : "#00FF00",
"bg" : "#000000",
"order" : 2
},
"wearable" : {
"slot" : "Head",
"armor_class" : 1.0
},
"weight_lbs" : 5.0,
"base_value" : 20.0,
"initiative_penalty" : 0.5,
"vendor_category" : "armor"
},
{
"name" : "Steel Helm",
"renderable": {
"glyph" : "[",
"fg" : "#00FF00",
"bg" : "#000000",
"order" : 2
},
"wearable" : {
"slot" : "Head",
"armor_class" : 2.0
},
"weight_lbs" : 15.0,
"base_value" : 100.0,
"initiative_penalty" : 1.0,
"vendor_category" : "armor"
},
这是它们更新后的生成信息:
{ "name" : "Cloth Cap", "weight" : 5, "min_depth" : 4, "max_depth" : 100 },
{ "name" : "Leather Cap", "weight" : 4, "min_depth" : 4, "max_depth" : 100 },
{ "name" : "Chain Coif", "weight" : 3, "min_depth" : 4, "max_depth" : 100 },
{ "name" : "Steel Helm", "weight" : 2, "min_depth" : 4, "max_depth" : 100 },
腿部物品
我们现在也有一些腿部物品,但不多:我们有破旧的裤子和布裤。 让我们也将它们扩展到包括皮革、锁甲和钢铁。
{
"name" : "Leather Pants",
"renderable": {
"glyph" : "[",
"fg" : "#00FFFF",
"bg" : "#000000",
"order" : 2
},
"wearable" : {
"slot" : "Legs",
"armor_class" : 0.2
},
"weight_lbs" : 5.0,
"base_value" : 25.0,
"initiative_penalty" : 0.2,
"vendor_category" : "clothes"
},
{
"name" : "Chain Leggings",
"renderable": {
"glyph" : "[",
"fg" : "#00FFFF",
"bg" : "#000000",
"order" : 2
},
"wearable" : {
"slot" : "Legs",
"armor_class" : 0.3
},
"weight_lbs" : 10.0,
"base_value" : 50.0,
"initiative_penalty" : 0.3,
"vendor_category" : "clothes"
},
{
"name" : "Steel Greaves",
"renderable": {
"glyph" : "[",
"fg" : "#00FFFF",
"bg" : "#000000",
"order" : 2
},
"wearable" : {
"slot" : "Legs",
"armor_class" : 0.5
},
"weight_lbs" : 20.0,
"base_value" : 100.0,
"initiative_penalty" : 0.5,
"vendor_category" : "clothes"
},
同样,我们需要给它们生成数据:
{ "name" : "Cloth Pants", "weight" : 6, "min_depth" : 1, "max_depth" : 100 },
{ "name" : "Leather Pants", "weight" : 5, "min_depth" : 1, "max_depth" : 100 },
{ "name" : "Chain Leggings", "weight" : 4, "min_depth" : 1, "max_depth" : 100 },
{ "name" : "Steel Greaves", "weight" : 3, "min_depth" : 5, "max_depth" : 100 },
足部物品
同样,我们关于足部盔甲的故事非常有限。 我们有旧靴子、拖鞋和皮靴。 我们也应该为这些添加锁甲和板甲选项:
{
"name" : "Leather Boots",
"renderable": {
"glyph" : "[",
"fg" : "#00FF00",
"bg" : "#000000",
"order" : 2
},
"wearable" : {
"slot" : "Feet",
"armor_class" : 0.2
},
"weight_lbs" : 2.0,
"base_value" : 5.0,
"initiative_penalty" : 0.25,
"vendor_category" : "clothes"
},
{
"name" : "Chain Boots",
"renderable": {
"glyph" : "[",
"fg" : "#00FF00",
"bg" : "#000000",
"order" : 2
},
"wearable" : {
"slot" : "Feet",
"armor_class" : 0.3
},
"weight_lbs" : 3.0,
"base_value" : 10.0,
"initiative_penalty" : 0.25,
"vendor_category" : "armor"
},
{
"name" : "Steel Boots",
"renderable": {
"glyph" : "[",
"fg" : "#00FF00",
"bg" : "#000000",
"order" : 2
},
"wearable" : {
"slot" : "Feet",
"armor_class" : 0.5
},
"weight_lbs" : 5.0,
"base_value" : 10.0,
"initiative_penalty" : 0.4,
"vendor_category" : "armor"
},
以及一些生成信息:
{ "name" : "Leather Boots", "weight" : 5, "min_depth" : 1, "max_depth" : 100 },
{ "name" : "Chain Boots", "weight" : 4, "min_depth" : 3, "max_depth" : 100 },
{ "name" : "Steel Boots", "weight" : 2, "min_depth" : 5, "max_depth" : 100 },
手部物品
我们现在的手部盔甲故事真的很糟糕:我们只有食人魔力量手套,仅此而已! 让我们添加一些“普通”手套来完善一下:
{
"name" : "Cloth Gloves",
"renderable": {
"glyph" : "[",
"fg" : "#FF9999",
"bg" : "#000000",
"order" : 2
},
"wearable" : {
"slot" : "Hands",
"armor_class" : 0.1
},
"weight_lbs" : 0.5,
"base_value" : 1.0,
"initiative_penalty" : 0.1,
"vendor_category" : "clothes"
},
{
"name" : "Leather Gloves",
"renderable": {
"glyph" : "[",
"fg" : "#FF9999",
"bg" : "#000000",
"order" : 2
},
"wearable" : {
"slot" : "Hands",
"armor_class" : 0.2
},
"weight_lbs" : 1.0,
"base_value" : 1.0,
"initiative_penalty" : 0.1,
"vendor_category" : "clothes"
},
{
"name" : "Chain Gloves",
"renderable": {
"glyph" : "[",
"fg" : "#FF9999",
"bg" : "#000000",
"order" : 2
},
"wearable" : {
"slot" : "Hands",
"armor_class" : 0.3
},
"weight_lbs" : 2.0,
"base_value" : 10.0,
"initiative_penalty" : 0.2,
"vendor_category" : "clothes"
},
{
"name" : "Steel Gloves",
"renderable": {
"glyph" : "[",
"fg" : "#FF9999",
"bg" : "#000000",
"order" : 2
},
"wearable" : {
"slot" : "Hands",
"armor_class" : 0.5
},
"weight_lbs" : 5.0,
"base_value" : 10.0,
"initiative_penalty" : 0.3,
"vendor_category" : "clothes"
},
当然,还有一些生成数据:
{ "name" : "Cloth Gloves", "weight" : 6, "min_depth" : 1, "max_depth" : 100 },
{ "name" : "Leather Gloves", "weight" : 5, "min_depth" : 1, "max_depth" : 100 },
{ "name" : "Chain Gloves", "weight" : 3, "min_depth" : 1, "max_depth" : 100 },
{ "name" : "Steel Gloves", "weight" : 2, "min_depth" : 5, "max_depth" : 100 },
总结
这就是我们所拥有的 - 一个可用的从堡垒到蘑菇林的过渡关卡,以及一个充实的物品表。 在下一章中,我们将继续在实现设计文档方面取得进展,完成蘑菇森林的剩余部分,并在改进物品故事方面做更多工作。
...
本章的源代码可以在这里找到
使用 web assembly 在您的浏览器中运行本章示例(需要 WebGL2)
版权所有 (C) 2019, Herbert Wolverson。