import time, random import soya import soya.sphere import soya.cube from soya import World, Vertex, Face, Vector, Point from soya.sphere import Sphere import soya.sdlconst SC = soya.sdlconst UP = Point(None, 0, 10, 0) class Bird(World): """ A bird. Flies, and wants to eat often. Might be called HungryBird, until gets food (test returns true then - perhaps gets to Breeding). Need to eat can of course be made via the EnergyBeing mechanics, which might be interesting w.r.t to flying (flight economics, gee?) but that is not the point here now. Just defining the want should be ok. "here now" being: a simple game around the (good) old SoyaPlant, where plant to add apples, which these birds will eat. Have been thinking of these as a swarm too, but can start as individuals too (there is no difference really, in a way, as we know ;) """ SPEED = 0.01 def __init__(self, scene, pos=(0, 0, 0)): """soya stuff for the model""" soya.World.__init__(self, scene) try: self.set_xyz(*pos) except TypeError: self.move(pos) head = Sphere(self) #, insert_into=self) head.set_xyz(0.8, 0.6, 0) head.scale(0.1, 0.1, 0.1) self.head = head """ neckgeom = { (0, 0) : [head.x, head.y, head.z], (0, 1) : [head.x, head.y, head.z], (0, 2) : [head.x, head.y, head.z], (0, 3) : [head.x, head.y, head.z], (0, 0) : [head.x, head.y, head.z], (0, 1) : [head.x, head.y, head.z], (0, 2) : [head.x, head.y, head.z], (0, 3) : [head.x, head.y, head.z], (0, 0) : [head.x, head.y, head.z], (0, 1) : [head.x, head.y, head.z], (0, 2) : [head.x, head.y, head.z], (0, 3) : [head.x, head.y, head.z], (0, 0) : [head.x, head.y, head.z], (0, 1) : [head.x, head.y, head.z], (0, 2) : [head.x, head.y, head.z], (0, 3) : [head.x, head.y, head.z], (0, 0) : [head.x, head.y, head.z], (0, 1) : [head.x, head.y, head.z], (0, 2) : [head.x, head.y, head.z], (0, 3) : [head.x, head.y, head.z], (0, 0) : [head.x, head.y, head.z], (0, 1) : [head.x, head.y, head.z], (0, 2) : [head.x, head.y, head.z], (0, 3) : [head.x, head.y, head.z] } neck = soya.cube(self) for i, side in enumerate(self.neck): for j, v in enumerate(side): v.set_xyz(*neckgeom[i, j]) """ self.neck = Face(self, [ Vertex(self, head.x, head.y, head.z), Vertex(self, 0.5, 0, 0) ]) self.wing1 = Face(self, [ Vertex(self, -0.5, 0, 0, diffuse = (1.0, 0.0, 0.0, 1.0)), Vertex(self, 0.5, 0, 0, diffuse = (0.0, 1.0, 0.0, 1.0)), Vertex(self, 0.0, 0.7, 0.5, diffuse = (0.0, 0.0, 1.0, 1.0)) ]) self.wing1.double_sided = True self.tip1 = self.wing1.vertices[-1] self.wing2 = Face(self, [ Vertex(self, -0.5, 0, 0, diffuse = (1.0, 0.0, 0.0, 1.0)), Vertex(self, 0.5, 0, 0, diffuse = (0.0, 1.0, 0.0, 1.0)), Vertex(self, 0.0, 0.7, -0.5, diffuse = (0.0, 0.0, 1.0, 1.0)) ]) self.wing2.double_sided = True #self.wing2.visible = False self.tip2 = self.wing2.vertices[-1] self.tail = Face(self, [ Vertex(self, -0.4, 0.0, 0.1), Vertex(self, -0.9, 0.3, 0.3), Vertex(self, -0.9, 0.3, -0.3), Vertex(self, -0.4, 0.0, -0.1) ]) self.tail.double_sided = True self.vel = Vector(None, 0, 0, 0) self.target = None self.food = [] #self.had_food = None #for test() self.was_at = pos #also for test() to monitor approaching success #should there be a state class, which could have prev state, #and a method to evaluate goodness? sounds like an interesting idea :o #self.prev_time = time.time() self.wdir = -1 #down def grow(self): self.head.scale(1.5, 1.5, 1.5) print self, "grew (it's head)" def begin_round(self): if self.target is None: if self.parent.food: self.target = random.choice(self.parent.food) print "Bird got to know there is food, goes to get some at", self.target, self else: if self.target in self.parent.food: t = self.target else: """oh no my food has disappeared!""" self.target = None if self.target is not None: if self.set_towards_target(): self.get(self.target) self.target = None self.vel /= 2 self.test() def set_towards_target(self): t = self.target #self.look_at_y(UP) self.look_at_x(t) d = self >> t if d.length() < 0.03: return True #self.parent.raypick(self, else: self.vel = d self.vel.set_length(Bird.SPEED) return False #not there yet def get(self, food): self.food.append(self.parent.give(food)) def advance_time(self, p): #self.rotate_lateral(1.0 * t) #print ".", """movement""" self.add_vector(self.vel * p) """wing flapping. not w.r.t. movement now""" v1 = self.tip1 #phase = v1.y += self.wdir * 0.06 * p if v1.y < -0.6: self.wdir = 1 elif v1.y > 0.85: self.wdir = -1 self.tip2.y = v1.y def test(self): """ trying to keep up with testing .. but as testing this requires environmental conds too might do it better from outside (normal / modshooter style) well try here too, doesn't hurt if is in both even :o """ #assert self.want(Food) - no need to code really, that's what this is #if self.had_food is None: # self.had_food = self.food if self.target is None: if self.parent.food: print "the world has food but this bird no target, what's wrong?", self.parent.food elif not self.food: #print "Has no food yet - going towards", self.target, self pass #else: #is this right - or should be 'if self.food'? if self.food: print "Bird got food succesfully!", self #target might go to None if these were to be not greedy? return True #success! return False #not (yet) class GrowingBird(Bird): """ a bird that eats all food that in can get, immediately, and grows as the result of that. possible idea for making something out of this is that different food would grow different parts: head, wings, tail and that might have different effects (vision/mem, speed/carriage pow, turnrate?) """ def begin_round(self): Bird.begin_round(self) if self.food: e = self.food.pop(0) self.grow() class BreedingBird(Bird): """ How birds breed. Here a simplistic naive thing in the appletree / food theme, now breeds as soon as can get food (and get landed) - and then becomes a mother. Motherhood and other basics w.r.t to food were already done in food.py - see e.g. food.test_mother(). Does that really get to use here? Let's see. """ def __init__(self, scene, pos=(0, 0, 0), maturity=1): Bird.__init__(self, scene, pos) self.offspring = [] #note: .children in abstract human.py but here that collided with soya3d scenegraph .children property XXX #self.nestpos = None #now random for each chick separately, dunno which way is more fun but trying that now self.maturity = maturity #0 babychild, 1 adult. chicks are given 0 from breed self.fertile = True """ these were previously implicitly autofertile -- now that FoodaffectedBird below needs to get fertile (from such food) being fertile is a variable, which is always true here but not elsewhere """ def get_maturity(self): return self._maturity def set_maturity(self, degree): s = 0.5 + (degree * 0.5) self.set_scale_factors(s, s, s) self._maturity = degree maturity = property(get_maturity, set_maturity, None) def begin_round(self): if self.maturity > 0: if not self.food: Bird.begin_round(self) #HungryBird else: """is an adult and has food""" print ".", if self.offspring: hungry = [o for o in self.offspring if not o.food] hungry_babys = [b for b in hungry if b.maturity == 0] if hungry_babys: print "|", h = hungry_babys[0] if self.target is not h: self.target = h print "BreedingBird: going to feed a baby", self.target if self.set_towards_target(): h.food.append(self.food.pop()) print ".. baby bird fed" #mature ones can do well on their own for now else: #print "no hungry babys in", self.offspring #pass self.offspring = [] #pretty extreme: just forget grown children elif self.fertile: #XXX should be able to do that even when has offspring """can go to breed""" print "+", if self.target is None: print "_", self.target = Point(None, (random.random() - 0.5) * 3, 0, 0) #look_at does not work for Vector if self.set_towards_target(): chick = self.breed() self.target = None else: """ has food but is not fertile - never happens here (is autofertile) but subclasses can act in this case (by noticing there is food) .. let's also report from here that they have that possibility to act """ return True else: #Chick.begin_round(self) if self.food: #self.grow() self.maturity += 1 self.scale(0.3, 0.3, 0.3) #hack: tree_birds scales birds by 0.3 self.food.pop() print "Chick had food and grew up", self, self.food else: pass #print " \/ ", #nokka auki :) def breed(self): """now can do alone - for simplicity sake now(?)""" if self.food: print self, "breeding .. :o", o = self.__class__(self.parent, self.position(), maturity=0) o.scale(*[d / (2) for d in self.get_dimension()]) self.offspring.append(o) self.food.pop() print " -- food after it:", "and offspring:", self.offspring return o else: return False #could not breed #def grow(self): # """changes to a BreedingBird .. so how to do this?""" # self.maturity += 1 def test(self): #NOTE: (Hungry)Bird.test(self) would return true if has gotten food(?) #print Bird.test(self) if self.food: if not self.offspring: #print "Should be making it's way to breed, has food but no children yet.." pass else: for o in self.offspring: if not o.food: print "BreedingBird has a hungry child (offspring), but has already food - should get it there soon!" else: Bird.test(self) """just the BreedingBird.maturity variable now, had some prob in using this""" #~ class Chick(BreedingBird): #~ """a young bird - starting from birth, can grow""" #~ def begin_round(self): #~ if self.food: #~ self.grow() #~ self.food.pop() class FoodaffectedBird(BreedingBird): """ Eats food, using the code from basic bird, but is affected by it (see effectfood.py) so that can either grow or get fertilezed/impregnated as a result. Breeds when has gotten pregnant, so is a BreedingBird, and works using that code w.r.t to feeding offspring etc. """ def __init__(self, *args, **kwargs): BreedingBird.__init__(self, *args, **kwargs) self.fertile = False def begin_round(self): if BreedingBird.begin_round(self): """has surplus food now - can eat it""" e = self.food.pop(0) #FIFO e.affect(self) def fertilize(self): if not self.fertile: self.fertile = True print self, "got fertilized" else: print "NOTE:", self, "was already fertilized" class OneBird(soya.World): def __init__(self): #should get plant etc. (current globals) soya.World.__init__(self) soya.set_grab_input(1) self.food = [] self.bird = BreedingBird(self) #self.bird = GrowingBird(self) """controls""" self.keybindings = { SC.K_ESCAPE: self.quit, SC.K_LEFT: lambda: self.bird.turn_lateral(-10), SC.K_RIGHT: lambda: self.bird.turn_lateral(10), SC.K_UP: self.bird.grow, SC.K_DOWN: self.test_breed } """lights, cam - action""" light = soya.Light(self) light.set_xyz(1.0, 0.7, 1.0) light2 = soya.Light(self) light2.set_xyz(-1.0, 0.9, 0.7) light3 = soya.Light(self) light3.set_xyz(0.3, 0.8, -0.7) camera = soya.Camera(self) camera.set_xyz(0.0, 1.7, 4) #camera.look_at(soya.Vector(self, 0.0, 1, 0)) soya.set_root_widget(camera) self.idler = soya.Idler(self) self.idler.idle() def test_breed(self): mom = self.bird mom.food.append(1) chick = mom.breed() mom.add_vector(Vector(self, 0, 1, 0)) def begin_round(self): soya.World.begin_round(self) #is there anything? for event in soya.process_event(): if event[0] == soya.sdlconst.KEYDOWN: try: cmd = self.keybindings[event[1]]() except KeyError: print "Unknown key", event[1] else: pass #~ elif event[0] == soya.sdlconst.MOUSEMOTION: #~ rel_x = event[3] #~ rel_y = event[4] #~ """spin the bird with x movement""" #~ #soya.set_mouse_pos(300, 300) #window middle would be nice #~ #soya.process_event() #resets rel mouse #~ self.bird.turn_lateral(rel_x) def quit(self): self.idler.stop() if __name__ == '__main__': soya.init(width = 700, height = 700) scene = OneBird()