Bug fixing and ready for Extra Challenge

This commit is contained in:
UnitedAirforce
2025-08-16 11:17:02 +08:00
parent f96605063d
commit f05520b958
8 changed files with 122 additions and 23 deletions

View File

@@ -48,9 +48,9 @@ If you'd like to upload it somewhere else and contribute the link, please contac
Download `common.zip` and a platform of your choosing. Unzip the folders within to the private server root directory. If in doubt, check the `File Structure` section at the end.
### GC4MAX Expansion
### GC4MAX Expansion and Extra Challenge Expansion
To install the `GC4MAX` expansion, which ports arcade exclusive tracks, avatars, and skins to 2OS, download the `4max expansion` folder's `common.zip` and a platform of your choosing. Unzip the folders within to the privates server root directory. Overwrite existing files. Note that the included installation `apk` or `ipa` must be used for correct avatar rendering.
To install the `GC4MAX Expansion` (which includes the `Extra Challenge Expansion`), which ports arcade exclusive tracks, avatars, and skins to 2OS, download the `4max expansion` folder's `common.zip` and a platform of your choosing. Unzip the folders within to the privates server root directory. Overwrite existing files. Note that the included installation `apk` or `ipa` must be used for correct avatar rendering.
Edit `config.py`'s `MODEL`, `TUNEFILE`, and `SKIN` value to match the `pak`'s timestamp. `paks` can be found at `/files/gc2/`.
@@ -313,9 +313,9 @@ Note that this data is for analytics only, and the functionality to embed this d
下载 `common.zip` 和您设备的平台。将里面的所有文件夹解压到服务器根目录。如有疑惑,请参照文末`文件结构`章节。
## GC4MAX扩展包
## GC4MAX扩展包和Extra Challenge扩展包
如想下载`GC4MAX`扩展包(包含了街机独占曲目,皮肤,和角色),下载`4max expansion`里的`common.zip`和和您设备的平台。将里面的所有文件夹解压到服务器根目录。如有重复,覆盖所有文件。请注意,必须使用包含的`apk`或`ipa`安装包来正确渲染角色。
如想下载`GC4MAX扩展包`(内含`Extra Challenge扩展包`(包含了街机独占曲目,皮肤,和角色),下载`4max expansion`里的`common.zip`和和您设备的平台。将里面的所有文件夹解压到服务器根目录。如有重复,覆盖所有文件。请注意,必须使用包含的`apk`或`ipa`安装包来正确渲染角色。
修改`config.py`的`MODEL`, `TUNEFILE`, and `SKIN` 值至现有`pak`文件的时间戳. `paks`文件的位置在`/files/gc2/`.

View File

@@ -36,12 +36,12 @@ def get_4max_version_string():
def parse_res(res):
parsed_data = []
if isinstance(res, int):
if isinstance(res, int) or res == None:
return "Failed to fetch version info: Error " + str(res)
for item in res:
if item.get("isOpen"):
version = item.get("version", "Unknown Version")
version = item.get("version", 0)
changelog = "<br>".join(item.get("changeLog", {}).get("en", []))
parsed_data.append(f"<strong>Version: {version}</strong><p><strong>Changelog:</strong><br>{changelog}</p>")
return "".join(parsed_data)

View File

@@ -8,7 +8,7 @@ import xml.etree.ElementTree as ET
from config import ROOT_FOLDER, START_COIN, COIN_REWARD, AUTHORIZATION_NEEDED
from api.database import database, user, daily_reward, result, result, check_blacklist, check_whitelist
from api.database import database, user, daily_reward, result, check_blacklist, check_whitelist
from api.crypt import decrypt_fields
from api.templates import START_STAGES, EXP_UNLOCKED_SONGS

View File

@@ -248,7 +248,10 @@ async def ranking_detail(request: Request):
play_record = None
if user_id:
play_record = next((record for record in play_results if int(record[3]) == user_id), None)
play_record = play_record = next(
(record for record in play_results if record[3] not in (None, '') and int(record[3]) == user_id),
None
)
if not play_record:
play_record = next((record for record in play_results if record[1] == device_id and record[3] is None), None)
@@ -257,10 +260,10 @@ async def ranking_detail(request: Request):
avatar_index = str(play_record[7]) if play_record else "1"
user_score = play_record[8] if play_record else 0
for rank, result_obj in enumerate(play_results, start=1):
if user_result and int(result_obj[3]) == user_id:
if user_result and result_obj[3] not in (None, '') and int(result_obj[3]) == user_id:
player_rank = rank
break
elif result_obj[1] == device_id and result_obj[3] is None:
elif result_obj[1] == device_id and result_obj[3] in (None, ''):
player_rank = rank
break

View File

@@ -7,7 +7,7 @@ import math
from sqlalchemy import select, update
import xml.etree.ElementTree as ET
from config import START_COIN, AUTHORIZATION_NEEDED, STAGE_PRICE, START_COIN, AVATAR_PRICE, ITEM_PRICE
from config import START_COIN, AUTHORIZATION_NEEDED, STAGE_PRICE, START_COIN, AVATAR_PRICE, ITEM_PRICE, FMAX_PRICE, EX_PRICE
from api.crypt import decrypt_fields
from api.misc import inform_page, parse_res, FMAX_VER, FMAX_RES
@@ -46,7 +46,7 @@ async def web_shop(request: Request):
if cnt_type == "1":
low_range = 100
up_range = 616
up_range = 615
if page < 0 or page > math.ceil(up_range - low_range) / 80:
return HTMLResponse("""<html><body><h1>Invalid page number</h1></body></html>""", status_code=400)
@@ -59,17 +59,31 @@ async def web_shop(request: Request):
low_range = low_range + page * 80
up_range = min(up_range, low_range + 80)
if 700 not in my_stage and os.path.isfile('./files/dlc_4max.html'):
if 700 not in my_stage and os.path.isfile('./files/dlc_4max.html') and not spawn_prev_page:
buttons_html += """
<a href="wwic://web_shop_detail?&cnt_type=1&cnt_id=-1">
<img src="/files/web/dlc_4max.jpg" style="width: 84%; margin-bottom: 20px; margin-top: -100px;" />
<img src="/files/web/dlc_4max.jpg" style="width: 84%; margin-bottom: 110px; margin-top: -100px;" />
</a><br>
"""
fmax_inc = 1
elif 700 in my_stage and os.path.isfile('./files/dlc_4max.html'):
elif 700 in my_stage and os.path.isfile('./files/dlc_4max.html') and not spawn_prev_page:
buttons_html += """
<a href="wwic://web_shop_detail?&cnt_type=1&cnt_id=-3">
<img src="/files/web/dlc_4max.jpg" style="width: 84%; margin-bottom: 110px; margin-top: -100px;" />
</a><br>
"""
if 980 not in my_stage and os.path.isfile('./files/dlc_extra.html') and not spawn_prev_page:
buttons_html += """
<a href="wwic://web_shop_detail?&cnt_type=1&cnt_id=-2">
<img src="/files/web/dlc_4max.jpg" style="width: 84%; margin-bottom: 20px; margin-top: -100px;" />
<img src="/files/web/dlc_extra.jpg" style="width: 84%; margin-bottom: 20px; margin-top: -100px;" />
</a><br>
"""
fmax_inc = 1
elif 980 in my_stage and os.path.isfile('./files/dlc_4max.html') and not spawn_prev_page:
buttons_html += """
<a href="wwic://web_shop_detail?&cnt_type=1&cnt_id=-4">
<img src="/files/web/dlc_extra.jpg" style="width: 84%; margin-bottom: 20px; margin-top: -100px;" />
</a><br>
"""
@@ -204,7 +218,7 @@ async def web_shop_detail(request: Request):
<span style="color: #FFFFFF; font-size: 44px; font-family: Hiragino Kaku Gothic ProN, sans-serif;">{song_stage_price}</span>
</div>
"""
elif cnt_id == -2:
elif cnt_id == -3:
log = parse_res(FMAX_RES)
html = f"""
<div class="text-content">
@@ -218,6 +232,36 @@ async def web_shop_detail(request: Request):
<p>{log}<p><br>
</div>
"""
elif cnt_id == -4:
html = f"""
<div class="text-content">
<p>You have unlocked the EXTRA Challenge!</p>
<p>Please report bugs/missing tracks to Discord: #AnTcfgss, or QQ 3421587952.</p>
<button class="quit-button-extra" onclick="window.location.href='wwic://web_shop?&cnt_type=1'">
Go Back
</button>
</div>
"""
elif cnt_id == -2:
html = f"""
<div class="text-content">
<p>Brace the Ultimate - Extra - Challenge.</p>
<p>170+ Arcade Extra difficulty charts await you.</p>
<p>You have been warned.</p>
</div>
<button class="buy-button-extra" onclick="window.location.href='wwic://web_purchase_coin?&cnt_type=1&cnt_id=-2&num=1'">
Buy
<div class="coin-container">
<img src="/files/web/coin_icon.png" alt="Coin Icon" class="coin-icon">
<span style="font-size: 22px; font-weight: bold;"> {EX_PRICE}</span>
</div>
</button>
<br><br>
<button class="quit-button-extra" onclick="window.location.href='wwic://web_shop?&cnt_type=1'">
Go Back
</button>
"""
elif cnt_id == -1:
html = f"""
<div class="text-content">
@@ -230,7 +274,7 @@ async def web_shop_detail(request: Request):
Buy
<div class="coin-container">
<img src="/files/web/coin_icon.png" alt="Coin Icon" class="coin-icon">
<span style="font-size: 22px; font-weight: bold;"> 300</span>
<span style="font-size: 22px; font-weight: bold;"> {FMAX_PRICE}</span>
</div>
</button>
<br><br>
@@ -279,8 +323,10 @@ async def web_shop_detail(request: Request):
else:
html = "<p>Item not found.</p>"
if cnt_type == "1" and cnt_id < 0:
if cnt_type == "1" and (cnt_id == -1 or cnt_id == -3):
source_html = f"files/dlc_4max.html"
elif cnt_type == "1" and (cnt_id == -2 or cnt_id == -4):
source_html = f"files/dlc_extra.html"
else:
source_html = f"files/web_shop_detail.html"
html += f"""
@@ -331,11 +377,19 @@ async def buy_by_coin(request: Request):
if cnt_type == "1":
if cnt_id == -1:
song_stage_price = 300
song_stage_price = FMAX_PRICE
if coin < song_stage_price:
return Response(fail_url, media_type="application/xml")
for i in range(616, 950):
for i in range(615, 926):
my_stage.add(i)
coin -= song_stage_price
elif cnt_id == -2:
song_stage_price = EX_PRICE
if coin < song_stage_price:
return Response(fail_url, media_type="application/xml")
for i in range(926, 985):
my_stage.add(i)
coin -= song_stage_price
else:

View File

@@ -11,7 +11,7 @@ START_STAGES = [7,23,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,55,56,57
START_AVATARS = []
EXCLUDE_STAGE_EXP = [121,134,166,167,168,169,170,213,214,215,225,277,397] # 134 and 170 unoccupied dummy tracks (filled with Departure -Remix-),
EXCLUDE_STAGE_EXP = [121,134,166,167,168,169,170,213,214,215,225,277,396] # 134 and 170 unoccupied dummy tracks (filled with Departure -Remix-),
#121 (and 93-96 lady gaga songs) removed (can be enabled by patching stageParam:isAvailable, or change the last byte before next song's name - 1 from 01 to 03 in stage_param.dat.
# Rest are exp unlocked songs.
EXCLUDE_AVATAR_EXP = [28,29]

View File

@@ -606,7 +606,7 @@ async def bonus(request: Request):
await database.execute(update_query)
elif cnt_type == 2:
avatars = set(json.loads(my_avatar)) if my_avatar else set()
avatars = set(my_avatar) if my_avatar else set()
if cnt_id not in avatars:
avatars.add(cnt_id)
my_avatar = json.dumps(list(avatars))

View File

@@ -13,11 +13,26 @@
margin: 0px auto;
padding: 20px;
}
.dlc_container_extra {
max-width: 800px;
height: 70vh;
margin: 0px auto;
padding: 20px;
}
.dlc_logo {
width: 100%;
max-width: 500px;
margin-bottom: 10px;
}
.dlc_extra_body {
background: url('/files/web/extra_bg.jpg') no-repeat center center fixed;
background-size: cover;
color: red;
font-family: Arial, sans-serif;
text-align: center;
margin: 0;
padding: 0;
}
.text-content {
font-size: 18px;
line-height: 1.6;
@@ -50,6 +65,33 @@
cursor: pointer;
position: relative;
}
.buy-button-extra {
display: inline-block;
width: 160px;
height: 110px;
background: url('/files/web/frame_buy_extra.png') no-repeat center center;
background-size: contain;
border: none;
color: white;
font-size: 22px;
font-weight: bold;
padding: 15px;
cursor: pointer;
position: relative;
}
.quit-button-extra {
display: inline-block;
width: 150px;
height: 30px;
background: url('/files/web/quit_button_extra.png') no-repeat center center;
background-size: contain;
border: none;
color: white;
font-size: 20px;
font-weight: bold;
cursor: pointer;
position: relative;
}
.coin-container {
display: flex;
justify-content: center;