added basic map rendering
This commit is contained in:
		
							
								
								
									
										10
									
								
								main.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								main.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,10 @@
 | 
				
			|||||||
 | 
					from src.editor import Editor
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def main():
 | 
				
			||||||
 | 
					    editor = Editor()
 | 
				
			||||||
 | 
					    editor.mainloop()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					if __name__ == '__main__':
 | 
				
			||||||
 | 
					    main()
 | 
				
			||||||
							
								
								
									
										0
									
								
								src/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								src/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										176
									
								
								src/editor.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										176
									
								
								src/editor.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,176 @@
 | 
				
			|||||||
 | 
					from math import floor
 | 
				
			||||||
 | 
					import os
 | 
				
			||||||
 | 
					from typing import Optional
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import pygame
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Editor:
 | 
				
			||||||
 | 
					    WIDTH = 800
 | 
				
			||||||
 | 
					    HEIGHT = 600
 | 
				
			||||||
 | 
					    MAP_SIZE = 1024
 | 
				
			||||||
 | 
					    MAPS_DIR = "/tmp/lycacraft_maps"
 | 
				
			||||||
 | 
					    MIN_ZOOM = 0.25
 | 
				
			||||||
 | 
					    MAX_ZOOM = 4
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __init__(self):
 | 
				
			||||||
 | 
					        self.width: int = self.WIDTH
 | 
				
			||||||
 | 
					        self.height: int = self.HEIGHT
 | 
				
			||||||
 | 
					        self.win: pygame.Surface = pygame.display.set_mode([self.width, self.height], pygame.RESIZABLE)
 | 
				
			||||||
 | 
					        self.center: list[int] = [0, 0]
 | 
				
			||||||
 | 
					        self.zoom: float = 1
 | 
				
			||||||
 | 
					        self.map_size: int = self.MAP_SIZE
 | 
				
			||||||
 | 
					        self.running: bool = False
 | 
				
			||||||
 | 
					        self.cache: dict[float, dict[tuple[int, int], pygame.Surface]] = {}
 | 
				
			||||||
 | 
					        self.load_maps()
 | 
				
			||||||
 | 
					        self.clock: pygame.time.Clock = pygame.time.Clock()
 | 
				
			||||||
 | 
					        self.visible_maps_count: int = 0
 | 
				
			||||||
 | 
					        self.drag_pos: Optional[tuple[int, int]] = None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def load_maps(self) -> None:
 | 
				
			||||||
 | 
					        cache = {}
 | 
				
			||||||
 | 
					        for path in os.listdir(self.MAPS_DIR):
 | 
				
			||||||
 | 
					            fullpath = os.path.join(self.MAPS_DIR, path)
 | 
				
			||||||
 | 
					            name, x, y = path.split(".")[0].split("_")
 | 
				
			||||||
 | 
					            cache[(int(x), int(y))] = pygame.image.load(fullpath)
 | 
				
			||||||
 | 
					        self.cache = {
 | 
				
			||||||
 | 
					            self.zoom: cache
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def resize_maps(self) -> None:
 | 
				
			||||||
 | 
					        if self.zoom in self.cache:
 | 
				
			||||||
 | 
					            return
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        cache = {}
 | 
				
			||||||
 | 
					        for pos, img in self.cache[1].items():
 | 
				
			||||||
 | 
					            cache[pos] = pygame.transform.scale_by(img, self.zoom)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.cache[self.zoom] = cache
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def get_map(self, x: int, y: int) -> Optional[pygame.Surface]:
 | 
				
			||||||
 | 
					        cache = self.cache.get(self.zoom, {})
 | 
				
			||||||
 | 
					        pos = (x, y)
 | 
				
			||||||
 | 
					        if pos not in cache:
 | 
				
			||||||
 | 
					            if pos not in self.cache[1]:
 | 
				
			||||||
 | 
					                return None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            img = self.cache[1][pos]
 | 
				
			||||||
 | 
					            cache[pos] = pygame.transform.scale_by(img, self.zoom)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return cache[pos]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def mainloop(self) -> None:
 | 
				
			||||||
 | 
					        self.running = True
 | 
				
			||||||
 | 
					        while self.running:
 | 
				
			||||||
 | 
					            pygame.display.set_caption(f"Lycacraft Map Editor - {self.clock.get_fps():.2f}fps - {self.visible_maps_count} maps rendered")
 | 
				
			||||||
 | 
					            self.process_events()
 | 
				
			||||||
 | 
					            self.render()
 | 
				
			||||||
 | 
					            self.clock.tick(30)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def process_events(self) -> None:
 | 
				
			||||||
 | 
					        events = pygame.event.get()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        for event in events:
 | 
				
			||||||
 | 
					            if event.type == pygame.QUIT:
 | 
				
			||||||
 | 
					                self.running = False
 | 
				
			||||||
 | 
					            elif event.type == pygame.WINDOWRESIZED:
 | 
				
			||||||
 | 
					                self.width = event.x
 | 
				
			||||||
 | 
					                self.height = event.y
 | 
				
			||||||
 | 
					            elif event.type == pygame.KEYDOWN:
 | 
				
			||||||
 | 
					                if event.key == pygame.K_ESCAPE:
 | 
				
			||||||
 | 
					                    self.running = False
 | 
				
			||||||
 | 
					                elif event.key == pygame.K_PAGEUP:
 | 
				
			||||||
 | 
					                    self.zoom_in()
 | 
				
			||||||
 | 
					                elif event.key == pygame.K_PAGEDOWN:
 | 
				
			||||||
 | 
					                    self.zoom_out()
 | 
				
			||||||
 | 
					            elif event.type == pygame.MOUSEBUTTONDOWN:
 | 
				
			||||||
 | 
					                if event.button == 2:
 | 
				
			||||||
 | 
					                    self.drag_pos = event.pos
 | 
				
			||||||
 | 
					            elif event.type == pygame.MOUSEBUTTONUP:
 | 
				
			||||||
 | 
					                if event.button == 2:
 | 
				
			||||||
 | 
					                    self.drag_pos = None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        keys = pygame.key.get_pressed()
 | 
				
			||||||
 | 
					        if keys[pygame.K_LEFT]:
 | 
				
			||||||
 | 
					            self.center[0] -= 4 / self.zoom
 | 
				
			||||||
 | 
					        if keys[pygame.K_RIGHT]:
 | 
				
			||||||
 | 
					            self.center[0] += 4 / self.zoom
 | 
				
			||||||
 | 
					        if keys[pygame.K_UP]:
 | 
				
			||||||
 | 
					            self.center[1] -= 4 / self.zoom
 | 
				
			||||||
 | 
					        if keys[pygame.K_DOWN]:
 | 
				
			||||||
 | 
					            self.center[1] += 4 / self.zoom
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        mbtns = pygame.mouse.get_pressed()
 | 
				
			||||||
 | 
					        mpos = pygame.mouse.get_pos()
 | 
				
			||||||
 | 
					        if mbtns[1]:
 | 
				
			||||||
 | 
					            dx = mpos[0] - self.drag_pos[0]
 | 
				
			||||||
 | 
					            dy = mpos[1] - self.drag_pos[1]
 | 
				
			||||||
 | 
					            self.center[0] -= dx / self.zoom
 | 
				
			||||||
 | 
					            self.center[1] -= dy / self.zoom
 | 
				
			||||||
 | 
					            self.drag_pos = mpos
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def render(self) -> None:
 | 
				
			||||||
 | 
					        self.win.fill((0, 0, 0))
 | 
				
			||||||
 | 
					        off_x = self.center[0] % self.MAP_SIZE
 | 
				
			||||||
 | 
					        off_y = self.center[1] % self.MAP_SIZE
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        w2 = self.width / 2
 | 
				
			||||||
 | 
					        h2 = self.height / 2
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        x0 = floor(self.center[0] - w2 / self.zoom)
 | 
				
			||||||
 | 
					        y0 = floor(self.center[1] - h2 / self.zoom)
 | 
				
			||||||
 | 
					        x1 = floor(self.center[0] + w2 / self.zoom)
 | 
				
			||||||
 | 
					        y1 = floor(self.center[1] + h2 / self.zoom)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        mx0 = floor(x0 / self.MAP_SIZE)
 | 
				
			||||||
 | 
					        my0 = floor(y0 / self.MAP_SIZE)
 | 
				
			||||||
 | 
					        mx1 = floor(x1 / self.MAP_SIZE)
 | 
				
			||||||
 | 
					        my1 = floor(y1 / self.MAP_SIZE)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        h_maps = mx1 - mx0 + 1
 | 
				
			||||||
 | 
					        v_maps = my1 - my0 + 1
 | 
				
			||||||
 | 
					        cx = floor(self.center[0] / self.MAP_SIZE)
 | 
				
			||||||
 | 
					        cy = floor(self.center[1] / self.MAP_SIZE)
 | 
				
			||||||
 | 
					        self.visible_maps_count = h_maps * v_maps
 | 
				
			||||||
 | 
					        ox = w2 + (mx0 - cx) * self.map_size - off_x * self.zoom
 | 
				
			||||||
 | 
					        oy = h2 + (my0 - cy) * self.map_size - off_y * self.zoom
 | 
				
			||||||
 | 
					        # cache = self.cache[self.zoom]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        for y in range(v_maps):
 | 
				
			||||||
 | 
					            for x in range(h_maps):
 | 
				
			||||||
 | 
					                # map_pos = (mx0 + x, my0 + y)
 | 
				
			||||||
 | 
					                map_image = self.get_map(mx0 + x, my0 + y)
 | 
				
			||||||
 | 
					                # if map_pos not in cache:
 | 
				
			||||||
 | 
					                if map_image is None:
 | 
				
			||||||
 | 
					                    continue
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                # map_image = cache[map_pos]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                sx = ox + x * self.map_size
 | 
				
			||||||
 | 
					                sy = oy + y * self.map_size
 | 
				
			||||||
 | 
					                ex = sx + self.map_size
 | 
				
			||||||
 | 
					                ey = sy + self.map_size
 | 
				
			||||||
 | 
					                sx2, ex2 = min(self.width, max(0, sx)), min(self.width, max(0, ex))
 | 
				
			||||||
 | 
					                sy2, ey2 = min(self.height, max(0, sy)), min(self.height, max(0, ey))
 | 
				
			||||||
 | 
					                """self.win.blit(map_image, [
 | 
				
			||||||
 | 
					                    ox + x * self.map_size,
 | 
				
			||||||
 | 
					                    oy + y * self.map_size,
 | 
				
			||||||
 | 
					                ])"""
 | 
				
			||||||
 | 
					                self.win.blit(map_image, [sx2, sy2], [sx2 - sx, sy2 - sy, ex2 - sx2, ey2 - sy2])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        for y in range(v_maps):
 | 
				
			||||||
 | 
					            for x in range(h_maps):
 | 
				
			||||||
 | 
					                pygame.draw.rect(self.win, (255, 0, 0), [ox + x * self.map_size, oy + y * self.map_size, self.map_size, self.map_size], 1)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        pygame.display.flip()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def set_zoom(self, zoom: float) -> None:
 | 
				
			||||||
 | 
					        self.zoom = max(self.MIN_ZOOM, min(self.MAX_ZOOM, zoom))
 | 
				
			||||||
 | 
					        self.map_size = self.MAP_SIZE * self.zoom
 | 
				
			||||||
 | 
					        # self.resize_maps()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def zoom_in(self) -> None:
 | 
				
			||||||
 | 
					        self.set_zoom(self.zoom * 2)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def zoom_out(self) -> None:
 | 
				
			||||||
 | 
					        self.set_zoom(self.zoom / 2)
 | 
				
			||||||
		Reference in New Issue
	
	Block a user