#! /usr/bin/env python # -*- coding: utf-8 -*- # # SprayPlay # Copyright (C) 2008, Brian Jordan, Gregory Jordan, Eric Jordan # Copyright (C) 2012, Alan Aguiar # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # # Contact information: # Brian Jordan # Gregory Jordan # Eric Jordan # Alan Aguiar from __future__ import division import os import gtk import pygame import random from math import * from pygame import * # To win PUCKS_TO_WIN = 5 # Bullet constants BULLET_RADIUS = 10.0 BULLET_SPEED = 4 BULLET_MASS = 0.2 AMMO_PER_PLAYER = 20 # Player rotation constants PLAYER_ARM_LENGTH = 20.0 MAX_ROTATION = 80 ROTATION_SPEED = 10 ROTATION_DECAY = .4 # Reload constants RELOAD_DELAY = 10 RELOAD_RANDOM = .4 # Playing field constants GRAVITY_FORCE = .015 WALL_DAMPING = .6 WALL_FRICTION = .07 COLLECTION_WIDTH = 100 MAX_COLLECTION_VELOCITY = 100 # No strict constants SCREEN_WIDTH = 0 SCREEN_HEIGHT = 0 CIRCLE_BORDER = 0 CIRCLE_RADIUS = 0 CIRCLE_LEFT = 0 CIRCLE_RIGHT = 0 GRAVITY_DIST = 0 SCALE = 1 def data_path(f): return os.path.abspath(os.path.join('./data/', f)) class StopGame(Exception): pass def angleToDirection(angle): return [cos(angle),sin(angle)] def lineNormal(la,lb): "Returns normal vector to line" lx = (lb[0]-la[0])/100.0 #for numerical reasons we divide down first, then normalize ly = (lb[1]-la[1])/100.0 invnorml = 1.0/sqrt(lx*lx+ly*ly) #will blow up if two vertices are in same place! nx = ly*invnorml ny = -lx*invnorml return [nx,ny] def consumeBullet(bullet,player): "Consumes a bullet from the playing field, sending it either to player a or b's stash" bullet.kill() player.collectBall(bullet) def containBullet(bullet): global players if (bullet.y > CIRCLE_BORDER and bullet.y < SCREEN_HEIGHT-CIRCLE_BORDER and bullet.x > CIRCLE_BORDER+CIRCLE_RADIUS and bullet.x < SCREEN_WIDTH-CIRCLE_BORDER-CIRCLE_RADIUS): return xleft = (CIRCLE_BORDER+CIRCLE_RADIUS) dxleft = bullet.x - xleft xright = (SCREEN_WIDTH-CIRCLE_BORDER-CIRCLE_RADIUS) dxright = bullet.x - xright dyleft = bullet.y - (CIRCLE_BORDER+CIRCLE_RADIUS) dyright = dyleft dleftsqr = dxleft*dxleft+dyleft*dyleft drightsqr = dxright*dxright+dyright*dyright if dleftsqr < CIRCLE_RADIUS*CIRCLE_RADIUS or drightsqr < CIRCLE_RADIUS*CIRCLE_RADIUS: return if bullet.x < xleft: if (bullet.y > SCREEN_HEIGHT/2 - COLLECTION_WIDTH/2 and bullet.y < SCREEN_HEIGHT/2 + COLLECTION_WIDTH/2): vnorm = sqrt(bullet.vx*bullet.vx+bullet.vy*bullet.vy) if vnorm < MAX_COLLECTION_VELOCITY: consumeBullet(bullet,players[0]) dist = sqrt(dleftsqr) direction = [ dxleft/(dist), dyleft/(dist) ] bullet.x -= (dist+1-CIRCLE_RADIUS)*direction[0] bullet.y -= (dist+1-CIRCLE_RADIUS)*direction[1] bulldotdir = bullet.vx*direction[0]+bullet.vy*direction[1] bullet.vx += -(1.0+WALL_DAMPING)*bulldotdir*direction[0] bullet.vy += -(1.0+WALL_DAMPING)*bulldotdir*direction[1] bullet.vx *= 1.0-WALL_FRICTION bullet.vy *= 1.0-WALL_FRICTION elif bullet.x > xright: if (bullet.y > SCREEN_HEIGHT/2 - COLLECTION_WIDTH/2 and bullet.y < SCREEN_HEIGHT/2 + COLLECTION_WIDTH/2): vnorm = sqrt(bullet.vx*bullet.vx+bullet.vy*bullet.vy) if vnorm < MAX_COLLECTION_VELOCITY: consumeBullet(bullet,players[1]) dist = sqrt(drightsqr) direction = [dxright/(dist), dyright/(dist) ] bullet.x -= (dist+1-CIRCLE_RADIUS)*direction[0] bullet.y -= (dist+1-CIRCLE_RADIUS)*direction[1] bulldotdir = bullet.vx*direction[0]+bullet.vy*direction[1] bullet.vx += -(1.0+WALL_DAMPING)*bulldotdir*direction[0] bullet.vy += -(1.0+WALL_DAMPING)*bulldotdir*direction[1] bullet.vx *= 1.0-WALL_FRICTION bullet.vy *= 1.0-WALL_FRICTION elif bullet.y < CIRCLE_BORDER: bullet.y = CIRCLE_BORDER+1 bullet.vy = abs(bullet.vy)*WALL_DAMPING bullet.vx = (1.0-WALL_FRICTION)*bullet.vx else: bullet.y = SCREEN_HEIGHT - CIRCLE_BORDER-1 bullet.vy = -abs(bullet.vy)*WALL_DAMPING bullet.vx = (1.0-WALL_FRICTION)*bullet.vx def applyGravity(bullet): if bullet.x < GRAVITY_DIST: bullet.vx -= GRAVITY_FORCE elif bullet.x > SCREEN_WIDTH-GRAVITY_DIST: bullet.vx += GRAVITY_FORCE elif bullet.x < SCREEN_WIDTH/2.0: bullet.vx -= GRAVITY_FORCE/5.0 else: bullet.vx += GRAVITY_FORCE/5.0 def forceField(puck): #print CIRCLE_RADIUS if (puck[1] > CIRCLE_BORDER and puck[1] < SCREEN_HEIGHT-CIRCLE_BORDER and puck[0] > CIRCLE_BORDER+CIRCLE_RADIUS and puck[0] < SCREEN_WIDTH-CIRCLE_BORDER-CIRCLE_RADIUS): return [0.0,0.0] xleft = (CIRCLE_BORDER+CIRCLE_RADIUS) dxleft = puck[0] - xleft xright = (SCREEN_WIDTH-CIRCLE_BORDER-CIRCLE_RADIUS) dxright = puck[0] - xright dyleft = puck[1] - (CIRCLE_BORDER+CIRCLE_RADIUS) dyright = dyleft dleftsqr = dxleft*dxleft+dyleft*dyleft drightsqr = dxright*dxright+dyright*dyright if dleftsqr < CIRCLE_RADIUS*CIRCLE_RADIUS or drightsqr < CIRCLE_RADIUS*CIRCLE_RADIUS: return [0.0,0.0] #Now the puck is definitely outside of the arena. #There are three different cases to deal with, left section, middle section, and right section. dist = 0 direction = [0.0,0.0] if puck[0] < xleft: dist = sqrt(dleftsqr) - CIRCLE_RADIUS direction = [ dxleft/(dist+CIRCLE_RADIUS), dyleft/(dist+CIRCLE_RADIUS) ] elif puck[0] > xright: dist = sqrt(drightsqr) - CIRCLE_RADIUS direction = [dxright/(dist+CIRCLE_RADIUS), dyright/(dist+CIRCLE_RADIUS) ] elif puck[1] < CIRCLE_BORDER: dist = abs(CIRCLE_BORDER - puck[1]) direction = [0.0, -1.0] else: dist = abs(puck[1]-SCREEN_HEIGHT+CIRCLE_BORDER) direction = [0.0,1.0] factor = -.001*dist return [direction[0]*factor,direction[1]*factor] class Background(pygame.sprite.Sprite): group = pygame.sprite.Group() def __init__(self, w, h): super(Background, self).__init__(Background.group) self.image_tmp = pygame.image.load(data_path('bg.png')).convert() self.image = pygame.transform.scale(self.image_tmp, (w, h)) self.image_tmp = None self.rect = self.image.get_rect() class Intro(pygame.sprite.Sprite): def __init__(self,base,num_images,delay): pygame.sprite.Sprite.__init__(self) self.num_images = num_images self.delay = delay self.demos = [] # Array of demo images for i in range(num_images): img = pygame.image.load(data_path(base + str(i) + '.png')).convert_alpha() self.demos.append(img) self.image = self.demos[0] self.rect = self.image.get_rect() self.update(0) def update(self,delta): img_num = pygame.time.get_ticks()//(self.delay)%(self.num_images) img = self.demos[img_num] self.image = img self.rect = img.get_rect() self.rect.centery = SCREEN_HEIGHT / 2 self.rect.centerx = SCREEN_WIDTH / 2 class Overlay(pygame.sprite.Sprite): def __init__(self): super(Overlay, self).__init__() self.rect = pygame.Rect(0,0,SCREEN_WIDTH,SCREEN_HEIGHT) self.image = pygame.Surface(self.rect.size).convert_alpha() self.alpha = 150 self.image.fill((255,255,255,self.alpha)) self.dying = 0 def update(self,delta): self.image.fill((255,255,255,self.alpha)) if self.dying: self.alpha *= .9 if self.alpha < 10: self.kill() class Bullet(pygame.sprite.Sprite): group = pygame.sprite.Group() orig_image = 0 color = (255,255,255) speed = 0.2 radius = BULLET_RADIUS mass = BULLET_MASS def __init__(self, pos, direction): super(Bullet, self).__init__( Bullet.group) # self.rect = pygame.Rect(0, 0, 2*self.radius, 2*self.radius) self.image = pygame.transform.scale(Bullet.orig_image, (int(2*self.radius),int(2*self.radius))) self.rect = self.image.get_rect() # self.image = pygame.Surface(self.rect.size).convert_alpha() # self.image.fill((0,0,0,0)) # pygame.draw.circle(self.image, self.color,self.rect.center,int(self.radius)) # pygame.draw.circle(self.image,(150,150,150),self.rect.center,int(self.radius-2)) self.rect.center = pos self.direction = direction self.vx = direction[0]*self.speed self.vy = direction[1]*self.speed self.x = pos[0] self.y = pos[1] self.lastx = self.x self.lasty = self.y self.noUpdate = 0 def update(self, delta): if not self.noUpdate: #self.rect.move_ip(delta*self.speed*self.direction[0], delta*self.speed*self.direction[1]) self.lastx = self.x self.lasty = self.y self.x += delta*self.vx#speed*self.direction[0] self.y += delta*self.vy#speed*self.direction[1] self.constrain() applyGravity(self) self.rect.center = self.x,self.y def constrain(self): containBullet(self) def crossing(pa, pb, ra, rb): #returns point of contact if lines p and r cross, 0 otherwise x1 = pa[0] y1 = pa[1] x2 = pb[0] y2 = pb[1] x3 = ra[0] y3 = ra[1] x4 = rb[0] y4 = rb[1] ua = ( (x4-x3)*(y1-y3)-(y4-y3)*(x1-x3) ) ub = ( (x2-x1)*(y1-y3)-(y2-y1)*(x1-x3) ) denom = (y4-y3)*(x2-x1) - (x4-x3)*(y2-y1) if (abs(denom) < .0001): return 0 ua /= denom ub /= denom if 0 < ua and ua < 1 and 0 < ub and ub < 1: return [x1+ua*(x2-x1),y1+ua*(y2-y1)] else: return 0 #declare puck vertices counterclockwise (i think) #also, these don't auto-close, so bear that in mind class Puck(pygame.sprite.Sprite): group = pygame.sprite.Group() massPerVertex = 1.0 color = [50,255,50] edgeWidth = 4 bounceLoss = .3 bounceFriction = .4 fric = .02 rotFric = .02 puckTemplates = [] def __init__(self,pos, vertices,bearing_radius): super(Puck,self).__init__(Puck.group) self.vertices = vertices minX = vertices[0][0] maxX = minX minY = vertices[0][1] maxY = minY sumX = 0 sumY = 0 for vertex in self.vertices: sumX += vertex[0] sumY += vertex[1] if vertex[0] > maxX: maxX = vertex[0] elif vertex[0] < minX: minX = vertex[0] if vertex[1] > maxY: maxY = vertex[1] elif vertex[1] < minY: minY = vertex[1] sumX -= vertices[0][0] #since we have to manually close objects, remove dupe entry sumY -= vertices[0][1] self.cmx = sumX/float(len(vertices)-1) self.cmy = sumY/float(len(vertices)-1) self.vcmx = 0 self.vcmy = 0 self.moment = 0 self.width = maxX-minX self.height = maxY-minY for vertex in self.vertices: vertex[0] -= self.cmx vertex[1] -= self.cmy self.moment += Puck.massPerVertex*(vertex[0]*vertex[0]+vertex[1]*vertex[1]) self.moment *= .3 #eh, why not, makes things more spinny self.rect = pygame.Rect(-(maxX-minX),-(maxY-minY),2*(maxX-minX),2*(maxY-minY)) self.shape_rect = self.rect self.image = pygame.Surface((SCREEN_WIDTH,SCREEN_HEIGHT)).convert_alpha() self.bearing_radius = bearing_radius self.bearing_image = pygame.transform.scale(Bullet.orig_image,(int(2*self.bearing_radius),int(2*self.bearing_radius))).convert_alpha() self.theta = 0 #angle self.omega = 0 #angular velocity self.costheta = 1.0 self.sintheta = 0.0 self.mass = 6.0#self.massPerVertex*(len(self.vertices)-1) self.cmx = pos[0] self.cmy = pos[1] self.drawnvertices = self.vertices[:] self.expandEdges(BULLET_RADIUS*1.0) self.update(0) def update(self, delta): #print self.rect.center for obj in self.getVertices(): impulse = forceField(obj) self.applyImpulse(impulse[0]*delta*.5, impulse[1]*delta*.5, obj[0], obj[1]) self.cmx += delta*self.vcmx self.cmy += delta*self.vcmy self.theta += delta*self.omega self.costheta = cos(self.theta) self.sintheta = sin(self.theta) self.cosdtheta = cos(delta*self.omega) self.sindtheta = sin(delta*self.omega) self.omega *= 1.0-self.rotFric self.vcmx *= 1.0-self.fric self.vcmy *= 1.0-self.fric self.image.fill((0,0,0,0)) self.image.get_rect().topleft = (0,0) r = pygame.Rect(self.cmx-self.width,self.cmy-self.height,2*self.width,2*self.height) pygame.draw.polygon(self.image,self.color+[200],self.getDrawnVertices()[:-1],0) pygame.draw.polygon(self.image,(0,0,0,255),self.getDrawnVertices()[:-1],3) self.image.blit(self.bearing_image,(r.centerx-self.bearing_radius,r.centery-self.bearing_radius)) # pygame.draw.circle(self.image,self.color+[50],r.center,self.bearing_radius) # pygame.draw.circle(self.image,self.color+[255],r.center,self.bearing_radius,3) self.rect = self.image.get_rect() self.shape_rect = r #def getRect(self): # return pygame.Rect(self.cmx-self.width,self.cmy-self.height,2*self.width,2*self.height) #print self.rect def expandEdges(self,rad): #pushes out edges so that we can bypass "true" circle collision detection and still have it look pretty good verticesa = [] for i in range(len(self.drawnvertices)-1): normal = lineNormal(self.drawnvertices[i+1],self.drawnvertices[i]) normal[0] *= rad normal[1] *= rad verticesa.append( [self.drawnvertices[i ][0]+normal[0],self.drawnvertices[i ][1]+normal[1] ]) verticesa.append( verticesa[0] ) verticesb = [] normal = lineNormal(self.drawnvertices[0],self.drawnvertices[len(verticesa)-2]) normal[0] *= rad normal[1] *= rad verticesb.append( [self.drawnvertices[0][0]+normal[0],self.drawnvertices[0][1]+normal[1] ]) for i in range(1,len(self.drawnvertices)): normal = lineNormal(self.drawnvertices[i],self.drawnvertices[i-1]) normal[0] *= rad normal[1] *= rad verticesb.append( [self.drawnvertices[i ][0]+normal[0],self.drawnvertices[i ][1]+normal[1] ]) self.vertices = [] for i in range(len(verticesa)): self.vertices.append( verticesb[i] )#[ (verticesa[i][0]+verticesb[i][0])/2.0, (verticesa[i][1]+verticesb[i][1])/2.0 ] ) self.vertices.append( verticesa[i] ) def getVertices(self): vlist = [] for vertex in self.vertices: myX = self.cmx + self.costheta*vertex[0] + self.sintheta*vertex[1] myY = self.cmy - self.sintheta*vertex[0] + self.costheta*vertex[1] vlist.append( [myX,myY] ) return vlist def getDrawnVertices(self): vlist = [] for vertex in self.drawnvertices: myX = self.cmx + self.costheta*vertex[0] + self.sintheta*vertex[1] myY = self.cmy - self.sintheta*vertex[0] + self.costheta*vertex[1] vlist.append( [myX,myY] ) return vlist # myXdot = cmxdot + -thetadot sintheta vertex[0] + thetadot costheta vertex[1] def handleCollision(self, bullet, delta): if not 1:#(self.rect.collidepoint(bullet.x,bullet.y) or self.rect.collidepoint(bullet.lastx,bullet.lasty)): #print self.rect return 0 else: vlist = self.getVertices() for i in range(len(vlist)-1): #First off we need the normal vector to surface lx = (vlist[i+1][0]-vlist[i][0])/100.0 #for numerical reasons we divide down first, then normalize ly = (vlist[i+1][1]-vlist[i][1])/100.0 invnorml = 1.0/sqrt(lx*lx+ly*ly) #will blow up if two vertices are in same place! nx = ly*invnorml #maybe a sign error in these, check it out ny = -lx*invnorml #before the check, we've got to pre-alter the old velocity - note that we must fix this before we leave preX = bullet.x preY = bullet.y difftocmx = preX - self.cmx difftocmy = preY - self.cmy afterrotx = self.cmx + self.cosdtheta*difftocmx - self.sindtheta*difftocmy afterroty = self.cmy + self.sindtheta*difftocmx + self.cosdtheta*difftocmy bullet.x = afterrotx - delta*self.vcmx bullet.y = afterroty - delta*self.vcmy contact = crossing(vlist[i],vlist[i+1],[bullet.x+nx,bullet.y+ny],[bullet.lastx,bullet.lasty]) bullet.x = preX bullet.y = preY #print contact if contact: #Gotta resolve this nasty collision... #Find the distance from cm to contact point dzx = (contact[0] - self.cmx)/10.0 dzy = (contact[1] - self.cmy)/10.0 dz = 100*sqrt( dzx*dzx+dzy*dzy ) #Use this to find the velocity of that point on the rigid body vzx = self.vcmx+self.omega*(-self.sintheta*dzx+self.costheta*dzy) vzy = self.vcmy+self.omega*(-self.costheta*dzx-self.sintheta*dzy) #Now transform the bullet velocity into this frame dyx = (bullet.x-bullet.lastx)/delta dyy = (bullet.y-bullet.lasty)/delta dyrx = dyx-vzx #relative components of velocity dyry = dyy-vzy #print nx,ny,lx,ly,dyrx,dyry dyperp = dyrx*nx + dyry*ny if dyperp > 0: #we _actually_ have a collision, since the normal component is positive #Set new bullet location to collision point bullet.x = contact[0] bullet.y = contact[1] bullet.rect.center = bullet.x, bullet.y #Find velocity changes dynorm = sqrt(dyx*dyx+dyy*dyy)+.001 dyperpx = dyperp*nx dyperpy = dyperp*ny dyparx = dyx-dyperpx dypary = dyy-dyperpy deltayperpx = -(2.0-self.bounceLoss)*dyperpx deltayperpy = -(2.0-self.bounceLoss)*dyperpy deltayparx = -self.bounceFriction*dyparx deltaypary = -self.bounceFriction*dypary #pygame.draw.line(screen,(255,255,255),(contact[0],contact[1]),(contact[0]+deltayperpx,contact[1]+deltayperpy)) #Now apply these velocity changes bullet.vx += deltayperpx+deltayparx bullet.vy += deltayperpy+deltaypary #And apply the resulting impulse to the rigid body... self.applyImpulse(-(deltayperpx+deltayparx)*bullet.mass,-(deltayperpy+deltaypary)*bullet.mass,contact[0],contact[1]) #bullet.x += delta*bullet.vx #Eh, gotta get it out of there somehow, right? #bullet.y += delta*bullet.vy #If I wasn't too lazy I'd figure out how far to actually move it, but .1 of a frame is a decent hack for now #print contact[0],contact[1] def applyImpulse(self, impx, impy, locx, locy): self.vcmx += impx / self.mass self.vcmy += impy / self.mass rx = locx-self.cmx ry = locy-self.cmy torqueimp = impx*ry-impy*rx self.omega += torqueimp / self.moment #CLASS CHANGED BY ERIC 12:30 class Player(pygame.sprite.Sprite): star = 0 group = pygame.sprite.Group() def __init__(self,pos,rot,player_num=1): super(Player,self).__init__(Player.group) player_filenames = ['player0.png','player1.png'] self.orig_image = pygame.image.load(data_path(player_filenames[player_num])).convert_alpha() if not(SCALE == 1): w, h = self.orig_image.get_size() w = int(w / SCALE) h = int(h / SCALE) self.orig_image = pygame.transform.scale(self.orig_image, (w, h)) player_color = [255,0,0] # Rotation and positioning of rect. self.orig_rotate = rot # "baseline" CW rotation, in degrees self.position = pos self.image = pygame.transform.rotate(self.orig_image,-self.orig_rotate) self.rect = self.image.get_rect() self.rect.center = self.position # Player variables. self.theta = self.orig_rotate # *current* player rotation, in degrees CW self.dtheta = 0 self.bulletSpeed = BULLET_SPEED #self.ammo = AMMO_PER_PLAYER #added when bullets are populated self.ammoList = pygame.sprite.Group() self.storedBulletPositions = [] self.num_pucks = 0 self.trophyCase = 0 self.makeTrophyCase() pygame.draw.rect(self.trophyCase, (0,100,0,255), self.trophyCase.get_rect(),5) self.isHuman = 0 # Boolean keystate values. self.turnRight = 0 self.turnLeft = 0 self.shootButton = 0 self.reloaded = 1 self.reloadTimer = 0 #self.ammoList = [] #self.ammo = 0 #reset below... self.generateStoredBulletPositions() def makeTrophyCase(self): self.trophyCase = pygame.Surface((200,60)).convert_alpha() self.trophyPos = self.trophyCase.get_rect() self.trophyPos.centerx = self.rect.centerx self.trophyPos.top = 10 self.trophyCase.fill((0,0,0,0)) pygame.draw.rect(self.trophyCase, (0,100,0,255), self.trophyCase.get_rect(),5) def reset(self): self.theta = self.orig_rotate self.dtheta = 0 self.ammoList = pygame.sprite.Group() for i in range(AMMO_PER_PLAYER): bullet = Bullet( [0.0,0.0], [0.0,0.0] ) consumeBullet(bullet, self) #self.ammoList.add(bullet) def update(self,delta): self.theta = self.theta + self.dtheta self.constrain() self.image = pygame.transform.rotate(self.orig_image,-self.theta) self.rect = self.image.get_rect() self.rect.center = self.position if not (self.turnRight or self.turnLeft): self.dtheta = self.dtheta * ROTATION_DECAY for i in range(len(self.ammoList.sprites())): self.ammoList.sprites()[i].x = self.getStoredBulletPos(i)[0] self.ammoList.sprites()[i].y = self.getStoredBulletPos(i)[1] def generateStoredBulletPositions(self): for i in range(2*AMMO_PER_PLAYER): self.storedBulletPositions.append( self.genStoredBulletPos(i) ) def genStoredBulletPos(self,num): rad = CIRCLE_RADIUS+2*BULLET_RADIUS maxAngle = pi/2.0 ang = maxAngle*( (num)/(2.0*AMMO_PER_PLAYER) ) if num%2==0: ang += maxAngle/(2.0*AMMO_PER_PLAYER) ang *= -1 yC = SCREEN_HEIGHT/2.0 + rad*sin(ang) if self.rect.center[0] < SCREEN_WIDTH/2: xCenter = (CIRCLE_BORDER+CIRCLE_RADIUS) xC = xCenter - rad*cos(ang) else: xCenter = (SCREEN_WIDTH-CIRCLE_BORDER-CIRCLE_RADIUS) xC = xCenter + rad*cos(ang) return [xC,yC]#self.rect.center[0]+diff, SCREEN_HEIGHT*( (num+.5)/(2.0*AMMO_PER_PLAYER+1) )] def getStoredBulletPos(self, num): return self.storedBulletPositions[num] def constrain(self): if (self.theta - self.orig_rotate) >= MAX_ROTATION: self.theta = self.orig_rotate + MAX_ROTATION if (self.theta - self.orig_rotate) <= -MAX_ROTATION: self.theta = self.orig_rotate - MAX_ROTATION if self.dtheta > ROTATION_SPEED: self.dtheta = ROTATION_SPEED if self.dtheta < -ROTATION_SPEED: self.dtheta = -ROTATION_SPEED def collectBall(self,bullet): bullet.noUpdate = 1 self.ammoList.add(bullet) def hasAmmo(self): if len(self.ammoList.sprites()) > 0: returnbullet = self.ammoList.sprites()[0] returnbullet.kill() return returnbullet else: return 0 def genPucks(puckTemplates): myBuf = [] for i in range(6+1): leg = 15 if i%2==0: leg = 60 myBuf.append( [leg*sin(2*pi*i/6.0),leg*cos(2*pi*i/6.0)] ) puckTemplates.append(myBuf) myBuf = [] for i in range(8+1): leg = 15 if i%2==0: leg = 60 myBuf.append( [leg*sin(2*pi*i/8.0),leg*cos(2*pi*i/8.0)] ) puckTemplates.append(myBuf) myBuf = [] for i in range(9+1): leg = 45 if i%3==0: leg = 15 myBuf.append( [leg*sin(2*pi*i/9.0),leg*cos(2*pi*i/9.0)] ) puckTemplates.append(myBuf) myBuf = [] for i in range(5+1): leg = 60 myBuf.append( [leg*sin(2*pi*i/5.0),leg*cos(2*pi*i/5.0)] ) puckTemplates.append(myBuf) myBuf = [] for i in range(3+1): leg = 60 myBuf.append( [leg*sin(2*pi*i/3.0),leg*cos(2*pi*i/3.0)] ) puckTemplates.append(myBuf) GAME_WON = 0 def newGame(): global GAME_WON GAME_WON = 1 for player in Player.group: player.makeTrophyCase() player.num_pucks = 0 def sendPuckToPlayer(puck,player): dest = (player.num_pucks * 30 + 10,10) scaled = pygame.transform.scale(player.star,(40,40)) player.trophyCase.blit(scaled,dest) # Remove current puck from Puck.group Puck.group.remove(puck) player.num_pucks = player.num_pucks + 1 print player.num_pucks newRound() def newRound(): for bullet in Bullet.group: bullet.kill() for player in Player.group: player.reset() if player.num_pucks == PUCKS_TO_WIN: newGame() # Add a new puck to the game. whichPuck = int( len(Puck.puckTemplates)*random.random() ) Puck.group.add(Puck( pygame.display.get_surface().get_rect().center, Puck.puckTemplates[whichPuck],20 )) def applyAI(player): #pick between turn left, turn right, shoot, and nothing for puck in Puck.group: cosx = cos((player.theta - 90)/180*pi) sinx = sin((player.theta - 90)/180*pi) bulletDir = [player.bulletSpeed * cosx,player.bulletSpeed * sinx] slope = bulletDir[1]/bulletDir[0] desiredSlope = (puck.cmy-player.rect.center[1])/(puck.cmx-player.rect.center[0]) #print desiredAngle, ang #if player.rect.center[0]>SCREEN_WIDTH/2: desiredAngle -= pi if slope>desiredSlope+random.random(): player.turnLeft = 1 player.turnRight = 0 elif slope.9: player.shootButton = 1 else: player.shootButton = 0 players = [] class SprayPlay(): def __init__(self): pass def run(self): global SCREEN_WIDTH, SCREEN_HEIGHT, CIRCLE_BORDER, CIRCLE_RADIUS, CIRCLE_LEFT, CIRCLE_RIGHT, GRAVITY_DIST, SCALE # Initialization pygame.init() screen = pygame.display.get_surface() SCREEN_WIDTH = screen.get_width() SCREEN_HEIGHT = screen.get_height() background = Background(SCREEN_WIDTH, SCREEN_HEIGHT) # Playing field constants CIRCLE_BORDER = SCREEN_HEIGHT / 20 CIRCLE_RADIUS = int((SCREEN_HEIGHT-2*CIRCLE_BORDER)/2) CIRCLE_LEFT = [CIRCLE_BORDER + CIRCLE_RADIUS, CIRCLE_BORDER + CIRCLE_RADIUS] CIRCLE_RIGHT = [SCREEN_WIDTH - CIRCLE_BORDER - CIRCLE_RADIUS, CIRCLE_BORDER + CIRCLE_RADIUS] if SCREEN_WIDTH == 1200: SCALE = 1 GRAVITY_DIST = CIRCLE_BORDER + 175 CONST = 80 else: SCALE = 1200.0 / SCREEN_WIDTH GRAVITY_DIST = CIRCLE_BORDER + int(SCREEN_WIDTH * 175.0 / 1200.0) CONST = int(SCREEN_WIDTH * 80.0 / 1200.0) # Initial game state. overlay = Overlay() #intro = Intro() keyboard_intro = Intro('keyboard-',4,1000) intro_group = pygame.sprite.Group(overlay,keyboard_intro) clock = pygame.time.Clock() # Bullet image. Bullet.orig_image = pygame.image.load(data_path('ball.png')).convert_alpha() # Star image. Player.star = pygame.image.load(data_path('star.png')).convert_alpha() # Players. global players a = Player((CIRCLE_BORDER + CONST,SCREEN_HEIGHT//2),90,0) b = Player((SCREEN_WIDTH-CIRCLE_BORDER - CONST,SCREEN_HEIGHT//2),-90,1) players = [a,b] # Puck defs genPucks(Puck.puckTemplates) newRound() try: while True: delta = clock.tick(20) #delta = 1.0 / 25.0 #GTK events while gtk.events_pending(): gtk.main_iteration() for evt in pygame.event.get(): if evt.type == pygame.QUIT: raise StopGame elif evt.type == pygame.KEYDOWN: if not overlay.dying: overlay.dying = 1 keyboard_intro.kill() if evt.key == pygame.K_F5: newGame() if evt.key == pygame.K_ESCAPE: #raise StopGame newGame() if (evt.key == pygame.K_RIGHT) or (evt.key == pygame.K_KP9): b.turnRight = 1 b.isHuman = 1 if (evt.key == pygame.K_LEFT) or (evt.key == pygame.K_KP3): b.turnLeft = 1 b.isHuman = 1 if (evt.key == pygame.K_RETURN) or (evt.key == pygame.K_KP7): b.shootButton = 1 b.isHuman = 1 overlay.kill() if (evt.key == pygame.K_d) or (evt.key == pygame.K_KP2): a.turnRight = 1 a.isHuman = 1 if (evt.key == pygame.K_a) or (evt.key == pygame.K_KP8): a.turnLeft = 1 a.isHuman = 1 if (evt.key == pygame.K_SPACE) or (evt.key == pygame.K_KP6): a.shootButton = 1 a.isHuman = 1 elif evt.type == pygame.KEYUP: if (evt.key == pygame.K_RIGHT) or (evt.key == pygame.K_KP9): b.turnRight = 0 b.isHuman = 1 if (evt.key == pygame.K_LEFT) or (evt.key == pygame.K_KP3): b.turnLeft = 0 b.isHuman = 1 if (evt.key == pygame.K_RETURN) or (evt.key == pygame.K_KP7): b.shootButton = 0 b.isHuman = 1 if (evt.key == pygame.K_d) or (evt.key == pygame.K_KP2): a.turnRight = 0 a.isHuman = 1 if (evt.key == pygame.K_a) or (evt.key == pygame.K_KP8): a.turnLeft = 0 a.isHuman = 1 if (evt.key == pygame.K_SPACE) or (evt.key == pygame.K_KP6): a.shootButton = 0 a.isHuman = 1 elif evt.type == pygame.VIDEORESIZE: pygame.display.set_mode(event.size, pygame.RESIZABLE) if not a.isHuman: applyAI(a) if not b.isHuman: applyAI(b) # inject round-wining logic here. for puck in Puck.group: if puck.cmx > SCREEN_WIDTH - GRAVITY_DIST: # Player 0 gets it. sendPuckToPlayer(puck,players[0]) elif puck.cmx < GRAVITY_DIST: # Player 1 gets it. sendPuckToPlayer(puck,players[1]) # inject AI logic here. for player in players: if player.shootButton and player.reloaded: bull = player.hasAmmo() if bull: bull.kill() cosx = cos((player.theta - 90)/180*pi) sinx = sin((player.theta - 90)/180*pi) bulletDir = [player.bulletSpeed * cosx,player.bulletSpeed * sinx] x = player.rect.center[0] + cosx * PLAYER_ARM_LENGTH y = player.rect.center[1] + sinx * PLAYER_ARM_LENGTH Bullet( (x,y),bulletDir ) #player.ammo -= 1 player.reloaded = 0 player.reloadTimer = int((1.0 + RELOAD_RANDOM*2*(random.random()-.5))*RELOAD_DELAY) if not player.reloaded: player.reloadTimer -= 1 if player.reloadTimer == 0: player.reloaded = 1 if player.turnRight: player.dtheta = player.dtheta + ROTATION_SPEED / 15.0 if player.turnLeft: player.dtheta = player.dtheta - ROTATION_SPEED / 15.0 # Update phase Bullet.group.update(delta) Player.group.update(delta) Puck.group.update(delta) #ADDED BY ERIC 12:30 for player in players: player.ammoList.update(delta) for bullet in Bullet.group: for puck in Puck.group: puck.handleCollision(bullet,delta) # Draw phase Background.group.draw(screen) Bullet.group.draw(screen) Player.group.draw(screen) Puck.group.draw(screen) #ADDED BY ERIC 12:30 for player in players: player.ammoList.draw(screen) screen.blit(player.trophyCase,player.trophyPos) intro_group.update(delta) intro_group.draw(screen) pygame.display.flip() except StopGame: pass if __name__ == '__main__': g = SprayPlay() g.run()