[Гайд] Все механики разведения в Cobblemon (мод Cobbreeding)
Автор: sanlover · Обновлён:
Гайд по всем механикам разведения в Cobblemon (мод Cobbreeding)
Этот гайд — результат анализа кода мода Cobbreeding. В нём нет воды и устаревших догадок — только реальная логика, на которую действительно опирается игра при формировании яйца.
На момент публикации: официальной вики или документации по Cobbreeding не существует. Этот гайд — единственный источник с цифрами, основанный на исходном коде самого мода.
Гайд подробно объясняет:
- Как формируется яйцо
- Какие параметры могут быть унаследованы
- Как влияют различные удерживаемые предметы
- Как работает шанс получения шайни
- Как передаются способности, приёмы, шары и аспекты
Предполагается, что читатель уже имеет общее представление о моде Cobblemon и возможности размножения.
Оглавление
- Формирование яйца
- Механика передачи IV при размножении покемонов
- Наследование характера
- Наследование способностей
-
Наследование Покебола - Наследование шайни-статуса
- Наследование приёмов
-
Наследование яйцевых приёмов через Зеркальный Лист - Наследование аспектов
- Правила подбора родителей и определение возможных яиц
- Полезные ссылки
Формирование яйца
Когда выбрана пара совместимых родителей, начинается процесс формирования яйца. На этом этапе система последовательно рассчитывает все параметры будущего покемона — от вида до приёмов.
Что рассчитывается?
- Вид и форма
- Индивидуальные значения IV
- Характер
- Способность
- Покебол
- Шайн-статус
- Egg Moves
- Аспекты
Общий алгоритм
- Выбирается родительская пара.
- Определяется вид и форма будущего покемона.
- Рассчитываются все свойства потомка.
- Эти данные объединяются в новое яйцо с готовыми параметрами.
Реализация в коде
Скрытый текст
fun chooseEgg(form: FormData, parents: Pair<Pokemon, Pokemon>): PokemonProperties {
return PokemonProperties.Builder(form)
.setParent1(parents.first.uuid) // UUID первого родителя
.setParent2(parents.second.uuid) // UUID второго родителя
.setIVs(calcStats(parents)) // Расчёт IV (уникальных параметров)
.setNature(calcNature(parents)) // Характер
.setAbility(calcAbility(form, parents)) // Способность
.setCaughtBall(calcBall(parents)) // Покебол
.setShiny(calcShiny(parents)) // Шайн-статус
.setEggMoves(calcEggMoves(parents, form)) // Наследуемые приёмы
.setAspects(calcAspects(parents)) // Аспекты (визуальные/сезонные)
.build() // Построение объекта потомка
}
Механика передачи IV при размножении покемонов
При размножении каждый покемон получает индивидуальные значения характеристик (IV), часть из которых может быть унаследована от родителей.
Всего у покемона 6 характеристик:
- HP — Очки здоровья
- Attack — Атака
- Defence — Защита
- Special Attack — Спец. атака
- Special Defence — Спец. защита
- Speed — Скорость
Общая логика наследования
- В начале для потомка создаются случайные IV для всех 6 характеристик.
- Затем система определяет, какие параметры будут унаследованы от родителей, в зависимости от:
- наличия удерживаемых предметов (Power Items);
- использования узла судьбы (Destiny Knot).
- Всего может быть унаследовано до 5 параметров, остальные останутся случайными.
Удерживаемые предметы (Power Items) и их влияние
-
Груз силы (power_weight) — Очки здоровья (HP) -
Наруч силы (power_bracer) — Атака (Attack) -
Пояс силы (power_belt) — Защита (Defence) -
Линзы силы (power_lens) — Спец. атака (Special Attack) -
Повязка силы (power_band) — Спец. защита (Special Defence) -
Браслет силы (power_anklet) — Скорость (Speed)
Как работают Power Items:
- Если один родитель держит Power Item — он обязательно передаёт соответствующий параметр.
- Если оба родителя держат Power Items, выбирается один случайный родитель, и передаётся только один параметр.
- Если ни один родитель не использует Power Item, система случайным образом выбирает:
- одну характеристику;
- одного родителя, от которого она будет унаследована.
Этот переданный параметр всегда становится первым унаследованным значением в итоговых IV.
Влияние узла судьбы
После передачи первого параметра определяется, сколько дополнительных характеристик будет унаследовано от родителей:
- Если у хотя бы одного родителя есть
узел судьбы (Destiny Knot), будет передано ещё 4 параметра. - Если
узел судьбы не используется, передаются ещё 2 параметра.
Каждый параметр выбирается по следующему принципу:
- Из оставшихся характеристик выбирается случайная.
- Выбирается случайный родитель.
- Если у него есть значение IV по этому параметру — оно передаётся.
- Характеристика исключается из дальнейшего выбора.
В результате потомок получает 3 или 5 унаследованных параметров, в зависимости от наличия узла судьбы.
Остальные IV
Все параметры, которые не были выбраны для наследования, остаются полностью случайными. Они не зависят от родителей или предметов и определяются заново при создании потомка.
Даже при идеальных условиях (Power Item + Destiny Knot) нельзя передать все 6 параметров — один из них всегда будет случайным.
Реализация в коде
Скрытый текст
private fun calcStats(parents: Pair<Pokemon, Pokemon>): IVs {
// Создаём случайные IV для потомка
val finalStats = IVs.createRandomIVs()
// Извлекаем родителей из пары
val (father, mother) = parents
// Набор всех возможных характеристик (IV)
val ivs = mutableSetOf(
Stats.HP,
Stats.ATTACK,
Stats.DEFENCE,
Stats.SPECIAL[/i]ATTACK,
Stats.SPECIAL[/i]DEFENCE,
Stats.SPEED
)
// Определяем, какие IV будут переданы с помощью удерживаемых предметов (Power Items)
val ivsToChooseFrom = mapOf(
Pair(father, powerItemToIV(father.heldItem().item)),
Pair(mother, powerItemToIV(mother.heldItem().item))
).filterNot { it.value == null }
// Временные переменные для текущего IV и родителя
var iv: Stats
var parent: Pokemon
// Если ни у одного из родителей нет Power Item — выбираем случайную характеристику и случайного родителя
if (ivsToChooseFrom.isEmpty()) {
iv = ivs.random()
parent = parents.random()
} else {
// Если Power Items есть у обоих — выбираем одного случайным образом и сохраняем соответствующий параметр
val (pokemon, stat) = ivsToChooseFrom.toList().random()
iv = stat!!
parent = pokemon
}
// Устанавливаем выбранное значение IV у потомка
parent.ivs[iv]?.let { finalStats[iv] = it }
// Исключаем переданный параметр из списка оставшихся
ivs.remove(iv)
// Определяем, сколько параметров будет дополнительно унаследовано:
// если есть узел судьбы (Destiny Knot) — 4 параметра, иначе — 2
var ivCount =
if (father.heldItem().item == CobblemonItems.DESTINY[/i]KNOT | | mother.heldItem().item == CobblemonItems.DESTINY[/i]KNOT) 4
else 2
// Передаём оставшиеся характеристики, пока не будет достигнуто нужное количество
while (ivCount != 0) {
// Выбираем случайную характеристику из оставшихся
iv = ivs.random()
// Выбираем случайного родителя
parent = parents.random()
// Если у родителя есть значение IV — передаём его потомку
parent.ivs[iv]?.let { finalStats[iv] = it }
// Удаляем характеристику из оставшихся
ivs.remove(iv)
// Уменьшаем счётчик
ivCount--
}
// Возвращаем сформированные IV потомка
return finalStats
}
Наследование характера
Каждый покемон имеет характер (Nature), который влияет на развитие его параметров. Один параметр усиливается на 10 %, другой — уменьшается на 10 %. Некоторые характеры — нейтральные и ничего не изменяют.
Как определяется характер у потомка:
- Если ни один родитель не держит
Камень вечности (Everstone) — характер потомка будет случайным. - Если один родитель держит
Камень вечности — потомок наследует характер именно этого родителя. - Если
Камень вечности есть у обоих родителей, один из них выбирается случайно, и его характер передаётся потомку.
Камень вечности — это единственный способ гарантировать нужный характер у потомка.
Таблица характеров и их влияния

Характеры без влияния (—) называются нейтральными и не влияют на параметры.
Переводы характеров соответствуют официальной русской локализации мода Cobblemon.
Авторские предпочтения
Некоторые характеры чаще используются для определённых ролей. Не силён в этом, но вот небольшая подборка:
Физический атакующий:
- Adamant — усиливает Атаку, снижает Спец. атаку.
- Jolly — увеличивает Скорость, снижает Спец. атаку.
Специальный атакующий:
- Modest — повышает Спец. атаку, снижает обычную атаку.
- Timid — увеличивает Скорость, снижает обычную атаку.
Танки и защитники:
- Bold — усиливает Защиту, снижает Атаку.
- Calm — усиливает Спец. защиту, снижает Атаку.
- Relaxed — усиливает Защиту, снижает Скорость (часто не критично).
Реализация в коде
Код реализации
private fun calcNature(parents: Pair < Pokemon, Pokemon > ): Nature {
// Извлекаем родителей
val(father, mother) = parents
// Проверяем, держит ли кто-то из родителей Камень вечности (Everstone)
val parentsItems = mapOf(
Pair(father, father.heldItem().item),
Pair(mother, mother.heldItem().item)
).filterNot {
it.value != CobblemonItems.EVERSTONE
}
// Если ни у кого нет Everstone — возвращаем случайный характер
if (parentsItems.isEmpty())
return Natures.getRandomNature()
// Иначе выбираем одного из родителей с Everstone и передаём его характер
return parentsItems.toList().random().first.nature
}
4. Наследование способностей
При размножении покемон может получить одну из доступных способностей ((Ability) своей формы. Форма покемона может иметь до трёх способностей: две обычные и одну скрытую.
Виды способностей

Как выбирается способность
- Определяется родитель, от которого будет передаваться способность:
- если в паре есть Дитто — используется родитель, не являющийся Дитто;
- если Дитто нет — используется мать.
- Система пытается сохранить способность этого родителя у потомка.
- Если родительская способность не сохраняется (по шансам), выбирается другая доступная способность формы.
- Если альтернатив нет — способность родителя сохраняется в любом случае.
Потомок всегда получит одну из доступных способностей своей формы. Но с высокой вероятностью это будет та же, что у выбранного родителя.
Шансы сохранить способность

Если альтернативная способность недоступна — родительская способность сохраняется автоматически.
Если у формы покемона только одна способность — шанс не применяется, потомок получает её гарантированно.
Реализация в коде
Код реализации
private fun calcAbility(form: FormData, parents: Pair<Pokemon, Pokemon>): String {
val (father, mother) = parents
// Определяем родителя, от которого будет передаваться способность:
// если в паре участвует Дитто — используем второго родителя,
// если нет — используем мать.
val ancestor =
if (mother.species == getByName("ditto")) father
else mother
val oldAbility = ancestor.ability
// Если способность "заставлена" (forced) и соответствующая настройка включена,
// то возвращаем её напрямую, без шанса.
// Однако по умолчанию (если настройка отключена), это поведение игнорируется.
return if (oldAbility.forced && Cobbreeding.config.forcedAbilitiesEnabled) oldAbility.name
else {
// Получаем приоритет и индекс способности родителя.
// Если индекс определён (≥ 0), используем его напрямую.
val (priority, index) = if (oldAbility.index >= 0) Pair(oldAbility.priority, oldAbility.index)
else {
// Если индекс не задан, пытаемся определить его вручную:
// ищем в мапе способностей ту, что соответствует шаблону родителя.
// Если не находим — по умолчанию берём 0 и наименьший приоритет.
val entry = ancestor.form.abilities.mapping.entries.firstOrNull { ([/i], abilities) ->
abilities.map { it.template }.contains(oldAbility.template)
}
entry?.run {
Pair(key, value.map { it.template }.indexOf(oldAbility.template))
} ?: Pair(Priority.LOWEST, 0)
}
// Определяем унаследованную способность:
// 1) сперва пытаемся взять способность на той же позиции и с тем же приоритетом;
// 2) если не находим — берём первую из этого приоритета;
// 3) если и тут не находим — первую из самого низкого приоритета;
// 4) если вообще ничего нет — подставляем "заглушку" (DUMMY).
val inheritedAbility = form.abilities.mapping[priority]?.getOrNull(index)?.template ?:
form.abilities.mapping[priority]?.firstOrNull()?.template ?:
form.abilities.mapping[Priority.LOWEST]?.firstOrNull()?.template ?:
Abilities.DUMMY
// Формируем список возможных альтернативных способностей (не совпадающих с inheritedAbility).
// Если скрытые способности разрешены — используем все приоритеты.
// Если запрещены — исключаем способности с приоритетом LOW (скрытые).
val remainingAbilities =
if (Cobbreeding.config.hiddenAbilitiesEnabled)
form.abilities.mapping.values
.flatten()
.map { it.template }
.filterNot { it == inheritedAbility }
else
form.abilities.mapping.values
.flatten()
.filterNot { it.priority == Priority.LOW }
.map { it.template }
.filterNot { it == inheritedAbility }
// Определяем шанс сохранить способность родителя:
// 60% для скрытых, 80% для остальных.
val chance = when (priority) {
Priority.LOW -> 6u // 60% для приоритета LOW (Hidden)
else -> 8u // 80% для остальных
}
// Если список альтернатив пуст или шанс прошёл — оставляем способность родителя.
// Иначе выбираем случайную из альтернативных.
val template = if (remainingAbilities.isEmpty() | | Random.nextUInt(10u) < chance) inheritedAbility
else remainingAbilities.random()
// Возвращаем имя итоговой способности
return template.name
}
}
Настройка hiddenAbilitiesEnabled в конфиге включена — скрытые способности передаются.
Настройка forcedAbilitiesEnabled по умолчанию выключена — «заставленные» способности игнорируются.
5.
Наследование Покебола
Покебол, в котором был пойман родитель, также может передаться потомку. Это влияет только на внешний вид и не даёт игрового преимущества, но может быть важно для коллекционеров.
Как выбирается покебол:
- По умолчанию потомок получает Покебол от матери.
- Если родители — одного вида, потомок получает Покебол одного из них случайным образом.
- Если мать — Дитто, Покебол передаётся от отца.
- Некоторые Покеболы не могут быть унаследованы и заменяются на стандартный: Черишбол (Cherish Ball) и Мастербол (Master Ball).
Дитто никогда не передаёт Покебол, даже если он — второй родитель.
Реализация в коде
Код реализации
private fun calcBall(parents: Pair < Pokemon, Pokemon > ): String {
var ball = parents.second.caughtBall // По умолчанию — Покебол матери
// Если оба родителя одного вида — Покебол выбирается случайно от одного из них
if (parents.first.species.name == parents.second.species.name)
ball = parents.random().caughtBall
// Если мать — Дитто, Покебол наследуется от отца
if (parents.second.species.name == "Ditto")
ball = parents.first.caughtBall
// Некоторые Покеболы нельзя передать напрямую: заменяются на обычный
if (ball == PokeBalls.CHERISH[/i]BALL | | ball == PokeBalls.MASTER[/i]BALL)
ball = PokeBalls.POKE[/i]BALL
return ball.name.toString()
}
6. Наследование шайни-статуса
Покемон может родиться шайни — с редкой и уникальной расцветкой. Это чисто визуальный эффект, не влияющий на силу или характеристики, но высоко ценится среди коллекционеров.
Базовая вероятность
- По умолчанию шанс рождения шайни — 1 к 8192.
- Этот шанс может увеличиться, если активированы специальные параметры в конфигурации сервера и соблюдены некоторые условия.
Методы увеличения шанса

По умолчанию включён только masuda. Метод crystal доступен, но неизвестно, активен ли он на сервере mcskill.
Как рассчитывается шанс
- Начальный шанс рождения шайни: 1 / 8192.
- За каждого родителя-шайни (если активен crystal) — шанс умножается на 4.
- Если родители пойманы разными тренерами (masuda) — шанс также умножается на 4.
- Итоговый шанс — это результат деления 8192 на 4 за каждое выполненное условие.
Примеры итоговых шансов

Если crystal отключён, максимальный шанс, который можно получить, — 1 / 2048 (только за счёт masuda). Значение 1 / 128 возможно только если crystal включён и оба родителя — шайни.
Реализация в коде
Код реализации
private fun calcShiny(parents: Pair<Pokemon, Pokemon>): Boolean? {
// Получаем список включённых методов повышения шанса (из конфигурации)
val shinyMethod = Cobbreeding.config.shinyMethod
// Если shiny-статус отключён полностью — не рассчитываем, возвращаем null
if ("disabled" in shinyMethod) return null
// Множитель, на который делится шанс при срабатывании условий (по умолчанию: 4)
val shinyMultiplier = Cobbreeding.config.shinyMultiplier
// Базовый шанс получения шайни (по умолчанию: 1 / 8192)
var shinyOdds = Cobblemon.config.shinyRate
// Метод crystal: увеличивает шанс, если один или оба родителя — шайни
if ("crystal" in shinyMethod) {
// Если первый родитель — шайни
if (parents.first.shiny)
shinyOdds /= shinyMultiplier
// Если второй родитель — шайни
if (parents.second.shiny)
shinyOdds /= shinyMultiplier
}
// Метод masuda: увеличивает шанс, если родители от разных оригинальных тренеров
if ("masuda" in shinyMethod) {
// Получаем UUID оригинальных тренеров обоих родителей
val owner1 = parents.first.originalTrainer ?: parents.first.getOwnerPlayer()?.stringUUID
val owner2 = parents.second.originalTrainer ?: parents.second.getOwnerPlayer()?.stringUUID
// Если тренеры отличаются — делим шанс ещё раз
if (owner1 != null && owner2 != null && owner1 != owner2)
shinyOdds /= shinyMultiplier
}
// Если шанс меньше 1 (например, при методе "always") — потомок точно будет шайни
if (shinyOdds < 1) return true
// Генерируем случайное число от 0 до shinyOdds
// Если выпадает 0 — потомок будет шайни
return Random.nextInt(0, shinyOdds.toInt()) == 0
}
7. Наследование приёмов
Яйцевые приёмы — это особые атаки, которые нельзя получить через уровень, ТМ или тренера. Они передаются только при размножении, если родитель владеет или имеет доступ к такому приёму.
На сервере механика упрощена: можно использовать ТМ/TR для изучения приёмов яйцевых групп.
Общая логика наследования
- У формы покемона есть список доступных яйцевых приёмов.
- У каждого из родителей проверяются:
- все изученные приёмы;
- все потенциальные приёмы, к которым у него есть доступ.
- Если хотя бы один из этих приёмов совпадает с доступным яйцевым — он добавляется к потомку.
- Исключение: если рождается Пичу (Pichu)), и у одного из родителей есть Световой шар (Light Ball)), то приём Volt Tackle добавляется всегда.
Достаточно, чтобы один из родителей мог выучить нужный приём, — не обязательно, чтобы он уже его знал.
Пример
- У Пикачу есть доступ к приёму Fake Out, а у Риолу — это яйцевый приём.
- Если Пикачу участвует в размножении — Риолу может унаследовать Fake Out.
Реализация в коде
Код реализации
private fun calcEggMoves(parents: Pair < Pokemon, Pokemon > , child: FormData): List < String > {
// Получаем список возможных яйцевых приёмов у данной формы потомка
val possibleEggMoves = child.moves.eggMoves
val eggMoves = LinkedList < String > ()
// Перебираем каждого родителя
for (parent in parents.toList()) {
// Получаем все потенциально доступные приёмы (включая наследуемые, ТМ и по уровню)
val allAccessibleMoves = parent.allAccessibleMoves.toMutableSet()
// Добавляем текущие выученные приёмы
for (move in parent.moveSet.getMoves())
allAccessibleMoves += move.template
// Сравниваем с яйцевыми приёмами потомка — добавляем совпадения
for (accessibleMove in allAccessibleMoves) {
if (possibleEggMoves.contains(accessibleMove))
eggMoves.add(accessibleMove.name)
}
}
// Специальный случай: если вылупляется Пичу и один из родителей держит Light Ball
if (
child.species.name == "Pichu" &&
parents.toList().any {
p - > p.heldItem().item == CobblemonItems.LIGHT[/i]BALL
}
)
eggMoves.add("volttackle")
return eggMoves
}
8.
Наследование яйцевых приёмов через Зеркальный Лист
Некоторые приёмы покемоны могут перенять напрямую, без размножения. Это становится возможным, если покемон держит специальный предмет —
Зеркальный Лист (Mirror Herb).
Как работает механика
- В пастбище проверяются все покемоны.
- Если покемон держит Зеркальный Лист:
- Определяется список приёмов, которые он теоретически может получить при рождении.
- Все остальные покемоны в загоне перебираются:
- Сравниваются их доступные и выученные приёмы.
- Если какой-либо приём совпадает с потенциальными приёмами покемона с Зеркальным Листом, он добавляется этому покемону.
Что это даёт?
- Позволяет выучить уникальные или редкие приёмы, не прибегая к размножению.
- Работает, даже если покемон-источник приёма не изучал его напрямую, а просто имеет к нему доступ.
- Применимо к одиночному покемону — не требует пары.
Реализация в коде
Код реализации
fun applyMirrorHerb(pokemon: List<Pokemon?>)
{
// Ищем покемонов, держащих Mirror Herb
for (holder in pokemon)
{
if (holder != null && holder.heldItem().item == CobblemonItems.MIRROR_HERB)
{
// Получаем список приёмов, которые этот покемон может получить при рождении
val possibleEggMoves = getBaby(holder.form).moves.eggMoves
// Перебираем всех других покемонов в загоне
for (other in pokemon)
{
if (other != null)
{
// Собираем все приёмы, к которым у них есть доступ
val allAccessibleMoves = other.allAccessibleMoves.toMutableSet()
for (move in other.moveSet.getMoves())
allAccessibleMoves.add(move.template)
// Если у другого покемона есть приём, совпадающий с возможностями holder'а — передаём
for (accessibleMove in allAccessibleMoves) {
if (possibleEggMoves.contains(accessibleMove))
holder.benchedMoves.add(BenchedMove(accessibleMove, 0))
}
}
}
}
}
}
9. Наследование аспектов
Аспекты — это скрытые метки, прикреплённые к покемону. Они используются для хранения нестандартных характеристик: например, визуальных эффектов, пола (male, female), шайни-статуса (shiny), а также уникальных особенностей, добавленных через датапаки или моды (например, сезонные или ивентовые аспекты).
Как работает наследование аспектов
Наследование аспектов
- Аспекты наследуются от матери, если в паре нет Дитто.
- Если мать — Дитто, аспекты передаются от отца.
- Перед передачей из набора аспектов удаляются следующие системные теги:
shinymalefemale
Это делается для того, чтобы не нарушить механику шайни-статуса и пола, которые определяются отдельно.
Что можно передать через аспекты?
- Особые формы покемонов, не влияющие на параметры, но изменяющие внешний вид (например, цвет глаз, узоры, зимние окраски).
- Модифицированные метки, добавленные другими модами, если они реализованы как aspect.
Пример
Если у матери установлен аспект snowfest2023, то этот аспект будет передан потомку. А вот аспект shiny — не будет передан, так как он определяется отдельной системой.
Реализация в коде
Код реализации
private fun calcAspects(parents: Pair<Pokemon, Pokemon>): List<String> {
// Определяем родителя, от которого будут унаследованы аспекты:
// если второй родитель — Дитто, используем первого (отца),
// иначе — второго (матери). Т.е. при наличии Дитто аспекты не берутся от него.
val mother = if (parents.second.species == getByName("ditto")) parents.first else parents.second
// Копируем все аспекты выбранного родителя (например, сезонные, пользовательские и т.п.)
val aspects = mother.aspects.toMutableList()
// Удаляем аспекты, которые не должны передаваться:
// shiny — рассчитывается отдельно;
// male/female — определяется системой пола.
aspects.remove("shiny")
aspects.remove("male")
aspects.remove("female")
// Возвращаем оставшиеся аспекты — они будут применены к потомку
return aspects
}
Правила подбора родителей и определение возможных яиц
Не любая пара покемонов может оставить яйцо. Перед созданием яйца система проверяет совместимость родителей — их пол, группы яиц, исключения и участие Дитто.
Условия совместимости
- Оба родителя должны принадлежать к одной и той же группе яиц, если только один из них не является Дитто.
- Минимум один из родителей должен быть мужского пола, другой — женского.
- Покемоны из группы Undiscovered не способны к размножению.
- Дитто может спариваться почти с любым другим покемоном.
Кто определяет вид потомка?
- Если в паре участвует Дитто — потомок всегда получается второго родителя (не-Дитто).
- В обычной паре (без Дитто) вид потомка определяется по женскому покемону.
Особые случаи

Как определяется возможный потомок
- Если у покемона есть цепочка эволюций — отслеживается базовая форма. Потомок всегда будет этой первой эволюцией.
- Некоторые формы (например, региональные) имеют исключения: их базовая форма может быть нестандартной.
- Для таких форм вручную заданы специальные варианты потомков:

Реализация в коде
Код определения яиц и формы потомка
Определение возможных яиц
fun getPossibleEggs(pokemon: List<Pokemon>): Collection<Map.Entry<FormData, List<Pair<Pokemon, Pokemon>>>> {
// Словарь, где ключ — форма потенциального потомка, значение — список родительских пар
val eggs = HashMap<FormData, MutableList<Pair<Pokemon, Pokemon>>>()
// Перебираем все возможные уникальные пары покемонов
for (i in pokemon.indices) {
for (j in i + 1 until pokemon.size) {
val pokemon1 = pokemon[i]
val species1 = pokemon1.species
val eggGroups1 = species1.eggGroups
val pokemon2 = pokemon[j]
val species2 = pokemon2.species
val eggGroups2 = species2.eggGroups
// Проверяем, что оба покемона не находятся в группе Undiscovered
// (такие покемоны не могут размножаться вообще)
if (!eggGroups1.contains(EggGroup.UNDISCOVERED) && !eggGroups2.contains(EggGroup.UNDISCOVERED)) {
// Если один из покемонов — Дитто
if (eggGroups1.contains(EggGroup.DITTO)) {
// Потомок формируется от второго покемона
dittoBreed(pokemon2)?.let {
// Добавляем пару (отец, мать) в список потомков соответствующей формы
if (eggs.containsKey(it)) eggs[it]?.add(Pair(pokemon2, pokemon1))
else eggs[it] = mutableListOf(Pair(pokemon2, pokemon1))
}
} else if (eggGroups2.contains(EggGroup.DITTO)) {
dittoBreed(pokemon1)?.let {
if (eggs.containsKey(it)) eggs[it]?.add(Pair(pokemon1, pokemon2))
else eggs[it] = mutableListOf(Pair(pokemon1, pokemon2))
}
}
// Если оба покемона НЕ Дитто и их группы яиц пересекаются
else if (eggGroups1.any(eggGroups2::contains)) {
// Женщина-самка слева
if (pokemon1.gender == Gender.FEMALE && pokemon2.gender == Gender.MALE) {
// Вид потомка определяется по самке
getBaby(pokemon1).let {
if (eggs.containsKey(it)) eggs[it]?.add(Pair(pokemon2, pokemon1))
else eggs[it] = mutableListOf(Pair(pokemon2, pokemon1))
}
}
// Женщина-самка справа
else if (pokemon2.gender == Gender.FEMALE && pokemon1.gender == Gender.MALE) {
getBaby(pokemon2).let {
if (eggs.containsKey(it)) eggs[it]?.add(Pair(pokemon1, pokemon2))
else eggs[it] = mutableListOf(Pair(pokemon1, pokemon2))
}
}
}
}
}
}
// Специальный случай: Нидоран♀ и Нидоран♂ — всегда выводятся вместе
if (eggs.containsKey(getByName("nidoranf")!!.standardForm)) {
eggs[getByName("nidoranm")!!.standardForm] = eggs[getByName("nidoranf")!!.standardForm]!!
} else if (eggs.containsKey(getByName("nidoranm")!!.standardForm)) {
eggs[getByName("nidoranf")!!.standardForm] = eggs[getByName("nidoranm")!!.standardForm]!!
}
// То же самое для Volbeat и Illumise — возможны оба результата
if (eggs.containsKey(getByName("illumise")!!.standardForm)) {
eggs[getByName("volbeat")!!.standardForm] = eggs[getByName("illumise")!!.standardForm]!!
} else if (eggs.containsKey(getByName("volbeat")!!.standardForm)) {
eggs[getByName("illumise")!!.standardForm] = eggs[getByName("volbeat")!!.standardForm]!!
}
// Возвращаем мапу: форма → список допустимых родительских пар
return eggs.mapValues { (_, v) -> v.toList() }.entries
}
Определение формы потомка
fun getBaby(pokemon: FormData): FormData {
// Переход к базовому виду — пока у текущего вида есть предэволюция
var species = pokemon.species
while (species.preEvolution != null) {
species = species.preEvolution!!.species
}
// Обработка исключений для региональных форм:
// У этих форм потомок — не стандартная базовая форма, а конкретный слот формы
if (pokemon.species.name == "Sirfetch’d" ||
pokemon.species.name == "Cursola" ||
pokemon.species.name == "Obstagoon" ||
pokemon.species.name == "Runerigus" ||
pokemon.species.name == "Clodsire" ||
pokemon.species.name == "Overqwil" ||
pokemon.species.name == "Sneasler")
return species.forms[1]
// Исключения, у которых потомок — третья форма (слот 2)
if (pokemon.species.name == "Perrserker" ||
pokemon.species.name == "Basculegion")
return species.forms[2]
// В остальных случаях: ищем подходящую форму по showdownId
return species.forms.find {
it.formOnlyShowdownId().contains(pokemon.formOnlyShowdownId())
} ?: species.standardForm
}
Полезные ссылки
- Репозиторий мода Cobbreeding
- Репозиторий мода Cobblemon
- Информация о шансах и местах спавна покемонов
- Расшифровка тегов биомов
- Краткая и частично устаревшая wiki от разработчика Cobbreeding (информация в гайде считается более приоритетной)
Послесловие
На этом я заканчиваю и благодарю вас за внимание. Буду рад комментариям, вопросам и обсуждению. Играю на втором сервере, бываю часто — не стесняйтесь писать в игре.
На всякий случай добавлю, что шанс вылупления любого яйца — 50 % каждые 10 минут. Знаю, что у некоторых игроков всё ещё возникают сомнения в работе механик, особенно касательно узла судьбы и power-айтемов. Специально для вас я вывел 54 потомка от фиксированной пары родителей, отследил все IV и провёл краткий анализ.
Результаты и визуализация — в отдельном исследовании: https://www.kaggle.com/code/sanlovty/breeding-theory-check-by-sanlover
Если вы замечаете неточности или хотите дополнить гайд — пишите. Я постараюсь продолжить отслеживать обновления мода и по возможности буду дополнять материал.
P.s.: Если руки дойдут оформить гайд, ждите гайд на cobblenav со сломанным pokefinder'om.
Благодарности
- wt04d — информация о возможности изучения яйцевых атак через ТМ/TR.


