diff --git a/resources/data/map/level_0.json b/resources/data/map/level_0.json index 77b1ded..fe4ab6f 100644 --- a/resources/data/map/level_0.json +++ b/resources/data/map/level_0.json @@ -2,6 +2,7 @@ "background_type":2, "init_sun_value":5000, "shovel":1, + "spawn_zombies":"zombie_list", "zombie_list":[ {"time":1000, "map_y":2, "name":"FootballZombie"}, {"time":60000, "map_y":2, "name":"Zombie"} diff --git a/resources/data/map/level_1.json b/resources/data/map/level_1.json index 359abec..a6c40b3 100644 --- a/resources/data/map/level_1.json +++ b/resources/data/map/level_1.json @@ -2,6 +2,9 @@ "background_type": 0, "init_sun_value": 50, "shovel": 1, + "spawn_zombies":"auto", + "included_zombies":["Zombie"], + "num_flags":2, "zombie_list": [ { "time": 20000, "name": "Zombie" }, { "time": 40000, "name": "FlagZombie" }, diff --git a/resources/data/map/level_2.json b/resources/data/map/level_2.json index eb21058..d53d136 100644 --- a/resources/data/map/level_2.json +++ b/resources/data/map/level_2.json @@ -2,6 +2,9 @@ "background_type":0, "init_sun_value":50, "shovel":1, + "spawn_zombies":"auto", + "included_zombies":["Zombie", "ConeheadZombie"], + "num_flags":3, "zombie_list":[ {"time":20000, "name":"Zombie"}, {"time":40000, "name":"FlagZombie"}, diff --git a/resources/data/map/level_3.json b/resources/data/map/level_3.json index 02bf49a..00ad792 100644 --- a/resources/data/map/level_3.json +++ b/resources/data/map/level_3.json @@ -2,6 +2,9 @@ "background_type":1, "init_sun_value":50, "shovel":1, + "spawn_zombies":"auto", + "included_zombies":["Zombie", "ConeheadZombie", "BucketheadZombie"], + "num_flags":3, "zombie_list":[ {"time":20000, "name":"Zombie"}, {"time":40000, "name":"ConeheadZombie"}, diff --git a/resources/data/map/level_4.json b/resources/data/map/level_4.json index 306f9c7..2675ff3 100644 --- a/resources/data/map/level_4.json +++ b/resources/data/map/level_4.json @@ -2,6 +2,9 @@ "background_type":2, "init_sun_value":50, "shovel":1, + "spawn_zombies":"auto", + "included_zombies":["Zombie", "ConeheadZombie", "BucketheadZombie", "FootballZombie"], + "num_flags":4, "zombie_list":[ {"time":20000, "name":"Zombie"}, {"time":40000, "name":"ConeheadZombie"}, diff --git a/resources/data/map/littleGame_1.json b/resources/data/map/littleGame_1.json index 7b58813..93595ae 100644 --- a/resources/data/map/littleGame_1.json +++ b/resources/data/map/littleGame_1.json @@ -2,6 +2,9 @@ "background_type":0, "choosebar_type":1, "shovel":1, + "spawn_zombies":"auto", + "num_flags":4, + "included_zombies":["Zombie", "ConeheadZombie", "BucketheadZombie"], "card_pool":[ {"name":"Peashooter"}, {"name":"SnowPea"}, diff --git a/resources/data/map/littleGame_2.json b/resources/data/map/littleGame_2.json index 9f2e217..dc56264 100644 --- a/resources/data/map/littleGame_2.json +++ b/resources/data/map/littleGame_2.json @@ -2,6 +2,9 @@ "background_type":6, "choosebar_type":2, "shovel":0, + "spawn_zombies":"auto", + "included_zombies":["Zombie", "ConeheadZombie", "BucketheadZombie"], + "num_flags":4, "card_pool":[ {"name":"WallNutBowling"}, {"name":"RedWallNutBowling"} diff --git a/source/constants.py b/source/constants.py index f2822d5..67a07a6 100755 --- a/source/constants.py +++ b/source/constants.py @@ -97,6 +97,16 @@ BACKGROUND_WALLNUTBOWLING = 6 BACKGROUND_SINGLE = 7 BACKGROUND_TRIPLE = 8 +# 僵尸生成方式 +SPAWN_ZOMBIES = 'spawn_zombies' +SPAWN_ZOMBIES_AUTO = 'auto' +SPAWN_ZOMBIES_LIST = 'list' +INCLUDED_ZOMBIES = 'included_zombies' +NUM_FLAGS = 'num_flags' +INEVITABLE_ZOMBIE_DICT = 'inevitable_zombie_list' +SURVIVAL_ROUNDS = 'survival_rounds' + + # 地图单元格属性 MAP_PLANT = 'plantnames' MAP_SLEEP = 'sleep' # 有没有休眠的蘑菇,作是否能种植咖啡豆的判断 diff --git a/source/state/level.py b/source/state/level.py index 7269313..16e4401 100644 --- a/source/state/level.py +++ b/source/state/level.py @@ -103,13 +103,90 @@ class Level(tool.State): self.zombie_groups.append(pg.sprite.Group()) self.hypno_zombie_groups.append(pg.sprite.Group()) self.bullet_groups.append(pg.sprite.Group()) - + + + # 按照规则生成每一波僵尸 + # 可以考虑将波刷新和一波中的僵尸生成分开 + # useableZombie是指可用的僵尸种类的元组 + # inevitableZombie指在本轮必然出现的僵尸,输入形式为字典: {波数1:(僵尸1, 僵尸2……), 波数2:(僵尸1, 僵尸2……)……} + def createWaves(self, useableZombies, numFlags, survivalRounds=0, inevitableZombieDict=None): + waves = [] + + # 权重值 + weights = [] + for zombie in useableZombies: + weights.append(self.createZombieInfo[zombie][1]) + + # 按照原版pvz设计的僵尸容量函数,是从无尽解析的,但是普通关卡也可以遵循 + for wave in range(1, 10 * numFlags + 1): + volume = int(int((wave + survivalRounds*20)*0.8)/2) + 1 + if wave % 10 == 0: + volume = int(volume*2.5) + zombieList = [] + + if inevitableZombieDict and (wave in inevitableZombieDict.keys()): + for newZombie in inevitableZombieDict[wave]: + zombieList.append(newZombie) + volume -= self.createZombieInfo[newZombie][0] + if volume < 0: + print('警告:第{}波中手动设置的僵尸级别总数超过上限!'.format(wave)) + + while (volume > 0) and (len(zombieList) < 50): + newZombie = choices(useableZombies, weights)[0] + if self.createZombieInfo[newZombie][0] <= volume: + zombieList.append(newZombie) + volume -= self.createZombieInfo[newZombie][0] + waves.append(zombieList) + + self.waves = waves + + + # 僵尸的刷新机制 + def refreshWaves(self, current_time, survivalRounds=0): + if self.waveNum >= self.map_data[c.NUM_FLAGS] * 10: + return + if (self.waveNum == 0): # 还未开始出现僵尸 + if (self.waveTime == 0): # 表明刚刚开始游戏 + self.waveTime = current_time + else: + if (survivalRounds == 0) and (self.bar_type == c.CHOOSEBAR_STATIC): # 首次选卡等待时间较长 + if current_time - self.waveTime >= 18000: + self.waveNum += 1 + self.waveTime = current_time + self.waveZombies = self.waves[self.waveNum - 1] + self.numZombie = len(self.waveZombies) + else: + if (current_time - self.waveTime >= 6000): + self.waveNum += 1 + self.waveTime = current_time + self.waveZombies = self.waves[self.waveNum - 1] + self.numZombie = len(self.waveZombies) + return + if (current_time - self.waveTime >= 25000 + randint(0, 6000)) or (self.bar_type != c.CHOOSEBAR_STATIC and current_time - self.waveTime >= 12500 + randint(0, 3000)): + self.waveNum += 1 + self.waveTime = current_time + self.waveZombies = self.waves[self.waveNum - 1] + self.numZombie = len(self.waveZombies) + return + + + numZombies = 0 + for i in range(self.map_y_len): + numZombies += len(self.zombie_groups[i]) + if numZombies / self.numZombie < 0.15: + self.waveNum += 1 + self.waveTime = current_time + self.waveZombies = self.waves[self.waveNum - 1] + return + + + def setupZombies(self): def takeTime(element): return element[0] self.zombie_list = [] - # 目前设置为从JSON文件中读取僵尸出现的时间、种类、位置信息,以后可以将时间设置为模仿原版的机制,位置设置为随机数 + # 目前设置为从JSON文件中读取僵尸出现的时间、种类、位置信息,以后可以将时间设置为模仿原版的机制 for data in self.map_data[c.ZOMBIE_LIST]: if 'map_y' in data.keys(): self.zombie_list.append((data['time'], data['name'], data['map_y'])) @@ -199,7 +276,34 @@ class Level(tool.State): self.removeMouseImage() self.setupGroups() - self.setupZombies() + if (c.ZOMBIE_LIST in self.map_data.keys()) and self.map_data[c.SPAWN_ZOMBIES] == c.SPAWN_ZOMBIES_LIST: + self.setupZombies() + else: + # 僵尸波数数据及僵尸生成数据 + self.waveNum = 0 # 还未出现僵尸时定义为0 + self.waveTime = 0 + self.waveZombies = [] + self.numZombie = 0 + # 新的僵尸生成机制:级别——权重生成 + self.createZombieInfo = {# 生成僵尸:(级别, 权重) + c.NORMAL_ZOMBIE:(1, 4000), + c.FLAG_ZOMBIE:(1, 0), + c.CONEHEAD_ZOMBIE:(2, 4000), + c.BUCKETHEAD_ZOMBIE:(4, 3000), + c.NEWSPAPER_ZOMBIE:(2, 1000), + c.FOOTBALL_ZOMBIE:(2, 2000) + } + + # 暂时没有生存模式,所以 survivalRounds = 0 + if c.INEVITABLE_ZOMBIE_DICT in self.map_data.keys(): + self.createWaves( useableZombies=self.map_data[c.INCLUDED_ZOMBIES], + numFlags=self.map_data[c.NUM_FLAGS], + survivalRounds=0, + inevitableZombieDict=self.map_data[c.INEVITABLE_ZOMBIE_DICT]) + else: + self.createWaves( useableZombies=self.map_data[c.INCLUDED_ZOMBIES], + numFlags=self.map_data[c.NUM_FLAGS], + survivalRounds=0) self.setupCars() # 地图有铲子才添加铲子 @@ -217,15 +321,6 @@ class Level(tool.State): self.setupLittleMenu() - # 新的僵尸生成机制:级别——权重生成 - self.createZombieInfo = {# 生成僵尸:(级别, 权重) - c.NORMAL_ZOMBIE:(1, 4000), - c.FLAG_ZOMBIE:(1, 0), - c.CONEHEAD_ZOMBIE:(2, 4000), - c.BUCKETHEAD_ZOMBIE:(4, 3000), - c.NEWSPAPER_ZOMBIE:(2, 1000), - c.FOOTBALL_ZOMBIE:(2, 2000) - } # 小菜单 def setupLittleMenu(self): @@ -357,19 +452,27 @@ class Level(tool.State): pg.mixer.music.play(-1, 0) return - # 旧僵尸生成方式 - if self.zombie_start_time == 0: - self.zombie_start_time = self.current_time - elif len(self.zombie_list) > 0: - data = self.zombie_list[0] # 因此要求僵尸列表按照时间顺序排列 - # data内容排列:[0]:时间 [1]:名称 [2]:坐标 - if data[0] <= (self.current_time - self.zombie_start_time): - if len(data) == 3: - self.createZombie(data[1], data[2]) - self.zombie_list.remove(data) - else: # len(data) == 2 没有指定map_y - self.createZombie(data[1]) - self.zombie_list.remove(data) + if (c.ZOMBIE_LIST in self.map_data.keys()) and self.map_data[c.SPAWN_ZOMBIES] == c.SPAWN_ZOMBIES_LIST: + # 旧僵尸生成方式 + if self.zombie_start_time == 0: + self.zombie_start_time = self.current_time + elif len(self.zombie_list) > 0: + data = self.zombie_list[0] # 因此要求僵尸列表按照时间顺序排列 + # data内容排列:[0]:时间 [1]:名称 [2]:坐标 + if data[0] <= (self.current_time - self.zombie_start_time): + if len(data) == 3: + self.createZombie(data[1], data[2]) + self.zombie_list.remove(data) + else: # len(data) == 2 没有指定map_y + self.createZombie(data[1]) + self.zombie_list.remove(data) + else: + self.refreshWaves(self.current_time) + for i in self.waveZombies: + self.createZombie(i) + else: + self.waveZombies = [] + for i in range(self.map_y_len): self.bullet_groups[i].update(self.game_info) @@ -451,42 +554,6 @@ class Level(tool.State): self.checkGameState() - # 按照规则生成每一波僵尸 - # 可以考虑将波刷新和一波中的僵尸生成分开 - # useableZombie是指可用的僵尸种类的元组 - # inevitableZombie指在本轮必然出现的僵尸,输入形式为字典: {波数1:(僵尸1, 僵尸2……), 波数2:(僵尸1, 僵尸2……)……} - def createWaves(self, useableZombies, numFlags, survivalRounds=0, inevitableZombieDict=None): - waves = [] - - # 权重值 - weights = [] - for zombie in useableZombies: - weights.append(self.createZombieInfo[zombie][1]) - - # 按照原版pvz设计的僵尸容量函数,是从无尽解析的,但是普通关卡也可以遵循 - for wave in range(1, 10 * numFlags + 1): - volume = int(int((wave + survivalRounds*20)*0.8)/2) + 1 - if wave % 10 == 0: - volume = int(volume*2.5) - zombieList = [] - - if inevitableZombieDict and (wave in inevitableZombieDict.keys()): - for newZombie in inevitableZombieDict[wave]: - zombieList += newZombie - volume -= self.createZombieInfo[newZombie][0] - if volume < 0: - print('警告:第{}波中手动设置的僵尸级别总数超过上限!'.format(wave)) - - while (volume > 0) and (len(zombieList) < 50): - newZombie = choices(useableZombies, weights) # 注意这个的输出是列表 - if self.createZombieInfo[newZombie][0] <= volume: - zombieList += newZombie - volume -= self.createZombieInfo[newZombie][0] - waves.append(zombieList) - - self.waves = waves - - def createZombie(self, name, map_y=None): # 有指定时按照指定生成,无指定时随机位置生成 # 0:白天 1:夜晚 2:泳池 3:浓雾 4:屋顶 5:月夜 6:坚果保龄球 @@ -911,12 +978,20 @@ class Level(tool.State): self.killPlant(plant) def checkVictory(self): - if len(self.zombie_list) > 0: - return False - for i in range(self.map_y_len): - if len(self.zombie_groups[i]) > 0: + if (c.ZOMBIE_LIST in self.map_data.keys()) and self.map_data[c.SPAWN_ZOMBIES] == c.SPAWN_ZOMBIES_LIST: + if len(self.zombie_list) > 0: return False - return True + for i in range(self.map_y_len): + if len(self.zombie_groups[i]) > 0: + return False + return True + else: + if self.waveNum < self.map_data[c.NUM_FLAGS] * 10: + return False + for i in range(self.map_y_len): + if len(self.zombie_groups[i]) > 0: + return False + return True def checkLose(self): for i in range(self.map_y_len):