castle_defence_v8_.py

Created by mathieu-croslacoste

Created on May 23, 2025

11.5 KB

Latest version of castle_defence, do not remove the last underscore in the name of the script (on Epsilon), under penalty of the fatal “Memory Error”. Link to the script on the Upsilon worshop: https://yaya-cout.github.io/Upsilon-Workshop/view/98d33455-f060-4c11-ab0a-f290c2318d0a

Credits: - Critor (https://tiplanet.org/forum/memberlist.php?mode=viewprofile&u=2043) pour le feedback/conseils - Afyu (https://tiplanet.org/forum/memberlist.php?mode=viewprofile&u=236306) pour les optimisations/conseils


from ion import keydown as kd
from random import randint as A
from kandinsky import draw_string as D,fill_rect as r,set_pixel as P
from time import sleep as S
R=range
can_place=0;w=0;M=100;HP=100;map_pos=[0,0]
prevPos=None;wave_on="";frozen=0
e_count=[];t_count=[];t_a_count=[]
try:D("",0,0,"k","k",0);s=D
except:
 ref="0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz?!.,:;%+-*/=<>'_-[](){}";font="2jg02oN06Fd07AE04l907dE07dl079907gl07h906lj06kl03aZ06jk07dd07da03bh05lj07IN079h05kj04ad05zj01li03jk06la06jwG6kj03YE07II05jl05jg05jz05gj05gI07Ad003l04cl003d01Bl007p03ca003lE4cj026N0119g4br04aZ007z006j007l006la03l907a003M04dZ005l005g005?005L005hE07AO7A202I200020002W0220022W5Ab00NG007000ge01AKW0uu01KH04HK02G00000u4NK03IJ06IM01IH04IK03MJ06JM0"
 def dml(l,x,y,c1):
  for seq in R(4):
   v=ref.index(font[(ref.index(l)*4)+seq]);b=32
   for i in R(6):
    if v>=b:P(i%3+x,i//3+(2*seq)+y,c1);v-=b
    b//=2
 def dm(s,x,y,c1):
  rx=x
  for l in R(len(s)):
   if s[l]!=" ":dml(s[l],x,y,c1)
   x+=4
 def s(t,x,y,c1="k",c2="w",m=0):
  if m:dm(t,x,y,c1)
  else:D(t,x,y,c1,c2)
def path(check=0,pos=0):
 x,y=Map[0];c=pathCol;x*=8;y*=8
 if check:
  j,k=0,0
  if 2-check:pos=pos[0]*8,pos[1]*8
 for i in Map[1:]:
  l=i[1]*8
  if check:
   if 2-check:
    if x-(l-8)*(i[0]==0)<=pos[0]<=x+l*(i[0]==3)and y-(l-8)*(i[0]==1)<=pos[1]<=y+l*(i[0]==2):return 0
    x+=[0,-l+8,l][(i[0]==0)+2*(i[0]==3)];y+=[0,-l+8,l][(i[0]==1)+2*(i[0]==2)]
   elif j+l-8*(i[0]<2)>pos:return[1,-1][i[0]<2],k
  else:
   if i[0]==0:r(x-l+8,y,l,8,c);x-=l-8
   elif i[0]==1:r(x,y-l+8,8,l,c);y-=l-8
   elif i[0]==2:r(x,y,8,l,c);y+=l
   else:r(x,y,l,8,c);x+=l
  if check:j+=l-8*(i[0]<2);k+=1
 if check==2:return 0,-1
 return 1
def castle():
 r(232,160,40,40,"k");r(238,170,30,30,(70,)*3)
 for i in R(0,30,10):r((239+i),163,7,7,(70,)*3)
 for i in(0,12):r((242+i),174,9,9,"k")
 for i in R(4):r((248+i),(190-i),(10-2*i),10,(149,69,49))
def intro(txt):
 txt="Press [OK] to "+txt
 while kd(4)|kd(52):0
 col=[200,10,25];opt=1
 while(kd(4)|kd(52))^1:
  if col[0]>70 and opt==1:col[0]-=2
  else:
   col[0]+=2
   if col[0]<200:opt=0
   else:opt=1
  s(txt,160-len(txt)*5,200,col,"k")
 while kd(4)|kd(52):0
col=(200,10,25);r(0,0,320,222,"k");s("Castle Defence",90,80,col,"k");S(.4)
intro("continue");r(0,0,320,222,"k");y=0
for i in("Select with arrow keys","1,2,3 and 4 to place turrets","(1 costs 50, 2 costs 100...)","Press [EXE] or select","the play button and press [OK]","to start next wave.","press toolbox to upgrade,","[(]/[)] for last upgrade."):s(i,160-len(i)*5,y,col,"k");y+=20
intro("start");del intro,col,y;r(0,0,320,222,"k")
MapOpts=(
("g",((0,10),(3,30),(1,5),(0,6),(2,8),(0,21),(2,7),(3,6),(1,10),(3,20),(2,9))),
((230,95,15),((0,10),(3,9),(1,5),(0,5),(2,12),(3,12),(1,9),(0,4),(1,4),(3,10),(2,11),(3,10),(1,4),(0,4),(2,6))),
((130,95,0),((0,4),(3,13),(2,5),(0,11),(2,7),(3,19),(1,10),(3,9),(2,14))),
((110,75,0),((0,23),(3,5),(1,16),(3,8),(2,12),(3,9),(1,14),(3,9),(2,14))))
diff=("Easy","Medium","Hard","Hard +")
sel=0
while(kd(4)|kd(52))^1:
 pathCol,Map=MapOpts[sel];path();castle();s("Choose Map : [<-] "+str(sel+1)+" [->]",80,0,(200,10,25),"k");s(diff[sel],0,0,pathCol,"k")
 while(kd(0)|kd(3)|kd(4)|kd(52))^1:0
 sel=(sel+kd(3)-kd(0))%4
 while kd(0)|kd(3):0
 r(0,0,320,222,"k")
del MapOpts,diff,sel
class ennemy():
 def __init__(it,lvl,speed):global e_count;e_count.append(it);it.eAttr=[lvl,lvl,Map[0][0]*8+1,Map[0][1]*8+1,speed,0]
 def draw(it):
  clrs=((20,255,0),(255,127,0),(255,0,20),(127,0,127),(20,0,255),"w")
  if it.eAttr[0]>6:r(it.eAttr[2],it.eAttr[3],4,4,"k")
  elif it.eAttr[0]>0:r(it.eAttr[2],it.eAttr[3],4,4,clrs[it.eAttr[0]-1])
 def remove_e(it):global M,e_count;e_count.remove(it);M+=it.eAttr[1]+3*it.eAttr[4]-3+A(0,2)-w//6;u_s(0)
 def move(it):
  global HP
  if it.eAttr[0]<=0:it.remove_e();return
  j,i=path(2,it.eAttr[5])
  if i==-1:HP-=(it.eAttr[0]*3);it.remove_e();castle();return
  it.eAttr[2+i%2]+=it.eAttr[4]*j;it.eAttr[5]+=it.eAttr[4]
class t_attack:
 def __init__(it,x,y,damage,type,mod):global t_a_count;t_a_count.append(it);it.aAttr=[x,y,damage,type,mod]
 def draw_attack(it):
  if it.aAttr[3]=="normal":r(it.aAttr[0],it.aAttr[1],3,3,(110,)*3)
  elif it.aAttr[3]=="double":r(it.aAttr[0],it.aAttr[1],2,2,(120,)*3)
  elif it.aAttr[3]=="fire":r(it.aAttr[0],it.aAttr[1],3,3,(255,155,155))
  elif it.aAttr[3]=="ice":r(it.aAttr[0],it.aAttr[1],3,3,(155,155,255))
 def move_attack(it):
  r(it.aAttr[0],it.aAttr[1],3,3,"k")
  if len(e_count)>0+it.aAttr[4]:
   if it.aAttr[0]<e_count[it.aAttr[4]].eAttr[2]:
    if it.aAttr[1]<e_count[it.aAttr[4]].eAttr[3]:it.aAttr[0]+=4;it.aAttr[1]+=4
    elif it.aAttr[1]>e_count[it.aAttr[4]].eAttr[3]:it.aAttr[0]+=4;it.aAttr[1]-=4
    else:it.aAttr[0]+=8
   elif it.aAttr[0]>e_count[it.aAttr[4]].eAttr[2]:
    if it.aAttr[1]<e_count[it.aAttr[4]].eAttr[3]:it.aAttr[0]-=4;it.aAttr[1]+=4
    elif it.aAttr[1]>e_count[it.aAttr[4]].eAttr[3]:it.aAttr[0]-=4;it.aAttr[1]-=4
    else:it.aAttr[0]-=8
   elif it.aAttr[1]<e_count[it.aAttr[4]].eAttr[3]:it.aAttr[1]+=8
   elif it.aAttr[1]>e_count[it.aAttr[4]].eAttr[3]:it.aAttr[1]-=8
   hit_target(it)
  else:t_a_count.remove(it);t_a_count.remove(it);del it
def hit_target(it):
 global t_a_count,frozen;attr=it.aAttr
 if attr[0]-9<e_count[attr[4]].eAttr[2]<attr[0]+9 and attr[1]-9<e_count[attr[4]].eAttr[3]<attr[1]+9:
  t_a_count.remove(it);t_a_count.remove(it);r(attr[0],attr[1],3,3,"k");e_count[attr[4]].eAttr[0]-=attr[2]
  if attr[3]=="ice":frozen=1
  del it
class t():
 def __init__(it,x,y,dmg,rel,range,type):global t_count;it.tx=x;it.ty=y;it.dmg=dmg;it.rel=rel;it.r_t=rel;it.ran=range;it.type=type;it.lv=1;it.c=(25,60,130,350)[("normal","double","fire","ice").index(it.type)]
 def draw_t(it):
  instr=(
((1,1,6,6,(70,)*3),(3,4,2,5,(120,)*3)),((1,1,6,6,(70,)*3),(1,4,2,4,(120,)*3),(4,4,2,4,(120,)*3)),((1,1,6,6,(200,50,15)),(3,4,2,5,(70,160,10))),((1,1,6,6,(80,80,180)),(3,4,2,5,(30,30,180))),
((1,1,6,6,(30,)*3),(3,3,2,6,(90,)*3)),((1,1,6,6,(90,)*3),(1,5,1,2,(160,)*3),(3,5,1,2,(160,)*3),(5,5,1,2,(160,)*3)),((1,1,6,6,(220,80,0)),(2,2,4,4,(150,30,0)),(3,6,2,2,(70,160,10))),((1,1,6,6,(80,80,220)),(0,3,8,2,(30,30,170)),(3,0,2,8,(30,30,170))),
((1,1,6,6,(120,)*3),(3,4,2,5,(70,)*3)),((1,1,6,6,(120,)*3),(1,4,2,4,(70,)*3),(4,4,2,4,(70,)*3)),((1,1,6,6,(210,0,0)),(3,4,2,5,"k")),((1,1,6,6,(0,0,120)),(3,4,2,5,(20,20,150))))[("normal","double","fire","ice").index(it.type)+(it.lv==5 and(it.maxUp and 8 or 4)or 0)]
  for i in instr:r(it.tx+i[0],it.ty+i[1],i[2],i[3],i[4])
 def do_t_attack(it):
  if it.r_t>=it.rel:
   e_mod,target=0,0
   for e in e_count:
    if it.tx-it.ran<e.eAttr[2]<it.tx+it.ran and it.ty-it.ran<e.eAttr[3]<it.ty+it.ran:target=1;break
    e_mod+=1
   if target:a=t_attack(it.tx,it.ty,it.dmg,it.type,e_mod);t_a_count.append(a);it.r_t=0
  else:it.r_t+=1
 def upgrade(it,upg_type):
  global M;UPGs=(
((25,0,4,12),(60,1,0,0),(150,0,5,8),(350,),(5,-12,32),(1,8,8)),
((60,0,2,12),(150,1,0,0),(350,0,3,8),(850,),(-1,9,24),(4,0,8)),
((130,0,4,12),(300,2,0,0),(700,0,8,8),(2000,),(0,12,0),(1,4,16)),
((350,0,3,12),(850,2,0,0),(2000,0,5,16),(4500,),(-4,9,16),(10,-6,40)))
  id=("normal","double","fire","ice").index(it.type);I=it.lv-1
  if M>=UPGs[id][I][0]:
   M-=UPGs[id][I][0]
   if it.lv<4:it.c=UPGs[id][it.lv][0];it.dmg+=UPGs[id][I][1];it.rel-=UPGs[id][I][2];it.ran+=UPGs[id][I][3]
   elif it.lv==4:del it.c;it.dmg+=UPGs[id][I+upg_type][0];it.rel-=UPGs[id][I+upg_type][1];it.ran+=UPGs[id][I+upg_type][2];it.maxUp=upg_type-1
   it.lv+=1;mapSel(-1);u_s(0)
def update_et():
 global frozen
 if 1-frozen or A(1,5)==1:
  path()
  for e in e_count:e.move();e.draw()
 frozen=0;S(.03)
 for t in t_count:t.do_t_attack();t.draw_t()
 for a in t_a_count:a.move_attack()
def z(nb,lvl,speed):
 for i in R(nb):
  e_count.append(ennemy(lvl,speed))
  for i in R(9-3*speed):update_et();control()
def start_w():
 global w;w+=1;u_s(1)
 if w==0:z(4,1,1)
 elif w==1:z(3,1,1);z(3,2,1)
 elif w==2:z(6,1,2);z(6,2,1)
 elif w<30:z(1+w//4+w//16,w//4,2);z(1+w//3+w//16,w//3,1);z(2+w//4+w//16,w//3+w%3,1)
 else:z(w//8+w//16,w//5,2);z(w//4+w//16,w//4,2);z(1+w//3+w//16,w//3,1);z(w//4+w//16,w//3+w%3,1)
def mapSel(key):
 global map_pos,can_place,wave_on,prevPos;upg,onWbutton="",0
 if key==52 and wave_on=="":wave_on="-ongoing";btn();start_w();return
 if wave_on!="":
  for a in t_a_count:a.draw_attack()
 if can_place+(prevPos==4):r(map_pos[0]*8,map_pos[1]*8,8,8,"k")
 if prevPos==0:
  path()
  for e in e_count:e.draw()
 elif prevPos==1:
  for t in t_count:t.draw_t()
  t_costs()
 elif prevPos==2:castle()
 elif prevPos==3:btn()
 elif prevPos==4:u_s(1)
 prevPos=-1;map_pos=(map_pos[0]+(key==3)-(key==0))%40,(map_pos[1]+(key==2)-(key==1))%25;can_place=1
 for i in t_count:
  if i.tx==map_pos[0]*8 and i.ty==map_pos[1]*8:can_place,upg,prevPos=0,i,1
 if can_place:
  if 28<map_pos[0]<34 and 19<map_pos[1]<25:can_place,prevPos=0,2
  elif 36<map_pos[0]<40 and map_pos[1]<3:
   can_place,onWbutton,prevPos=0,1,3
   if key==4 and wave_on=="":wave_on="-ongoing";start_w()
  elif map_pos[1]<3:can_place,prevPos=0,4
 if can_place:
  can_place=path(1,map_pos)
  if 1-can_place:prevPos=0
 if can_place:r(map_pos[0]*8+1,map_pos[1]*8+1,6,6,(200,)*3)
 elif upg!="":
  if upg.lv<4:
   r(map_pos[0]*8+1,map_pos[1]*8+1,6,6,(160,160,255));init_disp();s("level "+str(upg.lv)+". "+str(upg.c)+" to level up",0,205,(200,100,50),"k")
   if key==16:upg.upgrade(0);S(.5)
  elif upg.lv==4:
   r(map_pos[0]*8+1,map_pos[1]*8+1,6,6,(255,100,255));init_disp();s("level 4. "+str(upg.c)+" to max ([(] or [)]).",0,205,(200,120,100),"k",1)
   if key in(33,34):upg.upgrade(key-32);S(.5)
  else:init_disp();s("level MAX",0,205,(200,100,50),"k");r(map_pos[0]*8+1,map_pos[1]*8+1,6,6,(255,160,160))
 else:
  if onWbutton:r(map_pos[0]*8+1,map_pos[1]*8+1,6,6,(200,255,200))
  else:r(map_pos[0]*8+1,map_pos[1]*8+1,6,6,(255,160,160))
 S(.12)
def init_disp():r(0,200,238+10*(HP<100+HP<10),50,"k")
def t_costs():init_disp();s("50:  100:  200:  500:",0,205,(200,100,50),"k");r(33,205,12,12,(70,)*3);r(37,211,4,9,(120,)*3);r(93,205,12,12,(70,)*3);r(95,212,3,8,(120,)*3);r(100,212,3,8,(120,)*3);r(153,205,12,12,(200,50,15));r(157,211,4,10,(200,100,50));r(213,205,12,12,(80,80,180));r(217,211,4,10,(30,30,180))
def buy_t(key):
 global M,can_place
 if len(t_count)*2.75>=sum(t.lv for t in t_count)and len(t_count)>4:
  s("upgrade your turrets first",25,50,(200,10,25),"k");S(2);r(25,50,260,18,"k")
  for i in t_count:i.draw_t()
  if wave_on=="":path()
 else:
  if key==42 and M>=50 and can_place:place_t(map_pos[0],map_pos[1],1,40,56,"normal");M-=50;can_place=0
  elif key==43 and M>=100 and can_place:place_t(map_pos[0],map_pos[1],1,19,40,"double");M-=100;can_place=0
  elif key==44 and M>=200 and can_place:place_t(map_pos[0],map_pos[1],3,35,56,"fire");M-=200;can_place=0
  elif key==36 and M>=500 and can_place:place_t(map_pos[0],map_pos[1],3,38,72,"ice");M-=500;can_place=0
  u_s(0);S(.2)
def place_t(x,y,dmg,rel,ran,type):t_count.append(t(x*8,y*8,dmg,rel,ran,type));t_count[-1].draw_t();mapSel(-1)
def btn():
 r(296,0,24,24,(140,)*3)
 if wave_on=="":
  for i in R(10):r(303+i,2+i,1,17-2*i,"w")
 else:r(300,6,16,3,"w");r(300,15,16,3,"w")
def control():
 for k in(42,43,44,36):
  if kd(k):buy_t(k)
 for i in(0,1,2,3,4,16,33,34,52):
  if kd(i):mapSel(i)
def u_s(dw):
 c=(200,10,25)
 if dw:r(0,0,296,18,"k");s("Wave "+str(w)+wave_on,0,0,c,"k")
 s("   Money "+str(M),200-10*len(str(M)),0,(200,100,50),"k");r(238,203,82,18,"k");s("HP : "+str(HP),258-10*((HP>99)+(HP>9)),203,c,"k")
btn();t_costs();path();castle()
while HP>0:
 while len(e_count):
  update_et();control()
  if HP<=0:break
 wave_on="";t_a_count.clear();path();castle();btn()
 for i in t_count:i.rt=999;i.draw_t()
 u_s(1)
 while wave_on==""and HP>0:control()
r(0,0,320,222,"k");s("GAME OVER",115,50,(200,10,25),"k");s("you lost at wave "+str(w),70,100,(200,10,25),"k")

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.