Owned sprites

This commit is contained in:
Isse
2024-11-06 23:02:34 +01:00
parent 7b68c784af
commit 4123ea657a
16 changed files with 293 additions and 144 deletions

View File

@ -46,6 +46,7 @@ hud-deactivate = Deactivate
hud-collect = Collect
hud-pick_up = Pick up
hud-open = Open
hud-steal = Steal
hud-use = Use
hud-read = Read
hud-unlock-requires = Open with { $item }

View File

@ -593,6 +593,12 @@ impl Block {
}
}
#[inline]
pub fn is_owned(&self) -> bool {
self.get_attr::<sprite::Owned>()
.is_ok_and(|sprite::Owned(b)| b)
}
/// The tool required to mine this block. For blocks that cannot be mined,
/// `None` is returned.
#[inline]
@ -657,6 +663,16 @@ impl Block {
#[inline]
pub fn kind(&self) -> BlockKind { self.kind }
/// If possible, copy the sprite/color data of the other block.
#[inline]
#[must_use]
pub fn with_data_of(mut self, other: Block) -> Self {
if self.is_filled() == other.is_filled() {
self = self.with_data(other.data());
}
self
}
/// If this block is a fluid, replace its sprite.
#[inline]
#[must_use]

View File

@ -114,23 +114,6 @@ sprites! {
Loom = 0x27,
DismantlingBench = 0x28,
RepairBench = 0x29,
// Containers
Chest = 0x30,
DungeonChest0 = 0x31,
DungeonChest1 = 0x32,
DungeonChest2 = 0x33,
DungeonChest3 = 0x34,
DungeonChest4 = 0x35,
DungeonChest5 = 0x36,
CoralChest = 0x37,
HaniwaUrn = 0x38,
TerracottaChest = 0x39,
SahaginChest = 0x3A,
CommonLockedChest = 0x3B,
ChestBuried = 0x3C,
Crate = 0x3D,
Barrel = 0x3E,
CrateBlock = 0x3F,
// Wall
HangingBasket = 0x50,
HangingSign = 0x51,
@ -155,7 +138,7 @@ sprites! {
Hearth = 0x72,
},
// Sprites representing plants that may grow over time (this does not include plant parts, like fruit).
Plant = 3 has Growth {
Plant = 3 has Growth, Owned {
// Cacti
BarrelCactus = 0x00,
RoundCactus = 0x01,
@ -251,7 +234,7 @@ sprites! {
},
// Solid resources
// TODO: Remove small variants, make deposit size be an attribute
Resources = 4 {
Resources = 4 has Owned {
// Gems and ores
// Woods and twigs
Twigs = 0x00,
@ -394,6 +377,24 @@ sprites! {
FireBowlGround = 4,
MesaLantern = 5,
},
Container = 9 has Ori, Owned {
Chest = 0x00,
DungeonChest0 = 0x01,
DungeonChest1 = 0x02,
DungeonChest2 = 0x03,
DungeonChest3 = 0x04,
DungeonChest4 = 0x05,
DungeonChest5 = 0x06,
CoralChest = 0x07,
HaniwaUrn = 0x08,
TerracottaChest = 0x09,
SahaginChest = 0x0A,
CommonLockedChest = 0x0B,
ChestBuried = 0x0C,
Crate = 0x0D,
Barrel = 0x0E,
CrateBlock = 0x0F,
},
}
attributes! {
@ -401,6 +402,7 @@ attributes! {
Growth { bits: 4, err: Infallible, from: |bits| Ok(Self(bits as u8)), into: |Growth(x)| x as u16 },
LightEnabled { bits: 1, err: Infallible, from: |bits| Ok(Self(bits == 1)), into: |LightEnabled(x)| x as u16 },
Damage { bits: 3, err: Infallible, from: |bits| Ok(Self(bits as u8)), into: |Damage(x)| x as u16 },
Owned { bits: 1, err: Infallible, from: |bits| Ok(Self(bits == 1)), into: |Owned(x)| x as u16 },
}
// The orientation of the sprite, 0..16
@ -419,6 +421,9 @@ impl Default for Growth {
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub struct LightEnabled(pub bool);
#[derive(Default, Copy, Clone, Debug, PartialEq, Eq)]
pub struct Owned(pub bool);
impl Default for LightEnabled {
fn default() -> Self { Self(true) }
}

View File

@ -2046,6 +2046,7 @@ impl Hud {
vec![(
Some(GameInput::Interact),
i18n.get_msg("hud-pick_up").to_string(),
overitem::TEXT_COLOR,
)],
)
.set(overitem_id, ui_widgets);
@ -2077,17 +2078,19 @@ impl Hud {
let pos = mat.mul_point(Vec3::broadcast(0.5));
let over_pos = pos + Vec3::unit_z() * 0.7;
let interaction_text = |collect_default| match interaction {
let interaction_text = |collect_default, color| match interaction {
BlockInteraction::Collect => {
vec![(
Some(GameInput::Interact),
i18n.get_msg(collect_default).to_string(),
color,
)]
},
BlockInteraction::Craft(_) => {
vec![(
Some(GameInput::Interact),
i18n.get_msg("hud-use").to_string(),
color,
)]
},
BlockInteraction::Unlock(kind) => {
@ -2103,19 +2106,23 @@ impl Hud {
.unwrap_or_else(|| "modular item".to_string())
};
vec![(Some(GameInput::Interact), match kind {
UnlockKind::Free => i18n.get_msg("hud-open").to_string(),
UnlockKind::Requires(item_id) => i18n
.get_msg_ctx("hud-unlock-requires", &i18n::fluent_args! {
"item" => item_name(item_id),
})
.to_string(),
UnlockKind::Consumes(item_id) => i18n
.get_msg_ctx("hud-unlock-requires", &i18n::fluent_args! {
"item" => item_name(item_id),
})
.to_string(),
})]
vec![(
Some(GameInput::Interact),
match kind {
UnlockKind::Free => i18n.get_msg("hud-open").to_string(),
UnlockKind::Requires(item_id) => i18n
.get_msg_ctx("hud-unlock-requires", &i18n::fluent_args! {
"item" => item_name(item_id),
})
.to_string(),
UnlockKind::Consumes(item_id) => i18n
.get_msg_ctx("hud-unlock-requires", &i18n::fluent_args! {
"item" => item_name(item_id),
})
.to_string(),
},
color,
)]
},
BlockInteraction::Mine(mine_tool) => {
match (mine_tool, &info.active_mine_tool) {
@ -2123,24 +2130,35 @@ impl Hud {
vec![(
Some(GameInput::Primary),
i18n.get_msg("hud-mine").to_string(),
color,
)]
},
(ToolKind::Pick, _) => {
vec![(None, i18n.get_msg("hud-mine-needs_pickaxe").to_string())]
vec![(
None,
i18n.get_msg("hud-mine-needs_pickaxe").to_string(),
color,
)]
},
(ToolKind::Shovel, Some(ToolKind::Shovel)) => {
vec![(
Some(GameInput::Primary),
i18n.get_msg("hud-dig").to_string(),
color,
)]
},
(ToolKind::Shovel, _) => {
vec![(None, i18n.get_msg("hud-mine-needs_shovel").to_string())]
vec![(
None,
i18n.get_msg("hud-mine-needs_shovel").to_string(),
color,
)]
},
_ => {
vec![(
None,
i18n.get_msg("hud-mine-needs_unhandled_case").to_string(),
color,
)]
},
}
@ -2156,11 +2174,12 @@ impl Hud {
) => "hud-lay",
_ => "hud-sit",
};
vec![(Some(GameInput::Mount), i18n.get_msg(key).to_string())]
vec![(Some(GameInput::Mount), i18n.get_msg(key).to_string(), color)]
},
BlockInteraction::Read(_) => vec![(
Some(GameInput::Interact),
i18n.get_msg("hud-read").to_string(),
color,
)],
// TODO: change to turn on/turn off?
BlockInteraction::LightToggle(enable) => vec![(
@ -2171,6 +2190,7 @@ impl Hud {
"hud-deactivate"
})
.to_string(),
color,
)],
};
@ -2180,6 +2200,12 @@ impl Hud {
.filter(|s| s.is_container())
.and_then(|s| get_sprite_desc(s, i18n))
{
let (text, color) = if block.is_owned() {
("hud-steal", overitem::NEGATIVE_TEXT_COLOR)
} else {
("hud-open", overitem::TEXT_COLOR)
};
overitem::Overitem::new(
desc,
overitem::TEXT_COLOR,
@ -2190,7 +2216,7 @@ impl Hud {
overitem_properties,
self.pulse,
&global_state.window.key_layout,
interaction_text("hud-open"),
interaction_text(text, color),
)
.x_y(0.0, 100.0)
.position_ingame(over_pos)
@ -2204,6 +2230,11 @@ impl Hud {
.flatten()
.next()
{
let (text, color) = if block.is_owned() {
("hud-steal", overitem::NEGATIVE_TEXT_COLOR)
} else {
("hud-collect", overitem::TEXT_COLOR)
};
item.set_amount(amount.clamp(1, item.max_amount()))
.expect("amount >= 1 and <= max_amount is always a valid amount");
make_overitem(
@ -2212,11 +2243,16 @@ impl Hud {
pos.distance_squared(player_pos),
overitem_properties,
&self.fonts,
interaction_text("hud-collect"),
interaction_text(text, color),
)
.set(overitem_id, ui_widgets);
} else if let Some(desc) = block.get_sprite().and_then(|s| get_sprite_desc(s, i18n))
{
let (text, color) = if block.is_owned() {
("hud-steal", overitem::NEGATIVE_TEXT_COLOR)
} else {
("hud-collect", overitem::TEXT_COLOR)
};
overitem::Overitem::new(
desc,
overitem::TEXT_COLOR,
@ -2227,7 +2263,7 @@ impl Hud {
overitem_properties,
self.pulse,
&global_state.window.key_layout,
interaction_text("hud-collect"),
interaction_text(text, color),
)
.x_y(0.0, 100.0)
.position_ingame(over_pos)
@ -2285,6 +2321,7 @@ impl Hud {
"hud-use"
})
.to_string(),
overitem::TEXT_COLOR,
)],
)
.x_y(0.0, 100.0)

View File

@ -15,6 +15,7 @@ use crate::hud::{CollectFailedData, HudCollectFailedReason, HudLootOwner};
use keyboard_keynames::key_layout::KeyLayout;
pub const TEXT_COLOR: Color = Color::Rgba(0.61, 0.61, 0.89, 1.0);
pub const NEGATIVE_TEXT_COLOR: Color = Color::Rgba(0.91, 0.15, 0.17, 1.0);
pub const PICKUP_FAILED_FADE_OUT_TIME: f32 = 1.5;
widget_ids! {
@ -24,7 +25,7 @@ widget_ids! {
name,
// Interaction hints
btn_bg,
btn,
btns[],
// Inventory full
inv_full_bg,
inv_full,
@ -47,7 +48,7 @@ pub struct Overitem<'a> {
pulse: f32,
key_layout: &'a Option<KeyLayout>,
// GameInput optional so we can just show stuff like "needs pickaxe"
interaction_options: Vec<(Option<GameInput>, String)>,
interaction_options: Vec<(Option<GameInput>, String, Color)>,
}
impl<'a> Overitem<'a> {
@ -61,7 +62,7 @@ impl<'a> Overitem<'a> {
properties: OveritemProperties,
pulse: f32,
key_layout: &'a Option<KeyLayout>,
interaction_options: Vec<(Option<GameInput>, String)>,
interaction_options: Vec<(Option<GameInput>, String, Color)>,
) -> Self {
Self {
name,
@ -174,42 +175,56 @@ impl<'a> Widget for Overitem<'a> {
// Interaction hints
if !self.interaction_options.is_empty() && self.properties.active {
let text = self
let texts = self
.interaction_options
.iter()
.filter_map(|(input, action)| {
.filter_map(|(input, action, color)| {
let binding = if let Some(input) = input {
Some(self.controls.get_binding(*input)?)
} else {
None
};
Some((binding, action))
Some((binding, action, color))
})
.map(|(input, action)| {
.map(|(input, action, color)| {
if let Some(input) = input {
let input = input.display_string(self.key_layout);
format!("{} {action}", input.as_str())
(format!("{} {action}", input.as_str()), color)
} else {
action.to_string()
(action.to_string(), color)
}
})
.collect::<Vec<_>>()
.join("\n");
.collect::<Vec<_>>();
if state.ids.btns.len() < texts.len() {
state.update(|state| {
state
.ids
.btns
.resize(texts.len(), &mut ui.widget_id_generator());
})
}
let hints_text = Text::new(&text)
.font_id(self.fonts.cyri.conrod_id)
.font_size(btn_font_size as u32)
.color(TEXT_COLOR)
.x_y(0.0, btn_text_pos_y)
.depth(self.distance_from_player_sqr + 1.0)
.parent(id);
let mut max_w = btn_rect_size;
let mut max_h = 0.0;
let [w, h] = hints_text.get_wh(ui).unwrap_or([btn_rect_size; 2]);
for (idx, (text, color)) in texts.iter().enumerate() {
let hints_text = Text::new(text)
.font_id(self.fonts.cyri.conrod_id)
.font_size(btn_font_size as u32)
.color(**color)
.x_y(0.0, btn_text_pos_y + max_h)
.depth(self.distance_from_player_sqr + 1.0)
.parent(id);
let [w, h] = hints_text.get_wh(ui).unwrap_or([btn_rect_size; 2]);
max_w = max_w.max(w);
max_h += h;
hints_text.set(state.ids.btns[idx], ui);
}
hints_text.set(state.ids.btn, ui);
max_h = max_h.max(btn_rect_size);
RoundedRectangle::fill_with(
[w + btn_radius * 2.0, h + btn_radius * 2.0],
[max_w + btn_radius * 2.0, max_h + btn_radius * 2.0],
btn_radius,
btn_color,
)

View File

@ -233,10 +233,10 @@ impl Primitive {
#[derive(Clone)]
pub enum Fill {
Sprite(SpriteKind),
RotatedSprite(SpriteKind, u8),
RotatedSpriteWithCfg(SpriteKind, u8, SpriteCfg),
ResourceSprite(SpriteKind, u8),
Sprite(Block),
ResourceSprite(Block),
CfgSprite(Block, SpriteCfg),
Block(Block),
Brick(BlockKind, Rgb<u8>, u8),
Gradient(util::gradient::Gradient, BlockKind),
@ -248,6 +248,44 @@ pub enum Fill {
}
impl Fill {
pub fn sprite(kind: SpriteKind) -> Self { Fill::Block(Block::empty().with_sprite(kind)) }
pub fn sprite_ori(kind: SpriteKind, ori: u8) -> Self {
let block = Block::empty().with_sprite(kind);
let block = block.with_ori(ori).unwrap_or(block);
Fill::Sprite(block)
}
pub fn resource_sprite(kind: SpriteKind) -> Self {
Fill::ResourceSprite(Block::empty().with_sprite(kind))
}
pub fn resource_sprite_ori(kind: SpriteKind, ori: u8) -> Self {
let block = Block::empty().with_sprite(kind);
let block = block.with_ori(ori).unwrap_or(block);
Fill::ResourceSprite(block)
}
pub fn owned_resource_sprite_ori(kind: SpriteKind, ori: u8) -> Self {
let block = Block::empty().with_sprite(kind);
let block = block.with_ori(ori).unwrap_or(block);
let block = block
.with_attr(common::terrain::sprite::Owned(true))
.unwrap_or(block);
Fill::ResourceSprite(block)
}
pub fn sprite_ori_cfg(kind: SpriteKind, ori: u8, cfg: SpriteCfg) -> Self {
let block = Block::empty().with_sprite(kind);
let block = block.with_ori(ori).unwrap_or(block);
Fill::CfgSprite(block, cfg)
}
fn contains_at(
tree: &Store<Primitive>,
prim: Id<Primitive>,
@ -480,37 +518,22 @@ impl Fill {
) -> Option<Block> {
if Self::contains_at(tree, prim, pos, col) {
match self {
Fill::Block(block) => Some(*block),
Fill::Sprite(sprite) => Some(if old_block.is_filled() {
Block::air(*sprite)
} else {
old_block.with_sprite(*sprite)
}),
Fill::RotatedSprite(sprite, ori) | Fill::ResourceSprite(sprite, ori) => {
Fill::Sprite(sprite) | Fill::ResourceSprite(sprite) => {
Some(if old_block.is_filled() {
Block::air(*sprite)
.with_ori(*ori)
.unwrap_or_else(|| Block::air(*sprite))
*sprite
} else {
old_block
.with_sprite(*sprite)
.with_ori(*ori)
.unwrap_or_else(|| old_block.with_sprite(*sprite))
old_block.with_data_of(*sprite)
})
},
Fill::RotatedSpriteWithCfg(sprite, ori, cfg) => Some({
Fill::CfgSprite(sprite, cfg) => {
*sprite_cfg = Some(cfg.clone());
if old_block.is_filled() {
Block::air(*sprite)
.with_ori(*ori)
.unwrap_or_else(|| Block::air(*sprite))
Some(if old_block.is_filled() {
*sprite
} else {
old_block
.with_sprite(*sprite)
.with_ori(*ori)
.unwrap_or_else(|| old_block.with_sprite(*sprite))
}
}),
old_block.with_data_of(*sprite)
})
},
Fill::Block(block) => Some(*block),
Fill::Brick(bk, col, range) => Some(Block::new(
*bk,
*col + (RandomField::new(13)
@ -1080,7 +1103,7 @@ impl Painter {
min: pos,
max: pos + 1,
})
.fill(Fill::Sprite(sprite))
.fill(Fill::sprite(sprite))
}
/// Places a sprite at the provided location with the provided orientation.
@ -1089,7 +1112,7 @@ impl Painter {
min: pos,
max: pos + 1,
})
.fill(Fill::RotatedSprite(sprite, ori))
.fill(Fill::sprite_ori(sprite, ori))
}
/// Places a sprite at the provided location with the provided orientation
@ -1105,7 +1128,7 @@ impl Painter {
min: pos,
max: pos + 1,
})
.fill(Fill::RotatedSpriteWithCfg(sprite, ori, cfg))
.fill(Fill::sprite_ori_cfg(sprite, ori, cfg))
}
/// Places a sprite at the provided location with the provided orientation
@ -1116,7 +1139,18 @@ impl Painter {
min: pos,
max: pos + 1,
})
.fill(Fill::ResourceSprite(sprite, ori))
.fill(Fill::resource_sprite_ori(sprite, ori))
}
/// Places a sprite at the provided location with the provided orientation
/// which will be tracked by rtsim nature if the sprite has an associated
/// [`ChunkResource`].
pub fn owned_resource_sprite(&self, pos: Vec3<i32>, sprite: SpriteKind, ori: u8) {
self.aabb(Aabb {
min: pos,
max: pos + 1,
})
.fill(Fill::owned_resource_sprite_ori(sprite, ori))
}
/// Returns a `PrimitiveRef` of the largest pyramid with a slope of 1 that

View File

@ -671,7 +671,7 @@ fn render_tower(bridge: &Bridge, painter: &Painter, roof_kind: &RoofKind) {
1,
4,
)
.fill(Fill::Sprite(SpriteKind::FireBowlGround));
.fill(Fill::sprite(SpriteKind::FireBowlGround));
},
RoofKind::Hipped => {
painter
@ -829,7 +829,7 @@ fn render_hang(bridge: &Bridge, painter: &Painter) {
edges
.translate(Vec3::unit_z())
.fill(Fill::Sprite(SpriteKind::Rope));
.fill(Fill::sprite(SpriteKind::Rope));
edges.translate(Vec3::unit_z() * 2).fill(wood);

View File

@ -220,7 +220,7 @@ impl Structure for CliffTower {
max: (sprite_pos + 1).with_z(floor_level + 2),
})
.clear();
painter.sprite(
painter.owned_resource_sprite(
sprite_pos.with_z(floor_level + 1),
match (RandomField::new(0).get(sprite_pos.with_z(floor_level + 1)))
% 8
@ -231,6 +231,7 @@ impl Structure for CliffTower {
3 => SpriteKind::Pot,
_ => SpriteKind::MesaLantern,
},
0,
);
}
// planters
@ -504,7 +505,7 @@ impl Structure for CliffTower {
// distribute small sprites
for dir in LOCALITY {
let pos = plot_center + dir * ((length / 3) - 1);
painter.sprite(
painter.owned_resource_sprite(
pos.with_z(floor_level + 1),
match (RandomField::new(0).get(pos.with_z(floor_level)))
% 9
@ -519,6 +520,7 @@ impl Structure for CliffTower {
7 => SpriteKind::Bowl,
_ => SpriteKind::MesaLantern,
},
0,
);
}
// beds & wardrobes
@ -605,7 +607,7 @@ impl Structure for CliffTower {
SpriteKind::WallTableMesa,
(4 * d) as u8,
);
painter.rotated_sprite(
painter.owned_resource_sprite(
pos.with_z(floor_level + 4),
match (RandomField::new(0).get(pos.with_z(floor_level)))
% 3
@ -620,7 +622,7 @@ impl Structure for CliffTower {
// distribute small sprites
for dir in LOCALITY {
let pos = plot_center + dir * ((length / 3) + 1);
painter.sprite(
painter.owned_resource_sprite(
pos.with_z(floor_level + 1),
match (RandomField::new(0).get(pos.with_z(floor_level)))
% 12
@ -638,6 +640,7 @@ impl Structure for CliffTower {
10 => SpriteKind::MesaLantern,
_ => SpriteKind::FountainArabic,
},
0,
);
}
},
@ -666,7 +669,7 @@ impl Structure for CliffTower {
SpriteKind::WallTableMesa,
(4 * d) as u8,
);
painter.rotated_sprite(
painter.owned_resource_sprite(
pos.with_z(floor_level + 3),
match (RandomField::new(0).get(pos.with_z(floor_level)))
% 4
@ -682,7 +685,7 @@ impl Structure for CliffTower {
// distribute small sprites
for dir in LOCALITY {
let pos = plot_center + dir * ((length / 3) + 1);
painter.sprite(
painter.owned_resource_sprite(
pos.with_z(floor_level + 1),
match (RandomField::new(0).get(pos.with_z(floor_level)))
% 11
@ -700,6 +703,7 @@ impl Structure for CliffTower {
10 => SpriteKind::JugAndBowlArabic,
_ => SpriteKind::OvenArabic,
},
0,
);
}
},

View File

@ -364,7 +364,11 @@ impl Structure for CoastalHouse {
let sprite = sprites.swap_remove(
RandomField::new(0).get(position.with_z(base)) as usize % sprites.len(),
);
painter.sprite(position.with_z(base - 2 + (s * height)), sprite);
painter.owned_resource_sprite(
position.with_z(base - 2 + (s * height)),
sprite,
0,
);
}
}

View File

@ -489,7 +489,11 @@ impl Structure for DesertCityMultiPlot {
0 => {
for dir in NEIGHBORS {
let pos = center + dir * 4;
painter.sprite(pos.with_z(floor_level), SpriteKind::Crate);
painter.owned_resource_sprite(
pos.with_z(floor_level),
SpriteKind::Crate,
0,
);
}
for dir in NEIGHBORS {
let pos = center + dir * 8;
@ -1222,7 +1226,7 @@ impl Structure for DesertCityMultiPlot {
SpriteKind::WallTableArabic,
6 - (4 * d) as u8,
);
painter.rotated_sprite(
painter.owned_resource_sprite(
c_pos.with_z(floor_level + 1),
match (RandomField::new(0)
.get(c_pos.with_z(floor_level)))
@ -1317,7 +1321,7 @@ impl Structure for DesertCityMultiPlot {
SpriteKind::WallTableArabic,
6 - (4 * d) as u8,
);
painter.rotated_sprite(
painter.owned_resource_sprite(
a_pos.with_z(floor_level + 1),
match (RandomField::new(0)
.get(a_pos.with_z(floor_level)))

View File

@ -1,6 +1,6 @@
use super::*;
use crate::{ColumnSample, Land};
use common::terrain::{Block, BlockKind, SpriteKind};
use common::terrain::{sprite::Owned, Block, BlockKind, SpriteKind};
use rand::prelude::*;
use strum::{EnumIter, IntoEnumIterator};
use vek::*;
@ -253,7 +253,12 @@ impl Structure for FarmField {
.sprites()
.choose_weighted(rng, |(w, _)| *w)
.ok()
.and_then(|&(_, s)| Some(old.into_vacant().with_sprite(s?)))
.and_then(|&(_, s)| {
let new = old.into_vacant().with_sprite(s?);
let new = new.with_attr(Owned(true)).unwrap_or(new);
Some(new)
})
} else if z_off == 1 && rng.gen_bool(0.001) {
Some(old.into_vacant().with_sprite(SpriteKind::Scarecrow))
} else {

View File

@ -9,6 +9,7 @@ use common::{
terrain::{Block, BlockKind, SpriteKind},
};
use rand::prelude::*;
use strum::IntoEnumIterator;
use vek::*;
/// Represents house data generated by the `generate()` method
@ -1564,14 +1565,19 @@ impl Structure for House {
// drawer next to bed
painter.sprite(nightstand_pos.with_z(base), SpriteKind::DrawerSmall);
// collectible on top of drawer
let rng = RandomField::new(0).get(nightstand_pos.with_z(base + 1));
painter.sprite(nightstand_pos.with_z(base + 1), match rng % 5 {
0 => SpriteKind::Lantern,
1 => SpriteKind::PotionMinor,
2 => SpriteKind::VialEmpty,
3 => SpriteKind::Bowl,
_ => SpriteKind::Empty,
});
let rng0 = RandomField::new(0).get(nightstand_pos.with_z(base + 1));
let rng1 = RandomField::new(1).get(nightstand_pos.with_z(base + 1));
painter.owned_resource_sprite(
nightstand_pos.with_z(base + 1),
match rng0 % 5 {
0 => SpriteKind::Lantern,
1 => SpriteKind::PotionMinor,
2 => SpriteKind::VialEmpty,
3 => SpriteKind::Bowl,
_ => SpriteKind::Empty,
},
(rng1 % 4) as u8 * 2,
);
// wardrobe along wall in corner of the room
let (wardrobe_pos, drawer_ori) = match self.front {
Dir::Y => (Vec2::new(self.bounds.max.x - 2, self.bounds.min.y + 1), 4),
@ -1589,14 +1595,19 @@ impl Structure for House {
for dir in DIRS {
// random accent pieces and loot
let sprite_pos = self.bounds.center() + dir * 5;
let rng = RandomField::new(0).get(sprite_pos.with_z(base));
painter.sprite(sprite_pos.with_z(base), match rng % 32 {
0..=2 => SpriteKind::Crate,
3..=4 => SpriteKind::CoatRack,
5..=7 => SpriteKind::Pot,
8..=9 => SpriteKind::Lantern,
_ => SpriteKind::Empty,
});
let rng0 = RandomField::new(0).get(sprite_pos.with_z(base));
let rng1 = RandomField::new(1).get(sprite_pos.with_z(base));
painter.owned_resource_sprite(
sprite_pos.with_z(base),
match rng0 % 32 {
0..=2 => SpriteKind::Crate,
3..=4 => SpriteKind::CoatRack,
5..=7 => SpriteKind::Pot,
8..=9 => SpriteKind::Lantern,
_ => SpriteKind::Empty,
},
(rng1 % 4) as u8 * 2,
);
}
if self.bounds.max.x - self.bounds.min.x < 16
@ -1605,12 +1616,12 @@ impl Structure for House {
let table_pos = Vec2::new(half_x, half_y);
// room is smaller, so use small table
painter.sprite(table_pos.with_z(base), SpriteKind::TableDining);
for (idx, dir) in CARDINALS.iter().enumerate() {
let chair_pos = table_pos + dir;
for dir in Dir::iter() {
let chair_pos = table_pos + dir.to_vec2();
painter.rotated_sprite(
chair_pos.with_z(base),
SpriteKind::ChairSingle,
(idx * 2 + ((idx % 2) * 4)) as u8,
dir.opposite().sprite_ori(),
);
}
} else {

View File

@ -3,7 +3,7 @@ use crate::{
util::{RandomField, Sampler, CARDINALS, DIAGONALS},
Land,
};
use common::terrain::{BlockKind, SpriteKind};
use common::terrain::{sprite::Owned, BlockKind, SpriteKind};
use rand::prelude::*;
use std::{f32::consts::TAU, sync::Arc};
use vek::*;
@ -50,11 +50,15 @@ impl Structure for SavannahHut {
let center = self.bounds.center();
let sprite_fill = Fill::Sampling(Arc::new(|wpos| {
Some(match (RandomField::new(0).get(wpos)) % 25 {
0 => Block::air(SpriteKind::Bowl),
1 => Block::air(SpriteKind::VialEmpty),
0 => Block::air(SpriteKind::Bowl).with_attr(Owned(true)).unwrap(),
1 => Block::air(SpriteKind::VialEmpty)
.with_attr(Owned(true))
.unwrap(),
2 => Block::air(SpriteKind::Lantern),
3 => Block::air(SpriteKind::JugArabic),
4 => Block::air(SpriteKind::Crate),
4 => Block::air(SpriteKind::Crate)
.with_attr(Owned(true))
.unwrap(),
_ => Block::new(BlockKind::Air, Rgb::new(0, 0, 0)),
})
}));
@ -244,7 +248,7 @@ impl Structure for SavannahHut {
let sprite = sprites.swap_remove(
RandomField::new(0).get(position.with_z(base)) as usize % sprites.len(),
);
painter.sprite(position.with_z(base - 2), sprite);
painter.owned_resource_sprite(position.with_z(base - 2), sprite, 0);
}
}

View File

@ -6,7 +6,7 @@ use crate::{
};
use common::{
generation::{EntityInfo, SpecialEntity},
terrain::{BlockKind, SpriteKind},
terrain::{sprite::Owned, BlockKind, SpriteKind},
};
use rand::prelude::*;
use std::sync::Arc;
@ -43,11 +43,15 @@ impl Structure for SavannahPit {
let center = self.bounds.center();
let sprite_fill = Fill::Sampling(Arc::new(|wpos| {
Some(match (RandomField::new(0).get(wpos)) % 50 {
0 => Block::air(SpriteKind::Bowl),
1 => Block::air(SpriteKind::VialEmpty),
0 => Block::air(SpriteKind::Bowl).with_attr(Owned(true)).unwrap(),
1 => Block::air(SpriteKind::VialEmpty)
.with_attr(Owned(true))
.unwrap(),
2 => Block::air(SpriteKind::Lantern),
3 => Block::air(SpriteKind::JugArabic),
4 => Block::air(SpriteKind::Crate),
4 => Block::air(SpriteKind::Crate)
.with_attr(Owned(true))
.unwrap(),
_ => Block::new(BlockKind::Air, Rgb::new(0, 0, 0)),
})
}));
@ -727,7 +731,11 @@ impl Structure for SavannahPit {
RandomField::new(0).get(position.with_z(base)) as usize
% sprites.len(),
);
painter.sprite(position.with_z(base - ((1 + f) * length)), sprite);
painter.owned_resource_sprite(
position.with_z(base - ((1 + f) * length)),
sprite,
0,
);
}
}
},

View File

@ -5,7 +5,7 @@ use crate::{
};
use common::{
generation::SpecialEntity,
terrain::{BlockKind, SpriteKind},
terrain::{sprite::Owned, BlockKind, SpriteKind},
};
use rand::prelude::*;
use std::{f32::consts::TAU, sync::Arc};
@ -53,11 +53,15 @@ impl Structure for SavannahWorkshop {
let center = self.bounds.center();
let sprite_fill = Fill::Sampling(Arc::new(|wpos| {
Some(match (RandomField::new(0).get(wpos)) % 25 {
0 => Block::air(SpriteKind::Bowl),
1 => Block::air(SpriteKind::VialEmpty),
0 => Block::air(SpriteKind::Bowl).with_attr(Owned(true)).unwrap(),
1 => Block::air(SpriteKind::VialEmpty)
.with_attr(Owned(true))
.unwrap(),
2 => Block::air(SpriteKind::Lantern),
3 => Block::air(SpriteKind::JugArabic),
4 => Block::air(SpriteKind::Crate),
4 => Block::air(SpriteKind::Crate)
.with_attr(Owned(true))
.unwrap(),
_ => Block::new(BlockKind::Air, Rgb::new(0, 0, 0)),
})
}));

View File

@ -1491,10 +1491,7 @@ impl Structure for Tavern {
max: (wall_center + in_dir.rotated_cw().to_vec2())
.with_z(wall.base_alt + 2),
}))
.fill(Fill::RotatedSprite(
SpriteKind::Window1,
in_dir.sprite_ori(),
));
.fill(Fill::sprite_ori(SpriteKind::Window1, in_dir.sprite_ori()));
}
},
}