bad_guys_shooter.py

Created by mathieu-croslacoste

Created on September 15, 2025

8.71 KB

compression of https://github.com/arthur-jacquin/numworks-games slight speed upgrade. reduce score (bottom of the code) for less ennemies. doable with 25 bullets, even 22.


from math import*
from kandinsky import fill_rect as F,draw_string as D
from ion import keydown as K
from time import monotonic as M
from random import randint as R
px,py=0,0
pdx,pdy,pa=0,0,pi/4
shooting=None
bullets=25
pi2=pi/2
pi3=(3*pi)/2
_2pi=2*pi
map=[
1,1,2,1,1,1,1,1,1,
1,0,0,0,0,0,0,0,1,
1,1,1,0,1,1,5,3,5,
8,0,0,0,0,0,1,0,1,
1,1,1,0,1,1,1,0,1,
1,0,0,0,0,0,0,0,1,
1,0,1,5,3,5,1,0,1,
1,0,1,0,0,3,0,0,1,
1,1,1,0,1,5,1,1,1,
1,0,0,0,0,0,0,0,1,
1,0,4,0,0,0,4,0,1,
1,0,0,0,0,0,0,0,1,
1,0,4,4,0,4,4,0,1,
1,0,4,4,0,4,4,0,1,
1,0,0,0,0,0,0,0,1,
1,0,4,4,0,4,4,0,1,
1,0,4,4,0,4,4,0,1,
1,0,0,0,0,0,0,0,1,
1,1,1,1,1,1,5,3,5,
1,0,0,0,0,0,0,0,1,
1,0,4,4,4,4,4,0,1,
1,0,5,0,0,0,4,0,1,
1,0,3,0,6,0,4,0,1,
1,0,5,0,0,0,4,0,1,
1,0,4,4,4,4,4,0,1,
1,0,0,0,0,0,0,0,1,
1,1,7,7,3,7,7,1,1,
1,1,1,1,0,1,1,1,1,
1,0,0,0,0,0,0,0,1,
1,0,1,1,1,1,1,0,1,
1,0,1,0,0,0,1,0,1,
1,0,1,0,1,0,1,0,1,
1,0,1,0,1,0,1,0,1,
1,0,0,0,0,0,0,0,1,
1,1,1,1,1,1,1,1,1,
]
mapX=9
mapY=int(len(map)/mapX)
mapS=64
mapL=mapX*mapY
textureSize=8
textures=((0b01111110,
0b01111110,
0b00000000,
0b11100111,
0b11100111,
0b00000000,
0b01111110,
0b01111110),
(0b10011111,
0b10011011,
0b11111111,
0b11110111,
0b10111101,
0b10100101,
0b10111101,
0b11111111),
(0b00000000,
0b11100111,
0b10100101,
0b10100101,
0b00100100,
0b11100111,
0b11100111,
0b00000000),
(0b11100011,
0b11110001,
0b11111000,
0b01111100,
0b00111110,
0b00011111,
0b10001111,
0b11000111),
(0b00000000,
0b01111110,
0b00000000,
0b01111110,
0b01111110,
0b00000000,
0b01111110,
0b00000000),
(0b00000000,
0b00010000,
0b00110000,
0b01111110,
0b01111110,
0b00110000,
0b00010000,
0b00000000),
(0b00000000,
0b01111110,
0b01111110,
0b01111110,
0b01111110,
0b01111110,
0b01111110,
0b00000000),
(0b00000000,
0b00100100,
0b00100100,
0b00000000,
0b00111100,
0b01000010,
0b01000010,
0b00000000))
doorTexture=3
ennemySprites=((
(2,0,1,2),
(5,0,1,2),
(2,3,4,1),
(1,4,1,2),
(6,4,1,2)),
((2,0,1,2),
(5,0,1,2),
(2,4,4,1),
(1,5,1,2),
(6,5,1,2)),
((2,0,1,2),
(5,0,1,2),
(2,5,4,1),
(1,6,1,2),
(6,6,1,2)),
((2,0,1,2),
(5,0,1,2),
(2,4,4,1),
(1,5,1,2),
(6,5,1,2)))
ents=[]
def movePl(dt):
 global px,py,pa,pdx,pdy,shooting,bullets;xo,yo=20,20
 if pdx<0:xo=-20
 if pdy<0:yo=-20
 ipx,ipxAddXo,ipxSubXo=int(px)>>6,int(px+xo)>>6,int(px-xo)>>6;ipy,ipyAddYo,ipySubYo=int(py)>>6,int(py+yo)>>6,int(py-yo)>>6
 if K(4):
  if map[ipyAddYo*mapX+ipxAddXo]==doorTexture:map[ipyAddYo*mapX+ipxAddXo]=0
  elif shooting==None and bullets>0:shooting=1;bullets-=1
 if shooting==0:shooting=None
 if K(1):
  if map[ipy*mapX+ipxAddXo]==0:px+=pdx*50*dt
  if map[ipyAddYo*mapX+ipx]==0:py+=pdy*50*dt
 elif K(2):
  if map[ipy*mapX+ipxSubXo]==0:px-=pdx*50*dt
  if map[ipySubYo*mapX+ipx]==0:py-=pdy*50*dt
 if K(0):
  tpa=pa-pi2;tpdx,tpdy=cos(tpa)*50,sin(tpa)*50;xo,yo=20,20
  if tpdx<0:xo=-20
  if tpdy<0:yo=-20
  ipx,ipxAddXo,ipxSubXo=int(px)>>6,int(px+xo)>>6,int(px-xo)>>6;ipy,ipyAddYo,ipySubYo=int(py)>>6,int(py+yo)>>6,int(py-yo)>>6
  if map[ipy*mapX+ipxAddXo]==0:px+=tpdx*dt
  if map[ipyAddYo*mapX+ipx]==0:py+=tpdy*dt
 elif K(3):
  tpa=pa+pi2;tpdx,tpdy=cos(tpa)*50,sin(tpa)*50;xo,yo=20,20
  if tpdx<0:xo=-20
  if tpdy<0:yo=-20
  ipx,ipxAddXo,ipxSubXo=int(px)>>6,int(px+xo)>>6,int(px-xo)>>6;ipy,ipyAddYo,ipySubYo=int(py)>>6,int(py+yo)>>6,int(py-yo)>>6
  if map[ipy*mapX+ipxAddXo]==0:px+=tpdx*dt
  if map[ipyAddYo*mapX+ipx]==0:py+=tpdy*dt
 if K(33):pa=(pa-dt)%_2pi;pdx=cos(pa);pdy=sin(pa)
 elif K(34):pa=(pa+dt)%_2pi;pdx=cos(pa);pdy=sin(pa)
def dist(ax,ay,bx,by):return sqrt((bx-ax)*(bx-ax)+(by-ay)*(by-ay))
def distSq(ax,ay,bx,by):return(bx-ax)*(bx-ax)+(by-ay)*(by-ay)
def rays():
 global mapS,mapX,mapY,map,s,distMax;r=mx=my=mp=hit=rx=ry=ra=xo=yo=distMax=i=th=tv=0;ra=(pa-HFOV)%_2pi
 while i<RAYS:
  if ra==0 or ra==pi:rx=px;ry=py;hit=1
  else:
   hit=0;disH,hx,hy=1000000,px,py;aTan=-1/tan(ra)
   if ra>pi:ry=((int(py)>>6)<<6)-.0001;rx=(py-ry)*aTan+px;yo=-64;xo=-yo*aTan
   if ra<pi:ry=((int(py)>>6)<<6)+64;rx=(py-ry)*aTan+px;yo=64;xo=-yo*aTan
  while 1-hit:
   mx=int(rx)>>6;my=int(ry)>>6;mp=my*mapX+mx
   if mp<0 or mp>=mapL or map[mp]>0:
    if mp>0 and mp<mapL:th=map[mp]
    hx=rx;hy=ry;disH=distSq(px,py,hx,hy);hit=1
   else:rx+=xo;ry+=yo
  if ra==pi2 or ra==pi3:rx=px;ry=py;hit=1
  else:
   hit=0;disV,vx,vy=1000000,px,py;nTan=-tan(ra)
   if ra>pi2 and ra<pi3:rx=((int(px)>>6)<<6)-.0001;ry=(px-rx)*nTan+py;xo=-64;yo=-xo*nTan
   if ra<pi2 or ra>pi3:rx=((int(px)>>6)<<6)+64;ry=(px-rx)*nTan+py;xo=64;yo=-xo*nTan
  while 1-hit:
   mx=int(rx)>>6;my=int(ry)>>6;mp=my*mapX+mx
   if mp<0 or mp>=mapL or map[mp]>0:
    if mp>0 and mp<mapL:tv=map[mp]
    vx=rx;vy=ry;disV=distSq(px,py,vx,vy);hit=1
   else:rx+=xo;ry+=yo
  if disV<disH:t=tv;rx=vx;ry=vy;disT=disV;shade=.7
  else:t=th;rx=hx;ry=hy;disT=disH;shade=.9
  disT=sqrt(disT);depth=shade*25600/disT
  if depth>128*shade:depth=128*shade
  lineH=int((mapS<<8)/(disT*cos(pa-ra)));line8th=lineH/8;tyo=0
  if lineH>SH:tyo=int(lineH-SH)>>1;lineH=SH
  o=int(SH-lineH)>>1
  if shade==.9:
   tx=int(rx%mapS)>>3
   if ra<pi:tx=7-tx
  else:
   tx=int(ry%mapS)>>3
   if ra>pi2 and ra<pi3:tx=7-tx
  txo=7-tx;s[i][0]=o;s[i][1]=t;s[i][2]=txo;s[i][3]=tyo;s[i][4]=depth;s[i][5]=lineH;s[i][6]=line8th;s[i][7]=disT
  if disT>distMax:distMax=disT
  ra+=deltaa
  if ra<0:ra+=_2pi
  elif ra>_2pi:ra-=_2pi
  i+=1
def render():
 global s;x,i=0,0
 while x<SW:
  o=s[i][0];t=s[i][1];txo=s[i][2];tyo=s[i][3];depth=s[i][4];lineH=s[i][5];line8th=s[i][6];i+=1
  if not tyo:F(x,0,W,o,(75,)*3)
  if depth>20:
   off=o-tyo;ct=-1
   if tyo:outOfScreen=int(tyo/line8th);begin=1+outOfScreen;end=8-outOfScreen;starty=outOfScreen*line8th
   else:begin,end=1,8;starty=0
   c=(textures[t-1][begin-1]>>(txo))&1;l=line8th
   for y in range(begin,end):
    ct=(textures[t-1][y]>>(txo))&1
    if ct==c:l+=line8th
    else:
     if c:F(x,int(starty)+off,W,ceil(l),(depth,)*3)
     else:F(x,int(starty)+off,W,ceil(l),"k")
     c=ct;starty+=l;l=line8th
   if c:F(x,int(starty)+off,W,ceil(l),(depth,)*3)
   else:F(x,int(starty)+off,W,ceil(l),"k")
  else:F(x,o,WIDTH,lineH,"k")
  if not tyo:F(x,o+lineH,W,1+o,(100,)*3)
  x+=W
def sprites():
 global score,bullets;i=0
 while i<len(ents):
  ex,ey=ents[i][0],ents[i][1]
  if px>ex:xo=20
  else:xo=-20
  if py>ey:yo=20
  else:yo=-20
  ipx,ipxAddXo=int(ex)>>6,int(ex+xo)>>6;ipy,ipyAddYo=int(ey)>>6,int(ey+yo)>>6
  if map[ipy*mapX+ipxAddXo]==0:ex+=xo>>3
  if map[ipyAddYo*mapX+ipx]==0:ey+=yo>>3
  ents[i][0]=ex;ents[i][1]=ey;dsq=(px-ex)**2+(py-ey)**2;d=sqrt(dsq)
  if d>distMax:i+=1;continue
  ax=px+pdx*d;ay=py+pdy*d;a=(ax-ex)**2+(ay-ey)**2;delta=acos(1-a/(2*dsq))
  if delta<=HFOV:
   distToHigh=(px+(pdx*COSPI6-pdy*SINPI6)*d-ex)**2+(py+(pdy*COSPI6+SINPI6*pdx)*d-ey)**2;distToLow=(px+(pdx*COSPI6+pdy*SINPI6)*d-ex)**2+(py+(pdy*COSPI6-SINPI6*pdx)*d-ey)**2
   if distToLow<distToHigh:delta*=-1
   ray=int(HRES*delta/HFOV)+HRES
   if s[ray][7]>d:
    scale=int(1280/d);projx=tan(delta)*d*COSPI6;sx=int((projx*SW)/d+HSW)
    if shooting and projx<10 and projx>-10:
     ents.remove(ents[i]);score-=1
     if not score:win();info();i+=1;continue
    t=int(M()%len(ennemySprites));sprite=ennemySprites[t]
    for r in range(len(sprite)):F((sprite[r][0]-4)*scale+sx,(sprite[r][1]-4)*scale+HSH,sprite[r][2]*scale,sprite[r][3]*scale,(0,255,0))
  if d<20:lose()
  i+=1
def dGun():
 global shooting
 if shooting:F(140,109,10,3,"r");F(170,109,10,3,"r");F(159,91,3,10,"r");F(159,121,3,10,"r");shooting=0
 else:F(140,109,10,3,"w");F(170,109,10,3,"w");F(159,91,3,10,"w");F(159,121,3,10,"w")
def init():
 global pdx,pdy,s,px,py,pa;px,py=64*5.5,64*3.5;pdx,pdy,pa=0,0,pi;pdx,pdy=cos(pa),sin(pa);s.clear()
 for i in range(RAYS):s.append([0]*8)
def spawn():
 ex=R(0,(mapX-1)<<6);ey=R(0,(mapY-1)<<6)
 while map[(ey>>6)*mapX+(ex>>6)]!=0 or dist(px,py,ex,ey)<128:ex=R(0,(mapX-1)<<6);ey=R(0,(mapY-1)<<6)
 ents.append([ex,ey,0])
def rendergun():F(260,150,60,50,(100,100,150));F(240,140,40,40,(100,100,150));F(250,170,20,20,(100,100,150));F(270,145,40,20,(100,100,150));F(250,125,10,15,(100,100,150));F(280,180,45,40,(100,10,30));F(290,220,35,10,(100,10,30));F(282,170,35,10,(100,10,30))
def lose():global on;D("GAME OVER",115,60);on=0
def win():global on;D("YOU WIN",120,60);on=0
def info():D("ennemies left: "+str(score),0,0,"w",(75,)*3);D("bullets: "+str(bullets),0,20,"w",(75,)*3)
def startScreen():
 F(0,0,320,222,"w");D("BAD GUYS SHOOTER",75,20,"r");D("Instructions:",95,80);D("[ARROWS] move",95,110);D("[PARENTHESIS] turn",70,130);D("[OK] open doors and shoot",35,150);D("[XNT] start the game",60,170);D("Shoot all the bad guys!",50,200,"r")
 while 1-K(14):0
on=1
lv=1
def main():
 startScreen();init();rays();dt=0;t1=M();t2=t1;f=t1;c=0
 for i in range(score):spawn()
 i=0
 while on:t2=M();dt=t2-t1;t1=t2;movePl(dt);rays();render();info();sprites();dGun();rendergun()
SW=320
SH=222
HSW=SW>>1
HSH=SH>>1
FOV=pi/3
RAYS=32
HRES=RAYS>>1
W=SW//RAYS
s=[]
distMax=0
deltaa=FOV/RAYS
HFOV=pi/6
COSPI6=sqrt(3)/2
SINPI6=1/2
score=100
if SW%RAYS!=0:print("Screen resolution not compatible\nwith screen width")
else:main()

During your visit to our site, NumWorks needs to install "cookies" or use other technologies to collect data about you in order to:

With the exception of Cookies essential to the operation of the site, NumWorks leaves you the choice: you can accept Cookies for audience measurement by clicking on the "Accept and continue" button, or refuse these Cookies by clicking on the "Continue without accepting" button or by continuing your browsing. You can update your choice at any time by clicking on the link "Manage my cookies" at the bottom of the page. For more information, please consult our cookies policy.