diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..7e84bfb --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +__pycache__/ +.idea/ +*.swap diff --git a/experiments/window_gtk.py b/experiments/window_gtk.py new file mode 100644 index 0000000..6e81af0 --- /dev/null +++ b/experiments/window_gtk.py @@ -0,0 +1,57 @@ +import gi +gi.require_version("Gtk", "3.0") +from gi.repository import Gtk, Gdk, GdkPixbuf, cairo + + +class VPetWindow(Gtk.Window): + def __init__(self): + super().__init__() + + # Fensterkonfiguration + self.set_title("vPet") + self.set_default_size(48, 48) + self.set_app_paintable(True) + self.set_decorated(False) + self.set_keep_above(True) + self.set_accept_focus(False) + + # Entferne den Fensterschatten und wende RGBA-Visual an + screen = self.get_screen() + visual = screen.get_rgba_visual() + if visual: + self.set_visual(visual) + + # Mausereignisse ignorieren + self.connect("screen-changed", self.on_screen_changed) + self.set_app_paintable(True) + self.on_screen_changed(None) + + # Lade und zeige das Sprite (virtuelles Haustier) + self.image = Gtk.Image() + pixbuf = GdkPixbuf.Pixbuf.new_from_file("vpet_sprite.png") # Ersetze mit deinem Sprite + self.image.set_from_pixbuf(pixbuf) + self.add(self.image) + + self.show_all() + + def on_screen_changed(self, screen): + gdk_window = self.get_window() + if gdk_window: + # Eingabeform auf das Sprite beschränken + region = Gdk.cairo_region_create_from_surface(self.get_mask()) + gdk_window.input_shape_combine_region(region) + + def get_mask(self): + # Maske für die Eingabe basierend auf der Sprite-Form erstellen + pixbuf = self.image.get_pixbuf() + surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, pixbuf.get_width(), pixbuf.get_height()) + cr = cairo.Context(surface) + Gdk.cairo_set_source_pixbuf(cr, pixbuf, 0, 0) + cr.paint() + return surface + + +if __name__ == "__main__": + win = VPetWindow() + win.connect("destroy", Gtk.main_quit) + Gtk.main() diff --git a/experiments/window_opengl.py b/experiments/window_opengl.py new file mode 100644 index 0000000..39c2b0f --- /dev/null +++ b/experiments/window_opengl.py @@ -0,0 +1,90 @@ +import pygame +from OpenGL.GL import * +from OpenGL.GLU import * +from OpenGL.GLUT import * +import sys +import os + + +# Pet Attribute +class VirtualPet: + def __init__(self): + self.happiness = 50 + self.hunger = 50 + + def feed(self): + self.hunger -= 10 + self.happiness += 5 + + def pet(self): + self.happiness += 10 + + def update(self): + self.hunger += 0.1 + self.happiness -= 0.05 + + +# OpenGL-Setup +def init_opengl(): + glEnable(GL_BLEND) # Aktiviert Transparenz + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA) + glEnable(GL_DEPTH_TEST) + glClearColor(0.2, 0.6, 0.8, 0.0) # Himmelblau mit Transparenz (Alpha = 0.5) + gluPerspective(45, (800 / 600), 0.1, 50.0) + + +# Pet zeichnen (Würfel als Platzhalter) +def draw_pet(): + glPushMatrix() + glColor4f(0, 0, 0, 0.2) + glTranslatef(0.0, 0.0, -5) + glRotatef(pygame.time.get_ticks() * 0.05, 0, 1, 0) # Rotation + glutWireCube(1) # Zeichnet den Würfel + glPopMatrix() + + +# Hauptprogramm +def main(): + # Setze Umgebungsvariablen für transparentes Fenster + os.environ['SDL_VIDEODRIVER'] = 'x11' + os.environ['SDL_VIDEO_WINDOW_POS'] = '0,0' + os.environ['SDL_VIDEO_CENTERED'] = '1' + + pygame.init() + display = pygame.display.set_mode((48, 48), pygame.DOUBLEBUF | pygame.OPENGL | pygame.NOFRAME | pygame.SRCALPHA) + pygame.display.set_caption("Transparent Virtual Pet") + clock = pygame.time.Clock() + display.set + + pet = VirtualPet() + glutInit() # Initialisiert GLUT + init_opengl() + + # Setze Transparenz für das Fenster + # pygame.display.set_mode((800, 600), pygame.OPENGL | pygame.DOUBLEBUF | pygame.NOFRAME | pygame.SRCALPHA) + + running = True + while running: + for event in pygame.event.get(): + if event.type == pygame.QUIT: + running = False + elif event.type == pygame.KEYDOWN: + if event.key == pygame.K_f: + pet.feed() + elif event.key == pygame.K_p: + pet.pet() + + pet.update() + + # OpenGL Rendering + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) + draw_pet() + pygame.display.flip() + clock.tick(60) + + pygame.quit() + sys.exit() + + +if __name__ == "__main__": + main() diff --git a/main.py b/main.py new file mode 100644 index 0000000..e69de29 diff --git a/os_linux.py b/os_linux.py new file mode 100644 index 0000000..e69de29 diff --git a/os_windows.py b/os_windows.py new file mode 100644 index 0000000..e69de29 diff --git a/pets_sprites/demonite/demonite_float_correct.png b/pets_sprites/demonite/demonite_float_correct.png new file mode 100644 index 0000000..65d8f48 Binary files /dev/null and b/pets_sprites/demonite/demonite_float_correct.png differ diff --git a/pets_sprites/demonite/demonite_idle1.png b/pets_sprites/demonite/demonite_idle1.png new file mode 100644 index 0000000..e4b7a79 Binary files /dev/null and b/pets_sprites/demonite/demonite_idle1.png differ diff --git a/pets_sprites/demonite/demonite_idle2.png b/pets_sprites/demonite/demonite_idle2.png new file mode 100644 index 0000000..d8230ef Binary files /dev/null and b/pets_sprites/demonite/demonite_idle2.png differ diff --git a/pets_sprites/demonite/demonite_idle3.png b/pets_sprites/demonite/demonite_idle3.png new file mode 100644 index 0000000..cec3f58 Binary files /dev/null and b/pets_sprites/demonite/demonite_idle3.png differ diff --git a/pets_sprites/demonite/demonite_moving_right1.png b/pets_sprites/demonite/demonite_moving_right1.png new file mode 100644 index 0000000..24eabd5 Binary files /dev/null and b/pets_sprites/demonite/demonite_moving_right1.png differ diff --git a/pets_sprites/demonite/demonite_moving_right2.png b/pets_sprites/demonite/demonite_moving_right2.png new file mode 100644 index 0000000..93994b8 Binary files /dev/null and b/pets_sprites/demonite/demonite_moving_right2.png differ diff --git a/pets_sprites/demonite/heart_heart.png b/pets_sprites/demonite/heart_heart.png new file mode 100644 index 0000000..ec3c774 Binary files /dev/null and b/pets_sprites/demonite/heart_heart.png differ diff --git a/vpet.py b/vpet.py new file mode 100644 index 0000000..386cd8d --- /dev/null +++ b/vpet.py @@ -0,0 +1,121 @@ +from PIL import Image + + +class Rotate: + down = 0.0 + right = 90.0 + up = 180.0 + left = 270.0 + + +class Dialogs: + dreaming = [] + + +class Frames: + """These strings do match to the PNGs of the VPet which are representation the frames of an animation""" + idle_1 = 'idle1' + idle_2 = 'idle2' + idle_3 = 'idle3' + move_1 = 'moving_1' + move_2 = 'moving_2' + hearts_1 = 'heart' + floating = 'floating' + + +class Animations: + @staticmethod + def idle_1(rotation: float): + pass + + @staticmethod + def move_right(rotation: float): + pass + + @staticmethod + def move_left(rotation: float): + pass + + @staticmethod + def move_up(rotation: float): + pass + + @staticmethod + def move_down(rotation: float): + pass + + @staticmethod + def sleep(rotation: float): + pass + + @staticmethod + def wakeup(rotation: float): + pass + + +class VPet: + def __init__(self, x=0, y=0, screen=1): + self.screen = screen + self.x_postion = x + self.y_postion = y + self.rotation = Rotate.up + + self.is_idle = True + self.is_sleeping = False + self.is_dreaming = False + self.is_moving = False + self.is_talking = False + self.is_thinking = False + + def reset_activity_to_idle(self): + self.is_idle = True + self.is_sleeping = False + self.is_dreaming = False + self.is_moving = False + self.is_talking = False + self.is_thinking = False + + def choose_walk_destination(self): + """Decide, where the VPet should move to""" + pass + + def choose_activity(self): + """Decide, what kind of activity should be done""" + pass + + def move(self, x: int, y: int): + """Move the VPet to xy""" + self.is_moving = True + + def sleep(self): + """Set the VPet asleep""" + self.reset_activity_to_idle() + self.is_sleeping = True + Animations.sleep(rotation=self.rotation) + + def wakeup(self): + """Wake up the VPet from sleep""" + if not self.is_sleeping: return + Animations.wakeup(rotation=self.rotation) + self.reset_activity_to_idle() + + def blink_eyes(self): + """Make the VPet blink""" + pass + + def detect_orientation(self): + """Detect the desired rotation based on the postion on the screen, like left border, bottom, etc""" + pass + + def talk(self, message: str): + """Make the VPet talk to the user via a speech bubble""" + pass + + def think(self): + """Make the VPet think""" + pass + + def dream(self): + """Make the VPet dream""" + pass + diff --git a/window_qt.py b/window_qt.py new file mode 100755 index 0000000..576c3d0 --- /dev/null +++ b/window_qt.py @@ -0,0 +1,135 @@ +#!/bin/python3 + +import datetime +import time +import asyncio + +from PyQt5.QtWidgets import QOpenGLWidget +from PyQt5.QtWidgets import QMainWindow +from PyQt5.QtWidgets import QApplication +from PyQt5.QtCore import Qt +from OpenGL.GL import * +from OpenGL.GLU import gluOrtho2D +from PIL import Image + +from vpet import Rotate + + +class TransparentGLWidget(QOpenGLWidget): + def __init__(self, image_path): + super().__init__() + self.image_path = image_path + self.texture = None + self.image_width = 1 + self.image_height = 1 + + def initializeGL(self): + glEnable(GL_BLEND) + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA) + glEnable(GL_DEPTH_TEST) + glClearColor(0.0, 0.0, 0.0, 0.0) + + # Textur init + self.texture, self.image_width, self.image_height = self.load_texture(self.image_path) + + def resizeGL(self, w, h): + glViewport(0, 0, w, h) + glMatrixMode(GL_PROJECTION) + glLoadIdentity() + gluOrtho2D(0, w, 0, h) # Orthografische Projektion für 2D + glMatrixMode(GL_MODELVIEW) + + def paintGL(self): + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) + glLoadIdentity() + glPushMatrix() # Matrix speichern + self.draw_textured_quad() + glPopMatrix() # Matrix wiederherstellen + + def load_texture(self, image_path): + img = Image.open(image_path).convert("RGBA") + img = img.rotate(angle=Rotate.up) + img_data = img.tobytes() + width, height = img.size + + # Create OpenGL texture + texture_id = glGenTextures(1) + glBindTexture(GL_TEXTURE_2D, texture_id) + + # Set texture parameters + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE) + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE) + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR) + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR) + + # Load texture + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, img_data) + return texture_id, width, height + + def draw_textured_quad(self): + glEnable(GL_TEXTURE_2D) + glBindTexture(GL_TEXTURE_2D, self.texture) + + width, height = self.width(), self.height() + + glBegin(GL_QUADS) + glTexCoord2f(0.0, 0.0) + glVertex2f(0, 0) # Down left corner + glTexCoord2f(1.0, 0.0) + glVertex2f(width, 0) # Down right corner + glTexCoord2f(1.0, 1.0) + glVertex2f(width, height) # Up right corner + glTexCoord2f(0.0, 1.0) + glVertex2f(0, height) # Up left corner + glEnd() + + glDisable(GL_TEXTURE_2D) + + +class MainWindow(QMainWindow): + def __init__(self, image_path): + super().__init__() + # self.setWindowFlags(Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint) + self.setWindowFlags(Qt.WindowStaysOnTopHint) + self.setAttribute(Qt.WA_TranslucentBackground) + self.setCentralWidget(TransparentGLWidget(image_path)) + self.click_event_time = 0 + + img = Image.open(image_path) + self.resize(img.width, img.height) + + async def loop(self): + self.move(0, 0) + self.show() + print('Loop') + + def mousePressEvent(self, event): + self.click_event_time = time.time() + if event.button() == Qt.LeftButton: + self.drag_position = event.globalPos() + + def mouseReleaseEvent(self, event): + time_delta = time.time() - self.click_event_time + if time_delta <= 0.2: + if event.button() == Qt.LeftButton: + print('Click left') + if event.button() == Qt.RightButton: + print('Click right') + self.click_event_time = 0 + + def mouseMoveEvent(self, event): + self.click_event_time = 0 + if event.buttons() == Qt.LeftButton: + delta = event.globalPos() - self.drag_position + self.move(self.pos() + delta) + self.drag_position = event.globalPos() + + +async def start(): + app = QApplication([]) + image_path = 'pets_sprites/demonite/demonite_idle1.png' # Ersetze dies mit deinem Bildpfad + window = MainWindow(image_path) + await window.loop() + app.exec_() + +asyncio.run(start())