Metadata-Version: 2.4
Name: jwarol
Version: 0.12.0
Summary: High-performance creative engine (Win32/OpenGL/Python)
Home-page: https://github.com/r9441887-collab/jwarol-code-new
Author: jwarol-team
Author-email: r9441887@gmail.com
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: C++
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: Microsoft :: Windows
Classifier: Operating System :: POSIX :: Linux
Requires-Python: >=3.8
Description-Content-Type: text/markdown
License-File: LICENSE
Dynamic: author
Dynamic: author-email
Dynamic: classifier
Dynamic: description
Dynamic: description-content-type
Dynamic: home-page
Dynamic: license-file
Dynamic: requires-python
Dynamic: summary

# jwarol

Движок на C++ для 2D-игр на Python. Win32 API + сырой OpenGL (и X11 на Linux). Включено: рендеринг, шейдеры, текстуры, физика, звук .wav, игровые объекты, шифрование ассетов.

## Возможности

| Функция | Описание |
|---------|----------|
| **Window** | Создание окна с OpenGL-контекстом (Win32 / X11) |
| **Shader** | Компиляция GLSL-шейдеров из строк или отдельных .glsl файлов |
| **Renderer** | Рендеринг прямоугольников и текстур через GPU |
| **Texture** | Загрузка PNG/JPG/BMP, процедурные текстуры, наложение |
| **Input** | Клавиатурный ввод (GetAsyncKeyState) |
| **Camera** | Следование за игроком, скроллинг мира, smooth-камера |
| **Sprite/Animation** | Спрайт-листы, анимация по кадрам, генерация анимаций |
| **DetailGroup** | Группа деталей — батч-рендеринг тысяч объектов без просадки FPS |
| **Physics** | Verlet-интеграция, гравитация, коллизии, типы тел |
| **Sound** | Воспроизведение .wav (winmm) |
| **Object/Player/Character/GUI/Detail** | Система игровых объектов |
| **Texture.encrypt** | Шифрование текстур для скомпилированных игр |
| **Clock** | Замер дельта-тайма |
| **Vec2/Rect** | 2D-математика |

## Установка

```bash
pip install jwarol
```

## Быстрый старт

```python
import jwarol as jl

jl.init_graphics()
win = jl.Window(800, 600, "Hello jwarol")
renderer = jl.Renderer(800, 600)

while win.is_open:
    win.poll_events()
    win.clear(0.1, 0.1, 0.2)

    renderer.draw_rect(100, 100, 200, 150, 1.0, 0.3, 0.5)
    renderer.draw_rect(350, 200, 100, 100, 0.2, 0.7, 1.0)

    win.swap_buffers()
```

## Текстуры

```python
# Загрузка из файла (PNG/JPG/BMP на Windows, BMP на Linux)
tex = jl.Texture("sprite.png")

# Процедурная текстура из пикселей (RGBA)
import struct
w, h = 64, 64
pixels = bytearray()
for y in range(h):
    for x in range(w):
        pixels.extend([255, 0, 0, 255])  # красный пиксель
tex = jl.Texture(w, h, bytes(pixels), 4)

# Рисование с текстурой
renderer.draw_texture(tex, 100, 100, 200, 200)

# Текстура с прозрачностью
renderer.draw_texture(tex, 100, 100, 200, 200, alpha=0.5)

# Спрайт из tileset'а (draw_texture_ex)
renderer.draw_texture_ex(tileset, 0, 0, 32, 32, 0, 0, 32, 32)

# Умная загрузка всех текстур из папки
textures = jl.Texture.load_all("assets/textures/")

# Наложение текстур
combined = tex.overlay(other_tex, 10, 10, alpha=0.7)
```

## Физика

```python
world = jl.World()
world.gravity = 980.0

# Verlet-точки
p = world.add_point(400, 100)  # падает вниз

# Физические тела (прямоугольники)
body = world.add_body(300, 0, 50, 50, jl.BodyType.DYNAMIC)
static = world.add_body(0, 650, 800, 50, jl.BodyType.STATIC)

# Исключение из физики
body.enabled = False  # не участвует в симуляции

# Шаг физики
dt = clock.tick()
world.step(dt)

# Чтение позиции
x, y = body.pos.x, body.pos.y
```

## Ввод и время

```python
input = jl.Input()
clock = jl.Clock()

while win.is_open:
    dt = clock.tick()
    input.update()

    if input.is_down(ord('W')): pass      # клавиша зажата
    if input.is_pressed(ord(' ')): pass   # только что нажали
    if input.is_released(ord('A')): pass  # только что отпустили
```

## Шейдеры

```python
# Из строк
shader = jl.Shader("""
#version 330 core
layout(location = 0) in vec2 aPos;
void main() { gl_Position = vec4(aPos, 0.0, 1.0); }
""", """
#version 330 core
uniform vec4 uColor;
out vec4 FragColor;
void main() { FragColor = uColor; }
""")
shader.use()

# Из отдельных файлов (любые названия)
shader = jl.Shader.from_file("shaders/vertex.glsl", "shaders/fragment.glsl")
shader = jl.Shader.from_file("custom.vert", "custom.frag")

# Из одного файла с маркерами (#shader vertex / #shader fragment)
shader = jl.Shader.from_single_file("shader.glsl")

# Ошибки обрабатываются без краша:
# - файл не найден → RuntimeError
# - файл пустой → RuntimeError
# - ошибка компиляции → RuntimeError с логом
try:
    s = jl.Shader.from_file("bad.glsl", "empty.glsl")
except RuntimeError as e:
    print("Shader error:", e)
```

## Звук

```python
# Из папки
sound = jl.Sound("assets/sounds/effect.wav")
sound.play()
sound.set_volume(500)  # 0-1000
sound.stop()

# Быстрое воспроизведение без создания объекта
jl.Sound.play_file("assets/sounds/click.wav")
```

## Камера

```python
cam = jl.Camera(800, 600)

# Следование за игроком (с плавностью)
cam.follow(player)
cam.smooth = 0.1  # 0 = мгновенно, 1 = очень плавно

# Применить к рендереру перед отрисовкой
cam.update(dt)
cam.apply_to(renderer)

# Все draw_rect/draw_texture теперь сдвинуты камерой
# Объекты за экраном не рисуются (FPS не проседает)
renderer.draw_rect(0, 0, 2000, 2000, 0.2, 0.2, 0.3)  # большой мир
```

## Спрайты и анимация

```python
# Загрузка спрайт-листа (одна картинка с кадрами)
sheet = jl.Texture("player_walk.png")  # 4 колонки x 2 строки = 8 кадров

sprite = jl.Sprite()
sprite.set_texture(sheet, 4, 2)  # 4 колонки, 2 строки

# Анимация из последовательности кадров
anim = jl.Animation()
anim.from_range(0, 3)  # кадры 0,1,2,3
anim.speed = 0.1       # сек/кадр
anim.loop = True
sprite.set_animation(anim)

# В игровом цикле
sprite.update(dt)
sprite.draw_at(renderer, player.x, player.y, 64, 64)

# Генерация простых анимаций
idle = jl.Animation.generate("idle", 8)
walk = jl.Animation.generate("walk", 8)
run = jl.Animation.generate("run", 8)

# Рендерер сам обрезает невидимые объекты
renderer.set_camera(cam.x, cam.y)
renderer.is_visible(x, y, w, h)  # проверка видимости вручную
```

## Группа деталей (DetailGroup)

Тысячи мелких объектов без просадки FPS — все рендерятся за один draw call.

```python
details = jl.DetailGroup()

# Цветные прямоугольники
details.add(100, 200, 32, 32, 1.0, 0.5, 0.0, 0.8)

# С текстурой (атлас: одна картинка с кадрами)
tileset = jl.Texture("tileset.png")
details.set_texture(tileset, 4, 4)  # 4x4 сетка кадров

# Фрейм из атласа (col=0, row=0 — первый кадр)
details.add_frame(100, 100, 32, 32, 0, 0)
details.add_frame(140, 100, 32, 32, 1, 0)

# С произвольными UV-координатами
details.add_uv(200, 200, 64, 64, 0.0, 0.0, 0.25, 0.5)

# Дистанция — объекты дальше не рисуются
details.set_max_distance(500)

# Всё одним draw call
details.draw(cam.x, cam.y, 800, 600)

# Culling ON  → рисует только видимое (FPS стабильный)
# Culling OFF → рисует всё подряд (можно сделать просадку FPS)
details.set_culling(False)
```

## Игровые объекты

```python
# Базовый объект
obj = jl.Object(100, 100, 64, 64)
obj.active = True
obj.set_texture(texture)

# Игрок
player = jl.Player(400, 300, 32, 32)
player.speed = 250.0

# Персонаж
char = jl.Character(200, 200, 32, 48)
char.hp = 100

# GUI-элемент
gui = jl.GUI(10, 10, 200, 30)
gui.text = "Score: 0"

# Мелкая деталь
detail = jl.Detail(500, 400, 16, 16)
detail.alpha = 0.5
```

## Шифрование текстур

```python
# Зашифровать текстуру (создаёт .enc файл)
jl.Texture.encrypt("sprite.png", "my_secret_key")

# Загрузить зашифрованную текстуру
tex = jl.Texture.load_encrypted("sprite.png.enc", "my_secret_key")
```

## Сборка из исходников

```bash
# Windows (MSVC)
python setup.py build

# Linux (GCC)
python setup.py build
```

## Репозиторий

https://github.com/r9441887-collab/jwarol-code-new

## Лицензия

MIT License — Руслан (r9441887@gmail.com)
