Dev: Extract map worlds and planes, add extract framework

This commit is contained in:
LmeSzinc 2024-02-07 02:32:40 +08:00
parent 94b7bc4566
commit 1e79353294
9 changed files with 501 additions and 133 deletions

View File

@ -3,26 +3,15 @@ import os
import re import re
import typing as t import typing as t
from collections import defaultdict from collections import defaultdict
from functools import cache, cached_property from functools import cache
from hashlib import md5 from hashlib import md5
from dev_tools.keywords.base import TextMap, UI_LANGUAGES, replace_templates, text_to_variable
from module.base.code_generator import CodeGenerator from module.base.code_generator import CodeGenerator
from module.config.utils import deep_get, read_file from module.config.utils import deep_get, read_file
from module.exception import ScriptError from module.exception import ScriptError
from module.logger import logger from module.logger import logger
UI_LANGUAGES = ['cn', 'cht', 'en', 'jp', 'es']
def text_to_variable(text):
text = re.sub("'s |s' ", '_', text)
text = re.sub('[ \-—:\'/•.]+', '_', text)
text = re.sub(r'[(),#"?!&%*]|</?\w+>', '', text)
# text = re.sub(r'[#_]?\d+(_times?)?', '', text)
text = re.sub(r'<color=#?\w+>', '', text)
text = text.replace('é', 'e')
return text.strip('_')
def dungeon_name(name: str) -> str: def dungeon_name(name: str) -> str:
name = text_to_variable(name) name = text_to_variable(name)
@ -61,72 +50,6 @@ def convert_inner_character_to_keyword(name):
return convert_dict.get(name, name) return convert_dict.get(name, name)
class TextMap:
DATA_FOLDER = ''
def __init__(self, lang: str):
self.lang = lang
def __contains__(self, name: t.Union[int, str]) -> bool:
if isinstance(name, int) or (isinstance(name, str) and name.isdigit()):
return int(name) in self.data
return False
@cached_property
def data(self) -> dict[int, str]:
if not os.path.exists(TextMap.DATA_FOLDER):
logger.critical('`TextMap.DATA_FOLDER` does not exist, please set it to your path to StarRailData')
exit(1)
file = os.path.join(TextMap.DATA_FOLDER, 'TextMap', f'TextMap{self.lang.upper()}.json')
data = {}
for id_, text in read_file(file).items():
text = text.replace('\u00A0', '')
text = text.replace(r'{NICKNAME}', 'Trailblazer')
data[int(id_)] = text
return data
def find(self, name: t.Union[int, str]) -> tuple[int, str]:
"""
Args:
name:
Returns:
text id (hash in TextMap)
text
"""
if isinstance(name, int) or (isinstance(name, str) and name.isdigit()):
name = int(name)
try:
return name, self.data[name]
except KeyError:
pass
name = str(name)
for row_id, row_name in self.data.items():
if row_id >= 0 and row_name == name:
return row_id, row_name
for row_id, row_name in self.data.items():
if row_name == name:
return row_id, row_name
logger.error(f'Cannot find name: "{name}" in language {self.lang}')
return 0, ''
def replace_templates(text: str) -> str:
"""
Replace templates in data to make sure it equals to what is shown in game
Examples:
replace_templates("Complete Echo of War #4 time(s)")
== "Complete Echo of War 1 time(s)"
"""
text = re.sub(r'#4', '1', text)
text = re.sub(r'</?\w+>', '', text)
text = re.sub(r'<color=#?\w+>', '', text)
text = re.sub(r'{.*?}', '', text)
return text
class KeywordExtract: class KeywordExtract:
def __init__(self): def __init__(self):
self.text_map: dict[str, TextMap] = {lang: TextMap(lang) for lang in UI_LANGUAGES} self.text_map: dict[str, TextMap] = {lang: TextMap(lang) for lang in UI_LANGUAGES}
@ -438,32 +361,10 @@ class KeywordExtract:
self.write_keywords(keyword_class=class_name, output_file=output_file) self.write_keywords(keyword_class=class_name, output_file=output_file)
def generate_map_planes(self): def generate_map_planes(self):
planes = { from dev_tools.keywords.map_world import GenerateMapWorld
'Special': ['黑塔的办公室', '锋芒崭露'], GenerateMapWorld()()
'Rogue': [ '区域-战斗', '区域-事件', '区域-遭遇', '区域-休整', '区域-精英', '区域-首领', '区域-交易'], from dev_tools.keywords.map_plane import GenerateMapPlane
'Herta': ['观景车厢', '主控舱段', '基座舱段', '收容舱段', '支援舱段', '禁闭舱段'], GenerateMapPlane()()
'Jarilo': ['行政区', '城郊雪原', '边缘通路', '铁卫禁区', '残响回廊', '永冬岭',
'造物之柱', '旧武器试验场', '磐岩镇', '大矿区', '铆钉镇', '机械聚落'],
'Luofu': ['星槎海中枢', '流云渡', '迴星港', '长乐天', '金人巷', '太卜司',
'工造司', '绥园', '丹鼎司', '鳞渊境'],
}
def text_convert(world_):
def text_convert_wrapper(name):
name = text_to_variable(name).replace('_', '')
name = f'{world_}_{name}'
return name
return text_convert_wrapper
gen = None
for world, plane in planes.items():
self.load_keywords(plane)
gen = self.write_keywords(keyword_class='MapPlane', output_file='',
text_convert=text_convert(world), generator=gen)
gen.write('./tasks/map/keywords/plane.py')
self.load_keywords(['Herta Space Station', 'Jarilo-VI', 'The Xianzhou Luofu'], lang='en')
self.write_keywords(keyword_class='MapWorld', output_file='./tasks/map/keywords/world.py')
def generate_character_keywords(self): def generate_character_keywords(self):
self.load_character_name_keywords() self.load_character_name_keywords()

184
dev_tools/keywords/base.py Normal file
View File

@ -0,0 +1,184 @@
import os
import re
import typing as t
from functools import cached_property
from module.base.code_generator import CodeGenerator
from module.config.utils import read_file
from module.logger import logger
UI_LANGUAGES = ['cn', 'cht', 'en', 'jp', 'es']
class TextMap:
DATA_FOLDER = ''
def __init__(self, lang: str):
self.lang = lang
def __contains__(self, name: t.Union[int, str]) -> bool:
if isinstance(name, int) or (isinstance(name, str) and name.isdigit()):
return int(name) in self.data
return False
@cached_property
def data(self) -> dict[int, str]:
if not os.path.exists(TextMap.DATA_FOLDER):
logger.critical('`TextMap.DATA_FOLDER` does not exist, please set it to your path to StarRailData')
exit(1)
file = os.path.join(TextMap.DATA_FOLDER, 'TextMap', f'TextMap{self.lang.upper()}.json')
data = {}
for id_, text in read_file(file).items():
text = text.replace('\u00A0', '')
text = text.replace(r'{NICKNAME}', 'Trailblazer')
data[int(id_)] = text
return data
def find(self, name: t.Union[int, str]) -> tuple[int, str]:
"""
Args:
name:
Returns:
text id (hash in TextMap)
text
"""
if isinstance(name, int) or (isinstance(name, str) and name.isdigit()):
name = int(name)
try:
return name, self.data[name]
except KeyError:
pass
name = str(name)
for row_id, row_name in self.data.items():
if row_id >= 0 and row_name == name:
return row_id, row_name
for row_id, row_name in self.data.items():
if row_name == name:
return row_id, row_name
logger.error(f'Cannot find name: "{name}" in language {self.lang}')
return 0, ''
def text_to_variable(text):
text = re.sub("'s |s' ", '_', text)
text = re.sub(r'[ \-—:\'/•.]+', '_', text)
text = re.sub(r'[(),#"?!&%*]|</?\w+>', '', text)
# text = re.sub(r'[#_]?\d+(_times?)?', '', text)
text = re.sub(r'<color=#?\w+>', '', text)
text = text.replace('é', 'e')
return text.strip('_')
def replace_templates(text: str) -> str:
"""
Replace templates in data to make sure it equals to what is shown in game
Examples:
replace_templates("Complete Echo of War #4 time(s)")
== "Complete Echo of War 1 time(s)"
"""
text = re.sub(r'#4', '1', text)
text = re.sub(r'</?\w+>', '', text)
text = re.sub(r'<color=#?\w+>', '', text)
text = re.sub(r'{.*?}', '', text)
return text
class GenerateKeyword:
text_map: dict[str, TextMap] = {lang: TextMap(lang) for lang in UI_LANGUAGES}
text_map['cn'] = TextMap('chs')
@staticmethod
def read_file(file: str) -> dict:
"""
Args:
file: ./ExcelOutput/GameplayGuideData.json
Returns:
dict:
"""
file = os.path.join(TextMap.DATA_FOLDER, file)
return read_file(file)
@classmethod
def find_keyword(cls, keyword, lang) -> tuple[int, str]:
"""
Args:
keyword: text string or text id
lang: Language to find
Returns:
text id (hash in TextMap)
text
"""
text_map = cls.text_map[lang]
return text_map.find(keyword)
output_file = ''
def __init__(self):
self.gen = CodeGenerator()
self.keyword_class = self.__class__.__name__.removeprefix('Generate')
self.keyword_index = 0
self.keyword_format = {
'id': 0,
'name': 'Unnamed_Keyword'
}
for lang in UI_LANGUAGES:
self.keyword_format[lang] = ''
def gen_import(self):
self.gen.Import(
f"""
from .classes import {self.keyword_class}
"""
)
def iter_keywords(self) -> t.Iterable[dict]:
"""
Yields
dict: {'text_id': 123456, 'any_attr': 1}
"""
pass
def convert_name(self, text: str, keyword: dict) -> str:
return text_to_variable(text)
def format_keywords(self, keyword: dict) -> dict | None:
base = self.keyword_format.copy()
text_id = keyword.pop('text_id')
if text_id is None:
return
# id
self.keyword_index += 1
base['id'] = self.keyword_index
# Attrs
base.update(keyword)
# Name
_, name = self.find_keyword(text_id, lang='en')
name = self.convert_name(replace_templates(name), keyword=base)
base['name'] = name
# Translations
for lang in UI_LANGUAGES:
value = replace_templates(self.find_keyword(text_id, lang=lang)[1])
base[lang] = value
return base
def generate(self):
self.gen_import()
self.gen.CommentAutoGenerage('dev_tools.keyword_extract')
for keyword in self.iter_keywords():
keyword = self.format_keywords(keyword)
with self.gen.Object(key=keyword['name'], object_class=self.keyword_class):
for key, value in keyword.items():
self.gen.ObjectAttr(key, value)
if self.output_file:
print(f'Write {self.output_file}')
self.gen.write(self.output_file)
def __call__(self, *args, **kwargs):
self.generate()

View File

@ -0,0 +1,70 @@
import typing as t
from dev_tools.keywords.base import GenerateKeyword
from module.base.decorator import cached_property
from module.config.utils import deep_get
class GenerateMapPlane(GenerateKeyword):
output_file = './tasks/map/keywords/plane.py'
@cached_property
def data(self):
return self.read_file('./ExcelOutput/AreaMapConfig.json')
def iter_planes(self) -> t.Iterable[dict]:
for plane_id, data in self.data.items():
plane_id = int(plane_id)
world_id = int(str(plane_id)[-5])
sort_id = int(deep_get(data, 'MenuSortID', 0))
text_id = deep_get(data, 'Name.Hash')
yield dict(
text_id=text_id,
world_id=world_id,
plane_id=plane_id,
sort_id=sort_id,
)
def iter_keywords(self) -> t.Iterable[dict]:
"""
1010201
^^ floor
^^ plane
^ world
"""
def to_id(name):
return self.find_keyword(name, lang='cn')[0]
domains = ['黑塔的办公室', '锋芒崭露']
for index, domain in enumerate(domains):
yield dict(
text_id=to_id(domain),
world_id=-1,
plane_id=index + 1,
)
domains = ['区域-战斗', '区域-事件', '区域-遭遇', '区域-休整', '区域-精英', '区域-首领', '区域-交易']
for index, domain in enumerate(domains):
yield dict(
text_id=to_id(domain),
world_id=-2,
plane_id=index + 1,
)
keywords = sorted(self.iter_planes(), key=lambda x: (x['world_id'], x['sort_id']))
for keyword in keywords:
keyword.pop('sort_id')
yield keyword
def convert_name(self, text: str, keyword: dict) -> str:
text = super().convert_name(text, keyword=keyword)
text = text.replace('_', '')
from tasks.map.keywords import MapWorld
world = MapWorld.find_world_id(keyword['world_id'])
if world is None:
if text.startswith('Domain'):
return f'Rogue_{text}'
else:
return f'Special_{text}'
else:
return f'{world.short_name}_{text}'

View File

@ -0,0 +1,33 @@
import typing as t
from dev_tools.keywords.base import GenerateKeyword
class GenerateMapWorld(GenerateKeyword):
output_file = './tasks/map/keywords/world.py'
def iter_keywords(self) -> t.Iterable[dict]:
def to_id(name):
return self.find_keyword(name, lang='en')[0]
yield dict(
text_id=to_id('Herta Space Station'),
world_id=0,
short_name='Herta'
)
yield dict(
text_id=to_id('Jarilo-VI'),
world_id=1,
short_name='Jarilo'
)
yield dict(
text_id=to_id('The Xianzhou Luofu'),
world_id=2,
short_name='Luofu'
)
yield dict(
text_id=to_id('Penacony'),
world_id=3,
short_name='Penacony'
)

View File

@ -17,11 +17,11 @@ FLOOR_BUTTONS = [FLOOR_1, FLOOR_2, FLOOR_3]
def world_entrance(plane: MapPlane) -> ButtonWrapper: def world_entrance(plane: MapPlane) -> ButtonWrapper:
if plane.is_HertaSpaceStation: if plane.world.is_Herta:
return WORLD_HERTA return WORLD_HERTA
if plane.is_JariloVI: if plane.world.is_Jarilo:
return WORLD_JARILO return WORLD_JARILO
if plane.is_Luofu: if plane.world.is_Luofu:
return WORLD_LUOFU return WORLD_LUOFU
raise ScriptError(f'world_entrance() got unknown plane: {plane}') raise ScriptError(f'world_entrance() got unknown plane: {plane}')

View File

@ -10,6 +10,11 @@ from module.ocr.keyword import Keyword
class MapPlane(Keyword): class MapPlane(Keyword):
instances: ClassVar = {} instances: ClassVar = {}
# 0, 1, 2, 3
world_id: int
# 1010201
plane_id: int
# Map floors, 'F1' by default # Map floors, 'F1' by default
# Example: ['B1', 'F1', 'F2'] # Example: ['B1', 'F1', 'F2']
floors = ['F1'] floors = ['F1']
@ -18,33 +23,27 @@ class MapPlane(Keyword):
# 'top' or 'bottom' # 'top' or 'bottom'
page = 'top' page = 'top'
@classmethod
def find_plane_id(cls, plane_id):
"""
Args:
plane_id:
Returns:
MapPlane: MapPlane object or None
"""
for instance in cls.instances.values():
if instance.plane_id == plane_id:
return instance
return None
@cached_property @cached_property
def world(self) -> str: def world(self) -> "MapWorld":
""" """
Returns: Returns:
str: World name. Note that "Parlor Car" is considered as a plane of Herta. MapWorld: MapWorld object or None
"Herta" for Herta Space Station
"Jarilo" for Jarilo-VI
"Luofu" for The Xianzhou Luofu
"" for unknown
""" """
for world in ['Herta', 'Jarilo', 'Luofu']: return MapWorld.find_world_id(self.world_id)
if self.name.startswith(world):
return world
return ''
@cached_property
def is_HertaSpaceStation(self):
return self.world == 'Herta'
@cached_property
def is_JariloVI(self):
return self.world == 'Jarilo'
@cached_property
def is_Luofu(self):
return self.world == 'Luofu'
@cached_property @cached_property
def has_multiple_floors(self): def has_multiple_floors(self):
@ -135,3 +134,38 @@ class MapPlane(Keyword):
@dataclass(repr=False) @dataclass(repr=False)
class MapWorld(Keyword): class MapWorld(Keyword):
instances: ClassVar = {} instances: ClassVar = {}
# 0, 1, 2, 3
world_id: int
# Herta
short_name: str
@classmethod
def find_world_id(cls, world_id):
"""
Args:
world_id:
Returns:
MapWorld: MapWorld object or None
"""
for instance in cls.instances.values():
if instance.world_id == world_id:
return instance
return None
@cached_property
def is_Herta(self):
return self.short_name == 'Herta'
@cached_property
def is_Jarilo(self):
return self.short_name == 'Jarilo'
@cached_property
def is_Luofu(self):
return self.short_name == 'Luofu'
@cached_property
def is_Penacony(self):
return self.short_name == 'Penacony'

View File

@ -11,6 +11,8 @@ Special_HertaOffice = MapPlane(
en="Herta's Office", en="Herta's Office",
jp='ヘルタのオフィス', jp='ヘルタのオフィス',
es='Oficina de Herta', es='Oficina de Herta',
world_id=-1,
plane_id=1,
) )
Special_AptitudeShowcase = MapPlane( Special_AptitudeShowcase = MapPlane(
id=2, id=2,
@ -20,6 +22,8 @@ Special_AptitudeShowcase = MapPlane(
en='Aptitude Showcase', en='Aptitude Showcase',
jp='躍進する新星', jp='躍進する新星',
es='Demostración de aptitudes', es='Demostración de aptitudes',
world_id=-1,
plane_id=2,
) )
Rogue_DomainCombat = MapPlane( Rogue_DomainCombat = MapPlane(
id=3, id=3,
@ -29,6 +33,8 @@ Rogue_DomainCombat = MapPlane(
en='Domain — Combat', en='Domain — Combat',
jp='エリア-戦闘', jp='エリア-戦闘',
es='Batalla', es='Batalla',
world_id=-2,
plane_id=1,
) )
Rogue_DomainOccurrence = MapPlane( Rogue_DomainOccurrence = MapPlane(
id=4, id=4,
@ -38,6 +44,8 @@ Rogue_DomainOccurrence = MapPlane(
en='Domain — Occurrence', en='Domain — Occurrence',
jp='エリア-イベント', jp='エリア-イベント',
es='Evento', es='Evento',
world_id=-2,
plane_id=2,
) )
Rogue_DomainEncounter = MapPlane( Rogue_DomainEncounter = MapPlane(
id=5, id=5,
@ -47,6 +55,8 @@ Rogue_DomainEncounter = MapPlane(
en='Domain — Encounter', en='Domain — Encounter',
jp='エリア-遭遇', jp='エリア-遭遇',
es='Encuentro', es='Encuentro',
world_id=-2,
plane_id=3,
) )
Rogue_DomainRespite = MapPlane( Rogue_DomainRespite = MapPlane(
id=6, id=6,
@ -56,6 +66,8 @@ Rogue_DomainRespite = MapPlane(
en='Domain — Respite', en='Domain — Respite',
jp='エリア-休憩', jp='エリア-休憩',
es='Reposo', es='Reposo',
world_id=-2,
plane_id=4,
) )
Rogue_DomainElite = MapPlane( Rogue_DomainElite = MapPlane(
id=7, id=7,
@ -65,6 +77,8 @@ Rogue_DomainElite = MapPlane(
en='Domain — Elite', en='Domain — Elite',
jp='エリア-精鋭', jp='エリア-精鋭',
es='Élite', es='Élite',
world_id=-2,
plane_id=5,
) )
Rogue_DomainBoss = MapPlane( Rogue_DomainBoss = MapPlane(
id=8, id=8,
@ -74,6 +88,8 @@ Rogue_DomainBoss = MapPlane(
en='Domain — Boss', en='Domain — Boss',
jp='エリア-ボス', jp='エリア-ボス',
es='Jefe', es='Jefe',
world_id=-2,
plane_id=6,
) )
Rogue_DomainTransaction = MapPlane( Rogue_DomainTransaction = MapPlane(
id=9, id=9,
@ -83,6 +99,8 @@ Rogue_DomainTransaction = MapPlane(
en='Domain — Transaction', en='Domain — Transaction',
jp='エリア-取引', jp='エリア-取引',
es='Transacción', es='Transacción',
world_id=-2,
plane_id=7,
) )
Herta_ParlorCar = MapPlane( Herta_ParlorCar = MapPlane(
id=10, id=10,
@ -92,6 +110,8 @@ Herta_ParlorCar = MapPlane(
en='Parlor Car', en='Parlor Car',
jp='列車のラウンジ', jp='列車のラウンジ',
es='Vagón panorámico', es='Vagón panorámico',
world_id=0,
plane_id=1000001,
) )
Herta_MasterControlZone = MapPlane( Herta_MasterControlZone = MapPlane(
id=11, id=11,
@ -101,6 +121,8 @@ Herta_MasterControlZone = MapPlane(
en='Master Control Zone', en='Master Control Zone',
jp='主制御部分', jp='主制御部分',
es='Zona de mando principal', es='Zona de mando principal',
world_id=0,
plane_id=1000101,
) )
Herta_BaseZone = MapPlane( Herta_BaseZone = MapPlane(
id=12, id=12,
@ -110,6 +132,8 @@ Herta_BaseZone = MapPlane(
en='Base Zone', en='Base Zone',
jp='ベース部分', jp='ベース部分',
es='Zona de la base', es='Zona de la base',
world_id=0,
plane_id=2000101,
) )
Herta_StorageZone = MapPlane( Herta_StorageZone = MapPlane(
id=13, id=13,
@ -119,6 +143,8 @@ Herta_StorageZone = MapPlane(
en='Storage Zone', en='Storage Zone',
jp='収容部分', jp='収容部分',
es='Zona de almacenamiento', es='Zona de almacenamiento',
world_id=0,
plane_id=2000201,
) )
Herta_SupplyZone = MapPlane( Herta_SupplyZone = MapPlane(
id=14, id=14,
@ -128,6 +154,8 @@ Herta_SupplyZone = MapPlane(
en='Supply Zone', en='Supply Zone',
jp='サポート部分', jp='サポート部分',
es='Zona de suministros', es='Zona de suministros',
world_id=0,
plane_id=2000301,
) )
Herta_SeclusionZone = MapPlane( Herta_SeclusionZone = MapPlane(
id=15, id=15,
@ -137,6 +165,8 @@ Herta_SeclusionZone = MapPlane(
en='Seclusion Zone', en='Seclusion Zone',
jp='封鎖部分', jp='封鎖部分',
es='Zona de confinamiento', es='Zona de confinamiento',
world_id=0,
plane_id=2000401,
) )
Jarilo_AdministrativeDistrict = MapPlane( Jarilo_AdministrativeDistrict = MapPlane(
id=16, id=16,
@ -146,6 +176,8 @@ Jarilo_AdministrativeDistrict = MapPlane(
en='Administrative District', en='Administrative District',
jp='行政区', jp='行政区',
es='Distrito administrativo', es='Distrito administrativo',
world_id=1,
plane_id=1010101,
) )
Jarilo_OutlyingSnowPlains = MapPlane( Jarilo_OutlyingSnowPlains = MapPlane(
id=17, id=17,
@ -155,6 +187,8 @@ Jarilo_OutlyingSnowPlains = MapPlane(
en='Outlying Snow Plains', en='Outlying Snow Plains',
jp='郊外雪原', jp='郊外雪原',
es='Llanuras nevadas de las afueras', es='Llanuras nevadas de las afueras',
world_id=1,
plane_id=2010101,
) )
Jarilo_BackwaterPass = MapPlane( Jarilo_BackwaterPass = MapPlane(
id=18, id=18,
@ -164,6 +198,8 @@ Jarilo_BackwaterPass = MapPlane(
en='Backwater Pass', en='Backwater Pass',
jp='外縁通路', jp='外縁通路',
es='Paso del Remanso', es='Paso del Remanso',
world_id=1,
plane_id=2011101,
) )
Jarilo_SilvermaneGuardRestrictedZone = MapPlane( Jarilo_SilvermaneGuardRestrictedZone = MapPlane(
id=19, id=19,
@ -173,6 +209,8 @@ Jarilo_SilvermaneGuardRestrictedZone = MapPlane(
en='Silvermane Guard Restricted Zone', en='Silvermane Guard Restricted Zone',
jp='シルバーメイン禁区', jp='シルバーメイン禁区',
es='Zona restringida de la Guardia Crinargenta', es='Zona restringida de la Guardia Crinargenta',
world_id=1,
plane_id=2013101,
) )
Jarilo_CorridorofFadingEchoes = MapPlane( Jarilo_CorridorofFadingEchoes = MapPlane(
id=20, id=20,
@ -182,6 +220,8 @@ Jarilo_CorridorofFadingEchoes = MapPlane(
en='Corridor of Fading Echoes', en='Corridor of Fading Echoes',
jp='残響回廊', jp='残響回廊',
es='Pasadizo de los ecos apagados', es='Pasadizo de los ecos apagados',
world_id=1,
plane_id=2013201,
) )
Jarilo_EverwinterHill = MapPlane( Jarilo_EverwinterHill = MapPlane(
id=21, id=21,
@ -191,6 +231,8 @@ Jarilo_EverwinterHill = MapPlane(
en='Everwinter Hill', en='Everwinter Hill',
jp='常冬峰', jp='常冬峰',
es='Colina del Siempreinvierno', es='Colina del Siempreinvierno',
world_id=1,
plane_id=2013401,
) )
Jarilo_PillarsofCreation = MapPlane( Jarilo_PillarsofCreation = MapPlane(
id=22, id=22,
@ -200,6 +242,8 @@ Jarilo_PillarsofCreation = MapPlane(
en='Pillars of Creation', en='Pillars of Creation',
jp='造物の柱', jp='造物の柱',
es='Pilares de la Creación', es='Pilares de la Creación',
world_id=1,
plane_id=2013501,
) )
Jarilo_OldWeaponTestingGround = MapPlane( Jarilo_OldWeaponTestingGround = MapPlane(
id=23, id=23,
@ -209,6 +253,8 @@ Jarilo_OldWeaponTestingGround = MapPlane(
en='Old Weapon Testing Ground', en='Old Weapon Testing Ground',
jp='旧武器実験場', jp='旧武器実験場',
es='Antiguo campo de prueba de armas', es='Antiguo campo de prueba de armas',
world_id=1,
plane_id=2013601,
) )
Jarilo_BoulderTown = MapPlane( Jarilo_BoulderTown = MapPlane(
id=24, id=24,
@ -218,6 +264,8 @@ Jarilo_BoulderTown = MapPlane(
en='Boulder Town', en='Boulder Town',
jp='ボルダータウン', jp='ボルダータウン',
es='Villarroca', es='Villarroca',
world_id=1,
plane_id=1010201,
) )
Jarilo_GreatMine = MapPlane( Jarilo_GreatMine = MapPlane(
id=25, id=25,
@ -227,6 +275,8 @@ Jarilo_GreatMine = MapPlane(
en='Great Mine', en='Great Mine',
jp='大鉱区', jp='大鉱区',
es='Mina principal', es='Mina principal',
world_id=1,
plane_id=2012101,
) )
Jarilo_RivetTown = MapPlane( Jarilo_RivetTown = MapPlane(
id=26, id=26,
@ -236,6 +286,8 @@ Jarilo_RivetTown = MapPlane(
en='Rivet Town', en='Rivet Town',
jp='リベットタウン', jp='リベットタウン',
es='Villarremache', es='Villarremache',
world_id=1,
plane_id=2012201,
) )
Jarilo_RobotSettlement = MapPlane( Jarilo_RobotSettlement = MapPlane(
id=27, id=27,
@ -245,6 +297,8 @@ Jarilo_RobotSettlement = MapPlane(
en='Robot Settlement', en='Robot Settlement',
jp='機械集落', jp='機械集落',
es='Asentamiento robot', es='Asentamiento robot',
world_id=1,
plane_id=2012301,
) )
Luofu_CentralStarskiffHaven = MapPlane( Luofu_CentralStarskiffHaven = MapPlane(
id=28, id=28,
@ -254,6 +308,8 @@ Luofu_CentralStarskiffHaven = MapPlane(
en='Central Starskiff Haven', en='Central Starskiff Haven',
jp='星槎海中枢', jp='星槎海中枢',
es='Zona central de la Dársena de astroesquifes', es='Zona central de la Dársena de astroesquifes',
world_id=2,
plane_id=1020101,
) )
Luofu_Cloudford = MapPlane( Luofu_Cloudford = MapPlane(
id=29, id=29,
@ -263,6 +319,8 @@ Luofu_Cloudford = MapPlane(
en='Cloudford', en='Cloudford',
jp='流雲渡し', jp='流雲渡し',
es='Vado de las Nubes', es='Vado de las Nubes',
world_id=2,
plane_id=2021101,
) )
Luofu_StargazerNavalia = MapPlane( Luofu_StargazerNavalia = MapPlane(
id=30, id=30,
@ -272,6 +330,8 @@ Luofu_StargazerNavalia = MapPlane(
en='Stargazer Navalia', en='Stargazer Navalia',
jp='廻星港', jp='廻星港',
es='Puerto Miraestrellas', es='Puerto Miraestrellas',
world_id=2,
plane_id=2021201,
) )
Luofu_ExaltingSanctum = MapPlane( Luofu_ExaltingSanctum = MapPlane(
id=31, id=31,
@ -281,6 +341,8 @@ Luofu_ExaltingSanctum = MapPlane(
en='Exalting Sanctum', en='Exalting Sanctum',
jp='長楽天', jp='長楽天',
es='Sánctum de la Exaltación', es='Sánctum de la Exaltación',
world_id=2,
plane_id=1020201,
) )
Luofu_AurumAlley = MapPlane( Luofu_AurumAlley = MapPlane(
id=32, id=32,
@ -290,6 +352,8 @@ Luofu_AurumAlley = MapPlane(
en='Aurum Alley', en='Aurum Alley',
jp='金人巷', jp='金人巷',
es='Callejón Aurum', es='Callejón Aurum',
world_id=2,
plane_id=1020204,
) )
Luofu_DivinationCommission = MapPlane( Luofu_DivinationCommission = MapPlane(
id=33, id=33,
@ -299,6 +363,8 @@ Luofu_DivinationCommission = MapPlane(
en='Divination Commission', en='Divination Commission',
jp='太卜司', jp='太卜司',
es='Comisión de Adivinación', es='Comisión de Adivinación',
world_id=2,
plane_id=2022101,
) )
Luofu_ArtisanshipCommission = MapPlane( Luofu_ArtisanshipCommission = MapPlane(
id=34, id=34,
@ -308,6 +374,8 @@ Luofu_ArtisanshipCommission = MapPlane(
en='Artisanship Commission', en='Artisanship Commission',
jp='工造司', jp='工造司',
es='Comisión de Artesanía', es='Comisión de Artesanía',
world_id=2,
plane_id=2022201,
) )
Luofu_FyxestrollGarden = MapPlane( Luofu_FyxestrollGarden = MapPlane(
id=35, id=35,
@ -317,6 +385,8 @@ Luofu_FyxestrollGarden = MapPlane(
en='Fyxestroll Garden', en='Fyxestroll Garden',
jp='綏園', jp='綏園',
es='Jardín del Sosiego', es='Jardín del Sosiego',
world_id=2,
plane_id=2022301,
) )
Luofu_AlchemyCommission = MapPlane( Luofu_AlchemyCommission = MapPlane(
id=36, id=36,
@ -326,6 +396,8 @@ Luofu_AlchemyCommission = MapPlane(
en='Alchemy Commission', en='Alchemy Commission',
jp='丹鼎司', jp='丹鼎司',
es='Comisión de Alquimia', es='Comisión de Alquimia',
world_id=2,
plane_id=2023101,
) )
Luofu_ScalegorgeWaterscape = MapPlane( Luofu_ScalegorgeWaterscape = MapPlane(
id=37, id=37,
@ -335,4 +407,61 @@ Luofu_ScalegorgeWaterscape = MapPlane(
en='Scalegorge Waterscape', en='Scalegorge Waterscape',
jp='鱗淵境', jp='鱗淵境',
es='Desfiladero de Escamas', es='Desfiladero de Escamas',
world_id=2,
plane_id=2023201,
)
Penacony_TheReverieReality = MapPlane(
id=38,
name='Penacony_TheReverieReality',
cn='「白日梦」酒店-现实',
cht='「白日夢」飯店-現實',
en='The Reverie (Reality)',
jp='ホテル・レバリー-現実',
es='Hotel Fantasía (realidad)',
world_id=3,
plane_id=1030501,
)
Penacony_GoldenHour = MapPlane(
id=39,
name='Penacony_GoldenHour',
cn='黄金的时刻',
cht='黃金的時刻',
en='Golden Hour',
jp='黄金の刻',
es='Momento Dorado',
world_id=3,
plane_id=1030101,
)
Penacony_DreamEdge = MapPlane(
id=40,
name='Penacony_DreamEdge',
cn='筑梦边境',
cht='築夢邊境',
en="Dream's Edge",
jp='ドリームボーダー',
es='Frontera de los Sueños',
world_id=3,
plane_id=2031301,
)
Penacony_AChildDream = MapPlane(
id=41,
name='Penacony_AChildDream',
cn='稚子的梦',
cht='稚子的夢',
en="A Child's Dream",
jp='稚児の夢',
es='Sueño infantil',
world_id=3,
plane_id=2031201,
)
Penacony_TheReverieDreamscape = MapPlane(
id=42,
name='Penacony_TheReverieDreamscape',
cn='「白日梦」酒店-梦境',
cht='「白日夢」飯店-夢境',
en='The Reverie (Dreamscape)',
jp='ホテル・レバリー-夢境',
es='Hotel Fantasía (paisaje onírico)',
world_id=3,
plane_id=2031101,
) )

View File

@ -11,6 +11,8 @@ Herta_Space_Station = MapWorld(
en='Herta Space Station', en='Herta Space Station',
jp='宇宙ステーション「ヘルタ」', jp='宇宙ステーション「ヘルタ」',
es='Estación Espacial Herta', es='Estación Espacial Herta',
world_id=0,
short_name='Herta',
) )
Jarilo_VI = MapWorld( Jarilo_VI = MapWorld(
id=2, id=2,
@ -20,6 +22,8 @@ Jarilo_VI = MapWorld(
en='Jarilo-VI', en='Jarilo-VI',
jp='ヤリーロ-VI', jp='ヤリーロ-VI',
es='Jarilo-VI', es='Jarilo-VI',
world_id=1,
short_name='Jarilo',
) )
The_Xianzhou_Luofu = MapWorld( The_Xianzhou_Luofu = MapWorld(
id=3, id=3,
@ -29,4 +33,17 @@ The_Xianzhou_Luofu = MapWorld(
en='The Xianzhou Luofu', en='The Xianzhou Luofu',
jp='仙舟「羅浮」', jp='仙舟「羅浮」',
es='El Luofu de Xianzhou', es='El Luofu de Xianzhou',
world_id=2,
short_name='Luofu',
)
Penacony = MapWorld(
id=4,
name='Penacony',
cn='匹诺康尼',
cht='匹諾康尼',
en='Penacony',
jp='ピノコニー',
es='Colonipenal',
world_id=3,
short_name='Penacony',
) )

View File

@ -79,9 +79,9 @@ class MapResource(ResourceConst):
@cached_property @cached_property
def assets_file_basename(self): def assets_file_basename(self):
if self.plane.has_multiple_floors or self.is_special_plane: if self.plane.has_multiple_floors or self.is_special_plane:
return f'./position/{self.plane.world}/{self.plane.name}_{self.floor}' return f'./position/{self.plane.world.short_name}/{self.plane.name}_{self.floor}'
else: else:
return f'./position/{self.plane.world}/{self.plane.name}' return f'./position/{self.plane.world.short_name}/{self.plane.name}'
@cached_property @cached_property
def assets_floor(self): def assets_floor(self):