#! /usr/bin/env python '''Copyright 2008 Nolan Baker''' '''Welcome to my code. Everything should be pretty straight foreward. If you find anything in the code that needs to be fixed, or if there's anything that you don't understand, feel free to email me at nbaker@centenary.edu with the subject "Space Tag". Remember, the object here is to learn, so stay focused, and be safe out there.''' import math, random, pygame, logging, olpcgames from olpcgames import mesh, eventwrap from pygame.locals import * # interestingly enough, the log.debug function is just a print statement log = logging.getLogger( 'Thor vs. Demon run' ) log.setLevel( logging.DEBUG ) ################################################################################ # Load Images ################################################################################ red = pygame.image.load("red.gif") red.set_colorkey((255,255,255)) yellow = pygame.image.load("yellow.gif") yellow.set_colorkey((255,255,255)) ################################################################################ # Buttons ################################################################################ UP = pygame.K_w LEFT = pygame.K_a RIGHT = pygame.K_d DOWN = pygame.K_s CLIMB = pygame.K_DOWN DROP = pygame.K_UP BOOST = pygame.K_TAB ################################################################################ # Object Functions ################################################################################ def Distance(self, x, y): # finds distance between self and a given location on a 2D plane d = math.sqrt((x - self.s[0]) ** 2 + (y - self.s[1]) ** 2) if d == 0: return .0000000001 # returns a distance (not a displacement) else: return d def Angle(self, x, y): # this finds an angle from an object to a point distance = Distance(self, x, y) angle = math.acos(float(self.s[0] - x) / float(distance)) angle *= 180.0 / math.pi # pygame likes degrees angle -= 90 # adjustment for the orientation of the character # now we have to make some changes because math is silly # just remember, pygame only likes 0-359 (Go, go gadget modulo!) if (y < self.s[1]): angle = (-angle) % 360 elif(y > self.s[1]): angle = (angle + 180) % 360 else: if (x <= self.s[0]): angle = 90 elif (x > self.s[0]): angle = 270 return angle def Transform(self, angle): center = self.rect.center change = 200 * self.dt self.ang_diff = int(self.angle - angle) if (180 > self.ang_diff > 0) or (-180 >= self.ang_diff > -360): self.angle = (self.angle - change) % 360 if (-180 < self.ang_diff < 0) or (180 <= self.ang_diff < 360): self.angle = (self.angle + change) % 360 # rotozoom is used here because you always want to do your transformations # on the original image, otherwise you end up with heavy distortion # eventually, I'll make the switch to vector graphics self.image = pygame.transform.rotozoom(self.original, self.angle, .25 * (self.s[2] / 100 + 1)) self.rect = self.image.get_rect(center = center) ################################################################################ # Sprites ################################################################################ class It(pygame.sprite.Sprite): def __init__(self, game, owner = None): pygame.sprite.Sprite.__init__(self) self.owner = owner self.game = game self.wait = 200 self.angle = 0 self.original = pygame.image.load("it.gif") self.original.set_colorkey((255,255,255)) self.original.convert() self.waiting = pygame.image.load("waiting.gif") self.waiting.set_colorkey((255,255,255)) self.waiting.convert() self.image = self.waiting self.rect = self.image.get_rect() self.s = [self.game.res[0] / 2, self.game.res[1] / 2, 100] def setOwner(self, owner): self.owner = owner def dist(self, other): d = math.sqrt((self.s[0] - other.s[0]) ** 2 + (self.s[1] - other.s[1]) ** 2 + (self.s[2] - other.s[2]) ** 2) return d def touching(self, other): d = self.dist(other) r_self = self.rect.width / 2.0 r_other = other.rect.width / 2.0 # if the distance between the 2 objects is <= the sum of their radii log.debug(str(d) + " " + str(r_self + r_other)) if d <= (r_self + r_other): # a legal tag has been made return True return False def update(self): self.dt = self.game.clock.get_rawtime() / 1000.0 self.rect.center = (self.s[0], self.s[1]) if self.wait <= 0: log.debug("It owner: " + str(self.owner)) if (self.owner == None): self.owner = self.game.avatar elif (self.owner == self.game.avatar): log.debug("I'm it!") for other in self.game.others.sprites(): if self.touching(other): if self.owner != None: print 'here' self.owner.it = False other.it = True self.owner = other log.debug("You're it! " + other.handle) olpcgames.mesh.broadcast("tag|" + mesh.my_handle() + ";" + other.handle) else: log.debug("Waiting..." + str(self.wait)) self.wait -=1 if self.owner != None: self.angle += 7 self.angle %= 360 self.s = self.owner.s Transform(self, self.angle) class Character(pygame.sprite.Sprite): def __init__(self, game, image, x, y, z, mass): pygame.sprite.Sprite.__init__(self) self.game = game self.it = False self.original = image self.original.set_colorkey((255,255,255)) self.original.convert() self.image = self.original self.rect = self.image.get_rect() self.area = pygame.display.get_surface().get_rect() self.rect.center = (x,y) self.angle = 0 # physical variables self.s = [x, y, z] # position self.v = [0, 0, 0] # velocity self.a = [0, 0, 0] # acceleration self.f = [0, 0, 0] # sum of forces self.m = mass self.forces = [[],[],[]] # list of forces def SumForces(self): for i in range(0, 3): total = 0 for j in range(0, len(self.forces[i])): total += self.forces[i][j] self.f[i] = total def WrapScreen(self): # if you go off the screen, come out on the otherside self.s[0] %= self.game.res[0] self.s[1] %= self.game.res[1] def ConstrainHeight(self): # we also don't want you flying too high or too low if self.s[2] > 400: self.s[2] = 400 if self.s[2] < 0: self.s[2] = 0 def Move(self): # Think back to calculus... self.dt = self.game.clock.get_rawtime() / 1000.0 # let's move the Newton way for i in range(0, 3): self.a[i] = self.f[i] / float(self.m) self.v[i] += self.a[i] * self.dt self.s[i] += self.v[i] * self.dt # just in case we want to go off screen or fly too high self.WrapScreen() self.ConstrainHeight() # once the right spot has been found set the sprite there self.rect.centerx = int(self.s[0]) self.rect.centery = int(self.s[1]) # since we've put this frame's forces to good use # we must now clear out the list, so we can do it again self.forces = [[],[],[]] def FaceHeading(self): # if you're moving, turn to face where you're going if (pygame.key.get_pressed()[UP] or pygame.key.get_pressed()[DOWN] or pygame.key.get_pressed()[LEFT] or pygame.key.get_pressed()[RIGHT]): heading_x = self.s[0] + self.v[0] * self.dt heading_y = self.s[1] + self.v[1] * self.dt angle = Angle(self, heading_x, heading_y) else: angle = self.angle Transform(self, angle) def getSpeed(self): return int(math.sqrt(self.v[0] ** 2 + self.v[1] ** 2 + self.v[2] ** 2)) def update(self): # if you're it and the game doesn't know it yet if self.it and self.game.it.sprites()[0].owner != self: # let it know self.game.it.sprites()[0].setOwner(self) class Dummy(Character): def update(self): Character.update(self) self.Move() Transform(self, self.angle) class Human(Character): def update(self): Character.update(self) # boost, because every spaceman could use one boost = 1 if pygame.key.get_pressed()[BOOST]: boost = 2 # this speed cap is unnecessary when friction is added # however, this is Space Tag, not Earth Tag if self.getSpeed() < 600: # here's how to move if pygame.key.get_pressed()[UP]: self.forces[1].append(-200 * boost) # up if pygame.key.get_pressed()[DOWN]: self.forces[1].append(200 * boost) # down if pygame.key.get_pressed()[LEFT]: self.forces[0].append(-200 * boost) # left if pygame.key.get_pressed()[RIGHT]: self.forces[0].append(200 * boost) # right if pygame.key.get_pressed()[DROP]: self.forces[2].append(-200 * boost) # away from you if pygame.key.get_pressed()[CLIMB]: self.forces[2].append(200 * boost) # towards you # since space doesn't have a lot of things to slow you down # the space ship does it automatically if ((int(self.v[2]) < 0) and (not pygame.key.get_pressed()[DROP])): self.forces[2].append(200) if ((int(self.v[2]) > 0) and (not pygame.key.get_pressed()[CLIMB])): self.forces[2].append(-200) if ((int(self.v[1]) < 0) and (not pygame.key.get_pressed()[UP])): self.forces[1].append(200) if ((int(self.v[1]) > 0) and (not pygame.key.get_pressed()[DOWN])): self.forces[1].append(-200) if ((int(self.v[0]) < 0) and (not pygame.key.get_pressed()[LEFT])): self.forces[0].append(200) if ((int(self.v[0]) > 0) and (not pygame.key.get_pressed()[RIGHT])): self.forces[0].append(-200) #let everyone else know where you are and what you're doing connections = len(olpcgames.mesh.pygametubes) if connections != 0: # this is just a big, nasty instant message s = "%s,%s,%s" % (self.s[0], self.s[1], self.s[2]) + ";" ang = str(self.angle) olpcgames.mesh.broadcast("pos|" + s + ang) # order is super important here self.SumForces() self.Move() self.FaceHeading() ################################################################################ # Game ################################################################################ class Game(): def __init__(self, resolution = (1200,900)): pygame.init() self.res = resolution self.screen = pygame.display.set_mode(self.res) # this makes the mouse invisible pygame.mouse.set_visible(0) # this gives our game a sense of time self.clock = pygame.time.Clock() def pauseImage(self): self.pausescreen = pygame.image.load("pausescreen.gif") self.pausescreen.convert() def backgroundImage(self): self.background = pygame.image.load("background.gif") self.background.convert() def makeSprites(self): # Name_of_Character = Character(game, image, x, y, z, mass) x = random.randint(50, self.res[0] - 50) y = random.randint(50, self.res[1] - 50) self.avatar = Human(self, yellow, x, y, 100, 2) self.me = pygame.sprite.Group((self.avatar)) self.others = pygame.sprite.Group() self.handle_dict = {} self.itit = It(self) self.it = pygame.sprite.Group((self.itit)) def pauseScreen(self): # this loop starts when ESC is pressed in the mainloop self.paused = True while self.paused: self.clock.tick() # check to see if anything happened for event in pygame.event.get(): # make sure you can quit your game from here too if event.type == QUIT: self.running, self.paused = False, False if event.type == KEYDOWN: # ESC puts you back in the mainloop if event.key == K_ESCAPE: self.paused, self.running = False, True # 'q' quits the game (imagine that) if event.key == K_q: self.running, self.paused = False, False # draw the pausescreen self.screen.blit(self.pausescreen, (0, 0)) # refresh the monitor pygame.display.flip() def mainloop(self): # load your images, so you don't have to do it later self.pauseImage() self.backgroundImage() # now make your avatar and create some groups self.makeSprites() # then lather, rinse, and repeat self.running = True while self.running: self.clock.tick() # draw the background first self.screen.blit(self.background, (0, 0)) # then see if anything happened since last you checked for event in pygame.event.get([QUIT, KEYDOWN, olpcgames.PARTICIPANT_ADD, olpcgames.MESSAGE_MULTI]): # here's how to quit if event.type == QUIT: self.running = False # here's how to pause the game if event.type == KEYDOWN and event.key == K_ESCAPE: self.pauseScreen() # make a dummy sprite and put it in a dictionary if (event.type == olpcgames.PARTICIPANT_ADD and event.handle != olpcgames.mesh.my_handle()): dummy = Dummy(self, red, -30, -30, 0, 2) dummy.handle = event.handle self.handle_dict[event.handle] = dummy if (self.itit.owner == self.avatar): olpcgames.mesh.broadcast("tag|None;" + mesh.my_handle()) # Incoming! Those messages are hitting everyone... if (event.type == olpcgames.MESSAGE_MULTI and event.handle != olpcgames.mesh.my_handle()): # this part takes a global message and converts it to data mes, content = event.content.split("|") if mes == "pos": s, angle = content.split(";") s1, s2, s3 = s.split(",") # position s1, s2, s3 = float(s1), float(s2), float(s3) angle = float(angle) # this part inserts the data into the dummy character try: dummy = self.handle_dict[event.handle] dummy.s = [s1, s2, s3] dummy.angle = angle self.handle_dict[event.handle] = dummy except: log.debug('something bad happened') elif mes == "tag": tagger, tagee = content.split(";") if tagger != "None": self.handle_dict[tagger].it = False if tagee != mesh.my_handle(): self.handle_dict[tagee].it = True self.itit.owner = self.handle_dict[tagee] else: self.avatar.it = True self.itit.owner = self.avatar self.itit.wait = 2 * self.clock.get_fps() # if the dummy isn't already grouped, do it for dummy in self.handle_dict.values(): if dummy not in self.others.sprites(): self.others.add(dummy) # keep the sprites up to date self.me.update() self.others.update() self.it.update() # draw them to the screen self.me.draw(self.screen) self.others.draw(self.screen) self.it.draw(self.screen) # then refresh the monitor pygame.display.flip() # log.debug(str(int(self.clock.get_fps())) + " fps") ################################################################################ # Now for the show... ################################################################################ def main(): spacetag = Game() spacetag.mainloop() if __name__ == "__main__": logging.basicConfig() main() pygame.quit () '''Stay tuned, more games are on their way.'''