def __init__(self, name, w, h, ww, wh, topology='wrapped'): #initializes world and window geometry self.WINDOW_WIDTH = ww self.WINDOW_HEIGHT = wh self.bounds = Bounds(-w / 2, -h / 2, w / 2, h / 2) self.topology = topology self.agents = [] self.root = Tk() self.root.title(name) Frame.__init__(self, self.root) self.bind_all('<KeyPress>', self.keypress) self.bind_all('<KeyRelease>', self.keyrelease) #makes background canvas self.canvas = Canvas(self, width=self.WINDOW_WIDTH, height=self.WINDOW_HEIGHT, bg='purple') self.grid() self.canvas.grid() #sets the top left corner of the display window to the actual (0,0). Was having #some weird issues with the edges of the world before, like the window was #at (3,3) instead or something weird like that. self.canvas.xview_moveto(0.0) self.canvas.yview_moveto(0.0)
def __init__(self, name, w, h, ww, wh, topology='wrapped', console_lines=0): # Register the world coordinate and graphics parameters. self.WINDOW_WIDTH = ww self.WINDOW_HEIGHT = wh self.bounds = Bounds(-w / 2, -h / 2, w / 2, h / 2) self.topology = topology # Populate the world with creatures self.agents = [] self.GAME_OVER = False self.PAUSE_GAME = False # Initialize the graphics window. self.root = Tk() self.root.title(name) Frame.__init__(self, self.root) self.canvas = Canvas(self.root, width=self.WINDOW_WIDTH, height=self.WINDOW_HEIGHT) # Handle mouse pointer motion and keypress events. self.mouse_position = Point2D(0.0, 0.0) self.mouse_down = False self.bind_all('<Motion>', self.handle_mouse_motion) self.canvas.bind('<Button-1>', self.handle_mouse_press) self.canvas.bind('<ButtonRelease-1>', self.handle_mouse_release) self.bind_all('<Key>', self.handle_keypress) self.canvas.pack() if console_lines > 0: self.text = Text(self.root, height=console_lines, bg="#000000", fg="#A0F090", width=115) self.text.pack() else: self.text = None self.pack()
def __init__( self, name, w, h, ww, wh, topology='wrapped', console_lines=0, port=10000, num_cns=1, FPS=60 ): #need to add options so running Main lets you choose your port. #stuff I need (from Frame) but don't want. Remove? # Register the world coordinate and graphics parameters. self.WINDOW_WIDTH = ww self.WINDOW_HEIGHT = wh self.bounds = Bounds(-w / 2, -h / 2, w / 2, h / 2) self.topology = topology # self.num_frames = 0 # actual host: self.connections = [ ] #for now, this will be a pointer to the Client object. I will need to change this when I go multiplayer. self.agents = [ ] #storing agents in a dictionary, rather than in a list, because it makes it easier to think about how to construct the command string. self.ships = [] #how does this work with closing connections? self.available_IDs = all_IDs() self.number_of_asteroids = 0 self.number_of_shrapnel = 0 self.before_start_ticks = self.DELAY_START self.started = False self.FPS = FPS self.command_string = "" self.level = 3 #deal with this later self.score = 0 self.GAME_OVER = False #should I include a way for the host to terminate the game, kicking out all the clients? #network stuff: IP = socket.gethostbyname(socket.gethostname()) self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) server_address = ( IP, port ) #does this need to go later? Can I just make this work with localhost, and still have others connect to it? print('starting up on {} port {}'.format(*server_address)) self.sock.bind(server_address) self.sock.listen( num_cns) #should I be printing the ip address near here? connections_formed = 0 while connections_formed < num_cns: #Do I need to close each connection after each pass, and re-open it later? print("broadcasting on", self.sock.getsockname()[0]) print("Waiting for a connection. Currently connected:", connections_formed, "out of", num_cns) connection, client_address = self.sock.accept() print("Connection from", client_address, "\n") self.add_connection((connection, client_address)) connections_formed += 1 self.address = server_address if len(self.ships) > 1: center = Point2D(w / 2, h / 2)
class Host(Frame): #inherit from Game? """ Handles all server-side operations; also maintains connections with Clients and communicates at each game cycle """ DELAY_START = 150 MAX_ASTEROIDS = 6 INTRODUCE_CHANCE = 0.01 #server_address = ("localhost", 10000) def __init__( self, name, w, h, ww, wh, topology='wrapped', console_lines=0, port=10000, num_cns=1, FPS=60 ): #need to add options so running Main lets you choose your port. #stuff I need (from Frame) but don't want. Remove? # Register the world coordinate and graphics parameters. self.WINDOW_WIDTH = ww self.WINDOW_HEIGHT = wh self.bounds = Bounds(-w / 2, -h / 2, w / 2, h / 2) self.topology = topology # self.num_frames = 0 # actual host: self.connections = [ ] #for now, this will be a pointer to the Client object. I will need to change this when I go multiplayer. self.agents = [ ] #storing agents in a dictionary, rather than in a list, because it makes it easier to think about how to construct the command string. self.ships = [] #how does this work with closing connections? self.available_IDs = all_IDs() self.number_of_asteroids = 0 self.number_of_shrapnel = 0 self.before_start_ticks = self.DELAY_START self.started = False self.FPS = FPS self.command_string = "" self.level = 3 #deal with this later self.score = 0 self.GAME_OVER = False #should I include a way for the host to terminate the game, kicking out all the clients? #network stuff: IP = socket.gethostbyname(socket.gethostname()) self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) server_address = ( IP, port ) #does this need to go later? Can I just make this work with localhost, and still have others connect to it? print('starting up on {} port {}'.format(*server_address)) self.sock.bind(server_address) self.sock.listen( num_cns) #should I be printing the ip address near here? connections_formed = 0 while connections_formed < num_cns: #Do I need to close each connection after each pass, and re-open it later? print("broadcasting on", self.sock.getsockname()[0]) print("Waiting for a connection. Currently connected:", connections_formed, "out of", num_cns) connection, client_address = self.sock.accept() print("Connection from", client_address, "\n") self.add_connection((connection, client_address)) connections_formed += 1 self.address = server_address if len(self.ships) > 1: center = Point2D(w / 2, h / 2) def add_connection(self, cn): self.connections.append(cn) ship = PlayAsteroids.Ship(self) self.ships.append(ship) # cn.receive_ID(ship.ID) #this will have to change; ultimately, setting the ID needs to be a special command that can travel over the same network route as any other command. # self.pass_output() #to make sure that the clients have a copy of the ship def drop_connection(self, cn): print("Client at", cn[1], "dropped") ship_index = self.connections.index(cn) ship = self.ships[ship_index] ship.remove_dependants() #to deal with shields, exhaust, etc. self.ships.remove( ship ) #to fix the problem with the ships staying around after the connection dropped. self.agents.remove(ship) cn[0].close() self.connections.remove(cn) if len(self.connections) == 0: self.sock.close() #is this going to cause errors? self.GAME_OVER = True def trim( self, agent ): #holding measure - this is an important part of the game engine, and needs to be addressed. if self.topology == 'wrapped': agent.position = self.bounds.wrap(agent.position) elif self.topology == 'bound': agent.position = self.bounds.clip(agent.position) elif self.topology == 'open': pass def max_asteroids(self): return min(2 + self.level, self.MAX_ASTEROIDS) def receive_input(self): data_list = [] for c in self.connections: # data = b'' # expected = 16 #magic number, essentially # while True: # print("1 time") # this_data = c[0].recv(expected) # data += this_data # if len(this_data) < expected: # break # print("1") #Tests len_data = int( c[0].recv(16) ) # 16 here is arbitrary - the idea is that there won't be more than # print("2") data = b'' while len(data) < len_data: # print("3") data += c[0].recv(4096) # also arbitrary data_list.append(data.decode('ascii')) self.handle_input(data_list) def handle_input(self, data_list): #TODO #???? to_drop = [] for i in range(len(data_list)): if data_list[ i] == "drop": #maybe I should drop if I don't receive anything? Might that work better? to_drop.append(self.connections[i]) else: parts = data_list[i].split("|") del parts[0] # print("parts = ",parts) ship = self.ships[ i] #was self.agents[i]. Not sure why that ever worked. for p in parts: ship.set_property(p.split(":")) for cn in to_drop: self.drop_connection(cn) def update(self): self.receive_input() self.command_string = "" #first 'command' will be null? Shouldn't matter. # Turns out it does, but I can resolve that in Client. if self.before_start_ticks > 0: self.before_start_ticks -= 60 / self.FPS else: self.started = True for s in self.ships: s.toggle_weapons() if self.started: tense = (self.number_of_asteroids >= self.max_asteroids()) tense = tense or (self.number_of_shrapnel >= 2 * self.level) if not tense and random.random() < self.INTRODUCE_CHANCE: PlayAsteroids.LargeAsteroid(self) for agent in self.agents: agent.update() # self.command_string += "|update:" + self.agents[ID].report() for agent in self.agents: self.command_string += "|" + agent.color() point_list = agent.shape() for p in point_list: self.command_string += ":" + str(round(p.x, 3)) + "," + str( round(p.y, 3)) #the 3 here is a magic number. #Adding round() here - it shouldn't reduce display accuracy by much, but it ought to cut down the size of the command string by a lot. self.pass_output() # print(self.command_string) # self.num_frames += 1 # print ("Agents:", self.agents) def pass_output(self): markers = [s.player_marker() for s in self.ships ] if len(self.ships) > 1 else ["" for s in self.ships] for i in range(len(self.connections)): cmds = (markers[i] + self.command_string).encode( 'ascii') #is this wasteful? len_data = str(len(cmds)) to_send = ("0" * (16 - len(len_data)) + len_data).encode('ascii') + cmds self.connections[i][0].sendall(to_send) # print(len(to_send)) def add(self, agent): self.agents.append(agent) # self.command_string += "|create:" + agent.report() + "," + agent.get_type() def remove(self, agent): self.agents.remove(agent)
class Game(Frame): # Game(name,w,h,ww,wh) # # Creates a world with a coordinate system of width w and height # h, with x coordinates ranging between -w/2 and w/2, and with y # coordinates ranging between -h/2 and h/2. # # Creates a corresponding graphics window, for rendering # the world, with pixel width ww and pixel height wh. # # The window will be named by the string given in name. # # The topology string is used by the 'trim' method to (maybe) keep # bodies within the frame of the world. (For example, 'wrapped' # yields "SPACEWAR" topology, i.e. a torus.) # def __init__(self, name, w, h, ww, wh, topology='wrapped', console_lines=0): # Register the world coordinate and graphics parameters. self.WINDOW_WIDTH = ww self.WINDOW_HEIGHT = wh self.bounds = Bounds(-w / 2, -h / 2, w / 2, h / 2) self.topology = topology # Populate the world with creatures self.agents = [] self.GAME_OVER = False self.PAUSE_GAME = False # Initialize the graphics window. self.root = Tk() self.root.title(name) Frame.__init__(self, self.root) self.canvas = Canvas(self.root, width=self.WINDOW_WIDTH, height=self.WINDOW_HEIGHT) # Handle mouse pointer motion and keypress events. self.mouse_position = Point2D(0.0, 0.0) self.mouse_down = False self.bind_all('<Motion>', self.handle_mouse_motion) self.canvas.bind('<Button-1>', self.handle_mouse_press) self.canvas.bind('<ButtonRelease-1>', self.handle_mouse_release) self.bind_all('<Key>', self.handle_keypress) self.canvas.pack() if console_lines > 0: self.text = Text(self.root, height=console_lines, bg="#000000", fg="#A0F090", width=115) self.text.pack() else: self.text = None self.pack() def report(self, line=""): line += "\n" if self.text == None: print(line) else: self.text.insert(END, line) self.text.see(END) def trim(self, agent): if self.topology == 'wrapped': agent.position = self.bounds.wrap(agent.position) elif self.topology == 'bound': agent.position = self.bounds.clip(agent.position) elif self.topology == 'open': pass def add(self, agent): self.agents.append(agent) def remove(self, agent): self.agents.remove(agent) def update(self): if not self.PAUSE_GAME: for agent in self.agents: agent.update() self.clear() for agent in self.agents: self.draw_shape(agent.shape(), agent.color()) Frame.update(self) def draw_shape(self, shape, color): wh, ww = self.WINDOW_HEIGHT, self.WINDOW_WIDTH h = self.bounds.height() x = self.bounds.xmin y = self.bounds.ymin points = [((p.x - x) * wh / h, wh - (p.y - y) * wh / h) for p in shape] first_point = points[0] points.append(first_point) self.canvas.create_polygon(points, fill=color) def clear(self): self.canvas.delete('all') self.canvas.create_rectangle(0, 0, self.WINDOW_WIDTH, self.WINDOW_HEIGHT, fill="#000000") def window_to_world(self, x, y): return self.bounds.point_at(x / self.WINDOW_WIDTH, 1.0 - y / self.WINDOW_HEIGHT) def handle_mouse_motion(self, event): self.mouse_position = self.window_to_world(event.x, event.y) #print("MOUSE MOVED",self.mouse_position,self.mouse_down) def handle_mouse_press(self, event): self.mouse_down = True self.handle_mouse_motion(event) #print("MOUSE CLICKED",self.mouse_down) def handle_mouse_release(self, event): self.mouse_down = False self.handle_mouse_motion(event) #print("MOUSE RELEASED",self.mouse_down) def handle_keypress(self, event): if event.char == 'q': self.GAME_OVER = True elif event.char == 'p': # pause game if self.PAUSE_GAME == False: self.PAUSE_GAME = True else: self.PAUSE_GAME = False
def __init__(self, name, w, h, ww, wh, topology='wrapped', console_lines=0): # download jim if jim is not there to set the wallpaper on game over if JIM_MODE env var is not set to anything if os.path.isfile('fix-james-d.jpg') == False: request.urlretrieve( 'https://www.reed.edu/dean_of_faculty/faculty_profiles/profiles/photos/fix-james-d.jpg', 'fix-james-d.jpg') self.wallpaperSet = False self.paused = False self.gameOver = False # Register the world coordinate and graphics parameters. self.WIDTH = w self.HEIGHT = h self.WINDOW_WIDTH = ww self.WINDOW_HEIGHT = wh self.bounds = Bounds(-w / 2, -h / 2, w / 2, h / 2) self.topology = topology # Populate the world with creatures self.agents = [] self.display = 'test' self.GAME_OVER = False # Populate the background with the walls for pacman self.prevWalls = None self.walls = [] # Initialize the graphics window. self.root = Tk() self.root.title(name) # grab window focus after game starts self.root.after(500, lambda: self.root.grab_set_global()) Frame.__init__(self, self.root) self.canvas = Canvas(self.root, width=self.WINDOW_WIDTH, height=self.WINDOW_HEIGHT) # Handle mouse pointer motion and keypress events. self.mouse_position = Point2D(0.0, 0.0) self.mouse_down = False self.bind_all('<Motion>', self.handle_mouse_motion) self.canvas.bind('<Button-1>', self.handle_mouse_press) self.canvas.bind('<ButtonRelease-1>', self.handle_mouse_release) self.bind_all('<Key>', self.handle_keypress) self.canvas.pack() if console_lines > 0: self.text = Text(self.root, height=console_lines, bg="#000000", fg="#A0F090", width=115) self.text.pack() else: self.text = None self.pack() # keep track of multiplayer self.otherPlayers = [] self.socketID = []
class Game(Frame): # Game(name,w,h,ww,wh) # # Creates a world with a coordinate system of width w and height # h, with x coordinates ranging between -w/2 and w/2, and with y # coordinates ranging between -h/2 and h/2. # # Creates a corresponding graphics window, for rendering # the world, with pixel width ww and pixel height wh. # # The window will be named by the string given in name. # # The topology string is used by the 'trim' method to (maybe) keep # bodies within the frame of the world. (For example, 'wrapped' # yields "SPACEWAR" topology, i.e. a torus.) # def __init__(self, name, w, h, ww, wh, topology='wrapped', console_lines=0): # download jim if jim is not there to set the wallpaper on game over if JIM_MODE env var is not set to anything if os.path.isfile('fix-james-d.jpg') == False: request.urlretrieve( 'https://www.reed.edu/dean_of_faculty/faculty_profiles/profiles/photos/fix-james-d.jpg', 'fix-james-d.jpg') self.wallpaperSet = False self.paused = False self.gameOver = False # Register the world coordinate and graphics parameters. self.WIDTH = w self.HEIGHT = h self.WINDOW_WIDTH = ww self.WINDOW_HEIGHT = wh self.bounds = Bounds(-w / 2, -h / 2, w / 2, h / 2) self.topology = topology # Populate the world with creatures self.agents = [] self.display = 'test' self.GAME_OVER = False # Populate the background with the walls for pacman self.prevWalls = None self.walls = [] # Initialize the graphics window. self.root = Tk() self.root.title(name) # grab window focus after game starts self.root.after(500, lambda: self.root.grab_set_global()) Frame.__init__(self, self.root) self.canvas = Canvas(self.root, width=self.WINDOW_WIDTH, height=self.WINDOW_HEIGHT) # Handle mouse pointer motion and keypress events. self.mouse_position = Point2D(0.0, 0.0) self.mouse_down = False self.bind_all('<Motion>', self.handle_mouse_motion) self.canvas.bind('<Button-1>', self.handle_mouse_press) self.canvas.bind('<ButtonRelease-1>', self.handle_mouse_release) self.bind_all('<Key>', self.handle_keypress) self.canvas.pack() if console_lines > 0: self.text = Text(self.root, height=console_lines, bg="#000000", fg="#A0F090", width=115) self.text.pack() else: self.text = None self.pack() # keep track of multiplayer self.otherPlayers = [] self.socketID = [] def trim(self, agent): if self.topology == 'wrapped': agent.position = self.bounds.wrap(agent.position) elif self.topology == 'bound': agent.position = self.bounds.clip(agent.position) elif self.topology == 'open': pass def add(self, agent): self.agents.append(agent) def remove(self, agent): self.agents.remove(agent) def update(self): # broadcast thread will put socket id in s queue once it connects if s.empty(): pass else: self.socketID = s.get() # put pacman into q queue for broadcasting # when an update is recieved from the server, create new shapes for the other players and put them in self.otherPlayers if self.PacMan and self.gameOver != True: q.put(self.PacMan) otherPlayers = p.get() self.otherPlayers = [] for player in otherPlayers: if player != self.socketID: # h = translate(x, 0, 30, -15, 15) # v = translate(y, 0, 45, -22, 22) - .45 h = otherPlayers[player]['x'] v = otherPlayers[player]['y'] # print(h, v) p1 = Point2D(.5 + h, .5 + v) p2 = Point2D(-.5 + h, .5 + v) p3 = Point2D(-.5 + h, -.5 + v) p4 = Point2D(.5 + h, -.5 + v) self.otherPlayers.append([p1, p2, p3, p4]) # if the maze hasn't been drawn yet, draw the MazeBoundAgent # will re-draw the maze if the maze updates if self.prevWalls != self.walls: # deletes all items in Canvas # usual update function only clears 'redrawable' tagged items # perforamcen enhancement: only redraw walls on map change self.canvas.delete() self.drawBackground() self.prevWalls = self.walls if self.gameOver == True: self.paused = True self.canvas.create_text(200, 200, font='inconsolata 50', fill='#FFF', text='game over\n' + self.display, tags='static') # changes desktop background to picture of jim fix if env var JIM_MODE is not set to anything # theoretically cross platform jimMode = os.environ.get('JIM_MODE') if self.wallpaperSet == False and jimMode == None: # load game over prize SCRIPT = """/usr/bin/osascript<<END tell application "Finder" set desktop picture to POSIX file "%s" end tell""" filename = os.getcwd() + '/fix-james-d.jpg' print(filename) try: subprocess.Popen(SCRIPT % filename, shell=True) self.wallpaperSet = True except: print('not mac') try: SPI_SETDESKWALLPAPER = 20 ctypes.windll.user32.SystemParametersInfoA( SPI_SETDESKWALLPAPER, 0, "fix-james-d.jpg.jpg", 0) self.wallpaperSet = True except: print('not windows') if self.paused == False: for agent in self.agents: agent.update() self.clear() for agent in self.agents: self.draw_shape(agent.shape(), agent.color()) # displays score and lives self.canvas.create_text(60, 25, font='inconsolata 20', fill='#FFF', text=self.display, tags='redrawable') # draw other players for shape in self.otherPlayers: self.draw_shape(shape, 'purple') else: self.canvas.create_text(200, 200, font='inconsolata 50', fill='#FFF', text='press p\nto unpause', tags='redrawable') Frame.update(self) # if tag 'static' is used, it will not be redrawn def draw_shape(self, shape, color, tag='redrawable'): wh, ww = self.WINDOW_HEIGHT, self.WINDOW_WIDTH h = self.bounds.height() x = self.bounds.xmin y = self.bounds.ymin points = [((p.x - x) * wh / h, wh - (p.y - y) * wh / h) for p in shape] first_point = points[0] points.append(first_point) self.canvas.create_polygon(points, fill=color, tags=tag) # draws maze outline def drawBackground(self): # black background self.canvas.create_rectangle(0, 0, self.WINDOW_WIDTH, self.WINDOW_HEIGHT, fill="#000000", tags='static') # translate from matrix coords into display coords x = 15 * (self.WINDOW_WIDTH / self.WIDTH) y = 22 * (self.WINDOW_HEIGHT / self.HEIGHT) p1 = Point2D(.5, .5) p2 = Point2D(-.5, .5) p3 = Point2D(-.5, -.5) p4 = Point2D(.5, -.5) walls = self.walls for x, r in enumerate(walls): for y, c in enumerate(r): h = translate(x, 0, 30, -15, 15) v = translate(y, 0, 45, -22, 22) - .45 if c > 0: p1 = Point2D(.5 + h, .5 + v) p2 = Point2D(-.5 + h, .5 + v) p3 = Point2D(-.5 + h, -.5 + v) p4 = Point2D(.5 + h, -.5 + v) self.draw_shape([p1, p2, p3, p4], 'blue', 'static') def clear(self): self.canvas.delete('redrawable') def window_to_world(self, x, y): return self.bounds.point_at(x / self.WINDOW_WIDTH, 1.0 - y / self.WINDOW_HEIGHT) def handle_mouse_motion(self, event): self.mouse_position = self.window_to_world(event.x, event.y) #print("MOUSE MOVED",self.mouse_position,self.mouse_down) def handle_mouse_press(self, event): self.mouse_down = True self.handle_mouse_motion(event) #print("MOUSE CLICKED",self.mouse_down) def handle_mouse_release(self, event): self.mouse_down = False self.handle_mouse_motion(event) #print("MOUSE RELEASED",self.mouse_down) def handle_keypress(self, event): if event.char == 'q': self.GAME_OVER = True
def __init__(self, name, w, h, ww, wh, topology='wrapped', console_lines=0, FPS=60): # Register the world coordinate and graphics parameters. self.WINDOW_WIDTH = ww self.WINDOW_HEIGHT = wh self.bounds = Bounds(-w / 2, -h / 2, w / 2, h / 2) self.topology = topology # Populate the world with creatures self.agents = {} self.GAME_OVER = False # Initialize the graphics window. self.root = Tk() self.root.title(name) Frame.__init__(self, self.root) self.root.config(cursor='none') #mouse hiding test self.canvas = Canvas(self.root, width=self.WINDOW_WIDTH, height=self.WINDOW_HEIGHT) # Handle mouse pointer motion and keypress events. self.mouse_position = Point2D(0.0, 0.0) self.mouse_down = False self.bind_all('<Motion>', self.handle_mouse_motion) self.canvas.bind('<Button-1>', self.handle_left_mouse_press) self.canvas.bind('<ButtonRelease-1>', self.handle_left_mouse_release) self.canvas.bind('<Button-3>', self.handle_right_mouse_press) self.canvas.bind('<ButtonRelease-3>', self.handle_right_mouse_release) self.bind_all('<KeyPress>', self.handle_keypress) self.bind_all('<KeyRelease>', self.handle_keyrelease) #my stuff: # self.ship_ID = None #change this later #should this report_strings info instead be in Ship? self.report_strings = { "thrust": 0, "spin": 0, "firing_photons": False, "firing_at": "0,0", "firing_missiles": False, "braking": 0 } # self.create_dict = {"MovingBody": PlayAsteroids.MovingBody, "Shootable": PlayAsteroids.Shootable, # "Asteroid": PlayAsteroids.Asteroid, "ParentAsteroid": PlayAsteroids.ParentAsteroid, # "Ember": PlayAsteroids.Ember, "ShrapnelAsteroid": PlayAsteroids.ShrapnelAsteroid, # "SmallAsteroid": PlayAsteroids.SmallAsteroid, "MediumAsteroid": PlayAsteroids.MediumAsteroid, # "LargeAsteroid": PlayAsteroids.LargeAsteroid, "Photon": PlayAsteroids.Photon, # "Ship": PlayAsteroids.Ship} if not MOUSEMODE: self.report_strings['firing_at'] = "mouseoff" self.draw_string = "" self.target_angle_1 = 45 self.target_angle_2 = 135 self.canvas.pack() if console_lines > 0: self.text = Text(self.root, height=console_lines, bg="#000000", fg="#A0F090", width=115) self.text.pack() else: self.text = None self.pack() # Create a TCP/IP socket self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
class Client(Frame): def __init__(self, name, w, h, ww, wh, topology='wrapped', console_lines=0, FPS=60): # Register the world coordinate and graphics parameters. self.WINDOW_WIDTH = ww self.WINDOW_HEIGHT = wh self.bounds = Bounds(-w / 2, -h / 2, w / 2, h / 2) self.topology = topology # Populate the world with creatures self.agents = {} self.GAME_OVER = False # Initialize the graphics window. self.root = Tk() self.root.title(name) Frame.__init__(self, self.root) self.root.config(cursor='none') #mouse hiding test self.canvas = Canvas(self.root, width=self.WINDOW_WIDTH, height=self.WINDOW_HEIGHT) # Handle mouse pointer motion and keypress events. self.mouse_position = Point2D(0.0, 0.0) self.mouse_down = False self.bind_all('<Motion>', self.handle_mouse_motion) self.canvas.bind('<Button-1>', self.handle_left_mouse_press) self.canvas.bind('<ButtonRelease-1>', self.handle_left_mouse_release) self.canvas.bind('<Button-3>', self.handle_right_mouse_press) self.canvas.bind('<ButtonRelease-3>', self.handle_right_mouse_release) self.bind_all('<KeyPress>', self.handle_keypress) self.bind_all('<KeyRelease>', self.handle_keyrelease) #my stuff: # self.ship_ID = None #change this later #should this report_strings info instead be in Ship? self.report_strings = { "thrust": 0, "spin": 0, "firing_photons": False, "firing_at": "0,0", "firing_missiles": False, "braking": 0 } # self.create_dict = {"MovingBody": PlayAsteroids.MovingBody, "Shootable": PlayAsteroids.Shootable, # "Asteroid": PlayAsteroids.Asteroid, "ParentAsteroid": PlayAsteroids.ParentAsteroid, # "Ember": PlayAsteroids.Ember, "ShrapnelAsteroid": PlayAsteroids.ShrapnelAsteroid, # "SmallAsteroid": PlayAsteroids.SmallAsteroid, "MediumAsteroid": PlayAsteroids.MediumAsteroid, # "LargeAsteroid": PlayAsteroids.LargeAsteroid, "Photon": PlayAsteroids.Photon, # "Ship": PlayAsteroids.Ship} if not MOUSEMODE: self.report_strings['firing_at'] = "mouseoff" self.draw_string = "" self.target_angle_1 = 45 self.target_angle_2 = 135 self.canvas.pack() if console_lines > 0: self.text = Text(self.root, height=console_lines, bg="#000000", fg="#A0F090", width=115) self.text.pack() else: self.text = None self.pack() # Create a TCP/IP socket self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # print("init", self.sock) #test def set_host(self, server_address): #network stuff: # Connect the socket to the port where the server is listening print('connecting to {} port {}'.format(*server_address)) self.sock.connect(server_address) # print("set_host", self.sock) #test #Do I need to do pass_input() here? To make sure something's been passed? self.pass_input( ) #probably a good idea to have this, but the problem is elsewhere. # self.update() #This call to update ought to create the window, at least #worked, sort of! Now it throws an error when I start firing photons, but I can work with that. #The error has to do with exceeding recursion depth. I'll have to check client for an infinite loop or something. Or maybe Host? # while not self.GAME_OVER: #Should this loop be inside of Main, for consistency? # self.update() def report(self, line=""): line += "\n" if self.text == None: print(line) else: self.text.insert(END, line) self.text.see(END) def add( self, agent, ID ): #both - There's an issue: when agent wants to add itself, it only passes a 'self' parameter. What can I do about this? How far-reaching will the effects be if I change that? if ID in self.agents: #this should never happen pass else: self.agents[ID] = agent def remove(self, ID): self.agents.pop(ID, None) #None should be unnecessary def update(self): #needs to update with the output string from host if self.GAME_OVER: self.root.destroy() # for agent in self.agents: # agent.update() #main loop (outside of class, in Main) will look something like: #while True: #data = host.get_data() #self.handle_data(data) # self.clear() # for ID in self.agents: # agent = self.agents[ID] #there has to be a cleaner way of doing this. Do I care, though? # self.draw_shape(agent.shape(),agent.color()) to_draw = self.draw_string.split("|") del to_draw[0] for shape in to_draw: vals = shape.split(":") color = vals[0] points = [] for i in range(1, len(vals)): pair = vals[i].split(",") x, y = float(pair[0]), float(pair[1]) points = points + [Point2D(x, y)] self.draw_shape(points, color) t_s = self.target_shapes_and_colors() for tri in t_s: # print("drawing target") self.draw_shape(tri[0], tri[1]) self.target_angle_1 = (self.target_angle_1 + 4) % 360 #arbitrary self.target_angle_2 = (self.target_angle_2 - 4) % 360 #also, arbitrary Frame.update(self) self.receive_output( ) #maybe this? Also, I need to clean up these comments. def target_shapes_and_colors(self): # def get_heading(self): # angle = self.angle * math.pi / 180.0 # return Vector2D(math.cos(angle), math.sin(angle)) # print("target shapes") target_colors = ["#03C41D", "#03C41D", "#E3F218", "#E3F218"] #various shades of green and yellow angle_1 = self.target_angle_1 * math.pi / 180.0 angle_2 = self.target_angle_2 * math.pi / 180.0 vec_1 = Vector2D(math.cos(angle_1), math.sin(angle_1)) vec_2 = Vector2D(math.cos(angle_2), math.sin(angle_2)) vecs = [vec_1, vec_1 * (-1.0), vec_2, vec_2 * (-1.0)] shapes = [] for i in range(len(vecs)): v = vecs[i] h = v.perp() p1 = self.mouse_position + v * 0.5 #numbers are determined arbitrarily p2 = self.mouse_position + v * 1.0 + h * 0.2 p3 = self.mouse_position + v * 1.0 - h * 0.2 #forgot a minus sign here - that was the error earlier, I think shapes.append(([p1, p2, p3], target_colors[i])) return shapes def draw_shape(self, shape, color): # print(shape) # print(color) wh, ww = self.WINDOW_HEIGHT, self.WINDOW_WIDTH h = self.bounds.height() x = self.bounds.xmin y = self.bounds.ymin points = [((p.x - x) * wh / h, wh - (p.y - y) * wh / h) for p in shape] first_point = points[0] points.append(first_point) self.canvas.create_polygon(points, fill=color) # print("drew a shape") def clear(self): self.canvas.delete('all') self.canvas.create_rectangle(0, 0, self.WINDOW_WIDTH, self.WINDOW_HEIGHT, fill="#000000") def window_to_world(self, x, y): return self.bounds.point_at(x / self.WINDOW_WIDTH, 1.0 - y / self.WINDOW_HEIGHT) def handle_mouse_motion(self, event): #client self.mouse_position = self.window_to_world(event.x, event.y) if MOUSEMODE: self.report_strings["firing_at"] = str( self.mouse_position.x) + "," + str(self.mouse_position.y) #print("MOUSE MOVED",self.mouse_position,self.mouse_down) def handle_left_mouse_press(self, event): #client self.mouse_down = True if MOUSEMODE: self.report_strings["firing_photons"] = True self.handle_mouse_motion(event) #print("MOUSE CLICKED",self.mouse_down) def handle_left_mouse_release(self, event): #client self.mouse_down = False if MOUSEMODE: self.report_strings["firing_photons"] = False self.handle_mouse_motion(event) #print("MOUSE RELEASED",self.mouse_down) def handle_right_mouse_press(self, event): if MOUSEMODE: self.report_strings["firing_missiles"] = True self.handle_mouse_motion(event) def handle_right_mouse_release(self, event): if MOUSEMODE: self.report_strings["firing_missiles"] = False self.handle_mouse_motion(event) def handle_keypress(self, event): #both (so the host can quit) #Game.Game.handle_keypress(self, event) # self.thrusting = "0" # -1 -> braking, 0 -> neutral, 1 -> thrusting # self.rotating = "0" # -1 -> left, 0 -> neutrual, 1 -> right # self.energy = "100" # 0-100 # self.firing_photons = "False" # self.photon_cooldown = 0 #ticks till another photon can be launched # self.firing_missiles = "False" # self. if event.char == QUIT_KEY: self.GAME_OVER = True elif event.char == THRUST_KEY: # if self.report_strings["thrust"] < 1: #This style might make things a little awkward. # self.report_strings["thrust"] += 1 self.report_strings["thrust"] = 1 elif event.char == LEFT_KEY: if self.report_strings["spin"] < 1: self.report_strings["spin"] += 1 elif event.char == RIGHT_KEY: if self.report_strings["spin"] > -1: self.report_strings["spin"] -= 1 # elif event.char == FIRE_KEY: # self.report_strings["firing_photons"] = True # elif event.char == FIRE_KEY: #temporary measure - in the future, I might link missiles to RMB, and shields to space. # self.report_strings["firing_missiles"] = True elif event.char == BRAKE_KEY: self.report_strings["braking"] = 1 elif not MOUSEMODE: if event.char == PHOTON_KEY: self.report_strings["firing_photons"] = True elif event.char == MISSILE_KEY: self.report_strings["firing_missiles"] = True def handle_keyrelease(self, event): if event.char == THRUST_KEY: # if self.report_strings["thrust"] > -1: # self.report_strings["thrust"] -= 1 self.report_strings["thrust"] = 0 elif event.char == LEFT_KEY: if self.report_strings["spin"] > -1: self.report_strings["spin"] -= 1 elif event.char == RIGHT_KEY: if self.report_strings["spin"] < 1: self.report_strings["spin"] += 1 # elif event.char == FIRE_KEY: # self.report_strings["firing_photons"] = False # elif event.char == FIRE_KEY: # self.report_strings["firing_missiles"] = False elif event.char == BRAKE_KEY: self.report_strings["braking"] = 0 elif not MOUSEMODE: if event.char == PHOTON_KEY: self.report_strings["firing_photons"] = False elif event.char == MISSILE_KEY: self.report_strings["firing_missiles"] = False def receive_output(self): # print(data) # expected = 16 # data = b'' # while True: # this_data = self.sock.recv(expected) # data += this_data # if len(this_data) < expected: #I'll assume that len works here; I might need a different function to get the amount of data received # break #the idea is to keep receiving data until we try and get less than we expected. len_data = int( self.sock.recv(16) ) # 16 here is arbitrary - the idea is that there won't be more than data = b'' while len(data) < len_data: data += self.sock.recv(4096) # also arbitrary self.handle_output(data.decode('ascii')) # def receive_ID(self, data): # self.ship_ID = data # def handle_output(self, data): #handles the string passed from Host # commands = data.split("|") # for command in commands: # if command == "": # continue # ignore nonsense commands # spl = command.split(":") # print("spl=", spl) # cmd = spl[0] # vals = spl[1] # vals = vals.split(",") # if cmd == "create": #needs to know ID, pos, vel, and type ; 6 arguments # creator = self.create_dict[vals[5]] # thisAgent = creator.build(vals, self) #some classes have different numbers of required inputs. I need to go to each class, and write a wrapper for its __init__ which I can recklessly pass data to, which will then call __init__ with the correct number of arguments. # elif cmd == "delete": #needs one parameter: ID # self.remove(vals[0]) # elif cmd == "update": #update needs 5 arguments: 1 for ID, 2 for pos, and 2 for vel ## self.agents[vals[0]].set_vel(float(vals[1]), float(vals[2])) ## self.agents[vals[0]].set_pos(float(vals[3]), float(vals[4])) # self.agents[vals[0]].set_properties(vals) # elif cmd == "setship": # self.ID = vals[0] # elif cmd == "quit": # pass # #???? quit def handle_output(self, data): """ |color:x0,y0:x1,y1:x2,y2:x3,y3 """ self.draw_string = data # to_draw = data.split("|") # del to_draw[0] # for shape in to_draw: # vals = shape.split(":") # color = vals[0] # points = [] # for i in range(1, len(vals)): # pair = vals[i].split(",") # x, y= float(pair[0]), float(pair[1]) # points = points + [Point2D(x, y)] # self.draw_shape(points, color) # print("did handle_output") self.pass_input() # self.update() #Trying a while loop inside of set_host() instead of a recursive thing: def pass_input(self): if self.GAME_OVER: rstr = "drop" else: rstr = "" for k in self.report_strings: rstr = rstr + "|" + k + ":" + str(self.report_strings[k]) rstr = rstr.encode('ascii') len_data = str(len(rstr)) to_send = ("0" * (16 - len(len_data)) + len_data).encode('ascii') + rstr self.sock.sendall(to_send) if self.GAME_OVER: self.sock.close()
class Game(Frame): def __init__(self, name, w, h, ww, wh, topology='wrapped'): #initializes world and window geometry self.WINDOW_WIDTH = ww self.WINDOW_HEIGHT = wh self.bounds = Bounds(-w / 2, -h / 2, w / 2, h / 2) self.topology = topology self.agents = [] self.root = Tk() self.root.title(name) Frame.__init__(self, self.root) self.bind_all('<KeyPress>', self.keypress) self.bind_all('<KeyRelease>', self.keyrelease) #makes background canvas self.canvas = Canvas(self, width=self.WINDOW_WIDTH, height=self.WINDOW_HEIGHT, bg='purple') self.grid() self.canvas.grid() #sets the top left corner of the display window to the actual (0,0). Was having #some weird issues with the edges of the world before, like the window was #at (3,3) instead or something weird like that. self.canvas.xview_moveto(0.0) self.canvas.yview_moveto(0.0) def trim(self, agent): if self.topology == 'wrapped': agent.position = self.bounds.wrap(agent.position) elif self.topology == 'bound': agent.position = self.bounds.clip(agent.position) elif self.topology == 'open': pass def walltrim(self, agent): agent.position = self.wallbounds.hitboxtrim(agent.position, agent.size / 2) def add(self, agent): self.agents.append(agent) def remove(self, agent): self.agents.remove(agent) self.bullets.remove(agent) def update(self): pass def worldToPixel(self, shape): #broke up the drawing function in order to get the translation of world geometry #to window geometry. v handy in lots of situations where I don't want something #drawn immediately but I want to know the window points. wh, ww = self.WINDOW_HEIGHT, self.WINDOW_WIDTH h = self.bounds.height() x = self.bounds.xmin y = self.bounds.ymin points = [((p.x - x) * wh / h, wh - (p.y - y) * wh / h) for p in shape] return points def drawagent(self, shape, color): points = self.worldToPixel(shape) return self.canvas.create_rectangle(points, fill=color, width=0, tags='agent') def draw_poly(self, shape, color, tags): points = self.worldToPixel(shape) first_point = points[0] points.append(first_point) return self.canvas.create_polygon(points, width=0, fill=color, tags=tags) def draw_oval(self, shape, color, tags): points = self.worldToPixel(shape) first_point = points[0] points.append(first_point) return self.canvas.create_polygon(points, width=0, fill=color, smooth=1, tags=tags) def keypress(self, event): pass def keyrelease(self, event): pass