# Balazar # Copyright (C) 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 import tofu, soya, soya.tofu4soya import balazar.sound, balazar.base as base from balazar.character import Character, _P, _V, ValueState, STATE_HEAL_LIFE, NewItemState, FallingItem try: set except: import sets set = sets.Set TRAP_ACTION_OPEN = 201 TRAP_ACTION_CLOSE = 202 class TrapState(tofu.State): def __init__(self, action): tofu.State.__init__(self) self.action = action class IndicateState(tofu.State): def __init__(self, x, z): tofu.State.__init__(self) self.x = x self.z = z class OpenableTrap(soya.tofu4soya.Mobile): open_sound = "mecanism.wav" close_sound = "" def __init__(self): soya.tofu4soya.Mobile.__init__(self) self.opened = 0 self.bot = 1 self.close_duration = 0 self.autoclose_duration = 0 def big_round(self): if not self.doer.remote: if self.opened and self.close_duration: self.close_duration -= 1 if self.close_duration == 0: self.set_opened(0) def set_opened(self, opened): if opened != self.opened: if opened: self.doer.action_done(TrapState(TRAP_ACTION_OPEN)) self.close_duration = self.autoclose_duration else: self.doer.action_done(TrapState(TRAP_ACTION_CLOSE)) def set_state(self, state): if state.action == TRAP_ACTION_OPEN: if not self.opened: if self.open_sound: balazar.sound.play(self.open_sound, self) self.opened = 1 elif state.action == TRAP_ACTION_CLOSE: if self.opened: if self.close_sound: balazar.sound.play(self.close_sound, self) self.opened = 0 class TrappedTile(OpenableTrap, base.SensitiveFloor, base.Terraformer): open_sound = "strike4.wav" terraform_radius = 3.0 closed_tile_y = 0.0 opened_tile_y = -0.5 speed_y = 0.02 def __init__(self, typ = 1): OpenableTrap.__init__(self) self._need_push = None self.tile = soya.Volume(self, soya.Shape.get("dalle1@grille%s" % typ)) self.characters = set() self.typ = typ self.autoclose_duration = 2 def set_typ(self, typ): self.tile.shape = soya.Shape.get("dalle1@grille%s" % typ) def character_on(self, character): if character.ground.parent is self.tile: self.characters.add(character) def do_action(self, action): if not self._need_push is None: self.set_pushed(self._need_push) self._need_push = None if self.characters: self.characters.clear() if not self.opened: self.set_opened(1) else: self.close_duration = self.autoclose_duration def advance_time(self, proportion): OpenableTrap.advance_time(self, proportion) if self.opened: if self.tile.y > self.opened_tile_y: self.tile.y -= proportion * self.speed_y if self.tile.y <= self.opened_tile_y: self.tile.y = self.opened_tile_y self._need_push = 1 else: if self.tile.y < self.closed_tile_y: self.tile.y += proportion * self.speed_y * 0.5 if self.tile.y >= self.closed_tile_y: self.tile.y = self.closed_tile_y self._need_push = 0 def set_pushed(self, pushed): pass class FallingVigie(TrappedTile, base.SensitiveFloor, base.Photographiable): open_sound = "" radius = 4.8 closed_tile_y = 0.0 opened_tile_y = -16.5 speed_y = 0.3 def __init__(self): TrappedTile.__init__(self, 0) self.tile.shape = soya.Shape.get("vigie") class ManualGridOpener(TrappedTile): def __init__(self, typ = 1): TrappedTile.__init__(self, typ) self.grids = [] # [(grid, on_push, on_unpush),...] def add_grid(self, grid, on_push = 1, on_unpush = None): self.grids.append((grid, on_push, on_unpush)) if hasattr(grid, "typ"): self.set_typ(grid.typ) else: self.set_typ(0) def set_pushed(self, pushed): for grid__on_push__on_unpush in self.grids: opened = grid__on_push__on_unpush[2 - pushed] if not opened is None: grid__on_push__on_unpush[0].set_opened(opened) class GridOpener(TrappedTile): def __init__(self, typ = 1, on_push = 1, on_unpush = None, autoclose_duration = 2): TrappedTile.__init__(self, typ) self.actions = (on_unpush, on_push) self.on_unpush = on_unpush self.autoclose_duration = autoclose_duration def set_pushed(self, pushed): action = self.actions[pushed] if not action is None: for grid in self.level.mobiles: if isinstance(grid, Grid) and grid.typ == self.typ: grid.set_opened(action) class LifeAltar(TrappedTile): open_sound = "strike4.wav" terraform_radius = 4.0 closed_tile_y = 1.0 opened_tile_y = 0.5 def __init__(self): TrappedTile.__init__(self, 0) self.shape = soya.Shape.get("altar1") self.autoclose_duration = 2 self.gloom = Gloom(self) self.gloom.y = 3.0 def set_pushed(self, pushed): self.gloom.add_life = pushed * 5.0 for character in self.characters: character.doer.action_done(ValueState(character, STATE_HEAL_LIFE, 1.0)) class Gloom(soya.Particles): def __init__(self, parent = None, nb_particles = 7): soya.Particles.__init__(self, parent, soya.Material.get("x_lumiere_1"), nb_particles) self.auto_generate_particle = 1 self.set_colors((0.3, 0.1, 0.2, 1.0), (0.8, 0.2, 0.3, 1.0), (0.8, 0.2, 0.3, 1.0), (0.6, 0.2, 0.3, 1.0), (0.0, 0.0, 0.0, 1.0)) self.set_colors((0.3, 0.1, 0.2, 1.0), (0.6, 0.2, 0.3, 1.0), (0.6, 0.2, 0.3, 1.0), (0.6, 0.2, 0.3, 1.0), (0.0, 0.0, 0.0, 1.0)) self.set_sizes((0.2, 0.2), (3.2, 3.2), (0.2, 0.2)) self.max_particles_per_round = 400 self.add_life = 0.0 self.lit = 0 def generate(self, index): sx = random.uniform(-0.005, 0.005) sy = random.uniform(-0.0 , 0.005) sz = random.uniform(-0.005, 0.005) self.set_particle(index, self.add_life + random.uniform(1.0 , 3.0), 10.0 * sx, 10.0 * sy, 10.0 * sz, sx, sy, sz) class Grid(OpenableTrap): def __init__(self, typ = 1, autoclose_duration = 0, size = "8_8"): OpenableTrap.__init__(self) self.shape = soya.Shape.get("grille_%s@grille%s" % (size, typ)) self.herse = soya.Volume(self, soya.Shape.get("herse_%s@grille%s" % (size, typ))) self.typ = typ self.autoclose_duration = autoclose_duration def _added_into_level(self, level): if level: level.camera_can_pass_through.append(self.herse) elif self.level: self.level.camera_can_pass_through.remove(self.herse) soya.tofu4soya.Mobile._added_into_level(self, level) def do_action(self, action): if (not self.opened) and (self.herse.y > 0.0): for mobile in self.level.mobiles: if isinstance(mobile, Character) and (not mobile.invincible): _P.clone(mobile) _P.convert_to(self.herse) if (-5.0 < _P.x < 5.0) and (-3.0 < _P.y < 0.0) and (-1.5 < _P.z < 1.5): mobile.hurt(0.3, self, can_resist = 0) def advance_time(self, proportion): OpenableTrap.advance_time(self, proportion) if self.opened: if self.herse.y < 8.0: self.herse.y += proportion * 0.05 if self.herse.y >= 8.0: self.herse.y = 8.0 else: if self.herse.y > 0.0: self.herse.y -= proportion * 0.5 if self.herse.y <= 0.0: balazar.sound.play("lock-1.wav", self) self.herse.y = 0.0 class NoMonsterGrid(Grid): """A grid that auto-open when there is no monster.""" def __init__(self): Grid.__init__(self, 3) def next_action(self): if self.level.nb_monster: if self.herse.y == 8.0: self.set_opened(0) else: self.set_opened(1) class MiamGrid(Grid): def __init__(self, typ = 0, size = "8_8"): Grid.__init__(self, typ, 0, size) self.active = 0 self.opened = 1 self.herse.y = 8.0 def big_round(self): if not self.doer.remote: if (self.active <= 0) and (self.herse.y == 0.0): self.set_opened(1) if self.active <= 0: for mobile in self.level.mobiles: if isinstance(mobile, Character) and mobile.distance_to(self) < 5.5: self.active = 1 break if self.herse.y == 0.0: self.active -= 1 def next_action(self): if self.active > 0: if self.opened: if self.herse.y == 8.0: self.set_opened(0) def set_state(self, state): if state.action == TRAP_ACTION_OPEN: if not self.opened: balazar.sound.play("mecanism.wav", self, gain = 0.5) self.opened = 1 elif state.action == TRAP_ACTION_CLOSE: if self.opened: self.opened = 0 def advance_time(self, proportion): soya.World.advance_time(self, proportion) if self.opened: if self.herse.y < 8.0: self.herse.y += proportion * 0.3 if self.herse.y > 8.0: self.herse.y = 8.0 else: if self.herse.y > 0.0: self.herse.y -= proportion * 0.5 if self.herse.y < 0.0: balazar.sound.play("lock-1.wav", self, gain = 0.5) self.herse.y = 0.0 class Chest(OpenableTrap, base.Strikeable, base.Terraformer): radius = 3.0 terraform_radius = 3.0 open_sound = "porte-2.wav" treasure = None monsters = [] def __init__(self, typ = 1, treasure = None, autoclose_duration = 200): OpenableTrap.__init__(self) self.shape = soya.Shape.get("coffre1@typ%s" % typ) self.couvercle = soya.Volume(self, soya.Shape.get("coffre_couvercle1@typ%s" % typ)) self.couvercle.set_xyz(0.0, 2.0, 1.0) self.typ = typ self.opening = 0.0 self.autoclose_duration = autoclose_duration if typ == 1: self.scale(0.6, 0.6, 0.6) if not treasure is None: self.treasure = treasure def hurt(self, caster, damage): if not self.opening: self.set_opened(1) def set_opened(self, opened): if opened and (not self.opened) and self.monsters: Monster = random.choice(self.monsters) if Monster: m = Monster() m.move(self) self.level.add_mobile(m) OpenableTrap.set_opened(self, opened) def big_round(self): if not self.doer.remote: if self.opened and (not self.opening): if (self.close_duration == self.autoclose_duration): if self.treasure != 0: item = random.choice(self.treasure or ((self.typ == 2) and balazar.item.BIG_TREASURE) or balazar.item.SMALL_TREASURE) if item is balazar.item.Map: item = item(self.level.get_country().name) else: item = item() self.doer.action_done(NewItemState(item)) self.close_duration -= 1 if self.close_duration == 0: self.set_opened(0) def set_state(self, state): if isinstance(state, NewItemState): speed = soya.Vector(self, 0.0, 0.2, -0.1) speed.convert_to(self.level) speed.set_length(0.2236) f = FallingItem(self.level, state.item, speed) f.move(soya.Point(self, 0.0, 2.0, 0.0)) elif state.action == TRAP_ACTION_OPEN: if not self.opened: if self.open_sound: balazar.sound.play(self.open_sound, self) self.opened = 1 self.opening = 120 elif state.action == TRAP_ACTION_CLOSE: if self.opened: if self.close_sound: balazar.sound.play(self.close_sound, self) self.opened = 0 self.opening = -120 def advance_time(self, proportion): soya.World.advance_time(self, proportion) if self.opening: if self.opening > 0.0: self.couvercle.rotate_vertical( 3.0 * proportion) self.opening -= 3.0 * proportion if self.opening < 0.0: self.opening = 0.0 elif self.opening < 0.0: self.couvercle.rotate_vertical(-3.0 * proportion) self.opening += 3.0 * proportion if self.opening > 0.0: self.opening = 0.0 class Mill(OpenableTrap, base.Terraformer): radius = 8.0 terraform_radius = 8.0 def __init__(self, parent): OpenableTrap.__init__(self) self.shape = soya.Shape.get("pont_monte1") def advance_time(self, proportion): soya.World.advance_time(self, proportion) if self.opened: self.rotate_lateral(proportion * -1.0) class RotatingBridgeTile(OpenableTrap, base.SensitiveFloor): open_sound = "delectation.wav" def __init__(self): OpenableTrap.__init__(self) self.shape = soya.Shape.get("pont_trocon1") self.angle = 0.0 def set_state(self, state): if state.action == TRAP_ACTION_OPEN: if not self.opened: self.angle = 180.0 OpenableTrap.set_state(self, state) def advance_time(self, proportion): soya.World.advance_time(self, proportion) if self.angle: if self.angle > proportion * 5.0: self.turn_vertical(proportion * 5.0) self.angle -= proportion * 5.0 else: self.turn_vertical(self.angle) self.angle = 0.0 self.set_opened(0) def character_on(self, character): if not self.opened: if random.random() < 0.08: self.set_opened(1) class IndicatorPlatForm(soya.tofu4soya.Mobile, base.Terraformer): radius = 8.0 terraform_radius = 4.0 def __init__(self, parent): soya.tofu4soya.Mobile.__init__(self) self.bot = 1 self.shape = soya.Shape.get("arrow_platform") self.vector = None self.vector0 = soya.Vector(self, 0.0, 0.0, -1.0) def rotate_toward(self, x, z): self.doer.action_done(IndicateState(x, z)) def set_state(self, state): self.vector = soya.Vector(self.parent, state.x, 0.0, state.z) def advance_time(self, proportion): soya.World.advance_time(self, proportion) if self.vector: self.rotate_lateral(proportion * -8.0) if self.vector.distance_to(self.vector0) < 0.05: self.vector = None