From 1248390b1137e1f3c46541c3072174d1b2e9e79a Mon Sep 17 00:00:00 2001 From: ScriptSorcerer20 Date: Thu, 9 Jul 2026 08:24:12 +0200 Subject: [PATCH] added now functional cannon needs projectiles (and alot more) --- assets/images/entities/towers/0.png | Bin 0 -> 226 bytes assets/images/entities/towers/1.png | Bin 0 -> 247 bytes assets/images/entities/towers/2.png | Bin 0 -> 232 bytes assets/images/entities/towers/3.png | Bin 0 -> 218 bytes assets/images/entities/towers/4.png | Bin 0 -> 220 bytes assets/images/entities/towers/5.png | Bin 0 -> 247 bytes assets/images/entities/towers/6.png | Bin 0 -> 237 bytes assets/images/entities/towers/7.png | Bin 0 -> 202 bytes assets/images/entities/towers/8.png | Bin 0 -> 222 bytes .../images/entities/towers/cannon/shoot/0.png | Bin 226 -> 208 bytes .../images/entities/towers/cannon/shoot/1.png | Bin 247 -> 223 bytes .../images/entities/towers/cannon/shoot/2.png | Bin 232 -> 212 bytes .../images/entities/towers/cannon/shoot/3.png | Bin 218 -> 205 bytes .../images/entities/towers/cannon/shoot/4.png | Bin 220 -> 194 bytes .../images/entities/towers/cannon/shoot/5.png | Bin 247 -> 213 bytes .../images/entities/towers/cannon/shoot/6.png | Bin 237 -> 221 bytes .../images/entities/towers/cannon/shoot/7.png | Bin 202 -> 179 bytes .../images/entities/towers/cannon/shoot/8.png | Bin 222 -> 205 bytes game/enemy.py | 57 +++++++++++++++- game/game.py | 2 +- game/screens/game_play.py | 13 ++-- game/tower.py | 62 ++++++++++++++++-- game/util.py | 24 ++++++- 23 files changed, 141 insertions(+), 17 deletions(-) create mode 100644 assets/images/entities/towers/0.png create mode 100644 assets/images/entities/towers/1.png create mode 100644 assets/images/entities/towers/2.png create mode 100644 assets/images/entities/towers/3.png create mode 100644 assets/images/entities/towers/4.png create mode 100644 assets/images/entities/towers/5.png create mode 100644 assets/images/entities/towers/6.png create mode 100644 assets/images/entities/towers/7.png create mode 100644 assets/images/entities/towers/8.png diff --git a/assets/images/entities/towers/0.png b/assets/images/entities/towers/0.png new file mode 100644 index 0000000000000000000000000000000000000000..2fcde4bc255e8ccc1a76a1dc259d0f5e17e048a7 GIT binary patch literal 226 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`jKx9jP7LeL$-D$|=6Jd|hFJ6_ zrz9l&IN!jmp*h0<1nyK9uuT_YQ;QMm>FJ5M=_2i*b0CXBSy{Px(H_G&>oxLwE{kp{ zJSbq!V9Sum5bp4((NRp=Tg6yelF{r5}E)$PhboH literal 0 HcmV?d00001 diff --git a/assets/images/entities/towers/2.png b/assets/images/entities/towers/2.png new file mode 100644 index 0000000000000000000000000000000000000000..08f55e469fe755c75a9b046ab7e53a3c22c5b7e0 GIT binary patch literal 232 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`jKx9jP7LeL$-D$|7J0fjhFJ6_ zrz9l&IN!jmp*h0<1nyK9uuT_YQ;QMm>FJ5M=_2i*b0CXBSy{Px(H_G&>oxLwE{kp{ zJSbq!V9Sum5bp4(!T!-dBcRXn6?MKt$Q2ib4YbuZkRoNhQv9!CyE&i=Q2XB|8@^C zN_08LeWLELSg%2Y%-o(EM#9@(I9>0!&LDB(%&DXVAedp7D^(b$z|O!hMPIo#EzwX4 P=t2fhS3j3^P6Px#u}MThR5*>Ll06E-KoCaX1ntBtgj9Amf!xDmm;=SrSl9|_?5xBqY#VT`5;nUt z*`Nap%Pim9Z+-;u{1JuqFt}zl#K&iFO>z%&;SsBZ*ui#xSARDFA~Lt>^;T^xaaGo{ z-0R(|kfe#47@#Wa#8m>Y#7mF~kP}-Fa}NNnCzl19fajy2zwNRBbqWB7<0oej;ynm! x)5bVPI8Jquv~k74MZg&6wguqQb?Zv_CBDo^K=HfM5+48n002ovPDHLkV1gcmWj_D_ literal 0 HcmV?d00001 diff --git a/assets/images/entities/towers/6.png b/assets/images/entities/towers/6.png new file mode 100644 index 0000000000000000000000000000000000000000..e1e9ba0fcf1853755b8726effaafe2b966692d20 GIT binary patch literal 237 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`jKx9jP7LeL$-D$|mV3H5hFJ8T zon**$$U(sMKFix9+ASevW{r0aC(mF$dO6|-SLT~FN3?UK3)YI=d9wapS)GKKopt>` zp8E{vc_zPpCc^OgZRjVv(8;eCg-_LFXGjzC|M*FIbx3jWcERrzeL+i3WwK?wDtqX~ zHRaKHmZ0D4E39iu3+#3nvCrReU+dWEXUiMTFdoSJf6D5jQfH#OsEUhH>Xgugkm4YP l2RFVm%xP1sZw=bR=<8s0aJBKJML;Jrc)I$ztaD0e0suwJUr_)6 literal 0 HcmV?d00001 diff --git a/assets/images/entities/towers/7.png b/assets/images/entities/towers/7.png new file mode 100644 index 0000000000000000000000000000000000000000..b1f1bbcfbf4890f2b98bf7d67dbd8376366c61c9 GIT binary patch literal 202 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`jKx9jP7LeL$-D$|T0C7GLo9l) zPV(k$Fc5ILpR}ZbrAsrQk!wN|XI5kXq@t#lWzkg+56t;>)90R_8${hZ_dHIX9?>}l^N~NraBx_%VJ@;{qV!T$UFH? zclf4%b<1HDy37ADwAnlMKC?mZVzWtRCoXc%-Yrv+!Y)`3bPt24tDnm{r-UW|#(7J0 literal 0 HcmV?d00001 diff --git a/assets/images/entities/towers/8.png b/assets/images/entities/towers/8.png new file mode 100644 index 0000000000000000000000000000000000000000..4def5bd3edc2061a0b4277109b8a9056183b2121 GIT binary patch literal 222 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`jKx9jP7LeL$-D$|W_Y?dhFJ7o zoxEGH*+9VMzTvxOe@+Kskw*tY7HTP!NSY)HTN^DC66}<0;d^qR(R|Xgyz*~l?^rfy zXl#;d`2Kw1)(IJ`f}HynvnC0O2nc@K67|0_YNcM?;|J$i|1ijIQ3z#k)|B+AeDz?_ zL}}xBlbZSBzGs_AI{b+)DE?*X^I4c7d;9Xu!VeO@*KX2!P>>zX@M~SS{KIISptbTg ULDouFfo^2*boFyt=akR{0KI%s>;M1& literal 0 HcmV?d00001 diff --git a/assets/images/entities/towers/cannon/shoot/0.png b/assets/images/entities/towers/cannon/shoot/0.png index 2fcde4bc255e8ccc1a76a1dc259d0f5e17e048a7..af47b3f55cbb933322c8ae2bca1cf00fc8e7d2be 100644 GIT binary patch delta 135 zcmV;20C@l60nh=EF+++;L_t(IjbmWI1C$g@{wpb%{Kuz;36}&TBjbN;Y@!Vy7BJ$n zT~Xc`MeP2=vkd?L|Hn{Iu?vQQ0T_XTEdXHvTakAoIof}X#Mun@AD+cfh)s+EUrNL# pjx8nP1({h{|1-0)@a9H}0RYCbE#a`DyZHbB002ovPDHLkV1lW-J4yfm delta 153 zcmcb>_=s_Wq1zl!7sn8b-sF^ogdgV{m^CzK7=XZ?>H@auLTqX=LOne_5jS0=9dr(4 zF(@l5H!s>_IA^^^e$QpmErkaK%o%JM5*flLmMD8jHM>2^Y5UE8K1t(<+OY#b(4lMm z(5v+S|Kw-ZYuFArq=ktw1Hs`hnU@PR59Tu%GB9j8rw|?Z>U|9Z5O})!xvX}Kbie!fCeNsOtiCL kATulLe`Zz|=^9A^08`F40CP^=IsgCw07*qoM6N<$g5?uUAOHXW delta 206 zcmV;<05SjH0rvrrF@Ld1L_t(Ijn$Je3c^4TMc;_`h{+Ll#n#$`SeR4f2v)K50_k%J zwH5@8R}edku`v>K#|?so9|m^V`JX=nUjW5GHW~wPY&O;i1M5RXg74k}eGwE85z@pz z`!w;Ui{Ci-bAi_bwy+LqI;aarUAU-&p+%k*yC^y6V85RCCPxEEn}y0b{Paq;Jiue; zE>7ny0IKiNQ9vu_^mdsjpdu2icD7scDUFLf2Vhl}@fE(s9RxozaY}WPRsaA107*qo IM6N<$f(JcX9smFU diff --git a/assets/images/entities/towers/cannon/shoot/2.png b/assets/images/entities/towers/cannon/shoot/2.png index 08f55e469fe755c75a9b046ab7e53a3c22c5b7e0..5eac6806a2f86939b8336df8a5c248f0525853a9 100644 GIT binary patch delta 135 zcmV;20C@lC0n`DIF++|?L_t(IjbmWI1C$g@{wpb%{Kuz;36}&TBjbN;Y@!Vy7BJ$n zT~Xc`MeP2=vkd?L|Hn{Iu?vQQ0T_XTEdXHvTakAsIk>`rfq{VmTS8)BV893ioGB5T pIJT6C7i4B-{m;zG!kZf@1^|fYDge!RjGh1h002ovPDHLkV1h^9I1T^+ delta 155 zcmcb@_=0hQq1z%)7sn8b-sF^ogdgV{m^CzK7=XZ?>H@auLTqX=LOne_5jS0=9dr(4 zF(@l5H!s>_IA^^^e$QpmErkaK%o%JM5*flLmZ*F2@bL7!sAPQR|D|BZ{|`FMKydi~ z{}qw!hZ)#xGUa29{g@LJjJ3NPfgthMmdqp7jUfySmG-g~US}L8F#v(5tDnm{r-UW| Dn9w;L diff --git a/assets/images/entities/towers/cannon/shoot/3.png b/assets/images/entities/towers/cannon/shoot/3.png index 7c597e6239fb5f0cad3f8f1b0d04ccd7bf40aa53..9d9b9aa358e0b60be630e6669a3128851073d61d 100644 GIT binary patch delta 164 zcmV;V09*gs0nGuBF@J|iL_t(IjbmV-B~Vf@`LCp4@*kfXCR`GXjEw)Wv57W-Sip$O zc13w(6tVjc&ocb~{~tp=^<99?c6_d&t_$$lj>{EzU9dOs6G0c?G~nhXCWf;Ic?cT7 zf=fbO?;lY?gf$iz7#J9^r2_^A28>w1nUJuFV@pVQL1tFg{~gS%EWDYCVgLX|t1uWq SFs}>%0000sia`Sprl|zo&k)Ej9A$uxquje z)c{6DMg~QBV>n+?-WaDVsP6)F+X=aXx-P(FJ3d$7b-~`iPo%j38Qi?Y#BlZ?4?zQ1 za7n1^{Ua)f7}4_r%#Y|{asT021_lNOhX4Ojvkry<40w_*x+-Rz2EagORu%&T0|UeK fY14@J0;vE1Ssfc|KI@vl00000NkvXXu0mjfIKWG0 diff --git a/assets/images/entities/towers/cannon/shoot/4.png b/assets/images/entities/towers/cannon/shoot/4.png index 38638d8203fce4487b3f9f4c93a255747bbba43e..bc168e39bb4112815b205bd0cc67f28002a325b3 100644 GIT binary patch delta 153 zcmV;K0A~N(0m1>0F@JnXL_t(IjbmUK1&lZ)7#aT~^Z)<^it@%NV)q}O z#cBwq02a;o3_<1K(TvLw1cM08*bHI9r*LoJCxV9IG~nhXCWf;Ic?cT7f=fbO?;jxt zGB98ZL>L=eG{OMZK*VJbGQgFXaDvRNtpAx=Sy&CD8UO$iKoT}2t{$ge00000NkvXX Hu0mjfK@UF$ delta 179 zcmV;k08IbF0o(zQF@KgxL_t(IjbmhBVEE5K0brs;^WZRm1(yUPBO?m`|9|X3h1CE? zMn(ojd1Dl@`w!1zH3Y*8*fcXRFfb^}8)LW%!vI{G@fiX)fRJWfhTx5by@8(y8i>i&I`zX!xoJ&fE=BS*fJBg=qy6lh}QrZ$jr)OU|?Wi hm_BV9ab6$?005LPBYkFJF&Y2>002ovPDHLkV1gq|N8$hg diff --git a/assets/images/entities/towers/cannon/shoot/5.png b/assets/images/entities/towers/cannon/shoot/5.png index 01c0cba3ac5c2b226e0938201906bc391ec13e5b..6b3f376c1d88fca5a29560a3bc4811499b776612 100644 GIT binary patch delta 172 zcmV;d08{_>0o4JJF@KLqL_t(IjbmV-75M$h>_1gB!`QeCWF*!FtbC`is>NvlBO@b< z0ssI1C*A-?M#le&^2R7)_aC0cY6zwP7R~q!LFM4ljLQ%Ng9y#o3}M2jaBtuzf`;KV z;N~SJhO-BG2pYhGOF~`mA0Y=aFklNr7#mwO!T{Dl#AOgNz$KNKaDvRNtpAx=S>$Ib aVgLZ5t46KLU_e3u0000MY1>n+k>q__~zRXBK@w?Iz9{>OV07*qo IM6N<$g5rH&M*si- diff --git a/assets/images/entities/towers/cannon/shoot/6.png b/assets/images/entities/towers/cannon/shoot/6.png index e1e9ba0fcf1853755b8726effaafe2b966692d20..6b1f6c73954f48d25196d31c25a2c1818aa00e52 100644 GIT binary patch delta 180 zcmV;l089Vv0o?(RF@KjyL_t(IjbmiM0)Bro`;SeWmG2ZIHgTK)W)Lm|85kHCaGJ=- z$cUop|NsA3)nXT9Wc;rvZ;T>#|KVAzhF}U{(TvX!CR|=177V-rDE`6ce;7ay2fQxW z8~BN!3ve26^AZ!o*@HX;4Pe0~p|1Cjs35|c4j3317_cQI1}!Wp5@$-pCXOpP;{=&m iS^qP$vT$Z3ssI3V0y%CnDm!MecDF*;%7Ekf=8>>`v zQ@0WKc#cw}%4r5@>UQBu3Y6l{1XBA4s5^Xha6Y&)2cNj#Dm})_=BI$n0l;qm$~w^^ yiD5?-Bt#-*lvQ9qZ&4nHptNlo4Xu%WCsy{ P00000NkvXXu0mjfUpPeo diff --git a/assets/images/entities/towers/cannon/shoot/8.png b/assets/images/entities/towers/cannon/shoot/8.png index 4def5bd3edc2061a0b4277109b8a9056183b2121..fa6493fc580997054a4e67a7800b2a7f2ff57e5f 100644 GIT binary patch delta 164 zcmV;V09*gw0nGuBF@J|iL_t(IjbmWI1C$g@{wpb%{Kuz;36}&TBjbN;Y@!Vy7BJ$n zT~Xc`MeP2=vkd?L|Hn{Iu?vQQ0T_XTEdXHvTR`G;!S)6thV2bT1YN*_OS5OgIf9z8 zy5PqARH8LA;0gl<1_lOf35kJ$0V51>rbKMw*is^1keQYBKOHkG3vX_u7ytlbRx89E SG~cZN000089@A7aC7=OOfK^i0Bg6WvlHN8_kFS| j;BaeO0Q# 0: + self.fire_cooldown -= 1 + + if self.target is not None and self.fire_cooldown == 0: + print("shoot fired") + self.shoot() def set_action(self, action): if action != self.action: @@ -33,15 +49,16 @@ class Tower(pg.sprite.Sprite): )) anim_img = self.animation.img() + rotated_img = pg.transform.rotate(anim_img, self.angle) scaled_image = pg.transform.scale( - anim_img, + rotated_img, ( - int(anim_img.get_width() * camera.zoom), - int(anim_img.get_height() * camera.zoom) + int(rotated_img.get_width() * camera.zoom), + int(rotated_img.get_height() * camera.zoom) ) ) - rect = scaled_image.get_rect(center=screen_pos) + self.rect = scaled_image.get_rect(center=screen_pos) if self.selected: scaled_range = int(self.range * camera.zoom) @@ -50,7 +67,40 @@ class Tower(pg.sprite.Sprite): range_rect = range_surf.get_rect(center=screen_pos) surf.blit(range_surf, range_rect) - surf.blit(scaled_image, rect) + surf.blit(scaled_image, self.rect) + + def rotate(self): + if self.target is None: + # print("no target") + return # keep last angle, or reset to a default — your call + + tower_world_pos = pg.Vector2( + tile_to_pixel(self.tile_pos, self.game.tilemap.tile_size, True) + ) + direction = self.target.pos - tower_world_pos + self.angle = math.degrees(math.atan2(-direction[1], direction[0])) -90 + # print(f"angle {self.angle}") + + def pick_target(self, enemy_group): + closest = None + closest_dist = float('inf') + + tower_world_pos = pg.Vector2( + tile_to_pixel(self.tile_pos, self.game.tilemap.tile_size, True) + ) + + for enemy in enemy_group: + distance = tower_world_pos.distance_to(enemy.pos) + if distance <= self.range and distance < closest_dist: + closest = enemy + closest_dist = distance + + self.target = closest # explicitly None if nothing in range + + def shoot(self): + self.target.take_damage(self.damage) + self.animation.play_animation() + self.fire_cooldown = self.fire_rate class TowerGroup(pg.sprite.Group): def render(self, surf, camera): diff --git a/game/util.py b/game/util.py index 3c750f8..9d63f3a 100644 --- a/game/util.py +++ b/game/util.py @@ -15,6 +15,16 @@ def load_images(dir_path): images.append(load_image(dir_path + '/' + img_name)) return images +def load_image_alpha(path): + img = pg.image.load(img_path + path).convert_alpha() + return img + +def load_images_alpha(dir_path): + images = [] + for img_name in os.listdir(img_path + dir_path): + images.append(load_image_alpha(dir_path + '/' + img_name)) + return images + def tile_to_pixel(waypoint, tile_size, center=False): if center: return ( @@ -40,17 +50,27 @@ class Animation: self.img_duration = img_dur self.done = False self.frame = 0 + self.playing = False # not playing until told to def copy(self): return Animation(self.images, self.img_duration, self.loop) def update(self): + if not self.playing: + return + if self.loop: self.frame = (self.frame + 1) % (self.img_duration * len(self.images)) else: - self.frame = min(self.frame + 1, self.img_duration * len(self.images)) + self.frame = min(self.frame + 1, self.img_duration * len(self.images) - 1) if self.frame >= self.img_duration * len(self.images) - 1: self.done = True + self.playing = False + + def play_animation(self): + self.playing = True + self.done = False + self.frame = 0 def img(self): - return self.images[int(self.frame / self.img_duration) - 1] \ No newline at end of file + return self.images[self.frame // self.img_duration] \ No newline at end of file