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")