# This is full of destruction stuff import bge import aud import random import numpy import math import mathutils import types from Scripts import Reuse from Scripts import Opt from Scripts import Multiplayer_Shared from Scripts.Common import * scene = bge.logic.getCurrentScene() cam = scene.active_camera if "Destruction" not in bge.logic.globalDict: bge.logic.globalDict["Destruction"] = {} database = bge.logic.globalDict["Destruction"] # Particle system def particles(object, # Particle object point, # Point where to spawn them despawn, # Time ( in frames before disappearing ) amount=5, # Amount of them to spawn max=50, # Max amount of them in the scene spread=0, # Spread of particles on start normal=0.05, # Spread of particles over time velocity=[0,0,0], # Velocity of particles scale = 1 # Scale of the particle object ): # Performance kill switch if not Opt.GoodFPS("particles", 0.5): return # Not do the effect if not in view if cam.getDistanceTo(point) > 100 or not cam.pointInsideFrustum(point) == cam.INSIDE: return # We will need a place to register particles if "particles-registry" not in bge.logic.globalDict: bge.logic.globalDict["particles-registry"] = {} registry = bge.logic.globalDict["particles-registry"] # Making sure that registry contains particle type if object not in registry: registry[object] = [] for i in range(amount): # If we reached max, we abort inscene = len( registry[object] ) inreuse = len( Reuse.reuse.get(object, []) ) spawned = inscene - inreuse if spawned > max: return # Making particle particle = Reuse.Create(object, despawn) particle.scaling = [scale, scale, scale] # Recording into registry if particle not in registry[object]: registry[object].append(particle) # Puting it into position ( adding the spread ) particle.position = point + mathutils.Vector([random.uniform(-spread, spread), random.uniform(-spread, spread), random.uniform(-spread, spread)]) # The next part will require an actuator # Introducting random spread over time particle.actuators["Motion"].dLoc = mathutils.Vector([random.uniform(-normal, normal), random.uniform(-normal, normal), random.uniform(-normal, normal)]) # Adding the velocity particle.actuators["Motion"].dLoc += mathutils.Vector(velocity) def Smoke(point, strength=0.5, size=1.0): # Not do the effect if not in view if cam.getDistanceTo(point) > 100 or not cam.pointInsideFrustum(point) == cam.INSIDE: return if numpy.random.choice([True, False], p=[strength, 1-strength]): # Performance kill switch def do(point): #smokeParticle = Reuse.Create("Smoke", random.randint(30, 60)) #smokeParticle.scaling = [size, size, size] #smokeParticle.position = point # Adding some sparks to the fire particles("SmokeBurst", point, despawn = 10, amount = random.randint(0, 3), max = 50, spread = 0.5, normal = 0.01, velocity = [0,0,0.1], scale=random.uniform(0.2,1.0)) if Opt.GoodFPS("Smoke", strength): do(point) def Dust(point, strength=0.5): # Not do the effect if not in view if cam.getDistanceTo(point) > 100 or not cam.pointInsideFrustum(point) == cam.INSIDE: return if numpy.random.choice([True, False], p=[strength, 1-strength]): # Performance kill switch def do(point): smokeParticle = Reuse.Create("Dust", random.randint(30, 60)) smokeParticle.position = point if Opt.GoodFPS("Dust", 0.85): do(point) else: pass def FireElementTextureLogic(obj): if type(obj) == bge.types.SCA_PythonController: obj = obj.owner vect = obj.getVectTo(cam) obj.alignAxisToVect((0,0,1), 2, 1) obj.alignAxisToVect(vect[1], 1, 1) obj.blenderObject["fire"] -= 0.1 try: v = obj.parent.worldLinearVelocity.copy() except: try: v = obj.parent.parent.worldLinearVelocity.copy() except:return v = -v + mathutils.Vector((0,0,30)) obj.alignAxisToVect(v, 2, 1) def AttatchFireBall(point, attachment=None, size=1, duration=0): # This function will attach a fireball to a point. # Making the FireBall FireBall = Reuse.Create("FireBall", duration) # Attatching the Fireball FireBall.position = point.copy() FireBall.scaling = [size, size, size] if attachment: FireBall.setParent(attachment, True) # Attatching a light to it. if not duration: if "Light" not in FireBall: light = Reuse.Create("FireLamp") light.position = FireBall.position light.setParent(FireBall) FireBall["Light"] = light light = FireBall["Light"] light.blenderObject.data.use_shadow = False # In case the fireball is farther than 50 meters # from the camera, we execute this ones. FireElementTextureLogic(FireBall) # Storring it and returning #ID = Multiplayer_Shared.RandomString() #database[ID] = FireBall #FireBall["ID"] = ID return FireBall def DeleteFireBall(FireBall): # To save space we gonna do that. if "Light" in FireBall: light = FireBall["Light"] light.removeParent() Reuse.Delete(light) Reuse.Delete(FireBall) FireBall.removeParent() def FireBurst(point, size): # This will make a short burst of fire. FireBurst = Reuse.Create("FireBurst", 10) FireBurst.position = point.copy() FireBurst.scaling = [size, size, size] FireBurst.blenderObject["fire"] = 1 def SpreadOnGround(object, point, size, spread, amount, duration=0, randomDuration=True): # This works similarly to particles, but # instead puts the objects onto the ground. for i in range(amount): if randomDuration: d = random.uniform( duration / 2 , duration ) else: d = duration obj = Reuse.Create(object, d) obj.position = mathutils.Vector(point) + mathutils.Vector([random.uniform(-spread, spread), random.uniform(-spread, spread), random.uniform(-spread, spread)]) obj.scaling = [size, size, size] # Puttin the object on the flor obj.position.z += spread + 1 topos = obj.position.copy() topos.z -= 200 ray = obj.rayCast(topos) if ray[1]: obj.position = ray[1] def Fire(point, strength=0.5): # Not do the effect if not in view if cam.getDistanceTo(point) > 100 or not cam.pointInsideFrustum(point) == cam.INSIDE: return if numpy.random.choice([True, False], p=[strength, 1-strength]): # Performance kill switch def do(point): #fireParticle = Reuse.Create("Fire", random.randint(30, 60)) #fireParticle.position = point #FireBurst(point, 0.5) # Adding some sparks to the fire particles("FireBurst", point, despawn = 10, amount = random.randint(0, 3), max = 50, spread = 0.5, normal = 0.01, velocity = [0,0,0.1], scale=random.uniform(0.2,1.0)) if Opt.GoodFPS("Fire", strength): do(point) def WaterSplash(point, strength=0.5, rot=0, force=1): if numpy.random.choice([True, False], p=[strength, 1-strength]): waterSplash = Reuse.Create("WaterSplashCar", random.randint(10, 20)) waterSplash.position = point waterSplash.orientation = [0,0,rot] waterSplash.scaling = [ random.uniform(0.6, 1.4), random.uniform(0.6, 1.4), random.uniform(0.6*force, 1.4*force) ] # Sound device = bge.logic.globalDict["SoundDevice"] s = "//sfx/splash.ogg" if s not in bge.logic.globalDict["sounds"]: bge.logic.globalDict["sounds"][s] = {"sound":aud.Sound(bge.logic.expandPath(s)), "play":None} sound = bge.logic.globalDict["sounds"][s] sound["play"] = device.play(sound["sound"]) sound["play"].location = point sound["play"].relative = False sound["play"].distance_maximum = 100 sound["play"].distance_reference = 1 sound["play"].attenuation = 1 sound["play"].pitch = random.uniform(0.6, 1.4) sound["play"].volume = 5 * min(2, force) def Explosion(point, visible=True, size=3, mass=10, effectcars=True): # Shockwave # Shockwave will be done using math applied to every object within # a certain size to the explosion. dani = scene.objects["Dani_Box"] if dani.get("driving"): dani["driving"]["shake"] = mass * 10 / dani.getDistanceTo(point) for object in scene.objects: # Getting distance to the object distance = object.getDistanceTo(point) # Skipping every object outside of the size of the explosion if distance > size: continue # Calculating forces on the object ( father away less forces ). force = ( 1 - distance / size ) * mass * object.mass # Calculating direction of the force vector = object.getVectTo(point)[1] * -force # Applying force try: object.applyImpulse(point, vector) # If it is a car we can apply damage to it, by sending it a fake # impulse of collision. # But first we need to make sure to have the data for this # operation Apoint = types.SimpleNamespace() Apoint.worldPoint = mathutils.Vector(point) Apoint.appliedImpulse = force * 10 points = [Apoint] # Then we send it over for callback in object.collisionCallbacks: callback(None, point, None, points) except: pass # Then we also have objects that use this very module to be # destructable. The all have a collision actuator listening # to a collision with a property "explosion" to execute their # function. if "spawned_by" in object: for controller in object.controllers: # This is a bit hacky, but it works. try: if "Destruction" in controller.script: exec(controller.script.replace("Scripts.Destruction.", "")+"(controller)") except: pass # And also we want to unsuspend it's physics. object.restoreDynamics() # Fire particles if visible: # particles("Fire", point, # despawn = random.randint(int(size/2), int(size)), # amount = random.randint(int(size/2), int(size)), # max = 200, # spread = 0, # normal = 0.2, # velocity = [0,0,0.01], # scale = 2) FireBurst(point, size/2) particles("Sparkle", point, despawn = random.randint(0, 100), amount = random.randint(10, 50), max = 100, spread = size/2, normal = 0.5, velocity = [0,0,0.01]) Smoke(point, 1) #AttatchFireBall(point, # size = 0.5, # duration = 200) SpreadOnGround("FireBall", point, 0.5, size/2, 10, 500) # Sound device = bge.logic.globalDict["SoundDevice"] s = "//sfx/boom.ogg" if s not in bge.logic.globalDict["sounds"]: bge.logic.globalDict["sounds"][s] = {"sound":aud.Sound(bge.logic.expandPath(s)), "play":None} sound = bge.logic.globalDict["sounds"][s] sound["play"] = device.play(sound["sound"]) sound["play"].location = point sound["play"].relative = False sound["play"].distance_maximum = 100 sound["play"].distance_reference = 1 sound["play"].attenuation = 1 sound["play"].pitch = random.uniform(0.6, 1.4) sound["play"].volume = 20 def DeclareBroken(cont): #cont = bge.logic.getCurrentController() scene = bge.logic.getCurrentScene() part = cont.owner part["spawned_by"]["broken"] = True def WoodGate_level0(cont): #cont = bge.logic.getCurrentController() scene = bge.logic.getCurrentScene() part = cont.owner part["spawned_by"]["broken"] = True colision = cont.sensors["Collision"] car = colision.hitObject def do(cont, car, scene, part): # Optimization trick if len(Reuse.reuse.get("GatePart.Level0", [])) >= 4: # Spawning madness for i in range(4): chunk = Reuse.Create("GatePart.Level0", 10, selfDestructInactive=True) Reuse.SelfDestruct(chunk, 500) chunk.mass = 0 chunk.position = part.position chunk.orientation = part.orientation chunk.applyMovement( [ 0, i*1.24-2.45, 0 ] , True) if car: cv = car.getLinearVelocity() chunk.setLinearVelocity( cv / 2) # Sound device = bge.logic.globalDict["SoundDevice"] s = "//sfx/wood.ogg" if s not in bge.logic.globalDict["sounds"]: bge.logic.globalDict["sounds"][s] = {"sound":aud.Sound(bge.logic.expandPath(s)), "play":None} sound = bge.logic.globalDict["sounds"][s] sound["play"] = device.play(sound["sound"]) sound["play"].location = chunk.position sound["play"].velocity = chunk.getLinearVelocity() sound["play"].relative = False sound["play"].distance_maximum = 100 sound["play"].distance_reference = 1 sound["play"].attenuation = 1 sound["play"].pitch = random.uniform(0.6, 1.4) sound["play"].volume = 5 # Deleting the wooden gate Reuse.Delete(part) if Opt.GoodFPS("WoodGate_level0", 0.8) or (car and car.get("active")): do(cont, car, scene, part) else: Opt.ScheduleTask("WoodGate_level0 ["+str(id(part))+"]", 0.8, do, cont, car, scene, part) def WoodGate_level1(cont): # Performance kill switch if not Opt.GoodFPS("WoodGate_level1", 0.7): return #cont = bge.logic.getCurrentController() scene = bge.logic.getCurrentScene() part = cont.owner # Optimization trick if len(Reuse.reuse.get("GatePart.Level1", [])) >= 4: # Spawning madness chunks = [] for i in range(4): chunk = Reuse.Create("GatePart.Level1", 10, selfDestructInactive=True) Reuse.SelfDestruct(chunk, 100) chunk.mass = 0 chunk.position = part.position chunk.orientation = part.orientation chunks.append(chunk) chunks[0].applyMovement( [ 0, 0, 0.75 ] , True) chunks[0].applyRotation( [ math.pi/2, 0, 0 ] , True) chunks[1].applyMovement( [ 0, -0.75, 0 ] , True) chunks[2].applyMovement( [ 0.1, 0, 0 ] , True) chunks[2].applyRotation( [ math.pi/4, 0, 0 ] , True) chunks[3].applyRotation( [ -( math.pi/4 ), 0, 0, ] , True) # Deleting the wooden part Reuse.Delete(part) def LightStand(cont): #cont = bge.logic.getCurrentController() scene = bge.logic.getCurrentScene() part = cont.owner colision = cont.sensors["Collision"] car = colision.hitObject def do(cont, car, scene, part): part["spawned_by"]["broken"] = True head = None tail = None if Reuse.reuse.get("LightStand.Borked.Head"): # Head ( top part ) head = Reuse.Create("LightStand.Borked.Head", 300, selfDestructInactive=True) Reuse.SelfDestruct(head, 600) head.position = part.position head.orientation = part.orientation if Reuse.reuse.get("LightStand.Borked.Tail"): # Tail ( or leg ) tail = Reuse.Create("LightStand.Borked.Tail", 300, selfDestructInactive=True) Reuse.SelfDestruct(tail, 600) tail.position = part.position tail.orientation = part.orientation tail.applyMovement( [ 0, 0, -1.3 ] , True) tail.localAngularVelocity = [random.uniform(-1,1),random.uniform(-1,1),random.uniform(-1,1)] if car: cv = car.getLinearVelocity() if head: head.setLinearVelocity( cv ) if tail: tail.setLinearVelocity( cv ) # Deleting the light stand Reuse.Delete(part) if head: # Sound device = bge.logic.globalDict["SoundDevice"] s = "//sfx/hit.ogg" if s not in bge.logic.globalDict["sounds"]: bge.logic.globalDict["sounds"][s] = {"sound":aud.Sound(bge.logic.expandPath(s)), "play":None} sound = bge.logic.globalDict["sounds"][s] sound["play"] = device.play(sound["sound"]) sound["play"].location = head.position sound["play"].velocity = head.getLinearVelocity() sound["play"].relative = False sound["play"].distance_maximum = 100 sound["play"].distance_reference = 1 sound["play"].attenuation = 1 sound["play"].pitch = random.uniform(0.6, 1.4) sound["play"].volume = 5 if Opt.GoodFPS("LightStand", 0.9) or (car and car.get("active")): do(cont, car, scene, part) else: Opt.ScheduleTask("LightStand ["+str(id(part))+"]", 0.8, do, cont, car, scene, part) def HouseShelf(cont): # Performance kill switch if not Opt.GoodFPS("HouseShelf", 0.5): return #cont = bge.logic.getCurrentController() scene = bge.logic.getCurrentScene() part = cont.owner part["spawned_by"]["broken"] = True # -753 -940 412 despawn = 200 # Shelfs for i in range(5): shelf = Reuse.Create("Shelf", despawn) shelf.position = part.position shelf.orientation = part.orientation shelf.applyMovement([0.09,0,(0.3*i)-0.65], True) # Legs leg = Reuse.Create("Shelf_Leg1", despawn) leg.position = part.position leg.orientation = part.orientation leg.applyMovement([1.3,0,0], True) leg = Reuse.Create("Shelf_Leg2", despawn) leg.position = part.position leg.orientation = part.orientation leg.applyMovement([-1.1,0,0], True) # Lego bricks brick = Reuse.Create("LegoBrick", despawn) brick.position = part.position brick.orientation = part.orientation brick.applyMovement([0.3,0,0.31], True) brick.applyRotation([0,0,-(math.pi/4)], True) brick = Reuse.Create("LegoBrick", despawn) brick.position = part.position brick.orientation = part.orientation brick.applyMovement([-0.2,0,0], True) brick.applyRotation([0,0,-(math.pi/4)], True) # Toy Boxes box = Reuse.Create("Toy_Box", despawn) box.position = part.position box.orientation = part.orientation box.applyMovement([-0.6,0,0], True) box = Reuse.Create("Toy_Box", despawn) box.position = part.position box.orientation = part.orientation box.applyMovement([0.3,0,0.7], True) box = Reuse.Create("Toy_Box", despawn) box.position = part.position box.orientation = part.orientation box.applyMovement([0.3,0,-0.7], True) # Deleting the light stand Reuse.Delete(part) # Sound device = bge.logic.globalDict["SoundDevice"] s = "//sfx/wood.ogg" if s not in bge.logic.globalDict["sounds"]: bge.logic.globalDict["sounds"][s] = {"sound":aud.Sound(bge.logic.expandPath(s)), "play":None} sound = bge.logic.globalDict["sounds"][s] sound["play"] = device.play(sound["sound"]) sound["play"].location = box.position sound["play"].velocity = box.getLinearVelocity() sound["play"].relative = False sound["play"].distance_maximum = 100 sound["play"].distance_reference = 1 sound["play"].attenuation = 1 sound["play"].pitch = random.uniform(0.6, 1.4) sound["play"].volume = 5