̬̬̬̈̬̬̬̬̈̬̬̬̬̬̪쬪̬̬̬̪쬪 z : I  2BbctuU A "7<>           UUUUUUUUUUUUUUUUUPUPUUUU UUUU UUUU   UUUUUUUUUUUUUUUUUPUPUUUUU Uݍ @DDD @DDD @DDD @DDD@D@DDD@DDD@DDDUUUUUUUU UUUU а UUUU UPUP UUUU UUUU ݰ UUUU ۰ $-- title: Dual Being -- author: kolonochkaa -- desc: Play for both two characters and collect gems! -- script: lua -- input: gamepad -- palette: modern -- ============================================ -- НАСТРОЙКИ И ГЛОБАЛЬНЫЕ ПЕРЕМЕННЫЕ -- ============================================ -- Игроки (две сущности) players = { { x = 100, y = 68, -- позиция (верхний мир) color = 12, -- синий active = true, width = 6, height = 6 }, { x = 140, y = 68, -- позиция (нижний мир) color = 9, -- красный active = true, width = 6, height = 6 } } -- Кристаллы (цели) crystals = {} for i = 1, 8 do crystals[i] = { x = math.random(20, 220), y = math.random(20, 116), world = math.random(1, 2), -- 1 = верхний, 2 = нижний collected = false } end -- Враги (движутся в противоположном мире) enemies = {} for i = 1, 4 do enemies[i] = { x = math.random(20, 220), y = math.random(20, 116), world = math.random(1, 2), dx = (math.random() * 2 - 1) * 0.8, dy = (math.random() * 2 - 1) * 0.8, size = 8 } end -- Счет score = 0 gameOver = false win = false -- Таймер анимации animTimer = 0 -- ============================================ -- ВСПОМОГАТЕЛЬНЫЕ ФУНКЦИИ -- ============================================ function clamp(value, min, max) return math.max(min, math.min(max, value)) end function distance(x1, y1, x2, y2) local dx = x1 - x2 local dy = y1 - y2 return math.sqrt(dx*dx + dy*dy) end -- ============================================ -- ЗАГРУЗКА -- ============================================ function BOOT() -- В TIC-80 math.random уже инициализирован, -- дополнительный seed не требуется. -- Если нужно, можно использовать time() вместо os.time() -- math.randomseed(time()) end -- ============================================ -- ГЛАВНЫЙ ЦИКЛ -- ============================================ function TIC() if gameOver then showGameOver() return end if win then showWin() return end handleInput() updateEnemies() checkCollisions() draw() drawUI() animTimer = (animTimer + 1) % 60 end -- ============================================ -- УПРАВЛЕНИЕ (зеркальное движение) -- ============================================ function handleInput() local dx = 0 local dy = 0 if btn(0) then dx = -2 end -- влево if btn(1) then dx = 2 end -- вправо if btn(2) then dy = -2 end -- вверх if btn(3) then dy = 2 end -- вниз -- Перемещаем обоих игроков зеркально players[1].x = players[1].x + dx players[1].y = players[1].y + dy players[2].x = players[2].x - dx -- зеркально по X players[2].y = players[2].y - dy -- зеркально по Y -- Ограничения для первого игрока (верхний мир) players[1].x = clamp(players[1].x, 2, 238 - players[1].width) players[1].y = clamp(players[1].y, 2, 134 - players[1].height) -- Ограничения для второго игрока (нижний мир) players[2].x = clamp(players[2].x, 2, 238 - players[2].width) players[2].y = clamp(players[2].y, 2, 134 - players[2].height) end -- ============================================ -- ДВИЖЕНИЕ ВРАГОВ -- ============================================ function updateEnemies() for i, e in ipairs(enemies) do e.x = e.x + e.dx e.y = e.y + e.dy -- Отскок от границ if e.x < 4 then e.x = 4; e.dx = -e.dx end if e.x > 236 then e.x = 236; e.dx = -e.dx end if e.y < 4 then e.y = 4; e.dy = -e.dy end if e.y > 132 then e.y = 132; e.dy = -e.dy end end end -- ============================================ -- ПРОВЕРКА СТОЛКНОВЕНИЙ -- ============================================ function checkCollisions() -- Проверка сбора кристаллов for i, c in ipairs(crystals) do if not c.collected then local player = players[c.world] if distance(player.x + player.width/2, player.y + player.height/2, c.x + 4, c.y + 4) < 10 then c.collected = true score = score + 1 -- Звук (если настроить sfx) end end end -- Проверка столкновения с врагами (игра заканчивается) for _, e in ipairs(enemies) do local player = players[e.world] if distance(player.x + player.width/2, player.y + player.height/2, e.x + e.size/2, e.y + e.size/2) < 12 then gameOver = true end end -- Проверка победы (все кристаллы собраны) local allCollected = true for _, c in ipairs(crystals) do if not c.collected then allCollected = false break end end if allCollected then win = true end end -- ============================================ -- ОТРИСОВКА -- ============================================ function draw() cls(0) -- черный фон -- Разделительная линия между мирами line(0, 68, 240, 68, 8) -- Верхний мир (голубоватый оттенок) for y = 0, 68, 8 do for x = 0, 240, 8 do if (x+y)%16 == 0 then pix(x, y, 1) -- слабые точки фона end end end -- Нижний мир (красноватый оттенок) for y = 68, 136, 8 do for x = 0, 240, 8 do if (x+y)%16 == 0 then pix(x, y, 2) end end end -- Кристаллы for _, c in ipairs(crystals) do if not c.collected then local color = (c.world == 1) and 11 or 13 -- Рисуем ромб (кристалл) local cx, cy = c.x + 4, c.y + 4 line(cx, cy-4, cx+3, cy, color) line(cx+3, cy, cx, cy+4, color) line(cx, cy+4, cx-3, cy, color) line(cx-3, cy, cx, cy-4, color) pix(cx, cy, color+2) end end -- Враги (вращающиеся квадраты) for _, e in ipairs(enemies) do local color = (e.world == 1) and 4 or 5 rect(e.x, e.y, e.size, e.size, color) -- Глазки local eyeX = e.x + e.size/2 local eyeY = e.y + e.size/2 pix(eyeX-2, eyeY, 0) pix(eyeX+2, eyeY, 0) end -- Игроки for _, p in ipairs(players) do -- Тело rect(p.x, p.y, p.width, p.height, p.color) -- Глаза local eyeX = p.x + p.width/2 local eyeY = p.y + p.height/2 pix(eyeX-1, eyeY, 0) pix(eyeX+1, eyeY, 0) -- Анимация пульсации (каждый 30-й кадр) if animTimer % 30 < 15 then rect(p.x-1, p.y-1, p.width+2, p.height+2, p.color+1) end end end function drawUI() -- Счет print("CRYSTALS: " .. score .. "/" .. #crystals, 10, 5, 15) -- Подсказка print("CONTROL BOTH", 160, 5, 12) print(" < > ^ v", 160, 13, 12) -- Иконки миров print("▲ TOP WORLD", 10, 120, 11) print("▼ BOTTOM WORLD", 10, 128, 9) end -- ============================================ -- ЭКРАНЫ КОНЦА ИГРЫ -- ============================================ function showGameOver() cls(0) print("GAME OVER", 90, 60, 14) print("SCORE: " .. score, 100, 70, 15) print("PRESS Z TO RESTART", 60, 90, 12) if btn(4) then restartGame() end end function showWin() cls(0) print("VICTORY!", 100, 60, 14) print("YOU MASTERED DUALITY", 60, 70, 11) print("SCORE: " .. score, 100, 80, 15) print("PRESS Z TO PLAY AGAIN", 50, 100, 12) if btn(4) then restartGame() end end function restartGame() -- Сброс переменных score = 0 gameOver = false win = false -- Сброс позиций игроков players[1].x, players[1].y = 100, 68 players[2].x, players[2].y = 140, 68 -- Новые кристаллы crystals = {} for i = 1, 8 do crystals[i] = { x = math.random(20, 220), y = math.random(20, 116), world = math.random(1, 2), collected = false } end -- Новые враги enemies = {} for i = 1, 4 do enemies[i] = { x = math.random(20, 220), y = math.random(20, 116), world = math.random(1, 2), dx = (math.random() * 2 - 1) * 0.8, dy = (math.random() * 2 - 1) * 0.8, size = 8 } end end