Further generalize the typed module.

This enables us to automatically create configuration formats from enums
or structs.  For enums, we create a structure with a field for each
case, representing a pattern match; the configuration format can then
enter a different expression for each case.  For structs, we create an
enum with a variant for each field, representing projecting by that
field; the configuration format selects the field to project from, and
then can write a further expression on that field (for instance, it can
perform another pattern match).

So far we don't actually have anything that *uses* this functionality;
I decided to finish it for the purpose of specifying a default
lantern offset, only to discover that we already return a lantern offset
from the animation crate.  So I fixed it there instead.
This commit is contained in:
Joshua Yanovski 2020-08-31 03:01:20 +02:00
parent 62427cb01f
commit 6f3d5da6f3
16 changed files with 729 additions and 373 deletions

View File

@ -1,11 +1,15 @@
use crate::{make_case_elim, make_proj_elim};
use rand::{seq::SliceRandom, thread_rng};
use serde::{Deserialize, Serialize};
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct Body {
pub species: Species,
pub body_type: BodyType,
}
make_proj_elim!(
body,
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct Body {
pub species: Species,
pub body_type: BodyType,
}
);
impl Body {
pub fn random() -> Self {
@ -25,15 +29,18 @@ impl From<Body> for super::Body {
fn from(body: Body) -> Self { super::Body::BipedLarge(body) }
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[repr(u32)]
pub enum Species {
Ogre = 0,
Cyclops = 1,
Wendigo = 2,
Troll = 3,
Dullahan = 4,
}
make_case_elim!(
species,
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[repr(u32)]
pub enum Species {
Ogre = 0,
Cyclops = 1,
Wendigo = 2,
Troll = 3,
Dullahan = 4,
}
);
/// Data representing per-species generic data.
///
@ -77,10 +84,13 @@ impl<'a, SpeciesMeta: 'a> IntoIterator for &'a AllSpecies<SpeciesMeta> {
fn into_iter(self) -> Self::IntoIter { ALL_SPECIES.iter().copied() }
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[repr(u32)]
pub enum BodyType {
Female = 0,
Male = 1,
}
make_case_elim!(
body_type,
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[repr(u32)]
pub enum BodyType {
Female = 0,
Male = 1,
}
);
pub const ALL_BODY_TYPES: [BodyType; 2] = [BodyType::Female, BodyType::Male];

View File

@ -1,11 +1,15 @@
use crate::{make_case_elim, make_proj_elim};
use rand::{seq::SliceRandom, thread_rng};
use serde::{Deserialize, Serialize};
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct Body {
pub species: Species,
pub body_type: BodyType,
}
make_proj_elim!(
body,
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct Body {
pub species: Species,
pub body_type: BodyType,
}
);
impl Body {
pub fn random() -> Self {
@ -25,18 +29,21 @@ impl From<Body> for super::Body {
fn from(body: Body) -> Self { super::Body::BirdMedium(body) }
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[repr(u32)]
pub enum Species {
Duck = 0,
Chicken = 1,
Goose = 2,
Peacock = 3,
Eagle = 4,
Snowyowl = 5,
Parrot = 6,
Cockatrice = 7,
}
make_case_elim!(
species,
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[repr(u32)]
pub enum Species {
Duck = 0,
Chicken = 1,
Goose = 2,
Peacock = 3,
Eagle = 4,
Snowyowl = 5,
Parrot = 6,
Cockatrice = 7,
}
);
/// Data representing per-species generic data.
///
@ -89,10 +96,13 @@ impl<'a, SpeciesMeta: 'a> IntoIterator for &'a AllSpecies<SpeciesMeta> {
fn into_iter(self) -> Self::IntoIter { ALL_SPECIES.iter().copied() }
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[repr(u32)]
pub enum BodyType {
Female = 0,
Male = 1,
}
make_case_elim!(
body_type,
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[repr(u32)]
pub enum BodyType {
Female = 0,
Male = 1,
}
);
pub const ALL_BODY_TYPES: [BodyType; 2] = [BodyType::Female, BodyType::Male];

View File

@ -1,13 +1,18 @@
use crate::{make_case_elim, make_proj_elim};
use rand::{seq::SliceRandom, thread_rng};
use serde::{Deserialize, Serialize};
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct Body {
pub head: Head,
pub torso: Torso,
pub wing_l: WingL,
pub wing_r: WingR,
}
make_proj_elim!(
body,
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct Body {
pub head: Head,
pub torso: Torso,
pub wing_l: WingL,
pub wing_r: WingR,
}
);
impl Body {
pub fn random() -> Self {
let mut rng = thread_rng();
@ -20,30 +25,42 @@ impl Body {
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[repr(u32)]
pub enum Head {
Default,
}
make_case_elim!(
head,
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[repr(u32)]
pub enum Head {
Default = 0,
}
);
const ALL_HEADS: [Head; 1] = [Head::Default];
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[repr(u32)]
pub enum Torso {
Default,
}
make_case_elim!(
torso,
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[repr(u32)]
pub enum Torso {
Default = 0,
}
);
const ALL_TORSOS: [Torso; 1] = [Torso::Default];
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[repr(u32)]
pub enum WingL {
Default,
}
make_case_elim!(
wing_l,
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[repr(u32)]
pub enum WingL {
Default = 0,
}
);
const ALL_WING_LS: [WingL; 1] = [WingL::Default];
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[repr(u32)]
pub enum WingR {
Default,
}
make_case_elim!(
wing_r,
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[repr(u32)]
pub enum WingR {
Default = 0,
}
);
const ALL_WING_RS: [WingR; 1] = [WingR::Default];

View File

@ -1,11 +1,15 @@
use crate::{make_case_elim, make_proj_elim};
use rand::{seq::SliceRandom, thread_rng};
use serde::{Deserialize, Serialize};
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct Body {
pub species: Species,
pub body_type: BodyType,
}
make_proj_elim!(
body,
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct Body {
pub species: Species,
pub body_type: BodyType,
}
);
impl Body {
pub fn random() -> Self {
@ -25,16 +29,19 @@ impl From<Body> for super::Body {
fn from(body: Body) -> Self { super::Body::Critter(body) }
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[repr(u32)]
pub enum Species {
Rat = 0,
Axolotl = 1,
Gecko = 2,
Turtle = 3,
Squirrel = 4,
Fungome = 5,
}
make_case_elim!(
species,
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[repr(u32)]
pub enum Species {
Rat = 0,
Axolotl = 1,
Gecko = 2,
Turtle = 3,
Squirrel = 4,
Fungome = 5,
}
);
/// Data representing per-species generic data.
#[derive(Clone, Debug, Serialize, Deserialize)]
@ -79,10 +86,14 @@ impl<'a, SpeciesMeta: 'a> IntoIterator for &'a AllSpecies<SpeciesMeta> {
fn into_iter(self) -> Self::IntoIter { ALL_SPECIES.iter().copied() }
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[repr(u32)]
pub enum BodyType {
Female = 0,
Male = 1,
}
make_case_elim!(
body_type,
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[repr(u32)]
pub enum BodyType {
Female = 0,
Male = 1,
}
);
pub const ALL_BODY_TYPES: [BodyType; 2] = [BodyType::Female, BodyType::Male];

View File

@ -1,11 +1,15 @@
use crate::{make_case_elim, make_proj_elim};
use rand::{seq::SliceRandom, thread_rng};
use serde::{Deserialize, Serialize};
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct Body {
pub species: Species,
pub body_type: BodyType,
}
make_proj_elim!(
body,
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct Body {
pub species: Species,
pub body_type: BodyType,
}
);
impl Body {
pub fn random() -> Self {
@ -25,11 +29,14 @@ impl From<Body> for super::Body {
fn from(body: Body) -> Self { super::Body::Dragon(body) }
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[repr(u32)]
pub enum Species {
Reddragon = 0,
}
make_case_elim!(
species,
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[repr(u32)]
pub enum Species {
Reddragon = 0,
}
);
/// Data representing per-species generic data.
///
@ -59,10 +66,14 @@ impl<'a, SpeciesMeta: 'a> IntoIterator for &'a AllSpecies<SpeciesMeta> {
fn into_iter(self) -> Self::IntoIter { ALL_SPECIES.iter().copied() }
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[repr(u32)]
pub enum BodyType {
Female = 0,
Male = 1,
}
make_case_elim!(
body_type,
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[repr(u32)]
pub enum BodyType {
Female = 0,
Male = 1,
}
);
pub const ALL_BODY_TYPES: [BodyType; 2] = [BodyType::Female, BodyType::Male];

View File

@ -1,15 +1,20 @@
use crate::{make_case_elim, make_proj_elim};
use rand::{seq::SliceRandom, thread_rng};
use serde::{Deserialize, Serialize};
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct Body {
pub head: Head,
pub torso: Torso,
pub rear: Rear,
pub tail: Tail,
pub fin_l: FinL,
pub fin_r: FinR,
}
make_proj_elim!(
body,
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct Body {
pub head: Head,
pub torso: Torso,
pub rear: Rear,
pub tail: Tail,
pub fin_l: FinL,
pub fin_r: FinR,
}
);
impl Body {
pub fn random() -> Self {
let mut rng = thread_rng();
@ -24,44 +29,63 @@ impl Body {
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[repr(u32)]
pub enum Head {
Default,
}
make_case_elim!(
head,
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[repr(u32)]
pub enum Head {
Default = 0,
}
);
const ALL_HEADS: [Head; 1] = [Head::Default];
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[repr(u32)]
pub enum Torso {
Default,
}
make_case_elim!(
torso,
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[repr(u32)]
pub enum Torso {
Default = 0,
}
);
const ALL_TORSOS: [Torso; 1] = [Torso::Default];
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[repr(u32)]
pub enum Rear {
Default,
}
make_case_elim!(
rear,
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[repr(u32)]
pub enum Rear {
Default = 0,
}
);
const ALL_REARS: [Rear; 1] = [Rear::Default];
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[repr(u32)]
pub enum Tail {
Default,
}
make_case_elim!(
tail,
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[repr(u32)]
pub enum Tail {
Default = 0,
}
);
const ALL_TAILS: [Tail; 1] = [Tail::Default];
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[repr(u32)]
pub enum FinL {
Default,
}
make_case_elim!(
fin_l,
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[repr(u32)]
pub enum FinL {
Default = 0,
}
);
const ALL_FIN_LS: [FinL; 1] = [FinL::Default];
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[repr(u32)]
pub enum FinR {
Default,
}
make_case_elim!(
fin_r,
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[repr(u32)]
pub enum FinR {
Default = 0,
}
);
const ALL_FIN_RS: [FinR; 1] = [FinR::Default];

View File

@ -1,11 +1,16 @@
use crate::{make_case_elim, make_proj_elim};
use rand::{seq::SliceRandom, thread_rng};
use serde::{Deserialize, Serialize};
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct Body {
pub torso: Torso,
pub tail: Tail,
}
make_proj_elim!(
body,
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct Body {
pub torso: Torso,
pub tail: Tail,
}
);
impl Body {
pub fn random() -> Self {
let mut rng = thread_rng();
@ -16,16 +21,24 @@ impl Body {
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[repr(u32)]
pub enum Torso {
Default,
}
make_case_elim!(
torso,
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[repr(u32)]
pub enum Torso {
Default = 0,
}
);
const ALL_TORSOS: [Torso; 1] = [Torso::Default];
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[repr(u32)]
pub enum Tail {
Default,
}
make_case_elim!(
tail,
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[repr(u32)]
pub enum Tail {
Default = 0,
}
);
const ALL_TAILS: [Tail; 1] = [Tail::Default];

View File

@ -1,11 +1,15 @@
use crate::{make_case_elim, make_proj_elim};
use rand::{seq::SliceRandom, thread_rng};
use serde::{Deserialize, Serialize};
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct Body {
pub species: Species,
pub body_type: BodyType,
}
make_proj_elim!(
body,
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct Body {
pub species: Species,
pub body_type: BodyType,
}
);
impl Body {
pub fn random() -> Self {
@ -25,11 +29,14 @@ impl From<Body> for super::Body {
fn from(body: Body) -> Self { super::Body::Golem(body) }
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[repr(u32)]
pub enum Species {
StoneGolem = 0,
}
make_case_elim!(
species,
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[repr(u32)]
pub enum Species {
StoneGolem = 0,
}
);
/// Data representing per-species generic data.
///
@ -59,10 +66,14 @@ impl<'a, SpeciesMeta: 'a> IntoIterator for &'a AllSpecies<SpeciesMeta> {
fn into_iter(self) -> Self::IntoIter { ALL_SPECIES.iter().copied() }
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[repr(u32)]
pub enum BodyType {
Female = 0,
Male = 1,
}
make_case_elim!(
body_type,
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[repr(u32)]
pub enum BodyType {
Female = 0,
Male = 1,
}
);
pub const ALL_BODY_TYPES: [BodyType; 2] = [BodyType::Female, BodyType::Male];

View File

@ -1,20 +1,30 @@
use crate::make_case_elim;
use crate::{make_case_elim, make_proj_elim};
use rand::{seq::SliceRandom, thread_rng, Rng};
use serde::{Deserialize, Serialize};
use serde_repr::{Deserialize_repr, Serialize_repr};
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct Body {
pub species: Species,
pub body_type: BodyType,
pub hair_style: u8,
pub beard: u8,
pub eyes: u8,
pub accessory: u8,
pub hair_color: u8,
pub skin: u8,
pub eye_color: u8,
}
make_proj_elim!(
body,
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct Body {
pub species: Species,
pub body_type: BodyType,
#[typed(pure)]
pub hair_style: u8,
#[typed(pure)]
pub beard: u8,
#[typed(pure)]
pub eyes: u8,
#[typed(pure)]
pub accessory: u8,
#[typed(pure)]
pub hair_color: u8,
#[typed(pure)]
pub skin: u8,
#[typed(pure)]
pub eye_color: u8,
}
);
impl Body {
pub fn random() -> Self {

View File

@ -1,70 +1,74 @@
use crate::make_case_elim;
use rand::{seq::SliceRandom, thread_rng};
use serde::{Deserialize, Serialize};
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[repr(u32)]
pub enum Body {
Arrow = 0,
Bomb = 1,
Scarecrow = 2,
Cauldron = 3,
ChestVines = 4,
Chest = 5,
ChestDark = 6,
ChestDemon = 7,
ChestGold = 8,
ChestLight = 9,
ChestOpen = 10,
ChestSkull = 11,
Pumpkin = 12,
Pumpkin2 = 13,
Pumpkin3 = 14,
Pumpkin4 = 15,
Pumpkin5 = 16,
Campfire = 17,
LanternGround = 18,
LanternGroundOpen = 19,
LanternStanding2 = 20,
LanternStanding = 21,
PotionBlue = 22,
PotionGreen = 23,
PotionRed = 24,
Crate = 25,
Tent = 26,
WindowSpooky = 27,
DoorSpooky = 28,
Anvil = 29,
Gravestone = 30,
Gravestone2 = 31,
Bench = 32,
Chair = 33,
Chair2 = 34,
Chair3 = 35,
Table = 36,
Table2 = 37,
Table3 = 38,
Drawer = 39,
BedBlue = 40,
Carpet = 41,
Bedroll = 42,
CarpetHumanRound = 43,
CarpetHumanSquare = 44,
CarpetHumanSquare2 = 45,
CarpetHumanSquircle = 46,
Pouch = 47,
CraftingBench = 48,
BoltFire = 49,
ArrowSnake = 50,
CampfireLit = 51,
BoltFireBig = 52,
TrainingDummy = 53,
FireworkBlue = 54,
FireworkGreen = 55,
FireworkPurple = 56,
FireworkRed = 57,
FireworkYellow = 58,
MultiArrow = 59,
}
make_case_elim!(
body,
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[repr(u32)]
pub enum Body {
Arrow = 0,
Bomb = 1,
Scarecrow = 2,
Cauldron = 3,
ChestVines = 4,
Chest = 5,
ChestDark = 6,
ChestDemon = 7,
ChestGold = 8,
ChestLight = 9,
ChestOpen = 10,
ChestSkull = 11,
Pumpkin = 12,
Pumpkin2 = 13,
Pumpkin3 = 14,
Pumpkin4 = 15,
Pumpkin5 = 16,
Campfire = 17,
LanternGround = 18,
LanternGroundOpen = 19,
LanternStanding2 = 20,
LanternStanding = 21,
PotionBlue = 22,
PotionGreen = 23,
PotionRed = 24,
Crate = 25,
Tent = 26,
WindowSpooky = 27,
DoorSpooky = 28,
Anvil = 29,
Gravestone = 30,
Gravestone2 = 31,
Bench = 32,
Chair = 33,
Chair2 = 34,
Chair3 = 35,
Table = 36,
Table2 = 37,
Table3 = 38,
Drawer = 39,
BedBlue = 40,
Carpet = 41,
Bedroll = 42,
CarpetHumanRound = 43,
CarpetHumanSquare = 44,
CarpetHumanSquare2 = 45,
CarpetHumanSquircle = 46,
Pouch = 47,
CraftingBench = 48,
BoltFire = 49,
ArrowSnake = 50,
CampfireLit = 51,
BoltFireBig = 52,
TrainingDummy = 53,
FireworkBlue = 54,
FireworkGreen = 55,
FireworkPurple = 56,
FireworkRed = 57,
FireworkYellow = 58,
MultiArrow = 59,
}
);
impl Body {
pub fn random() -> Self {

View File

@ -1,11 +1,15 @@
use crate::{make_case_elim, make_proj_elim};
use rand::{seq::SliceRandom, thread_rng};
use serde::{Deserialize, Serialize};
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct Body {
pub species: Species,
pub body_type: BodyType,
}
make_proj_elim!(
body,
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct Body {
pub species: Species,
pub body_type: BodyType,
}
);
impl Body {
pub fn random() -> Self {
@ -25,19 +29,22 @@ impl From<Body> for super::Body {
fn from(body: Body) -> Self { super::Body::QuadrupedLow(body) }
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[repr(u32)]
pub enum Species {
Crocodile = 0,
Alligator = 1,
Salamander = 2,
Monitor = 3,
Asp = 4,
Tortoise = 5,
Rocksnapper = 6,
Pangolin = 7,
Maneater = 8,
}
make_case_elim!(
species,
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[repr(u32)]
pub enum Species {
Crocodile = 0,
Alligator = 1,
Salamander = 2,
Monitor = 3,
Asp = 4,
Tortoise = 5,
Rocksnapper = 6,
Pangolin = 7,
Maneater = 8,
}
);
/// Data representing per-species generic data.
///
@ -93,10 +100,13 @@ impl<'a, SpeciesMeta: 'a> IntoIterator for &'a AllSpecies<SpeciesMeta> {
fn into_iter(self) -> Self::IntoIter { ALL_SPECIES.iter().copied() }
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[repr(u32)]
pub enum BodyType {
Female = 0,
Male = 1,
}
make_case_elim!(
body_type,
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[repr(u32)]
pub enum BodyType {
Female = 0,
Male = 1,
}
);
pub const ALL_BODY_TYPES: [BodyType; 2] = [BodyType::Female, BodyType::Male];

View File

@ -1,11 +1,15 @@
use crate::{make_case_elim, make_proj_elim};
use rand::{seq::SliceRandom, thread_rng};
use serde::{Deserialize, Serialize};
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct Body {
pub species: Species,
pub body_type: BodyType,
}
make_proj_elim!(
body,
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct Body {
pub species: Species,
pub body_type: BodyType,
}
);
impl Body {
pub fn random() -> Self {
@ -25,21 +29,24 @@ impl From<Body> for super::Body {
fn from(body: Body) -> Self { super::Body::QuadrupedMedium(body) }
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[repr(u32)]
pub enum Species {
Grolgar = 0,
Saber = 1,
Tiger = 2,
Tuskram = 3,
Lion = 6,
Tarasque = 7,
Wolf = 8,
Frostfang = 9,
Mouflon = 10,
Catoblepas = 11,
Bonerattler = 12,
}
make_case_elim!(
species,
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[repr(u32)]
pub enum Species {
Grolgar = 0,
Saber = 1,
Tiger = 2,
Tuskram = 3,
Lion = 6,
Tarasque = 7,
Wolf = 8,
Frostfang = 9,
Mouflon = 10,
Catoblepas = 11,
Bonerattler = 12,
}
);
/// Data representing per-species generic data.
///
@ -101,10 +108,14 @@ impl<'a, SpeciesMeta: 'a> IntoIterator for &'a AllSpecies<SpeciesMeta> {
fn into_iter(self) -> Self::IntoIter { ALL_SPECIES.iter().copied() }
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[repr(u32)]
pub enum BodyType {
Female = 0,
Male = 1,
}
make_case_elim!(
body_type,
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[repr(u32)]
pub enum BodyType {
Female = 0,
Male = 1,
}
);
pub const ALL_BODY_TYPES: [BodyType; 2] = [BodyType::Female, BodyType::Male];

View File

@ -1,11 +1,15 @@
use crate::{make_case_elim, make_proj_elim};
use rand::{seq::SliceRandom, thread_rng};
use serde::{Deserialize, Serialize};
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct Body {
pub species: Species,
pub body_type: BodyType,
}
make_proj_elim!(
body,
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct Body {
pub species: Species,
pub body_type: BodyType,
}
);
impl Body {
pub fn random() -> Self {
@ -25,26 +29,29 @@ impl From<Body> for super::Body {
fn from(body: Body) -> Self { super::Body::QuadrupedSmall(body) }
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[repr(u32)]
pub enum Species {
Pig = 0,
Fox = 1,
Sheep = 2,
Boar = 3,
Jackalope = 4,
Skunk = 5,
Cat = 6,
Batfox = 7,
Raccoon = 8,
Quokka = 9,
Dodarock = 10,
Holladon = 11,
Hyena = 12,
Rabbit = 13,
Truffler = 14,
Frog = 15,
}
make_case_elim!(
species,
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[repr(u32)]
pub enum Species {
Pig = 0,
Fox = 1,
Sheep = 2,
Boar = 3,
Jackalope = 4,
Skunk = 5,
Cat = 6,
Batfox = 7,
Raccoon = 8,
Quokka = 9,
Dodarock = 10,
Holladon = 11,
Hyena = 12,
Rabbit = 13,
Truffler = 14,
Frog = 15,
}
);
/// Data representing per-species generic data.
///
@ -121,10 +128,14 @@ impl<'a, SpeciesMeta: 'a> IntoIterator for &'a AllSpecies<SpeciesMeta> {
fn into_iter(self) -> Self::IntoIter { ALL_SPECIES.iter().copied() }
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[repr(u32)]
pub enum BodyType {
Female = 0,
Male = 1,
}
make_case_elim!(
body_type,
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[repr(u32)]
pub enum BodyType {
Female = 0,
Male = 1,
}
);
pub const ALL_BODY_TYPES: [BodyType; 2] = [BodyType::Female, BodyType::Male];

View File

@ -1,4 +1,5 @@
use core::marker::PhantomData;
use serde::{Deserialize, Serialize};
pub trait SubContext<Context> {
fn sub_context(self) -> Context;
@ -16,10 +17,52 @@ pub trait Typed<Context, Type, S> {
fn reduce(self, context: Context) -> (Type, S);
}
/// Given a head expression (Self) and a target type (Type),
/// attempt to synthesize a term that reduces head into the target type.
///
/// How we do this depends on the type of the head expression:
///
/// - For enums, we synthesize a match on the current head. For each match arm,
/// we then repeat this process on the constructor arguments; if there are no
/// constructor arguments, we synthesize a literal (Pure term). (TODO: Handle
/// > 1 tuple properly--for now we just synthesize a Pure term for these
/// cases).
///
/// - For structs, we synthesize a projection on the current head. For each
/// projection, we then repeat this process on the type of the projected
/// field.
///
/// - For other types (which currently have to opt out during the field
/// declaration), we synthesize a literal.
///
/// TODO: Differentiate between the context and the stack at some point; for
/// now, we only use the context as a stack.
pub trait SynthTyped<Context, Target> {
type Expr;
}
/// Weak head reduction type (equivalent to applying a reduction to the head
/// variable, but this way we don't have to implement variable lookup and it
/// doesn't serialize with variables).
#[fundamental]
#[serde(transparent)]
#[derive(Deserialize, Serialize)]
pub struct WeakHead<Reduction, Type> {
pub red: Reduction,
#[serde(skip)]
pub ty: PhantomData<Type>,
}
#[serde(transparent)]
#[derive(Deserialize, Serialize)]
pub struct Pure<T>(pub T);
impl<Context: SubContext<S>, T, S> Typed<Context, Pure<T>, S> for T {
fn reduce(self, context: Context) -> (Pure<T>, S) { (Pure(self), context.sub_context()) }
impl<'a, Context: SubContext<S>, T, S> Typed<Context, &'a T, S> for &'a Pure<T> {
fn reduce(self, context: Context) -> (&'a T, S) { (&self.0, context.sub_context()) }
}
impl<Context, Target> SynthTyped<Context, Target> for WeakHead<Pure<Target>, Target> {
type Expr = Pure<Target>;
}
/// A lazy pattern match reified as a Rust type.
@ -50,7 +93,7 @@ impl<Context: SubContext<S>, T, S> Typed<Context, Pure<T>, S> for T {
/// #[derive(Clone,Copy)]
/// pub enum MyType {
/// Constr1 = 0,
/// Constr2(arg : u8) = 1,
/// #[typed(pure)] Constr2(arg : u8) = 1,
/// /* ..., */
/// }
/// );
@ -141,18 +184,26 @@ impl<Context: SubContext<S>, T, S> Typed<Context, Pure<T>, S> for T {
///
/// Limitations:
///
/// Unfortunately, due to restrictions on procedural macros, we currently always
/// Unfortunately, due to restrictions on macro_rules, we currently always
/// require the types defined to #[repr(inttype)] as you can see above. There
/// are also some other current limitations that we hopefully will be able to
/// lift at some point; struct variants are not yet supported, and neither
/// attributes on fields.
#[fundamental]
pub struct ElimCase<Expr, Cases, Type> {
pub expr: Expr,
#[serde(transparent)]
#[derive(Deserialize, Serialize)]
pub struct ElimCase<Cases> {
pub cases: Cases,
pub ty: PhantomData<Type>,
}
#[fundamental]
#[serde(transparent)]
#[derive(Deserialize, Serialize)]
pub struct ElimProj<Proj> {
pub proj: Proj,
}
pub type ElimWeak<Type, Elim> = <WeakHead<Type, Elim> as SynthTyped<((Type,), ()), Elim>>::Expr;
#[macro_export]
macro_rules! as_item {
($i:item) => {
@ -160,10 +211,33 @@ macro_rules! as_item {
};
}
#[macro_export]
/// This macro is used internally by typed.
///
/// We use this in order to reliably construct a "representative" type for the
/// weak head reduction type. We need this because for some types of arguments
/// (empty variants for an enum, fields or constructor arguments explicitly
/// marked as #[typed(pure)], etc.) won't directly implement the WeakHead trait;
/// in such cases, we just synthesize a literal of the appropriate type.
macro_rules! make_weak_head_type {
($Target:ty, $( #[$attr:meta] )* , ) => {
$crate::typed::Pure<$Target>
};
($Target:ty, #[ typed(pure) ] , $( $extra:tt )*) => {
$crate::typed::Pure<$Target>
};
($Target:ty, , $Type:ty, $( $extra:tt )*) => {
$crate::typed::Pure<$Target>
};
($Target:ty, , $Type:ty) => {
$Type
}
}
#[macro_export]
macro_rules! make_case_elim {
($mod:ident, $( #[$ty_attr:meta] )* $vis:vis enum $ty:ident {
$( $constr:ident $( ( $( $arg_name:ident : $arg_ty:ty ),* ) )? = $index:expr ),* $(,)?
($mod:ident, $( #[ $ty_attr:meta ] )* $vis:vis enum $ty:ident {
$( $( #[$( $constr_attr:tt )*] )* $constr:ident $( ( $( $arg_name:ident : $arg_ty:ty ),* ) )? = $index:expr ),* $(,)?
}) => {
$crate::as_item! {
$( #[$ty_attr] )*
@ -190,26 +264,48 @@ macro_rules! make_case_elim {
$( pub $constr : Elim::$constr, )*
}
impl<T> PackedElim for $crate::typed::Pure<T> {
$( type $constr = T; )*
}
pub type PureCases<Elim> = $crate::typed::ElimCase<Cases<$crate::typed::Pure<Elim>>>;
}
pub type PureCases<Elim> = Cases<$crate::typed::Pure<Elim>>;
impl<T> $mod::PackedElim for $crate::typed::Pure<T> {
$( type $constr = $crate::typed::Pure<T>; )*
}
#[allow(unused_parens)]
impl<Target> $mod::PackedElim for $crate::typed::WeakHead<$ty, Target>
where $(
$crate::typed::WeakHead<$crate::make_weak_head_type!(Target, $( #[$( $constr_attr )*] )* , $( $( $arg_ty ),* )?), Target> :
$crate::typed::SynthTyped<($( ($( $arg_ty, )*), )? ()), Target>,
)*
{
$( type $constr =
<$crate::typed::WeakHead<$crate::make_weak_head_type!(Target, $( #[$( $constr_attr )*] )* , $( $( $arg_ty ),* )?), Target>
as $crate::typed::SynthTyped<($( ($( $arg_ty, )*), )? ()), Target>>::Expr;
)*
}
#[allow(unused_parens)]
impl<Context, Target> $crate::typed::SynthTyped<(($ty,), Context), Target> for $crate::typed::WeakHead<$ty, Target>
where $(
$crate::typed::WeakHead<$crate::make_weak_head_type!(Target, $( #[$( $constr_attr )*] )* , $( $( $arg_ty ),* )?), Target> :
$crate::typed::SynthTyped<($( ($( $arg_ty, )*), )? ()), Target>,
)*
{
type Expr = $crate::typed::ElimCase<$mod::Cases<$crate::typed::WeakHead<$ty, Target>>>;
}
#[allow(unused_parens)]
impl<'a, 'b, Elim: $mod::PackedElim, Context, Type, S>
$crate::typed::Typed<Context, Type, S> for $crate::typed::ElimCase<&'a $ty, &'b $mod::Cases<Elim>, Type>
$crate::typed::Typed<((&'a $ty,), Context), Type, S> for &'b $crate::typed::ElimCase<$mod::Cases<Elim>>
where
$( &'b Elim::$constr: $crate::typed::Typed<($( ($( &'a $arg_ty, )*), )? Context), Type, S>, )*
$( &'b Elim::$constr: $crate::typed::Typed<($( ($( &'a $arg_ty, )*), )? Context), Type, S> ),*
{
fn reduce(self, context: Context) -> (Type, S)
fn reduce(self, ((head,), context): ((&'a $ty,), Context)) -> (Type, S)
{
let Self { expr, cases, .. } = self;
match expr {
match head {
$( $ty::$constr $( ($( $arg_name, )*) )? =>
<_ as $crate::typed::Typed<_, Type, _>>::reduce(
&cases.$constr,
&self.cases.$constr,
($( ($( $arg_name, )*), )? context),
),
)*
@ -218,24 +314,132 @@ macro_rules! make_case_elim {
}
impl $ty {
pub fn elim_case<'a, 'b, Elim: $mod::PackedElim, Context, S, Type>(&'a self, cases: &'b $mod::Cases<Elim>, context: Context) ->
(Type, S)
pub fn elim<'a, Elim, Context, S, Type>(&'a self, elim: Elim, context: Context) -> (Type, S)
where
$crate::typed::ElimCase<&'a $ty, &'b $mod::Cases<Elim>, Type> : $crate::typed::Typed<Context, Type, S>,
Elim : $crate::typed::Typed<((&'a $ty,), Context), Type, S>,
{
use $crate::typed::Typed;
let case = $crate::typed::ElimCase {
expr: self,
cases,
ty: ::core::marker::PhantomData,
};
case.reduce(context)
elim.reduce(((self,), context))
}
pub fn elim_case_pure<'a, 'b, Type>(&'a self, cases: &'b $mod::PureCases<Type>) -> &'b Type
{
let ($crate::typed::Pure(expr), ()) = self.elim_case(cases, ());
let (expr, ()) = self.elim(cases, ());
expr
}
#[allow(unused_parens)]
pub fn elim_case_weak<'a, 'b, Type>(&'a self, cases: &'b $crate::typed::ElimWeak<Self, Type>) -> &'b Type
where $(
$crate::typed::WeakHead<$crate::make_weak_head_type!(Type, $( #[$( $constr_attr )*] )* , $( $( $arg_ty ),* )?), Type> :
$crate::typed::SynthTyped<($( ($( $arg_ty, )*), )? ()), Type>,
)*
&'b $crate::typed::ElimWeak<Self, Type> : $crate::typed::Typed<((&'a $ty,), ()), &'b Type, ()>,
{
let (expr, ()) = self.elim(cases, ());
expr
}
}
}
}
#[macro_export]
macro_rules! make_proj_elim {
($mod:ident, $( #[ $ty_attr:meta ] )* $vis:vis struct $ty:ident {
$( $( #[$( $constr_attr:tt )*] )* $field_vis:vis $constr:ident : $arg_ty:ty ),* $(,)?
}) => {
$crate::as_item! {
$( #[$ty_attr] )*
$vis struct $ty {
$( $field_vis $constr : $arg_ty, )*
}
}
#[allow(non_camel_case_types)]
#[allow(dead_code)]
$vis mod $mod {
use ::serde::{Deserialize, Serialize};
pub trait PackedElim {
$( type $constr; )*
}
#[derive(Serialize, Deserialize)]
pub enum Proj<Elim: PackedElim> {
$( $constr(Elim::$constr), )*
}
pub type PureProj<Elim> = $crate::typed::ElimProj<Proj<$crate::typed::Pure<Elim>>>;
}
impl<T> $mod::PackedElim for $crate::typed::Pure<T> {
$( type $constr = $crate::typed::Pure<T>; )*
}
#[allow(unused_parens)]
impl<Target> $mod::PackedElim for $crate::typed::WeakHead<$ty, Target>
where $(
$crate::typed::WeakHead<$crate::make_weak_head_type!(Target, $( #[$( $constr_attr )*] )* , $arg_ty), Target> :
$crate::typed::SynthTyped<(($arg_ty,), ()), Target>,
)*
{
$( type $constr =
<$crate::typed::WeakHead<$crate::make_weak_head_type!(Target, $( #[$( $constr_attr )*] )* , $arg_ty), Target>
as $crate::typed::SynthTyped<(($arg_ty,), ()), Target>>::Expr;
)*
}
#[allow(unused_parens)]
impl<Context, Target> $crate::typed::SynthTyped<(($ty,), Context), Target> for $crate::typed::WeakHead<$ty, Target>
where $(
$crate::typed::WeakHead<$crate::make_weak_head_type!(Target, $( #[$( $constr_attr )*] )* , $arg_ty), Target> :
$crate::typed::SynthTyped<(($arg_ty,), ()), Target>,
)*
{
type Expr = $crate::typed::ElimProj<$mod::Proj<$crate::typed::WeakHead<$ty, Target>>>;
}
#[allow(unused_parens)]
impl<'a, 'b, Elim: $mod::PackedElim, Context, Type, S>
$crate::typed::Typed<((&'a $ty,), Context), Type, S> for &'b $crate::typed::ElimProj<$mod::Proj<Elim>>
where
$( &'b Elim::$constr: $crate::typed::Typed<((&'a $arg_ty,), Context), Type, S> ),*
{
fn reduce(self, ((head,), context): ((&'a $ty,), Context)) -> (Type, S)
{
match self.proj {
$( $mod::Proj::$constr(ref projection) =>
<_ as $crate::typed::Typed<_, Type, _>>::reduce(
projection,
((&head.$constr,), context),
),
)*
}
}
}
impl $ty {
pub fn elim<'a, Elim, Context, S, Type>(&'a self, elim: Elim, context: Context) -> (Type, S)
where
Elim : $crate::typed::Typed<((&'a $ty,), Context), Type, S>,
{
elim.reduce(((self,), context))
}
pub fn elim_proj_pure<'a, 'b, Type>(&'a self, cases: &'b $mod::PureProj<Type>) -> &'b Type
{
let (expr, ()) = self.elim(cases, ());
expr
}
#[allow(unused_parens)]
pub fn elim_proj_weak<'a, 'b, Type>(&'a self, cases: &'b $crate::typed::ElimWeak<Self, Type>) -> &'b Type
where $(
$crate::typed::WeakHead<$crate::make_weak_head_type!(Type, $( #[$( $constr_attr )*] )* , $arg_ty), Type> :
$crate::typed::SynthTyped<(($arg_ty,), ()), Type>,
)*
&'b $crate::typed::ElimWeak<Self, Type> : $crate::typed::Typed<((&'a $ty,), ()), &'b Type, ()>,
{
let (expr, ()) = self.elim(cases, ());
expr
}
}

View File

@ -35,7 +35,9 @@ impl Skeleton for ObjectSkeleton {
buf: &mut [FigureBoneData; super::MAX_BONE_COUNT],
) -> Vec3<f32> {
buf[0] = make_bone(base_mat * Mat4::scaling_3d(SCALE));
Vec3::default()
// TODO: Make dependent on bone, when we find an easier way to make that
// information available.
Vec3::unit_z() * 0.5
}
}

View File

@ -378,10 +378,10 @@ impl FigureMgr {
}
let dt = ecs.fetch::<DeltaTime>().0;
let updater = ecs.read_resource::<LazyUpdate>();
for (entity, waypoint, light_emitter_opt, light_anim) in (
for (entity, light_emitter_opt, body, light_anim) in (
&ecs.entities(),
ecs.read_storage::<common::comp::WaypointArea>().maybe(),
ecs.read_storage::<LightEmitter>().maybe(),
ecs.read_storage::<Body>().maybe(),
&mut ecs.write_storage::<LightAnimation>(),
)
.join()
@ -401,10 +401,7 @@ impl FigureMgr {
} else {
(vek::Rgb::zero(), 0.0, 0.0, true)
};
if let Some(_) = waypoint {
light_anim.offset = vek::Vec3::unit_z() * 5.0;
}
if let Some(state) = self.states.character_states.get(&entity) {
if let Some(state) = body.and_then(|body| self.states.get_mut(body, &entity)) {
light_anim.offset = vek::Vec3::from(state.lantern_offset);
}
if !light_anim.strength.is_normal() {