This commit is contained in:
memetrollsXD 2022-08-05 07:40:30 +02:00
commit 1f2b9f5726
No known key found for this signature in database
GPG Key ID: 105C2F3417AC32CD
16 changed files with 299 additions and 102 deletions

View File

@ -17,8 +17,8 @@ export default async function handle(command: Command) {
let count: number = 1; let count: number = 1;
let level: number = 1; let level: number = 1;
let rank: number = 1; let rank: number = 0;
let promotion: number = 1; let promotion: number = 0;
for (let i = 2; i < command.args.length; i++) { for (let i = 2; i < command.args.length; i++) {
const arg = command.args[i]; const arg = command.args[i];
@ -52,6 +52,9 @@ export default async function handle(command: Command) {
break; break;
} }
} }
// Sync session.
await player.session.sync();
} }
async function handleGive(player: Player, itemId: number, count:number, level: number, rank: number, promotion: number) { async function handleGive(player: Player, itemId: number, count:number, level: number, rank: number, promotion: number) {
@ -96,7 +99,10 @@ async function handleGiveAll(player: Player) {
const inventory = await player.getInventory(); const inventory = await player.getInventory();
for (const entry of ItemExcel.all()) { for (const entry of ItemExcel.all()) {
const count = entry.ItemType == "Material" ? 100 : 1; const count =
(entry.ItemType == "Material") ? 1000 :
(entry.ItemType == "Virtual") ? 10_000_000 :
1;
await inventory.addItem(entry.ID, count); await inventory.addItem(entry.ID, count);
} }

View File

@ -20,11 +20,9 @@ export default async function handle(command: Command) {
if (!planeID) return c.log("Usage: /scene <planeID>"); if (!planeID) return c.log("Usage: /scene <planeID>");
Interface.target.player.db.posData = { // Update scene information on player.
floorID: planeID.StartFloorID, Interface.target.player.db.posData.planeID = planeID!.PlaneID;
planeID: planeID.PlaneID, Interface.target.player.db.posData.floorID = planeID!.StartFloorID;
pos: Interface.target.player.db.posData.pos
};
await Interface.target.player.save() await Interface.target.player.save()
//ty for tamilpp25 scene //ty for tamilpp25 scene

View File

@ -49,9 +49,9 @@ export default class Avatar {
baseAvatarId: 1001, baseAvatarId: 1001,
avatarType: AvatarType.AVATAR_FORMAL_TYPE, avatarType: AvatarType.AVATAR_FORMAL_TYPE,
level: 1, level: 1,
exp: 1, exp: 0,
promotion: 1, promotion: 0,
rank: 1, rank: 0,
equipmentUniqueId: 20003, equipmentUniqueId: 20003,
equipRelicList: [], equipRelicList: [],
skilltreeList: [], skilltreeList: [],
@ -111,9 +111,9 @@ export default class Avatar {
baseAvatarId: baseAvatarId, baseAvatarId: baseAvatarId,
avatarType: AvatarType.AVATAR_FORMAL_TYPE, avatarType: AvatarType.AVATAR_FORMAL_TYPE,
level: 1, level: 1,
exp: 1, exp: 0,
promotion: 1, promotion: 0,
rank: 1, rank: 0,
equipmentUniqueId: 20003, equipmentUniqueId: 20003,
equipRelicList: [], equipRelicList: [],
skilltreeList: [], skilltreeList: [],

View File

@ -101,13 +101,24 @@ export default class Inventory {
} }
switch (itemData.ItemType) { switch (itemData.ItemType) {
case "Virtual": return 0; // ToDo: Handle virtual items. case "Virtual": return this.getVirtualItemCount(id);
case "Material": return this.db.materials[id] ?? 0; case "Material": return this.db.materials[id] ?? 0;
} }
return 0; return 0;
} }
private getVirtualItemCount(id: number) : number {
// ToDo: Figure out which virtual item ID is what.
switch (id) {
case 2:
return this.player.db.basicInfo.scoin;
break;
}
return 0;
}
/******************************************************************************** /********************************************************************************
Add items to the inventory. Add items to the inventory.
********************************************************************************/ ********************************************************************************/
@ -151,6 +162,14 @@ export default class Inventory {
*/ */
public async addVirtualItem(id: number, count: number) { public async addVirtualItem(id: number, count: number) {
// ToDo: Figure out which virtual item ID is what. // ToDo: Figure out which virtual item ID is what.
switch (id) {
case 2:
this.player.db.basicInfo.scoin += count;
break;
}
// Save.
this.player.save();
} }
/** /**

View File

@ -43,7 +43,11 @@ interface PlayerI {
posData: { posData: {
floorID: number; floorID: number;
planeID: number; planeID: number;
pos: Vector; pos: {
x: number,
y: number,
z: number
};
} }
} }
@ -52,7 +56,7 @@ export default class Player {
public readonly scene: Scene; public readonly scene: Scene;
private inventory!: Inventory; private inventory!: Inventory;
private constructor(readonly session: Session, public db: PlayerI) { private constructor(readonly session: Session, public readonly db: PlayerI) {
this.uid = db._id; this.uid = db._id;
this.scene = new Scene(this); this.scene = new Scene(this);
} }
@ -133,13 +137,13 @@ export default class Player {
heroBasicType: HeroBasicType.BoyWarrior, heroBasicType: HeroBasicType.BoyWarrior,
basicInfo: { basicInfo: {
exp: 0, exp: 0,
level: 1, level: 70,
hcoin: 0, hcoin: 0,
mcoin: 0, mcoin: 0,
nickname: acc.name, nickname: acc.name,
scoin: 0, scoin: 0,
stamina: 100, stamina: 180,
worldLevel: 1, worldLevel: 6,
}, },
lineup: { lineup: {
curIndex: 0, curIndex: 0,
@ -155,34 +159,25 @@ export default class Player {
} }
}, },
banned: false banned: false
} as PlayerI } as PlayerI;
const baseLineup = {
avatarList: [1001],
extraLineupType: ExtraLineupType.LINEUP_NONE,
index: 0,
isVirtual: false,
leaderSlot: 0,
mp: 100, // ?? Not sure what this is
name: "",
planeId: 10001
}
const LINEUPS = 6; const LINEUPS = 6;
for (let i = 0; i < LINEUPS; i++) { for (let i = 0; i < LINEUPS; i++) {
const copy = { const l : LineupI = {
...baseLineup, avatarList: [1001],
extraLineupType: ExtraLineupType.LINEUP_NONE,
index: i, index: i,
name: `Team ${i}` isVirtual: false,
leaderSlot: 0,
mp: 100,
name: `Team ${i}`,
planeId: 10001
}; };
dataObj.lineup.lineups[i] = copy; dataObj.lineup.lineups[i] = l;
} }
const player = new Player(session, dataObj); const player = new Player(session, dataObj);
await Avatar.addAvatarToPlayer(player, 1001); await Avatar.addAvatarToPlayer(player, 1001);
// await Avatar.create(uid, 1001, 0);
// Save to database and return. // Save to database and return.
await db.set("players", dataObj); await db.set("players", dataObj);

View File

@ -0,0 +1,88 @@
import { AvatarExpUpCsReq, AvatarExpUpScRsp } from "../../data/proto/StarRail";
import Avatar from "../../db/Avatar";
import { PayItemData } from "../../db/Inventory";
import AvatarExcel from "../../util/excel/AvatarExcel";
import AvatarExpItemExcel from "../../util/excel/AvatarExpItemExcel";
import AvatarPromotionExcel from "../../util/excel/AvatarPromotionExcel";
import ExpTypeExcel from "../../util/excel/ExpTypeExcel";
import Packet from "../kcp/Packet";
import Session from "../kcp/Session";
export default async function handle(session: Session, packet: Packet) {
const body = packet.body as AvatarExpUpCsReq;
const inventory = await session.player.getInventory();
// Get the target avatar.
const avatarId = body.baseAvatarId;
const avatar = await Avatar.loadAvatarForPlayer(session.player, avatarId);
const avatarExcelData = AvatarExcel.fromId(avatarId);
// Determine the next level cap based on the avatar's current promotion.
const levelCap = AvatarPromotionExcel.fromId(`${avatarId}:${avatar.db.promotion}`).MaxLevel;
// Determine the EXP we get from the consumed items.
let exp = 0;
const costMaterialList = [];
for (const item of body.itemCost!.itemList) {
// Determine amount of EXP given by that item.
// We know that the cost items given in this Req will be `PileItem`s.
const expPerItem = AvatarExpItemExcel.fromId(item.pileItem!.itemId).Exp;
// Add EXP for the number of items consumed.
exp += expPerItem * item.pileItem!.itemNum;
// Add material to cost.
costMaterialList.push({ id: item.pileItem!.itemId, count: item.pileItem!.itemNum } as PayItemData);
}
// Determine cost, which is always 10% of EXP, and add to the list of cost materials.
const coinCost = exp * 0.1;
costMaterialList.push({ id: 2, count: coinCost } as PayItemData);
// Try consuming materials.
const success = await inventory.payItems(costMaterialList);
if (!success) {
// ToDo: Correct retcode.
session.send(AvatarExpUpScRsp, { retcode: 1, returnItemList: [] } as AvatarExpUpScRsp);
return;
}
await inventory.save();
// Cost has been paid - now level up.
let currentAvatarExp = avatar.db.exp + exp;
let nextRequiredExp = ExpTypeExcel.fromId(`${avatarExcelData.ExpGroup}:${avatar.db.level}`).Exp;
while (currentAvatarExp >= nextRequiredExp && avatar.db.level < levelCap) {
// Increase level.
avatar.db.level++;
// Deduct EXP necessary for this level.
currentAvatarExp -= nextRequiredExp;
// Determine EXP necessary for the next level.
nextRequiredExp = ExpTypeExcel.fromId(`${avatarExcelData.ExpGroup}:${avatar.db.level}`).Exp;
}
// Calculate the character's new EXP and any excess EXP.
let excessExp = 0;
if (avatar.db.level == levelCap && currentAvatarExp >= nextRequiredExp) {
avatar.db.exp = nextRequiredExp;
excessExp = currentAvatarExp - nextRequiredExp;
}
else {
avatar.db.exp = currentAvatarExp;
}
// Save.
await avatar.save();
// ToDo: Handle return items.
// Done. Sync and send response.
await session.sync();
session.send(AvatarExpUpScRsp, {
retcode: 0,
returnItemList: []
} as AvatarExpUpScRsp);
}

View File

@ -6,7 +6,7 @@ import Session from "../kcp/Session";
export default async function handle(session: Session, packet: Packet) { export default async function handle(session: Session, packet: Packet) {
const body = packet.body as DressAvatarCsReq; const body = packet.body as DressAvatarCsReq;
let avatar = await Avatar.loadAvatarForPlayer(session.player, body.baseAvatarId); const avatar = await Avatar.loadAvatarForPlayer(session.player, body.baseAvatarId);
avatar.db.equipmentUniqueId = body.equipmentUniqueId; avatar.db.equipmentUniqueId = body.equipmentUniqueId;
await avatar.save(); await avatar.save();

View File

@ -11,31 +11,6 @@ export default async function handle(session: Session, packet: Packet) {
// Replace avatar in the player's lineup. // Replace avatar in the player's lineup.
const slot = body.slot ?? 0; const slot = body.slot ?? 0;
session.player.db.lineup.lineups[session.player.db.lineup.curIndex].avatarList[slot] = body.baseAvatarId; session.player.db.lineup.lineups[session.player.db.lineup.curIndex].avatarList[slot] = body.baseAvatarId;
/*
let lineup = await session.player.getLineup();
const avatarList = [];
// What in the fuck is the purpose of this loop supposed to be?!
for (const avatarId in lineup) {
const avatar = await Avatar.fromUID(session.player.db._id, Number(avatarId));
if (avatar.length === 0) return session.c.warn(`Avatar ${body.baseAvatarId} not found`);
if (avatar) avatarList.push(avatar[0]);
}
lineup.avatarList[slot] = {
avatarType: AvatarType.AVATAR_FORMAL_TYPE,
hp: 10000,
id: body.baseAvatarId,
satiety: 100,
slot,
sp: 10000
};
if (body.extraLineupType) lineup.extraLineupType = body.extraLineupType;
session.player.setLineup(lineup);
*/
await session.player.save(); await session.player.save();
session.send(SyncLineupNotify, { session.send(SyncLineupNotify, {

View File

@ -0,0 +1,40 @@
import { PromoteAvatarCsReq, PromoteAvatarScRsp } from "../../data/proto/StarRail";
import Avatar from "../../db/Avatar";
import { PayItemData } from "../../db/Inventory";
import AvatarExcel from "../../util/excel/AvatarExcel";
import AvatarExpItemExcel from "../../util/excel/AvatarExpItemExcel";
import AvatarPromotionExcel from "../../util/excel/AvatarPromotionExcel";
import ExpTypeExcel from "../../util/excel/ExpTypeExcel";
import Packet from "../kcp/Packet";
import Session from "../kcp/Session";
export default async function handle(session: Session, packet: Packet) {
const body = packet.body as PromoteAvatarCsReq;
const inventory = await session.player.getInventory();
// Get the target avatar.
const avatarId = body.baseAvatarId;
const avatar = await Avatar.loadAvatarForPlayer(session.player, avatarId);
const promotionExcelData = AvatarPromotionExcel.fromId(`${avatarId}:${avatar.db.promotion}`);
// Build list of consumed items. We take this from the excel, instead of the Req.
const costMaterialList = promotionExcelData.PromotionCostList.map(c => { return { id: c.ItemID, count: c.ItemNum } as PayItemData });
// Try consuming materials.
const success = await inventory.payItems(costMaterialList);
if (!success) {
// ToDo: Correct retcode.
session.send(PromoteAvatarScRsp, { retcode: 1 } as PromoteAvatarScRsp);
return;
}
await inventory.save();
// Promote the avatar and save.
avatar.db.promotion++;
await avatar.save();
// Done. Sync and send response.
await session.sync();
session.send(PromoteAvatarScRsp, { retcode: 0 } as PromoteAvatarScRsp);
}

View File

@ -1,5 +1,6 @@
import { RankUpAvatarCsReq, RankUpAvatarScRsp } from "../../data/proto/StarRail"; import { RankUpAvatarCsReq, RankUpAvatarScRsp } from "../../data/proto/StarRail";
import Avatar from "../../db/Avatar"; import Avatar from "../../db/Avatar";
import { PayItemData } from "../../db/Inventory";
import Logger from "../../util/Logger"; import Logger from "../../util/Logger";
import Packet from "../kcp/Packet"; import Packet from "../kcp/Packet";
import Session from "../kcp/Session"; import Session from "../kcp/Session";
@ -7,33 +8,37 @@ const c = new Logger("RankUpAvatarCsReq");
export default async function handle(session: Session, packet: Packet) { export default async function handle(session: Session, packet: Packet) {
const body = packet.body as RankUpAvatarCsReq; const body = packet.body as RankUpAvatarCsReq;
const dataObj: RankUpAvatarScRsp = {
retcode: 0
};
let retcode = 0; try {
if (body.costData) {
const inv = await session.player.getInventory(); const inv = await session.player.getInventory();
if (!body.costData) return;
const list = body.costData.itemList;
const arr: Array<PayItemData> = [];
for (const item of body.costData.itemList) { for (let i = 0; i < list.length; i++) {
if (!item.pileItem) return; const item = list[i];
if (!item.pileItem) continue;
const succ = await inv.payItems([{ arr.push({
count: item.pileItem.itemNum, count: item.pileItem.itemNum,
id: item.pileItem.itemId, id: item.pileItem.itemId
}]); });
if (!succ) {
retcode = 1301;
c.debug(`failed to pay item ${item.pileItem.itemId}`, item.pileItem);
break;
}
} }
}
if (retcode === 0) { if (await inv.payItems(arr)) {
const avatar = await Avatar.loadAvatarForPlayer(session.player, body.baseAvatarId); const avatar = await Avatar.loadAvatarForPlayer(session.player, body.baseAvatarId);
avatar.db.rank = body.rank // gidra moment: ez hack avatar.db.rank = body.rank // gidra moment: ez hack
await avatar.save(); await avatar.save();
} else {
dataObj.retcode = 1301;
}
} catch (e) {
c.error(e as Error);
dataObj.retcode = 2;
} finally {
session.send(RankUpAvatarScRsp, dataObj);
session.sync();
} }
session.send(RankUpAvatarScRsp, { retcode });
session.sync();
} }

View File

@ -23,7 +23,11 @@ export default async function handle(session: Session, packet: Packet) {
entity.pos = motion.pos; entity.pos = motion.pos;
if (entity instanceof ActorEntity) { if (entity instanceof ActorEntity) {
entity.mapLayer = entityMotion.mapLayer; entity.mapLayer = entityMotion.mapLayer;
session.player.db.posData.pos = motion.pos!; session.player.db.posData.pos = {
x: motion.pos.x,
y: motion.pos.y,
z: motion.pos.z
};
} }
} }

View File

@ -17,14 +17,14 @@ export default async function handle(session: Session, packet: Packet) {
rounds: 1, rounds: 1,
status: ChallengeStatus.CHALLENGE_DOING, status: ChallengeStatus.CHALLENGE_DOING,
killMonsterList: [{ killMonsterList: [{
monsterId: 1001010, monsterId: 8013010,
killNum: 1, killNum: 1,
}] }]
}, },
maze: { maze: {
// ? Data from MappingInfoExcelTable // ? Data from MappingInfoExcelTable
id: 30101, id: 30101,
mapEntryId: 1000, mapEntryId: 10001,
floor: { floor: {
floorId: 20121001, floorId: 20121001,
scene: { scene: {

View File

@ -1,6 +1,7 @@
import fs from 'fs'; import fs from 'fs';
import { resolve } from 'path'; import {resolve} from 'path';
import Logger from './Logger'; import Logger from './Logger';
const c = new Logger("Banner"); const c = new Logger("Banner");
type Banner = { type Banner = {
@ -18,15 +19,21 @@ function r(...args: string[]) {
export default class Banners { export default class Banners {
public static config: Banner[]; public static config: Banner[];
public static init(){ public static init() {
Banners.readConfig(); Banners.readConfig();
} }
private static readConfig(){ private static readConfig() {
let config: Banner[]; let config: Banner[];
const defaultConfig: Banner[] = [
{ const defaultConfig: Banner[] = [];
gachaId: 1001,
// TODO: figure where is GachaBasicInfoConfigExcelTable. Temporary hardcode
const bannersID = [1001, 2001, 2002, 3001, 3002, 4001]
for (let i = 0; i < bannersID.length; i++) {
defaultConfig.push({
gachaId: bannersID[i],
detailWebview: "", detailWebview: "",
rateUpItems4: [ rateUpItems4: [
1001, 1103 1001, 1103
@ -35,13 +42,13 @@ export default class Banners {
1102 1102
], ],
costItemId: 101 // Star Rail Pass costItemId: 101 // Star Rail Pass
} as Banner } as Banner)
]; }
try { try {
config = JSON.parse(fs.readFileSync(r('../../banners.json')).toString()); config = JSON.parse(fs.readFileSync(r('../../banners.json')).toString());
for(const [index, gachaBanner] of Object.entries(config)){ for (const [index, gachaBanner] of Object.entries(config)) {
const missing = Object.keys(defaultConfig[0]).filter(key => !gachaBanner.hasOwnProperty(key)); const missing = Object.keys(defaultConfig[0]).filter(key => !gachaBanner.hasOwnProperty(key));
if (missing.length > 0) { if (missing.length > 0) {
c.log(`Missing ${missing.join(', ')}, using default values.`); c.log(`Missing ${missing.join(', ')}, using default values.`);

View File

@ -0,0 +1,20 @@
import _AvatarExpItemConfigExcelTable from "../../data/excel/AvatarExpItemConfigExcelTable.json";
type AvatarExpItemConfigExcelTableEntry = typeof _AvatarExpItemConfigExcelTable[keyof typeof _AvatarExpItemConfigExcelTable]
const AvatarExpItemConfigExcelTable = _AvatarExpItemConfigExcelTable as { [key: string]: AvatarExpItemConfigExcelTableEntry };
export default class AvatarExpItemExcel {
private constructor() {
}
public static all() : AvatarExpItemConfigExcelTableEntry[] {
return Object.values(AvatarExpItemConfigExcelTable);
}
public static fromId(id: number) : AvatarExpItemConfigExcelTableEntry {
return AvatarExpItemConfigExcelTable[id];
}
public static fromIds(ids: number[]): AvatarExpItemConfigExcelTableEntry[] {
return ids.map(id => AvatarExpItemExcel.fromId(id));
}
}

View File

@ -0,0 +1,20 @@
import _AvatarPromotionExcelTable from "../../data/excel/AvatarPromotionExcelTable.json";
type AvatarPromotionExcelTableEntry = typeof _AvatarPromotionExcelTable[keyof typeof _AvatarPromotionExcelTable]
const AvatarPromotionExcelTable = _AvatarPromotionExcelTable as { [key: string]: AvatarPromotionExcelTableEntry };
export default class AvatarPromotionExcel {
private constructor() {
}
public static all() : AvatarPromotionExcelTableEntry[] {
return Object.values(AvatarPromotionExcelTable);
}
public static fromId(id: string) : AvatarPromotionExcelTableEntry {
return AvatarPromotionExcelTable[id];
}
public static fromIds(ids: string[]): AvatarPromotionExcelTableEntry[] {
return ids.map(id => AvatarPromotionExcel.fromId(id));
}
}

View File

@ -0,0 +1,20 @@
import _ExpTypeExcelTable from "../../data/excel/ExpTypeExcelTable.json";
type ExpTypeExcelTableEntry = typeof _ExpTypeExcelTable[keyof typeof _ExpTypeExcelTable]
const ExpTypeExcelTable = _ExpTypeExcelTable as { [key: string]: ExpTypeExcelTableEntry };
export default class ExpTypeExcel {
private constructor() {
}
public static all() : ExpTypeExcelTableEntry[] {
return Object.values(ExpTypeExcelTable);
}
public static fromId(id: string) : ExpTypeExcelTableEntry {
return ExpTypeExcelTable[id];
}
public static fromIds(ids: string[]): ExpTypeExcelTableEntry[] {
return ids.map(id => ExpTypeExcel.fromId(id));
}
}