DanisRace/Scripts/Destruction.py

684 lines
22 KiB
Python
Raw Normal View History

2024-07-13 15:15:50 +02:00
# 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):
# 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.position = point
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