XFE Git
XFE Studio Git
Git 首页 全局搜索
XFE 主站 文档 NuGet

XFESpaceNinjaServer

A simple server for a small space ninja game

公开
关注 0 Fork 0 Star 0
返回提交历史

XFEstudio/XFESpaceNinjaServer

feat: sortie seed validation (#3722)

Closes #2833 Reviewed-on: https://onlyg.it/OpenWF/SpaceNinjaServer/pulls/3722 Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com> Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>

d38a405c
Sainan <63328889+Sainan@users.noreply.github.com>
提交于

代码差异

2 个文件 +60 -9
Modified src/services/worldStateService.ts +59 -8
@@ -601,8 +601,7 @@ const pushTilesetModifiers = (modifiers: string[], tileset: TSortieTileset): voi
601 601 };
602 602
603 603 export const getSortie = (day: number): ISortie => {
604 const seed = new SRng(day).randomInt(0, 100_000);
605 const rng = new SRng(seed);
604 const rng = new SRng(new SRng(day).randomInt(0, 100_000));
606 605
607 606 const boss = rng.randomElement(sortieBosses)!;
608 607 const enemyFaction = sortieBossToFaction[boss];
@@ -682,7 +681,7 @@ export const getSortie = (day: number): ISortie => {
682 681 const tileset = sortieTilesets[node as keyof typeof sortieTilesets] as TSortieTileset;
683 682 pushTilesetModifiers(modifiers, tileset);
684 683
685 const missionType = rng.randomElement(sortieTilesetMissions[tileset])!;
684 const missionType = rng.randomElement(sortieTilesetMissions[tileset])! as TMissionType;
686 685
687 686 if (missionTypes.has(missionType) || missionType == "MT_ASSASSINATION") {
688 687 i--;
@@ -709,6 +708,11 @@ export const getSortie = (day: number): ISortie => {
709 708 missionTypes.add(missionType);
710 709 }
711 710
711 let clientSeed = rng.randomInt(0, 100_000);
712 while (!validateSortieSeed(clientSeed, selectedNodes)) {
713 clientSeed = rng.randomInt(0, 100_000);
714 }
715
712 716 const dayStart = getSortieTime(day);
713 717 const dayEnd = getSortieTime(day + 1);
714 718 return {
@@ -716,16 +720,63 @@ export const getSortie = (day: number): ISortie => {
716 720 Activation: { $date: { $numberLong: dayStart.toString() } },
717 721 Expiry: { $date: { $numberLong: dayEnd.toString() } },
718 722 Reward: "/Lotus/Types/Game/MissionDecks/SortieRewards",
719 Seed: selectedNodes.find(
720 x => x.tileset == "CorpusIcePlanetTileset" || x.tileset == "CorpusIcePlanetTilesetCaves"
721 )
722 ? 2081 // this seed produces 12 zeroes in a row if asked to pick (0, 1); this way the CorpusIcePlanetTileset/CorpusIcePlanetTilesetCaves image is always index 0, the 'correct' choice.
723 : seed,
723 Seed: clientSeed,
724 724 Boss: boss,
725 725 Variants: selectedNodes
726 726 };
727 727 };
728 728
729 const SORTIE_FALLBACK_MISSION_TYPES: TMissionType[] = [
730 "MT_EXTERMINATION",
731 "MT_SURVIVAL",
732 "MT_RESCUE",
733 "MT_SABOTAGE",
734 //"MT_CAPTURE",
735 "MT_INTEL",
736 "MT_DEFENSE",
737 "MT_MOBILE_DEFENSE",
738 "MT_TERRITORY",
739 "MT_RETRIEVAL",
740 "MT_HIVE",
741 "MT_EXCAVATE",
742 "MT_ARTIFACT"
743 ];
744
745 const validateSortieSeed = (seed: number, variants: ISortieMission[]): boolean => {
746 const rng = new SRng(seed);
747 for (const variant of variants) {
748 if (variant.missionType != "MT_ASSASSINATION") {
749 const tileset = ExportTilesets[variant.tileset];
750 let missionPermutation = tileset.missions[variant.missionType];
751 // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
752 if (!missionPermutation) {
753 const options = SORTIE_FALLBACK_MISSION_TYPES.filter(missionType => missionType in tileset.missions);
754 const replacementType = options[rng.randomInt(0, options.length - 1)];
755 missionPermutation = tileset.missions[replacementType];
756 /*logger.debug(
757 `${variant.missionType} not supported by ${variant.tileset}; picking ${replacementType} instead`
758 );*/
759 }
760 if (missionPermutation.enemySpecs?.length) {
761 rng.randomInt(1, missionPermutation.enemySpecs.length);
762 }
763 if (missionPermutation.extraEnemySpecs?.length) {
764 rng.randomInt(1, missionPermutation.extraEnemySpecs.length);
765 }
766 rng.randomFloat(); // difficulty
767 }
768 if (variant.missionType != "MT_ARENA" && variant.missionType != "MT_JUNCTION") {
769 const locationTextureIndex = rng.randomInt(0, 1); // In general, each tileset has 2 texture options (there are exceptions of course, like MT_ARENA and MT_JUNCTION).
770 if (variant.tileset == "CorpusIcePlanetTileset" || variant.tileset == "CorpusIcePlanetTilesetCaves") {
771 if (locationTextureIndex != 0) {
772 return false; // For the corpus ice planet tileset, index 1 is an infested corpus ship image, which we don't want.
773 }
774 }
775 }
776 }
777 return true;
778 };
779
729 780 interface IRotatingSeasonChallengePools {
730 781 daily: string[];
731 782 weekly: string[];
Modified src/types/worldStateTypes.ts +1 -1
@@ -274,7 +274,7 @@ export interface ISortie {
274 274 }
275 275
276 276 export interface ISortieMission {
277 missionType: string;
277 missionType: TMissionType;
278 278 modifierType: string;
279 279 node: string;
280 280 tileset: string;