Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
980 changes: 980 additions & 0 deletions source/1.txt

Large diffs are not rendered by default.

Binary file added source/__pycache__/__init__.cpython-39.pyc
Binary file not shown.
Binary file added source/__pycache__/constants.cpython-39.pyc
Binary file not shown.
Binary file added source/__pycache__/main.cpython-39.pyc
Binary file not shown.
Binary file added source/__pycache__/tool.cpython-39.pyc
Binary file not shown.
Binary file added source/component/__pycache__/__init__.cpython-39.pyc
Binary file not shown.
Binary file added source/component/__pycache__/grave.cpython-39.pyc
Binary file not shown.
Binary file added source/component/__pycache__/map.cpython-39.pyc
Binary file not shown.
Binary file not shown.
Binary file added source/component/__pycache__/plant.cpython-39.pyc
Binary file not shown.
Binary file not shown.
134 changes: 134 additions & 0 deletions source/component/grave.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
__author__ = 'marble_xu'

import pygame as pg
from .. import tool
from .. import constants as c

class Grave(pg.sprite.Sprite):
def __init__(self, x, y):
pg.sprite.Sprite.__init__(self)

self.name = c.GRAVE
self.frames = []
self.frame_index = 0
self.loadImages()
self.frame_num = len(self.frames)

self.image = self.frames[self.frame_index]
self.rect = self.image.get_rect()
self.rect.centerx = x
self.rect.bottom = y

self.health = 10 # Grave health
self.dead = False

self.animate_timer = 0
self.animate_interval = 150

def loadImages(self):
# Load grave images
grave_name = self.name
self.loadFrames(self.frames, grave_name, 1)

def loadFrames(self, frame_list, name, scale):
# Load frames from sprite sheet
frame_rect = tool.GRAVE_RECT[name]
for i in range(frame_rect['frame_num']):
x = frame_rect['x'] + i * frame_rect['width']
y = frame_rect['y']
width = frame_rect['width']
height = frame_rect['height']
image = tool.get_image(tool.GFX[name], x, y, width, height, c.BLACK, scale)
frame_list.append(image)

def update(self, game_info):
self.current_time = game_info[c.CURRENT_TIME]
self.animation()

def animation(self):
if (self.current_time - self.animate_timer) > self.animate_interval:
self.frame_index += 1
if self.frame_index >= self.frame_num:
self.frame_index = 0
self.animate_timer = self.current_time
self.image = self.frames[self.frame_index]

def setDamage(self, damage):
self.health -= damage
if self.health <= 0:
self.dead = True
self.kill()

class GraveBuster(pg.sprite.Sprite):
def __init__(self, x, y, grave):
pg.sprite.Sprite.__init__(self)

self.name = c.GRAVE_BUSTER
self.frames = []
self.frame_index = 0
self.loadImages()
self.frame_num = len(self.frames)

self.image = self.frames[self.frame_index]
self.rect = self.image.get_rect()
self.rect.centerx = x
self.rect.bottom = y

self.grave = grave
self.eat_timer = 0
self.eat_duration = c.GRAVE_BUSTER_EAT_TIME
self.state = c.IDLE

self.animate_timer = 0
self.animate_interval = 150

def loadImages(self):
# Load grave buster images
buster_name = self.name
self.loadFrames(self.frames, buster_name, 1)

def loadFrames(self, frame_list, name, scale):
# Load frames from sprite sheet
frame_rect = tool.PLANT_RECT[name]
for i in range(frame_rect['frame_num']):
x = frame_rect['x'] + i * frame_rect['width']
y = frame_rect['y']
width = frame_rect['width']
height = frame_rect['height']
image = tool.get_image(tool.GFX[name], x, y, width, height, c.BLACK, scale)
frame_list.append(image)

def update(self, game_info):
self.current_time = game_info[c.CURRENT_TIME]
self.handleState()
self.animation()

def handleState(self):
if self.state == c.IDLE:
self.idling()
elif self.state == c.ATTACK:
self.attacking()

def idling(self):
# Start eating grave
self.state = c.ATTACK
self.eat_timer = self.current_time

def attacking(self):
# Check if done eating
if (self.current_time - self.eat_timer) > self.eat_duration:
# Destroy both grave and grave buster
self.grave.setDamage(10) # Enough damage to destroy grave
self.kill()

def animation(self):
if (self.current_time - self.animate_timer) > self.animate_interval:
self.frame_index += 1
if self.frame_index >= self.frame_num:
self.frame_index = self.frame_num - 1 # Stay on last frame
self.animate_timer = self.current_time
self.image = self.frames[self.frame_index]

def canAttack(self, zombie):
# Grave buster doesn't attack zombies
return False
11 changes: 6 additions & 5 deletions source/component/menubar.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,17 @@
c.CARD_CHERRYBOMB, c.CARD_THREEPEASHOOTER, c.CARD_REPEATERPEA, c.CARD_CHOMPER,
c.CARD_PUFFSHROOM, c.CARD_POTATOMINE, c.CARD_SQUASH, c.CARD_SPIKEWEED,
c.CARD_JALAPENO, c.CARD_SCAREDYSHROOM, c.CARD_SUNSHROOM, c.CARD_ICESHROOM,
c.CARD_HYPNOSHROOM, c.CARD_WALLNUT, c.CARD_REDWALLNUT]
c.CARD_HYPNOSHROOM, c.CARD_WALLNUT, c.CARD_REDWALLNUT, c.CARD_GRAVE_BUSTER]
plant_name_list = [c.SUNFLOWER, c.PEASHOOTER, c.SNOWPEASHOOTER, c.WALLNUT,
c.CHERRYBOMB, c.THREEPEASHOOTER, c.REPEATERPEA, c.CHOMPER,
c.PUFFSHROOM, c.POTATOMINE, c.SQUASH, c.SPIKEWEED,
c.JALAPENO, c.SCAREDYSHROOM, c.SUNSHROOM, c.ICESHROOM,
c.HYPNOSHROOM, c.WALLNUTBOWLING, c.REDWALLNUTBOWLING]
plant_sun_list = [50, 100, 175, 50, 150, 325, 200, 150, 0, 25, 50, 100, 125, 25, 25, 75, 75, 0, 0]
c.HYPNOSHROOM, c.WALLNUTBOWLING, c.REDWALLNUTBOWLING, c.GRAVE_BUSTER]
plant_sun_list = [50, 100, 175, 50, 150, 325, 200, 150, 0, 25, 50, 100,
125, 25, 25, 75, 75, 0, 0, 75]
plant_frozen_time_list = [7500, 7500, 7500, 30000, 50000, 7500, 7500, 7500, 7500, 30000,
30000, 7500, 50000, 7500, 7500, 50000, 30000, 0, 0]
all_card_list = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]
30000, 7500, 50000, 7500, 7500, 50000, 30000, 0, 0, 7500]
all_card_list = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17]

def getSunValueImage(sun_value):
font = pg.font.SysFont(None, 22)
Expand Down
30 changes: 24 additions & 6 deletions source/component/plant.py
Original file line number Diff line number Diff line change
Expand Up @@ -453,6 +453,8 @@ def __init__(self, x, y, bullet_group):
Plant.__init__(self, x, y, c.PUFFSHROOM, c.PLANT_HEALTH, bullet_group)
self.can_sleep = True
self.shoot_timer = 0
self.life_timer = 0 # Timer for automatic disappearance
self.life_duration = 30000 # 30 seconds

def loadImages(self, name, scale):
self.idle_frames = []
Expand All @@ -469,15 +471,25 @@ def loadImages(self, name, scale):

self.frames = self.idle_frames

def handleState(self):
# Check if PuffShroom should disappear
if self.life_timer == 0:
self.life_timer = self.current_time
elif (self.current_time - self.life_timer) > self.life_duration:
self.health = 0 # Set health to 0 to trigger removal
else:
super().handleState()

def attacking(self):
if (self.current_time - self.shoot_timer) > 3000:
self.bullet_group.add(Bullet(self.rect.right, self.rect.y + 10, self.rect.y + 10,
c.BULLET_MUSHROOM, c.BULLET_DAMAGE_NORMAL, True))
self.shoot_timer = self.current_time

def canAttack(self, zombie):
# Close range attack (3 grids)
if (self.rect.x <= zombie.rect.right and
(self.rect.right + c.GRID_X_SIZE * 4 >= zombie.rect.x)):
(self.rect.right + c.GRID_X_SIZE * 3 >= zombie.rect.x)):
return True
return False

Expand Down Expand Up @@ -736,15 +748,21 @@ def idling(self):
if not self.is_big:
if self.change_timer == 0:
self.change_timer = self.current_time
elif (self.current_time - self.change_timer) > 25000:
elif (self.current_time - self.change_timer) > 60000: # 60 seconds to grow
self.changeFrames(self.big_frames)
self.is_big = True

# Produce sun every 15 seconds
if self.sun_timer == 0:
self.sun_timer = self.current_time - (c.FLOWER_SUN_INTERVAL - 6000)
elif (self.current_time - self.sun_timer) > c.FLOWER_SUN_INTERVAL:
self.sun_group.add(Sun(self.rect.centerx, self.rect.bottom, self.rect.right,
self.rect.bottom + self.rect.h // 2, self.is_big))
self.sun_timer = self.current_time
elif (self.current_time - self.sun_timer) > 15000:
# Small sunshroom produces 15 sun, big one produces 25
sun_value = 25 if self.is_big else 15
# Create sun with appropriate value
sun = Sun(self.rect.centerx, self.rect.bottom, self.rect.right,
self.rect.bottom + self.rect.h // 2, self.is_big)
sun.sun_value = sun_value
self.sun_group.add(sun)
self.sun_timer = self.current_time

class IceShroom(Plant):
Expand Down
10 changes: 9 additions & 1 deletion source/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -177,4 +177,12 @@

#BACKGROUND
BACKGROUND_DAY = 0
BACKGROUND_NIGHT = 1
BACKGROUND_NIGHT = 1

#GRAVE
GRAVE = 'Grave'
GRAVE_BUSTER = 'GraveBuster'
GRAVE_BUSTER_EAT_TIME = 5000 # 5 seconds to eat grave

#PLANT CARD INFO
CARD_GRAVE_BUSTER = 'card_grave_buster'
Binary file added source/state/__pycache__/__init__.cpython-39.pyc
Binary file not shown.
Binary file added source/state/__pycache__/level.cpython-39.pyc
Binary file not shown.
Binary file added source/state/__pycache__/mainmenu.cpython-39.pyc
Binary file not shown.
Binary file added source/state/__pycache__/screen.cpython-39.pyc
Binary file not shown.
48 changes: 47 additions & 1 deletion source/state/level.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import pygame as pg
from .. import tool
from .. import constants as c
from ..component import map, plant, zombie, menubar
from ..component import map, plant, zombie, menubar, grave

class Level(tool.State):
def __init__(self):
Expand Down Expand Up @@ -38,10 +38,17 @@ def setupBackground(self):
self.level = pg.Surface((self.bg_rect.w, self.bg_rect.h)).convert()
self.viewport = tool.SCREEN.get_rect(bottom=self.bg_rect.bottom)
self.viewport.x += c.BACKGROUND_OFFSET_X

# Add night blue filter for night levels
if self.background_type == c.BACKGROUND_NIGHT:
self.night_filter = pg.Surface((self.bg_rect.w, self.bg_rect.h))
self.night_filter.fill((0, 0, 100)) # Dark blue color
self.night_filter.set_alpha(80) # Transparency level

def setupGroups(self):
self.sun_group = pg.sprite.Group()
self.head_group = pg.sprite.Group()
self.grave_group = pg.sprite.Group()

self.plant_groups = []
self.zombie_groups = []
Expand Down Expand Up @@ -127,6 +134,10 @@ def initPlay(self, card_list):
self.setupGroups()
self.setupZombies()
self.setupCars()

# Generate graves for night levels
if self.background_type == c.BACKGROUND_NIGHT:
self.generateGraves()

def play(self, mouse_pos, mouse_click):
if self.zombie_start_time == 0:
Expand All @@ -148,6 +159,7 @@ def play(self, mouse_pos, mouse_click):

self.head_group.update(self.game_info)
self.sun_group.update(self.game_info)
self.grave_group.update(self.game_info)

if not self.drag_plant and mouse_pos and mouse_click[0]:
result = self.menubar.checkCardClick(mouse_pos)
Expand Down Expand Up @@ -186,6 +198,23 @@ def play(self, mouse_pos, mouse_click):
self.checkCarCollisions()
self.checkGameState()

def generateGraves(self):
import random
num_graves = random.randint(5, 8)

for _ in range(num_graves):
# Random column between 2 and 5 (inclusive)
map_x = random.randint(2, 5)
# Random row between 0 and 4 (inclusive)
map_y = random.randint(0, self.map_y_len - 1)

# Get the position for the grave
x, y = self.map.getMapGridPos(map_x, map_y)

# Create and add the grave
new_grave = grave.Grave(x, y)
self.grave_group.add(new_grave)

def createZombie(self, name, map_y):
x, y = self.map.getMapGridPos(0, map_y)
if name == c.NORMAL_ZOMBIE:
Expand Down Expand Up @@ -250,6 +279,15 @@ def addPlant(self):
new_plant = plant.WallNutBowling(x, y, map_y, self)
elif self.plant_name == c.REDWALLNUTBOWLING:
new_plant = plant.RedWallNutBowling(x, y)
elif self.plant_name == c.GRAVE_BUSTER:
# Check if there's a grave at this position
for g in self.grave_group:
if g.rect.collidepoint(x, y):
new_plant = grave.GraveBuster(x, y, g)
break
else:
# No grave found, can't plant
return

if new_plant.can_sleep and self.background_type == c.BACKGROUND_DAY:
new_plant.setSleep()
Expand Down Expand Up @@ -529,11 +567,19 @@ def drawZombieFreezeTrap(self, i, surface):

def draw(self, surface):
self.level.blit(self.background, self.viewport, self.viewport)

# Apply night filter for night levels
if self.background_type == c.BACKGROUND_NIGHT:
self.level.blit(self.night_filter, (0, 0))

surface.blit(self.level, (0,0), self.viewport)
if self.state == c.CHOOSE:
self.panel.draw(surface)
elif self.state == c.PLAY:
self.menubar.draw(surface)
# Draw graves
self.grave_group.draw(surface)

for i in range(self.map_y_len):
self.plant_groups[i].draw(surface)
self.zombie_groups[i].draw(surface)
Expand Down
Loading