mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Per-speces NPC names!
Is also able to refactor some of the uglier code and introduces a framework that (suitably extended) could be useful in removing boilerplate elsewhere.
This commit is contained in:
parent
2d9e60e566
commit
3fa21b3dc7
@ -91,6 +91,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
- Various performance improvements to world generation.
|
- Various performance improvements to world generation.
|
||||||
- Nametags now a fixed size and shown in a limited range
|
- Nametags now a fixed size and shown in a limited range
|
||||||
- Non-humanoid skeletons now utilize configs for hotloading, and skeletal attributes.
|
- Non-humanoid skeletons now utilize configs for hotloading, and skeletal attributes.
|
||||||
|
- Names of NPCs spawned in the wild now include their species.
|
||||||
|
|
||||||
### Removed
|
### Removed
|
||||||
|
|
||||||
|
@ -1,322 +1,475 @@
|
|||||||
{
|
{
|
||||||
"humanoid": [
|
"humanoid": {
|
||||||
"Adon",
|
"body": {
|
||||||
"Agro",
|
"keyword": "humanoid",
|
||||||
"Arlo",
|
"names": [
|
||||||
"Azamarr",
|
"Adon",
|
||||||
"Baashar",
|
"Agro",
|
||||||
"Barak",
|
"Arlo",
|
||||||
"Barton",
|
"Azamarr",
|
||||||
"Baske",
|
"Baashar",
|
||||||
"Baxar",
|
"Barak",
|
||||||
"Blaiz",
|
"Barton",
|
||||||
"Caelan",
|
"Baske",
|
||||||
"Cassian",
|
"Baxar",
|
||||||
"Clawsen",
|
"Blaiz",
|
||||||
"Colborn",
|
"Caelan",
|
||||||
"Dagfinn",
|
"Cassian",
|
||||||
"Dagrod",
|
"Clawsen",
|
||||||
"Dimian",
|
"Colborn",
|
||||||
"Domnhar",
|
"Dagfinn",
|
||||||
"Ebraheim",
|
"Dagrod",
|
||||||
"Eldermar",
|
"Dimian",
|
||||||
"Embre",
|
"Domnhar",
|
||||||
"Esdel",
|
"Ebraheim",
|
||||||
"Eune",
|
"Eldermar",
|
||||||
"Fangar",
|
"Embre",
|
||||||
"Favroe",
|
"Esdel",
|
||||||
"Feron",
|
"Eune",
|
||||||
"Feston",
|
"Fangar",
|
||||||
"Fintis",
|
"Favroe",
|
||||||
"Gatlen",
|
"Feron",
|
||||||
"Gatlin",
|
"Feston",
|
||||||
"Gentar",
|
"Fintis",
|
||||||
"Gethrod",
|
"Gatlen",
|
||||||
"Graff",
|
"Gatlin",
|
||||||
"Gunnar",
|
"Gentar",
|
||||||
"Hagalbar",
|
"Gethrod",
|
||||||
"Hawke",
|
"Graff",
|
||||||
"Hemm",
|
"Gunnar",
|
||||||
"Henndar",
|
"Hagalbar",
|
||||||
"Hezra",
|
"Hawke",
|
||||||
"Hodus",
|
"Hemm",
|
||||||
"Ishmael",
|
"Henndar",
|
||||||
"Jakrin",
|
"Hezra",
|
||||||
"Jareth",
|
"Hodus",
|
||||||
"Jaris",
|
"Ishmael",
|
||||||
"Jather",
|
"Jakrin",
|
||||||
"Jerrick",
|
"Jareth",
|
||||||
"Jessop",
|
"Jaris",
|
||||||
"Jinto",
|
"Jather",
|
||||||
"Joz",
|
"Jerrick",
|
||||||
"Kadric",
|
"Jessop",
|
||||||
"Kagran",
|
"Jinto",
|
||||||
"Kent",
|
"Joz",
|
||||||
"Khron",
|
"Kadric",
|
||||||
"Kontas",
|
"Kagran",
|
||||||
"Krinn",
|
"Kent",
|
||||||
"Lassrin",
|
"Khron",
|
||||||
"Lenox",
|
"Kontas",
|
||||||
"Lothe",
|
"Krinn",
|
||||||
"Lustros",
|
"Lassrin",
|
||||||
"Lydan",
|
"Lenox",
|
||||||
"Mavrek",
|
"Lothe",
|
||||||
"Moki",
|
"Lustros",
|
||||||
"Monty",
|
"Lydan",
|
||||||
"Nazim",
|
"Mavrek",
|
||||||
"Nesso",
|
"Moki",
|
||||||
"Ophni",
|
"Monty",
|
||||||
"Pakker",
|
"Nazim",
|
||||||
"Paquin",
|
"Nesso",
|
||||||
"Paskel",
|
"Ophni",
|
||||||
"Pike",
|
"Pakker",
|
||||||
"Ptorik",
|
"Paquin",
|
||||||
"Quintis",
|
"Paskel",
|
||||||
"Rankar",
|
"Pike",
|
||||||
"Renham",
|
"Ptorik",
|
||||||
"Revvyn",
|
"Quintis",
|
||||||
"Riordan",
|
"Rankar",
|
||||||
"Rivik",
|
"Renham",
|
||||||
"Rourke",
|
"Revvyn",
|
||||||
"Roux",
|
"Riordan",
|
||||||
"Ryven",
|
"Rivik",
|
||||||
"Sarkin",
|
"Rourke",
|
||||||
"Sturp",
|
"Roux",
|
||||||
"Straus",
|
"Ryven",
|
||||||
"Syrin",
|
"Sarkin",
|
||||||
"Talon",
|
"Sturp",
|
||||||
"Tekren",
|
"Straus",
|
||||||
"Tez",
|
"Syrin",
|
||||||
"Turrek",
|
"Talon",
|
||||||
"Tyvrik",
|
"Tekren",
|
||||||
"Vadim",
|
"Tez",
|
||||||
"Vale",
|
"Turrek",
|
||||||
"Varog",
|
"Tyvrik",
|
||||||
"Verssek",
|
"Vadim",
|
||||||
"Weston",
|
"Vale",
|
||||||
"Whit",
|
"Varog",
|
||||||
"Wulfe",
|
"Verssek",
|
||||||
"Yorjan",
|
"Weston",
|
||||||
"Zaden",
|
"Whit",
|
||||||
"Zagaroth",
|
"Wulfe",
|
||||||
"Zenner"
|
"Yorjan",
|
||||||
],
|
"Zaden",
|
||||||
"wolf": [
|
"Zagaroth",
|
||||||
"Achak",
|
"Zenner"
|
||||||
"Adalwolf",
|
]
|
||||||
"Akela",
|
},
|
||||||
"Alaska",
|
"species": {
|
||||||
"Aleu",
|
"danari": {
|
||||||
"Amarok",
|
"generic": "Danari"
|
||||||
"Apisi",
|
},
|
||||||
"Archer",
|
"dwarf": {
|
||||||
"Ares",
|
"generic": "Dwarf"
|
||||||
"Arrax",
|
},
|
||||||
"Artic",
|
"elf": {
|
||||||
"Aspen",
|
"generic": "Elf"
|
||||||
"Aura",
|
},
|
||||||
"Axel",
|
"human": {
|
||||||
"Balto",
|
"generic": "Human"
|
||||||
"Barwolf",
|
},
|
||||||
"Basil",
|
"orc": {
|
||||||
"Beja",
|
"generic": "Orc"
|
||||||
"Beowulf",
|
},
|
||||||
"Borris",
|
"undead": {
|
||||||
"Brassa",
|
"generic": "Undead"
|
||||||
"Bruno",
|
}
|
||||||
"Chronos",
|
}
|
||||||
"Colt",
|
},
|
||||||
"Comet",
|
"quadruped_medium": {
|
||||||
"Cronus",
|
"body": {
|
||||||
"Czar",
|
"keyword": "wolf",
|
||||||
"Dakota",
|
"names": [
|
||||||
"Dash",
|
"Achak",
|
||||||
"Diego",
|
"Adalwolf",
|
||||||
"Dire",
|
"Akela",
|
||||||
"Duke",
|
"Alaska",
|
||||||
"Echo",
|
"Aleu",
|
||||||
"Elda",
|
"Amarok",
|
||||||
"Eskimo",
|
"Apisi",
|
||||||
"Essos",
|
"Archer",
|
||||||
"Frey",
|
"Ares",
|
||||||
"Gabu",
|
"Arrax",
|
||||||
"Ghost",
|
"Artic",
|
||||||
"Giro",
|
"Aspen",
|
||||||
"Grey Wind",
|
"Aura",
|
||||||
"Gunner",
|
"Axel",
|
||||||
"Harou",
|
"Balto",
|
||||||
"Havoc",
|
"Barwolf",
|
||||||
"Hera",
|
"Basil",
|
||||||
"Hunter",
|
"Beja",
|
||||||
"Inuit",
|
"Beowulf",
|
||||||
"Jacob",
|
"Borris",
|
||||||
"Jenna",
|
"Brassa",
|
||||||
"Juno",
|
"Bruno",
|
||||||
"Kar",
|
"Chronos",
|
||||||
"Khal",
|
"Colt",
|
||||||
"Kiba",
|
"Comet",
|
||||||
"Kimbra",
|
"Cronus",
|
||||||
"Kodi",
|
"Czar",
|
||||||
"Lady",
|
"Dakota",
|
||||||
"Lakota",
|
"Dash",
|
||||||
"Larka",
|
"Diego",
|
||||||
"Leah",
|
"Dire",
|
||||||
"Leto",
|
"Duke",
|
||||||
"Lobo",
|
"Echo",
|
||||||
"Loki",
|
"Elda",
|
||||||
"Lotus",
|
"Eskimo",
|
||||||
"Louve",
|
"Essos",
|
||||||
"Lupa",
|
"Frey",
|
||||||
"Major",
|
"Gabu",
|
||||||
"Mathias",
|
"Ghost",
|
||||||
"Moro",
|
"Giro",
|
||||||
"Murdock",
|
"Grey Wind",
|
||||||
"Nomad",
|
"Gunner",
|
||||||
"Okami",
|
"Harou",
|
||||||
"Orbit",
|
"Havoc",
|
||||||
"Palla",
|
"Hera",
|
||||||
"Pyro",
|
"Hunter",
|
||||||
"Radolf",
|
"Inuit",
|
||||||
"Raven",
|
"Jacob",
|
||||||
"Rhea",
|
"Jenna",
|
||||||
"Rider",
|
"Juno",
|
||||||
"Rollo",
|
"Kar",
|
||||||
"Rune",
|
"Khal",
|
||||||
"Sable",
|
"Kiba",
|
||||||
"Saga",
|
"Kimbra",
|
||||||
"Sarge",
|
"Kodi",
|
||||||
"Shiro",
|
"Lady",
|
||||||
"Siku",
|
"Lakota",
|
||||||
"Sky",
|
"Larka",
|
||||||
"Stark",
|
"Leah",
|
||||||
"Storm",
|
"Leto",
|
||||||
"Suki",
|
"Lobo",
|
||||||
"Tala",
|
"Loki",
|
||||||
"Thor",
|
"Lotus",
|
||||||
"Tiva",
|
"Louve",
|
||||||
"Tyr",
|
"Lupa",
|
||||||
"Ubba",
|
"Major",
|
||||||
"Ulva",
|
"Mathias",
|
||||||
"Valor",
|
"Moro",
|
||||||
"Vechro",
|
"Murdock",
|
||||||
"Wolf",
|
"Nomad",
|
||||||
"Wolfgang",
|
"Okami",
|
||||||
"Yara",
|
"Orbit",
|
||||||
"Zeus",
|
"Palla",
|
||||||
"Ziva",
|
"Pyro",
|
||||||
"Zylo"
|
"Radolf",
|
||||||
],
|
"Raven",
|
||||||
"pig": [
|
"Rhea",
|
||||||
"Acorn",
|
"Rider",
|
||||||
"Adeline",
|
"Rollo",
|
||||||
"Ajna",
|
"Rune",
|
||||||
"Athena",
|
"Sable",
|
||||||
"Avacado",
|
"Saga",
|
||||||
"Babe",
|
"Sarge",
|
||||||
"Bella",
|
"Shiro",
|
||||||
"Buddy",
|
"Siku",
|
||||||
"Buttons",
|
"Sky",
|
||||||
"Charlie",
|
"Stark",
|
||||||
"Charlotte",
|
"Storm",
|
||||||
"Chubbs",
|
"Suki",
|
||||||
"Cinnamon",
|
"Tala",
|
||||||
"Clarence",
|
"Thor",
|
||||||
"Clover",
|
"Tiva",
|
||||||
"Cookie",
|
"Tyr",
|
||||||
"Corky",
|
"Ubba",
|
||||||
"Cupcake",
|
"Ulva",
|
||||||
"Daisy",
|
"Valor",
|
||||||
"Dani",
|
"Vechro",
|
||||||
"Delilah",
|
"Wolf",
|
||||||
"Dexter",
|
"Wolfgang",
|
||||||
"Dolly",
|
"Yara",
|
||||||
"Dottie",
|
"Zeus",
|
||||||
"Dudley",
|
"Ziva",
|
||||||
"Ellie",
|
"Zylo"
|
||||||
"Erwin",
|
]
|
||||||
"Evie",
|
},
|
||||||
"Gertrude",
|
"species": {
|
||||||
"Gilly",
|
"wolf": {
|
||||||
"Ginger",
|
"generic": "Wolf"
|
||||||
"Gizmo",
|
},
|
||||||
"Gwenivere",
|
"saber": {
|
||||||
"Hogrid",
|
"generic": "Sabertooth Tiger"
|
||||||
"Hazel",
|
},
|
||||||
"Hector",
|
"viper": {
|
||||||
"Herman",
|
"generic": "Lizard"
|
||||||
"Hermione",
|
},
|
||||||
"Hoover",
|
"tuskram": {
|
||||||
"Huck",
|
"generic": "Tusk Ram"
|
||||||
"Iggy",
|
},
|
||||||
"Jake",
|
"alligator": {
|
||||||
"Josie",
|
"generic": "Alligator"
|
||||||
"Leonardo",
|
},
|
||||||
"Lily",
|
"monitor": {
|
||||||
"Lola",
|
"generic": "Monitor Lizard"
|
||||||
"Lottie",
|
},
|
||||||
"Lucy",
|
"lion": {
|
||||||
"Lulu",
|
"generic": "Lion"
|
||||||
"Mabel",
|
},
|
||||||
"Madeline",
|
"tarasque": {
|
||||||
"Maisie",
|
"generic": "Tarasque"
|
||||||
"Millie",
|
}
|
||||||
"Mimzy",
|
}
|
||||||
"Nooch",
|
},
|
||||||
"Nutmeg",
|
"quadruped_small": {
|
||||||
"Oinkers",
|
"body": {
|
||||||
"Okja",
|
"keyword": "pig",
|
||||||
"Oliver",
|
"names": [
|
||||||
"Olivia",
|
"Acorn",
|
||||||
"Panda",
|
"Adeline",
|
||||||
"Pasley",
|
"Ajna",
|
||||||
"Peanut",
|
"Athena",
|
||||||
"Penelope",
|
"Avacado",
|
||||||
"Peppa",
|
"Babe",
|
||||||
"Petunia",
|
"Bella",
|
||||||
"Phoebe",
|
"Buddy",
|
||||||
"Piggie Smalls",
|
"Buttons",
|
||||||
"Piggles",
|
"Charlie",
|
||||||
"Piglet",
|
"Charlotte",
|
||||||
"Pinto Bean",
|
"Chubbs",
|
||||||
"Piper",
|
"Cinnamon",
|
||||||
"Poly",
|
"Clarence",
|
||||||
"Popcorn",
|
"Clover",
|
||||||
"Poppy",
|
"Cookie",
|
||||||
"Punky",
|
"Corky",
|
||||||
"Rey",
|
"Cupcake",
|
||||||
"Rooter",
|
"Daisy",
|
||||||
"Rosie",
|
"Dani",
|
||||||
"Ruby",
|
"Delilah",
|
||||||
"Sadie",
|
"Dexter",
|
||||||
"Scouter",
|
"Dolly",
|
||||||
"Skittles",
|
"Dottie",
|
||||||
"Snowball",
|
"Dudley",
|
||||||
"Snuffles",
|
"Ellie",
|
||||||
"Sonny",
|
"Erwin",
|
||||||
"Sprout",
|
"Evie",
|
||||||
"Squiggles",
|
"Gertrude",
|
||||||
"Sweetie Pie",
|
"Gilly",
|
||||||
"Theo",
|
"Ginger",
|
||||||
"Toffuti",
|
"Gizmo",
|
||||||
"Trixie",
|
"Gwenivere",
|
||||||
"Violet",
|
"Hogrid",
|
||||||
"Vishnu",
|
"Hazel",
|
||||||
"Wee Wee",
|
"Hector",
|
||||||
"Wilbur",
|
"Herman",
|
||||||
"Willow",
|
"Hermione",
|
||||||
"Winnie",
|
"Hoover",
|
||||||
"Wrinkles",
|
"Huck",
|
||||||
"Ziggy",
|
"Iggy",
|
||||||
"Zoe",
|
"Jake",
|
||||||
"Zoinks"
|
"Josie",
|
||||||
],
|
"Leonardo",
|
||||||
"duck": [
|
"Lily",
|
||||||
"ducky"
|
"Lola",
|
||||||
],
|
"Lottie",
|
||||||
"giant": [
|
"Lucy",
|
||||||
"leroybrown"
|
"Lulu",
|
||||||
],
|
"Mabel",
|
||||||
"rat": [
|
"Madeline",
|
||||||
"rat"
|
"Maisie",
|
||||||
]
|
"Millie",
|
||||||
|
"Mimzy",
|
||||||
|
"Nooch",
|
||||||
|
"Nutmeg",
|
||||||
|
"Oinkers",
|
||||||
|
"Okja",
|
||||||
|
"Oliver",
|
||||||
|
"Olivia",
|
||||||
|
"Panda",
|
||||||
|
"Pasley",
|
||||||
|
"Peanut",
|
||||||
|
"Penelope",
|
||||||
|
"Peppa",
|
||||||
|
"Petunia",
|
||||||
|
"Phoebe",
|
||||||
|
"Piggie Smalls",
|
||||||
|
"Piggles",
|
||||||
|
"Piglet",
|
||||||
|
"Pinto Bean",
|
||||||
|
"Piper",
|
||||||
|
"Poly",
|
||||||
|
"Popcorn",
|
||||||
|
"Poppy",
|
||||||
|
"Punky",
|
||||||
|
"Rey",
|
||||||
|
"Rooter",
|
||||||
|
"Rosie",
|
||||||
|
"Ruby",
|
||||||
|
"Sadie",
|
||||||
|
"Scouter",
|
||||||
|
"Skittles",
|
||||||
|
"Snowball",
|
||||||
|
"Snuffles",
|
||||||
|
"Sonny",
|
||||||
|
"Sprout",
|
||||||
|
"Squiggles",
|
||||||
|
"Sweetie Pie",
|
||||||
|
"Theo",
|
||||||
|
"Toffuti",
|
||||||
|
"Trixie",
|
||||||
|
"Violet",
|
||||||
|
"Vishnu",
|
||||||
|
"Wee Wee",
|
||||||
|
"Wilbur",
|
||||||
|
"Willow",
|
||||||
|
"Winnie",
|
||||||
|
"Wrinkles",
|
||||||
|
"Ziggy",
|
||||||
|
"Zoe",
|
||||||
|
"Zoinks"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"species": {
|
||||||
|
"pig": {
|
||||||
|
"generic": "Pig"
|
||||||
|
},
|
||||||
|
"fox": {
|
||||||
|
"generic": "Fox"
|
||||||
|
},
|
||||||
|
"sheep": {
|
||||||
|
"generic": "Sheep"
|
||||||
|
},
|
||||||
|
"boar": {
|
||||||
|
"generic": "Boar"
|
||||||
|
},
|
||||||
|
"jackalope": {
|
||||||
|
"generic": "Jackalope"
|
||||||
|
},
|
||||||
|
"skunk": {
|
||||||
|
"generic": "Skunk"
|
||||||
|
},
|
||||||
|
"cat": {
|
||||||
|
"generic": "Cat"
|
||||||
|
},
|
||||||
|
"batfox": {
|
||||||
|
"generic": "Bat Fox"
|
||||||
|
},
|
||||||
|
"raccoon": {
|
||||||
|
"generic": "Raccoon"
|
||||||
|
},
|
||||||
|
"quokka": {
|
||||||
|
"generic": "Quokka"
|
||||||
|
},
|
||||||
|
"dodarock": {
|
||||||
|
"generic": "Dodarock"
|
||||||
|
},
|
||||||
|
"holladon": {
|
||||||
|
"generic": "Holladon"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"bird_medium": {
|
||||||
|
"body": {
|
||||||
|
"keyword": "duck",
|
||||||
|
"names": [
|
||||||
|
"Donald"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"species": {
|
||||||
|
"duck": {
|
||||||
|
"generic": "Duck"
|
||||||
|
},
|
||||||
|
"chicken": {
|
||||||
|
"generic": "Chicken"
|
||||||
|
},
|
||||||
|
"goose": {
|
||||||
|
"generic": "Goose"
|
||||||
|
},
|
||||||
|
"peacock": {
|
||||||
|
"generic": "Peacock"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"biped_large": {
|
||||||
|
"body": {
|
||||||
|
"keyword": "giant",
|
||||||
|
"names": [
|
||||||
|
"Leroy Brown"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"species": {
|
||||||
|
"giant": {
|
||||||
|
"generic": "Giant"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"critter": {
|
||||||
|
"body": {
|
||||||
|
"keyword": "rat",
|
||||||
|
"names": [
|
||||||
|
"Remy"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"species": {
|
||||||
|
"rat": {
|
||||||
|
"generic": "Rat"
|
||||||
|
},
|
||||||
|
"axolotl": {
|
||||||
|
"generic": "Axolotl"
|
||||||
|
},
|
||||||
|
"gecko": {
|
||||||
|
"generic": "Gecko"
|
||||||
|
},
|
||||||
|
"turtle": {
|
||||||
|
"generic": "Turtle"
|
||||||
|
},
|
||||||
|
"squirrel": {
|
||||||
|
"generic": "Squirrel"
|
||||||
|
},
|
||||||
|
"fungome": {
|
||||||
|
"generic": "Fungome"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
@ -9,6 +9,7 @@ use log::error;
|
|||||||
use serde_json::Value;
|
use serde_json::Value;
|
||||||
use std::{
|
use std::{
|
||||||
any::Any,
|
any::Any,
|
||||||
|
fmt,
|
||||||
fs::{self, File, ReadDir},
|
fs::{self, File, ReadDir},
|
||||||
io::{BufReader, Read},
|
io::{BufReader, Read},
|
||||||
path::PathBuf,
|
path::PathBuf,
|
||||||
@ -18,12 +19,27 @@ use std::{
|
|||||||
/// The error returned by asset loading functions
|
/// The error returned by asset loading functions
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
|
/// An internal error occurred.
|
||||||
|
Internal(Arc<dyn std::error::Error>),
|
||||||
/// An asset of a different type has already been loaded with this specifier.
|
/// An asset of a different type has already been loaded with this specifier.
|
||||||
InvalidType,
|
InvalidType,
|
||||||
/// Asset does not exist.
|
/// Asset does not exist.
|
||||||
NotFound(String),
|
NotFound(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for Error {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
match self {
|
||||||
|
Error::Internal(err) => err.fmt(f),
|
||||||
|
Error::InvalidType => write!(
|
||||||
|
f,
|
||||||
|
"an asset of a different type has already been loaded with this specifier."
|
||||||
|
),
|
||||||
|
Error::NotFound(s) => write!(f, "{}", s),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl From<Arc<dyn Any + 'static + Sync + Send>> for Error {
|
impl From<Arc<dyn Any + 'static + Sync + Send>> for Error {
|
||||||
fn from(_: Arc<dyn Any + 'static + Sync + Send>) -> Self {
|
fn from(_: Arc<dyn Any + 'static + Sync + Send>) -> Self {
|
||||||
Error::InvalidType
|
Error::InvalidType
|
||||||
@ -32,7 +48,7 @@ impl From<Arc<dyn Any + 'static + Sync + Send>> for Error {
|
|||||||
|
|
||||||
impl From<std::io::Error> for Error {
|
impl From<std::io::Error> for Error {
|
||||||
fn from(err: std::io::Error) -> Self {
|
fn from(err: std::io::Error) -> Self {
|
||||||
Error::NotFound(format!("{:?}", err))
|
Error::NotFound(format!("{}", err))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -135,7 +151,12 @@ pub fn load_cloned<A: Asset + Clone + 'static>(specifier: &str) -> Result<A, Err
|
|||||||
/// let my_image = assets::load_expect::<DynamicImage>("core.ui.backgrounds.city");
|
/// let my_image = assets::load_expect::<DynamicImage>("core.ui.backgrounds.city");
|
||||||
/// ```
|
/// ```
|
||||||
pub fn load_expect<A: Asset + 'static>(specifier: &str) -> Arc<A> {
|
pub fn load_expect<A: Asset + 'static>(specifier: &str) -> Arc<A> {
|
||||||
load(specifier).unwrap_or_else(|_| panic!("Failed loading essential asset: {}", specifier))
|
load(specifier).unwrap_or_else(|err| {
|
||||||
|
panic!(
|
||||||
|
"Failed loading essential asset: {} (error={})",
|
||||||
|
specifier, err
|
||||||
|
)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Function used to load essential assets from the filesystem or the cache and return a clone. It will panic if the asset is not found.
|
/// Function used to load essential assets from the filesystem or the cache and return a clone. It will panic if the asset is not found.
|
||||||
|
@ -10,8 +10,13 @@ pub mod object;
|
|||||||
pub mod quadruped_medium;
|
pub mod quadruped_medium;
|
||||||
pub mod quadruped_small;
|
pub mod quadruped_small;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
assets::{self, Asset},
|
||||||
|
npc::NpcKind,
|
||||||
|
};
|
||||||
use specs::{Component, FlaggedStorage};
|
use specs::{Component, FlaggedStorage};
|
||||||
use specs_idvs::IDVStorage;
|
use specs_idvs::IDVStorage;
|
||||||
|
use std::{fs::File, io::BufReader, sync::Arc};
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||||
#[repr(u32)]
|
#[repr(u32)]
|
||||||
@ -29,6 +34,58 @@ pub enum Body {
|
|||||||
Critter(critter::Body) = 10,
|
Critter(critter::Body) = 10,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Data representing data generic to the body together with per-species data.
|
||||||
|
///
|
||||||
|
/// NOTE: Deliberately don't (yet?) implement serialize.
|
||||||
|
#[derive(Clone, Debug, Deserialize)]
|
||||||
|
pub struct BodyData<BodyMeta, SpeciesData> {
|
||||||
|
/// Shared metadata for this whole body type.
|
||||||
|
pub body: BodyMeta,
|
||||||
|
/// All the metadata for species with this body type.
|
||||||
|
pub species: SpeciesData,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Metadata intended to be stored per-body, together with data intended to be stored for each
|
||||||
|
/// species for each body.
|
||||||
|
///
|
||||||
|
/// NOTE: Deliberately don't (yet?) implement serialize.
|
||||||
|
#[derive(Clone, Debug, Deserialize)]
|
||||||
|
pub struct AllBodies<BodyMeta, SpeciesMeta> {
|
||||||
|
pub humanoid: BodyData<BodyMeta, humanoid::AllSpecies<SpeciesMeta>>,
|
||||||
|
pub quadruped_small: BodyData<BodyMeta, quadruped_small::AllSpecies<SpeciesMeta>>,
|
||||||
|
pub quadruped_medium: BodyData<BodyMeta, quadruped_medium::AllSpecies<SpeciesMeta>>,
|
||||||
|
pub bird_medium: BodyData<BodyMeta, bird_medium::AllSpecies<SpeciesMeta>>,
|
||||||
|
pub biped_large: BodyData<BodyMeta, biped_large::AllSpecies<SpeciesMeta>>,
|
||||||
|
pub critter: BodyData<BodyMeta, critter::AllSpecies<SpeciesMeta>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Can only retrieve body metadata by direct index.
|
||||||
|
impl<BodyMeta, SpeciesMeta> core::ops::Index<NpcKind> for AllBodies<BodyMeta, SpeciesMeta> {
|
||||||
|
type Output = BodyMeta;
|
||||||
|
|
||||||
|
fn index(&self, index: NpcKind) -> &Self::Output {
|
||||||
|
match index {
|
||||||
|
NpcKind::Humanoid => &self.humanoid.body,
|
||||||
|
NpcKind::Pig => &self.quadruped_small.body,
|
||||||
|
NpcKind::Wolf => &self.quadruped_medium.body,
|
||||||
|
NpcKind::Duck => &self.bird_medium.body,
|
||||||
|
NpcKind::Giant => &self.biped_large.body,
|
||||||
|
NpcKind::Rat => &self.critter.body,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<
|
||||||
|
BodyMeta: Send + Sync + for<'de> serde::Deserialize<'de>,
|
||||||
|
SpeciesMeta: Send + Sync + for<'de> serde::Deserialize<'de>,
|
||||||
|
> Asset for AllBodies<BodyMeta, SpeciesMeta>
|
||||||
|
{
|
||||||
|
const ENDINGS: &'static [&'static str] = &["json"];
|
||||||
|
fn parse(buf_reader: BufReader<File>) -> Result<Self, assets::Error> {
|
||||||
|
serde_json::de::from_reader(buf_reader).map_err(|e| assets::Error::Internal(Arc::new(e)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Body {
|
impl Body {
|
||||||
pub fn is_humanoid(&self) -> bool {
|
pub fn is_humanoid(&self) -> bool {
|
||||||
match self {
|
match self {
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
use rand::{seq::SliceRandom, thread_rng};
|
use rand::{seq::SliceRandom, thread_rng};
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||||
#[repr(C)]
|
|
||||||
pub struct Body {
|
pub struct Body {
|
||||||
pub species: Species,
|
pub species: Species,
|
||||||
pub body_type: BodyType,
|
pub body_type: BodyType,
|
||||||
@ -21,6 +20,25 @@ impl Body {
|
|||||||
pub enum Species {
|
pub enum Species {
|
||||||
Giant = 0,
|
Giant = 0,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Data representing per-species generic data.
|
||||||
|
///
|
||||||
|
/// NOTE: Deliberately don't (yet?) implement serialize.
|
||||||
|
#[derive(Clone, Debug, Deserialize)]
|
||||||
|
pub struct AllSpecies<SpeciesMeta> {
|
||||||
|
pub giant: SpeciesMeta,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<SpeciesMeta> core::ops::Index<Species> for AllSpecies<SpeciesMeta> {
|
||||||
|
type Output = SpeciesMeta;
|
||||||
|
|
||||||
|
fn index(&self, index: Species) -> &Self::Output {
|
||||||
|
match index {
|
||||||
|
Species::Giant => &self.giant,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub const ALL_SPECIES: [Species; 1] = [Species::Giant];
|
pub const ALL_SPECIES: [Species; 1] = [Species::Giant];
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
use rand::{seq::SliceRandom, thread_rng};
|
use rand::{seq::SliceRandom, thread_rng};
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||||
#[repr(C)]
|
|
||||||
pub struct Body {
|
pub struct Body {
|
||||||
pub species: Species,
|
pub species: Species,
|
||||||
pub body_type: BodyType,
|
pub body_type: BodyType,
|
||||||
@ -24,6 +23,31 @@ pub enum Species {
|
|||||||
Goose = 2,
|
Goose = 2,
|
||||||
Peacock = 3,
|
Peacock = 3,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Data representing per-species generic data.
|
||||||
|
///
|
||||||
|
/// NOTE: Deliberately don't (yet?) implement serialize.
|
||||||
|
#[derive(Clone, Debug, Deserialize)]
|
||||||
|
pub struct AllSpecies<SpeciesMeta> {
|
||||||
|
pub duck: SpeciesMeta,
|
||||||
|
pub chicken: SpeciesMeta,
|
||||||
|
pub goose: SpeciesMeta,
|
||||||
|
pub peacock: SpeciesMeta,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<SpeciesMeta> core::ops::Index<Species> for AllSpecies<SpeciesMeta> {
|
||||||
|
type Output = SpeciesMeta;
|
||||||
|
|
||||||
|
fn index(&self, index: Species) -> &Self::Output {
|
||||||
|
match index {
|
||||||
|
Species::Duck => &self.duck,
|
||||||
|
Species::Chicken => &self.chicken,
|
||||||
|
Species::Goose => &self.goose,
|
||||||
|
Species::Peacock => &self.peacock,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub const ALL_SPECIES: [Species; 4] = [
|
pub const ALL_SPECIES: [Species; 4] = [
|
||||||
Species::Duck,
|
Species::Duck,
|
||||||
Species::Chicken,
|
Species::Chicken,
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
use rand::{seq::SliceRandom, thread_rng};
|
use rand::{seq::SliceRandom, thread_rng};
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||||
#[repr(C)]
|
|
||||||
pub struct Body {
|
pub struct Body {
|
||||||
pub head: Head,
|
pub head: Head,
|
||||||
pub torso: Torso,
|
pub torso: Torso,
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
use rand::{seq::SliceRandom, thread_rng};
|
use rand::{seq::SliceRandom, thread_rng};
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||||
#[repr(C)]
|
|
||||||
pub struct Body {
|
pub struct Body {
|
||||||
pub species: Species,
|
pub species: Species,
|
||||||
pub body_type: BodyType,
|
pub body_type: BodyType,
|
||||||
@ -26,6 +25,33 @@ pub enum Species {
|
|||||||
Squirrel = 4,
|
Squirrel = 4,
|
||||||
Fungome = 5,
|
Fungome = 5,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Data representing per-species generic data.
|
||||||
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||||
|
pub struct AllSpecies<SpeciesMeta> {
|
||||||
|
pub rat: SpeciesMeta,
|
||||||
|
pub axolotl: SpeciesMeta,
|
||||||
|
pub gecko: SpeciesMeta,
|
||||||
|
pub turtle: SpeciesMeta,
|
||||||
|
pub squirrel: SpeciesMeta,
|
||||||
|
pub fungome: SpeciesMeta,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<SpeciesMeta> core::ops::Index<Species> for AllSpecies<SpeciesMeta> {
|
||||||
|
type Output = SpeciesMeta;
|
||||||
|
|
||||||
|
fn index(&self, index: Species) -> &Self::Output {
|
||||||
|
match index {
|
||||||
|
Species::Rat => &self.rat,
|
||||||
|
Species::Axolotl => &self.axolotl,
|
||||||
|
Species::Gecko => &self.gecko,
|
||||||
|
Species::Turtle => &self.turtle,
|
||||||
|
Species::Squirrel => &self.squirrel,
|
||||||
|
Species::Fungome => &self.fungome,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub const ALL_SPECIES: [Species; 6] = [
|
pub const ALL_SPECIES: [Species; 6] = [
|
||||||
Species::Rat,
|
Species::Rat,
|
||||||
Species::Axolotl,
|
Species::Axolotl,
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
use rand::{seq::SliceRandom, thread_rng};
|
use rand::{seq::SliceRandom, thread_rng};
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||||
#[repr(C)]
|
|
||||||
pub struct Body {
|
pub struct Body {
|
||||||
pub head: Head,
|
pub head: Head,
|
||||||
pub chest_front: ChestFront,
|
pub chest_front: ChestFront,
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
use rand::{seq::SliceRandom, thread_rng};
|
use rand::{seq::SliceRandom, thread_rng};
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||||
#[repr(C)]
|
|
||||||
pub struct Body {
|
pub struct Body {
|
||||||
pub head: Head,
|
pub head: Head,
|
||||||
pub torso: Torso,
|
pub torso: Torso,
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
use rand::{seq::SliceRandom, thread_rng};
|
use rand::{seq::SliceRandom, thread_rng};
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||||
#[repr(C)]
|
|
||||||
pub struct Body {
|
pub struct Body {
|
||||||
pub torso: Torso,
|
pub torso: Torso,
|
||||||
pub tail: Tail,
|
pub tail: Tail,
|
||||||
|
@ -1,77 +0,0 @@
|
|||||||
use rand::{seq::SliceRandom, thread_rng};
|
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
|
||||||
#[repr(C)]
|
|
||||||
pub struct Body {
|
|
||||||
pub head: Head,
|
|
||||||
pub shoulder: Shoulder,
|
|
||||||
pub chest: Chest,
|
|
||||||
pub hand: Hand,
|
|
||||||
pub belt: Belt,
|
|
||||||
pub pants: Pants,
|
|
||||||
pub foot: Foot,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Body {
|
|
||||||
pub fn random() -> Self {
|
|
||||||
let mut rng = thread_rng();
|
|
||||||
Self {
|
|
||||||
head: *(&ALL_HEADS).choose(&mut rng).unwrap(),
|
|
||||||
shoulder: *(&ALL_SHOULDERS).choose(&mut rng).unwrap(),
|
|
||||||
chest: *(&ALL_CHESTS).choose(&mut rng).unwrap(),
|
|
||||||
hand: *(&ALL_HANDS).choose(&mut rng).unwrap(),
|
|
||||||
belt: *(&ALL_BELTS).choose(&mut rng).unwrap(),
|
|
||||||
pants: *(&ALL_PANTS).choose(&mut rng).unwrap(),
|
|
||||||
foot: *(&ALL_FEET).choose(&mut rng).unwrap(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
|
||||||
#[repr(u32)]
|
|
||||||
pub enum Head {
|
|
||||||
Default,
|
|
||||||
}
|
|
||||||
const ALL_HEADS: [Head; 1] = [Head::Default];
|
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
|
||||||
#[repr(u32)]
|
|
||||||
pub enum Shoulder {
|
|
||||||
Default,
|
|
||||||
}
|
|
||||||
const ALL_SHOULDERS: [Shoulder; 1] = [Shoulder::Default];
|
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
|
||||||
#[repr(u32)]
|
|
||||||
pub enum Chest {
|
|
||||||
Default,
|
|
||||||
}
|
|
||||||
const ALL_CHESTS: [Chest; 1] = [Chest::Default];
|
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
|
||||||
#[repr(u32)]
|
|
||||||
pub enum Hand {
|
|
||||||
Default,
|
|
||||||
}
|
|
||||||
const ALL_HANDS: [Hand; 1] = [Hand::Default];
|
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
|
||||||
#[repr(u32)]
|
|
||||||
pub enum Belt {
|
|
||||||
Default,
|
|
||||||
}
|
|
||||||
const ALL_BELTS: [Belt; 1] = [Belt::Default];
|
|
||||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
|
||||||
#[repr(u32)]
|
|
||||||
pub enum Pants {
|
|
||||||
Default,
|
|
||||||
}
|
|
||||||
const ALL_FEET: [Foot; 1] = [Foot::Default];
|
|
||||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
|
||||||
#[repr(u32)]
|
|
||||||
pub enum Foot {
|
|
||||||
Default,
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -2,7 +2,6 @@ use rand::{seq::SliceRandom, thread_rng, Rng};
|
|||||||
use vek::Rgb;
|
use vek::Rgb;
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||||
#[repr(C)]
|
|
||||||
pub struct Body {
|
pub struct Body {
|
||||||
pub race: Race,
|
pub race: Race,
|
||||||
pub body_type: BodyType,
|
pub body_type: BodyType,
|
||||||
@ -69,6 +68,35 @@ pub enum Race {
|
|||||||
Orc = 4,
|
Orc = 4,
|
||||||
Undead = 5,
|
Undead = 5,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Data representing per-species generic data.
|
||||||
|
///
|
||||||
|
/// NOTE: Deliberately don't (yet?) implement serialize.
|
||||||
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||||
|
pub struct AllSpecies<SpeciesMeta> {
|
||||||
|
pub danari: SpeciesMeta,
|
||||||
|
pub dwarf: SpeciesMeta,
|
||||||
|
pub elf: SpeciesMeta,
|
||||||
|
pub human: SpeciesMeta,
|
||||||
|
pub orc: SpeciesMeta,
|
||||||
|
pub undead: SpeciesMeta,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<SpeciesMeta> core::ops::Index<Race> for AllSpecies<SpeciesMeta> {
|
||||||
|
type Output = SpeciesMeta;
|
||||||
|
|
||||||
|
fn index(&self, index: Race) -> &Self::Output {
|
||||||
|
match index {
|
||||||
|
Race::Danari => &self.danari,
|
||||||
|
Race::Dwarf => &self.dwarf,
|
||||||
|
Race::Elf => &self.elf,
|
||||||
|
Race::Human => &self.human,
|
||||||
|
Race::Orc => &self.orc,
|
||||||
|
Race::Undead => &self.undead,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub const ALL_RACES: [Race; 6] = [
|
pub const ALL_RACES: [Race; 6] = [
|
||||||
Race::Danari,
|
Race::Danari,
|
||||||
Race::Dwarf,
|
Race::Dwarf,
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
use rand::{seq::SliceRandom, thread_rng};
|
use rand::{seq::SliceRandom, thread_rng};
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||||
#[repr(C)]
|
|
||||||
pub struct Body {
|
pub struct Body {
|
||||||
pub species: Species,
|
pub species: Species,
|
||||||
pub body_type: BodyType,
|
pub body_type: BodyType,
|
||||||
@ -28,6 +27,39 @@ pub enum Species {
|
|||||||
Lion = 6,
|
Lion = 6,
|
||||||
Tarasque = 7,
|
Tarasque = 7,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Data representing per-species generic data.
|
||||||
|
///
|
||||||
|
/// NOTE: Deliberately don't (yet?) implement serialize.
|
||||||
|
#[derive(Clone, Debug, Deserialize)]
|
||||||
|
pub struct AllSpecies<SpeciesMeta> {
|
||||||
|
pub wolf: SpeciesMeta,
|
||||||
|
pub saber: SpeciesMeta,
|
||||||
|
pub viper: SpeciesMeta,
|
||||||
|
pub tuskram: SpeciesMeta,
|
||||||
|
pub alligator: SpeciesMeta,
|
||||||
|
pub monitor: SpeciesMeta,
|
||||||
|
pub lion: SpeciesMeta,
|
||||||
|
pub tarasque: SpeciesMeta,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<SpeciesMeta> core::ops::Index<Species> for AllSpecies<SpeciesMeta> {
|
||||||
|
type Output = SpeciesMeta;
|
||||||
|
|
||||||
|
fn index(&self, index: Species) -> &Self::Output {
|
||||||
|
match index {
|
||||||
|
Species::Wolf => &self.wolf,
|
||||||
|
Species::Saber => &self.saber,
|
||||||
|
Species::Viper => &self.viper,
|
||||||
|
Species::Tuskram => &self.tuskram,
|
||||||
|
Species::Alligator => &self.alligator,
|
||||||
|
Species::Monitor => &self.monitor,
|
||||||
|
Species::Lion => &self.lion,
|
||||||
|
Species::Tarasque => &self.tarasque,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub const ALL_SPECIES: [Species; 8] = [
|
pub const ALL_SPECIES: [Species; 8] = [
|
||||||
Species::Wolf,
|
Species::Wolf,
|
||||||
Species::Saber,
|
Species::Saber,
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
use rand::{seq::SliceRandom, thread_rng};
|
use rand::{seq::SliceRandom, thread_rng};
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||||
#[repr(C)]
|
|
||||||
pub struct Body {
|
pub struct Body {
|
||||||
pub species: Species,
|
pub species: Species,
|
||||||
pub body_type: BodyType,
|
pub body_type: BodyType,
|
||||||
@ -32,6 +31,47 @@ pub enum Species {
|
|||||||
Dodarock = 10,
|
Dodarock = 10,
|
||||||
Holladon = 11,
|
Holladon = 11,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Data representing per-species generic data.
|
||||||
|
///
|
||||||
|
/// NOTE: Deliberately don't (yet?) implement serialize.
|
||||||
|
#[derive(Clone, Debug, Deserialize)]
|
||||||
|
pub struct AllSpecies<SpeciesMeta> {
|
||||||
|
pub pig: SpeciesMeta,
|
||||||
|
pub fox: SpeciesMeta,
|
||||||
|
pub sheep: SpeciesMeta,
|
||||||
|
pub boar: SpeciesMeta,
|
||||||
|
pub jackalope: SpeciesMeta,
|
||||||
|
pub skunk: SpeciesMeta,
|
||||||
|
pub cat: SpeciesMeta,
|
||||||
|
pub batfox: SpeciesMeta,
|
||||||
|
pub raccoon: SpeciesMeta,
|
||||||
|
pub quokka: SpeciesMeta,
|
||||||
|
pub dodarock: SpeciesMeta,
|
||||||
|
pub holladon: SpeciesMeta,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<SpeciesMeta> core::ops::Index<Species> for AllSpecies<SpeciesMeta> {
|
||||||
|
type Output = SpeciesMeta;
|
||||||
|
|
||||||
|
fn index(&self, index: Species) -> &Self::Output {
|
||||||
|
match index {
|
||||||
|
Species::Pig => &self.pig,
|
||||||
|
Species::Fox => &self.fox,
|
||||||
|
Species::Sheep => &self.sheep,
|
||||||
|
Species::Boar => &self.boar,
|
||||||
|
Species::Jackalope => &self.jackalope,
|
||||||
|
Species::Skunk => &self.skunk,
|
||||||
|
Species::Cat => &self.cat,
|
||||||
|
Species::Batfox => &self.batfox,
|
||||||
|
Species::Raccoon => &self.raccoon,
|
||||||
|
Species::Quokka => &self.quokka,
|
||||||
|
Species::Dodarock => &self.dodarock,
|
||||||
|
Species::Holladon => &self.holladon,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub const ALL_SPECIES: [Species; 12] = [
|
pub const ALL_SPECIES: [Species; 12] = [
|
||||||
Species::Pig,
|
Species::Pig,
|
||||||
Species::Fox,
|
Species::Fox,
|
||||||
|
@ -19,7 +19,7 @@ pub use admin::Admin;
|
|||||||
pub use agent::{Agent, Alignment};
|
pub use agent::{Agent, Alignment};
|
||||||
pub use body::{
|
pub use body::{
|
||||||
biped_large, bird_medium, bird_small, critter, dragon, fish_medium, fish_small, humanoid,
|
biped_large, bird_medium, bird_small, critter, dragon, fish_medium, fish_small, humanoid,
|
||||||
object, quadruped_medium, quadruped_small, Body,
|
object, quadruped_medium, quadruped_small, AllBodies, Body, BodyData,
|
||||||
};
|
};
|
||||||
pub use character_state::{ActionState, CharacterState, MovementState};
|
pub use character_state::{ActionState, CharacterState, MovementState};
|
||||||
pub use controller::{
|
pub use controller::{
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
use crate::assets;
|
use crate::{assets, comp::AllBodies};
|
||||||
use lazy_static::lazy_static;
|
use lazy_static::lazy_static;
|
||||||
use rand::seq::SliceRandom;
|
use rand::seq::SliceRandom;
|
||||||
use serde_json;
|
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
@ -15,50 +14,61 @@ pub enum NpcKind {
|
|||||||
Rat,
|
Rat,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl NpcKind {
|
pub const ALL_NPCS: [NpcKind; 6] = [
|
||||||
fn as_str(self) -> &'static str {
|
NpcKind::Humanoid,
|
||||||
match self {
|
NpcKind::Wolf,
|
||||||
NpcKind::Humanoid => "humanoid",
|
NpcKind::Pig,
|
||||||
NpcKind::Wolf => "wolf",
|
NpcKind::Duck,
|
||||||
NpcKind::Pig => "pig",
|
NpcKind::Giant,
|
||||||
NpcKind::Duck => "duck",
|
NpcKind::Rat,
|
||||||
NpcKind::Giant => "giant",
|
];
|
||||||
NpcKind::Rat => "rat",
|
|
||||||
}
|
/// Body-specific NPC name metadata.
|
||||||
}
|
///
|
||||||
|
/// NOTE: Deliberately don't (yet?) implement serialize.
|
||||||
|
#[derive(Clone, Debug, Deserialize)]
|
||||||
|
pub struct BodyNames {
|
||||||
|
/// The keyword used to refer to this body type (e.g. via the command console). Should be
|
||||||
|
/// unique per body type.
|
||||||
|
pub keyword: String,
|
||||||
|
/// A list of canonical names for NPCs with this body types (currently used when spawning this
|
||||||
|
/// kind of NPC from the console). Going forward, these names will likely be split up by
|
||||||
|
/// species.
|
||||||
|
pub names: Vec<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Species-specific NPC name metadata.
|
||||||
|
///
|
||||||
|
/// NOTE: Deliberately don't (yet?) implement serialize.
|
||||||
|
#[derive(Clone, Debug, Deserialize)]
|
||||||
|
pub struct SpeciesNames {
|
||||||
|
/// The generic name for NPCs of this species.
|
||||||
|
pub generic: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Type holding configuration data for NPC names.
|
||||||
|
pub type NpcNames = AllBodies<BodyNames, SpeciesNames>;
|
||||||
|
|
||||||
|
lazy_static! {
|
||||||
|
pub static ref NPC_NAMES: Arc<NpcNames> = assets::load_expect("common.npc_names");
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FromStr for NpcKind {
|
impl FromStr for NpcKind {
|
||||||
type Err = ();
|
type Err = ();
|
||||||
|
|
||||||
fn from_str(s: &str) -> Result<Self, ()> {
|
fn from_str(s: &str) -> Result<Self, ()> {
|
||||||
match s {
|
let npc_names_json = &*NPC_NAMES;
|
||||||
"humanoid" => Ok(NpcKind::Humanoid),
|
ALL_NPCS
|
||||||
"wolf" => Ok(NpcKind::Wolf),
|
.iter()
|
||||||
"pig" => Ok(NpcKind::Pig),
|
.copied()
|
||||||
"duck" => Ok(NpcKind::Duck),
|
.find(|&npc| npc_names_json[npc].keyword == s)
|
||||||
"giant" => Ok(NpcKind::Giant),
|
.ok_or(())
|
||||||
"rat" => Ok(NpcKind::Rat),
|
|
||||||
|
|
||||||
_ => Err(()),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
lazy_static! {
|
pub fn get_npc_name(npc_type: NpcKind) -> &'static str {
|
||||||
static ref NPC_NAMES_JSON: Arc<serde_json::Value> = assets::load_expect("common.npc_names");
|
let BodyNames { keyword, names } = &NPC_NAMES[npc_type];
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_npc_name(npc_type: NpcKind) -> String {
|
// If no pretty name is found, fall back to the keyword.
|
||||||
let npc_names = NPC_NAMES_JSON
|
names.choose(&mut rand::thread_rng()).unwrap_or(keyword)
|
||||||
.get(npc_type.as_str())
|
|
||||||
.expect("accessing json using NPC type provided as key")
|
|
||||||
.as_array()
|
|
||||||
.expect("parsing accessed json value into an array");
|
|
||||||
let npc_name = npc_names
|
|
||||||
.choose(&mut rand::thread_rng())
|
|
||||||
.expect("getting a random NPC name")
|
|
||||||
.as_str()
|
|
||||||
.expect("parsing NPC name json value into a &str");
|
|
||||||
String::from(npc_name)
|
|
||||||
}
|
}
|
||||||
|
@ -487,7 +487,7 @@ fn handle_spawn(server: &mut Server, entity: EcsEntity, args: String, action: &C
|
|||||||
.state
|
.state
|
||||||
.create_npc(
|
.create_npc(
|
||||||
pos,
|
pos,
|
||||||
comp::Stats::new(get_npc_name(id), body, None),
|
comp::Stats::new(get_npc_name(id).into(), body, None),
|
||||||
body,
|
body,
|
||||||
)
|
)
|
||||||
.with(comp::Vel(vel))
|
.with(comp::Vel(vel))
|
||||||
|
@ -6,6 +6,7 @@ use common::{
|
|||||||
event::{EventBus, ServerEvent},
|
event::{EventBus, ServerEvent},
|
||||||
generation::EntityKind,
|
generation::EntityKind,
|
||||||
msg::ServerMsg,
|
msg::ServerMsg,
|
||||||
|
npc::{self, NPC_NAMES},
|
||||||
state::TerrainChanges,
|
state::TerrainChanges,
|
||||||
terrain::TerrainGrid,
|
terrain::TerrainGrid,
|
||||||
};
|
};
|
||||||
@ -100,6 +101,15 @@ impl<'a> System<'a> for Sys {
|
|||||||
if let EntityKind::Waypoint = entity.kind {
|
if let EntityKind::Waypoint = entity.kind {
|
||||||
server_emitter.emit(ServerEvent::CreateWaypoint(entity.pos));
|
server_emitter.emit(ServerEvent::CreateWaypoint(entity.pos));
|
||||||
} else {
|
} else {
|
||||||
|
fn get_npc_name<
|
||||||
|
Species,
|
||||||
|
SpeciesData: core::ops::Index<Species, Output = npc::SpeciesNames>,
|
||||||
|
>(
|
||||||
|
body_data: &comp::BodyData<npc::BodyNames, SpeciesData>,
|
||||||
|
species: Species,
|
||||||
|
) -> &str {
|
||||||
|
&body_data.species[species].generic
|
||||||
|
}
|
||||||
const SPAWN_NPCS: &'static [fn() -> (
|
const SPAWN_NPCS: &'static [fn() -> (
|
||||||
String,
|
String,
|
||||||
comp::Body,
|
comp::Body,
|
||||||
@ -107,49 +117,58 @@ impl<'a> System<'a> for Sys {
|
|||||||
comp::Alignment,
|
comp::Alignment,
|
||||||
)] = &[
|
)] = &[
|
||||||
(|| {
|
(|| {
|
||||||
|
let body = comp::humanoid::Body::random();
|
||||||
(
|
(
|
||||||
"Traveler".into(),
|
format!(
|
||||||
comp::Body::Humanoid(comp::humanoid::Body::random()),
|
"{} Traveler",
|
||||||
|
get_npc_name(&NPC_NAMES.humanoid, body.race)
|
||||||
|
),
|
||||||
|
comp::Body::Humanoid(body),
|
||||||
Some(assets::load_expect_cloned("common.items.weapons.staff_1")),
|
Some(assets::load_expect_cloned("common.items.weapons.staff_1")),
|
||||||
comp::Alignment::Npc,
|
comp::Alignment::Npc,
|
||||||
)
|
)
|
||||||
}) as _,
|
}) as _,
|
||||||
(|| {
|
(|| {
|
||||||
|
let body = comp::humanoid::Body::random();
|
||||||
(
|
(
|
||||||
"Bandit".into(),
|
format!("{} Bandit", get_npc_name(&NPC_NAMES.humanoid, body.race)),
|
||||||
comp::Body::Humanoid(comp::humanoid::Body::random()),
|
comp::Body::Humanoid(body),
|
||||||
Some(assets::load_expect_cloned("common.items.weapons.staff_1")),
|
Some(assets::load_expect_cloned("common.items.weapons.staff_1")),
|
||||||
comp::Alignment::Enemy,
|
comp::Alignment::Enemy,
|
||||||
)
|
)
|
||||||
}) as _,
|
}) as _,
|
||||||
(|| {
|
(|| {
|
||||||
|
let body = comp::quadruped_medium::Body::random();
|
||||||
(
|
(
|
||||||
"Wolf".into(),
|
get_npc_name(&NPC_NAMES.quadruped_medium, body.species).into(),
|
||||||
comp::Body::QuadrupedMedium(comp::quadruped_medium::Body::random()),
|
comp::Body::QuadrupedMedium(body),
|
||||||
None,
|
None,
|
||||||
comp::Alignment::Enemy,
|
comp::Alignment::Enemy,
|
||||||
)
|
)
|
||||||
}) as _,
|
}) as _,
|
||||||
(|| {
|
(|| {
|
||||||
|
let body = comp::bird_medium::Body::random();
|
||||||
(
|
(
|
||||||
"Duck".into(),
|
get_npc_name(&NPC_NAMES.bird_medium, body.species).into(),
|
||||||
comp::Body::BirdMedium(comp::bird_medium::Body::random()),
|
comp::Body::BirdMedium(body),
|
||||||
None,
|
None,
|
||||||
comp::Alignment::Wild,
|
comp::Alignment::Wild,
|
||||||
)
|
)
|
||||||
}) as _,
|
}) as _,
|
||||||
(|| {
|
(|| {
|
||||||
|
let body = comp::critter::Body::random();
|
||||||
(
|
(
|
||||||
"Rat".into(),
|
get_npc_name(&NPC_NAMES.critter, body.species).into(),
|
||||||
comp::Body::Critter(comp::critter::Body::random()),
|
comp::Body::Critter(body),
|
||||||
None,
|
None,
|
||||||
comp::Alignment::Wild,
|
comp::Alignment::Wild,
|
||||||
)
|
)
|
||||||
}) as _,
|
}) as _,
|
||||||
(|| {
|
(|| {
|
||||||
|
let body = comp::quadruped_small::Body::random();
|
||||||
(
|
(
|
||||||
"Pig".into(),
|
get_npc_name(&NPC_NAMES.quadruped_small, body.species).into(),
|
||||||
comp::Body::QuadrupedSmall(comp::quadruped_small::Body::random()),
|
comp::Body::QuadrupedSmall(body),
|
||||||
None,
|
None,
|
||||||
comp::Alignment::Wild,
|
comp::Alignment::Wild,
|
||||||
)
|
)
|
||||||
@ -168,10 +187,14 @@ impl<'a> System<'a> for Sys {
|
|||||||
|
|
||||||
if let EntityKind::Boss = entity.kind {
|
if let EntityKind::Boss = entity.kind {
|
||||||
if rand::random::<f32>() < 0.65 {
|
if rand::random::<f32>() < 0.65 {
|
||||||
body = comp::Body::Humanoid(comp::humanoid::Body::random());
|
let body_new = comp::humanoid::Body::random();
|
||||||
|
body = comp::Body::Humanoid(body_new);
|
||||||
alignment = comp::Alignment::Npc;
|
alignment = comp::Alignment::Npc;
|
||||||
stats = comp::Stats::new(
|
stats = comp::Stats::new(
|
||||||
"Fearless Giant".to_string(),
|
format!(
|
||||||
|
"Fearless Giant {}",
|
||||||
|
get_npc_name(&NPC_NAMES.humanoid, body_new.race)
|
||||||
|
),
|
||||||
body,
|
body,
|
||||||
Some(assets::load_expect_cloned("common.items.weapons.hammer_1")),
|
Some(assets::load_expect_cloned("common.items.weapons.hammer_1")),
|
||||||
);
|
);
|
||||||
|
Loading…
Reference in New Issue
Block a user