mirror of
https://github.com/m4rcel-lol/m5rcode.git
synced 2025-12-06 19:13:57 +05:30
Update and rename doom.m5r to maze.m5r
This commit is contained in:
committed by
GitHub
parent
f4ea07c4bf
commit
569cb28439
504
files/doom.m5r
504
files/doom.m5r
@@ -1,504 +0,0 @@
|
|||||||
<?py
|
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
# M5RCode Python Block: OBFUSCATED - Full 2.5D DOOM Game
|
|
||||||
|
|
||||||
import tkinter as _tk
|
|
||||||
import math as _m
|
|
||||||
import random as _r
|
|
||||||
|
|
||||||
_MW,_MH=20,20
|
|
||||||
_FOV=_m.pi/2.8
|
|
||||||
_RAYS=400
|
|
||||||
|
|
||||||
# Map: 1=wall, 0=empty, 2=door, 3=enemy, 4=pickup
|
|
||||||
_MAP=[
|
|
||||||
[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],
|
|
||||||
[1,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,3,1],
|
|
||||||
[1,0,1,1,1,0,0,0,1,1,1,0,0,1,1,1,1,0,0,1],
|
|
||||||
[1,0,1,4,1,0,0,0,0,0,0,0,0,1,0,0,1,0,0,1],
|
|
||||||
[1,0,1,0,1,0,1,1,1,1,1,1,0,1,0,0,2,0,0,1],
|
|
||||||
[1,0,0,0,0,0,1,3,0,0,0,1,0,0,0,0,1,0,0,1],
|
|
||||||
[1,0,1,1,1,0,1,0,0,0,0,1,0,1,1,1,1,0,3,1],
|
|
||||||
[1,0,0,0,0,0,2,0,0,0,0,2,0,0,0,0,0,0,0,1],
|
|
||||||
[1,1,1,1,0,0,1,0,0,0,0,1,0,0,1,1,1,1,1,1],
|
|
||||||
[1,0,0,0,0,0,1,0,0,0,0,1,0,0,0,0,0,0,0,1],
|
|
||||||
[1,0,1,1,1,0,1,1,1,2,1,1,1,0,1,1,1,1,0,1],
|
|
||||||
[1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1],
|
|
||||||
[1,0,1,0,1,1,1,0,0,3,0,0,1,1,1,0,0,1,0,1],
|
|
||||||
[1,0,0,0,1,4,1,0,0,0,0,0,1,4,1,0,0,0,0,1],
|
|
||||||
[1,0,1,0,1,1,1,0,1,1,1,0,1,1,1,0,1,0,3,1],
|
|
||||||
[1,0,1,0,0,0,0,0,1,0,1,0,0,0,0,0,1,0,0,1],
|
|
||||||
[1,0,1,1,1,1,1,0,1,0,1,0,1,1,1,1,1,1,0,1],
|
|
||||||
[1,0,0,0,0,0,0,0,2,0,2,0,0,0,0,0,0,0,0,1],
|
|
||||||
[1,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,1],
|
|
||||||
[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1]
|
|
||||||
]
|
|
||||||
|
|
||||||
class _DOOM25D:
|
|
||||||
def __init__(self):
|
|
||||||
self._rt=_tk.Tk()
|
|
||||||
self._rt.title('DOOM 2.5D M5RCode')
|
|
||||||
self._cv=_tk.Canvas(self._rt,bg='#000',highlightthickness=0)
|
|
||||||
self._cv.pack(fill='both',expand=True)
|
|
||||||
self._rt.focus_set()
|
|
||||||
self._rt.update()
|
|
||||||
self._width=self._cv.winfo_width()
|
|
||||||
self._height=self._cv.winfo_height()
|
|
||||||
self._cv.bind('<Configure>',self._resize)
|
|
||||||
|
|
||||||
self._mouse_locked=False
|
|
||||||
self._game_state="main"
|
|
||||||
self._rt.bind('<Button-1>',self._mousebtn)
|
|
||||||
self._rt.bind('<Escape>',self._esc)
|
|
||||||
self._rt.bind('<KeyPress>',self._kd)
|
|
||||||
self._rt.bind('<KeyRelease>',self._ku)
|
|
||||||
self._rt.bind('<Motion>',self._mouse_look)
|
|
||||||
|
|
||||||
self._px,self._py=1.5,1.5
|
|
||||||
self._pa=0.0
|
|
||||||
self._hp=100
|
|
||||||
self._ammo=60
|
|
||||||
self._armor=0
|
|
||||||
self._score=0
|
|
||||||
self._weapon_frame=0
|
|
||||||
self._muzzle_flash=0
|
|
||||||
self._pain_flash=0
|
|
||||||
|
|
||||||
self._doors={}
|
|
||||||
self._enemies=[]
|
|
||||||
self._pickups=[]
|
|
||||||
self._init_world()
|
|
||||||
|
|
||||||
self._keys=set()
|
|
||||||
self._last_mouse_x=int(self._width//2)
|
|
||||||
self._run()
|
|
||||||
self._rt.mainloop()
|
|
||||||
|
|
||||||
def _init_world(self):
|
|
||||||
self._doors.clear()
|
|
||||||
self._enemies.clear()
|
|
||||||
self._pickups.clear()
|
|
||||||
for y in range(_MH):
|
|
||||||
for x in range(_MW):
|
|
||||||
if _MAP[y][x]==2:
|
|
||||||
self._doors[(x,y)]={'open':0.0,'opening':False}
|
|
||||||
elif _MAP[y][x]==3:
|
|
||||||
self._enemies.append({
|
|
||||||
'x':x+0.5,'y':y+0.5,'hp':75,
|
|
||||||
'ai_timer':0,'type':_r.randint(0,2),
|
|
||||||
'moving':False,'target_x':x+0.5,'target_y':y+0.5
|
|
||||||
})
|
|
||||||
elif _MAP[y][x]==4:
|
|
||||||
ptype=_r.choice(['health','ammo','armor'])
|
|
||||||
self._pickups.append({
|
|
||||||
'x':x+0.5,'y':y+0.5,'type':ptype,
|
|
||||||
'bob':0,'collected':False
|
|
||||||
})
|
|
||||||
|
|
||||||
def _resize(self, e):
|
|
||||||
self._width=e.width
|
|
||||||
self._height=e.height
|
|
||||||
|
|
||||||
def _mousebtn(self, e):
|
|
||||||
if self._game_state=="main":
|
|
||||||
self._game_state="play"
|
|
||||||
self._lock_mouse(e)
|
|
||||||
elif self._game_state=="pause":
|
|
||||||
self._lock_mouse(e)
|
|
||||||
self._game_state="play"
|
|
||||||
elif self._game_state=="play" and self._mouse_locked:
|
|
||||||
self._shoot()
|
|
||||||
|
|
||||||
def _esc(self, e):
|
|
||||||
if self._game_state=="play" and self._mouse_locked:
|
|
||||||
self._unlock_mouse()
|
|
||||||
self._game_state="pause"
|
|
||||||
elif self._game_state=="pause":
|
|
||||||
self._game_state="main"
|
|
||||||
elif self._game_state=="main":
|
|
||||||
self._rt.destroy()
|
|
||||||
|
|
||||||
def _lock_mouse(self,e=None):
|
|
||||||
if not self._mouse_locked:
|
|
||||||
self._cv.grab_set()
|
|
||||||
self._rt.config(cursor="none")
|
|
||||||
self._mouse_locked=True
|
|
||||||
if e: self._last_mouse_x=e.x
|
|
||||||
|
|
||||||
def _unlock_mouse(self):
|
|
||||||
self._cv.grab_release()
|
|
||||||
self._rt.config(cursor="")
|
|
||||||
self._mouse_locked=False
|
|
||||||
|
|
||||||
def _kd(self,e): self._keys.add(e.keysym.lower())
|
|
||||||
def _ku(self,e): self._keys.discard(e.keysym.lower())
|
|
||||||
|
|
||||||
def _mouse_look(self,e):
|
|
||||||
if self._game_state=="play" and self._mouse_locked:
|
|
||||||
dx=e.x-self._last_mouse_x
|
|
||||||
self._pa+=dx*0.003
|
|
||||||
self._last_mouse_x=e.x
|
|
||||||
|
|
||||||
def _shoot(self):
|
|
||||||
if self._ammo<=0: return
|
|
||||||
self._ammo-=1
|
|
||||||
self._weapon_frame=8
|
|
||||||
self._muzzle_flash=6
|
|
||||||
|
|
||||||
# Hitscan shooting
|
|
||||||
for enemy in self._enemies[:]:
|
|
||||||
dx,dy=enemy['x']-self._px,enemy['y']-self._py
|
|
||||||
dist=_m.sqrt(dx*dx+dy*dy)
|
|
||||||
if dist<10:
|
|
||||||
angle_to=_m.atan2(dy,dx)
|
|
||||||
angle_diff=abs(angle_to-self._pa)
|
|
||||||
if angle_diff>_m.pi: angle_diff=2*_m.pi-angle_diff
|
|
||||||
if angle_diff<0.3: # In crosshair
|
|
||||||
damage=_r.randint(25,45)
|
|
||||||
enemy['hp']-=damage
|
|
||||||
if enemy['hp']<=0:
|
|
||||||
self._enemies.remove(enemy)
|
|
||||||
self._score+=100
|
|
||||||
|
|
||||||
def _move(self):
|
|
||||||
s=0.05*max(1,self._width/800)
|
|
||||||
nx,ny=self._px,self._py
|
|
||||||
if 'w' in self._keys:
|
|
||||||
nx=self._px+_m.cos(self._pa)*s
|
|
||||||
ny=self._py+_m.sin(self._pa)*s
|
|
||||||
if 's' in self._keys:
|
|
||||||
nx=self._px-_m.cos(self._pa)*s
|
|
||||||
ny=self._py-_m.sin(self._pa)*s
|
|
||||||
if 'a' in self._keys:
|
|
||||||
nx=self._px+_m.cos(self._pa-_m.pi/2)*s
|
|
||||||
ny=self._py+_m.sin(self._pa-_m.pi/2)*s
|
|
||||||
if 'd' in self._keys:
|
|
||||||
nx=self._px+_m.cos(self._pa+_m.pi/2)*s
|
|
||||||
ny=self._py+_m.sin(self._pa+_m.pi/2)*s
|
|
||||||
|
|
||||||
if self._can_walk(nx, ny):
|
|
||||||
self._px,self._py=nx,ny
|
|
||||||
if 'left' in self._keys: self._pa-=0.06
|
|
||||||
if 'right' in self._keys: self._pa+=0.06
|
|
||||||
|
|
||||||
def _can_walk(self,x,y):
|
|
||||||
mx,my=int(x),int(y)
|
|
||||||
if mx<0 or mx>=_MW or my<0 or my>=_MH: return False
|
|
||||||
cell=_MAP[my][mx]
|
|
||||||
if cell==0: return True
|
|
||||||
if cell==2 and self._doors.get((mx,my),{'open':0.0})['open']>0.7:
|
|
||||||
return True
|
|
||||||
return False
|
|
||||||
|
|
||||||
def _update_doors(self):
|
|
||||||
mx,my=int(self._px),int(self._py)
|
|
||||||
for dx in [-1,0,1]:
|
|
||||||
for dy in [-1,0,1]:
|
|
||||||
cx,cy=mx+dx,my+dy
|
|
||||||
if (cx,cy) in self._doors:
|
|
||||||
dist=_m.sqrt((self._px-(cx+0.5))**2+(self._py-(cy+0.5))**2)
|
|
||||||
if dist<2.0:
|
|
||||||
self._doors[(cx,cy)]['opening']=True
|
|
||||||
for pos,door in self._doors.items():
|
|
||||||
if door['opening'] and door['open']<1.0:
|
|
||||||
door['open']+=0.04
|
|
||||||
elif not door['opening'] and door['open']>0.0:
|
|
||||||
door['open']-=0.01
|
|
||||||
door['open']=max(0,min(1,door['open']))
|
|
||||||
|
|
||||||
def _update_enemies(self):
|
|
||||||
for enemy in self._enemies:
|
|
||||||
enemy['ai_timer']+=1
|
|
||||||
if enemy['ai_timer']>80:
|
|
||||||
enemy['ai_timer']=0
|
|
||||||
dx,dy=self._px-enemy['x'],self._py-enemy['y']
|
|
||||||
dist=_m.sqrt(dx*dx+dy*dy)
|
|
||||||
if dist<8:
|
|
||||||
enemy['target_x']=enemy['x']+dx*0.08
|
|
||||||
enemy['target_y']=enemy['y']+dy*0.08
|
|
||||||
enemy['moving']=True
|
|
||||||
if dist<2 and _r.random()<0.15:
|
|
||||||
damage=_r.randint(8,18)
|
|
||||||
if self._armor>0:
|
|
||||||
self._armor-=damage//2
|
|
||||||
self._hp-=damage//2
|
|
||||||
else:
|
|
||||||
self._hp-=damage
|
|
||||||
self._pain_flash=8
|
|
||||||
if enemy['moving']:
|
|
||||||
dx,dy=enemy['target_x']-enemy['x'],enemy['target_y']-enemy['y']
|
|
||||||
spd=0.015
|
|
||||||
enemy['x']+=dx*spd
|
|
||||||
enemy['y']+=dy*spd
|
|
||||||
if abs(dx)<0.1 and abs(dy)<0.1:
|
|
||||||
enemy['moving']=False
|
|
||||||
|
|
||||||
def _update_pickups(self):
|
|
||||||
for pickup in self._pickups[:]:
|
|
||||||
if pickup['collected']: continue
|
|
||||||
pickup['bob']+=0.1
|
|
||||||
dx,dy=self._px-pickup['x'],self._py-pickup['y']
|
|
||||||
if _m.sqrt(dx*dx+dy*dy)<0.6:
|
|
||||||
pickup['collected']=True
|
|
||||||
if pickup['type']=='health':
|
|
||||||
self._hp=min(100,self._hp+30)
|
|
||||||
elif pickup['type']=='ammo':
|
|
||||||
self._ammo+=25
|
|
||||||
elif pickup['type']=='armor':
|
|
||||||
self._armor=min(100,self._armor+30)
|
|
||||||
self._score+=50
|
|
||||||
self._pickups.remove(pickup)
|
|
||||||
|
|
||||||
def _raycast(self,angle):
|
|
||||||
x,y=self._px,self._py
|
|
||||||
dx,dy=_m.cos(angle),_m.sin(angle)
|
|
||||||
dist=0
|
|
||||||
while dist<25:
|
|
||||||
x+=dx*0.06;y+=dy*0.06;dist+=0.06
|
|
||||||
mx,my=int(x),int(y)
|
|
||||||
if mx<0 or mx>=_MW or my<0 or my>=_MH: return dist
|
|
||||||
cell=_MAP[my][mx]
|
|
||||||
if cell==1: return dist
|
|
||||||
if cell==2 and self._doors.get((mx,my),{'open':0.0})['open']<0.7:
|
|
||||||
return dist
|
|
||||||
return dist
|
|
||||||
|
|
||||||
def _draw_3d_box(self,x,y,w,h,d,color,outline='#000'):
|
|
||||||
# 2.5D isometric box
|
|
||||||
pts_front=[(x,y),(x+w,y),(x+w,y+h),(x,y+h)]
|
|
||||||
pts_top=[(x,y),(x+d,y-d),(x+w+d,y-d),(x+w,y)]
|
|
||||||
pts_right=[(x+w,y),(x+w+d,y-d),(x+w+d,y+h-d),(x+w,y+h)]
|
|
||||||
self._cv.create_polygon(pts_front,fill=color,outline=outline,width=2)
|
|
||||||
self._cv.create_polygon(pts_top,fill=self._brighten(color,1.3),outline=outline,width=1)
|
|
||||||
self._cv.create_polygon(pts_right,fill=self._darken(color,0.7),outline=outline,width=1)
|
|
||||||
|
|
||||||
def _brighten(self,color,factor):
|
|
||||||
if color.startswith('#'):
|
|
||||||
r,g,b=int(color[1:3],16),int(color[3:5],16),int(color[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 color
|
|
||||||
|
|
||||||
def _darken(self,color,factor):
|
|
||||||
if color.startswith('#'):
|
|
||||||
r,g,b=int(color[1:3],16),int(color[3:5],16),int(color[5:7],16)
|
|
||||||
r,g,b=int(r*factor),int(g*factor),int(b*factor)
|
|
||||||
return f'#{r:02x}{g:02x}{b:02x}'
|
|
||||||
return color
|
|
||||||
|
|
||||||
def _render_menu(self):
|
|
||||||
cv=self._cv
|
|
||||||
ww,wh=self._width,self._height
|
|
||||||
cv.delete('all')
|
|
||||||
# 2.5D background
|
|
||||||
cv.create_rectangle(0,0,ww,wh,fill="#0a0a15",outline="")
|
|
||||||
for i in range(0,ww,60):
|
|
||||||
for j in range(0,wh,60):
|
|
||||||
cv.create_rectangle(i,j,i+30,j+30,fill="#151525",outline="#333",width=1)
|
|
||||||
|
|
||||||
# 2.5D title
|
|
||||||
self._draw_3d_box(ww//2-180,wh//2-100,360,80,20,"#cc2222","#881111")
|
|
||||||
cv.create_text(ww//2,wh//2-60,text="DOOM 2.5D",fill="#fff",font=("Consolas",32,"bold"))
|
|
||||||
|
|
||||||
# 2.5D start button
|
|
||||||
self._draw_3d_box(ww//2-100,wh//2+20,200,60,15,"#224422","#112211")
|
|
||||||
cv.create_text(ww//2,wh//2+50,text="START GAME",fill="#fff",font=("Consolas",18,"bold"))
|
|
||||||
|
|
||||||
cv.create_text(ww//2,wh-40,text="Click to Start | ESC to Quit",fill="#88f",font=("Consolas",14))
|
|
||||||
|
|
||||||
def _render_pause(self):
|
|
||||||
cv=self._cv
|
|
||||||
ww,wh=self._width,self._height
|
|
||||||
cv.create_rectangle(0,0,ww,wh,fill="#000",stipple="gray50")
|
|
||||||
|
|
||||||
# 2.5D pause box
|
|
||||||
self._draw_3d_box(ww//2-150,wh//2-80,300,120,25,"#333366","#111133")
|
|
||||||
cv.create_text(ww//2,wh//2-40,text="PAUSED",fill="#ff4",font=("Consolas",28,"bold"))
|
|
||||||
cv.create_text(ww//2,wh//2+10,text="Click to Resume",fill="#fff",font=("Consolas",16))
|
|
||||||
cv.create_text(ww//2,wh//2+35,text="ESC for Main Menu",fill="#aaf",font=("Consolas",14))
|
|
||||||
|
|
||||||
def _render_game(self):
|
|
||||||
cv=self._cv
|
|
||||||
ww,wh=self._width,self._height
|
|
||||||
rays=int(_RAYS*ww/800)
|
|
||||||
cv.delete('all')
|
|
||||||
|
|
||||||
# Sky/floor
|
|
||||||
for i in range(wh//2):
|
|
||||||
brightness=int(50+i*0.2)
|
|
||||||
color=f'#{brightness//4:02x}{brightness//3:02x}{brightness//2:02x}'
|
|
||||||
cv.create_line(0,i,ww,i,fill=color)
|
|
||||||
for i in range(wh//2,wh):
|
|
||||||
brightness=int(80-(i-wh//2)*0.15)
|
|
||||||
color=f'#{brightness//3:02x}{brightness//2:02x}{brightness//4:02x}'
|
|
||||||
cv.create_line(0,i,ww,i,fill=color)
|
|
||||||
|
|
||||||
# Walls
|
|
||||||
for i in range(rays):
|
|
||||||
angle=self._pa-_FOV/2+(_FOV*i/rays)
|
|
||||||
dist=self._raycast(angle)
|
|
||||||
dist*=_m.cos(angle-self._pa)
|
|
||||||
h=int((wh*0.6)/max(0.2,dist))
|
|
||||||
top=wh//2-h//2
|
|
||||||
bright=max(20,min(255,int(255/max(1.2,dist))))
|
|
||||||
col=f'#{bright//3:02x}{bright//4:02x}{bright//5:02x}'
|
|
||||||
x=int(i*(ww/rays))
|
|
||||||
cv.create_rectangle(x,top,x+max(1,int(ww/rays)),top+h,fill=col,outline='')
|
|
||||||
|
|
||||||
# Render sprites
|
|
||||||
sprite_list=[]
|
|
||||||
for enemy in self._enemies:
|
|
||||||
dx,dy=enemy['x']-self._px,enemy['y']-self._py
|
|
||||||
dist=_m.sqrt(dx*dx+dy*dy)
|
|
||||||
angle=_m.atan2(dy,dx)-self._pa
|
|
||||||
while angle<-_m.pi: angle+=2*_m.pi
|
|
||||||
while angle>_m.pi: angle-=2*_m.pi
|
|
||||||
if abs(angle)<_FOV/2+0.5:
|
|
||||||
sprite_list.append((dist,'enemy',angle,enemy))
|
|
||||||
|
|
||||||
for pickup in self._pickups:
|
|
||||||
if pickup['collected']: continue
|
|
||||||
dx,dy=pickup['x']-self._px,pickup['y']-self._py
|
|
||||||
dist=_m.sqrt(dx*dx+dy*dy)
|
|
||||||
angle=_m.atan2(dy,dx)-self._pa
|
|
||||||
while angle<-_m.pi: angle+=2*_m.pi
|
|
||||||
while angle>_m.pi: angle-=2*_m.pi
|
|
||||||
if abs(angle)<_FOV/2+0.5:
|
|
||||||
sprite_list.append((dist,'pickup',angle,pickup))
|
|
||||||
|
|
||||||
sprite_list.sort(reverse=True)
|
|
||||||
for dist,stype,angle,obj in sprite_list:
|
|
||||||
screen_x=ww//2+int(angle*ww/_FOV)
|
|
||||||
size=max(15,min(180,int((wh*0.5)/max(0.3,dist))))
|
|
||||||
left=screen_x-size//2
|
|
||||||
top=wh//2-size//2
|
|
||||||
|
|
||||||
if stype=='enemy':
|
|
||||||
if obj['hp']>50: ecolor='#ff3333'
|
|
||||||
elif obj['hp']>25: ecolor='#ff6666'
|
|
||||||
else: ecolor='#ff9999'
|
|
||||||
|
|
||||||
# 2.5D enemy
|
|
||||||
d=size//8
|
|
||||||
self._draw_3d_box(left,top,size,size,d,ecolor,'#aa0000')
|
|
||||||
# Eyes
|
|
||||||
eye_size=max(3,size//12)
|
|
||||||
cv.create_rectangle(left+size//3,top+size//4,left+size//3+eye_size,top+size//4+eye_size,fill='#ff0',outline='#aa0')
|
|
||||||
cv.create_rectangle(left+2*size//3,top+size//4,left+2*size//3+eye_size,top+size//4+eye_size,fill='#ff0',outline='#aa0')
|
|
||||||
|
|
||||||
elif stype=='pickup':
|
|
||||||
bob=_m.sin(obj['bob'])*size//8
|
|
||||||
if obj['type']=='health': pcolor,symbol='#ff4444','+'
|
|
||||||
elif obj['type']=='ammo': pcolor,symbol='#ffff44','A'
|
|
||||||
else: pcolor,symbol='#4444ff','S'
|
|
||||||
|
|
||||||
# 2.5D pickup
|
|
||||||
d=size//6
|
|
||||||
self._draw_3d_box(left,top+bob,size//2,size//2,d,pcolor,'#666')
|
|
||||||
cv.create_text(screen_x,wh//2+bob,text=symbol,fill='#fff',font=('Courier',max(12,size//4),'bold'))
|
|
||||||
|
|
||||||
# 2.5D weapon
|
|
||||||
weapon_w,weapon_h=int(ww/6),int(wh/2.5)
|
|
||||||
weapon_x=ww-weapon_w-40
|
|
||||||
weapon_y=wh-weapon_h-40+self._weapon_frame*3
|
|
||||||
weapon_d=20
|
|
||||||
|
|
||||||
self._draw_3d_box(weapon_x,weapon_y,weapon_w,weapon_h,weapon_d,'#666666','#333')
|
|
||||||
# Barrel
|
|
||||||
self._draw_3d_box(weapon_x+weapon_w//4,weapon_y+20,weapon_w//2,30,10,'#444444','#222')
|
|
||||||
|
|
||||||
# Muzzle flash
|
|
||||||
if self._muzzle_flash>0:
|
|
||||||
flash_size=40+self._muzzle_flash*5
|
|
||||||
cv.create_oval(weapon_x+weapon_w//2-flash_size//2,weapon_y+10,
|
|
||||||
weapon_x+weapon_w//2+flash_size//2,weapon_y+50,
|
|
||||||
fill='#ffff00',outline='#ffaa00',width=3)
|
|
||||||
self._muzzle_flash-=1
|
|
||||||
|
|
||||||
# 2.5D HUD
|
|
||||||
hud_h=80
|
|
||||||
self._draw_3d_box(0,wh-hud_h,ww,hud_h,8,'#222244','#444')
|
|
||||||
|
|
||||||
# Health bar (2.5D)
|
|
||||||
health_w=int(200*self._hp/100)
|
|
||||||
self._draw_3d_box(20,wh-60,200,30,6,'#330000','#660000')
|
|
||||||
if health_w>0:
|
|
||||||
self._draw_3d_box(20,wh-60,health_w,30,6,'#ff4444','#aa0000')
|
|
||||||
cv.create_text(120,wh-45,text=f'HP: {self._hp}',fill='#fff',font=('Courier',14,'bold'))
|
|
||||||
|
|
||||||
# Armor bar
|
|
||||||
if self._armor>0:
|
|
||||||
armor_w=int(150*self._armor/100)
|
|
||||||
self._draw_3d_box(250,wh-60,150,30,6,'#000033','#000066')
|
|
||||||
self._draw_3d_box(250,wh-60,armor_w,30,6,'#4444ff','#0000aa')
|
|
||||||
cv.create_text(325,wh-45,text=f'ARM: {self._armor}',fill='#fff',font=('Courier',12,'bold'))
|
|
||||||
|
|
||||||
# Ammo and score
|
|
||||||
cv.create_text(ww-200,wh-55,text=f'AMMO: {self._ammo}',fill='#ff4',font=('Courier',14,'bold'))
|
|
||||||
cv.create_text(ww-200,wh-35,text=f'SCORE: {self._score}',fill='#4f4',font=('Courier',14,'bold'))
|
|
||||||
cv.create_text(ww-200,wh-15,text=f'ENEMIES: {len(self._enemies)}',fill='#f84',font=('Courier',12,'bold'))
|
|
||||||
|
|
||||||
# Crosshair
|
|
||||||
cx,cy=ww//2,wh//2
|
|
||||||
cv.create_line(cx-15,cy,cx+15,cy,fill='#fff',width=3)
|
|
||||||
cv.create_line(cx,cy-15,cx,cy+15,fill='#fff',width=3)
|
|
||||||
cv.create_oval(cx-5,cy-5,cx+5,cy+5,outline='#fff',width=2)
|
|
||||||
|
|
||||||
# Pain flash
|
|
||||||
if self._pain_flash>0:
|
|
||||||
cv.create_rectangle(0,0,ww,wh,fill='#ff0000',stipple='gray25')
|
|
||||||
self._pain_flash-=1
|
|
||||||
|
|
||||||
if self._weapon_frame>0: self._weapon_frame-=1
|
|
||||||
|
|
||||||
def _run(self):
|
|
||||||
if self._game_state=="main":
|
|
||||||
self._render_menu()
|
|
||||||
elif self._game_state=="pause":
|
|
||||||
self._render_game()
|
|
||||||
self._render_pause()
|
|
||||||
elif self._game_state=="play":
|
|
||||||
self._move()
|
|
||||||
self._update_doors()
|
|
||||||
self._update_enemies()
|
|
||||||
self._update_pickups()
|
|
||||||
self._render_game()
|
|
||||||
self._rt.after(28,self._run)
|
|
||||||
|
|
||||||
_DOOM25D()
|
|
||||||
?>
|
|
||||||
<?js
|
|
||||||
(function(){
|
|
||||||
var x=[68,79,79,77,32,50,46,53,68,32,70,117,108,108,32,71,97,109,101];
|
|
||||||
var s='';
|
|
||||||
for(var i of x){ s+=String.fromCharCode(i);}
|
|
||||||
console.log(s);
|
|
||||||
})();
|
|
||||||
?>
|
|
||||||
<?php
|
|
||||||
${a}=array(68,79,79,77,32,50,46,53,68,32,70,117,108,108,32,71,97,109,101);
|
|
||||||
echo implode(array_map('chr',${a})) . "\n";
|
|
||||||
?>
|
|
||||||
<?cs
|
|
||||||
using System;
|
|
||||||
class _D{
|
|
||||||
static void Main(){
|
|
||||||
var _a=new int[]{68,79,79,77,32,50,46,53,68,32,70,117,108,108,32,71,97,109,101};
|
|
||||||
Console.WriteLine(string.Join("",_a.Select(c=>(char)c)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
?>
|
|
||||||
<?cpp
|
|
||||||
#include <iostream>
|
|
||||||
int main() {
|
|
||||||
int _a[]={68,79,79,77,32,50,46,53,68,32,70,117,108,108,32,71,97,109,101};
|
|
||||||
for(int i=0;i<19;i++) std::cout<<(char)_a[i];
|
|
||||||
std::cout<<std::endl;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
?>
|
|
||||||
<?css
|
|
||||||
body{background:#0a0a15;color:#fff;font-family:'Courier New',monospace;overflow:hidden;}
|
|
||||||
canvas{cursor:crosshair;image-rendering:pixelated;}
|
|
||||||
.doom25d{filter:contrast(1.5) brightness(1.2) saturate(1.3);}
|
|
||||||
?>
|
|
||||||
327
files/maze.m5r
Normal file
327
files/maze.m5r
Normal file
@@ -0,0 +1,327 @@
|
|||||||
|
<?py
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
import tkinter as _tk, math as _m, random as _r, time as _t
|
||||||
|
|
||||||
|
def _gen_maze(_lv):
|
||||||
|
_sz = min(18 + _lv*4, 50)
|
||||||
|
_mp = [[1]*_sz for _ in range(_sz)]
|
||||||
|
# Create maze paths
|
||||||
|
def _carve(x, y):
|
||||||
|
_mp[y][x] = 0
|
||||||
|
dirs = [(0,2), (2,0), (0,-2), (-2,0)]
|
||||||
|
_r.shuffle(dirs)
|
||||||
|
for dx, dy in dirs:
|
||||||
|
nx, ny = x + dx, y + dy
|
||||||
|
if 2 <= nx < _sz-2 and 2 <= ny < _sz-2 and _mp[ny][nx] == 1:
|
||||||
|
_mp[y + dy//2][x + dx//2] = 0
|
||||||
|
_carve(nx, ny)
|
||||||
|
|
||||||
|
# Start carving from (1,1)
|
||||||
|
_carve(1, 1)
|
||||||
|
|
||||||
|
# Ensure start and exit are clear
|
||||||
|
_mp[1][1] = 0 # start
|
||||||
|
_mp[_sz-2][_sz-2] = 2 # exit (blue wall)
|
||||||
|
_mp[_sz-3][_sz-2] = 0 # space before exit
|
||||||
|
_mp[_sz-2][_sz-3] = 0 # space before exit
|
||||||
|
|
||||||
|
return _mp
|
||||||
|
|
||||||
|
class _MAZE25D:
|
||||||
|
def __init__(self):
|
||||||
|
self._rt=_tk.Tk()
|
||||||
|
self._rt.title("MAZE 2.5D M5RCode")
|
||||||
|
self._W,self._H=1200,820
|
||||||
|
self._cv=_tk.Canvas(self._rt,bg="#181319",width=self._W,height=self._H)
|
||||||
|
self._cv.pack(fill="both",expand=True)
|
||||||
|
self._game_state="main"
|
||||||
|
self._rt.bind("<Configure>",self._resize)
|
||||||
|
self._rt.bind("<KeyPress>",self._kd)
|
||||||
|
self._rt.bind("<KeyRelease>",self._ku)
|
||||||
|
self._rt.bind("<Button-1>", self._mouse_btn)
|
||||||
|
self._rt.bind("<Escape>",self._esc)
|
||||||
|
self._rt.bind("<Motion>", 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._sz=len(self._map)
|
||||||
|
self._px,self._py=1.5,1.5 # start position
|
||||||
|
self._pa=_m.pi/4
|
||||||
|
self._levelup_msg=0
|
||||||
|
|
||||||
|
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)
|
||||||
|
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
|
||||||
|
|
||||||
|
def _unlock_mouse(self,e=None):
|
||||||
|
self._mouse_locked=False
|
||||||
|
self._rt.config(cursor="")
|
||||||
|
self._cv.grab_release()
|
||||||
|
self._last_mouse_x=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
|
||||||
|
|
||||||
|
def _esc(self,e):
|
||||||
|
if self._game_state=="play" and self._mouse_locked:
|
||||||
|
self._unlock_mouse()
|
||||||
|
self._game_state="pause"
|
||||||
|
elif self._game_state=="pause":
|
||||||
|
self._game_state="main"
|
||||||
|
elif self._game_state=="main":
|
||||||
|
self._rt.destroy()
|
||||||
|
|
||||||
|
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'):
|
||||||
|
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)
|
||||||
|
pts_r=[(x+w,y),(x+w+d,y-d),(x+w+d,y+h-d),(x+w,y+h)]
|
||||||
|
self._cv.create_polygon(pts_r,fill=self._darken(col,0.75),outline=ol,width=1)
|
||||||
|
|
||||||
|
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")
|
||||||
|
|
||||||
|
def _raycast(self,a):
|
||||||
|
x,y = self._px,self._py
|
||||||
|
dx,dy = _m.cos(a)*.05,_m.sin(a)*.05
|
||||||
|
for d in range(1,400):
|
||||||
|
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'
|
||||||
|
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 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}"
|
||||||
|
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}"
|
||||||
|
self._cv.create_line(0,i,w,i,fill=sh)
|
||||||
|
|
||||||
|
# Render walls
|
||||||
|
rays=250
|
||||||
|
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}"
|
||||||
|
else:
|
||||||
|
cc="#000000"
|
||||||
|
|
||||||
|
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
|
||||||
|
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")
|
||||||
|
|
||||||
|
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")
|
||||||
|
|
||||||
|
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._levelup_msg-=1
|
||||||
|
|
||||||
|
def _tick(self):
|
||||||
|
if self._game_state=="play":
|
||||||
|
self._step()
|
||||||
|
self._render_game()
|
||||||
|
elif self._game_state=="main":
|
||||||
|
self._render_menu()
|
||||||
|
elif self._game_state=="pause":
|
||||||
|
self._render_game()
|
||||||
|
self._rt.after(30,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"
|
||||||
|
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")
|
||||||
|
|
||||||
|
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
|
||||||
|
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
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
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)
|
||||||
|
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)
|
||||||
|
if distance < 1.2:
|
||||||
|
self._reset_game(self._level + 1)
|
||||||
|
self._levelup_msg = 60
|
||||||
|
return
|
||||||
|
|
||||||
|
def _kd(self,e):
|
||||||
|
if self._game_state=="play" and self._mouse_locked:
|
||||||
|
self._keys.add(e.keysym.lower())
|
||||||
|
if e.keysym.lower()=='left':
|
||||||
|
self._pa-=_m.pi/20
|
||||||
|
if e.keysym.lower()=='right':
|
||||||
|
self._pa+=_m.pi/20
|
||||||
|
|
||||||
|
def _ku(self,e):
|
||||||
|
self._keys.discard(e.keysym.lower())
|
||||||
|
|
||||||
|
_MAZE25D()
|
||||||
|
?>
|
||||||
|
|
||||||
|
<?js
|
||||||
|
console.log("MAZE ESCAPE 2.5D M5RCode");
|
||||||
|
?>
|
||||||
|
|
||||||
|
<?php
|
||||||
|
echo "MAZE ESCAPE 2.5D M5RCode\n";
|
||||||
|
?>
|
||||||
|
|
||||||
|
<?cs
|
||||||
|
using System;
|
||||||
|
class _M{
|
||||||
|
static void Main(){ Console.WriteLine("MAZE ESCAPE 2.5D M5RCode"); }
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
|
||||||
|
<?cpp
|
||||||
|
#include <iostream>
|
||||||
|
int main(){ std::cout<<"MAZE ESCAPE 2.5D M5RCode"<<std::endl; return 0;}
|
||||||
|
?>
|
||||||
|
|
||||||
|
<?css
|
||||||
|
body{background:#1a2233;color:#fff;font-family:'Courier New',monospace;overflow:hidden;}
|
||||||
|
canvas{cursor:crosshair;image-rendering:pixelated;}
|
||||||
|
.maze-wall{filter:contrast(1.1);}
|
||||||
|
.maze-exit{filter:brightness(1.3) saturate(1.5);}
|
||||||
|
?>
|
||||||
Reference in New Issue
Block a user