|
|
本帖最后由 陆灏 于 2026-1-18 13:06 编辑
import pygame,sys,math,random,time
W,H=1280,720;F=60
FOV=math.radians(60);RAY=86;MAXD=900;SCALE=W//RAY
TILE=100;C=20;R=16
SP=2.2;SR=1.9;RT=0.9;HP=100
AM=12;DMG=40;RL=0.9
NE=8;EH=100;ES=1.1;ED=450;EA=40;EC=1.2
pygame.init();S=pygame.display.set_mode((W,H));T=pygame.time.Clock();FNT=pygame.font.SysFont(None,15)
def cl(x,a,b): return a if x<a else b if x>b else x
def put(u,p,c=(255,255,255)): S.blit(FNT.render(u,1,c),p)
def gen():
g=[[1]*C for _ in range(R)];rs=[]
for _ in range(44):
w=random.randint(3,7);h=random.randint(3,6)
x=random.randint(1,C-w-1);y=random.randint(1,R-h-1);r=pygame.Rect(x,y,w,h);ok=1
for q in rs:
if r.colliderect(q.inflate(2,2)): ok=0;break
if not ok: continue
rs.append(r)
for yy in range(y,y+h):
for xx in range(x,x+w): g[yy][xx]=0
def c0(r): return (r.x+r.w//2,r.y+r.h//2)
for i in range(1,len(rs)):
x1,y1=c0(rs[i-1]);x2,y2=c0(rs)
if random.random()<0.5:
for xx in range(min(x1,x2),max(x1,x2)+1): g[y1][xx]=0
for yy in range(min(y1,y2),max(y1,y2)+1): g[yy][x2]=0
else:
for yy in range(min(y1,y2),max(y1,y2)+1): g[yy][x1]=0
for xx in range(min(x1,x2),max(x1,x2)+1): g[y2][xx]=0
return g,rs
G,RS=gen()
WALL=[pygame.Rect(c*TILE,r*TILE,TILE,TILE) for r in range(R) for c in range(C) if G[r][c]==1]
if RS:
r=RS[0];SX=(r.x+r.w//2)*TILE+TILE//2;SY=(r.y+r.h//2)*TILE+TILE//2
else: SX,SY=TILE*2+TILE//2,TILE*2+TILE//2
class P:
def __init__(s,x,y):
s.x=x;s.y=y;s.a=0.0;s.hp=HP;s.sp=SP
s.am=AM;s.rl=0
Ply=P(SX,SY)
class E:
def __init__(s,x,y):
s.x=x;s.y=y;s.hp=EH;s.ok=1;s.st=0;s.pt=s.rp();s.cd=0
def rp(s):
a=random.random()*math.tau;return(s.x+math.cos(a)*180,s.y+math.sin(a)*180)
def d(s): return math.hypot(s.x-Ply.x,s.y-Ply.y)
def see(s):
d=s.d()
if d>ED: return 0
st=max(1,int(d/20))
for i in range(1,st+1):
t=i/st;sx=s.x+(Ply.x-s.x)*t;sy=s.y+(Ply.y-s.y)*t
r=pygame.Rect(sx-2,sy-2,4,4)
for w in WALL:
if w.colliderect(r): return 0
return 1
def upd(s,dt):
if not s.ok: return
if s.cd>0: s.cd-=dt
if s.see(): s.st=1
else:
if s.st!=2: s.st=2;s.pt=s.rp()
if s.st==1:
a=math.atan2(Ply.y-s.y,Ply.x-s.x)
nx=s.x+math.cos(a)*ES*60*dt;ny=s.y+math.sin(a)*ES*60*dt
r=pygame.Rect(nx-8,ny-8,16,16)
if not any(w.colliderect(r) for w in WALL): s.x,s.y=nx,ny
else:
a+=math.pi/2;nx=s.x+math.cos(a)*ES*60*dt;ny=s.y+math.sin(a)*ES*60*dt
r=pygame.Rect(nx-8,ny-8,16,16)
if not any(w.colliderect(r) for w in WALL): s.x,s.y=nx,ny
if s.d()<EA and s.cd<=0: Ply.hp-=10;s.cd=EC
else:
tx,ty=s.pt;a=math.atan2(ty-s.y,tx-s.x)
nx=s.x+math.cos(a)*ES*0.7*60*dt;ny=s.y+math.sin(a)*ES*0.7*60*dt
r=pygame.Rect(nx-8,ny-8,16,16)
if not any(w.colliderect(r) for w in WALL): s.x,s.y=nx,ny
if math.hypot(tx-s.x,ty-s.y)<20: s.pt=s.rp()
EN=[]
for i in range(NE):
if RS:
r=random.choice(RS);ex=(r.x+random.randint(0,r.w-1))*TILE+TILE//2;ey=(r.y+random.randint(0,r.h-1))*TILE+TILE//2
else:
ex=random.randint(TILE,C*TILE-TILE);ey=random.randint(TILE,R*TILE-TILE)
EN.append(E(ex,ey))
help_on=0;dead=0
def shoot(dmg):
mx,my=pygame.mouse.get_pos();dx=mx-W//2;off=(dx/(W//2))*(FOV/2);a=Pl.a+off if (Pl:=Ply) else Ply.a+off
for depth in range(0,MAXD,8):
wx=Pl.x+math.cos(a)*depth;wy=Pl.y+math.sin(a)*depth;r=pygame.Rect(wx-2,wy-2,4,4)
if any(w.colliderect(r) for w in WALL):return 0,None
for e in EN:
if e.ok and math.hypot(e.x-wx,e.y-wy)<16:
e.hp-=dmg
if e.hp<=0: e.ok=0
return 1,e
return 0,None
def blk(x,y):
r=pygame.Rect(x-6,y-6,12,12);return any(w.colliderect(r) for w in WALL)
last=0;sd=0.25
while 1:
dt=T.tick(F)/1000.0;k=pygame.key.get_pressed()
for ev in pygame.event.get():
if ev.type==pygame.QUIT:pygame.quit();sys.exit()
if dead:
if ev.type==pygame.KEYDOWN:
if ev.key==pygame.K_r:
Ply.x,Ply.y=SX,SY ly.hp=HP ly.am=AM ly.rl=0;dead=0
if ev.key==pygame.K_q:pygame.quit();sys.exit()
if ev.key==pygame.K_0:help_on^=1
continue
if ev.type==pygame.KEYDOWN:
if ev.key==pygame.K_SPACE:
now=time.time()
if Ply.rl<=0:
ok=0
if Ply.am>0 and now-last>=sd k=1
if ok:
last=now;d=DMG;shoot(d) ly.am-=1
if ev.key==pygame.K_r ly.rl=RL
if ev.key==pygame.K_0:help_on^=1
if ev.type==pygame.MOUSEBUTTONDOWN and not dead:
if ev.button==1:
now=time.time()
if Ply.rl<=0 and Ply.am>0 and now-last>=sd:
last=now;d=DMG;shoot(d) ly.am-=1
if dead:continue
rrot=RT*dt
if k[pygame.K_a] ly.a-=rrot
if k[pygame.K_d] ly.a+=rrot
if Ply.rl>0:
Ply.rl-=dt
if Ply.rl<=0 ly.am=AM
mv=Ply.sp*(SR if (k[pygame.K_LSHIFT] or k[pygame.K_RSHIFT]) else 1.0)
f=0
if k[pygame.K_w]:f+=1
if k[pygame.K_s]:f-=1
if f:
nx=Ply.x+math.cos(Ply.a)*f*mv*60*dt;ny=Ply.y+math.sin(Ply.a)*f*mv*60*dt
if not blk(nx,ny) ly.x,Ply.y=nx,ny
for e in EN:e.upd(dt)
if Ply.hp<=0:dead=1 ly.hp=0
S.fill((24,24,24))
sa=Ply.a-FOV/2
for i in range(RAY):
ra=sa+FOV*i/RAY;d=0;hit=0
while d<MAXD and not hit:
d+=6
wx=Ply.x+math.cos(ra)*d;wy=Ply.y+math.sin(ra)*d
tx=int(wx//TILE);ty=int(wy//TILE)
if tx<0 or ty<0 or tx>=C or ty>=R:hit=1;break
if G[ty][tx]==1:hit=1;break
if hit:
d*=math.cos(Ply.a-ra)
if d==0:d=0.0001
hh=cl(12000/(d+0.0001),2,H)
sh=255-cl(int(d/6),0,220)
pygame.draw.rect(S,(sh,sh,sh),(i*SCALE,(H-int(hh))//2,SCALE,int(hh)))
pygame.draw.circle(S,(255,0,0),(W//2,H//2),4)
vis=[]
for e in EN:
if not e.ok:continue
dx=e.x-Ply.x;dy=e.y-Ply.y;dist=math.hypot(dx,dy)
at=math.atan2(dy,dx);rel=at-Ply.a
while rel<-math.pi:rel+=2*math.pi
while rel>math.pi:rel-=2*math.pi
if abs(rel)<FOV/1.2 and dist<MAXD:
ok=1;stp=max(1,int(dist/20))
for j in range(1,stp+1):
t=j/stp;sx=Ply.x+dx*t;sy=Ply.y+dy*t
if blk(sx,sy) k=0;break
if ok:vis.append((dist,rel,e))
vis.sort(key=lambda x:x[0],reverse=True)
for dist,rel,e in vis:
sx=W//2+math.tan(rel)*(W//2)
ph=cl(9000/(dist+1),20,H*0.9);pw=ph*0.5
rct=pygame.Rect(int(sx-pw/2),int((H-ph)/2),int(pw),int(ph))
sh=200-int((dist/ED)*150);sh=cl(sh,40,200)
pygame.draw.rect(S,(30,sh,30),rct)
hb_w=rct.width;pygame.draw.rect(S,(50,50,50),(rct.x,rct.y-12,hb_w,6))
pygame.draw.rect(S,(200,30,30),(rct.x,rct.y-12,int(hb_w*cl(e.hp/EH,0,1)),6))
put(f"HP:{int(Ply.hp)}/{HP}",(6,6));put(f"AM:{Ply.am}",(6,26))
if help_on:
put("W/S move A/D turn Shift sprint Space/mouse fire R reload",(W//2-300,H//2-24))
put("Dead -> press R to respawn, Q to quit",(W//2-300,H//2-4))
pygame.display.flip()
|
|