diff --git a/files/maze.m5r b/files/maze.m5r index 2c2ef53..11e5ce9 100644 --- a/files/maze.m5r +++ b/files/maze.m5r @@ -1,88 +1,99 @@ ",self._resize) self._rt.bind("",self._kd) self._rt.bind("",self._ku) - self._rt.bind("", self._mouse_btn) + self._cv.bind("", self._mouse_btn) # bind to canvas, not root! self._rt.bind("",self._esc) + self._rt.bind("", self._toggle_fullscreen) self._rt.bind("", self._mouse_move) - self._mouse_locked = False - self._last_mouse_x = None - self._reset_game() - self._keys=set() self._tick() self._rt.mainloop() def _reset_game(self,level=1): self._level=level - self._map=_gen_maze(level) + self._map=_gen_backrooms(level) self._sz=len(self._map) - self._px,self._py=1.5,1.5 # start position + self._px,self._py=self._sz//2+0.5,self._sz//2+0.5 self._pa=_m.pi/4 self._levelup_msg=0 - def _resize(self,e):self._W,self._H=e.width,e.height + def _resize(self,e): self._W,self._H=e.width,e.height def _mouse_btn(self,e): if self._game_state=="main": - self._game_state="play" - self._lock_mouse(e) + # only start if inside start button area + if self._menu_btn_area: + x0, y0, x1, y1 = self._menu_btn_area + if x0 <= e.x <= x1 and y0 <= e.y <= y1: + self._game_state="play" + self._lock_mouse() elif self._game_state=="pause": - self._lock_mouse(e) - self._game_state="play" - - def _lock_mouse(self,e=None): - self._mouse_locked=True - self._rt.config(cursor="none") - self._cv.grab_set() - if e:self._last_mouse_x=e.x - + self._lock_mouse() + + def _lock_mouse(self): + if not self._mouse_locked: + self._mouse_locked=True + self._rt.config(cursor="none") + self._cv.grab_set() + self._mouse_x_last = None + def _unlock_mouse(self,e=None): self._mouse_locked=False self._rt.config(cursor="") self._cv.grab_release() - self._last_mouse_x=None - + self._mouse_x_last = None + def _mouse_move(self,e): if self._mouse_locked and self._game_state=="play": - if self._last_mouse_x: - self._pa+=(e.x-self._last_mouse_x)*0.005 - self._last_mouse_x=e.x + if self._mouse_x_last is not None: + dx = e.x - self._mouse_x_last + self._pa += dx * 0.0067 + self._mouse_x_last = e.x + else: + self._mouse_x_last = None def _esc(self,e): if self._game_state=="play" and self._mouse_locked: @@ -93,21 +104,23 @@ class _MAZE25D: elif self._game_state=="main": self._rt.destroy() + def _toggle_fullscreen(self,e=None): + self._fullscreen = not self._fullscreen + self._rt.attributes("-fullscreen", self._fullscreen) + def _brighten(self, col, factor): if col.startswith('#') and len(col)==7: r,g,b=int(col[1:3],16),int(col[3:5],16),int(col[5:7],16) r,g,b=min(255,int(r*factor)),min(255,int(g*factor)),min(255,int(b*factor)) return f'#{r:02x}{g:02x}{b:02x}' return col - def _darken(self, col, factor): if col.startswith('#') and len(col)==7: r,g,b=int(col[1:3],16),int(col[3:5],16),int(col[5:7],16) r,g,b=int(r*factor),int(g*factor),int(b*factor) return f'#{r:02x}{g:02x}{b:02x}' return col - - def _draw_3d_box(self,x,y,w,h,d,col,ol='#000000'): + def _draw_3d_box(self,x,y,w,h,d,col,ol='#a0933c'): self._cv.create_rectangle(x,y,x+w,y+h,fill=col,outline=ol,width=2) pts_top=[(x,y),(x+d,y-d),(x+w+d,y-d),(x+w,y)] self._cv.create_polygon(pts_top,fill=self._brighten(col,1.22),outline=ol,width=1) @@ -116,16 +129,10 @@ class _MAZE25D: def _draw_hud(self): y = self._H-80 - self._draw_3d_box(0,y,self._W,80,6,"#161b28","#313c32") - - # Level display - self._cv.create_text(120,y+25,text=f"LEVEL: {self._level}",font=("Consolas",24,"bold"),fill="#00ffcc") - - # Instructions - self._cv.create_text(self._W//2,y+25,text="FIND THE BLUE EXIT!",font=("Consolas",20,"bold"),fill="#ffff44") - - # Size info - self._cv.create_text(self._W-150,y+25,text=f"MAZE SIZE: {self._sz}x{self._sz}",font=("Consolas",16,"bold"),fill="#ff6644") + self._draw_3d_box(0,y,self._W,80,6,"#fef2a0","#d8c944") + self._cv.create_text(120,y+25,text=f"LEVEL: {self._level}",font=("Consolas",24,"bold"),fill="#665100") + self._cv.create_text(self._W//2,y+25,text="FIND THE BLUE EXIT!",font=("Consolas",20,"bold"),fill="#3e79ff") + self._cv.create_text(self._W-150,y+25,text=f"SIZE: {self._sz}x{self._sz}",font=("Consolas",16,"bold"),fill="#d8b144") def _raycast(self,a): x,y = self._px,self._py @@ -134,72 +141,54 @@ class _MAZE25D: x+=dx; y+=dy mx,my=int(x),int(y) if mx<0 or my<0 or mx>=self._sz or my>=self._sz: - return d/20,1,'#666666' + return d/20,1,'#c7bc54' cell = self._map[my][mx] if cell==1: - return d/20,1,'#888888' - elif cell==2: # Blue exit wall - return d/20,1,'#0066ff' + return d/20,1,'#f8ed6c' + elif cell==2: + return d/20,1,'#2979ff' return 18,0,'#000000' def _render_game(self): w,h = self._W,self._H self._cv.delete('all') - - # Sky gradient for i in range(h//2): - b=45+i//12 - sh=f"#{b:02x}{b:02x}{b//2+20:02x}" + b=235-i//7;sh=f"#{b:02x}{b:02x}{(b//2)+85:02x}" self._cv.create_line(0,i,w,i,fill=sh) - - # Floor gradient for i in range(h//2,h): - b=70-(i-h//2)//10 - sh=f"#{b:02x}{b//3:02x}{b//4:02x}" + b=220-(i-h//2)//7;sh=f"#{b:02x}{b:02x}{(b//3)+55:02x}" self._cv.create_line(0,i,w,i,fill=sh) - - # Render walls - rays=250 + rays=270 for i in range(rays): a=self._pa-_m.pi/2.3 + (_m.pi/1.15*i)/(rays-1) d,wall,wall_color=self._raycast(a) d = max(.08, d*_m.cos(a-self._pa)) - hwall=int(h*0.8/d) - - if wall: - # Apply distance shading to wall color - if wall_color == '#0066ff': # Blue exit wall - shade_factor = max(0.3, min(1.0, 1.0/d)) - r,g,b = 0, int(102*shade_factor), int(255*shade_factor) - cc = f"#{r:02x}{g:02x}{b:02x}" - else: # Regular walls - shade=min(255,max(30,int(200/(d+0.8)))) - cc=f"#{shade:02x}{shade:02x}{shade:02x}" + hwall=int(h*0.87/d) + if wall_color=="#2979ff": + r,g,b=41,int(121/max(1,d)),255 + cc=f"#{r:02x}{g:02x}{b:02x}" else: - cc="#000000" - + if wall_color=="#f8ed6c": + shade=min(255,max(170,int(200/(d+0.8)))) + cc=f"#{shade:02x}{shade:02x}{int(shade*0.88):02x}" + else: + cc=wall_color x=int(i*w/rays) self._cv.create_rectangle(x,h//2-hwall//2-10,x+int(w/rays+1),h//2+hwall//2,fill=cc,outline="") - self._draw_hud() - - # Compass/minimap indicator cx,cy=w-80,80 - self._cv.create_oval(cx-30,cy-30,cx+30,cy+30,fill="#333333",outline="#ffffff",width=2) - # Direction arrow + self._cv.create_oval(cx-30,cy-30,cx+30,cy+30,fill="#aaa924",outline="#ffffff",width=2) arrow_x = cx + 20*_m.cos(self._pa) arrow_y = cy + 20*_m.sin(self._pa) - self._cv.create_line(cx,cy,arrow_x,arrow_y,fill="#00ff00",width=3) - self._cv.create_text(cx,cy+45,text="N",font=("Consolas",12,"bold"),fill="#ffffff") - + self._cv.create_line(cx,cy,arrow_x,arrow_y,fill="#2979ff",width=3) + self._cv.create_text(cx,cy+45,text="N",font=("Consolas",12,"bold"),fill="#665100") if self._game_state=="pause": - self._draw_3d_box(w//2-150,h//2-70,300,69,10,"#333333","#242424") - self._cv.create_text(w//2,h//2-37,text="PAUSED",font=("Consolas",28,"bold"),fill="#ffff44") - self._cv.create_text(w//2,h//2+7,text="ESC to resume",font=("Consolas",13,"bold"),fill="#66ccdd") - + self._draw_3d_box(w//2-150,h//2-70,300,69,10,"#23232a","#343434") + self._cv.create_text(w//2,h//2-37,text="PAUSED",font=("Consolas",28,"bold"),fill="#ffee44") + self._cv.create_text(w//2,h//2+7,text="ESC to resume",font=("Consolas",13,"bold"),fill="#2979ff") if self._levelup_msg>0: - self._draw_3d_box(w//2-180,h//2-60,360,50,12,"#0066ff","#ffffff") - self._cv.create_text(w//2,h//2-35,text=f"LEVEL {self._level-1} COMPLETE!",font=("Consolas",18,"bold"),fill="#ffffff") + self._draw_3d_box(w//2-180,h//2-60,360,50,12,"#2979ff","#ffffff") + self._cv.create_text(w//2,h//2-35,text=f"LEVEL {self._level-1} ESCAPED!",font=("Consolas",18,"bold"),fill="#665100") self._levelup_msg-=1 def _tick(self): @@ -210,81 +199,52 @@ class _MAZE25D: self._render_menu() elif self._game_state=="pause": self._render_game() - self._rt.after(30,self._tick) - + self._rt.after(28,self._tick) + def _render_menu(self): w,h=self._W,self._H self._cv.delete('all') - - # Checkered background for y in range(0,h,80): for x in range(0,w,80): - cc="#2a3344" if (x//80+y//80)%2==0 else "#1a2233" + cc="#16181d" if (x//80+y//80)%2==0 else "#232333" self._cv.create_rectangle(x,y,x+80,y+80,fill=cc,width=0) - - # Title - self._draw_3d_box(w//2-200,h//3-100,400,80,25,"#0066ff","#003388") - self._cv.create_text(w//2,h//3-60,text="MAZE ESCAPE",fill="#ffffff",font=("Consolas",36,"bold")) - - # Subtitle - self._draw_3d_box(w//2-180,h//3,360,50,15,"#44aa44","#226622") - self._cv.create_text(w//2,h//3+25,text="2.5D ADVENTURE",fill="#ffffff",font=("Consolas",20,"bold")) - - # Start button - self._draw_3d_box(w//2-120,h//2+80,240,55,12,"#ff6644","#ffffff") - self._cv.create_text(w//2,h//2+107,text="START MAZE",fill="#ffffff",font=("Consolas",18,"bold")) - - # Instructions - self._cv.create_text(w//2,h-80,text="Find the BLUE WALL to escape each level!",font=("Consolas",16),fill="#aaccff") - self._cv.create_text(w//2,h-50,text="WASD: Move | Mouse: Look | Click: Lock Camera",font=("Consolas",14),fill="#88aacc") - + self._draw_3d_box(w//2-200,h//3-100,400,80,25,"#232333","#111833") + self._cv.create_text(w//2,h//3-60,text="BACKROOMS MAZE",fill="#d8d8ef",font=("Consolas",36,"bold")) + self._draw_3d_box(w//2-180,h//3,360,50,15,"#282848","#45455a") + self._cv.create_text(w//2,h//3+25,text="2.5D LIMINAL ADVENTURE",fill="#bcbcd2",font=("Consolas",21,"bold")) + # Start button area, properly recorded for click test + btn_x0,btn_y0=w//2-120,h//2+80 + btn_x1,btn_y1=w//2+120,h//2+135 + self._menu_btn_area = (btn_x0,btn_y0,btn_x1,btn_y1) + self._draw_3d_box(btn_x0,btn_y0,240,55,12,"#1199cc","#ffffff") + self._cv.create_text(w//2,h//2+107,text="START",fill="#ffffff",font=("Consolas",18,"bold")) + self._cv.create_text(w//2,h-78,text="Navigate the yellow maze. Find the BLUE EXIT!",font=("Consolas",16),fill="#2979ff") + self._cv.create_text(w//2,h-50,text="WASD: Move | Mouse: Look | Click: Lock Camera | F11: Fullscreen",font=("Consolas",14),fill="#bcbcd2") def _move_smooth(self,dx,dy): def is_blocked(x,y): mx,my=int(x),int(y) return mx<0 or my<0 or mx>=self._sz or my>=self._sz or self._map[my][mx]==1 - nx,ny = self._px+dx, self._py+dy - if not is_blocked(nx,ny): - self._px, self._py = nx,ny + if not is_blocked(nx,ny): self._px,self._py=nx,ny else: - # Try sliding along walls - if not is_blocked(self._px,ny): - self._py=ny - elif not is_blocked(nx,self._py): - self._px=nx - + if not is_blocked(self._px,ny): self._py=ny + elif not is_blocked(nx,self._py): self._px=nx def _step(self): - # Movement spd,dx,dy=0.12,0,0 - if 'w' in self._keys: - dx+=_m.cos(self._pa)*spd - dy+=_m.sin(self._pa)*spd - if 's' in self._keys: - dx-=_m.cos(self._pa)*spd*.8 - dy-=_m.sin(self._pa)*spd*.8 - if 'a' in self._keys: - dx+=_m.cos(self._pa-_m.pi/2)*spd*.7 - dy+=_m.sin(self._pa-_m.pi/2)*spd*.7 - if 'd' in self._keys: - dx+=_m.cos(self._pa+_m.pi/2)*spd*.7 - dy+=_m.sin(self._pa+_m.pi/2)*spd*.7 - + if 'w' in self._keys: dx+=_m.cos(self._pa)*spd; dy+=_m.sin(self._pa)*spd + if 's' in self._keys: dx-=_m.cos(self._pa)*spd*.8; dy-=_m.sin(self._pa)*spd*.8 + if 'a' in self._keys: dx+=_m.cos(self._pa-_m.pi/2)*spd*.7; dy+=_m.sin(self._pa-_m.pi/2)*spd*.7 + if 'd' in self._keys: dx+=_m.cos(self._pa+_m.pi/2)*spd*.7; dy+=_m.sin(self._pa+_m.pi/2)*spd*.7 self._move_smooth(dx,dy) - - # Check if player reached the blue exit wall - mx, my = int(self._px), int(self._py) - - # Check surrounding cells for the exit (blue wall) + mx,my=int(self._px),int(self._py) for check_x in range(max(0, mx-1), min(self._sz, mx+2)): for check_y in range(max(0, my-1), min(self._sz, my+2)): if self._map[check_y][check_x] == 2: - # Close enough to exit - distance = _m.sqrt((self._px - (check_x+0.5))**2 + (self._py - (check_y+0.5))**2) + distance = math.sqrt((self._px - (check_x+0.5))**2 + (self._py - (check_y+0.5))**2) if distance < 1.2: self._reset_game(self._level + 1) - self._levelup_msg = 60 + self._levelup_msg = 80 return - def _kd(self,e): if self._game_state=="play" and self._mouse_locked: self._keys.add(e.keysym.lower()) @@ -292,36 +252,37 @@ class _MAZE25D: self._pa-=_m.pi/20 if e.keysym.lower()=='right': self._pa+=_m.pi/20 - + if e.keysym.lower()=="f11": + self._toggle_fullscreen() def _ku(self,e): self._keys.discard(e.keysym.lower()) -_MAZE25D() +_BACKROOMS() ?> -int main(){ std::cout<<"MAZE ESCAPE 2.5D M5RCode"<