У кликера появятся ограничения и цель: сбор тратит энергию, улучшение стоит 10 кристаллов и покупается один раз, а 20 кристаллов завершают партию. Цвет кнопки будет лишь подсказкой; каждое правило проверим в JavaScript.

Попробуй правила прямо здесь: потрать десять единиц энергии, купи улучшение и отдохни. Сила станет 2; ещё десять сборов дадут 20 очков и победу. Ниже разберём проверки, которые разрешают и запрещают действия.

Для этого урока возьми final/ урока о DOM, функциях и событиях. Это start/ нынешнего комплекта. Ты уже умеешь объявить функцию, изменить число и вызвать render().

Открыть эту версию в отдельной вкладке

Исходники урока

Выбирай стартовую версию, промежуточный этап или финал в просмотрщике. Во вкладке «Инструкция» — как запустить проект и чем отличаются версии. Решения самостоятельных заданий открывай после своей попытки.

Скачать ZIP

Распакуй архив целиком. У каждой версии своя папка с index.html, style.css и game.js. Открывай HTML внутри выбранной папки двойным щелчком; после правки сохраняй файл и обновляй браузер. Установка Node.js, аккаунт и сервер здесь не нужны. CSS уже знаком по первому модулю и дан как готовое оформление.

Если энергии нет — прекращаем действие

В game.js замени только handleGather:

function handleGather() {
  if (energy <= 0) {
    return;
  }
  score = score + clickPower;
  energy = energy - 1;
  render();
}

if означает «если». Выражение в круглых скобках вычисляется в логическое значение: true или false. Если оно истинно, выполняется блок в фигурных скобках. <= проверяет «меньше или равно». При нулевой энергии выполняется return; — немедленный выход из текущей функции. Поэтому строки прибавления и расхода ниже уже не исполнятся. Возврат вычисленного значения изучим отдельно; здесь значение не возвращаем.

Проверка step-01: после десяти сборов 10/0. Одиннадцатый и двенадцатый ничего не меняют. Отдых возвращает 10 энергии. Проверь также функцию напрямую в Console: energy = 0; handleGather(); не должна прибавить очко. После проверки в консоли обновляй страницу.

Как сравнивать значения и объединять условия

> и < сравнивают строго; >= и <= включают границу. === проверяет строгое равенство: 10 === 10 истинно, а 10 === '10' ложно, потому что типы различаются. Один знак = по-прежнему присваивает. Не используй его вместо сравнения в условии.

&& означает «и»: для покупки должно хватать кристаллов, а улучшение ещё не должно быть куплено. || означает «или»: действие запрещено, если партия окончена или энергии нет. ! меняет логическое значение на противоположное. Например, !upgraded истинно до покупки. Скобки помогают явно выделить выражение, которое мы отрицаем.

console.log(9 >= 10);   // false
console.log(10 >= 10);  // true
console.log(11 >= 10);  // true
console.log(10 >= 10 && !false); // true

Добавляем покупку улучшения

В index.html внутрь блока actions, после отдыха, добавь кнопку:

<button id="upgrade-button" type="button">Улучшить за 10</button>

Рядом с поиском других кнопок в game.js найди новый элемент. Перед регистрацией событий добавь обработчик, а регистрацию размести рядом с остальными:

const upgradeButton = document.getElementById('upgrade-button');

function handleUpgrade() {
  if (won || upgraded || !(score >= upgradePrice)) {
    return;
  }
  score = score - upgradePrice;
  clickPower = 2;
  upgraded = true;
  render();
}

upgradeButton.addEventListener('click', handleUpgrade);

Цена upgradePrice уже объявлена равной 10. Флаг upgraded до покупки равен false. Условие можно прочитать так: «выйти, если уже победили, уже купили или кристаллов недостаточно». После удачной покупки списываем ровно 10, меняем силу и ставим флаг. На следующем сборе прибавится 2. Вторая покупка ничего не делает, даже если снова накопить деньги.

Проверка step-02: при 9 кристаллах покупка не меняет ни очки, ни силу. При ровно 10 она оставляет 0 и силу 2. При 11 оставит 1. Для проверки удобнее в Console выполнить score = 9; render();, затем нажать кнопку. Перед каждым следующим случаем обновляй страницу, чтобы upgraded снова был false. В этой промежуточной версии победы ещё нет.

Добавляем завершение партии и сообщение

В handleGather расширь первый запрет и после расхода энергии добавь проверку цели. Замени функцию целиком:

function handleGather() {
  if (won || energy <= 0) {
    return;
  }
  score = score + clickPower;
  energy = energy - 1;
  if (score >= winningScore) {
    won = true;
  }
  render();
}

Используем >=, чтобы не пропустить победу когда после улучшения счёт вырастет сразу с 19 до 21. Для победы нужно иметь 20 кристаллов в запасе. Потраченные на улучшение кристаллы не учитываются; победить можно и без покупки. В начало handleRest также добавь if (won) { return; }, чтобы после окончания партии отдых не менял данные. У покупки такая защита уже есть.

Теперь замени render этим вариантом:

function render() {
  scoreElement.textContent = score;
  energyElement.textContent = energy;
  powerElement.textContent = clickPower;

  if (won) {
    statusElement.textContent = 'Победа! Ты собрал 20 кристаллов.';
  } else if (energy === 0) {
    statusElement.textContent = 'Энергия закончилась. Нажми «Отдохнуть».';
  } else {
    statusElement.textContent = 'Цель: 20 кристаллов. Улучшение за 10 удваивает силу.';
  }

  gatherButton.disabled = won || energy <= 0;
  restButton.disabled = won;
  upgradeButton.disabled = won || upgraded || score < upgradePrice;
}

else if проверяет следующий случай, только если предыдущий не подошёл; else — всё оставшееся. Сначала проверяем победу, затем запас энергии. Если последним ходом игрок набрал нужные очки и потратил последнюю единицу энергии, показываем поздравление с победой.

disabled — свойство кнопки. Значение true делает её недоступной для обычного нажатия. В render мы вычисляем это состояние из тех же данных, но не меняем сами очки. Проверки в обработчиках тоже оставляем: они не дадут нарушить правило, даже если вызвать функцию из консоли.

Самостоятельно: два условия для улучшения

Сделай отдельную копию final. Измени правило: покупка разрешена, только если хватает кристаллов и осталось хотя бы 1 энергии. Улучшение по-прежнему можно купить один раз и только до победы. Обнови и обработчик, и доступность кнопки. Проверь пары score/energy: 9/1, 10/0, 10/1. В первых двух покупка запрещена, в третьем разрешена.

Подсказка 1

Сначала вырази разрешённый случай: достаточно очков и энергия положительна. Потом сформулируй случаи запрета. Не списывай цену до проверки.

Подсказка 2

К существующему условию выхода можно через || добавить energy <= 0. Тот же случай добавь к upgradeButton.disabled. Эквивалентное разрешение: score >= upgradePrice && energy > 0.

Эту вариацию сохрани отдельно. В следующий урок идём с базовым final: покупка требует только 10 кристаллов, некупленного улучшения и незавершённой партии.

Что проверить в готовой версии

  • От 0 до 10 очков энергия доходит до нуля и не становится отрицательной.
  • Покупка при 9/10/11 имеет три ожидаемых результата; вторая покупка запрещена.
  • Десять сборов, отдых и ещё десять сборов без улучшения дают победу на 20.
  • Десять сборов, покупка, отдых и ещё десять сборов с силой 2 также заканчивают партию.
  • После победы прямые вызовы обработчиков в Console не меняют состояние.

Готово, когда: ты можешь объяснить, почему при 9 кристаллах покупка запрещена, а при 10 — разрешена, и найти условие, которое это проверяет. Временный сброс здесь — обновление страницы; собственную кнопку полного перезапуска добавим при сборке проекта.

Разбор самостоятельного задания

Сначала попробуй решить задание сам. Затем сравни с примером ту функцию или условие, которые вызвали затруднение. Разбор самостоятельной задачи доступен во вкладке «Решение» в просмотрщике исходников.