use std::collections::HashMap;
use std::ops::Index;
use std::path::PathBuf;
use pyo3::exceptions::PyTypeError;
use pyo3::prelude::*;
use crate::devs::Developer;
use crate::locales::Language;
const KS2_MAPS_DIRECTORY: &str = "ks2-maps";
#[pyclass(frozen)]
#[derive(Clone, PartialEq, Debug)]
pub struct Map {
_index: usize,
_name_dict: HashMap<Language, String>,
_galaxy_representation: String,
_custom_file_list: Vec<String>,
_file_path: String,
_original_author: Developer,
_current_author: Option<Developer>,
_is_disliked: bool,
_is_retired: bool,
_is_active: bool
}
impl Map {
pub fn new(
index: usize,
name_dict: HashMap<Language, String>,
galaxy_representation: &str,
custom_file_list: Vec<String>,
file_path: &str,
original_author: Developer,
current_author: Option<Developer>,
is_disliked: bool,
is_retired: bool,
is_active: bool
) -> Self{
Map {
_index: index,
_name_dict: name_dict,
_galaxy_representation: galaxy_representation.to_string(),
_custom_file_list: custom_file_list,
_file_path: file_path.to_string(),
_original_author: original_author,
_current_author: current_author,
_is_disliked: is_disliked,
_is_retired: is_retired,
_is_active: is_active
}
}
pub fn get_index(&self) -> usize {
self._index
}
pub fn get_name(&self, locale: Language) -> Option<&str> {
self._name_dict.get(&locale).map(|s| s.as_str())
}
pub fn get_english_name(&self) -> Option<&str> {
self.get_name(Language::English)
}
pub fn get_galaxy_representation(&self) -> &str {
&self._galaxy_representation
}
pub fn original_author(&self) -> &Developer {
&self._original_author
}
pub fn current_author(&self) -> Option<&Developer> {
self._current_author.as_ref()
}
pub fn file_path(&self) -> &str {
&self._file_path
}
pub fn is_disliked(&self) -> bool {
self._is_disliked
}
pub fn is_active(&self) -> bool {
self._is_active
}
pub fn is_retired(&self) -> bool {
self._is_retired
}
pub fn custom_file_list(&self) -> &[String] {
&self._custom_file_list
}
}
#[pymethods]
impl Map {
#[pyo3(name = "get_index")]
pub fn py_get_index(&self) -> PyResult<usize> {
Ok(self.get_index())
}
#[pyo3(name = "get_name")]
pub fn py_get_name(&self, locale: Language) -> PyResult<Option<&str>> {
Ok(self.get_name(locale))
}
#[pyo3(name = "get_english_name")]
pub fn py_get_english_name(&self) -> PyResult<Option<&str>> {
Ok(self.get_english_name())
}
#[pyo3(name = "get_galaxy_representation")]
pub fn py_get_galaxy_representation(&self) -> PyResult<&str> {
Ok(self.get_galaxy_representation())
}
#[pyo3(name = "original_author")]
pub fn py_original_author(&self) -> PyResult<Developer> {
Ok(*self.original_author())
}
#[pyo3(name = "current_author")]
pub fn py_current_author(&self) -> PyResult<Option<Developer>> {
Ok(self.current_author().copied())
}
#[pyo3(name = "file_path")]
pub fn py_file_path(&self) -> PyResult<&str> {
Ok(self.file_path())
}
#[pyo3(name = "is_disliked")]
pub fn py_is_disliked(&self) -> PyResult<bool> {
Ok(self.is_disliked())
}
#[pyo3(name = "is_active")]
pub fn py_is_active(&self) -> PyResult<bool> {
Ok(self.is_active())
}
#[pyo3(name = "is_retired")]
pub fn py_is_retired(&self) -> PyResult<bool> {
Ok(self.is_retired())
}
#[pyo3(name = "custom_file_list")]
pub fn py_custom_file_list(&self) -> PyResult<&[String]> {
Ok(self.custom_file_list())
}
}
macro_rules! define_maps {
($($key:ident => $id:expr),* $(,)?) => {
#[pyclass(frozen)]
#[derive(Clone)]
pub struct Maps {
$(#[pyo3(get)]
pub $key: Map,)*
_maps: HashMap<String, Map>,
_raw_list: Vec<Map>
}
impl Maps {
pub fn new() -> Self {
let mut hash_map = HashMap::new();
let mut list = Vec::new();
$(
let map = $id;
hash_map.insert(stringify!($key).to_string(), map.clone());
list.push(map);
)*
Self { $($key: hash_map[stringify!($key)].clone(),)* _maps: hash_map, _raw_list: list }
}
pub fn from_index(&self, index: usize) -> &Map{
&self._raw_list[index]
}
}
#[pymethods]
impl Maps {
#[new]
pub fn py_new() -> Self {
Maps::new()
}
fn __iter__(slf: PyRef<Self>) -> PyMapsIter {
PyMapsIter {
inner: slf.clone().into_iter(),
}
}
}
}
}
define_maps!(
Classic => Map::new(
0,
HashMap::from([(Language::English, "Classic".to_string()), (Language::Korean, "기본".to_string()), (Language::Chinese, "经典永存".to_string())]),
"CLASSIC",
vec!(PathBuf::from("Base.SC2Data/GameData/Terrain/Classic.xml").to_str().unwrap().to_string()),
PathBuf::from(format!("{}/{}", KS2_MAPS_DIRECTORY, "classic.SC2Map")).to_str().unwrap(),
Developer::Luminous,
None,
false,
true,
false
),
Duck_Map => Map::new(
1,
HashMap::from([(Language::English, "Duck Map".to_string()), (Language::Korean, "오리".to_string()), (Language::Chinese, "鸭鸭".to_string())]),
"DUCK_MAP",
vec!(PathBuf::from("Base.SC2Data/GameData/Terrain/DuckMap.xml").to_str().unwrap().to_string()),
PathBuf::from(format!("{}/{}", KS2_MAPS_DIRECTORY, "duck_map.SC2Map")).to_str().unwrap(),
Developer::Luminous,
None,
false,
true,
false
),
Zerus_Volcanoes => Map::new(
2,
HashMap::from([(Language::English, "Zerus Volcanoes".to_string()), (Language::Korean, "제루스 화산".to_string()), (Language::Chinese, "泽鲁斯火山".to_string())]),
"ZERUS_VOLCANOES",
vec!(PathBuf::from("Base.SC2Data/GameData/Terrain/ZerusVolcanoes.xml").to_str().unwrap().to_string()),
PathBuf::from(format!("{}/{}", KS2_MAPS_DIRECTORY, "zerus_volcanoes.SC2Map")).to_str().unwrap(),
Developer::Luminous,
None,
false,
true,
false
),
Ruins_Of_Imladoon => Map::new(
3,
HashMap::from([(Language::English, "Ruins of Imladoon".to_string()), (Language::Korean, "임라둔의 잔해".to_string()), (Language::Chinese, "伊姆拉杜恩遗址".to_string())]),
"RUINS_OF_IMLADOON",
vec!(PathBuf::from("Base.SC2Data/GameData/Terrain/RuinsOfImladoon.xml").to_str().unwrap().to_string()),
PathBuf::from(format!("{}/{}", KS2_MAPS_DIRECTORY, "ruins_of_imladoon.SC2Map")).to_str().unwrap(),
Developer::Fatline,
Some(Developer::Templar),
false,
false,
true
),
Four_Seasons => Map::new(
4,
HashMap::from([(Language::English, "Four Seasons".to_string()), (Language::Korean, "사계절".to_string()), (Language::Chinese, "四季交叠".to_string())]),
"FOUR_SEASONS",
vec!(PathBuf::from("Base.SC2Data/GameData/Terrain/FourSeasons.xml").to_str().unwrap().to_string()),
PathBuf::from(format!("{}/{}", KS2_MAPS_DIRECTORY, "four_seasons.SC2Map")).to_str().unwrap(),
Developer::Luminous,
None,
true,
true,
false
),
Heart_Of_Amethyst => Map::new(
5,
HashMap::from([(Language::English, "Heart of Amethyst".to_string()), (Language::Korean, "자수정의 심장".to_string()), (Language::Chinese, "紫水晶之心".to_string())]),
"HEART_OF_AMETHYST",
vec!(PathBuf::from("Base.SC2Data/GameData/Terrain/HeartOfAmethyst.xml").to_str().unwrap().to_string()),
PathBuf::from(format!("{}/{}", KS2_MAPS_DIRECTORY, "heart_of_amethyst.SC2Map")).to_str().unwrap(),
Developer::Luminous,
None,
true,
true,
false
),
Vintage_Shores => Map::new(
6,
HashMap::from([(Language::English, "Vintage Shores".to_string()), (Language::Korean, "빈티지 해변".to_string()), (Language::Chinese, "夏日海滩".to_string())]),
"VINTAGE_SHORES",
vec!(PathBuf::from("Base.SC2Data/GameData/Terrain/VintageShores.xml").to_str().unwrap().to_string()),
PathBuf::from(format!("{}/{}", KS2_MAPS_DIRECTORY, "vintage_shores.SC2Map")).to_str().unwrap(),
Developer::Templar,
None,
true,
true,
false
),
Aiur_Fountains => Map::new(
7,
HashMap::from([
(Language::English, "Aiur Fountains".to_string()),
(Language::Korean, "아이어 분수".to_string()),
(Language::Chinese, "艾尔之泉".to_string())
]),
"VINTAGE_SHORES",
vec![PathBuf::from("Base.SC2Data/GameData/Terrain/VintageShores.xml").to_str().unwrap().to_string()],
PathBuf::from(format!("{}/{}", KS2_MAPS_DIRECTORY, "vintage_shores.SC2Map")).to_str().unwrap(),
Developer::Templar,
None,
true,
true,
false
),
Kaldir_Cliffs => Map::new(
8,
HashMap::from([
(Language::Chinese, "冰封王座".to_string()),
(Language::English, "Kaldir Cliffs".to_string()),
(Language::Korean, "칼디르 절벽".to_string()),
]),
"KALDIR_CLIFFS",
vec![PathBuf::from("Base.SC2Data/GameData/Terrain/KaldirCliffs.xml")
.to_str().unwrap().to_string()],
PathBuf::from(format!("{}/{}", KS2_MAPS_DIRECTORY, "kaldir_cliffs.SC2Map"))
.to_str().unwrap(),
Developer::Understudy,
None,
false,
false,
false
),
Lost_Tides => Map::new(
9,
HashMap::from([
(Language::Chinese, "失意之汐".to_string()),
(Language::English, "Lost Tides".to_string()),
(Language::Korean, "잃어버린 강".to_string()),
]),
"LOST_TIDES",
vec![PathBuf::from("Base.SC2Data/GameData/Terrain/LostTides.xml")
.to_str().unwrap().to_string()],
PathBuf::from(format!("{}/{}", KS2_MAPS_DIRECTORY, "lost_tides.SC2Map"))
.to_str().unwrap(),
Developer::Templar,
None,
true,
false,
false
),
Death_Valley => Map::new(
10,
HashMap::from([
(Language::Chinese, "死亡峡谷".to_string()),
(Language::English, "Death Valley".to_string()),
(Language::Korean, "죽음의 골짜기".to_string()),
]),
"DEATH_VALLEY",
vec![PathBuf::from("Base.SC2Data/GameData/Terrain/DeathValley.xml")
.to_str().unwrap().to_string()],
PathBuf::from(format!("{}/{}", KS2_MAPS_DIRECTORY, "death_valley.SC2Map"))
.to_str().unwrap(),
Developer::Fatline,
None,
true,
false,
true
),
Snow_Prism => Map::new(
11,
HashMap::from([
(Language::Chinese, "".to_string()),
(Language::English, "Snow Prism".to_string()),
(Language::Korean, "눈 프리즘".to_string()),
]),
"SNOW_PRISM",
vec![PathBuf::from("Base.SC2Data/GameData/Terrain/SnowPrism.xml")
.to_str().unwrap().to_string()],
PathBuf::from(format!("{}/{}", KS2_MAPS_DIRECTORY, "snow_prism.SC2Map"))
.to_str().unwrap(),
Developer::Luminous,
None,
true,
false,
false
),
Aduns_Forgotten_Temple => Map::new(
12,
HashMap::from([
(Language::Chinese, "".to_string()),
(Language::English, "Adun's Forgotten Temple".to_string()),
(Language::Korean, "아둔의 잊혀진 사원".to_string()),
]),
"ADUNS_FORGOTTEN_TEMPLE",
vec![],
PathBuf::from(format!("{}/{}", KS2_MAPS_DIRECTORY, "adun_temple.SC2Map"))
.to_str().unwrap(),
Developer::Luminous,
None,
true,
false,
false
),
Maze => Map::new(
13,
HashMap::from([
(Language::Chinese, "".to_string()),
(Language::English, "Maze".to_string()),
(Language::Korean, "미로".to_string()),
]),
"MAZE",
vec![],
PathBuf::from(format!("{}/{}", KS2_MAPS_DIRECTORY, "maze.SC2Map"))
.to_str().unwrap(),
Developer::Azaz,
Some(Developer::Luminous),
true,
false,
false
),
Canal => Map::new(
14,
HashMap::from([
(Language::Chinese, "".to_string()),
(Language::English, "Canal".to_string()),
(Language::Korean, "운하".to_string()),
]),
"CANAL",
vec![],
PathBuf::from(format!("{}/{}", KS2_MAPS_DIRECTORY, "canal.SC2Map"))
.to_str().unwrap(),
Developer::Azaz,
Some(Developer::Luminous),
true,
false,
false
),
Sand_Fort => Map::new(
15,
HashMap::from([
(Language::Chinese, "".to_string()),
(Language::English, "Sand Fort".to_string()),
(Language::Korean, "모래 요새".to_string()),
]),
"SAND_FORT",
vec![PathBuf::from("Base.SC2Data/GameData/Terrain/SandFort.xml")
.to_str().unwrap().to_string()],
PathBuf::from(format!("{}/{}", KS2_MAPS_DIRECTORY, "sand_fort.SC2Map"))
.to_str().unwrap(),
Developer::Azaz,
Some(Developer::Luminous),
true,
false,
false
),
Mar_Sara_Wastelands => Map::new(
16,
HashMap::from([
(Language::Chinese, "".to_string()),
(Language::English, "Mar Sara Wastelands".to_string()),
(Language::Korean, "Mar Sara 황무지".to_string()),
]),
"MAR_SARA_WASTELANDS",
vec![],
PathBuf::from(format!("{}/{}", KS2_MAPS_DIRECTORY, "mar_sara_wastelands.SC2Map"))
.to_str().unwrap(),
Developer::Unknown,
Some(Developer::Luminous),
true,
false,
false
),
Last_Sanctuary => Map::new(
17,
HashMap::from([
(Language::Chinese, "".to_string()),
(Language::English, "Last Sanctuary".to_string()),
(Language::Korean, "최후의 성소".to_string()),
]),
"LAST_SANCTUARY",
vec![PathBuf::from("Base.SC2Data/GameData/Terrain/LastSanctuary.xml")
.to_str().unwrap().to_string()],
PathBuf::from(format!("{}/{}", KS2_MAPS_DIRECTORY, "last_sanctuary.SC2Map"))
.to_str().unwrap(),
Developer::Unknown,
Some(Developer::Feanor),
true,
false,
false
),
No_Hope => Map::new(
18,
HashMap::from([
(Language::Chinese, "".to_string()),
(Language::English, "No Hope".to_string()),
(Language::Korean, "".to_string()),
]),
"NO_HOPE",
vec![PathBuf::from("Base.SC2Data/GameData/Terrain/NoHope.xml")
.to_str().unwrap().to_string()],
PathBuf::from(format!("{}/{}", KS2_MAPS_DIRECTORY, "no_hope.SC2Map"))
.to_str().unwrap(),
Developer::Unknown,
Some(Developer::SmugWyrmling),
false,
false,
false
),
Celestial_Skylines => Map::new(
19,
HashMap::from([
(Language::Chinese, "".to_string()),
(Language::English, "Celestial Skylines".to_string()),
(Language::Korean, "".to_string()),
]),
"CELESTIAL_SKYLINES",
vec![PathBuf::from("Base.SC2Data/GameData/Terrain/CelestialSkylines.xml")
.to_str().unwrap().to_string()],
PathBuf::from(format!("{}/{}", KS2_MAPS_DIRECTORY, "celestial_skylines.SC2Map"))
.to_str().unwrap(),
Developer::Unknown,
Some(Developer::SmugWyrmling),
false,
false,
false
),
);
impl Index<&str> for Maps {
type Output = Map;
fn index(&self, index: &str) -> &Self::Output {
&self._maps[index]
}
}
impl IntoIterator for Maps {
type IntoIter = MapsIntoIter;
type Item = Map;
fn into_iter(self) -> Self::IntoIter {
MapsIntoIter {
maps: self,
index: 0
}
}
}
pub struct MapsIntoIter {
maps: Maps,
index: usize
}
impl Iterator for MapsIntoIter {
type Item = Map;
fn next(&mut self) -> Option<Self::Item> {
if self.index == self.maps._raw_list.len() {
return None;
}
let res = self.maps._raw_list[self.index].clone();
self.index +=1;
Some(res)
}
}
#[pyclass]
struct PyMapsIter {
inner: MapsIntoIter,
}
#[pymethods]
impl PyMapsIter {
fn __iter__(slf: PyRef<Self>) -> PyRef<Self> {
slf
}
fn __next__(mut slf: PyRefMut<Self>) -> Option<Map> {
slf.inner.next()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_iter_order() {
let maps = Maps::new();
let maps2 = Maps::new();
let mut i = 0;
for map in maps {
assert!(map == maps2._raw_list[i]);
i += 1;
}
}
#[test]
fn select_map_direct() {
let maps = Maps::new();
assert!(&maps.Classic == &maps["Classic"]);
assert!(&maps.Duck_Map == &maps["Duck_Map"]);
}
}