diff --git a/files/doom.m5r b/files/doom.m5r new file mode 100644 index 0000000..32eda88 --- /dev/null +++ b/files/doom.m5r @@ -0,0 +1,504 @@ +',self._resize) + + self._mouse_locked=False + self._game_state="main" + self._rt.bind('',self._mousebtn) + self._rt.bind('',self._esc) + self._rt.bind('',self._kd) + self._rt.bind('',self._ku) + self._rt.bind('',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() +?> + + +(char)c))); + } +} +?> + +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< +