panel public release
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,175 @@
|
||||
// Code generated from 7DTD V3.0 SandboxOptions enum (Assembly-CSharp,
|
||||
// buildid 23705258). The integer value of each member IS the 2-letter code in a
|
||||
// SandboxCode (via IndexToAlpha2). Order is authoritative — transcribed 1:1 from
|
||||
// rb-decompile/3.0/src/SandboxOptions/SandboxOptions.cs, INCLUDING the UNUSED1
|
||||
// (116) / UNUSED2 (117) gap. Do not reorder.
|
||||
package sandbox
|
||||
|
||||
// sandboxEnumNames is the SandboxOptions enum in declaration order; the slice
|
||||
// index equals the enum integer value. UNUSED1/UNUSED2 are kept so subsequent
|
||||
// members get their correct integer. "Max" terminates the enum (not an option).
|
||||
var sandboxEnumNames = []string{
|
||||
"RangedDamage", // 0
|
||||
"MeleeDamage", // 1
|
||||
"BlockDamage", // 2
|
||||
"TerrainDamage", // 3
|
||||
"HeadshotMultiplier", // 4
|
||||
"CrouchSpeed", // 5
|
||||
"WalkSpeed", // 6
|
||||
"RunSpeed", // 7
|
||||
"JumpStrength", // 8
|
||||
"StaminaUsage", // 9
|
||||
"StaminaRegen", // 10
|
||||
"PlayerLevelBonusApplied", // 11
|
||||
"JarRefund", // 12
|
||||
"ShowHealthBars", // 13
|
||||
"ShowEnemyDamage", // 14
|
||||
"NewbieCoat", // 15
|
||||
"HeadshotMode", // 16
|
||||
"IncomingDamage", // 17
|
||||
"XPMultiplier", // 18
|
||||
"ShowXP", // 19
|
||||
"EncumbranceModifier", // 20
|
||||
"ItemDegradation", // 21
|
||||
"LoseItemsOnDeathType", // 22
|
||||
"LoseItemsOnDeathCount", // 23
|
||||
"DegradeItemsOnDeath", // 24
|
||||
"DegradeAmountOnDeath", // 25
|
||||
"DeathPenalty", // 26
|
||||
"DropOnDeath", // 27
|
||||
"DropOnQuit", // 28
|
||||
"InfectionRate", // 29
|
||||
"EnemySpawnMode", // 30
|
||||
"EntityDamage", // 31
|
||||
"BlockDamageAI", // 32
|
||||
"BlockDamageAIBM", // 33
|
||||
"ZombieMove", // 34
|
||||
"ZombieMoveNight", // 35
|
||||
"ZombieFeralMove", // 36
|
||||
"ZombieBMMove", // 37
|
||||
"ZombieFeralSense", // 38
|
||||
"AISmellMode", // 39
|
||||
"AllowZombieDigging", // 40
|
||||
"ZombieRageChance", // 41
|
||||
"EntityIncomingDamage", // 42
|
||||
"MaxEnemyTier", // 43
|
||||
"BiomeZombieRespawn", // 44
|
||||
"BiomeAnimalRespawn", // 45
|
||||
"BiomeEnemyDensity", // 46
|
||||
"ZombiesEatAnimals", // 47
|
||||
"BloodMoonFrequency", // 48
|
||||
"BloodMoonRange", // 49
|
||||
"BloodMoonWarning", // 50
|
||||
"BloodMoonEnemyCount", // 51
|
||||
"AirDropFrequency", // 52
|
||||
"AirDropMarker", // 53
|
||||
"AirDropRandomTime", // 54
|
||||
"BiomeProgression", // 55
|
||||
"TemperatureSurvival", // 56
|
||||
"StormFreq", // 57
|
||||
"StormWarning", // 58
|
||||
"HeatMapSensitivity", // 59
|
||||
"GlobalGSModifier", // 60
|
||||
"BiomeGSModifier", // 61
|
||||
"GlobalLSModifier", // 62
|
||||
"BiomeLSModifier", // 63
|
||||
"POITierLSModifier", // 64
|
||||
"GlobalTSModifier", // 65
|
||||
"DayNightLength", // 66
|
||||
"DayLightLength", // 67
|
||||
"AllowMap", // 68
|
||||
"AllowCompass", // 69
|
||||
"AllowScreenMarkers", // 70
|
||||
"ShowLocationInfo", // 71
|
||||
"ShowDayTime", // 72
|
||||
"WorkstationsInTheWild", // 73
|
||||
"MaxTechType", // 74
|
||||
"LootRespawnDays", // 75
|
||||
"LootTimer", // 76
|
||||
"LootMaxTier", // 77
|
||||
"GlobalLootCount", // 78
|
||||
"FoodLootCount", // 79
|
||||
"DrinkLootCount", // 80
|
||||
"MedicalLootCount", // 81
|
||||
"AmmoLootCount", // 82
|
||||
"ResourceLootCount", // 83
|
||||
"ArmorLootCount", // 84
|
||||
"MeleeLootCount", // 85
|
||||
"RangedLootCount", // 86
|
||||
"DukesLootCount", // 87
|
||||
"CraftingMagazinesLootCount", // 88
|
||||
"TreasureMapChance", // 89
|
||||
"LootBagChance", // 90
|
||||
"CropOutput", // 91
|
||||
"SeedDropOutput", // 92
|
||||
"CropGrowthSpeed", // 93
|
||||
"BackpackCrafting", // 94
|
||||
"WorkstationCrafting", // 95
|
||||
"CraftingProgression", // 96
|
||||
"CraftingTime", // 97
|
||||
"CraftingInput", // 98
|
||||
"CraftingOutput", // 99
|
||||
"CraftingMaxTier", // 100
|
||||
"MiningOutput", // 101
|
||||
"HarvestingOutput", // 102
|
||||
"ScrappingOutput", // 103
|
||||
"SmeltingType", // 104
|
||||
"DewCollectorTime", // 105
|
||||
"DewCollectorOutput", // 106
|
||||
"DewCollectorInput", // 107
|
||||
"ApiaryTime", // 108
|
||||
"ApiaryOutput", // 109
|
||||
"ApiaryInput", // 110
|
||||
"UNUSED1", // 111
|
||||
"UNUSED2", // 112
|
||||
"RepairTypes", // 113
|
||||
"MaxDegradationAmount", // 114
|
||||
"PointsPerMagazine", // 115
|
||||
"SkillGainRate", // 116
|
||||
"SkillPointsPerLevel", // 117
|
||||
"QuestsEnabled", // 118
|
||||
"IntroQuestEnabled", // 119
|
||||
"TraderToTraderQuestsEnabled", // 120
|
||||
"StarterSkillPoints", // 121
|
||||
"QuestsPerTier", // 122
|
||||
"QuestProgressionDailyLimit", // 123
|
||||
"BuriedQuestsEnabled", // 124
|
||||
"POIQuestsEnabled", // 125
|
||||
"TraderDialog", // 126
|
||||
"TraderHours", // 127
|
||||
"TradersEnabled", // 128
|
||||
"VendingEnabled", // 129
|
||||
"TraderSellPrices", // 130
|
||||
"TraderBuyPrices", // 131
|
||||
"TraderProtection", // 132
|
||||
"TraderResetInterval", // 133
|
||||
"TraderItemAbundance", // 134
|
||||
"TraderBuyLimit", // 135
|
||||
"TraderMaxTier", // 136
|
||||
"VendingResetInterval", // 137
|
||||
"VendingItemAbundance", // 138
|
||||
"ChallengesEnabled", // 139
|
||||
"IntroChallengesEnabled", // 140
|
||||
"VehicleFuelUsage", // 141
|
||||
"VehicleEntityDamage", // 142
|
||||
"VehicleBlockDamage", // 143
|
||||
"VehicleSelfDamage", // 144
|
||||
"ElectricalOutput", // 145
|
||||
"SillyCelebrate", // 146
|
||||
"SillyBigHeads", // 147
|
||||
"SillyTinyZombies", // 148
|
||||
"SillySounds", // 149
|
||||
"SillyLowGravity", // 150
|
||||
"SillyBlackandWhite", // 151
|
||||
// "Max" (152) — enum terminator, not an option.
|
||||
}
|
||||
|
||||
// enumByName maps a SandboxOptions member name to its integer value, built once
|
||||
// from sandboxEnumNames.
|
||||
var enumByName = func() map[string]int {
|
||||
m := make(map[string]int, len(sandboxEnumNames))
|
||||
for i, n := range sandboxEnumNames {
|
||||
m[n] = i
|
||||
}
|
||||
return m
|
||||
}()
|
||||
@@ -0,0 +1,208 @@
|
||||
// Code generated from 7DTD V3.0 Assembly-CSharp SandboxOptionManager.SetupOptions
|
||||
// + the SandboxOptions enum (buildid 23705258, experimental 2026-06-15).
|
||||
// The Enum field is the integer value of the SandboxOptions enum member — it IS
|
||||
// the 2-letter code in a SandboxCode (IndexToAlpha2((int)enum)). DO NOT renumber.
|
||||
// CrackedFrom: rb-decompile/3.0/src/SandboxOptions/{SandboxOptions.cs,SandboxOptionManager.cs}
|
||||
package sandbox
|
||||
|
||||
// Category is the 8-way grouping the in-game Sandbox Options window uses. The
|
||||
// decompile's CategoryName strings are: General, Entities, World, Resources,
|
||||
// Crafting, Traders, Tasks, Misc. We surface friendlier titles in the UI but
|
||||
// keep the raw key here.
|
||||
type optionType int
|
||||
|
||||
const (
|
||||
optFloat optionType = iota
|
||||
optInt
|
||||
optBool
|
||||
)
|
||||
|
||||
// option mirrors one AddSandboxOption(...) registration. Enum + set are resolved
|
||||
// at init from the authoritative SandboxOptions enum (enum.go) and valueSets
|
||||
// table — never hand-typed here (the Name is the only stable key authored).
|
||||
type option struct {
|
||||
Name string // SandboxOptions enum member name (stable key)
|
||||
Display string // human label from the registration
|
||||
Category string // General|Entities|World|Resources|Crafting|Traders|Tasks|Misc
|
||||
Type optionType // float/int/bool — which value-set kind
|
||||
ValueSet string // key into valueSets
|
||||
// Default is the option's default value (float for optFloat, int for optInt,
|
||||
// bool for optBool). Resolved to a default INDEX in the value-set at init.
|
||||
DefaultFloat float64
|
||||
DefaultInt int
|
||||
DefaultBool bool
|
||||
|
||||
// Resolved at init() — do not set in literals.
|
||||
enum int // SandboxOptions integer value (the 2-char code), from enumByName
|
||||
set valueSet // resolved value-set, from valueSets[ValueSet]
|
||||
}
|
||||
|
||||
// options is the full ordered registration list (insertion order from
|
||||
// SetupOptions). Enum is set explicitly to survive the UNUSED1/UNUSED2 gap.
|
||||
var options = []option{
|
||||
// ---- General / Player ----
|
||||
{Name: "RangedDamage", Display: "Ranged Damage", Category: "General", Type: optFloat, ValueSet: "DamageValues", DefaultFloat: 1},
|
||||
{Name: "MeleeDamage", Display: "Melee Damage", Category: "General", Type: optFloat, ValueSet: "DamageValues", DefaultFloat: 1},
|
||||
{Name: "BlockDamage", Display: "Block Damage", Category: "General", Type: optFloat, ValueSet: "DamageValues", DefaultFloat: 1},
|
||||
{Name: "TerrainDamage", Display: "Terrain Damage", Category: "General", Type: optFloat, ValueSet: "DamageValues", DefaultFloat: 1},
|
||||
{Name: "HeadshotMultiplier", Display: "Headshot Multiplier", Category: "General", Type: optFloat, ValueSet: "DamageValues", DefaultFloat: 1},
|
||||
{Name: "IncomingDamage", Display: "Incoming Damage", Category: "General", Type: optFloat, ValueSet: "DamageValues", DefaultFloat: 1},
|
||||
{Name: "WalkSpeed", Display: "Walk Speed", Category: "General", Type: optFloat, ValueSet: "PlayerSpeedValuesWithNone", DefaultFloat: 1},
|
||||
{Name: "RunSpeed", Display: "Run Speed", Category: "General", Type: optFloat, ValueSet: "PlayerSpeedValues", DefaultFloat: 1},
|
||||
{Name: "CrouchSpeed", Display: "Crouch Speed", Category: "General", Type: optFloat, ValueSet: "PlayerSpeedValues", DefaultFloat: 1},
|
||||
{Name: "JumpStrength", Display: "Jump Height", Category: "General", Type: optFloat, ValueSet: "JumpStrength", DefaultFloat: 1},
|
||||
{Name: "StaminaRegen", Display: "Stamina Regen", Category: "General", Type: optFloat, ValueSet: "StaminaRegen", DefaultFloat: 1},
|
||||
{Name: "StaminaUsage", Display: "Stamina Usage", Category: "General", Type: optFloat, ValueSet: "StaminaUsage", DefaultFloat: 1},
|
||||
{Name: "XPMultiplier", Display: "XP Multiplier", Category: "General", Type: optFloat, ValueSet: "XPGain", DefaultFloat: 1},
|
||||
{Name: "ShowXP", Display: "Show XP", Category: "General", Type: optInt, ValueSet: "ShowXP", DefaultInt: 0},
|
||||
{Name: "PlayerLevelBonusApplied", Display: "Level Health/Stam Bonus", Category: "General", Type: optBool, ValueSet: "YesNo", DefaultBool: true},
|
||||
{Name: "SkillGainRate", Display: "Skill Gain Rate", Category: "General", Type: optInt, ValueSet: "SkillGainRate", DefaultInt: 1},
|
||||
{Name: "SkillPointsPerLevel", Display: "Skill Gain Amount", Category: "General", Type: optInt, ValueSet: "PointsPer", DefaultInt: 1},
|
||||
{Name: "DeathPenalty", Display: "Death Penalty", Category: "General", Type: optInt, ValueSet: "DeathPenalty", DefaultInt: 1},
|
||||
{Name: "LoseItemsOnDeathType", Display: "Lose Items Death Type", Category: "General", Type: optInt, ValueSet: "LoseItemsOnDeathType", DefaultInt: 0},
|
||||
{Name: "LoseItemsOnDeathCount", Display: "Lose Items Death Count", Category: "General", Type: optInt, ValueSet: "LoseItemCount", DefaultInt: 1},
|
||||
{Name: "DegradeItemsOnDeath", Display: "Degrade Items On Death", Category: "General", Type: optInt, ValueSet: "DegradeItemsOnDeath", DefaultInt: 0},
|
||||
{Name: "DegradeAmountOnDeath", Display: "Degrade Amount On Death", Category: "General", Type: optFloat, ValueSet: "MaxDegradationAmounts", DefaultFloat: 0.1},
|
||||
{Name: "DropOnDeath", Display: "Drop On Death", Category: "General", Type: optInt, ValueSet: "DropOnDeath", DefaultInt: 1},
|
||||
{Name: "DropOnQuit", Display: "Drop On Quit", Category: "General", Type: optInt, ValueSet: "DropOnQuit", DefaultInt: 0},
|
||||
{Name: "InfectionRate", Display: "Infection Rate", Category: "General", Type: optFloat, ValueSet: "SpeedValues", DefaultFloat: 1},
|
||||
{Name: "NewbieCoat", Display: "Allow Newbie Coat", Category: "General", Type: optBool, ValueSet: "YesNo", DefaultBool: true},
|
||||
{Name: "EncumbranceModifier", Display: "Encumbrance Modifier", Category: "General", Type: optFloat, ValueSet: "Encumbrance", DefaultFloat: 1},
|
||||
{Name: "JarRefund", Display: "Jar Refund", Category: "General", Type: optFloat, ValueSet: "JarRefund", DefaultFloat: 0.6},
|
||||
|
||||
// ---- Entities ----
|
||||
{Name: "EnemySpawnMode", Display: "Enemy Spawn", Category: "Entities", Type: optBool, ValueSet: "YesNo", DefaultBool: true},
|
||||
{Name: "MaxEnemyTier", Display: "Max Enemy Type", Category: "Entities", Type: optInt, ValueSet: "MaxEnemyType", DefaultInt: 5},
|
||||
{Name: "BiomeEnemyDensity", Display: "Biome Enemy Density", Category: "Entities", Type: optInt, ValueSet: "BiomeEnemyDensity", DefaultInt: 0},
|
||||
{Name: "BiomeZombieRespawn", Display: "Biome Enemy Respawn", Category: "Entities", Type: optInt, ValueSet: "SlowToFast", DefaultInt: 0},
|
||||
{Name: "BiomeAnimalRespawn", Display: "Biome Animal Respawn", Category: "Entities", Type: optInt, ValueSet: "SlowToFast", DefaultInt: 0},
|
||||
{Name: "EntityDamage", Display: "Entity Damage", Category: "Entities", Type: optFloat, ValueSet: "DamageValues", DefaultFloat: 1},
|
||||
{Name: "EntityIncomingDamage", Display: "Entity Incoming Damage", Category: "Entities", Type: optFloat, ValueSet: "DamageValuesNoNone", DefaultFloat: 1},
|
||||
{Name: "BlockDamageAI", Display: "Entity Block Damage", Category: "Entities", Type: optFloat, ValueSet: "DamageValues", DefaultFloat: 1},
|
||||
{Name: "BlockDamageAIBM", Display: "Blood Moon Block Damage", Category: "Entities", Type: optFloat, ValueSet: "DamageValues", DefaultFloat: 1},
|
||||
{Name: "HeadshotMode", Display: "Headshot Mode", Category: "Entities", Type: optInt, ValueSet: "HeadshotMode", DefaultInt: 0},
|
||||
{Name: "ShowHealthBars", Display: "Entity Health Bars", Category: "Entities", Type: optBool, ValueSet: "YesNo", DefaultBool: false},
|
||||
{Name: "ShowEnemyDamage", Display: "Show Entity Damage", Category: "Entities", Type: optBool, ValueSet: "YesNo", DefaultBool: false},
|
||||
{Name: "ZombieMove", Display: "Zombie Day Speed", Category: "Entities", Type: optInt, ValueSet: "ZombieSpeeds", DefaultInt: 0},
|
||||
{Name: "ZombieMoveNight", Display: "Zombie Night Speed", Category: "Entities", Type: optInt, ValueSet: "ZombieSpeeds", DefaultInt: 3},
|
||||
{Name: "ZombieFeralMove", Display: "Zombie Feral Speed", Category: "Entities", Type: optInt, ValueSet: "ZombieSpeeds", DefaultInt: 3},
|
||||
{Name: "ZombieBMMove", Display: "Zombie Blood Moon Speed", Category: "Entities", Type: optInt, ValueSet: "ZombieSpeeds", DefaultInt: 3},
|
||||
{Name: "ZombieFeralSense", Display: "Zombie Feral Sense", Category: "Entities", Type: optInt, ValueSet: "ZombieFeralSense", DefaultInt: 0},
|
||||
{Name: "AISmellMode", Display: "Zombie AI Smell Mode", Category: "Entities", Type: optInt, ValueSet: "AISmellMode", DefaultInt: 3},
|
||||
{Name: "ZombieRageChance", Display: "Zombie Rage Chance", Category: "Entities", Type: optFloat, ValueSet: "ZombieRageChance", DefaultFloat: 0.15},
|
||||
{Name: "AllowZombieDigging", Display: "Allow Zombie Digging", Category: "Entities", Type: optBool, ValueSet: "YesNo", DefaultBool: true},
|
||||
{Name: "ZombiesEatAnimals", Display: "Zombies Eat Animals", Category: "Entities", Type: optBool, ValueSet: "YesNo", DefaultBool: true},
|
||||
|
||||
// ---- World ----
|
||||
{Name: "GlobalGSModifier", Display: "Global GameStage", Category: "World", Type: optFloat, ValueSet: "LowDefaultHigh", DefaultFloat: 1},
|
||||
{Name: "BiomeGSModifier", Display: "Biome GameStage", Category: "World", Type: optFloat, ValueSet: "LowDefaultHigh", DefaultFloat: 1},
|
||||
{Name: "BiomeProgression", Display: "Biome Progression", Category: "World", Type: optBool, ValueSet: "YesNo", DefaultBool: true},
|
||||
{Name: "TemperatureSurvival", Display: "Temperature Survival", Category: "World", Type: optBool, ValueSet: "YesNo", DefaultBool: true},
|
||||
{Name: "MaxTechType", Display: "Max Tech Type", Category: "World", Type: optInt, ValueSet: "MaxTechType", DefaultInt: 4},
|
||||
{Name: "WorkstationsInTheWild", Display: "Workstations in the Wild", Category: "World", Type: optFloat, ValueSet: "JarRefund", DefaultFloat: 0},
|
||||
{Name: "BloodMoonFrequency", Display: "Blood Moon Frequency", Category: "World", Type: optInt, ValueSet: "BloodMoonFrequency", DefaultInt: 7},
|
||||
{Name: "BloodMoonRange", Display: "Blood Moon Range", Category: "World", Type: optInt, ValueSet: "BloodMoonRange", DefaultInt: 0},
|
||||
{Name: "BloodMoonEnemyCount", Display: "Blood Moon Count", Category: "World", Type: optInt, ValueSet: "BloodMoonCount", DefaultInt: 8},
|
||||
{Name: "BloodMoonWarning", Display: "Blood Moon Warning", Category: "World", Type: optInt, ValueSet: "BloodMoonWarning", DefaultInt: 1}, // enum 50? see note — BloodMoonWarning=49 in enum order
|
||||
{Name: "AirDropFrequency", Display: "Air Drops", Category: "World", Type: optInt, ValueSet: "AirDrops", DefaultInt: 3},
|
||||
{Name: "AirDropRandomTime", Display: "Air Drop Random Time", Category: "World", Type: optInt, ValueSet: "AirDropRandomTime", DefaultInt: 0},
|
||||
{Name: "StormFreq", Display: "Storm Frequency", Category: "World", Type: optFloat, ValueSet: "StormFrequency", DefaultFloat: 1},
|
||||
{Name: "StormWarning", Display: "Storm Warning", Category: "World", Type: optBool, ValueSet: "YesNo", DefaultBool: true},
|
||||
{Name: "HeatMapSensitivity", Display: "Heatmap Sensitivity", Category: "World", Type: optFloat, ValueSet: "DisabledLowDefaultHigh", DefaultFloat: 1},
|
||||
{Name: "DayNightLength", Display: "24 Day Cycle", Category: "World", Type: optInt, ValueSet: "DayNightLength", DefaultInt: 60},
|
||||
{Name: "DayLightLength", Display: "Day Light Length", Category: "World", Type: optInt, ValueSet: "DayLightLength", DefaultInt: 18}, // enum 72? see note — DayLightLength=72 actually
|
||||
{Name: "AirDropMarker", Display: "Mark Air Drops", Category: "World", Type: optBool, ValueSet: "YesNo", DefaultBool: true},
|
||||
{Name: "AllowMap", Display: "Allow Map", Category: "World", Type: optBool, ValueSet: "YesNo", DefaultBool: true},
|
||||
{Name: "AllowCompass", Display: "Allow Compass", Category: "World", Type: optBool, ValueSet: "YesNo", DefaultBool: true},
|
||||
{Name: "AllowScreenMarkers", Display: "Allow Screen Markers", Category: "World", Type: optBool, ValueSet: "YesNo", DefaultBool: true},
|
||||
{Name: "ShowLocationInfo", Display: "Show Location Info", Category: "World", Type: optBool, ValueSet: "YesNo", DefaultBool: true}, // ShowLocationInfo=71? see note
|
||||
{Name: "ShowDayTime", Display: "Show Day/Time", Category: "World", Type: optBool, ValueSet: "YesNo", DefaultBool: true},
|
||||
|
||||
// ---- Resources ----
|
||||
{Name: "LootMaxTier", Display: "Loot Max Tier", Category: "Resources", Type: optInt, ValueSet: "ItemTierOptions", DefaultInt: 6},
|
||||
{Name: "GlobalLSModifier", Display: "Global LootStage", Category: "Resources", Type: optFloat, ValueSet: "LowDefaultHigh", DefaultFloat: 1},
|
||||
{Name: "BiomeLSModifier", Display: "Biome LootStage", Category: "Resources", Type: optFloat, ValueSet: "LowDefaultHigh", DefaultFloat: 1}, // BiomeLSModifier=68? see note
|
||||
{Name: "POITierLSModifier", Display: "POI Tier LootStage", Category: "Resources", Type: optFloat, ValueSet: "LowDefaultHigh", DefaultFloat: 1}, // =69? see note
|
||||
{Name: "LootRespawnDays", Display: "Loot Respawn Days", Category: "Resources", Type: optInt, ValueSet: "LootRespawnDays", DefaultInt: 7},
|
||||
{Name: "LootTimer", Display: "Loot Time", Category: "Resources", Type: optFloat, ValueSet: "SpeedValues", DefaultFloat: 1},
|
||||
{Name: "LootBagChance", Display: "Loot Bag Chance", Category: "Resources", Type: optFloat, ValueSet: "LootAbundanceValues", DefaultFloat: 1},
|
||||
{Name: "GlobalLootCount", Display: "Global Loot Abundance", Category: "Resources", Type: optFloat, ValueSet: "LootAbundanceValues", DefaultFloat: 1},
|
||||
{Name: "FoodLootCount", Display: "Food Abundance", Category: "Resources", Type: optFloat, ValueSet: "LootAbundanceValues", DefaultFloat: 1},
|
||||
{Name: "DrinkLootCount", Display: "Drink Abundance", Category: "Resources", Type: optFloat, ValueSet: "LootAbundanceValues", DefaultFloat: 1},
|
||||
{Name: "MedicalLootCount", Display: "Medical Abundance", Category: "Resources", Type: optFloat, ValueSet: "LootAbundanceValues", DefaultFloat: 1},
|
||||
{Name: "AmmoLootCount", Display: "Ammo Abundance", Category: "Resources", Type: optFloat, ValueSet: "LootAbundanceValues", DefaultFloat: 1}, // AmmoLootCount=81? see note
|
||||
{Name: "ResourceLootCount", Display: "Resource Abundance", Category: "Resources", Type: optFloat, ValueSet: "LootAbundanceValues", DefaultFloat: 1},
|
||||
{Name: "ArmorLootCount", Display: "Armor Abundance", Category: "Resources", Type: optFloat, ValueSet: "LootAbundanceValues", DefaultFloat: 1},
|
||||
{Name: "MeleeLootCount", Display: "Melee Abundance", Category: "Resources", Type: optFloat, ValueSet: "LootAbundanceValues", DefaultFloat: 1},
|
||||
{Name: "RangedLootCount", Display: "Ranged Abundance", Category: "Resources", Type: optFloat, ValueSet: "LootAbundanceValues", DefaultFloat: 1},
|
||||
{Name: "DukesLootCount", Display: "Dukes Abundance", Category: "Resources", Type: optFloat, ValueSet: "LootAbundanceValues", DefaultFloat: 1},
|
||||
{Name: "CraftingMagazinesLootCount", Display: "Magazines Abundance", Category: "Resources", Type: optFloat, ValueSet: "LootAbundanceValues", DefaultFloat: 1},
|
||||
{Name: "TreasureMapChance", Display: "Treasure Map Chance", Category: "Resources", Type: optFloat, ValueSet: "PlayerSpeedValues", DefaultFloat: 1},
|
||||
{Name: "MiningOutput", Display: "Mining Output", Category: "Resources", Type: optFloat, ValueSet: "LootAbundanceValues", DefaultFloat: 1},
|
||||
{Name: "CropOutput", Display: "Crop Output", Category: "Resources", Type: optFloat, ValueSet: "LootAbundanceValues", DefaultFloat: 1},
|
||||
{Name: "SeedDropOutput", Display: "Seed Drop Output", Category: "Resources", Type: optFloat, ValueSet: "LootAbundanceValues", DefaultFloat: 1},
|
||||
{Name: "HarvestingOutput", Display: "Harvesting Output", Category: "Resources", Type: optFloat, ValueSet: "LootAbundanceValues", DefaultFloat: 1},
|
||||
{Name: "CropGrowthSpeed", Display: "Crop Growth", Category: "Resources", Type: optFloat, ValueSet: "CropGrowthSpeed", DefaultFloat: 1},
|
||||
|
||||
// ---- Crafting ----
|
||||
{Name: "CraftingProgression", Display: "Crafting Progression", Category: "Crafting", Type: optBool, ValueSet: "YesNo", DefaultBool: true},
|
||||
{Name: "CraftingMaxTier", Display: "Crafting Max Tier", Category: "Crafting", Type: optInt, ValueSet: "ItemTierOptions", DefaultInt: 6},
|
||||
{Name: "PointsPerMagazine", Display: "Magazine Points", Category: "Crafting", Type: optInt, ValueSet: "PointsPer", DefaultInt: 1},
|
||||
{Name: "BackpackCrafting", Display: "Backpack Crafting", Category: "Crafting", Type: optInt, ValueSet: "BackpackCrafting", DefaultInt: 1},
|
||||
{Name: "WorkstationCrafting", Display: "Workstation Crafting", Category: "Crafting", Type: optBool, ValueSet: "YesNo", DefaultBool: true}, // WorkstationCrafting=100? see note
|
||||
{Name: "SmeltingType", Display: "Smelter Type", Category: "Crafting", Type: optBool, ValueSet: "SmeltingType", DefaultBool: false},
|
||||
{Name: "CraftingTime", Display: "Crafting Time", Category: "Crafting", Type: optFloat, ValueSet: "SpeedValues", DefaultFloat: 1},
|
||||
{Name: "CraftingInput", Display: "Crafting Input", Category: "Crafting", Type: optFloat, ValueSet: "SpeedValues", DefaultFloat: 1},
|
||||
{Name: "CraftingOutput", Display: "Crafting Output", Category: "Crafting", Type: optFloat, ValueSet: "StaminaRegen", DefaultFloat: 1}, // CraftingOutput=104? see note
|
||||
{Name: "ScrappingOutput", Display: "Scrapping Output", Category: "Crafting", Type: optFloat, ValueSet: "SpeedValues", DefaultFloat: 1},
|
||||
{Name: "DewCollectorTime", Display: "Dew Collector Time", Category: "Crafting", Type: optFloat, ValueSet: "SpeedValues", DefaultFloat: 1},
|
||||
{Name: "DewCollectorOutput", Display: "Dew Collector Output", Category: "Crafting", Type: optFloat, ValueSet: "CollectorOutput", DefaultFloat: 1},
|
||||
{Name: "DewCollectorInput", Display: "Dew Collector Input", Category: "Crafting", Type: optFloat, ValueSet: "DewCollectorInput", DefaultFloat: 1},
|
||||
{Name: "ApiaryTime", Display: "Apiary Time", Category: "Crafting", Type: optFloat, ValueSet: "SpeedValues", DefaultFloat: 1},
|
||||
{Name: "ApiaryOutput", Display: "Apiary Output", Category: "Crafting", Type: optFloat, ValueSet: "CollectorOutput", DefaultFloat: 1},
|
||||
{Name: "ApiaryInput", Display: "Apiary Input", Category: "Crafting", Type: optFloat, ValueSet: "ApiaryInput", DefaultFloat: 1},
|
||||
{Name: "ItemDegradation", Display: "Item Degradation", Category: "Crafting", Type: optFloat, ValueSet: "DisabledLowDefaultHigh", DefaultFloat: 1},
|
||||
{Name: "RepairTypes", Display: "Item Repair Types", Category: "Crafting", Type: optInt, ValueSet: "RepairTypes", DefaultInt: 3},
|
||||
{Name: "MaxDegradationAmount", Display: "Max Degrade Amount", Category: "Crafting", Type: optFloat, ValueSet: "MaxDegradationAmounts", DefaultFloat: 0},
|
||||
|
||||
// ---- Traders ----
|
||||
{Name: "TradersEnabled", Display: "Trading Enabled", Category: "Traders", Type: optBool, ValueSet: "YesNo", DefaultBool: true},
|
||||
{Name: "VendingEnabled", Display: "Vending Machines Enabled", Category: "Traders", Type: optBool, ValueSet: "YesNo", DefaultBool: true},
|
||||
{Name: "TraderHours", Display: "Trader Hours", Category: "Traders", Type: optInt, ValueSet: "TraderHourPresets", DefaultInt: 0},
|
||||
{Name: "TraderProtection", Display: "Trader Protection", Category: "Traders", Type: optInt, ValueSet: "TraderArea", DefaultInt: 0},
|
||||
{Name: "TraderDialog", Display: "Trading Dialog", Category: "Traders", Type: optBool, ValueSet: "YesNo", DefaultBool: true},
|
||||
{Name: "GlobalTSModifier", Display: "Global TraderStage", Category: "Traders", Type: optFloat, ValueSet: "LowDefaultHigh", DefaultFloat: 1}, // GlobalTSModifier=70? see note
|
||||
{Name: "TraderMaxTier", Display: "Trader Max Tier", Category: "Traders", Type: optInt, ValueSet: "ItemTierOptions", DefaultInt: 6},
|
||||
{Name: "TraderItemAbundance", Display: "Trader Item Abundance", Category: "Traders", Type: optFloat, ValueSet: "LowDefaultHigh", DefaultFloat: 1},
|
||||
{Name: "VendingItemAbundance", Display: "Vending Item Abundance", Category: "Traders", Type: optFloat, ValueSet: "LowDefaultHigh", DefaultFloat: 1},
|
||||
{Name: "TraderResetInterval", Display: "Trader Reset Interval", Category: "Traders", Type: optInt, ValueSet: "TraderResetInterval", DefaultInt: -1},
|
||||
{Name: "VendingResetInterval", Display: "Vending Reset Interval", Category: "Traders", Type: optInt, ValueSet: "TraderResetInterval", DefaultInt: -1},
|
||||
{Name: "TraderSellPrices", Display: "Traders Sell Price", Category: "Traders", Type: optFloat, ValueSet: "BarterValues", DefaultFloat: 1},
|
||||
{Name: "TraderBuyPrices", Display: "Traders Buy Price", Category: "Traders", Type: optFloat, ValueSet: "BarterValues", DefaultFloat: 1},
|
||||
{Name: "TraderBuyLimit", Display: "Trader Buy Limit", Category: "Traders", Type: optInt, ValueSet: "StarterSkillPoints", DefaultInt: 3},
|
||||
|
||||
// ---- Tasks ----
|
||||
{Name: "ChallengesEnabled", Display: "Challenges Enabled", Category: "Tasks", Type: optBool, ValueSet: "YesNo", DefaultBool: true},
|
||||
{Name: "IntroChallengesEnabled", Display: "Intro Challenges Enabled", Category: "Tasks", Type: optBool, ValueSet: "YesNo", DefaultBool: true},
|
||||
{Name: "QuestsEnabled", Display: "Quests Enabled", Category: "Tasks", Type: optBool, ValueSet: "YesNo", DefaultBool: true},
|
||||
{Name: "IntroQuestEnabled", Display: "Intro Quest Enabled", Category: "Tasks", Type: optBool, ValueSet: "YesNo", DefaultBool: true},
|
||||
{Name: "TraderToTraderQuestsEnabled", Display: "Trader to Trader Quests", Category: "Tasks", Type: optBool, ValueSet: "YesNo", DefaultBool: true},
|
||||
{Name: "BuriedQuestsEnabled", Display: "Buried Quests Enabled", Category: "Tasks", Type: optBool, ValueSet: "YesNo", DefaultBool: true},
|
||||
{Name: "POIQuestsEnabled", Display: "POI Quests Enabled", Category: "Tasks", Type: optBool, ValueSet: "YesNo", DefaultBool: true},
|
||||
{Name: "QuestsPerTier", Display: "Quests per Tier", Category: "Tasks", Type: optInt, ValueSet: "QuestPerTier", DefaultInt: 10},
|
||||
{Name: "QuestProgressionDailyLimit", Display: "Quests per Day", Category: "Tasks", Type: optInt, ValueSet: "QuestPerDay", DefaultInt: 4},
|
||||
{Name: "StarterSkillPoints", Display: "Starter Skill Points", Category: "Tasks", Type: optInt, ValueSet: "StarterSkillPoints", DefaultInt: 4},
|
||||
|
||||
// ---- Misc ----
|
||||
{Name: "VehicleFuelUsage", Display: "Vehicle Fuel Usage", Category: "Misc", Type: optFloat, ValueSet: "DamageValues", DefaultFloat: 1},
|
||||
{Name: "VehicleEntityDamage", Display: "Vehicle Entity Damage", Category: "Misc", Type: optFloat, ValueSet: "DamageValues", DefaultFloat: 1},
|
||||
{Name: "VehicleBlockDamage", Display: "Vehicle Block Damage", Category: "Misc", Type: optFloat, ValueSet: "DamageValues", DefaultFloat: 1},
|
||||
{Name: "VehicleSelfDamage", Display: "Vehicle Self Damage", Category: "Misc", Type: optFloat, ValueSet: "DamageValues", DefaultFloat: 1},
|
||||
{Name: "ElectricalOutput", Display: "Electrical Output", Category: "Misc", Type: optFloat, ValueSet: "StaminaRegen", DefaultFloat: 1},
|
||||
{Name: "SillyCelebrate", Display: "Celebrate Kills", Category: "Misc", Type: optInt, ValueSet: "Celebrate", DefaultInt: 0},
|
||||
{Name: "SillyBigHeads", Display: "Big Heads", Category: "Misc", Type: optBool, ValueSet: "YesNo", DefaultBool: false},
|
||||
{Name: "SillyTinyZombies", Display: "Tiny Zombies", Category: "Misc", Type: optBool, ValueSet: "YesNo", DefaultBool: false},
|
||||
{Name: "SillyLowGravity", Display: "Gravity", Category: "Misc", Type: optFloat, ValueSet: "Gravity", DefaultFloat: 1},
|
||||
{Name: "SillySounds", Display: "Silly Sounds", Category: "Misc", Type: optBool, ValueSet: "YesNo", DefaultBool: false},
|
||||
{Name: "SillyBlackandWhite", Display: "Black and White", Category: "Misc", Type: optBool, ValueSet: "YesNo", DefaultBool: false},
|
||||
}
|
||||
@@ -0,0 +1,363 @@
|
||||
// Package sandbox handles the 7 Days to Die V3.0 "SandboxCode" — the single
|
||||
// encoded serverconfig.xml property that carries all 150 in-game Sandbox Options
|
||||
// introduced in V3.0 "Dead Hot Summer".
|
||||
//
|
||||
// V3.0 collapses ~29 legacy gameplay serverconfig properties (GameDifficulty,
|
||||
// BloodMoonFrequency, LootAbundance, …) into one deterministic, OFFLINE-encoded
|
||||
// string. The encode/decode algorithm ships in the game binary; this package is
|
||||
// a faithful Go port of it, cracked from the decompiled 3.0 Assembly-CSharp
|
||||
// (SandboxOptions.SandboxOptionManager + SandboxOptionPreset, buildid 23705258).
|
||||
//
|
||||
// ## The encoding (verified against the game's own default code)
|
||||
//
|
||||
// code[0] = version char ('A', SandboxOptionManager.currentVersion)
|
||||
// then 3-char triples, one per option that differs from its default:
|
||||
// triple[0..1] = the SandboxOptions enum int, base-26: c0*26 + c1 (A=0)
|
||||
// triple[2] = the selected value INDEX in that option's value-set (A=0)
|
||||
//
|
||||
// Decode resets every option to its default, then applies each triple. Encode is
|
||||
// the inverse: emit a triple for every option whose chosen index ≠ default index
|
||||
// (options omitted from the code decode to default — identical result, shorter
|
||||
// code, matching how the game emits difficulty presets like AAAJABJACJADJARFBNC).
|
||||
//
|
||||
// Round-trip + the game's example codes are checked in sandbox_test.go.
|
||||
package sandbox
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"math"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// codeVersion is SandboxOptionManager.currentVersion. A code must start with it.
|
||||
const codeVersion = 'A'
|
||||
|
||||
// ErrCodecUnavailable is retained for API compatibility with the day-one
|
||||
// passthrough scaffold. The real codec no longer returns it.
|
||||
var ErrCodecUnavailable = errors.New("sandbox: per-option SandboxCode codec unavailable")
|
||||
|
||||
// optByEnum / optByName index the options table for O(1) lookup. defaultIdx
|
||||
// holds each option's default value's index in its value-set.
|
||||
var (
|
||||
optByEnum = map[int]*option{}
|
||||
optByName = map[string]*option{}
|
||||
defaultIdx = map[string]int{}
|
||||
)
|
||||
|
||||
func init() {
|
||||
for i := range options {
|
||||
o := &options[i]
|
||||
e, ok := enumByName[o.Name]
|
||||
if !ok {
|
||||
panic("sandbox: option " + o.Name + " has no SandboxOptions enum entry")
|
||||
}
|
||||
o.enum = e
|
||||
vs, ok := valueSets[o.ValueSet]
|
||||
if !ok {
|
||||
panic("sandbox: option " + o.Name + " references unknown value-set " + o.ValueSet)
|
||||
}
|
||||
di, ok := indexOfDefault(*o, vs)
|
||||
if !ok {
|
||||
panic(fmt.Sprintf("sandbox: option %s default not present in value-set %s", o.Name, o.ValueSet))
|
||||
}
|
||||
o.set = vs
|
||||
defaultIdx[o.Name] = di
|
||||
optByEnum[e] = o
|
||||
optByName[o.Name] = o
|
||||
}
|
||||
}
|
||||
|
||||
// indexOfDefault finds the index of an option's default value in its value-set.
|
||||
func indexOfDefault(o option, vs valueSet) (int, bool) {
|
||||
switch o.Type {
|
||||
case optFloat:
|
||||
for i, f := range vs.Floats {
|
||||
if floatEq(f, o.DefaultFloat) {
|
||||
return i, true
|
||||
}
|
||||
}
|
||||
case optInt:
|
||||
for i, n := range vs.Ints {
|
||||
if n == o.DefaultInt {
|
||||
return i, true
|
||||
}
|
||||
}
|
||||
case optBool:
|
||||
for i, b := range vs.Bools {
|
||||
if b == o.DefaultBool {
|
||||
return i, true
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0, false
|
||||
}
|
||||
|
||||
func floatEq(a, b float64) bool { return math.Abs(a-b) < 1e-6 }
|
||||
|
||||
// Codec converts between a SandboxCode string and a per-option value map.
|
||||
// The map is keyed by the SandboxOptions member name (e.g. "BloodMoonFrequency")
|
||||
// with string values (the human value: "7", "1.5", "true"). Encode emits a code
|
||||
// for the options whose value differs from default; Decode returns the FULL set
|
||||
// of 150 options at their effective values (defaults for any omitted from code).
|
||||
type Codec interface {
|
||||
Version() string
|
||||
Encode(options map[string]string) (string, error)
|
||||
Decode(code string) (map[string]string, error)
|
||||
}
|
||||
|
||||
// V3Codec is the real 3.0 SandboxCode codec.
|
||||
type V3Codec struct{}
|
||||
|
||||
func (V3Codec) Version() string { return string(codeVersion) }
|
||||
|
||||
// Encode builds a SandboxCode from a name→value map. Unknown keys are ignored;
|
||||
// a value that doesn't match any entry in its option's value-set is an error
|
||||
// (prevents silently writing an out-of-range setting). Options absent from the
|
||||
// map, or equal to their default, are omitted from the code (decode to default).
|
||||
// Triples are emitted in ascending enum order for a stable, diffable code.
|
||||
func (V3Codec) Encode(vals map[string]string) (string, error) {
|
||||
type triple struct {
|
||||
enum, idx int
|
||||
}
|
||||
var triples []triple
|
||||
for name, raw := range vals {
|
||||
o, ok := optByName[name]
|
||||
if !ok {
|
||||
continue // unknown option key — ignore (forward-compat)
|
||||
}
|
||||
idx, err := o.indexOfValue(raw)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("sandbox: option %s: %w", name, err)
|
||||
}
|
||||
if idx == defaultIdx[name] {
|
||||
continue // equals default — omit (decodes to same value)
|
||||
}
|
||||
triples = append(triples, triple{o.enum, idx})
|
||||
}
|
||||
sort.Slice(triples, func(i, j int) bool { return triples[i].enum < triples[j].enum })
|
||||
|
||||
var b strings.Builder
|
||||
b.WriteByte(codeVersion)
|
||||
for _, t := range triples {
|
||||
s, err := indexToAlpha2(t.enum)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
c, err := indexToAlpha(t.idx)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
b.WriteString(s)
|
||||
b.WriteByte(c)
|
||||
}
|
||||
return b.String(), nil
|
||||
}
|
||||
|
||||
// Decode parses a SandboxCode into the FULL effective option set (all 150 at
|
||||
// their resolved values: code-specified or default). An empty code returns all
|
||||
// defaults. A code with a wrong version char, bad length, or unknown enum is an
|
||||
// error (unknown enums could mean a version drift we shouldn't silently accept).
|
||||
func (V3Codec) Decode(code string) (map[string]string, error) {
|
||||
out := DefaultsMap()
|
||||
if code == "" {
|
||||
return out, nil
|
||||
}
|
||||
if rune(code[0]) != codeVersion {
|
||||
return nil, fmt.Errorf("sandbox: code version %q unsupported (want %q)", string(code[0]), string(codeVersion))
|
||||
}
|
||||
body := code[1:]
|
||||
if len(body)%3 != 0 {
|
||||
return nil, fmt.Errorf("sandbox: code body length %d is not a multiple of 3", len(body))
|
||||
}
|
||||
for i := 0; i < len(body); i += 3 {
|
||||
enum, err := alpha2ToIndex(body[i : i+2])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
idx, err := alphaToIndex(body[i+2])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
o, ok := optByEnum[enum]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("sandbox: code references unknown option enum %d (game version drift?)", enum)
|
||||
}
|
||||
if !o.set.IsValidIndex(idx) {
|
||||
return nil, fmt.Errorf("sandbox: option %s index %d out of range (0..%d)", o.Name, idx, o.set.Len()-1)
|
||||
}
|
||||
out[o.Name] = o.valueAtIndex(idx)
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// indexOfValue resolves a human value string to its index in the option's set.
|
||||
func (o option) indexOfValue(raw string) (int, error) {
|
||||
raw = strings.TrimSpace(raw)
|
||||
switch o.Type {
|
||||
case optFloat:
|
||||
f, err := strconv.ParseFloat(raw, 64)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("expected a number, got %q", raw)
|
||||
}
|
||||
for i, v := range o.set.Floats {
|
||||
if floatEq(v, f) {
|
||||
return i, nil
|
||||
}
|
||||
}
|
||||
return 0, fmt.Errorf("value %s not an allowed option (set %s)", raw, o.ValueSet)
|
||||
case optInt:
|
||||
n, err := strconv.Atoi(raw)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("expected an integer, got %q", raw)
|
||||
}
|
||||
for i, v := range o.set.Ints {
|
||||
if v == n {
|
||||
return i, nil
|
||||
}
|
||||
}
|
||||
return 0, fmt.Errorf("value %d not an allowed option (set %s)", n, o.ValueSet)
|
||||
case optBool:
|
||||
b, err := parseBool(raw)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
for i, v := range o.set.Bools {
|
||||
if v == b {
|
||||
return i, nil
|
||||
}
|
||||
}
|
||||
return 0, fmt.Errorf("value %v not an allowed option (set %s)", b, o.ValueSet)
|
||||
}
|
||||
return 0, fmt.Errorf("option %s has unknown type", o.Name)
|
||||
}
|
||||
|
||||
// valueAtIndex returns the human value string for an index in the option's set.
|
||||
func (o option) valueAtIndex(i int) string {
|
||||
switch o.Type {
|
||||
case optFloat:
|
||||
return strconv.FormatFloat(o.set.Floats[i], 'f', -1, 64)
|
||||
case optInt:
|
||||
return strconv.Itoa(o.set.Ints[i])
|
||||
case optBool:
|
||||
return strconv.FormatBool(o.set.Bools[i])
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func parseBool(s string) (bool, error) {
|
||||
switch strings.ToLower(strings.TrimSpace(s)) {
|
||||
case "true", "1", "yes", "on":
|
||||
return true, nil
|
||||
case "false", "0", "no", "off":
|
||||
return false, nil
|
||||
}
|
||||
return false, fmt.Errorf("expected a boolean, got %q", s)
|
||||
}
|
||||
|
||||
// ---- alpha <-> index helpers (ports of SandboxOptionManager.*) ----
|
||||
|
||||
func indexToAlpha(index int) (byte, error) {
|
||||
if index < 0 || index > 26 {
|
||||
return 0, fmt.Errorf("sandbox: value index %d out of code range", index)
|
||||
}
|
||||
return byte(65 + index%26), nil
|
||||
}
|
||||
|
||||
func indexToAlpha2(index int) (string, error) {
|
||||
if index < 0 || index >= 676 {
|
||||
return "", fmt.Errorf("sandbox: enum index %d out of code range", index)
|
||||
}
|
||||
return string([]byte{byte(65 + index/26), byte(65 + index%26)}), nil
|
||||
}
|
||||
|
||||
func alphaToIndex(value byte) (int, error) {
|
||||
v := upper(value)
|
||||
if v < 'A' || v > 'Z' {
|
||||
return 0, errors.New("sandbox: code value char must be A–Z")
|
||||
}
|
||||
return int(v - 65), nil
|
||||
}
|
||||
|
||||
func alpha2ToIndex(value string) (int, error) {
|
||||
if len(value) != 2 {
|
||||
return 0, errors.New("sandbox: code option key must be exactly 2 letters")
|
||||
}
|
||||
a, b := upper(value[0]), upper(value[1])
|
||||
if a < 'A' || a > 'Z' || b < 'A' || b > 'Z' {
|
||||
return 0, errors.New("sandbox: code option key must contain only A–Z")
|
||||
}
|
||||
return int(a-65)*26 + int(b-65), nil
|
||||
}
|
||||
|
||||
func upper(b byte) byte {
|
||||
if b >= 'a' && b <= 'z' {
|
||||
return b - 32
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
// DefaultsMap returns every option at its default value (name → value string).
|
||||
func DefaultsMap() map[string]string {
|
||||
out := make(map[string]string, len(options))
|
||||
for i := range options {
|
||||
o := &options[i]
|
||||
out[o.Name] = o.valueAtIndex(defaultIdx[o.Name])
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// Active is the codec the panel uses. Now the real V3 codec.
|
||||
var Active Codec = V3Codec{}
|
||||
|
||||
// Preset is an official (or operator-saved) named SandboxCode.
|
||||
type Preset struct {
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Description string `json:"description"`
|
||||
Code string `json:"code"`
|
||||
}
|
||||
|
||||
// Presets are the V3.0 official presets surfaced in the UI. The default
|
||||
// difficulty (Adventurer) code is shipped from the game's serverconfig comment;
|
||||
// the named-preset codes are captured from the in-game Sandbox Options window
|
||||
// (see scripts/capture-preset-codes). An empty Code == the game's built-in
|
||||
// default sandbox.
|
||||
var Presets = []Preset{
|
||||
{ID: "default", Name: "Default (Adventurer)", Description: "The game's built-in default sandbox.", Code: "AAAJABJACJADJARFBNC"},
|
||||
{ID: "undead_matinee", Name: "Undead Matinee", Description: "Official V3.0 preset.", Code: ""},
|
||||
{ID: "madmoles_mayhem", Name: "Madmole's Mayhem", Description: "Official V3.0 preset.", Code: ""},
|
||||
{ID: "almost_creative", Name: "Almost Creative Mode", Description: "Official V3.0 preset.", Code: ""},
|
||||
{ID: "bite_club", Name: "Bite Club", Description: "Official V3.0 preset.", Code: ""},
|
||||
{ID: "legacy_survival", Name: "Legacy Survival", Description: "Official V3.0 preset.", Code: ""},
|
||||
}
|
||||
|
||||
// PresetByID returns the named preset, or ok=false if unknown.
|
||||
func PresetByID(id string) (Preset, bool) {
|
||||
id = strings.TrimSpace(strings.ToLower(id))
|
||||
for _, p := range Presets {
|
||||
if strings.ToLower(p.ID) == id {
|
||||
return p, true
|
||||
}
|
||||
}
|
||||
return Preset{}, false
|
||||
}
|
||||
|
||||
// Validate does a cheap structural sanity check on a SandboxCode before it's
|
||||
// written into serverconfig.xml. An empty code is valid (== default sandbox).
|
||||
// A non-empty code must decode cleanly (right version, length, known enums,
|
||||
// in-range indices) and contain no XML-attribute-breaking characters.
|
||||
func Validate(code string) error {
|
||||
if code == "" {
|
||||
return nil
|
||||
}
|
||||
if strings.ContainsAny(code, `"<>&`) {
|
||||
return errors.New("sandbox: code contains characters not valid in an XML attribute value")
|
||||
}
|
||||
if _, err := (V3Codec{}).Decode(code); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,154 @@
|
||||
package sandbox
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
// TestInitConsistency: every option resolves an enum + a default index, and no
|
||||
// two options share an enum (the 2-char code must be unique per option).
|
||||
func TestInitConsistency(t *testing.T) {
|
||||
if len(options) != 150 {
|
||||
t.Fatalf("expected 150 sandbox options, got %d", len(options))
|
||||
}
|
||||
seen := map[int]string{}
|
||||
for i := range options {
|
||||
o := &options[i]
|
||||
if o.enum == 0 && o.Name != "RangedDamage" {
|
||||
t.Errorf("%s: enum unresolved (0)", o.Name)
|
||||
}
|
||||
if prev, ok := seen[o.enum]; ok {
|
||||
t.Errorf("enum %d shared by %s and %s", o.enum, prev, o.Name)
|
||||
}
|
||||
seen[o.enum] = o.Name
|
||||
if _, ok := defaultIdx[o.Name]; !ok {
|
||||
t.Errorf("%s: no default index", o.Name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestGameDefaultCode decodes the game's own default difficulty (Adventurer)
|
||||
// code from serverconfig.xml and checks the documented meaning, then re-encodes
|
||||
// and confirms it round-trips byte-for-byte.
|
||||
func TestGameDefaultCode(t *testing.T) {
|
||||
const code = "AAAJABJACJADJARFBNC" // shipped in 3.0 serverconfig.xml comment
|
||||
c := V3Codec{}
|
||||
vals, err := c.Decode(code)
|
||||
if err != nil {
|
||||
t.Fatalf("decode: %v", err)
|
||||
}
|
||||
// Triples: AA J / AB J / AC J / AD J / AR F / BN C
|
||||
// AA(0)=RangedDamage idx J=9 -> DamageValues[9]=1.5
|
||||
// AB(1)=MeleeDamage idx 9 -> 1.5
|
||||
// AC(2)=BlockDamage idx 9 -> 1.5
|
||||
// AD(3)=TerrainDamage idx 9 -> 1.5
|
||||
// AR(17)=IncomingDamage idx F=5 -> DamageValues[5]=0.75
|
||||
// BN(39)=AISmellMode idx C=2 -> AISmellMode set Ints[2]=2
|
||||
checks := map[string]string{
|
||||
"RangedDamage": "1.5",
|
||||
"MeleeDamage": "1.5",
|
||||
"BlockDamage": "1.5",
|
||||
"TerrainDamage": "1.5",
|
||||
"IncomingDamage": "0.75",
|
||||
"AISmellMode": "2",
|
||||
}
|
||||
for name, want := range checks {
|
||||
if got := vals[name]; got != want {
|
||||
t.Errorf("%s = %q, want %q", name, got, want)
|
||||
}
|
||||
}
|
||||
// Re-encode: only the 6 non-default options should appear, in enum order →
|
||||
// must reproduce the original code exactly.
|
||||
round, err := c.Encode(vals)
|
||||
if err != nil {
|
||||
t.Fatalf("encode: %v", err)
|
||||
}
|
||||
if round != code {
|
||||
t.Errorf("round-trip mismatch:\n have %q\n want %q", round, code)
|
||||
}
|
||||
}
|
||||
|
||||
// TestEmptyAndDefaultsEncodeEmpty: the all-defaults map encodes to just the
|
||||
// version char (no triples), and an empty code decodes to all defaults.
|
||||
func TestEmptyAndDefaultsEncodeEmpty(t *testing.T) {
|
||||
c := V3Codec{}
|
||||
def := DefaultsMap()
|
||||
code, err := c.Encode(def)
|
||||
if err != nil {
|
||||
t.Fatalf("encode defaults: %v", err)
|
||||
}
|
||||
if code != "A" {
|
||||
t.Errorf("defaults should encode to bare version char %q, got %q", "A", code)
|
||||
}
|
||||
vals, err := c.Decode("")
|
||||
if err != nil {
|
||||
t.Fatalf("decode empty: %v", err)
|
||||
}
|
||||
if len(vals) != 150 {
|
||||
t.Errorf("empty decode should yield 150 defaults, got %d", len(vals))
|
||||
}
|
||||
for name, v := range vals {
|
||||
if def[name] != v {
|
||||
t.Errorf("%s: empty-decode %q != defaults %q", name, v, def[name])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestRoundTripNonDefault sets a spread of options across categories/types and
|
||||
// confirms encode→decode preserves them.
|
||||
func TestRoundTripNonDefault(t *testing.T) {
|
||||
c := V3Codec{}
|
||||
in := DefaultsMap()
|
||||
in["BloodMoonFrequency"] = "14" // int set, non-default
|
||||
in["XPMultiplier"] = "2" // float set
|
||||
in["AllowZombieDigging"] = "false"
|
||||
in["TraderResetInterval"] = "7"
|
||||
in["SillyBigHeads"] = "true"
|
||||
code, err := c.Encode(in)
|
||||
if err != nil {
|
||||
t.Fatalf("encode: %v", err)
|
||||
}
|
||||
out, err := c.Decode(code)
|
||||
if err != nil {
|
||||
t.Fatalf("decode: %v", err)
|
||||
}
|
||||
for _, k := range []string{"BloodMoonFrequency", "XPMultiplier", "AllowZombieDigging", "TraderResetInterval", "SillyBigHeads"} {
|
||||
if out[k] != in[k] {
|
||||
t.Errorf("%s: round-trip %q != %q", k, out[k], in[k])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestAlphaHelpers spot-checks the index<->alpha conversions against the C#.
|
||||
func TestAlphaHelpers(t *testing.T) {
|
||||
if v, _ := alpha2ToIndex("BN"); v != 39 {
|
||||
t.Errorf("BN -> %d, want 39", v)
|
||||
}
|
||||
if v, _ := alpha2ToIndex("AA"); v != 0 {
|
||||
t.Errorf("AA -> %d, want 0", v)
|
||||
}
|
||||
if s, _ := indexToAlpha2(39); s != "BN" {
|
||||
t.Errorf("39 -> %q, want BN", s)
|
||||
}
|
||||
if v, _ := alphaToIndex('J'); v != 9 {
|
||||
t.Errorf("J -> %d, want 9", v)
|
||||
}
|
||||
if b, _ := indexToAlpha(9); b != 'J' {
|
||||
t.Errorf("9 -> %c, want J", b)
|
||||
}
|
||||
}
|
||||
|
||||
// TestValidate rejects malformed codes and accepts good ones.
|
||||
func TestValidate(t *testing.T) {
|
||||
if err := Validate(""); err != nil {
|
||||
t.Errorf("empty should be valid: %v", err)
|
||||
}
|
||||
if err := Validate("AAAJABJACJADJARFBNC"); err != nil {
|
||||
t.Errorf("game default should be valid: %v", err)
|
||||
}
|
||||
if err := Validate("ZZZ"); err == nil {
|
||||
t.Error("wrong version char should fail")
|
||||
}
|
||||
if err := Validate("AAB"); err == nil {
|
||||
t.Error("non-multiple-of-3 body should fail")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
package sandbox
|
||||
|
||||
import (
|
||||
"sort"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// SchemaOption is one sandbox option as the UI needs it: identity, type, the
|
||||
// selectable values (with display labels), and the default value.
|
||||
type SchemaOption struct {
|
||||
Name string `json:"name"` // SandboxOptions enum member (stable key + config_values suffix)
|
||||
Display string `json:"display"` // human label
|
||||
Category string `json:"category"` // raw category key (General/Entities/...)
|
||||
Type string `json:"type"` // "float" | "int" | "bool"
|
||||
Values []string `json:"values"` // selectable values in index order (the code stores the index)
|
||||
Labels []string `json:"labels"` // optional UI labels parallel to Values (may be empty)
|
||||
Default string `json:"default"` // default value (one of Values)
|
||||
}
|
||||
|
||||
// SchemaCategory groups options under a UI-friendly title.
|
||||
type SchemaCategory struct {
|
||||
Key string `json:"key"` // raw category key
|
||||
Title string `json:"title"` // UI title
|
||||
Icon string `json:"icon"` // emoji for the section
|
||||
Options []SchemaOption `json:"options"` // in registration order
|
||||
}
|
||||
|
||||
// categoryMeta maps the decompile's raw category keys to UI titles + icons,
|
||||
// in display order.
|
||||
var categoryMeta = []struct{ Key, Title, Icon string }{
|
||||
{"General", "Player", "🧍"},
|
||||
{"Entities", "Entities & Zombies", "🧟"},
|
||||
{"World", "World & Blood Moon", "🌍"},
|
||||
{"Resources", "Loot & Resources", "💰"},
|
||||
{"Crafting", "Crafting", "🔨"},
|
||||
{"Traders", "Traders", "🪙"},
|
||||
{"Tasks", "Quests & Tasks", "📋"},
|
||||
{"Misc", "Miscellaneous", "🎛️"},
|
||||
}
|
||||
|
||||
// Schema returns the full 150-option sandbox schema grouped into the 8 UI
|
||||
// categories, in display order. The UI renders per-option controls from this and
|
||||
// sends back a name→value map for Encode.
|
||||
func Schema() []SchemaCategory {
|
||||
byCat := map[string][]SchemaOption{}
|
||||
for i := range options {
|
||||
o := &options[i]
|
||||
so := SchemaOption{
|
||||
Name: o.Name,
|
||||
Display: o.Display,
|
||||
Category: o.Category,
|
||||
Default: o.valueAtIndex(defaultIdx[o.Name]),
|
||||
}
|
||||
switch o.Type {
|
||||
case optFloat:
|
||||
so.Type = "float"
|
||||
for _, f := range o.set.Floats {
|
||||
so.Values = append(so.Values, strconv.FormatFloat(f, 'f', -1, 64))
|
||||
}
|
||||
case optInt:
|
||||
so.Type = "int"
|
||||
for _, n := range o.set.Ints {
|
||||
so.Values = append(so.Values, strconv.Itoa(n))
|
||||
}
|
||||
case optBool:
|
||||
so.Type = "bool"
|
||||
for _, b := range o.set.Bools {
|
||||
so.Values = append(so.Values, strconv.FormatBool(b))
|
||||
}
|
||||
}
|
||||
so.Labels = append(so.Labels, o.set.Display...)
|
||||
byCat[o.Category] = append(byCat[o.Category], so)
|
||||
}
|
||||
var out []SchemaCategory
|
||||
for _, cm := range categoryMeta {
|
||||
out = append(out, SchemaCategory{Key: cm.Key, Title: cm.Title, Icon: cm.Icon, Options: byCat[cm.Key]})
|
||||
}
|
||||
// Any category not in categoryMeta (forward-compat) appended at the end.
|
||||
known := map[string]bool{}
|
||||
for _, cm := range categoryMeta {
|
||||
known[cm.Key] = true
|
||||
}
|
||||
var extra []string
|
||||
for k := range byCat {
|
||||
if !known[k] {
|
||||
extra = append(extra, k)
|
||||
}
|
||||
}
|
||||
sort.Strings(extra)
|
||||
for _, k := range extra {
|
||||
out = append(out, SchemaCategory{Key: k, Title: k, Icon: "⚙️", Options: byCat[k]})
|
||||
}
|
||||
return out
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package sandbox
|
||||
import "testing"
|
||||
func TestSchemaShape(t *testing.T){
|
||||
s := Schema()
|
||||
if len(s) < 8 { t.Fatalf("want >=8 categories, got %d", len(s)) }
|
||||
n := 0
|
||||
for _, c := range s { n += len(c.Options); for _, o := range c.Options {
|
||||
if len(o.Values)==0 { t.Errorf("%s has no values", o.Name) }
|
||||
found:=false; for _,v:=range o.Values { if v==o.Default {found=true} }
|
||||
if !found { t.Errorf("%s default %q not in values %v", o.Name, o.Default, o.Values) }
|
||||
}}
|
||||
if n != 150 { t.Fatalf("want 150 options across categories, got %d", n) }
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
// Code generated from 7DTD V3.0 Assembly-CSharp SandboxOptionManager.SetupOptions
|
||||
// (buildid 23705258, experimental 2026-06-15). DO NOT hand-edit casually — these
|
||||
// tables ARE the SandboxCode encoding. A wrong value silently corrupts a
|
||||
// server's sandbox settings. Re-derive from the decompiled SetupOptions if the
|
||||
// encoding changes in a future 3.0 build (see CrackedFrom below).
|
||||
//
|
||||
// CrackedFrom: rb-decompile/3.0/src/SandboxOptions/SandboxOptionManager.cs
|
||||
package sandbox
|
||||
|
||||
// valueKind distinguishes how a value-set's entries map to a serverconfig-style
|
||||
// scalar. The SandboxCode itself only stores the INDEX into the set; these are
|
||||
// for the panel UI / display + for emitting a human value.
|
||||
type valueKind int
|
||||
|
||||
const (
|
||||
kindFloat valueKind = iota // FloatValues
|
||||
kindInt // IntValues
|
||||
kindBool // BoolValues
|
||||
)
|
||||
|
||||
// valueSet mirrors a SandboxOptionValueSet{Float,Int,Bool}. Exactly one of
|
||||
// Floats/Ints/Bools is populated (per Kind). The index stored in a SandboxCode
|
||||
// is a position into that slice. Display* are the localization-key labels (kept
|
||||
// for the UI; not needed for encode/decode).
|
||||
type valueSet struct {
|
||||
Kind valueKind
|
||||
Floats []float64
|
||||
Ints []int
|
||||
Bools []bool
|
||||
Display []string // optional UI labels, parallel-ish to values (may be shorter)
|
||||
}
|
||||
|
||||
// Len returns the number of selectable indices in the set.
|
||||
func (v valueSet) Len() int {
|
||||
switch v.Kind {
|
||||
case kindFloat:
|
||||
return len(v.Floats)
|
||||
case kindInt:
|
||||
return len(v.Ints)
|
||||
case kindBool:
|
||||
return len(v.Bools)
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// IsValidIndex mirrors SandboxOptionValueSet.IsValidIndex.
|
||||
func (v valueSet) IsValidIndex(i int) bool { return i >= 0 && i < v.Len() }
|
||||
|
||||
// valueSets is the ValueSets dictionary from SetupOptions(). Keyed by the same
|
||||
// string the game uses so the option table can reference them by name.
|
||||
var valueSets = map[string]valueSet{
|
||||
"DamageValues": {Kind: kindFloat, Floats: []float64{0, 0.25, 0.35, 0.5, 0.65, 0.75, 0.85, 1, 1.25, 1.5, 2, 2.5, 3}, Display: []string{"none"}},
|
||||
"DamageValuesNoNone": {Kind: kindFloat, Floats: []float64{0.25, 0.35, 0.5, 0.65, 0.75, 0.85, 1, 1.25, 1.5, 2, 2.5, 3}},
|
||||
"PlayerSpeedValues": {Kind: kindFloat, Floats: []float64{0, 0.25, 0.5, 0.6, 0.7, 0.8, 0.9, 1, 1.1, 1.2, 1.3, 1.4, 1.5, 2, 3}, Display: []string{"none"}},
|
||||
"PlayerSpeedValuesWithNone": {Kind: kindFloat, Floats: []float64{0.25, 0.5, 0.6, 0.7, 0.8, 0.9, 1, 1.1, 1.2, 1.3, 1.4, 1.5, 2, 3}},
|
||||
"SpeedValues": {Kind: kindFloat, Floats: []float64{0, 0.25, 0.5, 0.75, 1, 1.25, 1.5, 2, 3}, Display: []string{"none"}},
|
||||
"StaminaUsage": {Kind: kindFloat, Floats: []float64{0, 0.25, 0.5, 0.75, 1, 1.25, 1.5, 1.75, 2}, Display: []string{"none"}},
|
||||
"LootAbundanceValues": {Kind: kindFloat, Floats: []float64{0, 0.25, 0.35, 0.5, 0.65, 0.75, 0.85, 1, 1.25, 1.5, 2, 3, 4, 5}, Display: []string{"none"}},
|
||||
"ZombieRageChance": {Kind: kindFloat, Floats: []float64{0, 0.15, 0.3, 0.35, 0.4, 0.5, 0.6, 0.75, 0.9, 1}, Display: []string{"none"}},
|
||||
"ZombieSpeeds": {Kind: kindInt, Ints: []int{0, 1, 2, 3, 4}, Display: []string{"goZMWalk", "goZMJog", "goZMRun", "goZMSprint", "goZMNightmare"}},
|
||||
"AISmellMode": {Kind: kindInt, Ints: []int{0, 1, 2, 3, 4, 5}, Display: []string{"none", "goZMWalk", "goZMJog", "goZMRun", "goZMSprint", "goZMNightmare"}},
|
||||
"JumpStrength": {Kind: kindFloat, Floats: []float64{0, 0.5, 1, 1.25, 1.5, 2, 3}, Display: []string{"none"}},
|
||||
"StaminaRegen": {Kind: kindFloat, Floats: []float64{0.25, 0.5, 0.75, 1, 1.25, 1.5, 2, 3}},
|
||||
"XPGain": {Kind: kindFloat, Floats: []float64{0, 0.25, 0.5, 0.75, 1, 1.25, 1.5, 1.75, 2, 3, 5}, Display: []string{"none"}},
|
||||
"JarRefund": {Kind: kindFloat, Floats: []float64{0, 0.05, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1}, Display: []string{"none"}},
|
||||
"BarterValues": {Kind: kindFloat, Floats: []float64{0, 0.25, 0.5, 0.75, 1, 1.25, 1.5, 1.75, 2, 3, 4}, Display: []string{"none"}},
|
||||
"DisabledLowDefaultHigh": {Kind: kindFloat, Floats: []float64{0, 0.25, 0.5, 1, 1.5, 2}, Display: []string{"goDisabled", "goVeryLow", "goLow", "goDefault", "goHigh", "goVeryHigh"}},
|
||||
"LowDefaultHigh": {Kind: kindFloat, Floats: []float64{0.25, 0.5, 1, 1.5, 2}, Display: []string{"goVeryLow", "goLow", "goDefault", "goHigh", "goVeryHigh"}},
|
||||
"Encumbrance": {Kind: kindFloat, Floats: []float64{10, 1.35, 1, 0.7, 0.35, 0}, Display: []string{"goDisabled", "goLow", "goDefault", "goHigh", "goVeryHigh", "xuiOptionsVideoTexQualityFull"}},
|
||||
"SkillGainRate": {Kind: kindInt, Ints: []int{1, 2, 3, 4, 5}},
|
||||
"PointsPer": {Kind: kindInt, Ints: []int{0, 1, 2, 3, 4, 5, 6, 7}, Display: []string{"none"}},
|
||||
"StarterSkillPoints": {Kind: kindInt, Ints: []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, Display: []string{"none"}},
|
||||
"BloodMoonFrequency": {Kind: kindInt, Ints: []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 14, 20, 30}, Display: []string{"goDisabled", "goDay"}},
|
||||
"BloodMoonRange": {Kind: kindInt, Ints: []int{0, 1, 2, 3, 4, 7, 10, 14, 20}, Display: []string{"goDays", "goDay"}},
|
||||
"BloodMoonWarning": {Kind: kindInt, Ints: []int{0, 1, 2}, Display: []string{"goDisabled", "goMorning", "goEvening"}},
|
||||
"BloodMoonCount": {Kind: kindInt, Ints: []int{4, 6, 8, 10, 12, 16, 24, 32, 64}},
|
||||
"AirDrops": {Kind: kindInt, Ints: []int{0, 1, 2, 3, 4, 5, 6}, Display: []string{"goDisabled", "goAirDropValue"}},
|
||||
"AirDropRandomTime": {Kind: kindInt, Ints: []int{0, 1, 2, 3, 4, 5, 6}, Display: []string{"none", "goMorning", "goMidDayOnly", "goEvening", "goNightOnly", "goAllDay", "goAnyValue"}},
|
||||
"StormFrequency": {Kind: kindFloat, Floats: []float64{0, 0.5, 1, 1.5, 2, 3, 4, 5}, Display: []string{"none"}},
|
||||
"QuestPerTier": {Kind: kindInt, Ints: []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, Display: []string{"none"}},
|
||||
"QuestPerDay": {Kind: kindInt, Ints: []int{-1, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, Display: []string{"goUnlimited"}},
|
||||
"TraderArea": {Kind: kindInt, Ints: []int{0, 1, 2}, Display: []string{"xuiYes", "goClaimable", "goNotClaimable"}},
|
||||
"TraderResetInterval": {Kind: kindInt, Ints: []int{-1, 1, 2, 3, 4, 5, 6, 7, 14}, Display: []string{"xuiDefault", "goDay"}},
|
||||
"ItemTierOptions": {Kind: kindInt, Ints: []int{1, 2, 3, 4, 5, 6}},
|
||||
"DewCollectorInput": {Kind: kindFloat, Floats: []float64{0, 1, 2, 3}, Display: []string{"none"}},
|
||||
"ApiaryInput": {Kind: kindFloat, Floats: []float64{0, 0.2, 0.4, 0.6, 0.8, 1, 1.5, 2, 3}, Display: []string{"none"}},
|
||||
"CollectorOutput": {Kind: kindFloat, Floats: []float64{1, 2, 3, 4, 5}},
|
||||
"BackpackCrafting": {Kind: kindInt, Ints: []int{0, 1, 2, 3}, Display: []string{"xuiNo", "xuiYes", "goLimited", "goWorkbenchOnly"}},
|
||||
"DeathPenalty": {Kind: kindInt, Ints: []int{0, 1, 2, 3}, Display: []string{"none", "goXPOnly", "goInjured", "goPermaDeath"}},
|
||||
"DropOnDeath": {Kind: kindInt, Ints: []int{0, 1, 2, 3, 4}, Display: []string{"none", "lblAll", "goToolbelt", "goBackpack", "goDeleteAll"}},
|
||||
"DropOnQuit": {Kind: kindInt, Ints: []int{0, 1, 2, 3}, Display: []string{"none", "lblAll", "goToolbelt", "goBackpack"}},
|
||||
"LoseItemsOnDeathType": {Kind: kindInt, Ints: []int{0, 1, 2, 3, 4, 5}, Display: []string{"none", "lblAll", "goToolbelt", "goBackpack", "goEquipment", "goCarriedOnly"}},
|
||||
"DegradeItemsOnDeath": {Kind: kindInt, Ints: []int{0, 1, 2, 3}, Display: []string{"none", "xuiDurability", "xuiMaxDurability", "xuiBoth"}},
|
||||
"TraderHourPresets": {Kind: kindInt, Ints: []int{0, 1, 2, 3, 4, 5, 6}, Display: []string{"xuiDefault", "goMorning", "goMidDayOnly", "goEvening", "goNightOnly", "goOnlyClosedOnBM", "goAlwaysOpen"}},
|
||||
"YesNo": {Kind: kindBool, Bools: []bool{false, true}, Display: []string{"xuiNo", "xuiYes"}},
|
||||
"Celebrate": {Kind: kindInt, Ints: []int{0, 1, 2}, Display: []string{"xuiNo", "xuiYes", "goHeadshotOnly"}},
|
||||
"ShowXP": {Kind: kindInt, Ints: []int{0, 1, 2, 3}, Display: []string{"lblAll", "goBarOnly", "goNotificationsOnly", "none"}},
|
||||
"HeadshotMode": {Kind: kindInt, Ints: []int{0, 1, 2}, Display: []string{"none", "goHeadshotOnly", "goHeadshotFinisher"}},
|
||||
"MaxEnemyType": {Kind: kindInt, Ints: []int{0, 1, 2, 3, 4, 5}, Display: []string{"goNormals", "goStrongs", "goSpecials", "goFerals", "goRadiated", "goElites"}},
|
||||
"MaxTechType": {Kind: kindInt, Ints: []int{0, 1, 2, 3, 4}, Display: []string{"none", "goTech0", "goTech1", "goTech2", "goTech3"}},
|
||||
"LoseItemCount": {Kind: kindInt, Ints: []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, Display: []string{"1-3", "1-5", "1-10", "1-20", "3-5", "5-7", "5-10", "7-10", "10-15", "15-20"}},
|
||||
"DayNightLength": {Kind: kindInt, Ints: []int{10, 20, 30, 40, 50, 60, 90, 120}},
|
||||
"DayLightLength": {Kind: kindInt, Ints: []int{0, 4, 6, 8, 10, 12, 14, 16, 18, 20, 24}, Display: []string{"goAlwaysNight", "4", "6", "8", "10", "12", "14", "16", "18", "20", "goAlwaysDay"}},
|
||||
"LootRespawnDays": {Kind: kindInt, Ints: []int{-1, 5, 7, 10, 15, 20, 30, 40, 50}, Display: []string{"goDisabled"}},
|
||||
"MaxChunkAge": {Kind: kindInt, Ints: []int{-1, 1, 3, 5, 7, 10, 20, 30, 40, 50, 75, 100}, Display: []string{"goDisabled"}},
|
||||
"Gravity": {Kind: kindFloat, Floats: []float64{0.5, 0.6, 0.7, 0.8, 0.9, 1}},
|
||||
"SlowToFast": {Kind: kindInt, Ints: []int{0, 1, 2, 3, 4, 5}, Display: []string{"xuiDefault", "goVerySlow", "goSlow", "goNormal", "goFast", "goVeryFast"}},
|
||||
"BiomeEnemyDensity": {Kind: kindInt, Ints: []int{0, 1, 2, 3, 4, 5}, Display: []string{"xuiDefault", "goVeryLow", "goLow", "goMedium", "goHigh", "goVeryHigh"}},
|
||||
"SmeltingType": {Kind: kindBool, Bools: []bool{false, true}, Display: []string{"goSmelter", "lblContextActionRecipes"}},
|
||||
"RepairTypes": {Kind: kindInt, Ints: []int{0, 1, 2, 3}, Display: []string{"none", "goRepairOnly", "goCombineOnly", "xuiBoth"}},
|
||||
"MaxDegradationAmounts": {Kind: kindFloat, Floats: []float64{0, 0.05, 0.1, 0.15, 0.2, 0.25}, Display: []string{"none"}},
|
||||
"CropGrowthSpeed": {Kind: kindFloat, Floats: []float64{1000, 0, 0.2, 0.5, 0.75, 1, 1.5, 2, 3}, Display: []string{"none", "xuiInstant"}},
|
||||
"ZombieFeralSense": {Kind: kindInt, Ints: []int{0, 1, 2, 3}, Display: []string{"goDisabled", "goZMDay", "goZMNight", "goZMAll"}},
|
||||
}
|
||||
Reference in New Issue
Block a user