feat(state): 迁移 garage 切片(31 字段 / 60 分支)

This commit is contained in:
TermiNexus committed 2026-10-23 13:58:47 +08:00
1 parent f66f431670
commit 3c0ed0a998
14 files changed
+3366 -153

No files matched your search

@@ -268,66 +268,66 @@ object ActionCatalog {
ActionMeta("ShowNotificationAlreadyInOutgoingFriends", ShowNotificationAlreadyInOutgoingFriends::class, listOf(ActionFieldMeta("uid", "Any?"), ActionFieldMeta("userId", "Any?"))),
ActionMeta("ShowNotificationAcceptedLimitExceeded", ShowNotificationAcceptedLimitExceeded::class, listOf(ActionFieldMeta("uid", "Any?"), ActionFieldMeta("userId", "Any?"))),
ActionMeta("Discount", Discount::class, listOf(ActionFieldMeta("itemId", "Any?"), ActionFieldMeta("discount", "Float"), ActionFieldMeta("timeLeft", "Any?"), ActionFieldMeta("timeStart", "Any?"), ActionFieldMeta("isBuyable", "Boolean"))),
ActionMeta("UpdateDiscount", UpdateDiscount::class, listOf(ActionFieldMeta("item", "Any?"))),
ActionMeta("GarageItem", GarageItem::class, listOf(ActionFieldMeta("id", "Any?"), ActionFieldMeta("name", "Any?"), ActionFieldMeta("category", "Int"), ActionFieldMeta("viewCategory", "Int"), ActionFieldMeta("priceWithoutDiscount", "Int"), ActionFieldMeta("description", "Any?"), ActionFieldMeta("descriptionMedia", "Any?"), ActionFieldMeta("preview", "Any?"), ActionFieldMeta("isPremium", "Any?"), ActionFieldMeta("owned", "Boolean"), ActionFieldMeta("mountable", "Any?"), ActionFieldMeta("mounted", "Boolean"), ActionFieldMeta("mountIndex", "Any?"), ActionFieldMeta("position", "Int"), ActionFieldMeta("minRank", "Int"), ActionFieldMeta("maxRank", "Int"), ActionFieldMeta("discount", "Any?"), ActionFieldMeta("modification", "Any?"), ActionFieldMeta("properties", "Any?"), ActionFieldMeta("upgradeableParams", "Any?"), ActionFieldMeta("count", "Any?"), ActionFieldMeta("kit", "Any?"), ActionFieldMeta("countable", "Any?"), ActionFieldMeta("hasDeviceSale", "Boolean"), ActionFieldMeta("devicesAvailable", "Boolean"), ActionFieldMeta("devicePreview", "Any?"), ActionFieldMeta("buyable", "Boolean"), ActionFieldMeta("availableForAll", "Boolean"), ActionFieldMeta("availableSkins", "Any?"), ActionFieldMeta("availableShotSkins", "Any?"), ActionFieldMeta("mountedSkin", "Any?"), ActionFieldMeta("mountedShotSkin", "Any?"), ActionFieldMeta("skinPreview", "Any?"), ActionFieldMeta("group", "Any?"), ActionFieldMeta("rarity", "Any?"), ActionFieldMeta("favorite", "Boolean"), ActionFieldMeta("resistanceBackgroundImg", "Any?"), ActionFieldMeta("visible", "Boolean"), ActionFieldMeta("secret", "Boolean"), ActionFieldMeta("saleByKitItems", "Any?"), ActionFieldMeta("isSellingForCoin", "Boolean"), ActionFieldMeta("isSellingForCrystal", "Boolean"), ActionFieldMeta("isSellingForRuby", "Boolean"), ActionFieldMeta("isSellingForMoney", "Boolean"), ActionFieldMeta("isSellingForADS", "Boolean"))),
ActionMeta("UpdateDiscount", UpdateDiscount::class, listOf(ActionFieldMeta("item", "GarageItem"))),
ActionMeta("GarageItem", GarageItem::class, listOf(ActionFieldMeta("id", "Long"), ActionFieldMeta("name", "Any?"), ActionFieldMeta("category", "Int"), ActionFieldMeta("viewCategory", "Int"), ActionFieldMeta("priceWithoutDiscount", "Int"), ActionFieldMeta("description", "Any?"), ActionFieldMeta("descriptionMedia", "Any?"), ActionFieldMeta("preview", "Any?"), ActionFieldMeta("isPremium", "Any?"), ActionFieldMeta("owned", "Boolean"), ActionFieldMeta("mountable", "Any?"), ActionFieldMeta("mounted", "Boolean"), ActionFieldMeta("mountIndex", "Any?"), ActionFieldMeta("position", "Int"), ActionFieldMeta("minRank", "Int"), ActionFieldMeta("maxRank", "Int"), ActionFieldMeta("discount", "Any?"), ActionFieldMeta("modification", "GarageItemModification?"), ActionFieldMeta("properties", "Any?"), ActionFieldMeta("upgradeableParams", "UpgradableItemParams?"), ActionFieldMeta("count", "Int"), ActionFieldMeta("kit", "Any?"), ActionFieldMeta("countable", "Any?"), ActionFieldMeta("hasDeviceSale", "Boolean"), ActionFieldMeta("devicesAvailable", "Boolean"), ActionFieldMeta("devicePreview", "Any?"), ActionFieldMeta("buyable", "Boolean"), ActionFieldMeta("availableForAll", "Boolean"), ActionFieldMeta("availableSkins", "Any?"), ActionFieldMeta("availableShotSkins", "Any?"), ActionFieldMeta("mountedSkin", "Any?"), ActionFieldMeta("mountedShotSkin", "Any?"), ActionFieldMeta("skinPreview", "Any?"), ActionFieldMeta("group", "Any?"), ActionFieldMeta("rarity", "Any?"), ActionFieldMeta("favorite", "Boolean"), ActionFieldMeta("resistanceBackgroundImg", "Any?"), ActionFieldMeta("visible", "Boolean"), ActionFieldMeta("secret", "Boolean"), ActionFieldMeta("saleByKitItems", "Any?"), ActionFieldMeta("isSellingForCoin", "Boolean"), ActionFieldMeta("isSellingForCrystal", "Boolean"), ActionFieldMeta("isSellingForRuby", "Boolean"), ActionFieldMeta("isSellingForMoney", "Boolean"), ActionFieldMeta("isSellingForADS", "Boolean"))),
ActionMeta("ItemProperty", ItemProperty::class, listOf(ActionFieldMeta("params", "Any?"), ActionFieldMeta("value", "Any?"))),
ActionMeta("Kit", Kit::class, listOf(ActionFieldMeta("items", "Any?"))),
ActionMeta("GarageItemObject", GarageItemObject::class, listOf(ActionFieldMeta("id", "Any?"), ActionFieldMeta("description", "Any?"), ActionFieldMeta("item", "Any?"), ActionFieldMeta("isPremium", "Any?"), ActionFieldMeta("itemCategory", "Any?"), ActionFieldMeta("itemViewCategory", "Any?"), ActionFieldMeta("buyable", "Any?"), ActionFieldMeta("availableForAll", "Any?"), ActionFieldMeta("discount", "Any?"), ActionFieldMeta("timePeriod", "Any?"), ActionFieldMeta("modification", "Any?"), ActionFieldMeta("properties", "Any?"), ActionFieldMeta("upgradeableParams", "Any?"), ActionFieldMeta("devices", "Any?"), ActionFieldMeta("count", "Any?"), ActionFieldMeta("kit", "Any?"), ActionFieldMeta("countable", "Any?"), ActionFieldMeta("mountIndex", "Any?"), ActionFieldMeta("availableSkins", "Any?"), ActionFieldMeta("availableShotSkins", "Any?"), ActionFieldMeta("mountedSkin", "Any?"), ActionFieldMeta("mountedShotSkin", "Any?"), ActionFieldMeta("skinPreview", "Any?"), ActionFieldMeta("grouped", "Any?"), ActionFieldMeta("rarity", "Any?"), ActionFieldMeta("favorite", "Boolean"), ActionFieldMeta("isSellingForCoin", "Any?"), ActionFieldMeta("isSellingForCrystal", "Any?"), ActionFieldMeta("isSellingForRuby", "Any?"), ActionFieldMeta("resistanceBackgroundImg", "Any?"), ActionFieldMeta("visible", "Any?"), ActionFieldMeta("descriptionMedia", "Any?"), ActionFieldMeta("saleByKitItems", "Any?"))),
ActionMeta("GarageItemObject", GarageItemObject::class, listOf(ActionFieldMeta("id", "Long"), ActionFieldMeta("description", "Any?"), ActionFieldMeta("item", "Any?"), ActionFieldMeta("isPremium", "Boolean"), ActionFieldMeta("itemCategory", "Int"), ActionFieldMeta("itemViewCategory", "Int"), ActionFieldMeta("buyable", "Boolean"), ActionFieldMeta("availableForAll", "Boolean"), ActionFieldMeta("discount", "Any?"), ActionFieldMeta("timePeriod", "Any?"), ActionFieldMeta("modification", "GarageItemModification?"), ActionFieldMeta("properties", "Any?"), ActionFieldMeta("upgradeableParams", "UpgradableItemParams?"), ActionFieldMeta("devices", "Any?"), ActionFieldMeta("count", "Int"), ActionFieldMeta("kit", "Any?"), ActionFieldMeta("countable", "Any?"), ActionFieldMeta("mountIndex", "Any?"), ActionFieldMeta("availableSkins", "Any?"), ActionFieldMeta("availableShotSkins", "Any?"), ActionFieldMeta("mountedSkin", "Any?"), ActionFieldMeta("mountedShotSkin", "Any?"), ActionFieldMeta("skinPreview", "Any?"), ActionFieldMeta("grouped", "Any?"), ActionFieldMeta("rarity", "Any?"), ActionFieldMeta("favorite", "Boolean"), ActionFieldMeta("isSellingForCoin", "Any?"), ActionFieldMeta("isSellingForCrystal", "Any?"), ActionFieldMeta("isSellingForRuby", "Any?"), ActionFieldMeta("resistanceBackgroundImg", "Any?"), ActionFieldMeta("visible", "Boolean"), ActionFieldMeta("descriptionMedia", "Any?"), ActionFieldMeta("saleByKitItems", "Any?"))),
ActionMeta("KitItem", KitItem::class, listOf(ActionFieldMeta("id", "Any?"), ActionFieldMeta("count", "Int"), ActionFieldMeta("mount", "Boolean"))),
ActionMeta("GarageItemsState", GarageItemsState::class, listOf(ActionFieldMeta("itemsByBase", "Any?"))),
ActionMeta("GarageItemsState", GarageItemsState::class, listOf(ActionFieldMeta("itemsByBase", "Map<Long, GarageBaseItems>"))),
ActionMeta("GarageObject", GarageObject::class, listOf(ActionFieldMeta("temperatureCC", "Any?"))),
ActionMeta("Garage", Garage::class, listOf(ActionFieldMeta("itemsOnDepot", "Any?"), ActionFieldMeta("itemsOnMarket", "Any?"), ActionFieldMeta("mountedItems", "Any?"), ActionFieldMeta("skinsItems", "Any?"), ActionFieldMeta("items", "Any?"), ActionFieldMeta("devices", "Any?"), ActionFieldMeta("garageObject", "Any?"), ActionFieldMeta("isLoaded", "Boolean"), ActionFieldMeta("currentCategory", "Any?"), ActionFieldMeta("currentItemId", "Any?"), ActionFieldMeta("selectAlterationId", "Any?"), ActionFieldMeta("selectedSkinId", "Any?"), ActionFieldMeta("selectedShotSkinId", "Any?"), ActionFieldMeta("newItems", "Any?"), ActionFieldMeta("resistanceReadyToMount", "Any?"), ActionFieldMeta("delayMountTimeMs", "Any?"), ActionFieldMeta("cooldownReduceEndTime", "Any?"), ActionFieldMeta("timeToReduceUpdateInMin", "Any?"), ActionFieldMeta("currentRarity", "Any?"), ActionFieldMeta("purchaseOfUpgradesShopItemId", "Any?"), ActionFieldMeta("garageDiscountNotification", "Any?"), ActionFieldMeta("showVideoInDescription", "Any?"), ActionFieldMeta("unlockedProtectionSlots", "Any?"), ActionFieldMeta("pricesToUnlockSlot", "Any?"), ActionFieldMeta("numberRowsInPaintsUI", "Any?"), ActionFieldMeta("overdriveDescriptions", "Any?"), ActionFieldMeta("weaponToResistanceModule", "Any?"), ActionFieldMeta("waitServerItemMount", "Any?"), ActionFieldMeta("bazookaToSupply", "Any?"), ActionFieldMeta("upgradeCoolDownEndTimes", "Any?"), ActionFieldMeta("upgradeItemInProcessingId", "Any?"))),
ActionMeta("AddItemToOrderProcessing", AddItemToOrderProcessing::class, listOf(ActionFieldMeta("itemId", "Any?"), ActionFieldMeta("onSuccess", "Any?"))),
ActionMeta("RequestItem3DPart", RequestItem3DPart::class, listOf(ActionFieldMeta("itemId", "Any?"))),
ActionMeta("GarageUpgradeItemAction", GarageUpgradeItemAction::class, listOf(ActionFieldMeta("itemId", "Any?"), ActionFieldMeta("expectedPrice", "Any?"), ActionFieldMeta("currency", "Any?"))),
ActionMeta("GarageSelectItem", GarageSelectItem::class, listOf(ActionFieldMeta("itemId", "Any?"))),
ActionMeta("GarageUpgradeItemAction", GarageUpgradeItemAction::class, listOf(ActionFieldMeta("itemId", "Long"), ActionFieldMeta("expectedPrice", "Any?"), ActionFieldMeta("currency", "Any?"))),
ActionMeta("GarageSelectItem", GarageSelectItem::class, listOf(ActionFieldMeta("itemId", "Long"))),
ActionMeta("GarageBuyResistanceModule", GarageBuyResistanceModule::class, listOf(ActionFieldMeta("resistance", "Any?"))),
ActionMeta("GarageItemBought", GarageItemBought::class, listOf(ActionFieldMeta("item", "Any?"), ActionFieldMeta("count", "Int"), ActionFieldMeta("select", "Any?"))),
ActionMeta("GarageItemMounted", GarageItemMounted::class, listOf(ActionFieldMeta("item", "Any?"), ActionFieldMeta("needServerMount", "Boolean"))),
ActionMeta("GarageResistanceUnMount", GarageResistanceUnMount::class, listOf(ActionFieldMeta("resistance", "Any?"), ActionFieldMeta("needServerUnmount", "Boolean"))),
ActionMeta("GarageResistanceUnMount", GarageResistanceUnMount::class, listOf(ActionFieldMeta("resistance", "Long"), ActionFieldMeta("needServerUnmount", "Boolean"))),
ActionMeta("GarageResistanceMount", GarageResistanceMount::class, listOf(ActionFieldMeta("resistance", "Any?"), ActionFieldMeta("index", "Any?"))),
ActionMeta("BuyProtectionSlot", BuyProtectionSlot::class, listOf(ActionFieldMeta("slotToUnlock", "Any?"))),
ActionMeta("BuyProtectionSlot", BuyProtectionSlot::class, listOf(ActionFieldMeta("slotToUnlock", "Int"))),
ActionMeta("SetItemRarity", SetItemRarity::class, listOf(ActionFieldMeta("rarity", "Any?"))),
ActionMeta("SwitchingRowsScroll", SwitchingRowsScroll::class, listOf(ActionFieldMeta("numberRowsInPaintsUI", "Int"))),
ActionMeta("BuyGarageKit", BuyGarageKit::class, listOf(ActionFieldMeta("item", "Any?"))),
ActionMeta("GarageSelectAlteration", GarageSelectAlteration::class, listOf(ActionFieldMeta("alterationId", "Any?"))),
ActionMeta("GarageSelectSkin", GarageSelectSkin::class, listOf(ActionFieldMeta("skinId", "Any?"))),
ActionMeta("GarageSelectShotSkin", GarageSelectShotSkin::class, listOf(ActionFieldMeta("shotSkinId", "Any?"))),
ActionMeta("GarageSelectAlteration", GarageSelectAlteration::class, listOf(ActionFieldMeta("alterationId", "Long"))),
ActionMeta("GarageSelectSkin", GarageSelectSkin::class, listOf(ActionFieldMeta("skinId", "Long"))),
ActionMeta("GarageSelectShotSkin", GarageSelectShotSkin::class, listOf(ActionFieldMeta("shotSkinId", "Long"))),
ActionMeta("GarageLoadAvailableDevices", GarageLoadAvailableDevices::class, listOf(ActionFieldMeta("itemId", "Any?"))),
ActionMeta("RemoveFromFavoriteItem", RemoveFromFavoriteItem::class, listOf(ActionFieldMeta("itemId", "Any?"))),
ActionMeta("AddToFavoriteItem", AddToFavoriteItem::class, listOf(ActionFieldMeta("itemId", "Any?"))),
ActionMeta("RemoveFromFavoriteItem", RemoveFromFavoriteItem::class, listOf(ActionFieldMeta("itemId", "Long"))),
ActionMeta("AddToFavoriteItem", AddToFavoriteItem::class, listOf(ActionFieldMeta("itemId", "Long"))),
ActionMeta("GoToGarageAndSelectItem", GoToGarageAndSelectItem::class, listOf(ActionFieldMeta("category", "Int"), ActionFieldMeta("itemId", "Long"))),
ActionMeta("AddRelatedBazooka", AddRelatedBazooka::class, listOf(ActionFieldMeta("bazookaId", "Long"), ActionFieldMeta("supplyId", "Long"))),
ActionMeta("GarageUpgradeItemConfirm", GarageUpgradeItemConfirm::class, listOf(ActionFieldMeta("itemId", "Long"), ActionFieldMeta("level", "Int"))),
ActionMeta("GarageDevicesLoadedAction", GarageDevicesLoadedAction::class, listOf(ActionFieldMeta("baseItemId", "Long"), ActionFieldMeta("devices", "List<GarageDeviceObject>"), ActionFieldMeta("mountedDevice", "List<String>?"))),
ActionMeta("GarageDevicesLoadedAction", GarageDevicesLoadedAction::class, listOf(ActionFieldMeta("baseItemId", "Long"), ActionFieldMeta("devices", "List<GarageDeviceObject>"), ActionFieldMeta("mountedDevice", "GarageDevice?"))),
ActionMeta("SetRestrictionMount", SetRestrictionMount::class, listOf(ActionFieldMeta("delayMs", "Int"))),
ActionMeta("SetGarageSkinsAction", SetGarageSkinsAction::class, listOf(ActionFieldMeta("skins", "List<GarageItemObject>"))),
ActionMeta("GarageRemoveItemFromStore", GarageRemoveItemFromStore::class, listOf(ActionFieldMeta("itemId", "Any?"))),
ActionMeta("GarageRemoveItemFromStore", GarageRemoveItemFromStore::class, listOf(ActionFieldMeta("itemId", "Long"))),
ActionMeta("GarageUpdateDepotItem", GarageUpdateDepotItem::class, listOf(ActionFieldMeta("itemId", "Int"), ActionFieldMeta("count", "Int"))),
ActionMeta("GarageApplyResistanceMount", GarageApplyResistanceMount::class, listOf(ActionFieldMeta("resistance", "Any?"), ActionFieldMeta("index", "Any?"), ActionFieldMeta("needServerMount", "Boolean"))),
ActionMeta("GarageApplyResistanceMount", GarageApplyResistanceMount::class, listOf(ActionFieldMeta("resistance", "GarageItem"), ActionFieldMeta("index", "Int"), ActionFieldMeta("needServerMount", "Boolean"))),
ActionMeta("GarageApplyResistanceMountOnBuy", GarageApplyResistanceMountOnBuy::class, listOf(ActionFieldMeta("resistance", "Any?"), ActionFieldMeta("index", "Any?"), ActionFieldMeta("currency", "Any?"))),
ActionMeta("RemoveItemFromOrderProcessing", RemoveItemFromOrderProcessing::class, listOf(ActionFieldMeta("itemId", "Any?"), ActionFieldMeta("success", "Boolean"))),
ActionMeta("AddOverdriveDescription", AddOverdriveDescription::class, listOf(ActionFieldMeta("itemId", "Long"), ActionFieldMeta("descriptionData", "OverdriveDescriptionData"))),
ActionMeta("AddRelatedResistanceModule", AddRelatedResistanceModule::class, listOf(ActionFieldMeta("itemId", "Long"), ActionFieldMeta("resistanceModuleId", "Long"))),
ActionMeta("GarageUpgradeItemCoolDownStarted", GarageUpgradeItemCoolDownStarted::class, listOf(ActionFieldMeta("itemId", "Any?"), ActionFieldMeta("endTime", "Any?"))),
ActionMeta("GarageUpgradeItemCoolDownFinished", GarageUpgradeItemCoolDownFinished::class, listOf(ActionFieldMeta("itemId", "Any?"), ActionFieldMeta("endTime", "Any?"))),
ActionMeta("RemoveFromSale", RemoveFromSale::class, listOf(ActionFieldMeta("removeItems", "Any?"))),
ActionMeta("GarageUpdateItemCount", GarageUpdateItemCount::class, listOf(ActionFieldMeta("item", "Any?"), ActionFieldMeta("count", "Any?"))),
ActionMeta("GarageRemoveItemFromDepot", GarageRemoveItemFromDepot::class, listOf(ActionFieldMeta("item", "Any?"))),
ActionMeta("GarageAddItemToDepot", GarageAddItemToDepot::class, listOf(ActionFieldMeta("itemId", "Any?"), ActionFieldMeta("count", "Any?"))),
ActionMeta("GarageUpgradeItemCoolDownStarted", GarageUpgradeItemCoolDownStarted::class, listOf(ActionFieldMeta("itemId", "Long"), ActionFieldMeta("endTime", "Long"))),
ActionMeta("GarageUpgradeItemCoolDownFinished", GarageUpgradeItemCoolDownFinished::class, listOf(ActionFieldMeta("itemId", "Long"), ActionFieldMeta("endTime", "Long"))),
ActionMeta("RemoveFromSale", RemoveFromSale::class, listOf(ActionFieldMeta("removeItems", "Collection<Long>"))),
ActionMeta("GarageUpdateItemCount", GarageUpdateItemCount::class, listOf(ActionFieldMeta("item", "GarageItem"), ActionFieldMeta("count", "Int"))),
ActionMeta("GarageRemoveItemFromDepot", GarageRemoveItemFromDepot::class, listOf(ActionFieldMeta("item", "GarageItemObject"))),
ActionMeta("GarageAddItemToDepot", GarageAddItemToDepot::class, listOf(ActionFieldMeta("itemId", "Long"), ActionFieldMeta("count", "Int"))),
ActionMeta("ItemPurchaseData", ItemPurchaseData::class, listOf(ActionFieldMeta("isAvailable", "Boolean"), ActionFieldMeta("item", "Any?"), ActionFieldMeta("amount", "Any?"), ActionFieldMeta("priceWithoutDiscount", "Any?"), ActionFieldMeta("priceWithDiscount", "Any?"), ActionFieldMeta("discountInPercent", "Any?"), ActionFieldMeta("discountEndTime", "Any?"), ActionFieldMeta("isSellingForCrystal", "Boolean"), ActionFieldMeta("isSellingForCoin", "Boolean"), ActionFieldMeta("isSellingForRuby", "Boolean"), ActionFieldMeta("isAvailableByCrystals", "Boolean"), ActionFieldMeta("isAvailableByCoins", "Boolean"), ActionFieldMeta("isAvailableByRubies", "Boolean"), ActionFieldMeta("isAvailableByRank", "Boolean"), ActionFieldMeta("minRank", "Any?"), ActionFieldMeta("isAvailableByPremium", "Boolean"))),
ActionMeta("OverdriveDescriptionData", OverdriveDescriptionData::class, listOf(ActionFieldMeta("name", "Any?"), ActionFieldMeta("icon", "Any?"), ActionFieldMeta("description", "Any?"))),
ActionMeta("GarageLoadDevicesIfNotLoaded", GarageLoadDevicesIfNotLoaded::class, listOf(ActionFieldMeta("item", "Any?"))),
ActionMeta("GarageRemoveDevice", GarageRemoveDevice::class, listOf(ActionFieldMeta("device", "Any?"), ActionFieldMeta("item", "Any?"))),
ActionMeta("GarageInsertDeviceClientAndServer", GarageInsertDeviceClientAndServer::class, listOf(ActionFieldMeta("device", "Any?"), ActionFieldMeta("item", "Any?"))),
ActionMeta("GarageRemoveDevice", GarageRemoveDevice::class, listOf(ActionFieldMeta("device", "GarageDevice"), ActionFieldMeta("item", "GarageItem"))),
ActionMeta("GarageInsertDeviceClientAndServer", GarageInsertDeviceClientAndServer::class, listOf(ActionFieldMeta("device", "GarageDevice"), ActionFieldMeta("item", "GarageItem"))),
ActionMeta("GarageBuyAndInsertDevice", GarageBuyAndInsertDevice::class, listOf(ActionFieldMeta("device", "Any?"), ActionFieldMeta("price", "Any?"), ActionFieldMeta("itemId", "Any?"))),
ActionMeta("GarageBuyDevice", GarageBuyDevice::class, listOf(ActionFieldMeta("device", "Any?"), ActionFieldMeta("item", "Any?"), ActionFieldMeta("currency", "Any?"), ActionFieldMeta("mount", "Boolean"))),
ActionMeta("GarageDeviceTrySetInfinityLifetime", GarageDeviceTrySetInfinityLifetime::class, listOf(ActionFieldMeta("deviceId", "Any?"))),
ActionMeta("GarageRemoveDiscountForDevice", GarageRemoveDiscountForDevice::class, listOf(ActionFieldMeta("item", "Any?"))),
ActionMeta("Devices", Devices::class, listOf(ActionFieldMeta("baseItemIdToDevices", "Any?"))),
ActionMeta("GarageDevice", GarageDevice::class, listOf(ActionFieldMeta("id", "Any?"), ActionFieldMeta("baseItemId", "Any?"), ActionFieldMeta("installed", "Boolean"), ActionFieldMeta("name", "Any?"), ActionFieldMeta("category", "Int"), ActionFieldMeta("viewCategory", "Int"), ActionFieldMeta("priceWithoutDiscount", "Int"), ActionFieldMeta("description", "Any?"), ActionFieldMeta("descriptionMedia", "Any?"), ActionFieldMeta("previewImage", "Any?"), ActionFieldMeta("isPremium", "Boolean"), ActionFieldMeta("position", "Int"), ActionFieldMeta("minRank", "Int"), ActionFieldMeta("maxRank", "Int"), ActionFieldMeta("discount", "Int"), ActionFieldMeta("timeDiscountEnd", "Any?"), ActionFieldMeta("timeDiscountToStart", "Any?"), ActionFieldMeta("infinityLifetimeItem", "Boolean"), ActionFieldMeta("lifeTimeInSec", "Int"), ActionFieldMeta("property", "String"), ActionFieldMeta("properties", "Any?"), ActionFieldMeta("isSellingForCoin", "Boolean"), ActionFieldMeta("isSellingForCrystal", "Boolean"), ActionFieldMeta("isSellingForRuby", "Boolean"), ActionFieldMeta("isSellingForADS", "Boolean"), ActionFieldMeta("isSellingForMoney", "Boolean"), ActionFieldMeta("rarity", "Any?"), ActionFieldMeta("visible", "Boolean"), ActionFieldMeta("secret", "Boolean"), ActionFieldMeta("buyable", "Boolean"))),
ActionMeta("GarageBuyDevice", GarageBuyDevice::class, listOf(ActionFieldMeta("device", "GarageDevice"), ActionFieldMeta("item", "GarageItem"), ActionFieldMeta("currency", "Any?"), ActionFieldMeta("mount", "Boolean"))),
ActionMeta("GarageDeviceTrySetInfinityLifetime", GarageDeviceTrySetInfinityLifetime::class, listOf(ActionFieldMeta("deviceId", "Long"))),
ActionMeta("GarageRemoveDiscountForDevice", GarageRemoveDiscountForDevice::class, listOf(ActionFieldMeta("item", "GarageItem"))),
ActionMeta("Devices", Devices::class, listOf(ActionFieldMeta("baseItemIdToDevices", "Map<Long, List<GarageDevice>>"))),
ActionMeta("GarageDevice", GarageDevice::class, listOf(ActionFieldMeta("id", "Long"), ActionFieldMeta("baseItemId", "Long"), ActionFieldMeta("installed", "Boolean"), ActionFieldMeta("name", "Any?"), ActionFieldMeta("category", "Int"), ActionFieldMeta("viewCategory", "Int"), ActionFieldMeta("priceWithoutDiscount", "Int"), ActionFieldMeta("description", "Any?"), ActionFieldMeta("descriptionMedia", "Any?"), ActionFieldMeta("previewImage", "Any?"), ActionFieldMeta("isPremium", "Boolean"), ActionFieldMeta("position", "Int"), ActionFieldMeta("minRank", "Int"), ActionFieldMeta("maxRank", "Int"), ActionFieldMeta("discount", "Int"), ActionFieldMeta("timeDiscountEnd", "Any?"), ActionFieldMeta("timeDiscountToStart", "Any?"), ActionFieldMeta("infinityLifetimeItem", "Boolean"), ActionFieldMeta("lifeTimeInSec", "Int"), ActionFieldMeta("property", "String"), ActionFieldMeta("properties", "Any?"), ActionFieldMeta("isSellingForCoin", "Boolean"), ActionFieldMeta("isSellingForCrystal", "Boolean"), ActionFieldMeta("isSellingForRuby", "Boolean"), ActionFieldMeta("isSellingForADS", "Boolean"), ActionFieldMeta("isSellingForMoney", "Boolean"), ActionFieldMeta("rarity", "Any?"), ActionFieldMeta("visible", "Boolean"), ActionFieldMeta("secret", "Boolean"), ActionFieldMeta("buyable", "Boolean"))),
ActionMeta("DescriptionAlteration", DescriptionAlteration::class, listOf(ActionFieldMeta("description", "Any?"), ActionFieldMeta("property", "String"))),
ActionMeta("GarageDeviceObject", GarageDeviceObject::class, listOf(ActionFieldMeta("id", "Any?"), ActionFieldMeta("baseItemId", "Any?"), ActionFieldMeta("temporaryItemCC", "Any?"), ActionFieldMeta("description", "Any?"), ActionFieldMeta("item", "Any?"), ActionFieldMeta("isPremium", "Any?"), ActionFieldMeta("itemCategory", "Any?"), ActionFieldMeta("itemViewCategory", "Any?"), ActionFieldMeta("buyable", "Any?"), ActionFieldMeta("discount", "Any?"), ActionFieldMeta("timePeriod", "Any?"), ActionFieldMeta("deviceProperty", "Any?"), ActionFieldMeta("rarity", "Any?"), ActionFieldMeta("visible", "Any?"), ActionFieldMeta("descriptionMedia", "Any?"))),
ActionMeta("GarageDeviceObject", GarageDeviceObject::class, listOf(ActionFieldMeta("id", "Long"), ActionFieldMeta("baseItemId", "Long"), ActionFieldMeta("temporaryItemCC", "Any?"), ActionFieldMeta("description", "Any?"), ActionFieldMeta("item", "Any?"), ActionFieldMeta("isPremium", "Boolean"), ActionFieldMeta("itemCategory", "Int"), ActionFieldMeta("itemViewCategory", "Int"), ActionFieldMeta("buyable", "Boolean"), ActionFieldMeta("discount", "Any?"), ActionFieldMeta("timePeriod", "Any?"), ActionFieldMeta("deviceProperty", "Any?"), ActionFieldMeta("rarity", "Any?"), ActionFieldMeta("visible", "Boolean"), ActionFieldMeta("descriptionMedia", "Any?"))),
ActionMeta("UpgradableItemParams", UpgradableItemParams::class, listOf(ActionFieldMeta("itemPropertyParams", "Any?"), ActionFieldMeta("properties", "Any?"), ActionFieldMeta("currentLevel", "Int"), ActionFieldMeta("upgradeParams", "Any?"), ActionFieldMeta("upgradeDiscount", "Int"), ActionFieldMeta("upgradeConfirmed", "Any?"))),
ActionMeta("ItemPropertyParams", ItemPropertyParams::class, listOf(ActionFieldMeta("sortIndex", "Int"), ActionFieldMeta("name", "Any?"), ActionFieldMeta("properties", "Any?"), ActionFieldMeta("aggregationMode", "Any?"), ActionFieldMeta("multiplier", "Any?"), ActionFieldMeta("additiveShift", "Any?"), ActionFieldMeta("isKernelProperty", "Boolean"), ActionFieldMeta("unitName", "Any?"))),
ActionMeta("GaragePreview", GaragePreview::class, listOf(ActionFieldMeta("isVisible", "Any?"), ActionFieldMeta("photoMode", "Any?"), ActionFieldMeta("needVisible", "Any?"), ActionFieldMeta("viewStyle", "Any?"), ActionFieldMeta("dynViewStyle", "Any?"), ActionFieldMeta("tankPreviewSettings", "Any?"), ActionFieldMeta("needShowDrone", "Any?"), ActionFieldMeta("params", "Any?"), ActionFieldMeta("hullData", "Any?"), ActionFieldMeta("weaponData", "Any?"), ActionFieldMeta("droneData", "Any?"), ActionFieldMeta("grenadeData", "Any?"), ActionFieldMeta("hasGrenades", "Any?"), ActionFieldMeta("hullEngineType", "Any?"), ActionFieldMeta("turretMechanism", "Any?"), ActionFieldMeta("paint", "Any?"), ActionFieldMeta("properties", "Any?"), ActionFieldMeta("captureScreenshotAction", "Any?"), ActionFieldMeta("captureVideoAction", "Any?"), ActionFieldMeta("item3dModelActionCache", "Any?"), ActionFieldMeta("requestedItems", "Any?"), ActionFieldMeta("expectedItemId", "Any?"))),
@@ -0,0 +1,44 @@
// GarageModels.kt —— garage 切片里没有 toString 的嵌套模型(原 module_10032)
//
// 证据链:
// · PB = GarageItemsState(`function PB(t){ this.or9_1 = t === cd ? ag() : t }`),
// `or9_1` 是 Map<baseItemId, iB>。iB 没有 toString(`function iB(t){ this.xrb_1 = wm(t, QL(tB)) … }`),
// 保存一组 aB(GarageItem) 并按首项派生出 owned/mounted/selected/position/… 等只读字段。
// 这里保留 reducer 真正用到的部分:Items 列表 + Selected/HasOwned/HasMounted。
// · aB 的派生字段:
// urb_1 = modification?.vcf_1 ?: id (baseItemId,见 aB 构造 5342–5344)
// trb_1 = hasDeviceSale && owned (见 aB 构造 5341)
// GarageItem 由 gen_action_kt.js 生成,派生值用扩展函数计算。
// · qra_1(modification)来自另一模块,只有 vcf_1(baseItemId) 与 xcf_1(level) 被本切片使用。
package App.State
/** 原 `aB.qra_1`:改装/等级信息。字段名与运行时对象一致(vcf_1 / xcf_1)。 */
data class GarageItemModification(
val vcf_1: Long? = null,
val xcf_1: Int = 0,
)
/** 原 `iB`:同一个 baseItemId 下的一组 GarageItem。 */
data class GarageBaseItems(
val Items: List<GarageItem> = emptyList(),
) {
/** 原 `arc_1`:第一个已拥有项;没有则第一项。 */
val Selected: GarageItem? get() = Items.firstOrNull { it.owned } ?: Items.firstOrNull()
/** 原 `yrb_1`:组内是否有已拥有项。 */
val HasOwned: Boolean get() = Items.any { it.owned }
/** 原 `zrb_1`:组内是否有已装配项。 */
val HasMounted: Boolean get() = Items.any { it.mounted }
/** 原 `iB.orc(i)`:按下标取项(原包直接 `g1(i)`,越界会抛)。 */
fun At(Index: Int): GarageItem = Items[Index]
/** 原 `iB.krc(id, fn)`:按 id 替换一项。 */
fun Replace(ItemId: Long, F: (GarageItem) -> GarageItem): GarageBaseItems =
copy(Items = Items.map { if (it.id == ItemId) F(it) else it })
/** 原 `iB.lrc(fn)`:整组映射。 */
fun MapAll(F: (GarageItem) -> GarageItem): GarageBaseItems =
copy(Items = Items.map(F))
}
@@ -5,7 +5,7 @@ package App.State
import Core.Vector3
// 动作/状态类共 816 个,字段 2150 个(已定类型 511 个)
// 动作/状态类共 816 个,字段 2150 个(已定类型 567 个)
data class AnnouncementState(
val isEnabled: Any?, // 仅 copy 转发(无类型信息)
val modelLoaded: Any?, // 仅 copy 转发(无类型信息)
@@ -1445,11 +1445,11 @@ data class Discount(
)
data class UpdateDiscount(
val item: Any?, // 调用点: HL(t).paq_1.dpv_1.yq5_1.rr9(t.gr9_1)
val item: GarageItem, // override
)
data class GarageItem(
val id: Any?, // 调用点: h | t
val id: Long, // override
val name: Any?, // 调用点: c | n
val category: Int, // dataflow
val viewCategory: Int, // dataflow
@@ -1466,10 +1466,10 @@ data class GarageItem(
val minRank: Int, // dataflow
val maxRank: Int, // dataflow
val discount: Any?, // 调用点: y | w
val modification: Any?, // 调用点: $ | d
val modification: GarageItemModification?, // override
val properties: Any?, // 调用点: R | g
val upgradeableParams: Any?, // 调用点: M | m
val count: Any?, // 调用点: O | k
val upgradeableParams: UpgradableItemParams?, // override
val count: Int, // override
val kit: Any?, // 调用点: D | z
val countable: Any?, // 调用点: L | b
val hasDeviceSale: Boolean, // dataflow
@@ -1506,21 +1506,21 @@ data class Kit(
)
data class GarageItemObject(
val id: Any?, // 调用点: t | o
val id: Long, // override
val description: Any?, // 调用点: n | _
val item: Any?, // 调用点: i | h
val isPremium: Any?, // 调用点: r | a
val itemCategory: Any?, // 调用点: e | c
val itemViewCategory: Any?, // 调用点: u | l
val buyable: Any?, // 调用点: s | f
val availableForAll: Any?, // 调用点: o | v
val isPremium: Boolean, // override
val itemCategory: Int, // override
val itemViewCategory: Int, // override
val buyable: Boolean, // override
val availableForAll: Boolean, // override
val discount: Any?, // 调用点: _ | w
val timePeriod: Any?, // 调用点: h | k
val modification: Any?, // 调用点: a | d
val modification: GarageItemModification?, // override
val properties: Any?, // 调用点: c | g
val upgradeableParams: Any?, // 调用点: l | m
val upgradeableParams: UpgradableItemParams?, // override
val devices: Any?, // 调用点: f | z
val count: Any?, // 调用点: v | y
val count: Int, // override
val kit: Any?, // 调用点: p | $
val countable: Any?, // 调用点: w | x
val mountIndex: Any?, // 调用点: d | i
@@ -1536,7 +1536,7 @@ data class GarageItemObject(
val isSellingForCrystal: Any?, // 调用点: j | Ct
val isSellingForRuby: Any?, // 调用点: S | Ct
val resistanceBackgroundImg: Any?, // 调用点: C | H
val visible: Any?, // 调用点: E | W
val visible: Boolean, // override
val descriptionMedia: Any?, // 调用点: T | V
val saleByKitItems: Any?, // 调用点: I | K
)
@@ -1548,7 +1548,7 @@ data class KitItem(
)
data class GarageItemsState(
val itemsByBase: Any?, // 调用点: cg(this.or9_1, Vd(i, new iB(e))) | cg(c, Vd(t.urb_1, l))
val itemsByBase: Map<Long, GarageBaseItems>, // override
)
data class GarageObject(
@@ -1599,13 +1599,13 @@ data class RequestItem3DPart(
)
data class GarageUpgradeItemAction(
val itemId: Any?, // 调用点: n.us1_1.zr9_1 | n.us1_1.zr9_1
val itemId: Long, // override
val expectedPrice: Any?, // 调用点: n.ws1_1 | n.xs1_1
val currency: Any?, // 调用点: rB() | uB()
)
data class GarageSelectItem(
val itemId: Any?, // 调用点: n.zr9_1 | n.zr9_1
val itemId: Long, // override
)
data class GarageBuyResistanceModule(
@@ -1624,7 +1624,7 @@ data class GarageItemMounted(
)
data class GarageResistanceUnMount(
val resistance: Any?, // 调用点: k | f
val resistance: Long, // override
val needServerUnmount: Boolean, // callsite
)
@@ -1634,7 +1634,7 @@ data class GarageResistanceMount(
)
data class BuyProtectionSlot(
val slotToUnlock: Any?, // 调用点: s
val slotToUnlock: Int, // override
)
data class SetItemRarity(
@@ -1650,15 +1650,15 @@ data class BuyGarageKit(
)
data class GarageSelectAlteration(
val alterationId: Any?, // 调用点: n.arh_1 | n.arh_1
val alterationId: Long, // override
)
data class GarageSelectSkin(
val skinId: Any?, // 调用点: n.zr9_1 | null
val skinId: Long, // override
)
data class GarageSelectShotSkin(
val shotSkinId: Any?, // 调用点: n.zr9_1 | null
val shotSkinId: Long, // override
)
data class GarageLoadAvailableDevices(
@@ -1666,11 +1666,11 @@ data class GarageLoadAvailableDevices(
)
data class RemoveFromFavoriteItem(
val itemId: Any?, // 调用点: _
val itemId: Long, // override
)
data class AddToFavoriteItem(
val itemId: Any?, // 调用点: _
val itemId: Long, // override
)
data class GoToGarageAndSelectItem(
@@ -1691,7 +1691,7 @@ data class GarageUpgradeItemConfirm(
data class GarageDevicesLoadedAction(
val baseItemId: Long, // runtime
val devices: List<GarageDeviceObject>, // runtime
val mountedDevice: List<String>?, // runtime
val mountedDevice: GarageDevice?, // override
)
data class SetRestrictionMount(
@@ -1703,7 +1703,7 @@ data class SetGarageSkinsAction(
)
data class GarageRemoveItemFromStore(
val itemId: Any?, // 调用点: t.x57()
val itemId: Long, // override
)
data class GarageUpdateDepotItem(
@@ -1712,8 +1712,8 @@ data class GarageUpdateDepotItem(
)
data class GarageApplyResistanceMount(
val resistance: Any?, // 调用点: n | n
val index: Any?, // 调用点: r._v | r._v
val resistance: GarageItem, // override
val index: Int, // override
val needServerMount: Boolean, // callsite
)
@@ -1739,31 +1739,31 @@ data class AddRelatedResistanceModule(
)
data class GarageUpgradeItemCoolDownStarted(
val itemId: Any?, // 调用点: e
val endTime: Any?, // 调用点: i
val itemId: Long, // override
val endTime: Long, // override
)
data class GarageUpgradeItemCoolDownFinished(
val itemId: Any?, // 调用点: n
val endTime: Any?, // 调用点: i
val itemId: Long, // override
val endTime: Long, // override
)
data class RemoveFromSale(
val removeItems: Any?, // 调用点: h | h
val removeItems: Collection<Long>, // override
)
data class GarageUpdateItemCount(
val item: Any?, // 调用点: _
val count: Any?, // 调用点: r
val item: GarageItem, // override
val count: Int, // override
)
data class GarageRemoveItemFromDepot(
val item: Any?, // 调用点: o.rrf(cd, cd, cd, cd, cd, cd, cd, cd, cd
val item: GarageItemObject, // override
)
data class GarageAddItemToDepot(
val itemId: Any?, // 调用点: l.frd_1
val count: Any?, // 调用点: r
val itemId: Long, // override
val count: Int, // override
)
data class ItemPurchaseData(
@@ -1796,13 +1796,13 @@ data class GarageLoadDevicesIfNotLoaded(
)
data class GarageRemoveDevice(
val device: Any?, // 调用点: f | e
val item: Any?, // 调用点: u | t.zas_1.o1p8_1
val device: GarageDevice, // override
val item: GarageItem, // override
)
data class GarageInsertDeviceClientAndServer(
val device: Any?, // 调用点: n | n
val item: Any?, // 调用点: t.zas_1.o1p8_1 | t.zas_1.z1pm_1
val device: GarageDevice, // override
val item: GarageItem, // override
)
data class GarageBuyAndInsertDevice(
@@ -1812,27 +1812,27 @@ data class GarageBuyAndInsertDevice(
)
data class GarageBuyDevice(
val device: Any?, // 调用点: e
val item: Any?, // 调用点: u
val device: GarageDevice, // override
val item: GarageItem, // override
val currency: Any?, // 调用点: o
val mount: Boolean, // dataflow
)
data class GarageDeviceTrySetInfinityLifetime(
val deviceId: Any?, // 调用点: i
val deviceId: Long, // override
)
data class GarageRemoveDiscountForDevice(
val item: Any?, // 调用点: u
val item: GarageItem, // override
)
data class Devices(
val baseItemIdToDevices: Any?, // 调用点: i | cg(this.xrg_1, Vd(t, l))
val baseItemIdToDevices: Map<Long, List<GarageDevice>>, // override
)
data class GarageDevice(
val id: Any?, // 调用点: e | t
val baseItemId: Any?, // 调用点: u | n
val id: Long, // override
val baseItemId: Long, // override
val installed: Boolean, // callsite
val name: Any?, // 调用点: s | r
val category: Int, // dataflow
@@ -1869,20 +1869,20 @@ data class DescriptionAlteration(
)
data class GarageDeviceObject(
val id: Any?, // 调用点: h
val baseItemId: Any?, // 调用点: i
val id: Long, // override
val baseItemId: Long, // override
val temporaryItemCC: Any?, // 调用点: a
val description: Any?, // 调用点: c
val item: Any?, // 调用点: l
val isPremium: Any?, // 调用点: f
val itemCategory: Any?, // 调用点: v
val itemViewCategory: Any?, // 调用点: p
val buyable: Any?, // 调用点: w
val isPremium: Boolean, // override
val itemCategory: Int, // override
val itemViewCategory: Int, // override
val buyable: Boolean, // override
val discount: Any?, // 调用点: d
val timePeriod: Any?, // 调用点: g
val deviceProperty: Any?, // 调用点: m
val rarity: Any?, // 调用点: null == z ? Yr() : z
val visible: Any?, // 调用点: Gr(_, Z(rx))
val visible: Boolean, // override
val descriptionMedia: Any?, // 调用点: Gr(_, Z(ox))
)
@@ -0,0 +1,653 @@
// GarageReducer.kt —— 车库切片(对应 module_10032 的 UB 状态 + hF reducer)
//
// 证据链:
// 状态类 UB:`function UB(t, n, i, r, e, u, s, o, _, h, a, c, l, f, v, p, w, d, g, m, k, z, b, q, y, $, x, j, S, C, E)`,
// 31 个字段,toString 名为 `Garage(itemsOnDepot=…, …, upgradeItemInProcessingId=…)`。默认值取自构造:
// itemsOnDepot/itemsOnMarket/mountedItems/skinsItems = Yg()(HashSet)、items = new PB()、
// devices = new UF()、garageObject = null、isLoaded = false、currentCategory = BB().sq5_1、
// currentItemId/selectAlterationId/selectedSkinId/selectedShotSkinId = null、newItems = ag()(emptyMap)、
// resistanceReadyToMount = false、delayMountTimeMs/cooldownReduceEndTime = new Dd(0,0)(Long 0)、
// timeToReduceUpdateInMin = 0、currentRarity = null、purchaseOfUpgradesShopItemId = new Dd(886599726,214)、
// garageDiscountNotification = false、showVideoInDescription = true、unlockedProtectionSlots = 1、
// pricesToUnlockSlot = md()(emptyList)、numberRowsInPaintsUI = 2、overdriveDescriptions = ag()、
// weaponToResistanceModule = ag()、waitServerItemMount = ag()、bazookaToSupply = ag()、
// upgradeCoolDownEndTimes = ag()、upgradeItemInProcessingId = null。
//
// 根委派:`i = hF(t, n)`(根 reducer 把整个状态传入具名 reducer,切片槽位 n.dpv_1 只在 hF 体内出现)。
//
// hF 顶层分支:58 个 `t instanceof <动作>` + 2 个“接口/单例”分支:
// · `Xd(t, LF)`:NF = GarageInsertDeviceClientAndServer(LF 是标记接口,NF 实现它)
// · `yd(t, Nu)`:xU 单例动作(无字段,效果是把 upgradeItemInProcessingId 置 null)
// 其余 58 个分支与 slice_garage.json 一一对应;`FX` / `UB` 是屏幕/状态标识,不是动作。
//
// 嵌套模型:
// PB = GarageItemsState(Map<baseItemId, iB>),iB 无 toString → 见 GarageModels.kt 的 GarageBaseItems。
// UF = Devices(Map<baseItemId, List<VF>>),VF = GarageDevice。
// 物品对象 fB = GarageItemObject;aB = GarageItem(生成物,45 字段)。
// 关键派生:urb_1 = modification.vcf_1 ?: id;trb_1 = hasDeviceSale && owned。
package App.State.Reducers
import App.State.AddOverdriveDescription
import App.State.AddRelatedBazooka
import App.State.AddRelatedResistanceModule
import App.State.AddToFavoriteItem
import App.State.BuyProtectionSlot
import App.State.Devices
import App.State.GarageApplyResistanceMount
import App.State.GarageAddItemToDepot
import App.State.GarageBaseItems
import App.State.GarageBuyDevice
import App.State.GarageDevice
import App.State.GarageDeviceObject
import App.State.GarageDeviceTrySetInfinityLifetime
import App.State.GarageDevicesLoadedAction
import App.State.GarageInsertDeviceClientAndServer
import App.State.GarageItem
import App.State.GarageItemModification
import App.State.GarageItemObject
import App.State.GarageItemsState
import App.State.GarageObject
import App.State.GarageRemoveDevice
import App.State.GarageRemoveDiscountForDevice
import App.State.GarageRemoveItemFromDepot
import App.State.GarageRemoveItemFromStore
import App.State.GarageResistanceUnMount
import App.State.GarageSelectAlteration
import App.State.GarageSelectItem
import App.State.GarageSelectShotSkin
import App.State.GarageSelectSkin
import App.State.GarageUpgradeItemAction
import App.State.GarageUpgradeItemConfirm
import App.State.GarageUpgradeItemCoolDownFinished
import App.State.GarageUpgradeItemCoolDownStarted
import App.State.GarageUpdateItemCount
import App.State.GoTo
import App.State.OverdriveDescriptionData
import App.State.RemoveFromFavoriteItem
import App.State.RemoveFromSale
import App.State.SetGarageSkinsAction
import App.State.SetItemRarity
import App.State.SetRestrictionMount
import App.State.SwitchingRowsScroll
import App.State.UpdateDiscount
import State.InitState
import kotlin.js.Date
/** 原 `BB().sq5_1`:Garage 屏幕的默认分类。真实值由屏幕对象携带;S3 用 null 作为默认分类。 */
val GarageDefaultCategory: Any? = null
/** 原 `nz()`:抵抗模块的等待装配键。原包是分类常量,这里用一个不会与普通 category 冲突的哨兵。 */
const val GarageResistanceCategoryKey: Int = Int.MIN_VALUE
/* ------------------------------------------------------------------ 匿名动作 */
// 原 rF:没有 toString / 不在 action_inventory。效果见 gen_root_wiring.js 的 GarageAnonymousEffects。
class ResetGarage
data class SetGarageObject(val garageObject: GarageObject?)
data class SetItemsOnDepot(val items: Collection<GarageItemObject>)
data class SetItemsOnMarket(val items: Collection<GarageItemObject>)
data class SetMountedItems(val items: Collection<GarageItemObject>)
class GarageLoaded
class SetGarageNotLoaded
data class SelectGarageCategory(val category: Any?)
data class AddGarageItemCount(val item: GarageItem, val delta: Int)
data class GarageMountItem(val item: GarageItem)
data class GarageUnmountItem(val itemId: Long)
data class GarageMountItems(val itemIds: Collection<Long>)
data class SetMountedSkinForItems(val skinId: Long, val item: GarageItem)
data class SetMountedShotSkinForItems(val shotSkinId: Long, val item: GarageItem)
data class SetNewItems(val items: Map<Long, Set<Long>>)
data class AddNewItem(val itemId: Long, val value: Long)
data class RemoveNewItem(val itemId: Long, val value: Long)
data class SetResistanceReadyToMount(val ready: Boolean)
class ResetMountDelay
data class StartCooldownReduce(val seconds: Int, val minutes: Int)
data class SetCooldownReduceEndTime(val seconds: Int)
class ShowGarageDiscountNotification
class HideVideoInDescription
data class RemoveWaitServerItemMount(val categories: Collection<Int>)
class ClearWaitServerItemMount
/** 原 xU:`Nu = new xU()` 单例动作,效果只有 upgradeItemInProcessingId = null。 */
class ClearUpgradeItemInProcessing
/* ------------------------------------------------------------------ 状态 */
data class GarageStateSlice(
val ItemsOnDepot: Collection<GarageItemObject> = emptySet(),
val ItemsOnMarket: Collection<GarageItemObject> = emptySet(),
val MountedItems: Collection<GarageItemObject> = emptySet(),
val SkinsItems: Collection<GarageItemObject> = emptySet(),
val Items: GarageItemsState = GarageItemsState(emptyMap()),
val Devices: Devices = Devices(emptyMap()),
val GarageObject: GarageObject? = null,
val IsLoaded: Boolean = false,
val CurrentCategory: Any? = GarageDefaultCategory,
val CurrentItemId: Long? = null,
val SelectAlterationId: Long? = null,
val SelectedSkinId: Long? = null,
val SelectedShotSkinId: Long? = null,
val NewItems: Map<Long, Set<Long>> = emptyMap(),
val ResistanceReadyToMount: Boolean = false,
val DelayMountTimeMs: Long = 0L,
val CooldownReduceEndTime: Long = 0L,
val TimeToReduceUpdateInMin: Int = 0,
val CurrentRarity: Any? = null,
val PurchaseOfUpgradesShopItemId: Long = 920109776942L,
val GarageDiscountNotification: Boolean = false,
val ShowVideoInDescription: Boolean = true,
val UnlockedProtectionSlots: Int = 1,
val PricesToUnlockSlot: List<Any?> = emptyList(),
val NumberRowsInPaintsUI: Int = 2,
val OverdriveDescriptions: Map<Long, Any?> = emptyMap(),
val WeaponToResistanceModule: Map<Long, Long> = emptyMap(),
val WaitServerItemMount: Map<Int, Boolean> = emptyMap(),
val BazookaToSupply: Map<Long, Long> = emptyMap(),
val UpgradeCoolDownEndTimes: Map<Long, Long> = emptyMap(),
val UpgradeItemInProcessingId: Long? = null,
)
/* ------------------------------------------------------------------ 小工具 */
private fun ToLong(Value: Any?): Long? = when (Value) {
is Long -> Value
is Int -> Value.toLong()
is Number -> Value.toLong()
else -> null
}
private fun ToInt(Value: Any?, Default: Int = 0): Int = when (Value) {
is Int -> Value
is Long -> Value.toInt()
is Number -> Value.toInt()
else -> Default
}
private fun ToBool(Value: Any?): Boolean = Value == true
private fun NowMs(): Long = Date.now().toLong()
/** 原 `aB.urb_1`:modification.vcf_1 ?: id。 */
internal fun GarageItem.BaseItemId(): Long = modification?.vcf_1 ?: id
/** 原 `aB.trb_1`:hasDeviceSale && owned。 */
private fun GarageItem.HasDeviceSaleAndOwned(): Boolean = hasDeviceSale && owned
/* ------------------------------------------------------------------ GarageItemsState(PB) */
private val GarageItemsState.ItemById: Map<Long, GarageItem>
get() = itemsByBase.values.flatMap { it.Items }.associateBy { it.id }
private fun GarageItemsState.Item(ItemId: Long): GarageItem =
ItemById[ItemId] ?: error("GarageItemsState 里没有 item $ItemId")
private fun GarageItemsState.HasItem(ItemId: Long): Boolean = ItemById.containsKey(ItemId)
private fun GarageItemsState.GroupOf(Item: GarageItem): GarageBaseItems =
itemsByBase[Item.BaseItemId()] ?: error("GarageItemsState 里没有 baseItemId ${Item.BaseItemId()}")
private fun GarageItemsState.UpdateGroup(BaseId: Long, Group: GarageBaseItems): GarageItemsState =
copy(itemsByBase = itemsByBase + (BaseId to Group))
/** 原 `PB.rr9`:按 itemId 取项(原包用 Map.getValue,缺键会抛)。 */
private fun GarageItemsState.ItemOrThrow(ItemId: Long): GarageItem = Item(ItemId)
/** 原 `PB.grg`:在 item 所属 base 组里按 id 替换一项。 */
private fun GarageItemsState.ReplaceItem(Item: GarageItem, F: (GarageItem) -> GarageItem): GarageItemsState {
val BaseId = Item.BaseItemId()
val Group = itemsByBase[BaseId] ?: error("GarageItemsState 里没有 baseItemId $BaseId")
return copy(itemsByBase = itemsByBase + (BaseId to Group.Replace(Item.id, F)))
}
/** 原 `PB.jrg`:同一 viewCategory 内先卸下已装配项,再装配本项。 */
private fun GarageItemsState.MountItem(Item: GarageItem): GarageItemsState {
val SameCategory = itemsByBase.values.filter { Group ->
Group.Items.any { it.viewCategory == Item.viewCategory }
}
val Mounted = SameCategory.flatMap { it.Items }.firstOrNull { it.mounted }
var Result = this
if (Mounted != null) {
Result = Result.ReplaceItem(Mounted) { it.copy(mounted = false) }
}
return Result.ReplaceItem(Item) { it.copy(mounted = true) }
}
/** 原 `PB.krg`:批量装配;先卸下同 viewCategory 的已装配项,再置 owned + mounted。 */
private fun GarageItemsState.MountItems(ItemIds: Collection<Long>): GarageItemsState {
val Items = ItemIds.map { ItemOrThrow(it) }
val Categories = Items.map { it.viewCategory }.toSet()
val Groups = itemsByBase.values.filter { Group ->
Group.Items.any { it.viewCategory in Categories }
}
val Mounted = Groups.flatMap { it.Items }.filter { it.mounted }
var Result = this
for (M in Mounted) Result = Result.ReplaceItem(M) { it.copy(mounted = false) }
for (I in Items) Result = Result.ReplaceItem(I) { it.copy(owned = true, mounted = true) }
return Result
}
/** 原 `PB.mrg`:卸下单项。 */
private fun GarageItemsState.UnmountItem(ItemId: Long): GarageItemsState =
ReplaceItem(ItemOrThrow(ItemId)) { it.copy(mounted = false) }
/** 原 `PB.lrg`:装配抵抗模块;先检查 unlockedProtectionSlots。 */
private fun GarageItemsState.MountResistance(
State: GarageStateSlice,
Item: GarageItem,
Index: Int,
): GarageItemsState {
val MountedCount = itemsByBase.values
.flatMap { it.Items }
.count { it.mounted && it.viewCategory == Item.viewCategory }
if (MountedCount >= State.UnlockedProtectionSlots) {
error("已装配抵抗模块数 $MountedCount 超过 unlockedProtectionSlots ${State.UnlockedProtectionSlots}")
}
return ReplaceItem(Item) { it.copy(mounted = true, mountIndex = Index) }
}
/** 原 `PB.qrg`:整组 mountedSkin = skinId。 */
private fun GarageItemsState.SetGroupMountedSkin(SkinId: Long, Item: GarageItem): GarageItemsState {
val Group = GroupOf(Item)
return UpdateGroup(Item.BaseItemId(), Group.MapAll { it.copy(mountedSkin = SkinId) })
}
/** 原 `PB.rrg`:整组 mountedShotSkin = skinId。 */
private fun GarageItemsState.SetGroupMountedShotSkin(SkinId: Long, Item: GarageItem): GarageItemsState {
val Group = GroupOf(Item)
return UpdateGroup(Item.BaseItemId(), Group.MapAll { it.copy(mountedShotSkin = SkinId) })
}
/** 原 `PB.srg`:把给定 baseItemId 集合里的所有项 buyable 置 false。 */
private fun GarageItemsState.RemoveFromSale(BaseIds: Collection<Long>): GarageItemsState {
val Set = BaseIds.toSet()
var Result = this
for ((BaseId, Group) in itemsByBase) {
if (BaseId in Set) {
Result = Result.UpdateGroup(BaseId, Group.MapAll { it.copy(buyable = false) })
}
}
return Result
}
/** 原 `PB.trg`:置 owned=true、count=n。 */
private fun GarageItemsState.SetOwnedCount(Item: GarageItem, Count: Int): GarageItemsState =
ReplaceItem(Item) { it.copy(owned = true, count = Count) }
/* ------------------------------------------------------------------ Devices(UF) */
private fun Devices.UpdateByIds(Ids: Set<Long>, F: (GarageDevice) -> GarageDevice): Devices =
copy(baseItemIdToDevices = baseItemIdToDevices.mapValues { (_, List) ->
List.map { if (it.id in Ids) F(it) else it }
})
/** 原 `UF.frn`:去掉同组里旧 id 的项,再追加从 GarageDeviceObject 转换出的新项。 */
private fun Devices.LoadDevices(
BaseItemId: Long,
Objects: List<GarageDeviceObject>,
MountedDevice: GarageDevice?,
): Devices {
val NewIds = Objects.map { it.id }.toSet()
val Old = baseItemIdToDevices[BaseItemId].orEmpty().filter { it.id !in NewIds }
val Converted = Objects.map { DeviceFromObject(it, MountedDevice?.id == it.id) }
return copy(baseItemIdToDevices = baseItemIdToDevices + (BaseItemId to (Old + Converted)))
}
private fun DeviceFromObject(Object: GarageDeviceObject, Installed: Boolean): GarageDevice =
GarageDevice(
id = Object.id,
baseItemId = Object.baseItemId,
installed = Installed,
name = Object.description?.toString() ?: "",
category = Object.itemCategory,
viewCategory = Object.itemViewCategory,
priceWithoutDiscount = 0,
description = Object.description,
descriptionMedia = Object.descriptionMedia,
previewImage = Object.item,
isPremium = Object.isPremium,
position = 0,
minRank = 0,
maxRank = 0,
discount = ToInt(Object.discount),
timeDiscountEnd = null,
timeDiscountToStart = null,
infinityLifetimeItem = false,
lifeTimeInSec = 0,
property = Object.deviceProperty?.toString() ?: "",
properties = emptyList<Any?>(),
isSellingForCoin = false,
isSellingForCrystal = false,
isSellingForRuby = false,
isSellingForADS = false,
isSellingForMoney = false,
rarity = Object.rarity,
visible = Object.visible,
secret = false,
buyable = Object.buyable,
)
/* ------------------------------------------------------------------ 物品对象 → GarageItem */
private fun GarageItemObject.ToGarageItem(Owned: Boolean, Mounted: Boolean): GarageItem =
GarageItem(
id = id,
name = description?.toString() ?: "",
category = itemCategory,
viewCategory = itemViewCategory,
priceWithoutDiscount = 0,
description = description,
descriptionMedia = descriptionMedia,
preview = item,
isPremium = isPremium,
owned = Owned,
mountable = false,
mounted = Mounted,
mountIndex = mountIndex,
position = 0,
minRank = 0,
maxRank = 0,
discount = discount,
modification = modification,
properties = properties,
upgradeableParams = upgradeableParams,
count = count,
kit = kit,
countable = ToBool(countable),
hasDeviceSale = false,
devicesAvailable = devices != null,
devicePreview = null,
buyable = buyable,
availableForAll = availableForAll,
availableSkins = availableSkins,
availableShotSkins = availableShotSkins,
mountedSkin = mountedSkin,
mountedShotSkin = mountedShotSkin,
skinPreview = skinPreview,
group = grouped,
rarity = rarity,
favorite = favorite,
resistanceBackgroundImg = resistanceBackgroundImg,
visible = visible,
secret = false,
saleByKitItems = saleByKitItems,
isSellingForCoin = ToBool(isSellingForCoin),
isSellingForCrystal = ToBool(isSellingForCrystal),
isSellingForRuby = ToBool(isSellingForRuby),
isSellingForMoney = false,
isSellingForADS = false,
)
/** 原 `eF`:按 depot / mounted / market / skins 四个物品对象集合重建 PB。 */
private fun RebuildItems(State: GarageStateSlice): GarageItemsState {
val All = LinkedHashMap<Long, GarageItem>()
for (Object in State.ItemsOnDepot) All[Object.id] = Object.ToGarageItem(Owned = true, Mounted = false)
for (Object in State.MountedItems) All[Object.id] = Object.ToGarageItem(Owned = true, Mounted = true)
for (Object in State.ItemsOnMarket) All[Object.id] = Object.ToGarageItem(Owned = false, Mounted = false)
for (Object in State.SkinsItems) All[Object.id] = Object.ToGarageItem(Owned = false, Mounted = false)
val Grouped = All.values.groupBy { it.BaseItemId() }.mapValues { GarageBaseItems(it.value) }
return GarageItemsState(Grouped)
}
/* ------------------------------------------------------------------ reducer */
fun ReduceGarage(State0: GarageStateSlice, Action: Any): GarageStateSlice = when (Action) {
is InitState -> {
// 原:if (t.zap_1.equals(og(UB))) { var h = t.aaq_1(o); _ = h instanceof UB ? h : wd() }
// InitState(type, action) 里的 action 是「当前状态 → 新状态」的函数。
if (Action.type == GarageStateSlice::class) {
val Result = when (val F = Action.action) {
is Function1<*, *> -> @Suppress("UNCHECKED_CAST") (F as (GarageStateSlice) -> Any?)(State0)
is Function0<*> -> (F as () -> Any?)()
else -> null
}
Result as? GarageStateSlice ?: State0
} else State0
}
is ResetGarage -> State0.copy(
Devices = Devices(emptyMap()),
UpgradeCoolDownEndTimes = emptyMap(),
UpgradeItemInProcessingId = null,
)
is SetGarageObject -> State0.copy(GarageObject = Action.garageObject)
is SetItemsOnDepot -> State0.copy(ItemsOnDepot = Action.items)
is SetItemsOnMarket -> State0.copy(ItemsOnMarket = Action.items)
is SetMountedItems -> State0.copy(MountedItems = Action.items)
is SetGarageSkinsAction -> State0.copy(SkinsItems = Action.skins)
is GarageLoaded -> State0.copy(Items = RebuildItems(State0), IsLoaded = true)
is SetGarageNotLoaded -> State0.copy(IsLoaded = false)
is SelectGarageCategory -> State0.copy(CurrentCategory = Action.category, CurrentItemId = null)
is GarageSelectItem -> State0.copy(CurrentItemId = Action.itemId)
is AddGarageItemCount -> {
val Item = Action.item
val Level = Item.modification?.xcf_1 ?: 0
val Index = if (Level > 0) Level - 1 else 0
val Preview = State0.Items.GroupOf(Item).Items.getOrNull(Index)?.devicePreview
State0.copy(
Items = State0.Items.ReplaceItem(Item) {
it.copy(
owned = true,
count = it.count + Action.delta,
devicePreview = Preview,
)
},
)
}
is GarageUpdateItemCount ->
State0.copy(Items = State0.Items.ReplaceItem(Action.item) { it.copy(count = Action.count) })
is GarageMountItem -> State0.copy(
Items = State0.Items.MountItem(Action.item),
WaitServerItemMount = State0.WaitServerItemMount + (Action.item.category to true),
)
is GarageUnmountItem ->
State0.copy(Items = State0.Items.UnmountItem(Action.itemId))
is GarageMountItems ->
State0.copy(Items = State0.Items.MountItems(Action.itemIds))
is SetMountedSkinForItems ->
State0.copy(Items = State0.Items.SetGroupMountedSkin(Action.skinId, Action.item))
is SetMountedShotSkinForItems ->
State0.copy(Items = State0.Items.SetGroupMountedShotSkin(Action.shotSkinId, Action.item))
is GarageApplyResistanceMount -> State0.copy(
Items = State0.Items.MountResistance(State0, Action.resistance, Action.index),
// 原 WU 分支:`cg(o.vq6_1, new Km(nz(), !0))`,键是抵抗模块分类哨兵,不是物品 category。
WaitServerItemMount = State0.WaitServerItemMount + (GarageResistanceCategoryKey to true),
)
is GarageResistanceUnMount -> State0.copy(
Items = State0.Items.UnmountItem(Action.resistance),
WaitServerItemMount = State0.WaitServerItemMount + (GarageResistanceCategoryKey to true),
)
is GarageUpgradeItemAction -> {
val Item = State0.Items.ItemOrThrow(Action.itemId)
State0.copy(
Items = State0.Items.ReplaceItem(Item) {
val Params = it.upgradeableParams
it.copy(
upgradeableParams = Params?.copy(
currentLevel = Params.currentLevel + 1,
upgradeConfirmed = false,
) ?: Params,
)
},
UpgradeItemInProcessingId = Action.itemId,
)
}
is GarageUpgradeItemCoolDownStarted -> State0.copy(
UpgradeCoolDownEndTimes = State0.UpgradeCoolDownEndTimes + (Action.itemId to Action.endTime),
)
is GarageUpgradeItemCoolDownFinished ->
if (State0.UpgradeCoolDownEndTimes[Action.itemId] == Action.endTime) {
State0.copy(UpgradeCoolDownEndTimes = State0.UpgradeCoolDownEndTimes - Action.itemId)
} else State0
is GarageUpgradeItemConfirm -> {
val Item = State0.Items.ItemOrThrow(Action.itemId)
val Confirmed = if (State0.UpgradeItemInProcessingId == Action.itemId) null else State0.UpgradeItemInProcessingId
State0.copy(
Items = State0.Items.ReplaceItem(Item) {
val Params = it.upgradeableParams
it.copy(
upgradeableParams = Params?.copy(
upgradeConfirmed = Params.upgradeConfirmed == true || Params.currentLevel == Action.level,
) ?: Params,
)
},
UpgradeItemInProcessingId = Confirmed,
)
}
is ClearUpgradeItemInProcessing -> State0.copy(UpgradeItemInProcessingId = null)
is GarageDevicesLoadedAction -> State0.copy(
Devices = State0.Devices.LoadDevices(Action.baseItemId, Action.devices, Action.mountedDevice),
)
is GarageRemoveDevice -> State0.copy(
Items = State0.Items.ReplaceItem(Action.item) { it.copy(devicePreview = null) },
Devices = State0.Devices.UpdateByIds(setOf(Action.device.id)) { it.copy(installed = false) },
)
is GarageInsertDeviceClientAndServer -> State0.copy(
Items = State0.Items.ReplaceItem(Action.item) {
it.copy(devicePreview = Action.device.previewImage)
},
Devices = State0.Devices.UpdateByIds(setOf(Action.device.id)) { it.copy(installed = true) },
)
is GarageBuyDevice -> State0.copy(
Items = State0.Items.ReplaceItem(Action.item) {
it.copy(devicePreview = Action.device.previewImage)
},
Devices = State0.Devices.UpdateByIds(setOf(Action.device.id)) {
it.copy(installed = Action.mount, infinityLifetimeItem = true)
},
)
is GarageDeviceTrySetInfinityLifetime -> State0.copy(
Devices = State0.Devices.UpdateByIds(setOf(Action.deviceId)) {
it.copy(infinityLifetimeItem = true)
},
)
is GarageSelectAlteration -> State0.copy(SelectAlterationId = Action.alterationId)
is GarageSelectSkin -> State0.copy(SelectedSkinId = Action.skinId)
is GarageSelectShotSkin -> State0.copy(SelectedShotSkinId = Action.shotSkinId)
is GarageRemoveDiscountForDevice -> State0.copy(
Items = State0.Items.ReplaceItem(Action.item) {
it.copy(hasDeviceSale = it.HasDeviceSaleAndOwned())
},
)
is UpdateDiscount -> State0.copy(
// 原 zF 只把 discount 换成同一对象的副本(重新计算时间窗),字段值不变。
Items = State0.Items.ReplaceItem(Action.item) { it.copy(discount = it.discount) },
)
is GarageAddItemToDepot -> State0.copy(
Items = State0.Items.SetOwnedCount(State0.Items.ItemOrThrow(Action.itemId), Action.count),
)
is GarageRemoveItemFromDepot -> State0.copy(
Items = State0.Items.ReplaceItem(State0.Items.ItemOrThrow(Action.item.id)) {
it.copy(owned = false, count = Action.item.count)
},
)
is GarageRemoveItemFromStore -> State0.copy(
Items = State0.Items.ReplaceItem(State0.Items.ItemOrThrow(Action.itemId)) {
it.copy(buyable = false)
},
)
is SetNewItems -> State0.copy(NewItems = Action.items)
is AddNewItem -> {
val Existing = State0.NewItems[Action.itemId].orEmpty()
State0.copy(NewItems = State0.NewItems + (Action.itemId to (Existing + Action.value)))
}
is RemoveNewItem -> {
val Existing = State0.NewItems[Action.itemId].orEmpty() - Action.value
State0.copy(
NewItems = if (Existing.isEmpty()) State0.NewItems - Action.itemId
else State0.NewItems + (Action.itemId to Existing),
)
}
is SetResistanceReadyToMount -> State0.copy(ResistanceReadyToMount = Action.ready)
is RemoveFromSale -> State0.copy(Items = State0.Items.RemoveFromSale(Action.removeItems))
is SetRestrictionMount -> State0.copy(
DelayMountTimeMs = if (Action.delayMs > 0) NowMs() + Action.delayMs else 0L,
)
is ResetMountDelay -> State0.copy(DelayMountTimeMs = 0L)
is StartCooldownReduce -> State0.copy(
CooldownReduceEndTime = NowMs() + Action.seconds * 1000L,
TimeToReduceUpdateInMin = Action.minutes,
)
is SetCooldownReduceEndTime -> State0.copy(
CooldownReduceEndTime = NowMs() + Action.seconds * 1000L,
)
is SetItemRarity -> State0.copy(CurrentRarity = Action.rarity)
is AddToFavoriteItem -> State0.copy(
Items = State0.Items.ReplaceItem(State0.Items.ItemOrThrow(Action.itemId)) {
it.copy(favorite = true)
},
)
is RemoveFromFavoriteItem -> State0.copy(
Items = State0.Items.ReplaceItem(State0.Items.ItemOrThrow(Action.itemId)) {
it.copy(favorite = false)
},
)
is GoTo -> {
// 原:if (Tk(t.grm_1).equals(og(FX))) return o.hri(…, Y.irm_1); 否则 i = o
if (Action.screen.name == "Garage") {
State0.copy(CurrentCategory = GarageDefaultCategory)
} else State0
}
is ShowGarageDiscountNotification -> State0.copy(GarageDiscountNotification = true)
is HideVideoInDescription -> State0.copy(ShowVideoInDescription = false)
is BuyProtectionSlot -> State0.copy(UnlockedProtectionSlots = State0.UnlockedProtectionSlots + 1)
is SwitchingRowsScroll -> State0.copy(NumberRowsInPaintsUI = Action.numberRowsInPaintsUI)
is AddOverdriveDescription -> State0.copy(
OverdriveDescriptions = State0.OverdriveDescriptions + (Action.itemId to Action.descriptionData),
)
is AddRelatedResistanceModule -> State0.copy(
WeaponToResistanceModule = State0.WeaponToResistanceModule + (Action.itemId to Action.resistanceModuleId),
)
is AddRelatedBazooka -> State0.copy(
BazookaToSupply = State0.BazookaToSupply + (Action.bazookaId to Action.supplyId),
)
is RemoveWaitServerItemMount -> State0.copy(
WaitServerItemMount = State0.WaitServerItemMount + Action.categories.map { it to false },
)
is ClearWaitServerItemMount -> State0.copy(WaitServerItemMount = emptyMap())
else -> State0
}
@@ -11,12 +11,14 @@
// shop → ReduceShop(j4 / `(inline@shop)` 分支)
// screen → ReduceScreen(WK / dY().ks5)
// lootBoxes → ReduceLootBoxes(aH / EH.wrv)
// garage → ReduceGarage(UB / hF,根委派 `i = hF(t, n)`)
//
// 其余 81 个字段按原样透传(defaultValue 取原构造)。每个切片 reducer 对无关动作都返回原状态,
// 其余 80 个字段按原样透传(defaultValue 取原构造)。每个切片 reducer 对无关动作都返回原状态,
// 所以一次 copy 里串起全部已迁移 reducer,与按动作路由再归约的结果一致。
// 迁移进度与每个字段的证据见生成物 RootWiring.kt(不要手改)。
package App.State
import App.State.Reducers.ReduceGarage
import App.State.Reducers.ReduceHome
import App.State.Reducers.ReduceInvite
import App.State.Reducers.ReduceLocalBattleUser
@@ -47,6 +49,7 @@ object RootReducer {
Shop = ReduceShop(State0.Shop, Action),
Screen = ReduceScreen(State0.Screen, Action),
LootBoxes = ReduceLootBoxes(State0.LootBoxes, Action),
Garage = ReduceGarage(State0.Garage, Action),
)
}
@@ -4,6 +4,7 @@
// 已迁移的切片用强类型;其余保持 Any?(原样透传),迁移一个改一个再重新生成。
package App.State
import App.State.Reducers.GarageStateSlice
import App.State.Reducers.HomeStateSlice
import App.State.Reducers.InviteStateSlice
import App.State.Reducers.LocalBattleUserStateSlice
@@ -28,8 +29,8 @@ data class RootState(
val BattleStatistics: Any? = null,
/** 原 battleTutorial(rE / Q.sq8)。未迁移:原样透传。 */
val BattleTutorial: Any? = null,
/** 原 garage(UB / hF)。未迁移:原样透传。 */
val Garage: Any? = null,
/** 原 garage(UB / hF)。已迁移。 */
val Garage: GarageStateSlice = GarageStateSlice(),
/** 原 garagePreview(IG / fs.eru)。未迁移:原样透传。 */
val GaragePreview: Any? = null,
/** 原 challenges(CZ / nh.hsj)。未迁移:原样透传。 */
@@ -19,7 +19,7 @@ object RootWiring {
SliceWiring("modalScreen", "apv_1", "nV", "Fs.js1", "Fs.js1(t, n.apv_1)", listOf("?XW", "AddModal", "RemoveCurrentByName", "RemoveModal")),
SliceWiring("battleStatistics", "bpv_1", "jR", "rr.fqr", "rr.fqr(t, n.bpv_1)", listOf("?DR", "?HR", "?KR", "?PR", "?QR", "?UR", "?XR", "?YR", "?ZR", "?nP", "?tP", "Init", "InitBattleModeWithLevels", "SetBattleMode", "SetDisplayClientInfo", "SetFlagOrBallOwner", "SetRankRange", "SetReArmor", "SetRemainingTime", "SetRoundRestartTime", "UpdateFund", "UpdateScore")),
SliceWiring("battleTutorial", "cpv_1", "rE", "Q.sq8", "Q.sq8(t, n.cpv_1)", listOf("?oE", "?sE", "SetFragsCount", "ShowMessage")),
SliceWiring("garage", "dpv_1", "UB", "hF", "hF(t, n)", listOf("?EU", "?FB", "?FX", "?JB", "?MU", "?NU", "?OU", "?PU", "?QU", "?RU", "?SU", "?VB", "?WB", "?aF", "?bU", "?cF", "?dU", "?eF", "?fF", "?iU", "?lF", "?pF", "?rF", "?vF", "?wU", "?yU", "AddOverdriveDescription", "AddRelatedBazooka", "AddRelatedResistanceModule", "AddToFavoriteItem", "BuyProtectionSlot", "Garage", "GarageAddItemToDepot", "GarageApplyResistanceMount", "GarageBuyDevice", "GarageDeviceTrySetInfinityLifetime", "GarageDevicesLoadedAction", "GarageRemoveDevice", "GarageRemoveDiscountForDevice", "GarageRemoveItemFromDepot", "GarageRemoveItemFromStore", "GarageResistanceUnMount", "GarageSelectAlteration", "GarageSelectItem", "GarageSelectShotSkin", "GarageSelectSkin", "GarageUpdateItemCount", "GarageUpgradeItemAction", "GarageUpgradeItemConfirm", "GarageUpgradeItemCoolDownFinished", "GarageUpgradeItemCoolDownStarted", "GoTo", "InitState", "RemoveFromFavoriteItem", "RemoveFromSale", "SetGarageSkinsAction", "SetItemRarity", "SetRestrictionMount", "SwitchingRowsScroll", "UpdateDiscount")),
SliceWiring("garage", "dpv_1", "UB", "hF", "hF(t, n)", listOf("AddGarageItemCount", "AddNewItem", "AddOverdriveDescription", "AddRelatedBazooka", "AddRelatedResistanceModule", "AddToFavoriteItem", "BuyProtectionSlot", "ClearWaitServerItemMount", "GarageAddItemToDepot", "GarageApplyResistanceMount", "GarageBuyDevice", "GarageDeviceTrySetInfinityLifetime", "GarageDevicesLoadedAction", "GarageLoaded", "GarageMountItem", "GarageMountItems", "GarageRemoveDevice", "GarageRemoveDiscountForDevice", "GarageRemoveItemFromDepot", "GarageRemoveItemFromStore", "GarageResistanceUnMount", "GarageSelectAlteration", "GarageSelectItem", "GarageSelectShotSkin", "GarageSelectSkin", "GarageUnmountItem", "GarageUpdateItemCount", "GarageUpgradeItemAction", "GarageUpgradeItemConfirm", "GarageUpgradeItemCoolDownFinished", "GarageUpgradeItemCoolDownStarted", "GoTo", "HideVideoInDescription", "InitState", "RemoveFromFavoriteItem", "RemoveFromSale", "RemoveNewItem", "RemoveWaitServerItemMount", "ResetGarage", "ResetMountDelay", "SelectGarageCategory", "SetCooldownReduceEndTime", "SetGarageNotLoaded", "SetGarageObject", "SetGarageSkinsAction", "SetItemRarity", "SetItemsOnDepot", "SetItemsOnMarket", "SetMountedItems", "SetMountedShotSkinForItems", "SetMountedSkinForItems", "SetNewItems", "SetResistanceReadyToMount", "SetRestrictionMount", "ShowGarageDiscountNotification", "StartCooldownReduce", "SwitchingRowsScroll", "UpdateDiscount")),
SliceWiring("garagePreview", "epv_1", "IG", "fs.eru", "fs.eru(t, n.epv_1)", listOf("?BG", "?JG", "?KG", "?OG", "?QG", "?UG", "?VG", "?XG", "?YG", "?ZG", "?nH", "?tH", "AddRequestedItem", "InitGarageResources", "ResetItem3dModelActionCache", "SetCaptureActions", "SetDrone", "SetExpectedItemId", "SetMountedItemsProperties", "SetPaint", "ShowRequest", "UpdatePreviewSettings")),
SliceWiring("challenges", "fpv_1", "CZ", "nh.hsj", "nh.hsj(t, n.fpv_1)", listOf("?LZ", "RemoveChallenge", "SetBattlePass", "SetBattlePassPackages", "SetChallenge", "SetRewardForDialog", "SetTakenBattlePassRewardIndex", "SetTakenFreeRewardIndex", "UpdateStars")),
SliceWiring("loginByPassword", "gpv_1", "gD", "nu.cr6", "nu.cr6(t, n.gpv_1)", listOf("?ND", "?PD", "?RD", "IncorrectLogin2FA", "RequestsNumber2FA", "SetLogin", "SetPassword", "SetRememberMe", "SetStatus")),
@@ -106,7 +106,7 @@ object RootWiring {
)
/** 已迁移(由 RootReducer 真正归约)的字段。 */
val Migrated: Set<String> = setOf("inviteState", "loginByPassword", "twitchState", "home", "localBattleUserState", "shop", "screen", "lootBoxes")
val Migrated: Set<String> = setOf("inviteState", "loginByPassword", "twitchState", "home", "localBattleUserState", "shop", "screen", "lootBoxes", "garage")
/** 未迁移字段:归约时原样透传,作为后续迁移清单。 */
val Unmigrated: List<SliceWiring> get() = All.filter { it.Field !in Migrated }
@@ -9,8 +9,8 @@
// ⑥ 动作目录:816 个类与 138 个真实派发名对齐
// ⑦ 回放:真实 toString 样例的类/字段/类型校验
// ⑧ 全目录动作类都能按 KClass 路由
// ⑨~⑱ 切片 reducer:InviteState / LoginByPasswordState / TwitchState / HomeScreen / LocalBattleUserState /
// Shop(含 U7 类别表与 w3 物品表两个子 reducer)/ Screen / LootBoxes;⑭ 根 reducer 的 89 槽位委派
// ⑨~⑲ 切片 reducer:InviteState / LoginByPasswordState / TwitchState / HomeScreen / LocalBattleUserState /
// Shop(含 U7 类别表与 w3 物品表两个子 reducer)/ Screen / LootBoxes / Garage;⑭ 根 reducer 的 89 槽位委派
//
// 用法:$env:TANKIOFFLINE_STATE_TEST='1'; node …(见 TankiOffline/tools/RunStateTest.ps1)
package App
@@ -166,6 +166,80 @@ import App.State.Reducers.SetIsBlockOpenButtons
import App.State.Reducers.SetIsReopening
import App.State.Reducers.SetPeriodOfGiveLootBox
import App.State.Reducers.SetTimeToGiveLootBox
import App.State.Devices
import App.State.AddRelatedBazooka
import App.State.AddRelatedResistanceModule
import App.State.AddToFavoriteItem
import App.State.AddOverdriveDescription
import App.State.BuyProtectionSlot
import App.State.GarageAddItemToDepot
import App.State.GarageApplyResistanceMount
import App.State.GarageBaseItems
import App.State.GarageBuyDevice
import App.State.GarageDevice
import App.State.GarageDeviceObject
import App.State.GarageDeviceTrySetInfinityLifetime
import App.State.GarageDevicesLoadedAction
import App.State.GarageInsertDeviceClientAndServer
import App.State.GarageItem
import App.State.GarageItemModification
import App.State.GarageItemObject
import App.State.GarageItemsState
import App.State.GarageObject
import App.State.GarageRemoveDevice
import App.State.GarageRemoveDiscountForDevice
import App.State.GarageRemoveItemFromDepot
import App.State.GarageRemoveItemFromStore
import App.State.GarageResistanceUnMount
import App.State.GarageSelectAlteration
import App.State.GarageSelectItem
import App.State.GarageSelectShotSkin
import App.State.GarageSelectSkin
import App.State.GarageUpdateItemCount
import App.State.GarageUpgradeItemAction
import App.State.GarageUpgradeItemConfirm
import App.State.GarageUpgradeItemCoolDownFinished
import App.State.GarageUpgradeItemCoolDownStarted
import App.State.OverdriveDescriptionData
import App.State.RemoveFromFavoriteItem
import App.State.RemoveFromSale
import App.State.SetGarageSkinsAction
import App.State.SetItemRarity
import App.State.SetRestrictionMount
import App.State.SwitchingRowsScroll
import App.State.UpdateDiscount
import App.State.UpgradableItemParams
import App.State.Reducers.AddGarageItemCount
import App.State.Reducers.AddNewItem
import App.State.Reducers.ClearUpgradeItemInProcessing
import App.State.Reducers.ClearWaitServerItemMount
import App.State.Reducers.GarageDefaultCategory
import App.State.Reducers.GarageLoaded
import App.State.Reducers.GarageMountItem
import App.State.Reducers.GarageMountItems
import App.State.Reducers.GarageResistanceCategoryKey
import App.State.Reducers.GarageStateSlice
import App.State.Reducers.GarageUnmountItem
import App.State.Reducers.HideVideoInDescription
import App.State.Reducers.ReduceGarage
import App.State.Reducers.RemoveNewItem
import App.State.Reducers.RemoveWaitServerItemMount
import App.State.Reducers.ResetGarage
import App.State.Reducers.ResetMountDelay
import App.State.Reducers.SelectGarageCategory
import App.State.Reducers.SetCooldownReduceEndTime
import App.State.Reducers.SetGarageNotLoaded
import App.State.Reducers.SetGarageObject
import App.State.Reducers.SetItemsOnDepot
import App.State.Reducers.SetItemsOnMarket
import App.State.Reducers.SetMountedItems
import App.State.Reducers.SetMountedShotSkinForItems
import App.State.Reducers.SetMountedSkinForItems
import App.State.Reducers.SetNewItems
import App.State.Reducers.SetResistanceReadyToMount
import App.State.Reducers.ShowGarageDiscountNotification
import App.State.Reducers.StartCooldownReduce
import State.InitState
private data class AddFrags(val fragsCount: Int)
private data class SetScreen(val screen: String)
@@ -539,7 +613,7 @@ object StateCheck {
Check("切片 LocalBattleUser:接 Store 派发后状态已归约", Store.State.IsReArmorTemporaryDisabled)
}
// ⑭ 根 reducer:89 个槽位的委派表 + 八个已迁移切片一次归约
// ⑭ 根 reducer:89 个槽位的委派表 + 九个已迁移切片一次归约
run {
Check("根:根状态 89 个切片槽位", RootWiring.All.size == 89)
Check(
@@ -603,13 +677,13 @@ object StateCheck {
LocalBattleW.Reducer == "(inline@localBattleUserState)"
)
Check(
"根:迁移表 = 八个已迁移字段,其余 81 个待迁",
"根:迁移表 = 九个已迁移字段,其余 80 个待迁",
RootReducer.Migrated ==
setOf(
"inviteState", "loginByPassword", "twitchState", "home",
"localBattleUserState", "shop", "screen", "lootBoxes",
"localBattleUserState", "shop", "screen", "lootBoxes", "garage",
) &&
RootWiring.Unmigrated.size == 81
RootWiring.Unmigrated.size == 80
)
// 一次 ReduceRoot 里八个切片各归约自己的动作,互不串片
@@ -665,7 +739,7 @@ object StateCheck {
S5.User == "user-1" && S5.Screen.Current == NamedScreen("Preloader")
)
Check("根:无关动作返回等值状态", ReduceRoot(S5, AddFrags(1)) == S5)
Check("根:Coverage 文本", RootReducer.Coverage == "已迁移 8/89")
Check("根:Coverage 文本", RootReducer.Coverage == "已迁移 9/89")
val Store = ReduxStore(RootState(), { S, A -> ReduceRoot(S, A) })
Store.Dispatch(SetStatus("LOGGING_IN"))
@@ -1252,6 +1326,634 @@ object StateCheck {
}
}
// ⑲ 切片 reducer:Garage(原 module_10032 的 UB / hF,31 字段;58 个 instanceof + 2 个接口/单例臂)
run {
fun ToObj(
Id: Long,
Base: Long? = null,
Category: Int = 1,
View: Int = 1,
Count: Int = 3,
Buyable: Boolean = true,
): GarageItemObject = GarageItemObject(
id = Id,
description = "obj-$Id",
item = "preview-$Id",
isPremium = false,
itemCategory = Category,
itemViewCategory = View,
buyable = Buyable,
availableForAll = false,
discount = null,
timePeriod = null,
modification = if (Base != null) GarageItemModification(Base, 0) else null,
properties = null,
upgradeableParams = null,
devices = null,
count = Count,
kit = null,
countable = false,
mountIndex = null,
availableSkins = null,
availableShotSkins = null,
mountedSkin = null,
mountedShotSkin = null,
skinPreview = null,
grouped = null,
rarity = null,
favorite = false,
isSellingForCoin = false,
isSellingForCrystal = false,
isSellingForRuby = false,
resistanceBackgroundImg = null,
visible = true,
descriptionMedia = null,
saleByKitItems = null,
)
fun ToItem(
Id: Long,
Base: Long? = null,
Category: Int = 1,
View: Int = 1,
Owned: Boolean = true,
Mounted: Boolean = false,
Count: Int = 1,
Buyable: Boolean = true,
Favorite: Boolean = false,
HasDeviceSale: Boolean = false,
DevicePreview: Any? = null,
Level: Int = 0,
Confirmed: Boolean = false,
Discount: Any? = null,
): GarageItem = GarageItem(
id = Id,
name = "item-$Id",
category = Category,
viewCategory = View,
priceWithoutDiscount = 100,
description = "item-$Id",
descriptionMedia = null,
preview = "preview-$Id",
isPremium = false,
owned = Owned,
mountable = true,
mounted = Mounted,
mountIndex = null,
position = 0,
minRank = 0,
maxRank = 0,
discount = Discount,
modification = if (Base != null) GarageItemModification(Base, Level) else null,
properties = null,
upgradeableParams = UpgradableItemParams(
itemPropertyParams = null,
properties = null,
currentLevel = Level,
upgradeParams = null,
upgradeDiscount = 0,
upgradeConfirmed = Confirmed,
),
count = Count,
kit = null,
countable = false,
hasDeviceSale = HasDeviceSale,
devicesAvailable = false,
devicePreview = DevicePreview,
buyable = Buyable,
availableForAll = false,
availableSkins = null,
availableShotSkins = null,
mountedSkin = null,
mountedShotSkin = null,
skinPreview = null,
group = null,
rarity = null,
favorite = Favorite,
resistanceBackgroundImg = null,
visible = true,
secret = false,
saleByKitItems = null,
isSellingForCoin = false,
isSellingForCrystal = false,
isSellingForRuby = false,
isSellingForMoney = false,
isSellingForADS = false,
)
fun ToDevice(
Id: Long,
Base: Long,
Installed: Boolean = false,
Preview: Any? = "device-$Id",
Infinity: Boolean = false,
): GarageDevice = GarageDevice(
id = Id,
baseItemId = Base,
installed = Installed,
name = "device-$Id",
category = 1,
viewCategory = 1,
priceWithoutDiscount = 50,
description = "device-$Id",
descriptionMedia = null,
previewImage = Preview,
isPremium = false,
position = 0,
minRank = 0,
maxRank = 0,
discount = 0,
timeDiscountEnd = null,
timeDiscountToStart = null,
infinityLifetimeItem = Infinity,
lifeTimeInSec = 0,
property = "",
properties = null,
isSellingForCoin = false,
isSellingForCrystal = false,
isSellingForRuby = false,
isSellingForADS = false,
isSellingForMoney = false,
rarity = null,
visible = true,
secret = false,
buyable = true,
)
fun ToDeviceObj(Id: Long, Base: Long, Preview: Any? = "device-$Id"): GarageDeviceObject =
GarageDeviceObject(
id = Id,
baseItemId = Base,
temporaryItemCC = null,
description = "device-$Id",
item = Preview,
isPremium = false,
itemCategory = 1,
itemViewCategory = 1,
buyable = true,
discount = null,
timePeriod = null,
deviceProperty = "",
rarity = null,
visible = true,
descriptionMedia = null,
)
fun BaseState(
ItemList: List<GarageItem>,
DeviceMap: Map<Long, List<GarageDevice>> = emptyMap(),
): GarageStateSlice {
val Grouped = ItemList
.groupBy { it.modification?.vcf_1 ?: it.id }
.mapValues { GarageBaseItems(it.value) }
return GarageStateSlice(
Items = GarageItemsState(Grouped),
Devices = Devices(DeviceMap),
)
}
fun ItemIn(State: GarageStateSlice, Id: Long): GarageItem =
State.Items.itemsByBase.values.flatMap { it.Items }.first { it.id == Id }
val S0 = GarageStateSlice()
Check(
"切片 Garage:默认值与原构造一致(31 字段)",
S0.Items.itemsByBase.isEmpty() && S0.Devices.baseItemIdToDevices.isEmpty() &&
S0.GarageObject == null && !S0.IsLoaded &&
S0.CurrentCategory == GarageDefaultCategory && S0.CurrentItemId == null &&
S0.SelectAlterationId == null && S0.SelectedSkinId == null &&
S0.SelectedShotSkinId == null && S0.NewItems.isEmpty() &&
!S0.ResistanceReadyToMount && S0.DelayMountTimeMs == 0L &&
S0.CooldownReduceEndTime == 0L && S0.TimeToReduceUpdateInMin == 0 &&
S0.CurrentRarity == null && S0.PurchaseOfUpgradesShopItemId == 920109776942L &&
!S0.GarageDiscountNotification && S0.ShowVideoInDescription &&
S0.UnlockedProtectionSlots == 1 && S0.PricesToUnlockSlot.isEmpty() &&
S0.NumberRowsInPaintsUI == 2 && S0.OverdriveDescriptions.isEmpty() &&
S0.WeaponToResistanceModule.isEmpty() && S0.WaitServerItemMount.isEmpty() &&
S0.BazookaToSupply.isEmpty() && S0.UpgradeCoolDownEndTimes.isEmpty() &&
S0.UpgradeItemInProcessingId == null
)
val O1 = ToObj(101, Count = 3)
val O2 = ToObj(202, Count = 1, Buyable = false)
val O3 = ToObj(303, Count = 2)
val O4 = ToObj(404, Count = 1)
val GarageObj = GarageObject("temp")
val S1 = ReduceGarage(S0, SetGarageObject(GarageObj))
Check("切片 Garage:SetGarageObject", S1.GarageObject == GarageObj)
val S1a = ReduceGarage(S1, SetItemsOnDepot(listOf(O1, O3)))
Check(
"切片 Garage:SetItemsOnDepot",
S1a.ItemsOnDepot.toList() == listOf(O1, O3) && S1a.GarageObject == GarageObj
)
val S1b = ReduceGarage(S1a, SetItemsOnMarket(listOf(O2)))
Check("切片 Garage:SetItemsOnMarket", S1b.ItemsOnMarket.toList() == listOf(O2))
val S1c = ReduceGarage(S1b, SetMountedItems(listOf(O3)))
Check("切片 Garage:SetMountedItems", S1c.MountedItems.toList() == listOf(O3))
val S1d = ReduceGarage(S1c, SetGarageSkinsAction(listOf(O4)))
Check("切片 Garage:SetGarageSkinsAction", S1d.SkinsItems.toList() == listOf(O4))
val Loaded0 = GarageStateSlice(
ItemsOnDepot = listOf(O1, O3, ToObj(505, Count = 1)),
MountedItems = listOf(O3),
ItemsOnMarket = listOf(O2),
SkinsItems = listOf(O4),
)
val Loaded1 = ReduceGarage(Loaded0, GarageLoaded())
val LoadedItems = Loaded1.Items.itemsByBase.values.flatMap { it.Items }
Check(
"切片 Garage:GarageLoaded 重建 PB(depot/mounted/market/skins)",
Loaded1.IsLoaded && LoadedItems.map { it.id }.sorted() == listOf(101L, 202L, 303L, 404L, 505L) &&
LoadedItems.first { it.id == 101L }.owned &&
LoadedItems.first { it.id == 303L }.owned && LoadedItems.first { it.id == 303L }.mounted &&
!LoadedItems.first { it.id == 202L }.owned && !LoadedItems.first { it.id == 202L }.buyable &&
!LoadedItems.first { it.id == 404L }.owned
)
Check(
"切片 Garage:SetGarageNotLoaded",
!ReduceGarage(Loaded1, SetGarageNotLoaded()).IsLoaded
)
val SC = ReduceGarage(S0, SelectGarageCategory("paints"))
Check(
"切片 Garage:SelectGarageCategory 同时清空 currentItemId",
SC.CurrentCategory == "paints" && SC.CurrentItemId == null
)
val SI = ReduceGarage(SC, GarageSelectItem(101))
Check(
"切片 Garage:GarageSelectItem 只改 currentItemId",
SI.CurrentItemId == 101L && SI.CurrentCategory == "paints"
)
val I101 = ToItem(101, Count = 3)
val SU = ReduceGarage(BaseState(listOf(I101)), GarageUpdateItemCount(I101, 9))
Check("切片 Garage:GarageUpdateItemCount", ItemIn(SU, 101).count == 9)
val L1 = ToItem(110, Base = 110, Level = 1, DevicePreview = "lvl-1")
val L2 = ToItem(111, Base = 110, Level = 2, DevicePreview = "lvl-2")
val L3 = ToItem(112, Base = 110, Level = 3, DevicePreview = "lvl-3")
val AddCount = ReduceGarage(BaseState(listOf(L1, L2, L3)), AddGarageItemCount(L2, 4))
Check(
"切片 Garage:AddGarageItemCount(owned=true、count+=、devicePreview 取等级项)",
ItemIn(AddCount, 111).owned && ItemIn(AddCount, 111).count == 5 &&
ItemIn(AddCount, 111).devicePreview == "lvl-2"
)
val M1 = ToItem(201, Mounted = true)
val M2 = ToItem(202)
val Mounted = ReduceGarage(BaseState(listOf(M1, M2)), GarageMountItem(M2))
Check(
"切片 Garage:GarageMountItem 先卸同 viewCategory 再装,并写 waitServerItemMount",
!ItemIn(Mounted, 201).mounted && ItemIn(Mounted, 202).mounted &&
Mounted.WaitServerItemMount[1] == true
)
val Unmounted = ReduceGarage(Mounted, GarageUnmountItem(202))
Check("切片 Garage:GarageUnmountItem", !ItemIn(Unmounted, 202).mounted)
val MountAll = ReduceGarage(
BaseState(listOf(M1, M2)),
GarageMountItems(listOf(201, 202)),
)
Check(
"切片 Garage:GarageMountItems 批量 owned+mounted",
ItemIn(MountAll, 201).mounted && ItemIn(MountAll, 201).owned &&
ItemIn(MountAll, 202).mounted && ItemIn(MountAll, 202).owned
)
val SkinBase = BaseState(listOf(ToItem(301, Base = 301), ToItem(302, Base = 301)))
val Skinned = ReduceGarage(SkinBase, SetMountedSkinForItems(9001, ItemIn(SkinBase, 302)))
Check(
"切片 Garage:SetMountedSkinForItems 整组写入 mountedSkin",
ItemIn(Skinned, 301).mountedSkin == 9001L && ItemIn(Skinned, 302).mountedSkin == 9001L
)
val Shot = ReduceGarage(SkinBase, SetMountedShotSkinForItems(9002, ItemIn(SkinBase, 302)))
Check(
"切片 Garage:SetMountedShotSkinForItems 整组写入 mountedShotSkin",
ItemIn(Shot, 301).mountedShotSkin == 9002L && ItemIn(Shot, 302).mountedShotSkin == 9002L
)
val R1 = ToItem(401, View = 5)
val R2 = ToItem(402, View = 5)
val Resist = ReduceGarage(BaseState(listOf(R1, R2)), GarageApplyResistanceMount(R1, 3, false))
Check(
"切片 Garage:GarageApplyResistanceMount(mounted + mountIndex + waitServerItemMount)",
ItemIn(Resist, 401).mounted && ItemIn(Resist, 401).mountIndex == 3 &&
Resist.WaitServerItemMount[GarageResistanceCategoryKey] == true
)
var ResistanceThrew = false
try {
ReduceGarage(Resist, GarageApplyResistanceMount(R2, 4, false))
} catch (_: Throwable) {
ResistanceThrew = true
}
Check("切片 Garage:抵抗模块装配数受 unlockedProtectionSlots 限制", ResistanceThrew)
val ResistOff = ReduceGarage(Resist, GarageResistanceUnMount(401, true))
Check(
"切片 Garage:GarageResistanceUnMount",
!ItemIn(ResistOff, 401).mounted &&
ResistOff.WaitServerItemMount[GarageResistanceCategoryKey] == true
)
val Up0 = ReduceGarage(
BaseState(listOf(ToItem(501, Level = 2, Confirmed = true))),
GarageUpgradeItemAction(501, 100, "coin"),
)
Check(
"切片 Garage:GarageUpgradeItemAction(level+1、confirmed=false、processing=itemId)",
ItemIn(Up0, 501).upgradeableParams!!.currentLevel == 3 &&
ItemIn(Up0, 501).upgradeableParams!!.upgradeConfirmed == false &&
Up0.UpgradeItemInProcessingId == 501L
)
val Up1 = ReduceGarage(Up0, GarageUpgradeItemCoolDownStarted(501, 12345))
Check("切片 Garage:GarageUpgradeItemCoolDownStarted", Up1.UpgradeCoolDownEndTimes[501L] == 12345L)
val Up2 = ReduceGarage(Up1, GarageUpgradeItemCoolDownFinished(501, 12345))
Check("切片 Garage:GarageUpgradeItemCoolDownFinished 命中时删键", Up2.UpgradeCoolDownEndTimes.isEmpty())
Check(
"切片 Garage:GarageUpgradeItemCoolDownFinished 不命中时原样返回",
ReduceGarage(Up1, GarageUpgradeItemCoolDownFinished(501, 999)) == Up1
)
val Confirmed = ReduceGarage(Up0, GarageUpgradeItemConfirm(501, 3))
Check(
"切片 Garage:GarageUpgradeItemConfirm 确认并清 processing",
ItemIn(Confirmed, 501).upgradeableParams!!.upgradeConfirmed == true &&
Confirmed.UpgradeItemInProcessingId == null
)
val ConfirmedOther = ReduceGarage(
Up0.copy(UpgradeItemInProcessingId = 999L),
GarageUpgradeItemConfirm(501, 3),
)
Check(
"切片 Garage:Confirm 其他 processing id 不清",
ConfirmedOther.UpgradeItemInProcessingId == 999L
)
Check(
"切片 Garage:ClearUpgradeItemInProcessing(原 xU 单例)",
ReduceGarage(Up0, ClearUpgradeItemInProcessing()).UpgradeItemInProcessingId == null
)
val DItem = ToItem(601)
val DBase = BaseState(listOf(DItem), mapOf(601L to listOf(ToDevice(9001, 601))))
val DLoaded = ReduceGarage(
DBase,
GarageDevicesLoadedAction(
601,
listOf(ToDeviceObj(9001, 601), ToDeviceObj(9002, 601)),
ToDevice(9002, 601),
),
)
val DList = DLoaded.Devices.baseItemIdToDevices[601L].orEmpty()
Check(
"切片 Garage:GarageDevicesLoadedAction 合并旧列表并按 mountedDevice 置 installed",
DList.map { it.id }.sorted() == listOf(9001L, 9002L) &&
!DList.first { it.id == 9001L }.installed &&
DList.first { it.id == 9002L }.installed
)
val DRemoved = ReduceGarage(DLoaded, GarageRemoveDevice(ToDevice(9001, 601), DItem))
Check(
"切片 Garage:GarageRemoveDevice(devicePreview=null、installed=false)",
ItemIn(DRemoved, 601).devicePreview == null &&
!DRemoved.Devices.baseItemIdToDevices[601L]!!.first { it.id == 9001L }.installed
)
val DInserted = ReduceGarage(
DLoaded,
GarageInsertDeviceClientAndServer(ToDevice(9002, 601, Preview = "dev-preview"), DItem),
)
Check(
"切片 Garage:GarageInsertDeviceClientAndServer(devicePreview=设备预览图、installed=true)",
ItemIn(DInserted, 601).devicePreview == "dev-preview" &&
DInserted.Devices.baseItemIdToDevices[601L]!!.first { it.id == 9002L }.installed
)
val DBought = ReduceGarage(
DLoaded,
GarageBuyDevice(ToDevice(9002, 601, Preview = "bought"), DItem, "coin", true),
)
Check(
"切片 Garage:GarageBuyDevice(installed=mount、infinityLifetimeItem=true、devicePreview)",
ItemIn(DBought, 601).devicePreview == "bought" &&
DBought.Devices.baseItemIdToDevices[601L]!!.first { it.id == 9002L }.installed &&
DBought.Devices.baseItemIdToDevices[601L]!!.first { it.id == 9002L }.infinityLifetimeItem
)
val DInf = ReduceGarage(DLoaded, GarageDeviceTrySetInfinityLifetime(9001))
Check(
"切片 Garage:GarageDeviceTrySetInfinityLifetime",
DInf.Devices.baseItemIdToDevices[601L]!!.first { it.id == 9001L }.infinityLifetimeItem
)
val Sel = ReduceGarage(S0, GarageSelectAlteration(7001))
val Sel2 = ReduceGarage(Sel, GarageSelectSkin(7002))
val Sel3 = ReduceGarage(Sel2, GarageSelectShotSkin(7003))
Check(
"切片 Garage:三个选择动作",
Sel3.SelectAlterationId == 7001L && Sel3.SelectedSkinId == 7002L &&
Sel3.SelectedShotSkinId == 7003L
)
val DiscItem = ToItem(801, Owned = true, HasDeviceSale = true)
val NoDisc = ReduceGarage(BaseState(listOf(DiscItem)), GarageRemoveDiscountForDevice(DiscItem))
Check(
"切片 Garage:GarageRemoveDiscountForDevice(hasDeviceSale && owned)",
ItemIn(NoDisc, 801).hasDeviceSale
)
val UnownedDisc = ToItem(802, Owned = false, HasDeviceSale = true)
val NoDisc2 = ReduceGarage(BaseState(listOf(UnownedDisc)), GarageRemoveDiscountForDevice(UnownedDisc))
Check("切片 Garage:未拥有项去折扣后 hasDeviceSale=false", !ItemIn(NoDisc2, 802).hasDeviceSale)
Check(
"切片 Garage:UpdateDiscount 保留原 discount 值",
ReduceGarage(BaseState(listOf(ToItem(803, Discount = "d"))), UpdateDiscount(ToItem(803, Discount = "d")))
.let { ItemIn(it, 803).discount == "d" }
)
val DepotItem = ToItem(901, Owned = false, Count = 1)
val DepotState = BaseState(listOf(DepotItem))
val Added = ReduceGarage(DepotState, GarageAddItemToDepot(901, 5))
Check("切片 Garage:GarageAddItemToDepot(owned=true、count 覆盖)",
ItemIn(Added, 901).owned && ItemIn(Added, 901).count == 5)
val Removed = ReduceGarage(Added, GarageRemoveItemFromDepot(ToObj(901, Count = 2)))
Check("切片 Garage:GarageRemoveItemFromDepot(owned=false、count 取动作 item)",
!ItemIn(Removed, 901).owned && ItemIn(Removed, 901).count == 2)
val NotSold = ReduceGarage(Added, GarageRemoveItemFromStore(901))
Check("切片 Garage:GarageRemoveItemFromStore(buyable=false)", !ItemIn(NotSold, 901).buyable)
val New0 = ReduceGarage(S0, SetNewItems(mapOf(1L to setOf(10L, 20L))))
val New1 = ReduceGarage(New0, AddNewItem(1, 30))
val New2 = ReduceGarage(New1, AddNewItem(2, 5))
Check(
"切片 Garage:SetNewItems / AddNewItem",
New2.NewItems[1L] == setOf(10L, 20L, 30L) && New2.NewItems[2L] == setOf(5L)
)
val New3 = ReduceGarage(New2, RemoveNewItem(1, 10))
val New4 = ReduceGarage(New3, RemoveNewItem(1, 20))
val New5 = ReduceGarage(New4, RemoveNewItem(1, 30))
Check(
"切片 Garage:RemoveNewItem 逐个移除并在空集时删键",
New3.NewItems[1L] == setOf(20L, 30L) && !New5.NewItems.containsKey(1L) &&
New5.NewItems[2L] == setOf(5L)
)
Check(
"切片 Garage:SetResistanceReadyToMount",
ReduceGarage(S0, SetResistanceReadyToMount(true)).ResistanceReadyToMount
)
val SaleBase = BaseState(
listOf(ToItem(1001, Base = 1001), ToItem(1002, Base = 1001), ToItem(1003, Base = 1003))
)
val Sale = ReduceGarage(SaleBase, RemoveFromSale(listOf(1001L)))
Check(
"切片 Garage:RemoveFromSale 只影响给定 baseItemId 组",
!ItemIn(Sale, 1001).buyable && !ItemIn(Sale, 1002).buyable && ItemIn(Sale, 1003).buyable
)
val Delay = ReduceGarage(S0, SetRestrictionMount(5000))
Check(
"切片 Garage:SetRestrictionMount 正延迟写成 now+delay",
Delay.DelayMountTimeMs > 0L && ReduceGarage(S0, SetRestrictionMount(0)).DelayMountTimeMs == 0L
)
Check("切片 Garage:ResetMountDelay", ReduceGarage(Delay, ResetMountDelay()).DelayMountTimeMs == 0L)
val Cool = ReduceGarage(S0, StartCooldownReduce(60, 5))
Check(
"切片 Garage:StartCooldownReduce(now+seconds*1000、minutes)",
Cool.CooldownReduceEndTime > 0L && Cool.TimeToReduceUpdateInMin == 5
)
Check(
"切片 Garage:SetCooldownReduceEndTime(now+seconds*1000)",
ReduceGarage(S0, SetCooldownReduceEndTime(10)).CooldownReduceEndTime > 0L
)
Check("切片 Garage:SetItemRarity", ReduceGarage(S0, SetItemRarity("rare")).CurrentRarity == "rare")
val Fav = BaseState(listOf(ToItem(1101)))
val Favorited = ReduceGarage(Fav, AddToFavoriteItem(1101))
Check("切片 Garage:AddToFavoriteItem", ItemIn(Favorited, 1101).favorite)
Check(
"切片 Garage:RemoveFromFavoriteItem",
!ItemIn(ReduceGarage(Favorited, RemoveFromFavoriteItem(1101)), 1101).favorite
)
val GoGarage = ReduceGarage(SC.copy(CurrentItemId = 101L), GoTo(NamedScreen("Garage"), false))
Check(
"切片 Garage:GoTo Garage 复位 currentCategory 并原样返回 currentItemId",
GoGarage.CurrentCategory == GarageDefaultCategory && GoGarage.CurrentItemId == 101L
)
val GoShop = ReduceGarage(SC, GoTo(NamedScreen("Shop"), false))
Check("切片 Garage:GoTo 其他屏幕不改分类", GoShop.CurrentCategory == "paints")
Check(
"切片 Garage:ShowGarageDiscountNotification",
ReduceGarage(S0, ShowGarageDiscountNotification()).GarageDiscountNotification
)
Check(
"切片 Garage:HideVideoInDescription",
!ReduceGarage(S0, HideVideoInDescription()).ShowVideoInDescription
)
Check(
"切片 Garage:BuyProtectionSlot",
ReduceGarage(S0, BuyProtectionSlot(1)).UnlockedProtectionSlots == 2
)
Check(
"切片 Garage:SwitchingRowsScroll",
ReduceGarage(S0, SwitchingRowsScroll(4)).NumberRowsInPaintsUI == 4
)
val Desc = OverdriveDescriptionData("name", "icon", "text")
Check(
"切片 Garage:AddOverdriveDescription",
ReduceGarage(S0, AddOverdriveDescription(12, Desc)).OverdriveDescriptions[12L] == Desc
)
Check(
"切片 Garage:AddRelatedResistanceModule",
ReduceGarage(S0, AddRelatedResistanceModule(12, 34)).WeaponToResistanceModule[12L] == 34L
)
Check(
"切片 Garage:AddRelatedBazooka",
ReduceGarage(S0, AddRelatedBazooka(12, 34)).BazookaToSupply[12L] == 34L
)
val Wait0 = S0.copy(WaitServerItemMount = mapOf(1 to true, 2 to true))
val Wait1 = ReduceGarage(Wait0, RemoveWaitServerItemMount(listOf(1)))
Check(
"切片 Garage:RemoveWaitServerItemMount 给定键置 false",
Wait1.WaitServerItemMount[1] == false && Wait1.WaitServerItemMount[2] == true
)
Check(
"切片 Garage:ClearWaitServerItemMount",
ReduceGarage(Wait1, ClearWaitServerItemMount()).WaitServerItemMount.isEmpty()
)
val Reset0 = S0.copy(
Devices = Devices(mapOf(1L to listOf(ToDevice(1, 1)))),
UpgradeCoolDownEndTimes = mapOf(1L to 2L),
UpgradeItemInProcessingId = 3L,
)
val Reset1 = ReduceGarage(Reset0, ResetGarage())
Check(
"切片 Garage:ResetGarage 清 devices / coolDown / processing",
Reset1.Devices.baseItemIdToDevices.isEmpty() &&
Reset1.UpgradeCoolDownEndTimes.isEmpty() && Reset1.UpgradeItemInProcessingId == null
)
val Init = ReduceGarage(
S0,
InitState(GarageStateSlice::class, { S: GarageStateSlice -> S.copy(IsLoaded = true) }),
)
Check("切片 Garage:InitState 按 GarageStateSlice::class 灌入状态", Init.IsLoaded)
Check(
"切片 Garage:InitState 其他类型不改状态",
ReduceGarage(S0, InitState(String::class, { S: GarageStateSlice -> S.copy(IsLoaded = true) })) == S0
)
Check("切片 Garage:无关动作返回等值状态", ReduceGarage(BaseState(listOf(I101)), AddFrags(1)) == BaseState(listOf(I101)))
val Root0 = RootState()
val Root1 = ReduceRoot(Root0, GarageSelectItem(101))
Check(
"根:GarageSelectItem 只动 garage",
Root1.Garage.CurrentItemId == 101L && Root1.Shop == Root0.Shop && Root1.Screen == Root0.Screen &&
Root1.LootBoxes == Root0.LootBoxes && Root1.Home == Root0.Home
)
val Root2 = ReduceRoot(Root1, SetGarageObject(GarageObj))
Check(
"根:SetGarageObject 保留 garage 前一次归约",
Root2.Garage.GarageObject == GarageObj && Root2.Garage.CurrentItemId == 101L &&
Root2.LootBoxes == Root1.LootBoxes
)
Check("根:garage 迁移后无关动作仍返回等值状态", ReduceRoot(Root2, AddFrags(1)) == Root2)
val W = RootWiring.ByField("garage")!!
Check(
"根:garage 委派到 UB / hF 且 58 个动作全部具名",
W.SliceClass == "UB" && W.Reducer == "hF" && W.Actions.size == 58 &&
W.Actions.none { it.startsWith("?") }
)
Check(
"根:garage 的动作名覆盖 58 个 instanceof 臂",
listOf(
"ResetGarage", "SetGarageObject", "SetItemsOnDepot", "SetItemsOnMarket",
"SetMountedItems", "GarageLoaded", "SetGarageNotLoaded", "SelectGarageCategory",
"GarageSelectItem", "GarageUpdateItemCount", "AddGarageItemCount", "GarageMountItem", "GarageUnmountItem",
"GarageMountItems", "SetMountedSkinForItems", "SetMountedShotSkinForItems",
"SetGarageSkinsAction",
"GarageApplyResistanceMount", "GarageResistanceUnMount", "GarageUpgradeItemAction",
"GarageUpgradeItemCoolDownStarted", "GarageUpgradeItemCoolDownFinished",
"GarageUpgradeItemConfirm", "GarageDevicesLoadedAction", "GarageRemoveDevice",
"GarageBuyDevice",
"GarageDeviceTrySetInfinityLifetime", "GarageSelectAlteration", "GarageSelectSkin",
"GarageSelectShotSkin", "GarageRemoveDiscountForDevice", "UpdateDiscount",
"GarageAddItemToDepot", "GarageRemoveItemFromDepot", "GarageRemoveItemFromStore",
"SetNewItems", "AddNewItem", "RemoveNewItem", "SetResistanceReadyToMount",
"RemoveFromSale", "SetRestrictionMount", "ResetMountDelay", "StartCooldownReduce",
"SetCooldownReduceEndTime", "SetItemRarity", "AddToFavoriteItem",
"RemoveFromFavoriteItem", "GoTo", "ShowGarageDiscountNotification",
"HideVideoInDescription", "BuyProtectionSlot", "SwitchingRowsScroll",
"AddOverdriveDescription", "AddRelatedResistanceModule", "AddRelatedBazooka",
"RemoveWaitServerItemMount", "ClearWaitServerItemMount", "InitState",
).all { it in W.Actions }
)
Check(
"根:garage 的动作表不含 FX / UB 标识类",
W.Actions.none { it == "FX" || it == "UB" || it == "Garage" }
)
}
val Failed = Results.filter { !it.second }
println("TankiOffline 状态层自检:${Results.size} 项")
for ((Name, Ok) in Results) println((if (Ok) " ✓ " else " ✗ ") + Name)