Metadata-Version: 2.4
Name: jwarol
Version: 0.15.3
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, игровые объекты, шифрование ассетов, скелетная анимация из Blender, сборка в .exe через PyInstaller.

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

| Функция | Описание |
|---------|----------|
| **Window** | Окно с OpenGL-контекстом (Win32 / X11), `init_graphics()` встроен |
| **Shader** | GLSL-шейдеры из строк/файлов, проверка ошибок, move-семантика |
| **Renderer** | 2D-рендеринг с ортографической проекцией, камерой, culling |
| **Texture** | PNG/JPG/BMP (WIC), процедурные, наложение, шифрование AES-256-CBC |
| **Input** | Клавиатурный ввод (Win32 / X11) |
| **Camera** | Следование за объектом или по координатам, zoom, smooth |
| **Sprite/Animation** | Спрайт-листы, покадровая анимация |
| **DetailGroup** | Батч-рендеринг тысяч объектов за 1 draw call |
| **AnimatedSprite2D** | Скелетная 2D-анимация из Blender |
| **Physics** | Verlet-интеграция, коллизии, STATIC/DYNAMIC/KINEMATIC |
| **Sound** | .wav через winmm (Windows), заглушка на Linux |
| **GameObjects** | Object, Player, Character, GUI, Detail |
| **PyInstaller Build** | Сборка игры в .exe |

## Установка

```bash
pip install jwarol
```

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

```bash
python setup.py build
```

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

```python
import jwarol as jl

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
import jwarol as jl, random

win = jl.Window(1280, 720, "Jwarol Demo")
renderer = jl.Renderer(1280, 720)
clock = jl.Clock()
input = jl.Input()

# Мир физики
world = jl.World()
world.gravity = 1200.0
world.add_body(0, 680, 2000, 40, jl.BodyType.STATIC)  # земля
player_idx = world.add_body(400, 300, 50, 50, jl.BodyType.DYNAMIC)
player_speed = 400.0
jump_force = 700.0

# Камера
cam = jl.Camera(1280, 720)
cam.smooth = 0.1

# 1000 декоративных частиц
details = jl.DetailGroup()
for i in range(1000):
    details.add(random.randint(-1000, 2000),
                random.randint(0, 680), 5, 5,
                random.random(), random.random(), 1.0, 0.6)

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

    pb = world.get_body(player_idx)

    # Управление
    if input.is_down(ord('A')): pb.pos.x -= player_speed * dt
    if input.is_down(ord('D')): pb.pos.x += player_speed * dt
    if input.is_pressed(ord('W')) and pb.pos.y >= 680 - pb.size.y:
        pb.velocity.y = -jump_force

    world.step(dt)

    # Камера
    pb = world.get_body(player_idx)
    cam.follow_pos(pb.pos.x, pb.pos.y)
    cam.update(dt)
    cam.apply_to(renderer)

    # Отрисовка
    win.clear(0.1, 0.1, 0.15)
    details.draw(cam.x, cam.y, 1280, 720)
    renderer.draw_rect(0, 680, 2000, 40, 0.2, 0.8, 0.2)
    pb = world.get_body(player_idx)
    renderer.draw_rect(pb.pos.x, pb.pos.y, 50, 50, 1.0, 0.5, 0.0)
    win.swap_buffers()
```

## Текстуры

```python
tex = jl.Texture("sprite.png")

# Процедурная текстура
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, alpha=0.5)
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)
```

## Физика

`add_body()` возвращает индекс тела. Для изменений используйте `get_body(index)`.

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

idx = world.add_body(300, 0, 50, 50, jl.BodyType.DYNAMIC)
world.add_body(0, 650, 800, 50, jl.BodyType.STATIC)

world.step(dt)

body = world.get_body(idx)
x, y = body.pos.x, body.pos.y
body.velocity.y = -500  # прыжок
body.apply_force(jl.Vec2(100, 0))
```

### BodyType

- `BodyType.DYNAMIC` — движется, реагирует на коллизии
- `BodyType.STATIC` — неподвижен, не двигается при коллизиях
- `BodyType.KINEMATIC` — движется, не реагирует на коллизии

### PhysicsBody свойства

- `pos` (Vec2) — позиция
- `size` (Vec2) — размер
- `velocity` (Vec2) — скорость
- `type` (BodyType) — тип тела
- `enabled` (bool) — активно ли тело
- `apply_force(Vec2)` — добавить силу к скорости

### Всеми телами можно управлять через индексы

```python
idx = world.add_body(100, 100, 32, 32, jl.BodyType.DYNAMIC)
body = world.get_body(idx)
body.pos.x += 10
body.velocity.y = -jump_force
```

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

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

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

    if input.is_down(ord('W')): ...
    if input.is_pressed(ord(' ')): ...
    if input.is_released(ord('A')): ...
```

- `is_down` — зажата
- `is_pressed` — только что нажата
- `is_released` — только что отпущена

## Шейдеры

```python
shader = jl.Shader("#version 330 core\n...", "#version 330 core\n...")
shader.use()

shader = jl.Shader.from_file("vertex.glsl", "fragment.glsl")
shader = jl.Shader.from_single_file("shader.glsl")

try:
    s = jl.Shader.from_file("bad.glsl", "empty.glsl")
except RuntimeError as e:
    print("Shader error:", e)
```

## Камера

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

# Следование за объектом
cam.follow(player)
cam.smooth = 0.1

# Следование за координатами (без привязки к объекту)
cam.follow_pos(player_x, player_y)

cam.zoom = 2.0
cam.update(dt)
cam.apply_to(renderer)

# Ручное управление камерой
cam.x = player_x - 400
cam.y = player_y - 300
```

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

```python
sheet = jl.Texture("player_walk.png")
sprite = jl.Sprite()
sprite.set_texture(sheet, 4, 2)

anim = jl.Animation()
anim.from_range(0, 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)
```

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

```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)
details.add_frame(100, 100, 32, 32, 0, 0)

details.set_max_distance(500)
details.draw(cam.x, cam.y, 800, 600)
```

## Скелетная анимация (AnimatedSprite2D)

```python
hero = jl.AnimatedSprite2D.from_blender("character.json", "character.png")
hero.play("idle")
hero.anim_loop = True

while win.is_open:
    dt = clock.tick()
    hero.update(dt)
    hero.draw(renderer, player.x, player.y, scale=1.0)
```

### Программное создание скелета

```python
skel = jl.SkeletonData()
skel.add_bone("root")
skel.add_bone("body", 0)
skel.add_bone("head", 1)
skel.add_bone("arm_l", 1)
skel.add_bone("arm_r", 1)

skel.bones[1].y = -30
skel.bones[2].y = -30
skel.bones[3].x = -20
skel.bones[4].x = 20

walk = jl.SkeletalAnimation()
walk.name = "walk"
walk.duration = 1.0
walk.loop = True

track = jl.BoneTrack()
track.bone_index = 3
kf1 = jl.BoneKeyframe(); kf1.time = 0.0; kf1.rotation = -30
kf2 = jl.BoneKeyframe(); kf2.time = 0.5; kf2.rotation = 30
kf3 = jl.BoneKeyframe(); kf3.time = 1.0; kf3.rotation = -30
track.keyframes = [kf1, kf2, kf3]
walk.tracks = [track]

sprite = jl.AnimatedSprite2D()
sprite.skeleton = skel
sprite.add_animation(walk)
sprite.set_texture(jl.Texture("character.png"))
sprite.play("walk")
# В цикле:
sprite.update(dt)
sprite.draw(renderer, x, y)
```

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

```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 = jl.GUI(10, 10, 200, 30)
gui.text = "Score: 0"

detail = jl.Detail(500, 400, 16, 16)
detail.alpha = 0.5
```

## Шифрование текстур (AES-256-CBC)

```python
# Зашифровать файл
jl.Texture.encrypt("sprite.png", "my_secret_key")
# → sprite.png.enc

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

## Сборка в .exe (PyInstaller)

```bash
python build_exe.py game.py
python build_exe.py game.py --onefile --icon icon.ico --name "MyGame"
python build_exe.py game.py --assets assets/ --console
```

Результат в папке `dist/`.

## Структура проекта

```
jwarol/
├── include/           # C++ заголовки
│   ├── Window.hpp     # Окно (Win32 / X11)
│   ├── Shader.hpp     # GLSL-шейдеры (move-семантика, деструктор)
│   ├── Renderer.hpp   # 2D-рендерер (ортографическая проекция)
│   ├── Texture.hpp    # Текстуры (PNG/JPG/BMP/WIC)
│   ├── Input.hpp      # Клавиатурный ввод (Win32 / X11)
│   ├── Physics.hpp    # Verlet-физика (STATIC fix, getBody)
│   ├── Sound.hpp      # WAV-звук (winmm)
│   ├── Camera.hpp     # Камера с follow/follow_pos/zoom
│   ├── Sprite.hpp     # Спрайты и анимация
│   ├── DetailGroup.hpp# Батч-рендеринг
│   ├── Anim2D.hpp     # Скелетная 2D-анимация
│   ├── GameObject.hpp # Игровые объекты
│   ├── Math.hpp       # Vec2, Rect, Mat3
│   ├── Clock.hpp      # Дельта-таймер
│   ├── Crypto.hpp     # AES-256-CBC шифрование
│   └── GLFunctions.hpp# OpenGL-функции (кросс-платформа)
├── src/               # C++ исходники
├── tools/
│   └── blender_export.py
├── build_exe.py       # PyInstaller сборщик
├── setup.py
└── README.md
```

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

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

## Лицензия

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