# Balazar # Copyright (C) 2003-2005 Jean-Baptiste LAMY # # 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 2 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, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA import random, math import tofu, soya, soya.tofu4soya import balazar, balazar.sound, balazar.base as base, balazar.character as character from balazar.character import noop, FallingItem, NewItemState, ACTION_HURTED, ACTION_KILLED, ACTION_WALKED_ON, _P, _V UP = soya.Vector(None, 0.0, 1.0, 0.0) DOWN = soya.Vector(None, 0.0, -1.0, 0.0) ACTION_GROW_1 = 151 ACTION_GROW_2 = 152 ACTION_GROW_3 = 153 ACTION_GROW_4 = 154 ACTION_GROW_5 = 155 ACTION_GROW_6 = 156 class VegetalAction(tofu.Action): def __init__(self, action): tofu.Action.__init__(self) self.action = action def is_crucial(self): return 1 class VegetalState(soya.tofu4soya.CoordSystState): def __init__(self, mobile): soya.tofu4soya.CoordSystState.__init__(self, mobile) class SpcState(tofu.State): def __init__(self, mobile, spc): tofu.State.__init__(self) self.spc = spc class HurtState(SpcState): def __init__(self, mobile, spc, dir_x, dir_y, dir_z): SpcState.__init__(self, mobile, spc) self.dir_x = dir_x self.dir_y = dir_y self.dir_z = dir_z def is_crucial(self): return 1 class Vegetal(base.Entity): pass MUSHROOM_SHAPES = (soya.Shape.get("champignon@pos1"), soya.Shape.get("champignon@pos2"), soya.Shape.get("champignon@pos3")) MUSHROOM_CAL3D_SHAPE = soya.Cal3dShape.get("champignon") class Mushroom(soya.tofu4soya.Mobile, Vegetal, base.Strikeable, base.Photographiable): radius = 1.0 map_color = 198, 196, 128, 255 def __init__(self, state = -0.8, grow = 1.0, size = 1.0): soya.tofu4soya.Mobile.__init__(self) self.volume = soya.Volume(self, MUSHROOM_SHAPES[int(state)]) self.volume.name = "mushroom.volume" self.state = state self.grow = grow self.animating = 0 self.sub_mushrooms = [] self.parent_mushroom = None self.center = soya.Point(self, 0.0, 1.2, 0.0) self.bot = 1 self.scale(0.2176291357901485, 0.2176291357901485, 0.2176291357901485) self.scale(size, size, size) def kesako(self): return _("__kesako__champignon") def begin_round(self): soya.tofu4soya.Mobile.begin_round(self) if self.animating < 0: # Disappear self.animating -= 1 self.scale(0.9, 0.9, 0.9) if self.animating < -15: if not self.doer.remote: self.parent.remove_mobile(self) elif self.state < 0.0: # Appear self.state += 0.05 self.scale(1.1, 1.1, 1.1) def set_state(self, state): if isinstance(state, SpcState): getattr(self, "set_state_%s" % state.spc, noop)(state) elif isinstance(state, NewItemState): f = FallingItem(self.level, state.item, soya.Vector(None, 0.0, 0.3, 0.0)) f.move(soya.Point(self, 0.0, 2.0, 0.0)) def set_state_151(self, state): self.state = 1.01 self.animating = 2 self.perso = soya.Cal3dVolume(self, MUSHROOM_CAL3D_SHAPE) self.perso.animate_execute_action("pousse1", 0.0, 0.0) self.volume.visible = self.volume.solid = 0 def set_state_152(self, state): self.state = 2.01 self.animating = 2 self.perso = soya.Cal3dVolume(self, MUSHROOM_CAL3D_SHAPE) self.perso.animate_execute_action("pousse2", 0.0, 0.0) self.volume.visible = self.volume.solid = 0 def set_state_153(self, state): self.state = 3.01 self.animating = -1 if self.parent_mushroom: self.parent_mushroom.sub_mushrooms.remove(self) if not self.parent_mushroom.sub_mushrooms: self.parent_mushroom.grow = 1.0 balazar.sound.play("morfle1.wav", self, gain = 4.0) spore = Spore(self.parent) spore.move(self) def big_round(self): if self.state < 0.0: return if not self.level: return if self.animating > 0: # Playing animation self.animating -= 1 if self.animating == 0: self.remove(self.perso) self.volume.set_shape(MUSHROOM_SHAPES[int(self.state)]) self.volume.visible = self.volume.solid = 1 return if not self.doer.remote: old_state = self.state self.state += self.grow if old_state < 1.0 <= self.state: self.doer.action_done(SpcState(self, ACTION_GROW_1)) elif old_state < 2.0 <= self.state: self.doer.action_done(SpcState(self, ACTION_GROW_2)) elif old_state < 3.0 <= self.state: self.doer.action_done(SpcState(self, ACTION_GROW_3)) parent = self.parent if parent.nb_mushroom < self.level.nb_max_mushroom: self.solid = 0 context = parent.get_root().RaypickContext(self, 10.0) self.solid = 1 if self.scale_x > 2.0: for obj in self.parent: if isinstance(obj, GiantMushroom): break # Only ONE giant mushroom per level else: child = GiantMushroom() child.rotate_lateral(random.uniform(0.0, 360.0)) child.set_xyz( self.x + random.uniform(-5.0, 5.0), self.y + 10.0, self.z + random.uniform(-5.0, 5.0), ) parent.add(child) r = context.raypick(child, DOWN, -1.0, 1, 1) if r: r[0].convert_to(parent) child.y = r[0].y - 1.0 else: parent.remove(child) else: for i in range(random.randint(1, 3)): child = Mushroom(grow = random.uniform(0.01, 0.1), size = max(0.4, self.scale_x + random.uniform(-0.3, 0.3))) child.rotate_lateral(random.uniform(0.0, 360.0)) child.set_xyz( self.x + random.uniform(-5.0, 5.0), self.y + 5.0, self.z + random.uniform(-5.0, 5.0), ) parent.add(child) r = context.raypick(child, DOWN, -1.0, 1, 1) if r: if isinstance(r[0].parent.parent, character.Character): continue elif isinstance(r[0].parent.parent, TreePompon): continue elif isinstance(r[0].parent.parent, Mushroom): child.parent_mushroom = r[0].parent.parent child.parent_mushroom.grow = 0.0 child.parent_mushroom.sub_mushrooms.append(child) elif getattr(r[0].parent, "name", "") == "giant_mushroom.volume": continue r[0].convert_to(parent) child.y = r[0].y self.level.add_mobile(child) else: self.level.remove(child) def hurt(self, caster, damage): if isinstance(self.parent, base.EjectedBody): return 1 if isinstance(self.parent, base.HurtedBody ): return 0 if self.state >= 3.0: return if damage > -5.0: if isinstance(self.parent, base.EjectedBody): return if self.state >= 3.0: return for mushroom in self.sub_mushrooms[:]: mushroom.hurt(caster, damage) if not self.parent_mushroom: h = random.random() if h < 0.2: self.doer.action_done(NewItemState(balazar.item.MushroomFood())) elif h < 0.4: self.doer.action_done(NewItemState(balazar.item.OldMushroomFood())) _V.set_start_end(caster, self) _V.convert_to(self.parent) self.doer.action_done(HurtState(self, ACTION_KILLED, _V.x, _V.y, _V.z)) return 1 else: for mushroom in self.sub_mushrooms: mushroom.hurt(caster, damage) _V.set_start_end(caster, self) _V.convert_to(self.parent) self.doer.action_done(HurtState(self, ACTION_HURTED, _V.x, _V.y, _V.z)) return 0 def set_state_11(self, state): # ACTION_HURTED balazar.sound.play("morfle1.wav", self, gain = 4.0) Spore(self.parent, 10).move(self) base.HurtedBody(self.parent, self, soya.Vector(self.parent, state.dir_x, state.dir_y, state.dir_z)) def set_state_12(self, state): # ACTION_KILLED if self.parent_mushroom: self.parent_mushroom.sub_mushrooms.remove(self) if not self.parent_mushroom.sub_mushrooms: self.parent_mushroom.grow = 1.0 level = self.level if not self.doer.remote: self.level.remove_mobile(self) Spore(level, 16).move(self) base.EjectedBody(level, self, soya.Vector(level, state.dir_x, state.dir_y, state.dir_z)) balazar.sound.play("morfle1.wav", self, gain = 4.0) class GiantMushroom(soya.tofu4soya.Mobile, Vegetal, base.Photographiable): radius = 10.0 map_color = 198, 196, 128, 255 def __init__(self, state = -0.8): soya.tofu4soya.Mobile.__init__(self) if state < 1.0: self.volume = soya.Volume(self, soya.Shape.get("champignon_geant@pos1")) else: self.volume = soya.Volume(self, soya.Shape.get("champignon_geant@pos2")) self.volume.name = "giant_mushroom.volume" self.state = state self.scale(0.2176291357901485, 0.2176291357901485, 0.2176291357901485) self.animating = 0 self.bot = 1 def kesako(self): return _("__kesako__champignon_geant") def set_state(self, state): if isinstance(state, SpcState): getattr(self, "set_state_%s" % state.spc, noop)(state) def set_state_151(self, state): self.state = 1.01 self.animating = 35 self.perso = soya.Cal3dVolume(self, soya.Cal3dShape.get("champignon_geant")) self.perso.animate_execute_action("pousse1", 0.0, 0.0) self.volume.visible = self.volume.solid = 0 def begin_round(self): soya.tofu4soya.Mobile.begin_round(self) if self.state < 0.0: # Appear self.state += 0.05 self.scale(1.1, 1.1, 1.1) else: old_state = self.state if self.state < 3.0: if self.animating > 0: # Playing animation self.animating -= 1 if self.animating == 0: self.remove(self.perso) self.volume.set_shape(soya.Shape.get("champignon_geant@pos2")) self.volume.visible = self.volume.solid = 1 return if not self.doer.remote: self.state += 0.001 if old_state < 1.0 <= self.state: self.doer.action_done(SpcState(self, ACTION_GROW_1)) def advance_time(self, proportion): soya.tofu4soya.Mobile.advance_time(self, proportion) if (not self.animating) and (0.0 <= self.state < 3.0): self.scale_x = self.scale_y = self.scale_z = self.scale_x + proportion * 0.002 #import os, os.path, cPickle as pickle #SPORE_MATERIAL = pickle.load(open(os.path.join(soya.DATADIR, "fx.data"), "rb")) SPORE_MATERIAL = soya.PARTICLE_DEFAULT_MATERIAL class Spore(soya.Particles): def __init__(self, parent = None, nb_particles = 15): #soya.Particles.__init__(self, parent, particle._default() or SPORE_MATERIAL, nb_particles) soya.Particles.__init__(self, parent, SPORE_MATERIAL, nb_particles) self.auto_generate_particle = 1 self.removable = 1 self.set_colors((0.5, 0.95, 0.05, 0.5), (0.5, 0.95, 0.05, 1.0), (0.0, 0.7, 0.0, 0.2)) self.set_sizes((2.0, 2.0)) self.life = 25 def generate(self, index): angle = random.random() * 3.1417 sx = math.cos(angle) sy = math.sqrt(random.random() * 3.0) sz = math.sin(angle) l = (0.06 * (1.5 + random.random())) / math.sqrt(sx * sx + sy * sy + sz * sz) self.set_particle(index, 0.5 + random.random(), sx * l, sy * l, sz * l, 0.0, 0.01, 0.0) def begin_round(self): soya.Particles.begin_round(self) if self.life > 0: self.life -= 1 elif self.life == 0: self.auto_generate_particle = 0 self.life = -1 class PlantElement(object): pass FASME_SHAPE_NAMES = ["fasme@var1", "fasme@var2"] class Fasme(soya.tofu4soya.Mobile, Vegetal, base.Photographiable): map_color = 128, 196, 128, 255 radius = 5.0 def __init__(self, size = 8): soya.tofu4soya.Mobile.__init__(self) self.shape = soya.Shape.get(random.choice(FASME_SHAPE_NAMES)) self.oscille_angle = 0.0 self.oscille_vx = 1.0 self.oscille_vy = 0.0 self.oscille_vz = 0.0 self.size = size self.bot = 1 def placed(self): if self.size: previous = None child = Fasme(self.size - 1) child.solid = 0 _P.parent = self.parent for j in range(8): _P.set_xyz(self.x + random.uniform(-3.0, 3.0), 10000.0, self.z + random.uniform(-3.0, 3.0)) r = self.parent.raypick(_P, DOWN, -1.0, 1, 1) if r and r[0].parent is self: break else: return child.solid = 1 r[0].parent.add(child) child.move(r[0]) child.rotate_lateral(random.uniform(0.0, 360.0)) child.oscille_vx, child.oscille_vy, child.oscille_vz = child.parent.transform_vector(self.oscille_vx, self.oscille_vy, self.oscille_vz, self.parent) child.placed() def kesako(self): return _("__kesako__fasme") def begin_round(self): soya.tofu4soya.Mobile.begin_round(self) self.oscille_angle += 0.01 def advance_time(self, proportion): soya.tofu4soya.Mobile.advance_time(self, proportion) self.rotate_axe_xyz(proportion * 0.1 * math.cos(self.oscille_angle), self.oscille_vx, self.oscille_vy, self.oscille_vz) def set_state(self, state): soya.tofu4soya.Mobile.set_state(self, state) def big_round(self): if not self.doer.remote: self.doer.action_done(VegetalState(self)) # class TreePompon(soya.tofu4soya.Mobile, Vegetal, base.Strikeable, base.Photographiable, base.Terraformer): # radius = 5.5 # terraform_radius = 3.0 # map_color = 128, 64, 128, 255 # def __init__(self): # soya.tofu4soya.Mobile.__init__(self) # self.shape = soya.Shape.get("scn-tree5") # self.bot = 1 # def set_state(self, state): # if isinstance(state, SpcState): # getattr(self, "set_state_%s" % state.spc, noop)(state) # def hurt(self, caster, damage): # if isinstance(self.parent, base.HurtedBody ): return 0 # _V.set_start_end(caster, self) # _V.convert_to(self.parent) # self.doer.action_done(HurtState(self, ACTION_HURTED, _V.x, _V.y, _V.z)) # self.solid = 0 # return 0 # def big_round(self): # if self.solid == 0: self.solid = 1 # def set_state_11(self, state): # ACTION_HURTED # balazar.sound.play("morfle1.wav", self, gain = 4.0) # base.HurtedBody(self.parent, self, soya.Vector(self.parent, state.dir_x, state.dir_y, state.dir_z)) class TreePompon(soya.tofu4soya.Mobile, Vegetal, base.Strikeable, base.Photographiable, base.Terraformer, base.SensitiveFloor): radius = 5.0 terraform_radius = 4.0 map_color = 128, 64, 128, 255 def __init__(self): soya.tofu4soya.Mobile.__init__(self) self.shape = soya.Shape.get("scn-tree5") self.bot = 1 self.moving = 0 self.angle = 0.0 def set_state(self, state): if isinstance(state, SpcState): getattr(self, "set_state_%s" % state.spc, noop)(state) def character_on(self, character): if not self.moving: self.doer.action_done(SpcState(self, ACTION_WALKED_ON)) else: self.moving = 3 def hurt(self, caster, damage): if isinstance(self.parent, base.HurtedBody): return 0 _V.set_start_end(caster, self) _V.convert_to(self.parent) self.doer.action_done(HurtState(self, ACTION_HURTED, _V.x, _V.y, _V.z)) self.solid = 0 return 0 def big_round(self): if self.solid == 0: self.solid = 1 def set_state_11(self, state): # ACTION_HURTED balazar.sound.play("morfle1.wav", self, gain = 4.0) base.HurtedBody(self.parent, self, soya.Vector(self.parent, state.dir_x, state.dir_y, state.dir_z)) def begin_round(self): soya.tofu4soya.Mobile.begin_round(self) if self.moving: self.angle += 0.25 self.rotate_vertical(math.cos(self.angle) / (1 + (self.angle // 6.2832))) if not self.doer.remote: self.moving -= 1 def set_state_10(self, state): # ACTION_WAIT self.moving = 0 def set_state_14(self, state): # ACTION_WALKED_ON self.moving = 3 class TreasureTreePompon(TreePompon): treasure = [balazar.item.Knife, balazar.item.Knife, balazar.item.Knife, balazar.item.Knife, balazar.item.Axe, balazar.item.MushroomFood, balazar.item.OldMushroomFood, lambda : balazar.item.Map("foret_pompon")] def __init__(self): soya.tofu4soya.Mobile.__init__(self) self.shape = soya.Shape.get("scn-tree5") self.bot = 1 self.moving = 0 self.angle = 0.0 self.treasure_duration = 0 def set_state(self, state): if isinstance(state, NewItemState): f = FallingItem(self.level, state.item, soya.Vector(None, 0.0, -0.1, 0.0)) f.move(soya.Point(self, 0.0, 8.0, -6.0)) else: TreePompon.set_state(self, state) def big_round(self): if self.solid == 0: self.solid = 1 if self.treasure_duration: self.treasure_duration -= 1 def hurt(self, caster, damage): r = TreePompon.hurt(self, caster, damage) if self.treasure and (not self.treasure_duration): item = random.choice(self.treasure)() self.doer.action_done(NewItemState(item)) self.treasure_duration = 300 return r