diff --git a/titles/chuni/frontend.py b/titles/chuni/frontend.py index 69f1ae9..381c08c 100644 --- a/titles/chuni/frontend.py +++ b/titles/chuni/frontend.py @@ -11,7 +11,7 @@ from core.frontend import FE_Base, UserSession from core.config import CoreConfig from .database import ChuniData from .config import ChuniConfig -from .const import ChuniConstants +from .const import ChuniConstants, AvatarCategory, ItemKind def pairwise(iterable): @@ -99,6 +99,12 @@ class ChuniFrontend(FE_Base): Route("/{index}", self.render_GET_playlog, methods=['GET']), ]), Route("/favorites", self.render_GET_favorites, methods=['GET']), + Route("/userbox", self.render_GET_userbox, methods=['GET']), + Route("/avatar", self.render_GET_avatar, methods=['GET']), + Route("/update.map-icon", self.update_map_icon, methods=['POST']), + Route("/update.system-voice", self.update_system_voice, methods=['POST']), + Route("/update.userbox", self.update_userbox, methods=['POST']), + Route("/update.avatar", self.update_avatar, methods=['POST']), Route("/update.name", self.update_name, methods=['POST']), Route("/update.favorite_music_playlog", self.update_favorite_music_playlog, methods=['POST']), Route("/update.favorite_music_favorites", self.update_favorite_music_favorites, methods=['POST']), @@ -123,15 +129,33 @@ class ChuniFrontend(FE_Base): usr_sesh.chunithm_version = versions[0] profile = await self.data.profile.get_profile_data(usr_sesh.user_id, usr_sesh.chunithm_version) + user_id = usr_sesh.user_id + version = usr_sesh.chunithm_version + + # While map icons and system voices weren't present prior to AMAZON, we don't need to bother checking + # version here - it'll just end up being empty sets and the jinja will ignore the variables anyway. + user_map_icons = await self.data.item.get_items(user_id, ItemKind.MAP_ICON.value) + user_map_icons = [icon["itemId"] for icon in user_map_icons] + [profile.mapIconId] + user_system_voices = await self.data.item.get_items(user_id, ItemKind.SYSTEM_VOICE.value) + user_system_voices = [icon["itemId"] for icon in user_system_voices] + [profile.voiceId] + + map_icons, total_map_icons = await self.get_available_map_icons(version, user_map_icons) + system_voices, total_system_voices = await self.get_available_system_voices(version, user_system_voices) + resp = Response(template.render( title=f"{self.core_config.server.name} | {self.nav_name}", game_list=self.environment.globals["game_list"], sesh=vars(usr_sesh), - user_id=usr_sesh.user_id, + user_id=user_id, profile=profile, version_list=ChuniConstants.VERSION_NAMES, versions=versions, - cur_version=usr_sesh.chunithm_version + cur_version=version, + cur_version_name=ChuniConstants.game_ver_to_string(version), + map_icons=map_icons, + system_voices=system_voices, + total_map_icons=total_map_icons, + total_system_voices=total_system_voices ), media_type="text/html; charset=utf-8") if usr_sesh.chunithm_version >= 0: @@ -189,6 +213,8 @@ class ChuniFrontend(FE_Base): profile=profile, hot_list=hot_list, base_list=base_list, + cur_version=usr_sesh.chunithm_version, + cur_version_name=ChuniConstants.game_ver_to_string(usr_sesh.chunithm_version) ), media_type="text/html; charset=utf-8") else: return RedirectResponse("/gate/", 303) @@ -217,7 +243,9 @@ class ChuniFrontend(FE_Base): title=f"{self.core_config.server.name} | {self.nav_name}", game_list=self.environment.globals["game_list"], sesh=vars(usr_sesh), - playlog_count=0 + playlog_count=0, + cur_version=version, + cur_version_name=ChuniConstants.game_ver_to_string(version) ), media_type="text/html; charset=utf-8") playlog = await self.data.score.get_playlogs_limited(user_id, version, index, 20) playlog_with_title = [] @@ -257,6 +285,7 @@ class ChuniFrontend(FE_Base): user_id=user_id, playlog=playlog_with_title, playlog_count=playlog_count, + cur_version=version, cur_version_name=ChuniConstants.game_ver_to_string(version) ), media_type="text/html; charset=utf-8") else: @@ -319,11 +348,318 @@ class ChuniFrontend(FE_Base): user_id=user_id, favorites_by_genre=favorites_by_genre, favorites_count=favorites_count, + cur_version=version, cur_version_name=ChuniConstants.game_ver_to_string(version) ), media_type="text/html; charset=utf-8") else: return RedirectResponse("/gate/", 303) + async def get_available_map_icons(self, version: int, user_unlocked_items: List[int]) -> (List[dict], int): + items = dict() + rows = await self.data.static.get_map_icons(version) + if rows: + for row in rows: + # Only include items that are either available by default or in the user unlocked list + if row["defaultHave"] or row["mapIconId"] in user_unlocked_items: + item = dict() + item["id"] = row["mapIconId"] + item["name"] = row["name"] + item["iconPath"] = path.splitext(row["iconPath"])[0] + ".png" + items[row["mapIconId"]] = item + + return (items, len(rows)) + + async def get_available_system_voices(self, version: int, user_unlocked_items: List[int]) -> (List[dict], int): + items = dict() + rows = await self.data.static.get_system_voices(version) + if rows: + for row in rows: + # Only include items that are either available by default or in the user unlocked list + if row["defaultHave"] or row["voiceId"] in user_unlocked_items: + item = dict() + item["id"] = row["voiceId"] + item["name"] = row["name"] + item["imagePath"] = path.splitext(row["imagePath"])[0] + ".png" + items[row["voiceId"]] = item + + return (items, len(rows)) + + async def get_available_nameplates(self, version: int, user_unlocked_items: List[int]) -> (List[dict], int): + items = dict() + rows = await self.data.static.get_nameplates(version) + if rows: + for row in rows: + # Only include items that are either available by default or in the user unlocked list + if row["defaultHave"] or row["nameplateId"] in user_unlocked_items: + item = dict() + item["id"] = row["nameplateId"] + item["name"] = row["name"] + item["texturePath"] = path.splitext(row["texturePath"])[0] + ".png" + items[row["nameplateId"]] = item + + return (items, len(rows)) + + async def get_available_trophies(self, version: int, user_unlocked_items: List[int]) -> (List[dict], int): + items = dict() + rows = await self.data.static.get_trophies(version) + if rows: + for row in rows: + # Only include items that are either available by default or in the user unlocked list + if row["defaultHave"] or row["trophyId"] in user_unlocked_items: + item = dict() + item["id"] = row["trophyId"] + item["name"] = row["name"] + item["rarity"] = row["rareType"] + items[row["trophyId"]] = item + + return (items, len(rows)) + + async def get_available_characters(self, version: int, user_unlocked_items: List[int]) -> (List[dict], int): + items = dict() + rows = await self.data.static.get_characters(version) + if rows: + for row in rows: + # Only include items that are either available by default or in the user unlocked list + if row["defaultHave"] or row["characterId"] in user_unlocked_items: + item = dict() + item["id"] = row["characterId"] + item["name"] = row["name"] + item["iconPath"] = path.splitext(row["imagePath3"])[0] + ".png" + items[row["characterId"]] = item + + return (items, len(rows)) + + async def get_available_avatar_items(self, version: int, category: AvatarCategory, user_unlocked_items: List[int]) -> (List[dict], int): + items = dict() + rows = await self.data.static.get_avatar_items(version, category.value) + if rows: + for row in rows: + # Only include items that are either available by default or in the user unlocked list + if row["defaultHave"] or row["avatarAccessoryId"] in user_unlocked_items: + item = dict() + item["id"] = row["avatarAccessoryId"] + item["name"] = row["name"] + item["iconPath"] = path.splitext(row["iconPath"])[0] + ".png" + item["texturePath"] = path.splitext(row["texturePath"])[0] + ".png" + items[row["avatarAccessoryId"]] = item + + return (items, len(rows)) + + async def render_GET_userbox(self, request: Request) -> bytes: + template = self.environment.get_template( + "titles/chuni/templates/chuni_userbox.jinja" + ) + usr_sesh = self.validate_session(request) + if not usr_sesh: + usr_sesh = UserSession() + + if usr_sesh.user_id > 0: + if usr_sesh.chunithm_version < 0: + return RedirectResponse("/game/chuni/", 303) + + user_id = usr_sesh.user_id + version = usr_sesh.chunithm_version + + # Get the user profile so we know how the userbox is currently configured + profile = await self.data.profile.get_profile_data(user_id, version) + # Get all the user unlocked components so we know what to populate as options + user_nameplates = await self.data.item.get_items(user_id, ItemKind.NAMEPLATE.value) + user_nameplates = [item["itemId"] for item in user_nameplates] + [profile.nameplateId] + user_trophies = await self.data.item.get_items(user_id, ItemKind.TROPHY.value) + user_trophies = [item["itemId"] for item in user_trophies] + [profile.trophyId] + user_characters = await self.data.item.get_characters(user_id) + user_characters = [chara["characterId"] for chara in user_characters] + [profile.charaIllustId] + + # Build up available list of components + nameplates, total_nameplates = await self.get_available_nameplates(version, user_nameplates) + trophies, total_trophies = await self.get_available_trophies(version, user_trophies) + characters, total_characters = await self.get_available_characters(version, user_characters) + + # Get the user's team + team_name = "ARTEMiS" + if profile["teamId"]: + team = await self.data.profile.get_team_by_id(profile["teamId"]) + team_name = team["teamName"] + # Figure out the rating color we should use (rank maps to the stylesheet) + rating = profile.playerRating / 100; + rating_rank = 0 + if rating >= 16: + rating_rank = 8 + elif rating >= 15.25: + rating_rank = 7 + elif rating >= 14.5: + rating_rank = 6 + elif rating >= 13.25: + rating_rank = 5 + elif rating >= 12: + rating_rank = 4 + elif rating >= 10: + rating_rank = 3 + elif rating >= 7: + rating_rank = 2 + elif rating >= 4: + rating_rank = 1 + + return Response(template.render( + title=f"{self.core_config.server.name} | {self.nav_name}", + game_list=self.environment.globals["game_list"], + sesh=vars(usr_sesh), + user_id=user_id, + cur_version=version, + cur_version_name=ChuniConstants.game_ver_to_string(version), + profile=profile, + team_name=team_name, + rating_rank=rating_rank, + nameplates=nameplates, + trophies=trophies, + characters=characters, + total_nameplates=total_nameplates, + total_trophies=total_trophies, + total_characters=total_characters + ), media_type="text/html; charset=utf-8") + else: + return RedirectResponse("/gate/", 303) + + async def render_GET_avatar(self, request: Request) -> bytes: + template = self.environment.get_template( + "titles/chuni/templates/chuni_avatar.jinja" + ) + usr_sesh = self.validate_session(request) + if not usr_sesh: + usr_sesh = UserSession() + + if usr_sesh.user_id > 0: + if usr_sesh.chunithm_version < 11: + # Avatar configuration only for NEW!! and newer + return RedirectResponse("/game/chuni/", 303) + + user_id = usr_sesh.user_id + version = usr_sesh.chunithm_version + + # Get the user profile so we know what avatar items are currently in use + profile = await self.data.profile.get_profile_data(user_id, version) + # Get all the user avatar accessories so we know what to populate + user_accessories = await self.data.item.get_items(user_id, ItemKind.AVATAR_ACCESSORY.value) + user_accessories = [item["itemId"] for item in user_accessories] + \ + [profile.avatarBack, profile.avatarItem, profile.avatarWear, \ + profile.avatarFront, profile.avatarSkin, profile.avatarHead, profile.avatarFace] + + # Build up available list of items for each avatar category + wears, total_wears = await self.get_available_avatar_items(version, AvatarCategory.WEAR, user_accessories) + faces, total_faces = await self.get_available_avatar_items(version, AvatarCategory.FACE, user_accessories) + heads, total_heads = await self.get_available_avatar_items(version, AvatarCategory.HEAD, user_accessories) + skins, total_skins = await self.get_available_avatar_items(version, AvatarCategory.SKIN, user_accessories) + items, total_items = await self.get_available_avatar_items(version, AvatarCategory.ITEM, user_accessories) + fronts, total_fronts = await self.get_available_avatar_items(version, AvatarCategory.FRONT, user_accessories) + backs, total_backs = await self.get_available_avatar_items(version, AvatarCategory.BACK, user_accessories) + + return Response(template.render( + title=f"{self.core_config.server.name} | {self.nav_name}", + game_list=self.environment.globals["game_list"], + sesh=vars(usr_sesh), + user_id=user_id, + cur_version=version, + cur_version_name=ChuniConstants.game_ver_to_string(version), + profile=profile, + wears=wears, + faces=faces, + heads=heads, + skins=skins, + items=items, + fronts=fronts, + backs=backs, + total_wears=total_wears, + total_faces=total_faces, + total_heads=total_heads, + total_skins=total_skins, + total_items=total_items, + total_fronts=total_fronts, + total_backs=total_backs + ), media_type="text/html; charset=utf-8") + else: + return RedirectResponse("/gate/", 303) + + async def update_map_icon(self, request: Request) -> bytes: + usr_sesh = self.validate_session(request) + if not usr_sesh: + return RedirectResponse("/gate/", 303) + + form_data = await request.form() + new_map_icon: str = form_data.get("id") + + if not new_map_icon: + return RedirectResponse("/gate/?e=4", 303) + + if not await self.data.profile.update_map_icon(usr_sesh.user_id, usr_sesh.chunithm_version, new_map_icon): + return RedirectResponse("/gate/?e=999", 303) + + return RedirectResponse("/game/chuni/", 303) + + async def update_system_voice(self, request: Request) -> bytes: + usr_sesh = self.validate_session(request) + if not usr_sesh: + return RedirectResponse("/gate/", 303) + + form_data = await request.form() + new_system_voice: str = form_data.get("id") + + if not new_system_voice: + return RedirectResponse("/gate/?e=4", 303) + + if not await self.data.profile.update_system_voice(usr_sesh.user_id, usr_sesh.chunithm_version, new_system_voice): + return RedirectResponse("/gate/?e=999", 303) + + return RedirectResponse("/game/chuni/", 303) + + async def update_userbox(self, request: Request) -> bytes: + usr_sesh = self.validate_session(request) + if not usr_sesh: + return RedirectResponse("/gate/", 303) + + form_data = await request.form() + new_nameplate: str = form_data.get("nameplate") + new_trophy: str = form_data.get("trophy") + new_character: str = form_data.get("character") + + if not new_nameplate or \ + not new_trophy or \ + not new_character: + return RedirectResponse("/game/chuni/userbox?e=4", 303) + + if not await self.data.profile.update_userbox(usr_sesh.user_id, usr_sesh.chunithm_version, new_nameplate, new_trophy, new_character): + return RedirectResponse("/gate/?e=999", 303) + + return RedirectResponse("/game/chuni/userbox", 303) + + async def update_avatar(self, request: Request) -> bytes: + usr_sesh = self.validate_session(request) + if not usr_sesh: + return RedirectResponse("/gate/", 303) + + form_data = await request.form() + new_wear: str = form_data.get("wear") + new_face: str = form_data.get("face") + new_head: str = form_data.get("head") + new_skin: str = form_data.get("skin") + new_item: str = form_data.get("item") + new_front: str = form_data.get("front") + new_back: str = form_data.get("back") + + if not new_wear or \ + not new_face or \ + not new_head or \ + not new_skin or \ + not new_item or \ + not new_front or \ + not new_back: + return RedirectResponse("/game/chuni/avatar?e=4", 303) + + if not await self.data.profile.update_avatar(usr_sesh.user_id, usr_sesh.chunithm_version, new_wear, new_face, new_head, new_skin, new_item, new_front, new_back): + return RedirectResponse("/gate/?e=999", 303) + + return RedirectResponse("/game/chuni/avatar", 303) + + async def update_name(self, request: Request) -> bytes: usr_sesh = self.validate_session(request) if not usr_sesh: diff --git a/titles/chuni/templates/chuni_avatar.jinja b/titles/chuni/templates/chuni_avatar.jinja new file mode 100644 index 0000000..f53ce5c --- /dev/null +++ b/titles/chuni/templates/chuni_avatar.jinja @@ -0,0 +1,302 @@ +{% extends "core/templates/index.jinja" %} +{% block content %} + + +
+ {% include 'titles/chuni/templates/chuni_header.jinja' %} + + +
+
+
+ + + + + + + + + + + + + +
AVATAR
+ + + + + + + + + + + + +
Wear:
Face:
Head:
Skin:
Item:
Front:
Back:
+      + +
+
+
+
+ + +
+ + + +
+ {% for item in wears.values() %} + {{ item[ + + {% endfor %} +
+
+ + + +
+ {% for item in faces.values() %} + {{ item[ + + {% endfor %} +
+
+ + + +
+ {% for item in heads.values() %} + {{ item[ + + {% endfor %} +
+
+ + + +
+ {% for item in skins.values() %} + {{ item[ + + {% endfor %} +
+
+ + + +
+ {% for item in items.values() %} + {{ item[ + + {% endfor %} +
+
+ + + +
+ {% for item in fronts.values() %} + {{ item[ + + {% endfor %} +
+
+ + + +
+ {% for item in backs.values() %} + {{ item[ + + {% endfor %} +
+ +
+ + {% if error is defined %} + {% include "core/templates/widgets/err_banner.jinja" %} + {% endif %} +
+ + + +{% endblock content %} \ No newline at end of file diff --git a/titles/chuni/templates/chuni_favorites.jinja b/titles/chuni/templates/chuni_favorites.jinja index a386f6a..9ed23c3 100644 --- a/titles/chuni/templates/chuni_favorites.jinja +++ b/titles/chuni/templates/chuni_favorites.jinja @@ -7,15 +7,10 @@ {% include 'titles/chuni/templates/chuni_header.jinja' %} {% if favorites_by_genre is defined and favorites_by_genre is not none %}
-

{{ cur_version_name }}

Favorite Count: {{ favorites_count }}

{% for key, genre in favorites_by_genre.items() %}

{{ key }}

{% for favorite in genre %} -
@@ -28,7 +23,7 @@
{{ favorite.artist }}


- +
@@ -51,5 +46,16 @@ } }); }); + + // Remove Favorite + function removeFavorite(musicId) { + $.post("/game/chuni/update.favorite_music_favorites", { musicId: musicId, isAdd: 0 }) + .done(function (data) { + location.reload(); + }) + .fail(function () { + alert("Failed to remove favorite."); + }); + } {% endblock content %} \ No newline at end of file diff --git a/titles/chuni/templates/chuni_header.jinja b/titles/chuni/templates/chuni_header.jinja index 6085d14..56f8b39 100644 --- a/titles/chuni/templates/chuni_header.jinja +++ b/titles/chuni/templates/chuni_header.jinja @@ -1,5 +1,5 @@
-

Chunithm

+

{{ cur_version_name }}

\ No newline at end of file diff --git a/titles/chuni/templates/chuni_index.jinja b/titles/chuni/templates/chuni_index.jinja index 1854a89..c0e22b9 100644 --- a/titles/chuni/templates/chuni_index.jinja +++ b/titles/chuni/templates/chuni_index.jinja @@ -69,9 +69,48 @@ Last Play Date: {{ profile.lastPlayDate }} + {% if cur_version >= 6 %} + + Map Icon: +
{{ map_icons[profile.mapIconId]["name"] }}
+ + + System Voice: +
{{ system_voices[profile.voiceId]["name"] }}
+ + {% endif %}
+ + {% if cur_version >= 6 %} + +
+
+ +
+ {% for item in map_icons.values() %} + {{ item[ + + {% endfor %} +
+
+
+ + +
+
+ +
+ {% for item in system_voices.values() %} + {{ item[ + + {% endfor %} +
+
+
+ {% endif %} +
@@ -147,4 +186,93 @@ }); } + +{% if cur_version >= 6 %} + +{% endif %} + {% endblock content %} \ No newline at end of file diff --git a/titles/chuni/templates/chuni_playlog.jinja b/titles/chuni/templates/chuni_playlog.jinja index 8e035f3..cdd3f98 100644 --- a/titles/chuni/templates/chuni_playlog.jinja +++ b/titles/chuni/templates/chuni_playlog.jinja @@ -7,20 +7,15 @@ {% include 'titles/chuni/templates/chuni_header.jinja' %} {% if playlog is defined and playlog is not none %}
-

{{ cur_version_name }}

Playlog Count: {{ playlog_count }}

{% set rankName = ['D', 'C', 'B', 'BB', 'BBB', 'A', 'AA', 'AAA', 'S', 'S+', 'SS', 'SS+', 'SSS', 'SSS+'] %} {% set difficultyName = ['normal', 'hard', 'expert', 'master', 'ultimate'] %} {% for record in playlog %} - - - -
-

+

{{ '★' if record.isFav else '☆' }}

{{ record.title }}
@@ -191,5 +186,23 @@ } }); }); + + // Add/Remove Favorite + function updateFavorite(elementId, musicId) { + element = document.getElementById(elementId); + isAdd = 1; + if (element.classList.contains("fav-set")) + { + isAdd = 0; + } + + $.post("/game/chuni/update.favorite_music_favorites", { musicId: musicId, isAdd: isAdd }) + .done(function (data) { + location.reload(); + }) + .fail(function () { + alert("Failed to update favorite."); + }); + } {% endblock content %} \ No newline at end of file diff --git a/titles/chuni/templates/chuni_userbox.jinja b/titles/chuni/templates/chuni_userbox.jinja new file mode 100644 index 0000000..bd64943 --- /dev/null +++ b/titles/chuni/templates/chuni_userbox.jinja @@ -0,0 +1,256 @@ +{% extends "core/templates/index.jinja" %} +{% block content %} + + +
+ {% include 'titles/chuni/templates/chuni_header.jinja' %} + + +
+
+
+
+ + + + + + + + + + +
USER BOX
+ + + + + +
{{team_name}}
+ + + +
+ + + +
+ Lv. + {{ profile.level }}   {{ profile.userName }} +
+
+ RATING +   {{ profile.playerRating/100 }} +
+ + + + +
Nameplate:
Trophy:
+ +
Character:
+      + +
+
+
+ + + +
+ + + +
+ {% for item in nameplates.values() %} + {{ item[ + + {% endfor %} +
+
+ + + +
+ {% for item in characters.values() %} + {{ item[ + + {% endfor %} +
+ +
+ + {% if error is defined %} + {% include "core/templates/widgets/err_banner.jinja" %} + {% endif %} + + + + +{% endblock content %} \ No newline at end of file diff --git a/titles/chuni/templates/css/chuni_style.css b/titles/chuni/templates/css/chuni_style.css index 39c68b7..18ce6c5 100644 --- a/titles/chuni/templates/css/chuni_style.css +++ b/titles/chuni/templates/css/chuni_style.css @@ -159,6 +159,45 @@ caption { font-weight: bold; } +.rating { + font-weight: bold; + -webkit-text-stroke-width: 1px; + -webkit-text-stroke-color: black; +} + +.rating-rank0 { + color: #008000; +} + +.rating-rank1 { + color: #ffa500; +} +.rating-rank2 { + color: #ff0000; +} +.rating-rank3 { + color: #800080; +} +.rating-rank4 { + color: #cd853f; +} +.rating-rank5 { + color: #c0c0c0; +} +.rating-rank6 { + color: #ffd700; +} +.rating-rank7 { + color: #a9a9a9; +} + +.rating-rank8 { + background: linear-gradient(to right, red, yellow, lime, aqua, blue, fuchsia) 0 / 5em; + background-clip: text; + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; +} + .scrolling-text { overflow: hidden; } @@ -194,6 +233,41 @@ caption { } } +/* + Styles to support collapsible boxes (used for browsing images) +*/ +.collapsible { + background-color: #555; + cursor: pointer; + padding-bottom: 16px; + width: 100%; + border: none; + text-align: left; + outline: none; + font-family: monospace; + font-weight: bold; +} + + .collapsible:after { + content: '[+]'; + float: right; + } + +.collapsible-active:after { + content: "[-]"; +} + +.collapsible-content { + max-height: 0px; + overflow: hidden; + opacity: 0; + transition: max-height 0.2s ease-out; + background-color: #DDD; +} + +/* + Styles for favorites star in /playlog +*/ .fav { padding: 0; padding-left: 4px; @@ -206,7 +280,257 @@ caption { color: gold; } +/* + Styles for favorites in /favorites +*/ .btn-fav-remove { padding:10px; width:100%; +} + +/* + Styles for userbox configuration +*/ +.userbox { + position: absolute; +} + +.userbox-nameplate { + top: 72px; + left: 32px; +} + +.userbox-teamframe { + top: 74px; + left: 156px; +} + +.userbox-teamname { + top: 72px; + left: 254px; + padding: 8px 20px; + font-size: 22px; + text-shadow: rgba(0,0,0,0.8) 2px 2px; + color: #DDD; + width: 588px; + text-align: left; +} + +.userbox-trophy { + top: 170px; + left: 250px; + zoom: 0.70; +} + +.userbox-trophy-name { + top: 170px; + left: 250px; + padding: 8px 20px; + font-size: 28px; + font-weight: bold; + color: #333; + width: 588px; + text-align: center; +} + +.userbox-ratingframe { + top: 160px; + left: 175px; +} + +.userbox-charaframe { + top: 267px; + left: 824px; + zoom: 0.61; +} + +.userbox-chara { + top: 266px; + left: 814px; + zoom: 0.62; +} + +.userbox-name { + top: 160px; + left: 162px; + padding: 8px 20px; + font-size: 32px; + font-weight: bold; + color: #333; + text-align: left; +} + +.userbox-name-level-label { + font-size: 24px; +} + +.userbox-rating { + top: 204px; + left: 166px; + padding: 8px 20px; + font-size: 24px; + text-align: left; +} + +.userbox-rating-label { + font-size: 16px; +} + +.trophy-rank0 { + color: #111; + background-color: #DDD; +} +.trophy-rank1 { + color: #111; + background-color: #D85; +} +.trophy-rank2 { + color: #111; + background-color: #ADF; +} +.trophy-rank3 { + color: #111; + background-color: #EB3; +} +.trophy-rank4 { + color: #111; + background-color: #EB3; +} +.trophy-rank5 { + color: #111; + background-color: #FFA; +} +.trophy-rank6 { + color: #111; + background-color: #FFA; +} +.trophy-rank7 { + color: #111; + background-color: #FCF; +} +.trophy-rank8 { + color: #111; + background-color: #FCF; +} +.trophy-rank9 { + color: #111; + background-color: #07C; +} +.trophy-rank10 { + color: #111; + background-color: #7FE; +} +.trophy-rank11 { + color: #111; + background-color: #8D7; +} + +/* + Styles for scrollable divs (used for browsing images) +*/ +.scrolling-lists { + table-layout: fixed; +} + + .scrolling-lists div { + overflow: auto; + white-space: nowrap; + } + + .scrolling-lists img { + width: 128px; + } + +.scrolling-lists-lg { + table-layout: fixed; + width: 100%; +} + + .scrolling-lists-lg div { + overflow: auto; + white-space: nowrap; + padding: 4px; + } + + .scrolling-lists-lg img { + padding: 4px; + width: 128px; + } + +/* + Styles for avatar configuration +*/ +.avatar-preview { + position:absolute; + zoom:0.5; +} + +.avatar-preview-wear { + top: 280px; + left: 60px; +} + +.avatar-preview-face { + top: 262px; + left: 200px; +} + +.avatar-preview-head { + top: 130px; + left: 120px; +} + +.avatar-preview-skin-body { + top: 250px; + left: 190px; + height: 406px; + width: 256px; + object-fit: cover; + object-position: top; +} + +.avatar-preview-skin-leftfoot { + top: 625px; + left: 340px; + object-position: -84px -406px; +} + +.avatar-preview-skin-rightfoot { + top: 625px; + left: 40px; + object-position: 172px -406px; +} + +.avatar-preview-common { + top: 250px; + left: 135px; +} + +.avatar-preview-item-lefthand { + top: 180px; + left: 370px; + height: 544px; + width: 200px; + object-fit: cover; + object-position: right; +} + +.avatar-preview-item-righthand { + top: 180px; + left: 65px; + height: 544px; + width: 200px; + object-fit: cover; + object-position: left; +} + +.avatar-preview-back { + top: 140px; + left: 46px; +} + +.avatar-preview-platform { + top: 310px; + left: 55px; + zoom: 1; } \ No newline at end of file diff --git a/titles/chuni/templates/scripts/collapsibles.js b/titles/chuni/templates/scripts/collapsibles.js new file mode 100644 index 0000000..ff2b5a3 --- /dev/null +++ b/titles/chuni/templates/scripts/collapsibles.js @@ -0,0 +1,66 @@ +/// +/// Handles the collapsible behavior of each of the scrollable containers +/// +/// @note Intent is to include this file via jinja in the same