From cecb1efc4b7951b742859530209629d8b3de260e Mon Sep 17 00:00:00 2001 From: Ethan Dalool Date: Mon, 20 Sep 2021 20:18:25 -0700 Subject: [PATCH] Rewrite most of Dodgy. --- Dodgy/README.md | 4 +- Dodgy/dodgy.py | 973 +++++++++++++++++++++++++----------------------- 2 files changed, 510 insertions(+), 467 deletions(-) diff --git a/Dodgy/README.md b/Dodgy/README.md index 24b6b37..b38b832 100644 --- a/Dodgy/README.md +++ b/Dodgy/README.md @@ -19,7 +19,7 @@ Hats. Enemies move faster as the game goes on -Randomzied walls in the middle of the arena +Randomized walls in the middle of the arena Arena walls may be boobytrapped. Running alongside a wall may result in death/stun to disincentivize this tactic @@ -29,4 +29,4 @@ Hats. Sound -Stop using tkinter like a caveman \ No newline at end of file +Stop using tkinter like a caveman diff --git a/Dodgy/dodgy.py b/Dodgy/dodgy.py index b2ec1ce..28bc7c7 100644 --- a/Dodgy/dodgy.py +++ b/Dodgy/dodgy.py @@ -1,540 +1,583 @@ -#29 October 11:34 -import threading -import tkinter -from tkinter import Tk, Label, Frame import random +import sys +import tkinter + +def clamp_abs(x, clamp): + if x > 0: + return min(x, clamp) + if x < 0: + return max(x, -clamp) + return x + +def dist(xa, ya, xb, yb): + #distance formula + result = (xa - xb) **2 + result += (ya - yb) ** 2 + result = result ** 0.5 + result = int(result) + return result + +def reduce_to_one(x): + if x < 0: + return -1 + + if x > 0: + return 1 + + return 0 class dodgygame: def __init__(self): - tkvar = Tk() - tkvar.resizable(0,0) - tkvar.wm_title('Dodgy') - tkvar.iconbitmap('Excl.ico') + self.tk = tkinter.Tk() + self.tk.resizable(0,0) + self.tk.wm_title('Dodgy') + self.tk.iconbitmap('Excl.ico') - arenasize = 40 - self.xpos = int((arenasize-1)/2) - self.ypos = int((arenasize-1)/2) + self.arenasize = 40 + self.arenasize_walls = self.arenasize + 2 - self.data = ['#'*arenasize, '#'*arenasize] - for x in range(arenasize-2): - self.data[1:1] = ['#' + '.'*(arenasize-2) + '#'] + self.player = Player(x=0, y=0) - self.labelframe = Frame(tkvar) + self.labelframe = tkinter.Frame(self.tk) self.labelframe.grid(row=1, column=0, columnspan=100) #Allows me to center the gui units - self.datalabel = Label(tkvar, text='\n'.join(self.data), font=('Terminal', 10)) + self.datalabel = tkinter.Label(self.tk, text='', font=('Terminal', 10)) self.datalabel.grid(row=0, column=0, columnspan=100) - self.stepslabel = Label(self.labelframe, text='0', font=('Consolas', 10)) + self.stepslabel = tkinter.Label(self.labelframe, text='0', font=('Consolas', 10)) self.stepslabel.grid(row=0, column=0) - self.collectlabel = Label(self.labelframe, text='0', font=('Consolas', 10), bg="#ccc") + self.collectlabel = tkinter.Label(self.labelframe, text='0', font=('Consolas', 10), bg='#ccc') self.collectlabel.grid(row=0, column=1) - self.bomblabel = Label(self.labelframe, text='0', font=('Consolas', 10)) + self.bomblabel = tkinter.Label(self.labelframe, text='0', font=('Consolas', 10)) self.bomblabel.grid(row=0, column=2) - self.phantlabel = Label(self.labelframe, text='0',font=('Consolas', 10), bg="#ccc") + self.phantlabel = tkinter.Label(self.labelframe, text='0', font=('Consolas', 10), bg='#ccc') self.phantlabel.grid(row=0, column=3) - self.poslabel = Label(self.labelframe, text=str(self.xpos) + " " + str(self.ypos), font=('Consolas', 10)) + self.poslabel = tkinter.Label(self.labelframe, text='', font=('Consolas', 10)) self.poslabel.grid(row=0, column=4) - self.helplabel = Label(tkvar, text="Press H for help ->", font=('Consolas', 8)) + self.helplabel = tkinter.Label(self.tk, text='Press H for help ->', font=('Consolas', 8)) self.helplabel.grid(row=2, column=0, columnspan=100) - self.consolelabel = Label(tkvar, text="Welcome", font=("Terminal", 8), width=arenasize, height=4) + self.consolelabel = tkinter.Label(self.tk, text='Welcome', font=('Terminal', 8), width=self.arenasize_walls, height=4) self.consolelabeldata = [] - self.consolelabel.configure(anchor="nw", justify="left") + self.consolelabel.configure(anchor='nw', justify='left') self.consolelabel.grid(row=3, column=0) - tkvar.bind('w', lambda data=self.data: mfresh(ymove=-1)) - tkvar.bind('', lambda data=self.data: mfresh(ymove=-1)) - tkvar.bind('s', lambda data=self.data: mfresh(ymove=1)) - tkvar.bind('', lambda data=self.data: mfresh(ymove=1)) - tkvar.bind('a', lambda data=self.data: mfresh(xmove=-1)) - tkvar.bind('', lambda data=self.data: mfresh(xmove=-1)) - tkvar.bind('d', lambda data=self.data: mfresh(xmove=1)) - tkvar.bind('', lambda data=self.data: mfresh(xmove=1)) - #tkvar.bind('c', lambda data=self.data: spawncandy()) - tkvar.bind('j', lambda data=self.data: spawnbomb()) - tkvar.bind('z', lambda data=self.data: spawnbomb()) - tkvar.bind('k', lambda data=self.data: spawnphantom()) - tkvar.bind('x', lambda data=self.data: spawnphantom()) - tkvar.bind('r', lambda data=self.data: restart()) - tkvar.bind('h', lambda data=self.data: helpreel()) - tkvar.bind('', quit) + self.tk.bind('w', lambda *args: self.move_up()) + self.tk.bind('', lambda *args: self.move_up()) + + self.tk.bind('s', lambda *args: self.move_down()) + self.tk.bind('', lambda *args: self.move_down()) + + self.tk.bind('a', lambda *args: self.move_left()) + self.tk.bind('', lambda *args: self.move_left()) + + self.tk.bind('d', lambda *args: self.move_right()) + self.tk.bind('', lambda *args: self.move_right()) + + self.tk.bind('', self.move_by_mouse) + self.tk.bind('', lambda *args: self.middlemouse()) + self.tk.bind('', lambda *args: self.spawnbomb()) + self.tk.bind('j', lambda *args: self.spawnbomb()) + self.tk.bind('z', lambda *args: self.spawnbomb()) + self.tk.bind('k', lambda *args: self.spawnphantom()) + self.tk.bind('x', lambda *args: self.spawnphantom()) + self.tk.bind('r', lambda *args: self.restart()) + self.tk.bind('h', lambda *args: self.helpreel()) + self.tk.bind('', lambda *args: self.tk.quit()) self.candylist = [] self.enemylist = [] self.bomblist = [] self.phantomlist = [] self.goldcandylist = [] self.entlist = [] - self.symbols = {'char':'H', 'wall':'#', 'floor':' '} - - self.stepstaken = 0 - self.isdeath = False + self.symbol_wall ='#' + self.symbol_floor = ' ' self.candyspawnrate = 4 - self.collections = 0 self.enemyspawnrate = 50 self.enemyspawnmindist = 9 self.enemydocollide = False - self.bombs = 0 self.bombcost = 4 self.phantomcost = 60 self.goldcandyflashrate = 8 self.goldcandyspawnrate = 40 - self.goldcandyspawnrand = 4 self.helplabelindex = -1 self.helplabeltexts = [ - "=Movement =Bomb =Phantom =Restart ->", - "=Movement =Bomb =Phantom =Restart ->", - "=Movement =Bomb =Phantom/Restart ->", - self.symbols['char'] + " = You ->", - enemy.symbol + " = Exclamator ->", - candy.symbol + " = Candy ->", - bomb.symbol + " = Bomb ->", - "Avoid the Exclamators ->", - "Collect candy to earn bombs ->", - "Drop bombs to snare Exclamators ->", - "Deploy phantoms to distract Exclamators ->", - "Phantoms have a minimum cost of " + str(self.phantomcost) + " ->", - "But deploying phantom will consume all candy ->", - "More candy consumed = longer phantom lifespan ->", - "Enjoy •"] + '=Movement =Bomb =Phantom =Restart ->', + '=Movement =Bomb =Phantom =Restart ->', + '=Movement =Bomb =Phantom/Restart ->', + f'{Player.symbol} = You ->', + f'{Enemy.symbol} = Exclamator ->', + f'{Candy.symbol} = Candy ->', + f'{Bomb.symbol} = Bomb ->', + 'Avoid the Exclamators ->', + 'Collect candy to earn bombs ->', + 'Drop bombs to snare Exclamators ->', + 'Deploy phantoms to distract Exclamators ->', + f'Phantoms have a minimum cost of {self.phantomcost} ->', + 'But deploying phantom will consume all candy ->', + 'More candy consumed = longer phantom lifespan ->', + 'Enjoy •', + 'Press H for help ->', + ] + self.restart() - def mfresh(xmove=0, ymove=0): - if not self.isdeath: - #print(self.xpos, self.ypos, "==> ", end="") - hasmoved = False - if xmove != 0: - if (self.xpos > 1 or xmove > 0) and (self.xpos < (arenasize -2) or xmove < 0): - self.xpos += xmove - if self.phantomlist != []: - ph = self.phantomlist[0] - ph.x -= xmove - if ph.x < 1: - ph.x = 1 - if ph.x > (arenasize-2): - ph.x = arenasize-2 - hasmoved = True - if ymove != 0: - if (self.ypos > 1 or ymove > 0) and (self.ypos < (arenasize -2) or ymove < 0): - self.ypos += ymove - if self.phantomlist != []: - ph = self.phantomlist[0] - ph.y -= ymove - if ph.y < 1: - ph.y = 1 - if ph.y > (arenasize-2): - ph.y = arenasize-2 + self.tk.update() + width = self.tk.winfo_reqwidth() + height = self.tk.winfo_reqheight() + x_offset = (self.tk.winfo_screenwidth() - width) / 2 + y_offset = (self.tk.winfo_screenheight() - height) / 2 + self.tk.geometry('%dx%d+%d+%d' % (width, height, x_offset, y_offset-50)) + self.tk.mainloop() - hasmoved = True - if hasmoved: - if self.phantomlist != []: - ph = self.phantomlist[0] - ph.lifespan -= 1 - if ph.lifespan <= 0: - self.phantomlist.remove(ph) - self.entlist.remove(ph) - del ph - #print(self.xpos, self.ypos, '|', self.stepstaken) + def collect(self, score): + for x in range(score): + self.collections += 1 + if self.collections % self.bombcost == 0: + self.bombs += 1 - for candies in self.candylist: - if candies.x == self.xpos and candies.y == self.ypos: - #print('Collection') - collect(1) - self.candylist.remove(candies) - self.entlist.remove(candies) - del candies - - for goldcandies in self.goldcandylist: - if goldcandies.x == self.xpos and goldcandies.y == self.ypos: - tempvar = '[ ' + goldcandy.symbol + ' ] Got gold' - print(tempvar) - printr(tempvar) - collect(5) - self.goldcandylist.remove(goldcandies) - self.entlist.remove(goldcandies) - - for enemies in self.enemylist: - if enemies.x == self.xpos and enemies.y == self.ypos: - tempvar = '[ ' + self.symbols['char'] + ' ] Death' - print(tempvar) - printr(tempvar) - self.isdeath = True - self.datalabel.configure(fg='Red') - - if not self.isdeath: - if hasmoved: - self.stepstaken += 1 - if self.stepstaken % self.candyspawnrate == 0: - spawncandy() - - goldchance = random.randint(self.stepstaken-self.goldcandyspawnrand, self.stepstaken+self.goldcandyspawnrand) - #print(goldchance) - if goldchance % self.goldcandyspawnrate == 0: - spawngoldcandy() - if self.stepstaken % self.enemyspawnrate == 0: - spawnenemy() - for x in range(self.stepstaken // 200): - spawnenemy() - - for en in self.enemylist: - oldx = en.x - oldy = en.y - if self.phantomlist == []: - en.approach(self.xpos, self.ypos) - else: - ph = self.phantomlist[0] - en.approach(ph.x, ph.y) - if self.enemydocollide: - for otheren in self.enemylist: - if en != otheren: - if en.x == otheren.x and en.y == otheren.y: - tempvar = '[ '+enemy.symbol+' ] Enemy collision at '+str(en.x)+' '+str(en.y) - print(tempvar) - printr(tempvar) - en.x = oldx - en.y = oldy - #print(dist(en.x, en.y, self.xpos, self.ypos)) - if en.x < 1: - en.x = 1 - if en.x > (arenasize-2): - en.x = arenasize-2 - - if en.y < 1: - en.y = 1 - if en.y > (arenasize-2): - en.y = arenasize-2 - mfresh() - - for goldcandies in self.goldcandylist: - goldcandies.tick(self.goldcandyflashrate) - if goldcandies.lifespan <= 0: - self.goldcandylist.remove(goldcandies) - self.entlist.remove(goldcandies) - del goldcandies - - for bombs in self.bomblist: - for enemies in self.enemylist: - if enemies.x == bombs.x and enemies.y == bombs.y: - self.bomblist.remove(bombs) - self.enemylist.remove(enemies) - self.entlist.remove(bombs) - self.entlist.remove(enemies) - tempvar = '[ ' + bomb.symbol + ' ] Bang' - print(tempvar) - printr(tempvar) - mfresh() - - self.data = [self.symbols['wall']*arenasize, self.symbols['wall']*arenasize] - for x in range(arenasize-2): - self.data[1:1] = [self.symbols['wall'] + self.symbols['floor']*(arenasize-2) + self.symbols['wall']] - for ycoord in range(len(self.data)): - yl = self.data[ycoord] - if ycoord == self.ypos and not self.isdeath: - #print(ycoord) - yl = yl[:self.xpos] + self.symbols['char'] + yl[self.xpos+1:] - #print(yl) - self.entlist.sort(key=lambda p: p.x) - for entities in self.entlist: - if entities.y == ycoord: - yl = yl[:entities.x] + entities.symbol + yl[entities.x+1:] - self.data[ycoord] = yl - - - self.datalabel.configure(text='\n'.join(self.data)) - self.stepslabel.configure(text="%04d"%(self.stepstaken) + " steps") - self.collectlabel.configure(text="%03d"%(self.collections) + " candy") - self.bomblabel.configure(text="%02d"%(self.bombs) + " bombs") - self.poslabel.configure(text="%02d"%(self.xpos) + " " + "%02d"%(self.ypos)) - if self.collections >= self.phantomcost: - self.phantlabel.configure(text='1 phantom') - else: - self.phantlabel.configure(text='0 phantom') - - def printr(info): - self.consolelabeldata.append(str(info)) - self.consolelabeldata = self.consolelabeldata[-4:] - self.consolelabel.configure(text='\n'.join(self.consolelabeldata)) - - - def translatemouse(event, middlemouse=False): - event.x -= 9 - event.y -= 9 - #485 - - event.x /= 8 - event.y /= 12 - #spawncandy(round(event.x), round(event.y)) - #print(event.x, event.y) - xdif = event.x - self.xpos - ydif = event.y - self.ypos - if abs(xdif) >= 0.5 or abs(ydif) >= 0.5: - if abs(xdif) >= abs(ydif): - xdif /= abs(xdif) - xdif = int(xdif) - mfresh(xmove= xdif) - else: - ydif /= abs(ydif) - ydif = int(ydif) - mfresh(ymove= ydif) - - def middlemouse(): - if self.isdeath: - restart() - else: - spawnphantom() - tkvar.bind('', translatemouse) - tkvar.bind('', lambda data=self.data: middlemouse()) - tkvar.bind('', lambda data=self.data: spawnbomb()) - - def helpreel(): - self.helplabelindex += 1 - if self.helplabelindex > (len(self.helplabeltexts) - 1): - self.helplabelindex = 0 - self.helplabel.configure(text=self.helplabeltexts[self.helplabelindex]) - - def collect(score): - for x in range(score): - self.collections += 1 - if self.collections % self.bombcost == 0: - self.bombs += 1 - - def spawnbomb(): - goodtogo = True - for bombs in self.bomblist: - if bombs.x == self.xpos and bombs.y == self.ypos: - goodtogo = False - if goodtogo: - if self.bombs > 0: - self.bombs -= 1 - newbomb = bomb(self.xpos, self.ypos) - self.bomblist.append(newbomb) - self.entlist.append(newbomb) - mfresh() - - def spawnphantom(): - goodtogo = True - if self.collections < self.phantomcost: - tempvar='[ ' + self.symbols['char'] + ' ] Not enough Candy. ' + str(self.collections) + '/' + str(self.phantomcost) + '. -' + str(self.phantomcost-self.collections) - print(tempvar) - printr(tempvar) - goodtogo = False - if self.phantomlist != []: - goodtogo = False - - if goodtogo: - life = 15 - self.collections = 0 - life += round(self.collections / 3) - tempvar = '[ ' + self.symbols['char'] + ' ] New phantom with ' + str(life) + ' life' - print(tempvar) - printr(tempvar) - newphantom = phantom(self.xpos, self.ypos, life) - self.phantomlist.append(newphantom) - self.entlist.append(newphantom) - mfresh() - - def spawncandy(forcex=None, forcey=None): - goodtogo = True - if forcex == None or forcey == None: - newx = random.randint(1, arenasize-2) - newy = random.randint(1, arenasize-2) - else: - newx = forcex - newy = forcey - if self.xpos == newx and self.ypos == newy: - goodtogo = False - if goodtogo: - for candies in self.candylist: - if candies.x == newx and candies.y == newy: - goodtogo = False - if goodtogo: - tempvar = '[ ' + candy.symbol + ' ] New candy at ' + str(newx)+' '+str(newy) - print(tempvar) - printr(tempvar) - newcan = candy(newx, newy) - self.candylist.append(newcan) - self.entlist.append(newcan) - mfresh() - - - def spawngoldcandy(forcex=None, forcey=None): - goodtogo = True - if forcex == None or forcey == None: - newx = random.randint(1, arenasize-2) - newy = random.randint(1, arenasize-2) - spawntries = 0 - while (dist(self.xpos, self.ypos, newx, newy) < self.enemyspawnmindist): - newx = random.randint(1, arenasize-2) - newy = random.randint(1, arenasize-2) - spawntries += 1 - #print('Rerolling from', newx, newy) - if spawntries == 20: - #print('Could not spawn enemy') - goodtogo = False - break - else: - newx = forcex - newy = forcey - if goodtogo: - for entity in self.entlist: - if entity.x == newx and entity.y == newy: - goodtogo = False - if goodtogo: - lifespan= dist(self.xpos, self.ypos, newx, newy) - lifespan *= 2 - tempvar = '[ ' + goldcandy.symbol + ' ] New gold candy' - print(tempvar) - printr(tempvar) - newcan = goldcandy(newx, newy, lifespan) - self.goldcandylist.append(newcan) - self.entlist.append(newcan) - mfresh() - - def spawnenemy(): - newx = random.randint(1, arenasize-2) - newy = random.randint(1, arenasize-2) - goodtogo = True - spawntries = 0 - while (dist(self.xpos, self.ypos, newx, newy) < self.enemyspawnmindist): - newx = random.randint(1, arenasize-2) - newy = random.randint(1, arenasize-2) - spawntries += 1 - #print('Rerolling from', newx, newy) - if spawntries == 10: - print('Could not spawn enemy') - goodtogo = False + def do_enemy_bomb_collisions(self): + for bomb in list(self.bomblist): + for enemy in list(self.enemylist): + if enemy.x == bomb.x and enemy.y == bomb.y: + self.bomblist.remove(bomb) + self.enemylist.remove(enemy) + self.entlist.remove(bomb) + self.entlist.remove(enemy) + self.printr(f'[ {Bomb.symbol} ] Bang') break - if goodtogo: - for ens in self.enemylist: - if ens.x == newx and ens.y == newy: - goodtogo = False - if goodtogo: - tempvar = '[ ' + enemy.symbol + ' ] New enemy at '+str(newx)+' '+str(newy) - print(tempvar) - printr(tempvar) - newen = enemy(newx, newy, 1) - self.enemylist.append(newen) - self.entlist.append(newen) - mfresh() + def do_player_enemy_collisions(self): + for enemy in list(self.enemylist): + if enemy.x == self.player.x and enemy.y == self.player.y: + self.printr(f'[ {Player.symbol} ] Death') + self.isdead = True + self.datalabel.configure(fg='Red') - def restart(): - tempvar = 'Resetting game.' - print(tempvar) - printr(tempvar) - self.consolelabeldata = [] - self.consolelabel.configure(text='') - self.xpos = int((arenasize-1)/2) - self.ypos = int((arenasize-1)/2) - while self.entlist != []: - del self.entlist[0] - self.candylist = [] - self.enemylist = [] - self.bomblist = [] - self.phantomlist = [] - self.entlist = [] - self.stepstaken = 0 + def do_player_item_collisions(self): + for entity in list(self.entlist): + if entity.xy == self.player.xy: + if isinstance(entity, Candy): + self.collect(entity.value) + self.candylist.remove(entity) + self.entlist.remove(entity) + elif isinstance(entity, Goldcandy): + self.printr(f'[ {Goldcandy.symbol} ] Got gold') + self.collect(entity.value) + self.goldcandylist.remove(entity) + self.entlist.remove(entity) + + def _game_tick(self): + self.do_player_item_collisions() + self.do_player_enemy_collisions() + if self.isdead: + return + + self.move_enemies() + self.do_player_enemy_collisions() + if self.isdead: + return + + self.do_enemy_bomb_collisions() + + self.ticks_elapsed += 1 + + if self.ticks_elapsed % self.candyspawnrate == 0: + self.spawncandy() + + if self.ticks_elapsed % self.goldcandyspawnrate == 0 and random.random() < 0.75: + self.spawngoldcandy() + + if self.ticks_elapsed % self.enemyspawnrate == 0: + enemy_count = (self.ticks_elapsed // 200) + 1 + for x in range(enemy_count): + self.spawnenemy() + + self.expire_goldcandies() + self.expire_phantoms() + + def game_tick(self): + self._game_tick() + self.redraw() + + def expire_goldcandies(self): + for goldcandy in list(self.goldcandylist): + goldcandy.tick(self.goldcandyflashrate) + if goldcandy.lifespan <= 0: + self.goldcandylist.remove(goldcandy) + self.entlist.remove(goldcandy) + + def expire_phantoms(self): + for phantom in list(self.phantomlist): + if phantom.lifespan <= 0: + self.phantomlist.remove(phantom) + self.entlist.remove(phantom) + + def helpreel(self): + self.helplabelindex += 1 + if self.helplabelindex > (len(self.helplabeltexts) - 1): + self.helplabelindex = 0 + self.helplabel.configure(text=self.helplabeltexts[self.helplabelindex]) + + def middlemouse(self): + if self.isdead: + self.restart() + else: + self.spawnphantom() + + def move_by_mouse(self, event): + event.x -= 9 + event.y -= 9 + + event.x /= 8 + event.y /= 12 + + xdif = event.x - self.player.x + ydif = event.y - self.player.y + if abs(xdif) >= 0.5 or abs(ydif) >= 0.5: + if abs(xdif) >= abs(ydif): + if xdif > 0: + self.move_right() + else: + self.move_left() + else: + if ydif > 0: + self.move_down() + else: + self.move_up() + + def move_enemies(self): + for enemy in self.enemylist: + oldx = enemy.x + oldy = enemy.y + if self.phantomlist == []: + enemy.approach(self.player.x, self.player.y) + else: + ph = self.phantomlist[0] + enemy.approach(ph.x, ph.y) + if self.enemydocollide: + for otheren in self.enemylist: + if enemy != otheren: + if enemy.xy == otheren.xy: + self.printr(f'[ {Enemy.symbol} ] Enemy collision at {enemy.x} {enemy.y}') + enemy.x = oldx + enemy.y = oldy + if enemy.x < 1: + enemy.x = 1 + if enemy.x > (self.arenasize): + enemy.x = self.arenasize + + if enemy.y < 1: + enemy.y = 1 + if enemy.y > (self.arenasize): + enemy.y = self.arenasize + + def move_left(self): + if self.isdead: + return + + if self.player.x > 1: + self.player.x -= 1 + self.stepstaken += 1 + for phantom in self.phantomlist: + if phantom.x < self.arenasize: + phantom.x += 1 + phantom.lifespan -= 1 + + self.game_tick() + + def move_right(self): + if self.isdead: + return + + if self.player.x < self.arenasize: + self.player.x += 1 + self.stepstaken += 1 + for phantom in self.phantomlist: + if phantom.x > 1: + phantom.x -= 1 + phantom.lifespan -= 1 + + self.game_tick() + + def move_up(self): + if self.isdead: + return + + if self.player.y > 1: + self.player.y -= 1 + self.stepstaken += 1 + for phantom in self.phantomlist: + if phantom.y < self.arenasize: + phantom.y += 1 + phantom.lifespan -= 1 + + self.game_tick() + + def move_down(self): + if self.isdead: + return + + if self.player.y < self.arenasize: + self.player.y += 1 + self.stepstaken += 1 + for phantom in self.phantomlist: + if phantom.y > 1: + phantom.y -= 1 + phantom.lifespan -= 1 + + self.game_tick() + + def printr(self, info): + print(info) + self.consolelabeldata.append(str(info)) + self.consolelabeldata = self.consolelabeldata[-4:] + self.consolelabel.configure(text='\n'.join(self.consolelabeldata)) + + def redraw(self): + display = [] + display.append([self.symbol_wall] * self.arenasize_walls) + for y in range(self.arenasize): + display.append([self.symbol_wall] + [self.symbol_floor] * self.arenasize + [self.symbol_wall]) + display.append([self.symbol_wall] * self.arenasize_walls) + + if not self.isdead: + display[self.player.y][self.player.x] = Player.symbol + + self.entlist.sort(key=lambda ent: ent.precedence, reverse=True) + for entity in self.entlist: + display[entity.y][entity.x] = entity.symbol + + display = '\n'.join(''.join(line) for line in display) + self.datalabel.configure(text=display) + self.stepslabel.configure(text=f'{self.stepstaken:04d} steps') + self.collectlabel.configure(text=f'{self.collections:03d} candy') + self.bomblabel.configure(text=f'{self.bombs:02d} bombs') + self.poslabel.configure(text=f'{self.player.x:02d} {self.player.y:02d}') + if self.collections >= self.phantomcost: + self.phantlabel.configure(text='1 phantom') + else: + self.phantlabel.configure(text='0 phantom') + + def restart(self): + self.printr('Resetting game.') + self.consolelabeldata = [] + self.consolelabel.configure(text='') + self.player.x = self.arenasize // 2 + self.player.y = self.arenasize // 2 + self.entlist = [] + self.candylist = [] + self.goldcandylist = [] + self.enemylist = [] + self.bomblist = [] + self.phantomlist = [] + self.ticks_elapsed = 0 + self.stepstaken = 0 + self.collections = 0 + self.bombs = 0 + self.isdead = False + self.datalabel.configure(fg='Black') + + self.spawncandy() + self.game_tick() + + def spawnbomb(self): + goodtogo = True + for bomb in self.bomblist: + if bomb.xy == self.player.xy: + goodtogo = False + if goodtogo: + if self.bombs > 0: + self.bombs -= 1 + newbomb = Bomb(self.player.x, self.player.y) + self.bomblist.append(newbomb) + self.entlist.append(newbomb) + self.redraw() + + def spawncandy(self, forcex=None, forcey=None): + if forcex is None or forcey is None: + newx = random.randint(1, self.arenasize) + newy = random.randint(1, self.arenasize) + else: + newx = forcex + newy = forcey + + new_candy = Candy(newx, newy) + + if self.player.xy == new_candy.xy: + return + + for candy in self.candylist: + if candy.xy == new_candy.xy: + return + + self.printr(f'[ {Candy.symbol} ] New candy at {newx} {newy}') + self.candylist.append(new_candy) + self.entlist.append(new_candy) + + def spawnenemy(self): + newx = random.randint(1, self.arenasize) + newy = random.randint(1, self.arenasize) + + spawntries = 0 + while (dist(self.player.x, self.player.y, newx, newy) < self.enemyspawnmindist): + newx = random.randint(1, self.arenasize) + newy = random.randint(1, self.arenasize) + spawntries += 1 + #print('Rerolling from', newx, newy) + if spawntries == 10: + print('Could not spawn enemy') + return + + new_enemy = Enemy(newx, newy, 1) + + for enemy in self.enemylist: + if enemy.xy == new_enemy.xy: + return + + self.printr(f'[ {Enemy.symbol} ] New enemy at {newx} {newy}') + self.enemylist.append(new_enemy) + self.entlist.append(new_enemy) + + def spawngoldcandy(self, forcex=None, forcey=None): + if forcex is None or forcey is None: + newx = random.randint(1, self.arenasize) + newy = random.randint(1, self.arenasize) + spawntries = 0 + while (dist(self.player.x, self.player.y, newx, newy) < self.enemyspawnmindist): + newx = random.randint(1, self.arenasize) + newy = random.randint(1, self.arenasize) + spawntries += 1 + #print('Rerolling from', newx, newy) + if spawntries == 20: + #print('Could not spawn enemy') + return + else: + newx = forcex + newy = forcey + + lifespan = dist(self.player.x, self.player.y, newx, newy) + lifespan *= 2 + new_candy = Goldcandy(newx, newy, lifespan) + + for entity in self.entlist: + if entity.xy == new_candy.xy: + return + + self.printr(f'[ {Goldcandy.symbol} ] New gold candy') + self.goldcandylist.append(new_candy) + self.entlist.append(new_candy) + + def spawnphantom(self): + goodtogo = True + if self.collections < self.phantomcost: + self.printr(f'[ {Player.symbol} ] Not enough Candy. {self.collections} / {self.phantomcost}.') + goodtogo = False + if self.phantomlist != []: + goodtogo = False + + if goodtogo: + life = 15 self.collections = 0 - self.bombs = 0 - self.isdeath = False - spawncandy() - self.datalabel.configure(fg='Black') - mfresh() + life += round(self.collections / 3) + self.printr(f'[ {Player.symbol} ] New phantom with {life} life.') + newphantom = Phantom(self.player.x, self.player.y, life) + self.phantomlist.append(newphantom) + self.entlist.append(newphantom) + self.redraw() - - def dist(xa, ya, xb, yb): - #distance formula - result = (xa - xb) **2 - result += (ya - yb) ** 2 - result = result ** 0.5 - result = int(result) - return result - - - - mfresh() - spawncandy() - tkvar.mainloop() - -class candy: - symbol = "@" +class Entity: def __init__(self, x, y): self.x = x self.y = y - self.symbol = candy.symbol -class enemy: - symbol = "!" + @property + def xy(self): + return (self.x, self.y) + +class Bomb(Entity): + symbol = 'X' + +class Candy(Entity): + symbol = '@' + value = 1 + +class Enemy(Entity): + symbol = '!' + def __init__(self, x, y, anger): - self.symbol = enemy.symbol - self.x = x - self.y = y + super().__init__(x, y) self.anger = anger - self.movementx = 0 - self.movementy = 0 + self.vel_x = 0 + self.vel_y = 0 def approach(self, targx, targy): - hasmoved = False - xdif = abs(targx - self.x) - ydif = abs(targy - self.y) - if xdif > ydif: - if targx != self.x and hasmoved == False: - self.movementx += int(((targx-self.x) / (abs(targx-self.x)))) - if self.movementx > 1: - self.movementx = 1 - if self.movementx < -1: - self.movementx = -1 - else: - if targy != self.y and hasmoved == False: - self.movementy += int(((targy-self.y) / (abs(targy-self.y)))) - if self.movementy > 1: - self.movementy = 1 - if self.movementy < -1: - self.movementy = -1 + accel_x = targx - self.x + accel_y = targy - self.y - if not hasmoved: - self.x += self.movementx - if not hasmoved: - self.y += self.movementy + self.vel_x = clamp_abs(self.vel_x + accel_x, 5) * 0.85 + self.vel_y = clamp_abs(self.vel_y + accel_y, 5) * 0.85 -class bomb: - symbol = "X" - def __init__(self, x, y): - self.x = x - self.y = y - self.symbol = bomb.symbol + mag_x = abs(self.vel_x) + mag_y = abs(self.vel_y) + + if mag_x >= 1 and mag_x > mag_y: + self.x += reduce_to_one(self.vel_x) + + if mag_y >= 1 and mag_y >= mag_x: + self.y += reduce_to_one(self.vel_y) + +class Goldcandy(Entity): + symbol = '$' + value = 5 -class phantom: - symbol = "H" def __init__(self, x, y, lifespan): - self.x = x - self.y = y - self.lifespan = lifespan - self.symbol = phantom.symbol - -class goldcandy: - symbol = "$" - def __init__(self, x, y, lifespan): - self.x = x - self.y = y + super().__init__(x, y) self.lifespan = lifespan - self.symbol = goldcandy.symbol + self.symbol = Goldcandy.symbol + def tick(self, flashrate): self.lifespan -= 1 if self.lifespan % flashrate == 0 or self.lifespan % flashrate == 1: - self.symbol = goldcandy.symbol + self.symbol = Goldcandy.symbol else: - self.symbol = candy.symbol + self.symbol = Candy.symbol -t = dodgygame() +class Player(Entity): + symbol = 'H' + +class Phantom(Entity): + symbol = 'H' + + def __init__(self, x, y, lifespan): + super().__init__(x, y) + self.lifespan = lifespan + +ENTITY_PRECEDENCE = [ + Bomb, + Player, + Enemy, + Phantom, + Goldcandy, + Candy, +] +for (index, cls) in enumerate(ENTITY_PRECEDENCE): + cls.precedence = index + +def main(argv): + dodgygame() + return 0 + +if __name__ == '__main__': + raise SystemExit(main(sys.argv[1:]))