(Only) Enter Forgotten Hall, Set Game Mode, Clean Up Map/Maze Excels (#56)

* Implement avatar levelup and promotion.

* Clean up maz and map excels, set the correct game mode based on plane type, make forgotten hall enterable.

* Remove non-working spawn command.
This commit is contained in:
GanyusLeftHorn 2022-08-07 16:16:40 +02:00 committed by GitHub
parent 5e1209deba
commit 22cd1f6ba1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 157 additions and 99 deletions

View File

@ -3,6 +3,7 @@ import { ActorEntity } from "../game/entities/Actor";
import Interface, { Command } from "./Interface"; import Interface, { Command } from "./Interface";
import { GetCurSceneInfoScRsp } from "../data/proto/StarRail"; import { GetCurSceneInfoScRsp } from "../data/proto/StarRail";
import MazePlaneExcel from "../util/excel/MazePlaneExcel"; import MazePlaneExcel from "../util/excel/MazePlaneExcel";
import MapEntryExcel from "../util/excel/MapEntryExcel";
const c = new Logger("/scene", "blue"); const c = new Logger("/scene", "blue");
export default async function handle(command: Command) { export default async function handle(command: Command) {
@ -11,8 +12,8 @@ export default async function handle(command: Command) {
return; return;
} }
const planeID = MazePlaneExcel.fromPlaneId(parseInt(command.args[0])) const planeID = MazePlaneExcel.fromPlaneId(parseInt(command.args[0]));
const uid = Interface.target.player.db._id; const entryId = MapEntryExcel.fromFloorId(planeID.StartFloorID).ID;
const posData = Interface.target.player.db.posData; const posData = Interface.target.player.db.posData;
const lineup2 = await Interface.target.player.getLineup(); const lineup2 = await Interface.target.player.getLineup();
@ -35,9 +36,9 @@ export default async function handle(command: Command) {
curAvatarEntity curAvatarEntity
], ],
entityBuffList: [], entityBuffList: [],
entryId: 10001, entryId: entryId,
envBuffList: [], envBuffList: [],
gameModeType: 1, gameModeType: MazePlaneExcel.getGameModeForPlaneType(planeID.PlaneType),
lightenSectionList: [] lightenSectionList: []
}, },
} as unknown as GetCurSceneInfoScRsp); } as unknown as GetCurSceneInfoScRsp);

View File

@ -13,9 +13,11 @@ export class Scene {
public spawnEntity(entity: Entity, silent: boolean = false) { public spawnEntity(entity: Entity, silent: boolean = false) {
this.entities.set(entity.entityId, entity); this.entities.set(entity.entityId, entity);
if (!silent) { if (!silent) {
this.player.session.send(SceneEntityUpdateScNotify, { const dataObj : SceneEntityUpdateScNotify = {
entityList: [entity.getSceneEntityInfo()] entityList: [entity.getSceneEntityInfo()]
} as SceneEntityUpdateScNotify); };
this.player.session.send(SceneEntityUpdateScNotify, dataObj);
} }
} }

View File

@ -10,15 +10,16 @@ export class ActorEntity extends Entity
super(scene, pos, rot); super(scene, pos, rot);
} }
public getSceneEntityInfo(): SceneEntityInfo { public getSceneActorInfo(): SceneEntityInfo {
const sceneEntityInfo = super.getSceneEntityInfo(); return {
sceneEntityInfo.actor = { ...super.getSceneEntityInfo(),
avatarType: AvatarType.AVATAR_FORMAL_TYPE, actor: {
baseAvatarId: this.avatarId, avatarType: AvatarType.AVATAR_FORMAL_TYPE,
uid: this.scene.player.db._id, baseAvatarId: this.avatarId,
mapLayer: 0 //? uid: this.scene.player.db._id,
mapLayer: 0 //?
}
}; };
return sceneEntityInfo;
} }
public getEntityType(): EntityType { public getEntityType(): EntityType {

View File

@ -1,4 +1,5 @@
import { EnterMazeCsReq, EnterMazeScRsp } from "../../data/proto/StarRail"; import { EnterMazeCsReq, EnterMazeScRsp } from "../../data/proto/StarRail";
import MapEntryExcel from "../../util/excel/MapEntryExcel";
import MazePlaneExcel from "../../util/excel/MazePlaneExcel"; import MazePlaneExcel from "../../util/excel/MazePlaneExcel";
import Packet from "../kcp/Packet"; import Packet from "../kcp/Packet";
import Session from "../kcp/Session"; import Session from "../kcp/Session";
@ -6,7 +7,8 @@ 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 EnterMazeCsReq; const body = packet.body as EnterMazeCsReq;
const mazeEntry = MazePlaneExcel.getEntry(body.entryId); const mazeEntry = MapEntryExcel.fromId(body.entryId);
const mazePlane = MazePlaneExcel.fromPlaneId(mazeEntry.PlaneID);
let curLineup = await session.player.getLineup(); let curLineup = await session.player.getLineup();
curLineup.planeId = mazeEntry.PlaneID; curLineup.planeId = mazeEntry.PlaneID;
@ -23,7 +25,7 @@ export default async function handle(session: Session, packet: Packet) {
scene: { scene: {
planeId: mazeEntry.PlaneID, planeId: mazeEntry.PlaneID,
floorId: mazeEntry.FloorID, floorId: mazeEntry.FloorID,
gameModeType: 1, gameModeType: MazePlaneExcel.getGameModeForPlaneType(mazePlane.PlaneType),
} }
}, },
id: mazeEntry.PlaneID, id: mazeEntry.PlaneID,

View File

@ -1,28 +1,38 @@
import { GetCurSceneInfoScRsp, Vector } from "../../data/proto/StarRail"; import { GetCurSceneInfoScRsp, MotionInfo, SceneEntityInfo, SceneNpcMonsterInfo, Vector } from "../../data/proto/StarRail";
import { ActorEntity } from "../../game/entities/Actor"; import { ActorEntity } from "../../game/entities/Actor";
import MapEntryExcel from "../../util/excel/MapEntryExcel";
import MazePlaneExcel from "../../util/excel/MazePlaneExcel";
import Packet from "../kcp/Packet"; import Packet from "../kcp/Packet";
import Session from "../kcp/Session"; import Session from "../kcp/Session";
export default async function handle(session: Session, packet: Packet) { export default async function handle(session: Session, packet: Packet) {
// Get data.
const posData = session.player.db.posData; const posData = session.player.db.posData;
const _lineup = session.player.db.lineup; const _lineup = session.player.db.lineup;
const lineup = _lineup.lineups[_lineup.curIndex]; const lineup = _lineup.lineups[_lineup.curIndex];
const curAvatarEntity = new ActorEntity(session.player.scene, lineup.avatarList[0], posData.pos); const curAvatarEntity = new ActorEntity(session.player.scene, lineup.avatarList[lineup.leaderSlot], posData.pos);
const entryId = MapEntryExcel.fromFloorId(posData.floorID).ID;
const mazePlane = MazePlaneExcel.fromPlaneId(posData.planeID);
// Scene management.
session.player.scene.spawnEntity(curAvatarEntity, true);
session.player.scene.entryId = entryId;
// Send response.
session.send(GetCurSceneInfoScRsp, { session.send(GetCurSceneInfoScRsp, {
retcode: 0, retcode: 0,
scene: { scene: {
planeId: posData.planeID, planeId: posData.planeID,
floorId: posData.floorID, floorId: posData.floorID,
entityList: [ entityList: [
curAvatarEntity curAvatarEntity.getSceneEntityInfo()
], ],
lightenSectionList: [],
leaderEntityId: curAvatarEntity.entityId,
entityBuffList: [], entityBuffList: [],
entryId: 10001, entryId: entryId,
envBuffList: [], envBuffList: [],
gameModeType: 1, gameModeType: MazePlaneExcel.getGameModeForPlaneType(mazePlane.PlaneType),
lightenSectionList: []
}, },
} as unknown as GetCurSceneInfoScRsp); } as GetCurSceneInfoScRsp);
session.player.scene.spawnEntity(curAvatarEntity, true);
session.player.scene.entryId = 10001;
} }

View File

@ -1,4 +1,5 @@
import { GetMazeMapInfoCsReq, GetMazeMapInfoScRsp } from "../../data/proto/StarRail"; import { GetMazeMapInfoCsReq, GetMazeMapInfoScRsp } from "../../data/proto/StarRail";
import MapEntryExcel from "../../util/excel/MapEntryExcel";
import MappingInfoExcel from "../../util/excel/MappingInfoExcel"; import MappingInfoExcel from "../../util/excel/MappingInfoExcel";
import MazePlaneExcel from "../../util/excel/MazePlaneExcel"; import MazePlaneExcel from "../../util/excel/MazePlaneExcel";
import Packet from "../kcp/Packet"; import Packet from "../kcp/Packet";
@ -24,7 +25,7 @@ export default async function handle(session: Session, packet: Packet) {
dataObj.lightenSectionList.push(i) dataObj.lightenSectionList.push(i)
} }
dataObj.unlockTeleportList = MazePlaneExcel.getAllEntries().map(x => x.ID); dataObj.unlockTeleportList = MapEntryExcel.all().map(x => x.ID);
session.send(GetMazeMapInfoScRsp, dataObj); session.send(GetMazeMapInfoScRsp, dataObj);
} }

View File

@ -5,6 +5,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 SceneEntityMoveCsReq; const body = packet.body as SceneEntityMoveCsReq;
if (session.player.scene.entryId !== body.entryId) { if (session.player.scene.entryId !== body.entryId) {
return; return;
} }

View File

@ -1,59 +1,76 @@
import { ChallengeStatus, ExtraLineupType, StartChallengeCsReq, StartChallengeScRsp } from "../../data/proto/StarRail"; import { ChallengeStatus, CurChallenge, ExtraLineupType, Maze, SceneActorInfo, SceneEntityInfo, SceneInfo, SceneNpcInfo, SceneNpcMonsterInfo, ScenePropInfo, StartChallengeCsReq, StartChallengeScRsp } from "../../data/proto/StarRail";
import { ActorEntity } from "../../game/entities/Actor";
import Packet from "../kcp/Packet"; import Packet from "../kcp/Packet";
import Session from "../kcp/Session"; import Session from "../kcp/Session";
// StartChallengeCsReq { challengeId: 101 } // StartChallengeCsReq { challengeId: 101 }
export default async function handle(session: Session, packet: Packet) { export default async function handle(session: Session, packet: Packet) {
const body = packet.body as StartChallengeCsReq; const body = packet.body as StartChallengeCsReq;
console.log(JSON.stringify(body, undefined, 4));
// TODO: This packet is just a base // TODO: This packet is just a base
const _lineup = session.player.db.lineup;
const lineup = _lineup.lineups[_lineup.curIndex];
const curAvatarEntity = new ActorEntity(session.player.scene, lineup.avatarList[lineup.leaderSlot], { x: 0, y: 0, z: 0 });
session.send(StartChallengeScRsp, { session.send(StartChallengeScRsp, {
retcode: 0, retcode: 0,
curChallenge: { curChallenge: {
challengeId: body.challengeId, challengeId: body.challengeId,
deadAvatarNum: 0,
extraLineupType: ExtraLineupType.LINEUP_CHALLENGE,
rounds: 1, rounds: 1,
status: ChallengeStatus.CHALLENGE_DOING, status: ChallengeStatus.CHALLENGE_DOING,
killMonsterList: [{ extraLineupType: ExtraLineupType.LINEUP_NONE,
monsterId: 8013010, killMonsterList: [],
killNum: 1, deadAvatarNum: 0,
}] } as CurChallenge,
},
maze: { maze: {
// ? Data from MappingInfoExcelTable // ? Data from MappingInfoExcelTable
id: 30101, id: 30104,
mapEntryId: 10001, mapEntryId: 3000401,
floor: { floor: {
floorId: 20121001, floorId: 20121001,
scene: { scene: {
planeId: 20121, planeId: 30104,
entryId: 1000, entryId: 3000401,
floorId: 20121001, floorId: 30104001,
gameModeType: 1, lightenSectionList: [],
entityList: [{ gameModeType: 4,
entityId: 10010101, entityList: [
npcMonster: { curAvatarEntity.getSceneEntityInfo(),
monsterId: 8013010, {
worldLevel: 1, entityId: 10000,
}, motion: {
groupId: 11, pos: {
motion: { x: 74719,
pos: { y: 2014,
x: 0, z: -94205,
y: 100, },
z: 0, rot: {
x: 0,
y: 0,
z: 0
}
}, },
rot: { groupId: 3,
x: 0, instId: 1,
y: 0, actor: {} as SceneActorInfo,
z: 0 npc: {} as SceneNpcInfo,
} prop: {} as ScenePropInfo,
}, npcMonster: {
}] monsterId: 1003020,
} isGenMonster: false,
eventId: 0,
isSetWorldLevel: false,
worldLevel: 6
} as SceneNpcMonsterInfo
} as SceneEntityInfo,
],
leaderEntityId: curAvatarEntity.entityId,
entityBuffList: [],
envBuffList: [],
snar: ""
} as SceneInfo
} }
} } as Maze
} as StartChallengeScRsp); } as StartChallengeScRsp);
} }

View File

@ -0,0 +1,24 @@
import _MapEntryExcelTable from "../../data/excel/MapEntryExcelTable.json";
type MapEntryExcelTableEntry = typeof _MapEntryExcelTable[keyof typeof _MapEntryExcelTable]
const MapEntryExcelTable = _MapEntryExcelTable as { [key: string]: MapEntryExcelTableEntry };
export default class MapEntryExcel {
private constructor() {
}
public static all() : MapEntryExcelTableEntry[] {
return Object.values(MapEntryExcelTable);
}
public static fromId(id: number) : MapEntryExcelTableEntry {
return MapEntryExcelTable[id];
}
public static fromIds(ids: number[]): MapEntryExcelTableEntry[] {
return ids.map(id => MapEntryExcel.fromId(id));
}
public static fromFloorId(id: number) : MapEntryExcelTableEntry {
return Object.values(MapEntryExcelTable).filter(e => e.FloorID == id)?.[0];
}
}

View File

@ -1,5 +1,5 @@
import _MapEntryExcelTable from "../../data/excel/MapEntryExcelTable.json";
import _MazePlaneExcelTable from "../../data/excel/MazePlaneExcelTable.json"; import _MazePlaneExcelTable from "../../data/excel/MazePlaneExcelTable.json";
import MapEntryExcel from "./MapEntryExcel";
interface MazePlaneExcelTableEntry { interface MazePlaneExcelTableEntry {
PlaneID: number; PlaneID: number;
@ -11,44 +11,13 @@ interface MazePlaneExcelTableEntry {
FloorIDList: number[]; FloorIDList: number[];
} }
interface TextMap {
hash: number;
}
type EntranceType = "Town" | "Mission" | "Explore";
interface MapEntryExcelTableEntry {
ID: number;
IsShowInMapMenu: boolean;
MapMenuSortID: number;
EntranceType: EntranceType | number; // Actually an enum. Town | Mission | Explore
EntranceGroupID: number;
Name: TextMap;
Desc: TextMap;
EntranceListIcon: string;
ImagePath: string;
MiniMapIconHintList: any[];
ShowReward: number;
PlaneID: number;
FloorID: number;
StartGroupID: number;
StartAnchorID: number;
TargetMission: number;
TargetMainMissionList: number[];
BeginMainMissionList: number[];
FinishMainMissionList: number[];
FinishQuestList: number[];
UnlockQuest: number;
}
const MazePlaneExcelTable = _MazePlaneExcelTable as { [key: string]: MazePlaneExcelTableEntry }; const MazePlaneExcelTable = _MazePlaneExcelTable as { [key: string]: MazePlaneExcelTableEntry };
const MapEntryExcelTable = _MapEntryExcelTable as { [key: string]: MapEntryExcelTableEntry };
export default class MazePlaneExcel { export default class MazePlaneExcel {
private constructor() { } private constructor() { }
public static fromEntryId(entryId: number): MazePlaneExcelTableEntry { public static fromEntryId(entryId: number): MazePlaneExcelTableEntry {
const mapEntry = MapEntryExcelTable[entryId.toString()]; const mapEntry = MapEntryExcel.fromId(entryId);
return MazePlaneExcelTable[mapEntry.PlaneID.toString()]; return MazePlaneExcelTable[mapEntry.PlaneID.toString()];
} }
@ -56,11 +25,21 @@ export default class MazePlaneExcel {
return MazePlaneExcelTable[planeId.toString()]; return MazePlaneExcelTable[planeId.toString()];
} }
public static getEntry(entryId: number): MapEntryExcelTableEntry { public static getGameModeForPlaneType(planeType: string): number {
return MapEntryExcelTable[entryId.toString()]; switch (planeType) {
} case "Town": return 1;
case "Maze": return 2;
case "Train": return 3;
case "Challenge": return 4;
case "RogueExplore": return 5;
case "RogueChallenge": return 6;
case "TownRoom": return 7;
case "Raid": return 8;
case "FarmRelic": return 9;
case "Client": return 10;
case "ChallengeActivity": return 11;
}
public static getAllEntries(): MapEntryExcelTableEntry[] { return 0;
return Object.values(MapEntryExcelTable);
} }
} }

View File

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