idac: battle gift event, tips, QoL improvements added

This commit is contained in:
Dniel97 2024-04-01 20:19:37 +02:00
parent c741c052e9
commit 9379172791
Signed by: Dniel97
GPG Key ID: 6180B3C768FB2E08
14 changed files with 669 additions and 53 deletions

View File

@ -0,0 +1,83 @@
"""IDAC Battle Gift and Tips added
Revision ID: e4e8d89c9b02
Revises: 81e44dd6047a
Create Date: 2024-04-01 17:49:50.009718
"""
from alembic import op
import sqlalchemy as sa
from sqlalchemy.dialects import mysql
# revision identifiers, used by Alembic.
revision = "e4e8d89c9b02"
down_revision = "81e44dd6047a"
branch_labels = None
depends_on = None
def upgrade():
op.create_table(
"idac_user_battle_gift",
sa.Column("id", sa.Integer(), nullable=False),
sa.Column("user", sa.Integer(), nullable=False),
sa.Column("battle_gift_event_id", sa.Integer(), nullable=True),
sa.Column("gift_id", sa.Integer(), nullable=True),
sa.Column("gift_status", sa.Integer(), nullable=True),
sa.Column(
"received_date",
sa.TIMESTAMP(),
server_default=sa.text("now()"),
nullable=True,
),
sa.ForeignKeyConstraint(
["user"], ["aime_user.id"], onupdate="cascade", ondelete="cascade"
),
sa.PrimaryKeyConstraint("id"),
sa.UniqueConstraint(
"user", "battle_gift_event_id", "gift_id", name="idac_user_battle_gift_uk"
),
mysql_charset="utf8mb4",
)
op.create_table(
"idac_profile_tips",
sa.Column("id", sa.Integer(), nullable=False),
sa.Column("user", sa.Integer(), nullable=False),
sa.Column("version", sa.Integer(), nullable=False),
sa.Column(
"tips_list",
sa.String(length=16),
server_default="QAAAAAAAAAAAAAAA",
nullable=True,
),
sa.Column(
"timetrial_play_count", sa.Integer(), server_default="0", nullable=True
),
sa.Column("story_play_count", sa.Integer(), server_default="0", nullable=True),
sa.Column(
"store_battle_play_count", sa.Integer(), server_default="0", nullable=True
),
sa.Column(
"online_battle_play_count", sa.Integer(), server_default="0", nullable=True
),
sa.Column(
"special_play_count", sa.Integer(), server_default="0", nullable=True
),
sa.Column(
"challenge_play_count", sa.Integer(), server_default="0", nullable=True
),
sa.Column("theory_play_count", sa.Integer(), server_default="0", nullable=True),
sa.ForeignKeyConstraint(
["user"], ["aime_user.id"], onupdate="cascade", ondelete="cascade"
),
sa.PrimaryKeyConstraint("id"),
sa.UniqueConstraint("user", "version", name="idac_profile_tips_uk"),
mysql_charset="utf8mb4",
)
def downgrade():
op.drop_table("idac_user_battle_gift")
op.drop_table("idac_profile_tips")

View File

@ -775,7 +775,7 @@ python dbutils.py --game SDGT upgrade
| Display ID | Description |
| ---------------------------------------- | ------------------------------------------------------------------------------------------------- |
| 1 | ADV image in the size 1280x720, shown during attract |
| 1 | ADV image in the size 1920x1080, shown during attract |
| 2 | Start image in the size 1280x720, shown in the Main Menu after selection the corresponding banner |
| 3 | Banner image in the size 640×120, shown in the Main Menu |
| 5 | Stamp Background image in the size 1780x608 |
@ -809,6 +809,19 @@ python dbutils.py --game SDGT upgrade
},
```
### Battle Gift
- `gift_id`: unique gift index (starts from 0 f.e.)
- `battle_gift_event_id`: unique event id
- `reward_category`: item category (f.e. 21 = Chat Stamp)
- `reward_type`: item id (f.e. 483 = Remilia Scarlet)
- `reward_name`: name of the reward
- `rarity`: 2 Golden, 1 Silver, 3 Bronze
- `cash_rate`: ?
- `customize_point_rate`: ?
- `avatar_point_rate`: ?
- `first_distribution_rate`: only used server side for the probability of the first distribution
### Credits:
- Bottersnike: For the HUGE Reverse Engineering help

View File

@ -10,6 +10,10 @@ server:
port_echo2: 20002
port_matching_p2p: 20003
timerelease:
timerelease_no: 3
timerelease_avatar_gacha_no: 3
stamp:
enable: True
enabled_stamps: # max 3 play stamps
@ -20,3 +24,7 @@ stamp:
timetrial:
enable: True
enabled_timetrial: "touhou_remilia_scarlet"
battle_event:
enabled: True
enabled_battle_event: "touhou_1st"

View File

@ -66,6 +66,22 @@ class IDACServerConfig:
return CoreConfig.get_config_field(
self.__config, "idac", "server", "port_matching_p2p", default=20003
)
class IDACTimereleaseConfig:
def __init__(self, parent: "IDACConfig") -> None:
self.__config = parent
@property
def timerelease_no(self) -> int:
return CoreConfig.get_config_field(
self.__config, "idac", "timerelease", "timerelease_no", default=1
)
@property
def timerelease_avatar_gacha_no(self) -> int:
return CoreConfig.get_config_field(
self.__config, "idac", "timerelease", "timerelease_avatar_gacha_no", default=1
)
class IDACStampConfig:
@ -112,10 +128,32 @@ class IDACTimetrialConfig:
"enabled_timetrial",
default="touhou_remilia_scarlet",
)
class IDACTBattleGiftConfig:
def __init__(self, parent: "IDACConfig") -> None:
self.__config = parent
@property
def enable(self) -> bool:
return CoreConfig.get_config_field(
self.__config, "idac", "battle_gift", "enable", default=True
)
@property
def enabled_battle_gift(self) -> str:
return CoreConfig.get_config_field(
self.__config,
"idac",
"battle_gift",
"enabled_battle_gift",
default="touhou_1st",
)
class IDACConfig(dict):
def __init__(self) -> None:
self.server = IDACServerConfig(self)
self.timerelease = IDACTimereleaseConfig(self)
self.stamp = IDACStampConfig(self)
self.timetrial = IDACTimetrialConfig(self)
self.battle_gift = IDACTBattleGiftConfig(self)

View File

@ -0,0 +1,82 @@
{
"battle_gift_event_id": 2,
"event_nm": "東方Projectコラボ",
"start_dt": "2024-04-01",
"end_dt": "2024-06-03",
"mode_id": 1,
"delivery_type": 1,
"gift_data": [
{
"gift_id": 0,
"battle_gift_event_id": 2,
"reward_category": 21,
"reward_type": 418,
"reward_name": "素敵なお賽銭箱はそこよ",
"rarity": 3,
"cash_rate": 0,
"customize_point_rate": 0,
"avatar_point_rate": 0,
"first_distribution_rate": 60
},
{
"gift_id": 1,
"battle_gift_event_id": 2,
"reward_category": 21,
"reward_type": 419,
"reward_name": "面倒な種族ね",
"rarity": 2,
"cash_rate": 0,
"customize_point_rate": 0,
"avatar_point_rate": 0,
"first_distribution_rate": 20
},
{
"gift_id": 2,
"battle_gift_event_id": 2,
"reward_category": 21,
"reward_type": 423,
"reward_name": "調子がそこそこだぜ",
"rarity": 3,
"cash_rate": 0,
"customize_point_rate": 0,
"avatar_point_rate": 0,
"first_distribution_rate": 60
},
{
"gift_id": 3,
"battle_gift_event_id": 2,
"reward_category": 21,
"reward_type": 422,
"reward_name": "あー?",
"rarity": 2,
"cash_rate": 0,
"customize_point_rate": 0,
"avatar_point_rate": 0,
"first_distribution_rate": 20
},
{
"gift_id": 4,
"battle_gift_event_id": 2,
"reward_category": 21,
"reward_type": 427,
"reward_name": "さ、記事にするわよー",
"rarity": 3,
"cash_rate": 0,
"customize_point_rate": 0,
"avatar_point_rate": 0,
"first_distribution_rate": 60
},
{
"gift_id": 5,
"battle_gift_event_id": 2,
"reward_category": 21,
"reward_type": 426,
"reward_name": "号外~、号外だよ~",
"rarity": 2,
"cash_rate": 0,
"customize_point_rate": 0,
"avatar_point_rate": 0,
"first_distribution_rate": 20
}
]
}

View File

@ -30,7 +30,6 @@
180,
180,
180,
200,
200
],
"reward": [

View File

@ -30,7 +30,6 @@
180,
180,
180,
200,
200
],
"reward": [

View File

@ -30,7 +30,6 @@
180,
180,
180,
200,
200
],
"reward": [

View File

@ -321,6 +321,23 @@ timetrial_event = Table(
mysql_charset="utf8mb4",
)
battle_gift = Table(
"idac_user_battle_gift",
metadata,
Column("id", Integer, primary_key=True, nullable=False),
Column(
"user",
ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"),
nullable=False,
),
Column("battle_gift_event_id", Integer),
Column("gift_id", Integer),
Column("gift_status", Integer),
Column("received_date", TIMESTAMP, server_default=func.now()),
UniqueConstraint("user", "battle_gift_event_id", "gift_id", name="idac_user_battle_gift_uk"),
mysql_charset="utf8mb4",
)
class IDACItemData(BaseData):
async def get_random_user_car(self, aime_id: int, version: int) -> Optional[List[Row]]:
@ -843,6 +860,19 @@ class IDACItemData(BaseData):
if result is None:
return None
return result.fetchone()
async def get_battle_gifts(self, aime_id: int, battle_gift_event_id: int) -> Optional[Row]:
sql = select(battle_gift).where(
and_(
battle_gift.c.user == aime_id,
battle_gift.c.battle_gift_event_id == battle_gift_event_id,
)
)
result = await self.execute(sql)
if result is None:
return None
return result.fetchall()
async def put_car(self, aime_id: int, version: int, car_data: Dict) -> Optional[int]:
car_data["user"] = aime_id
@ -1074,3 +1104,19 @@ class IDACItemData(BaseData):
)
return None
return result.lastrowid
async def put_battle_gift(
self, aime_id: int, battle_gift_data: Dict
) -> Optional[int]:
battle_gift_data["user"] = aime_id
sql = insert(battle_gift).values(**battle_gift_data)
conflict = sql.on_duplicate_key_update(**battle_gift_data)
result = await self.execute(conflict)
if result is None:
self.logger.warn(
f"put_battle_gift: Failed to update! aime_id: {aime_id}"
)
return None
return result.lastrowid

View File

@ -244,6 +244,28 @@ theory = Table(
mysql_charset="utf8mb4",
)
tips = Table(
"idac_profile_tips",
metadata,
Column("id", Integer, primary_key=True, nullable=False),
Column(
"user",
ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"),
nullable=False,
),
Column("version", Integer, nullable=False),
Column("tips_list", String(16), server_default="QAAAAAAAAAAAAAAA"),
Column("timetrial_play_count", Integer, server_default="0"),
Column("story_play_count", Integer, server_default="0"),
Column("store_battle_play_count", Integer, server_default="0"),
Column("online_battle_play_count", Integer, server_default="0"),
Column("special_play_count", Integer, server_default="0"),
Column("challenge_play_count", Integer, server_default="0"),
Column("theory_play_count", Integer, server_default="0"),
UniqueConstraint("user", "version", name="idac_profile_tips_uk"),
mysql_charset="utf8mb4",
)
class IDACProfileData(BaseData):
def __init__(self, cfg: CoreConfig, conn: Connection) -> None:
@ -348,6 +370,19 @@ class IDACProfileData(BaseData):
if result is None:
return None
return result.fetchone()
async def get_profile_tips(self, aime_id: int, version: int) -> Optional[Row]:
sql = select(tips).where(
and_(
tips.c.user == aime_id,
tips.c.version == version,
)
)
result = await self.execute(sql)
if result is None:
return None
return result.fetchone()
async def put_profile(
self, aime_id: int, version: int, profile_data: Dict
@ -438,3 +473,20 @@ class IDACProfileData(BaseData):
)
return None
return result.lastrowid
async def put_profile_tips(
self, aime_id: int, version: int, tips_data: Dict
) -> Optional[int]:
tips_data["user"] = aime_id
tips_data["version"] = version
sql = insert(tips).values(**tips_data)
conflict = sql.on_duplicate_key_update(**tips_data)
result = await self.execute(conflict)
if result is None:
self.logger.warn(
f"put_profile_tips: Failed to update! aime_id: {aime_id}"
)
return None
return result.lastrowid

View File

@ -1,6 +1,6 @@
from datetime import datetime, timedelta
import os
from random import choice
from random import choice, randint
from typing import Any, Dict, List
import json
import logging
@ -17,20 +17,27 @@ class IDACSeason2(IDACBase):
super().__init__(core_cfg, game_cfg)
self.version = IDACConstants.VER_IDAC_SEASON_2
# load the play stamps and timetrial events into memory
# load timerelease numbers from config
self.timerelease_no = self.game_config.timerelease.timerelease_no
self.timerelease_avatar_gacha_no = (
self.game_config.timerelease.timerelease_avatar_gacha_no
)
# load the user configured play stamps (up to 3)
self.stamp_info = []
if self.game_config.stamp.enable:
for stamp in self.game_config.stamp.enabled_stamps:
if not os.path.exists(f"./titles/idac/data/stamps/{stamp}.json"):
if not os.path.exists(f"./titles/idac/data/stamp/{stamp}.json"):
self.logger.warning(f"Stamp {stamp} is enabled but does not exist!")
continue
with open(
f"./titles/idac/data/stamps/{stamp}.json", encoding="UTF-8"
f"./titles/idac/data/stamp/{stamp}.json", encoding="UTF-8"
) as f:
self.logger.debug(f"Loading stamp {stamp}")
self.stamp_info.append(self._fix_dates(json.load(f)))
# load the user configured time trials (only one)
self.timetrial_event = {}
self.timetrial_event_id = None
if self.game_config.timetrial.enable:
@ -53,6 +60,25 @@ class IDACSeason2(IDACBase):
"timetrial_event_id"
)
# load the user configured battle gifts (only one)
self.battle_gift_event = None
if self.game_config.battle_gift.enable:
battle_gift = self.game_config.battle_gift.enabled_battle_gift
if battle_gift is not None:
if not os.path.exists(
f"./titles/idac/data/battle_gift/{battle_gift}.json"
):
self.logger.warning(
f"Battle gift {battle_gift} is enabled but does not exist!"
)
else:
self.logger.debug(f"Loading battle gift {battle_gift}")
with open(
f"./titles/idac/data/battle_gift/{battle_gift}.json",
encoding="UTF-8",
) as f:
self.battle_gift_event = self._fix_dates(json.load(f))
async def handle_alive_get_request(self, data: Dict, headers: Dict):
return {
"status_code": "0",
@ -122,7 +148,6 @@ class IDACSeason2(IDACBase):
"difference_time_to_jp": 0,
# has to match the game asset version to show theory of street
"asset_version": "1",
# option version? MV01?
"optional_version": "1",
"disconnect_offset": 0,
"boost_balance_version": "0",
@ -130,8 +155,8 @@ class IDACSeason2(IDACBase):
"play_stamp_enable": 1,
"play_stamp_bonus_coin": 1,
"gacha_chara_needs": 1,
"both_win_system_control": 1,
"subcard_system_congrol": 1,
"both_win_system_control": 0,
"subcard_system_congrol": 0,
"server_maintenance_start_hour": 0,
"server_maintenance_start_minutes": 0,
"server_maintenance_end_hour": 0,
@ -141,7 +166,10 @@ class IDACSeason2(IDACBase):
"domain_echo1": f"{self.core_cfg.server.hostname}:{self.game_config.server.echo1}",
"domain_echo2": f"{self.core_cfg.server.hostname}:{self.game_config.server.echo1}",
"domain_ping": f"{self.core_cfg.server.hostname}",
"battle_gift_event_master": [],
"battle_gift_event_master": (
[self.battle_gift_event] if self.battle_gift_event else []
),
# online battle round event
"round_event": [
{
"round_event_id": 30,
@ -180,7 +208,7 @@ class IDACSeason2(IDACBase):
"reward_upper_limit": 180,
"reward_lower_limit": 180,
"reward": [{"reward_category": 21, "reward_type": 462}],
}
},
],
"rank": [],
"point": [],
@ -245,26 +273,148 @@ class IDACSeason2(IDACBase):
"round_event_exp": [],
"stamp_info": self.stamp_info,
# 0 = use default data, 1+ = server version of timereleasedata response
"timerelease_no": 3,
"timerelease_no": self.timerelease_no,
# 0 = use default data, 1+ = server version of gachadata response
"timerelease_avatar_gacha_no": 3,
"takeover_reward": [],
"timerelease_avatar_gacha_no": self.timerelease_avatar_gacha_no,
# takover reward from other games such as 1=IDZ and 3=SWDC
"takeover_reward": [
{
"takeover_reward_type": 0,
"takeover_reward_data": [
{"reward_no": 1, "reward_category": 1, "reward_type": 5000},
{"reward_no": 2, "reward_category": 25, "reward_type": 1},
],
},
{
"takeover_reward_type": 1,
"takeover_reward_data": [
{"reward_no": 1, "reward_category": 15, "reward_type": 82},
{"reward_no": 1, "reward_category": 15, "reward_type": 190},
{"reward_no": 2, "reward_category": 3, "reward_type": 2},
{"reward_no": 2, "reward_category": 5, "reward_type": 2},
{"reward_no": 3, "reward_category": 3, "reward_type": 3},
{"reward_no": 3, "reward_category": 5, "reward_type": 3},
{"reward_no": 4, "reward_category": 3, "reward_type": 5},
{"reward_no": 4, "reward_category": 5, "reward_type": 5},
{"reward_no": 5, "reward_category": 15, "reward_type": 104},
{"reward_no": 5, "reward_category": 15, "reward_type": 212},
],
},
{
"takeover_reward_type": 2,
"takeover_reward_data": [
{"reward_no": 1, "reward_category": 24, "reward_type": 4052},
{"reward_no": 2, "reward_category": 24, "reward_type": 4053},
{"reward_no": 3, "reward_category": 24, "reward_type": 4054},
{"reward_no": 4, "reward_category": 24, "reward_type": 4055},
{"reward_no": 5, "reward_category": 24, "reward_type": 4056},
{"reward_no": 6, "reward_category": 24, "reward_type": 4057},
{"reward_no": 7, "reward_category": 24, "reward_type": 4058},
],
},
{
"takeover_reward_type": 3,
"takeover_reward_data": [
{"reward_no": 1, "reward_category": 15, "reward_type": 81},
{"reward_no": 1, "reward_category": 15, "reward_type": 189},
{"reward_no": 2, "reward_category": 25, "reward_type": 1},
{"reward_no": 2, "reward_category": 15, "reward_type": 103},
{"reward_no": 2, "reward_category": 15, "reward_type": 211},
],
},
],
"subcard_judge": [
{
"condition_id": 1,
"lower_rank": 0,
"higher_rank": 10,
"condition_start": 2,
"condition_end": 3,
}
],
"special_promote": [{"counter": 1, "online_rank_id": 1}],
"matching_id": 1,
"matching_group": [
"lower_rank": 1,
"higher_rank": 7,
"condition_start": 17,
"condition_end": 20,
"calc": 3,
},
{
"group_id": 1,
"group_percent": 1,
}
"condition_id": 2,
"lower_rank": 1,
"higher_rank": 15,
"condition_start": 10,
"condition_end": 10,
"calc": 20,
},
{
"condition_id": 3,
"lower_rank": 11,
"higher_rank": 15,
"condition_start": 80,
"condition_end": 89,
"calc": 7,
},
{
"condition_id": 3,
"lower_rank": 11,
"higher_rank": 15,
"condition_start": 90,
"condition_end": 100,
"calc": 10,
},
{
"condition_id": 3,
"lower_rank": 1,
"higher_rank": 15,
"condition_start": 0,
"condition_end": 40,
"calc": -100,
},
{
"condition_id": 1,
"lower_rank": 1,
"higher_rank": 15,
"condition_start": 21,
"condition_end": 24,
"calc": 7,
},
{
"condition_id": 1,
"lower_rank": 1,
"higher_rank": 15,
"condition_start": 25,
"condition_end": 25,
"calc": 10,
},
{
"condition_id": 3,
"lower_rank": 16,
"higher_rank": 20,
"condition_start": 70,
"condition_end": 89,
"calc": 6,
},
{
"condition_id": 3,
"lower_rank": 16,
"higher_rank": 20,
"condition_start": 90,
"condition_end": 100,
"calc": 12,
},
{
"condition_id": 3,
"lower_rank": 16,
"higher_rank": 20,
"condition_start": 0,
"condition_end": 50,
"calc": -100,
},
],
"special_promote": [
{"counter": 50, "online_rank_id": 8},
{"counter": 200, "online_rank_id": 16},
{"counter": 255, "online_rank_id": 21},
],
"matching_id": 0,
"matching_group": [
{"group_id": 1, "group_percent": 30},
{"group_id": 2, "group_percent": 50},
{"group_id": 3, "group_percent": 100},
],
"timetrial_disp_date": int(
datetime.strptime("2023-10-01", "%Y-%m-%d").timestamp()
@ -272,21 +422,22 @@ class IDACSeason2(IDACBase):
# price for every car
"buy_car_need_cash": 5000,
# number of buyable shop/customization time limits
"time_extension_limit": 1,
"time_extension_limit": 5,
"collabo_id": 0,
"driver_debut_end_date": int(
datetime.strptime("2029-01-01", "%Y-%m-%d").timestamp()
),
"online_battle_param1": 1,
"online_battle_param2": 1,
"online_battle_param3": 1,
"online_battle_param4": 1,
"online_battle_param5": 1,
"online_battle_param6": 1,
"online_battle_param7": 1,
"online_battle_param8": 1,
"theory_open_version": "1.30",
"theory_close_version": "1.50",
"online_battle_param1": 0,
"online_battle_param2": 0,
"online_battle_param3": 0,
"online_battle_param4": 0,
"online_battle_param5": 0,
"online_battle_param6": 2,
"online_battle_param7": 0,
"online_battle_param8": 3900,
"theory_open_version": "1.30.00",
"theory_close_version": "9.99.99",
# unlocks teh version specific special mode
"special_mode_data": {
"start_dt": int(
datetime.strptime("2023-01-01", "%Y-%m-%d").timestamp()
@ -692,6 +843,20 @@ class IDACSeason2(IDACBase):
vs_info["course_select_priority"] = data.get("course_select_priority")
return vs_info
def _choose_gift_id(self) -> Dict:
gift_data = self.battle_gift_event["gift_data"]
# calculate the total_rate based on the first_distribution_rate
total_rate = sum(gift["first_distribution_rate"] for gift in gift_data)
# randomly choose a number between 1 and the total rate
rand_num = randint(1, total_rate)
cumulative_rate = 0
for gift in gift_data:
# if the random number is less than the cumulative rate, return the gift
cumulative_rate += gift["first_distribution_rate"]
if rand_num <= cumulative_rate:
return gift
async def handle_user_getdata_request(self, data: Dict, headers: Dict):
user_id = int(headers["session"])
@ -926,6 +1091,74 @@ class IDACSeason2(IDACBase):
"point": timetrial["point"],
}
# check if the battle gift event is active
if self.battle_gift_event:
# get the users battle gifts, for the current active battle gift event
battle_gifts = await self.data.item.get_battle_gifts(
user_id, self.battle_gift_event.get("battle_gift_event_id")
)
if battle_gifts:
# just return all aquired gifts
battle_gift_data = [
{
"first_distribution_flag": 0,
"gift_data": [
{
"gift_id": gift["gift_id"],
# 1 = acquired
"gift_get_status": gift["gift_status"],
}
for gift in battle_gifts
],
}
]
else:
# get a random gift from the active battle gift event
gift_id = self._choose_gift_id()["gift_id"]
# save the battle_gift inside the database
await self.data.item.put_battle_gift(
user_id,
{
"battle_gift_event_id": self.battle_gift_event.get(
"battle_gift_event_id"
),
"gift_id": gift_id,
"gift_status": 1, # aquired
},
)
battle_gift_data = [
{
# trigger the first gift animation
"first_distribution_flag": 1,
"gift_data": [
{
# return a random selected gift_id
"gift_id": gift_id,
# set the status to 2 = new gift
"gift_get_status": 2,
}
],
}
]
# get the tips from the database
tips = await self.data.profile.get_profile_tips(user_id, self.version)
# if there are no tips, create a new one
if tips is None:
await self.data.profile.put_profile_tips(user_id, self.version, {})
# get the tips from the database
tips = await self.data.profile.get_profile_tips(user_id, self.version)
tips_info = tips._asdict()
del tips_info["id"]
del tips_info["user"]
del tips_info["version"]
return {
"status_code": "0",
"user_base_data": user_data,
@ -969,7 +1202,9 @@ class IDACSeason2(IDACBase):
"frozen_data": {"frozen_status": 2},
"penalty_data": {"penalty_flag": 0, "penalty_2_level": 0},
"config_data": config_data,
"battle_gift_data": [],
# gift_get_status 2 = new gift, 1 = acquired
# first_distribution related are useless since this is all handled by server
"battle_gift_data": battle_gift_data,
"ticket_data": ticket_data,
"round_event": [],
"last_round_event": [],
@ -984,17 +1219,16 @@ class IDACSeason2(IDACBase):
"car_use_count": [],
"maker_use_count": [],
"story_course": [{"course_id": 0, "count": 1}],
# TODO!
# "driver_debut": {
# "play_count": 137,
# "daily_play": 5,
# "last_play_dt": 0,
# "use_start_date": 0,
# "use_end_date": 0,
# "use_dt": 0,
# "ticket_cnt": 0,
# "ticket_get_bit": 0,
# },
"driver_debut": {
# "play_count": 5,
# "daily_play": 5,
# "last_play_dt": datetime.now().timestamp() - 86400,
# "use_start_date": datetime.now().timestamp() - 86400,
# "use_end_date": datetime.now().timestamp() + 86400,
# "use_dt": datetime.now().timestamp(),
# "ticket_cnt": 1,
# "ticket_get_bit": (2 ** 5) - 1,
},
"theory_data": theory_data,
"theory_course_data": theory_course_data,
"theory_partner_data": theory_partner_data,
@ -1004,6 +1238,7 @@ class IDACSeason2(IDACBase):
"season_rewards_data": [],
"timetrial_event_data": timetrial_event_data,
"special_mode_hint_data": {"story_type": 0, "hint_display_flag": 0},
"tips_info": tips_info,
}
async def handle_timetrial_getbestrecordpreta_request(
@ -1596,6 +1831,12 @@ class IDACSeason2(IDACBase):
user_id, self.timetrial_event_id, event_point
)
# update the tips play count
tips = await self.data.profile.get_profile_tips(user_id, self.version)
await self.data.profile.put_profile_tips(
user_id, self.version, {"timetrial_play_count": tips["timetrial_play_count"] + 1}
)
return {
"status_code": "0",
"course_rank": course_rank,
@ -1733,6 +1974,12 @@ class IDACSeason2(IDACBase):
},
)
# update the tips play count
tips = await self.data.profile.get_profile_tips(user_id, self.version)
await self.data.profile.put_profile_tips(
user_id, self.version, {"story_play_count": tips["story_play_count"] + 1}
)
return {
"status_code": "0",
"story_data": await self._generate_story_data(user_id),
@ -1806,6 +2053,12 @@ class IDACSeason2(IDACBase):
# finally save the special mode with story_type=4 in database
await self.data.item.put_challenge(user_id, data)
# update the tips play count
tips = await self.data.profile.get_profile_tips(user_id, self.version)
await self.data.profile.put_profile_tips(
user_id, self.version, {"special_play_count": tips["special_play_count"] + 1}
)
return {
"status_code": "0",
"special_mode_data": await self._generate_special_data(user_id),
@ -1886,6 +2139,12 @@ class IDACSeason2(IDACBase):
# finally save the challenge mode with story_type=3 in database
await self.data.item.put_challenge(user_id, data)
# update the tips play count
tips = await self.data.profile.get_profile_tips(user_id, self.version)
await self.data.profile.put_profile_tips(
user_id, self.version, {"challenge_play_count": tips["challenge_play_count"] + 1}
)
return {
"status_code": "0",
"challenge_mode_data": await self._generate_challenge_data(user_id),
@ -2118,7 +2377,12 @@ class IDACSeason2(IDACBase):
# not required?
mode_id = data.pop("mode_id")
standby_play_flag = data.pop("standby_play_flag")
# save the tips list in database
tips_list = data.pop("tips_list")
await self.data.profile.put_profile_tips(
user_id, self.version, {"tips_list": tips_list}
)
# save stock data in database
await self._save_stock_data(user_id, stock_data)
@ -2522,6 +2786,12 @@ class IDACSeason2(IDACBase):
},
)
# update the tips play count
tips = await self.data.profile.get_profile_tips(user_id, self.version)
await self.data.profile.put_profile_tips(
user_id, self.version, {"theory_play_count": tips["theory_play_count"] + 1}
)
return {
"status_code": "0",
"played_powerhouse_lv": data.get("powerhouse_lv"),
@ -2584,7 +2854,9 @@ class IDACSeason2(IDACBase):
"bothwin_penalty": 1,
}
async def handle_user_updateonlinebattleresult_request(self, data: Dict, headers: Dict):
async def handle_user_updateonlinebattleresult_request(
self, data: Dict, headers: Dict
):
user_id = headers["session"]
stock_data: Dict = data.pop("stock_obj")
@ -2640,7 +2912,15 @@ class IDACSeason2(IDACBase):
},
)
vs_info = self._update_vs_info(user_id, IDACConstants.BATTLE_MODE_ONLINE, data)
vs_info = await self._update_vs_info(
user_id, IDACConstants.BATTLE_MODE_ONLINE, data
)
# update the tips play count
tips = await self.data.profile.get_profile_tips(user_id, self.version)
await self.data.profile.put_profile_tips(
user_id, self.version, {"online_battle_play_count": tips["online_battle_play_count"] + 1}
)
return {
"status_code": "0",
@ -2671,9 +2951,20 @@ class IDACSeason2(IDACBase):
# no idea?
result = data.pop("result")
# save the received battle gift in database, hopefully that works
battle_gift_event_id = data.pop("battle_gift_event_id")
gift_id = data.pop("gift_id")
await self.data.item.put_battle_gift(
user_id,
{
"battle_gift_event_id": battle_gift_event_id,
"gift_id": gift_id,
"gift_status": 1, # aquired
},
)
# save stock data in database
await self._save_stock_data(user_id, stock_data)
@ -2726,6 +3017,12 @@ class IDACSeason2(IDACBase):
user_id, IDACConstants.BATTLE_MODE_OFFLINE, data
)
# update the tips play count
tips = await self.data.profile.get_profile_tips(user_id, self.version)
await self.data.profile.put_profile_tips(
user_id, self.version, {"store_battle_play_count": tips["store_battle_play_count"] + 1}
)
return {
"status_code": "0",
"vsinfo_data": vs_info,