#
# This is v1 of a player moddable city.
#
# NOTE: Partial catalog listing at bottom of file for piece ID reference
#
# Import everything we need to create the city. This should exist in every script.
from supersculpt3 import *
import patterns
import time
import bloom, sequencer, mutator, selector, playground
# Default falling speed:
DEBUG_SPEED_MULTIPLIER = 1.0 # The level won't sync with the music if you change this from 1.0
# Color defines
color_major = (79,96,147)
color_minor = (255,152,40)
color_accent = (246,217,48)
color_alt = (135,113,175)
# Tell the user what module this is:
console_write("Module: "+__name__, FOREGROUND_MAGENTA | FOREGROUND_INTENSITY )
'''
######## ### ######## ######## ######## ######## ## ## ######
## ## ## ## ## ## ## ## ## ### ## ## ##
## ## ## ## ## ## ## ## ## #### ## ##
######## ## ## ## ## ###### ######## ## ## ## ######
## ######### ## ## ## ## ## ## #### ##
## ## ## ## ## ## ## ## ## ### ## ##
## ## ## ## ## ######## ## ## ## ## ######
'''
# The pattern:
class Pattern(Generator):
# Generate a single instance of this pattern. This script will call this function directly.
def generate(self):
# We'll collect the individual "pieces" here, then return them at the end of the function:
pieces =☻
# Variable initialization:
LOW_ENERGY_THRESHOLD = 0.14 # What counts as a low energy volume drop
active_patterns = [""]
next_sub_pattern_layer = 64 # Sets number of blank intro layers that begin the level
new_sub_pattern = 1 # True if we've just changed to a new sub-pattern
current_pattern = None # Stores the current pattern number that was picked
# Calculate the z_scale based on average_bpm for beat placed patterns below
avg_beats_per_second = 60.0 / music.average_bpm
length_of_beat_in_meters = avg_beats_per_second * level.hack_meters_per_second
beat_z_scale = length_of_beat_in_meters * 0.8 # Use 80% of beat
# These are state values for the extreme_stress pattern
gf_size = 5
gf_width = 5
gf_pos = [rndint(gf_size), rndint(gf_size)]
'''
######## ###### ######
## ## ## ## ## ##
## ## ## ##
######## ## ## ####
## ## ## ##
## ## ## ## ##
## ###### ######
'''
#
# Create the entire level:
#
total_layers = music.length_in_meters
console_write("Total layers: "+str(total_layers))
for ilayer in range( total_layers*4 ):
layer = ilayer*0.25
# Have BeatBox update energy, stress, and so forth once for each layer:
beatbox_for_layer(layer)
meta_queue =☻
'''
### ###### ######## #### ## ## ########
## ## ## ## ## ## ## ## ##
## ## ## ## ## ## ## ##
## ## ## ## ## ## ## ######
######### ## ## ## ## ## ##
## ## ## ## ## ## ## ## ##
## ## ###### ## #### ### ########
'''
# Time for a new sub-pattern:
if layer >= next_sub_pattern_layer:
next_sub_pattern_layer = layer + 1
# Store previous pattern:
previous_pattern = current_pattern
# Pick pattern based on stress of music (calm vs energetic):
if music.stress < 0.2: current_pattern = "no_stress"
elif music.stress < 0.4: current_pattern = "low_stress"
elif music.stress < 0.6: current_pattern = "mid_stress"
elif music.stress < 0.8: current_pattern = "high_stress"
else: current_pattern = "extreme_stress"
# Volume (energy) drop override:
if music.energy < LOW_ENERGY_THRESHOLD:
current_pattern = "low_energy"
# If a new pattern has been picked, set our flag (patterns can use this to init things):
if previous_pattern != current_pattern:
new_sub_pattern = 1
# Now set active pattern based on pattern num picked:
if current_pattern=="low_energy": active_patterns = ["low_energy"]
elif current_pattern=="no_stress": active_patterns = ["no_stress"]
elif current_pattern=="low_stress": active_patterns = ["low_stress","midground"]
elif current_pattern=="mid_stress": active_patterns = ["mid_stress","midground"]
elif current_pattern=="high_stress": active_patterns = ["high_stress","midground"]
elif current_pattern=="extreme_stress": active_patterns = ["extreme_stress","midground"]
'''
######## ####### ######## ######## ###### ######## ####### ## ## ## ## #######
## ## ## ## ## ## ## ## ## ## ## ## ## ## ### ## ## ##
## ## ## ## ## ## ## ## ## ## ## ## ## #### ## ## ##
###### ## ## ######## ###### ## #### ######## ## ## ## ## ## ## ## ## ##
## ## ## ## ## ## ## ## ## ## ## ## ## ## ## #### ## ##
## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ### ## ##
## ####### ## ## ######## ###### ## ## ####### ####### ## ## #######
'''
# Active gameplay patterns near "core" of world
#
'''
## ####### ## ## ######## ## ## ######## ######## ###### ## ##
## ## ## ## ## ## ## ### ## ## ## ## ## ## ## ##
## ## ## ## ## ## ## #### ## ## ## ## ## ####
## ## ## ## ## ## ###### ## ## ## ###### ######## ## #### ##
## ## ## ## ## ## ## ## #### ## ## ## ## ## ##
## ## ## ## ## ## ## ## ### ## ## ## ## ## ##
######## ####### ### ### ######## ## ## ######## ## ## ###### ##
'''
# Low energy pattern will trigger during low enough volume drops in the song.
#
if "low_energy" in active_patterns:
island_size_length = 15
cube_spacing = 8
# Instantitate sequencer:
try:
sequencer_fg_island
except:
sequencer_fg_island = sequencer.Matrix(layer_delta=128, width=island_size_length,
height=island_size_length, dx=cube_spacing, dy=cube_spacing)
# One time inits
next_island_delta = [0, 0, 0]
island_pepper_delta = [0.0, 0.0, 0.0]
queue = sequencer_fg_island.iterate(layer)
max_radius = island_size_length*cube_spacing*0.5
# Kick blocks out further from the center of the island:
queue = filter(lambda p: chance( 100-100*vec_length([p.x, p.y, 0])/max_radius ), queue)
trees_queue =☻
# Blocks:
for p in queue[:]:
p.id = catalog.cube
# Island squares:
bell = clamp(1-1.0*vec_length([p.x, p.y, 0])/max_radius, 0, 1)
p.scale = 8
p.scale[2] = 32*pow(bell, 3)
p.color = color_major
if chance(10):
p.scale[2] *= 3
p.color = color_alt
# Add a tree:
if chance(15):
p3 = p.copy()
queue.append(p3)
p3.scale = 3+rnd(4)
p3.pan = rnd(360)
p3.color = color_accent
p3.z -= 3
# Create nearby islands:
ds = [rndelem([-1, 1])*64, rndelem([-1, 1])*64, 0]
for p in trees_queue:
p2 = p.copy()
queue.append(p2)
p2.position = vec_rotate(p2.position, [0, 0, rndelem([90, 180, 270])])
p2.position = vec_add(p2.position, ds)
#
# Shift everything:
#
if queue:
shift_radius = 32
ds = next_island_delta
next_island_delta = [rndsym(shift_radius), rndsym(shift_radius), 0]
for p in queue:
p.position = vec_add(p.position, ds)
#
# Create "pepper" on the beat to connect the islands:
#
pepper_jitter = 2 # 8?
try: sequencer_fg_island_pepper
except:
sequencer_fg_island_pepper = sequencer.Column(layer_delta=0.25)
# Iterate through pepper if on beat:
if music.beat:
pepper_queue = sequencer_fg_island_pepper.iterate(layer)
queue += pepper_queue
for p in pepper_queue:
p.id = catalog.cube
p.color = color_minor
p.scale = [1, beat_z_scale, 3+pow(music.stress, 2)*32]
p.flags["pepper"] = 1
# Move (jitterishly) towards next island:
p.translate(island_pepper_delta)
diff = vec_normalize(vec_diff(next_island_delta, island_pepper_delta), 1)
island_pepper_delta = vec_add(island_pepper_delta, diff)
island_pepper_delta = vec_add(island_pepper_delta, [rndsym(pepper_jitter), rndsym(pepper_jitter), 0])
p.rotation = [0, 0, p.z]
p.scale = [p.scale[2], p.scale[0], p.scale[1]]
meta_queue += queue
'''
## ## ####### ###### ######## ######## ######## ###### ######
### ## ## ## ## ## ## ## ## ## ## ## ## ##
#### ## ## ## ## ## ## ## ## ## ##
## ## ## ## ## ###### ## ######## ###### ###### ######
## #### ## ## ## ## ## ## ## ## ##
## ### ## ## ## ## ## ## ## ## ## ## ## ##
## ## ####### ###### ## ## ## ######## ###### ######
'''
if "no_stress" in active_patterns:
try:
sequencer_no_stress
except:
sequencer_no_stress = sequencer.Column(layer_delta=0.25)
if music.beat:
# Iterate through sequencer:
queue = sequencer_no_stress.iterate(layer)
# Iterate through all pieces:
for p in queue[:]:
p.scale = [32, 32, beat_z_scale]
p.color = color_minor
p.id = catalog.halfpipe
p.rotation = [0, 0, rnd(360)]
# p.y+=sin(layer)*16
# Add pieces to metaqueue:
meta_queue += queue
'''
## ####### ## ## ###### ######## ######## ######## ###### ######
## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ##
## ## ## ## ## ## ## ## ## ## ## ## ##
## ## ## ## ## ## ###### ## ######## ###### ###### ######
## ## ## ## ## ## ## ## ## ## ## ## ##
## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ##
######## ####### ### ### ###### ## ## ## ######## ###### ######
'''
if "low_stress" in active_patterns:
try:
sequencer_low_stress
except:
sequencer_low_stress = sequencer.Column(layer_delta=0.25)
# One time inits
slabs_offset=0
slab_number = 0
slab_za = 0
if music.beat:
# Iterate through sequencer:
queue = sequencer_low_stress.iterate(layer)
# Iterate through all pieces:
for p in queue[:]:
slab_number += 1
p.scale = [8, 1, beat_z_scale]
p.color = color_minor
p.id = catalog.cube
slabs_offset *= 0.95
slabs_offset += rndsym(8)
slab_za += rndsym(20)
p.rotation[2] = slab_za
p.x += slabs_offset
p.position = vec_rotate(p.position, p.rotation)
# Add pieces to metaqueue:
meta_queue += queue
'''
## ## #### ####### ###### ######## ######## ######## ###### ######
### ### ## ## ## ## ## ## ## ## ## ## ## ## ##
#### #### ## ## ## ## ## ## ## ## ## ##
## ### ## ## ## ## ###### ## ######## ###### ###### ######
## ## ## ## ## ## ## ## ## ## ## ##
## ## ## ## ## ## ## ## ## ## ## ## ## ## ##
## ## #### ####### ###### ## ## ## ######## ###### ######
'''
if "mid_stress" in active_patterns:
try:
sequencer_mid_stress
except:
sequencer_mid_stress = sequencer.Column(layer_delta=0.25)
if music.beat:
# Iterate through sequencer:
queue = sequencer_mid_stress.iterate(layer)
# Iterate through all pieces:
for p in queue[:]:
# Half-cylinders:
p.scale = [32, 32, beat_z_scale]
p.color = color_minor
p.id = catalog.halfpipe
p.rotation = [0, 0, rnd(360)]
# Cylinders:
p2 = p.copy()
queue.append(p2)
p2.id = catalog.newcylinder
p2.scale = [8, 8, beat_z_scale]
# Cubes:
p3 = p.copy()
queue.append(p3)
p3.color = color_accent
p3.id = catalog.cube
p3.scale = [28, 2, 1]
p3.rotation[2] += 45+rndsym(20)
p3.ang_velocity[2] = rndsym(60)
# Add pieces to metaqueue:
meta_queue += queue
'''
## ## #### ###### ## ## ###### ######## ######## ######## ###### ######
## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ##
## ## ## ## ## ## ## ## ## ## ## ## ##
######### ## ## #### ######### ###### ## ######## ###### ###### ######
## ## ## ## ## ## ## ## ## ## ## ## ## ##
## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ##
## ## #### ###### ## ## ###### ## ## ## ######## ###### ######
'''
if "high_stress" in active_patterns:
try:
sequencer_high_stress
except:
sequencer_high_stress = sequencer.Column(layer_delta=0.25)
# Calculate our animate in distance and duration based on average_bpm
animate_in_distance = 100
animate_in_duration = avg_beats_per_second
# Pos tracking vars
evolver_last_x = 0
evolver_last_y = 0
evolver_last_z = layer
if music.beat:
# Iterate through sequencer:
queue = sequencer_high_stress.iterate(layer)
# Determine which "mode" we are in
evolver_mode = periodic_ease(["grooves", "buzzes"], layer, period=768, lead=0, loop=True)
pos_range = periodic_ease([ [0,0], [5,5], [10,5], [5,10], [10,10], [5,0] ], layer, period=1000, lead=256, loop=True)
x = periodic_ease([-pos_range[0],pos_range[0]], layer, period=500, lead=500, loop=True)
y = periodic_ease([pos_range[1],-pos_range[1]], layer, period=500, lead=500, loop=True)
# Calculate x and y angle based on slopes
dx = x - evolver_last_x
dy = y - evolver_last_y
dz = layer - evolver_last_z
angle_x = atan2d(dy, dz)
angle_y = atan2d(dx, dz)
# Update tracking vars
evolver_last_x = x
evolver_last_y = y
evolver_last_z = layer
if evolver_mode == "grooves":
# Groove centric mode
#
s = periodic_ease([4,25,10,30], layer, period=250, lead=125, loop=True) # Scale
piece_id = periodic_ease([catalog.newcube, catalog.newcylinder, catalog.newhexagon, catalog.halfpipe], layer, period=750, lead=0, loop=True)
hollow_chance = periodic_ease([0,100,0,0], layer, period=384, lead=0, loop=True)
num_obstacles = periodic_ease([0,1,rndelem([3,4]),rndelem([4,5,6]),rndelem([1,2])], layer, period=384, lead=0, loop=True)
# Iterate through all pieces:
for p in queue[:]:
p.id = piece_id
p.scale = [s, s, beat_z_scale]
p.position = [x, y, layer]
p.rotation = [-angle_x,angle_y,0]
p.model_rotation = [0,0,rndelem([0,30,45,60,90,180,270])] # Rotating just the model's z!
p.color = color_major
# small chance to make hollow
if chance(hollow_chance):
if p.id == catalog.newcube:
p.id = catalog.cubetubethin
elif p.id == catalog.newcylinder:
p.id = catalog.cylindertubethin
elif p.id == catalog.newhexagon:
p.id = catalog.hexagontubethin
obstacle_queue =☻
# Create obstacles?
if queue and num_obstacles > 0 and piece_id != catalog.halfpipe:
obstacle_angle = rnd(360)
obstacle_angle_step = 360/num_obstacles
for n in range(num_obstacles):
o = queue[0].copy()
obstacle_queue.append(o)
p.id = catalog.newcube
p.scale = [s/8.0, s*2.0, 2]
p.position = [x, y, layer-(beat_z_scale/2.0)-1]
p.rotation = [-angle_x,angle_y,obstacle_angle]
p.model_rotation = [0,0,0]
p.color = color_accent
obstacle_angle += obstacle_angle_step
for p in queue:
p.animate_flyin(distance=animate_in_distance, duration=animate_in_duration)
for p in obstacle_queue:
p.animate_flyin(distance=animate_in_distance * 0.75, duration=animate_in_duration)
meta_queue += queue + obstacle_queue
else:
# Buzz centric mode
#
s = periodic_ease([25,4,10,30], layer, period=500, lead=250, loop=True) # Scale
piece_id = periodic_ease([catalog.newcube, catalog.newcylinder, catalog.newhexagon], layer, period=750, lead=0, loop=True)
hollow_chance = periodic_ease([100,0,25,75,15,0], layer, period=384, lead=128, loop=True)
if queue:
num_pepper = int(30 / s)
queue = queue[0].multiply(num_pepper)
# Iterate through all pieces:
for p in queue[:]:
p.id = piece_id
p.scale = [s, s, beat_z_scale]
scatter = 60 / s
p.position = [x+rndsym(scatter), y+rndsym(scatter), layer+rndsym(1)]
p.rotation = [-angle_x,angle_y,0]
p.model_rotation = [0,0,rndelem([0,30,45,60,90,180,270])] # Rotating just the model's z!
p.color = color_minor
# small chance to make hollow
if chance(hollow_chance):
if p.id == catalog.newcube:
p.id = catalog.cubetubethin
elif p.id == catalog.newcylinder:
p.id = catalog.cylindertubethin
elif p.id == catalog.newhexagon:
p.id = catalog.hexagontubethin
p.animate_flyin(distance=animate_in_distance, duration=animate_in_duration)
meta_queue += queue
'''
######## ## ## ######## ######## ######## ## ## ######## ###### ######## ######## ######## ###### ######
## ## ## ## ## ## ## ### ### ## ## ## ## ## ## ## ## ## ## ##
## ## ## ## ## ## ## #### #### ## ## ## ## ## ## ## ##
###### ### ## ######## ###### ## ### ## ###### ###### ## ######## ###### ###### ######
## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ##
## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ##
######## ## ## ## ## ## ######## ## ## ######## ###### ## ## ## ######## ###### ######
'''
if "extreme_stress" in active_patterns:
try: sequencer_extreme_stress
except:
sequencer_extreme_stress = sequencer.Matrix(layer_delta=10, width=gf_size, height=gf_size, dx=gf_width, dy=gf_width)
queue = sequencer_extreme_stress.iterate(layer)
tiny_tube_on = False
# change focal_point position on the beat
if music.beat and not tiny_tube_on:
move = [-1,1]
if chance(50):
if gf_pos[0] == 0: move.remove(-1)
if gf_pos[0] == gf_size-1: move.remove(1)
gf_pos[0] = (gf_pos[0] + rndelem(move))
else:
if gf_pos[1] == 0: move.remove(-1)
if gf_pos[1] == gf_size-1: move.remove(1)
gf_pos[1] = (gf_pos[1] + rndelem(move))
avoid_pos = [cosd(layer%360) * -10, sind(layer%360) * -10, layer]
for idx, p in enumerate(queue[:]):
cur_pos = [ idx % gf_size, idx / gf_size ]
dist_from_avoid = 100
if tiny_tube_on:
dist_from_avoid = vec_dist(avoid_pos, p.position)
if dist_from_avoid <= 5:
queue.remove(p)
continue
if cur_pos == gf_pos and not tiny_tube_on:
p.alert_distance = 500
p.angular_velocity_roll = 100
p.kill_distance = 4
p.add_color_node(color=palette.YellowOrange)
else:
p.alert_distance = rndrange(32,64)
# move
if dist_from_avoid > 8:
dist = [rndsym(4)+1, rndsym(4)+1, 1]
# shiftList(dist, rndint(10))
scale_new = [ sum(x) for x in zip(dist, p.scale) ]
p.add_scale_node(scale=p.scale)
p.add_scale_node(scale=scale_new, duration=0.4, ease="BackOut", play_count=1)
p.add_color_node(color=colors.desaturate(color_major, rndsym(1)))
# add to the queue
meta_queue += queue
'''
## ## #### ####### ###### ######## ####### ## ## ## ## #######
### ### ## ## ## ## ## ## ## ## ## ## ## ### ## ## ##
#### #### ## ## ## ## ## ## ## ## ## ## #### ## ## ##
## ### ## ## ## ## ## #### ######## ## ## ## ## ## ## ## ## ##
## ## ## ## ## ## ## ## ## ## ## ## ## ## #### ## ##
## ## ## ## ## ## ## ## ## ## ## ## ## ## ### ## ##
## ## #### ####### ###### ## ## ####### ####### ## ## #######
'''
# Aesthetic midground patterns around the core to add sense of buildings and space
#
if "midground" in active_patterns:
try:
sequencer_midground
except:
sequencer_midground = sequencer.PeriodicBloom(bloom.Frame, layer_delta=0.25)
# One time inits:
tp_num = rndint(16)
tp_last_dropped_layer = 0
last_tp_x = 0
last_tp_y = 0
# Clockwise mapping of queue tp_num to frame pieces from top-left corner
frame_piece_to_queue_map = [4,6,8,10,15,14,13,12,11,9,7,5,0,1,2,3]
if music.beat:
final_queue =☻
raw_queue = sequencer_midground.iterate(layer)
# Move pieces out/in based on average stress
core_dist_range = [89,55,34,21,13,8,5,4,3.5,3][music.decimal_stress_avg]
core_dist_range /= 14.0
core_dist_range_base = 2
# Time to create a new target piece?
if layer >= tp_last_dropped_layer:
tp_last_dropped_layer = layer + 100
change_target_chance = 100 # Could come from volatility in future? (Or at least stress)
if chance(change_target_chance):
tp_num += rndsign()
# Range check our target and loop around if bounds exceeded
if tp_num < 0:
tp_num = len(raw_queue) - 1
elif tp_num >= len(raw_queue):
tp_num = 0
tp = raw_queue[frame_piece_to_queue_map[tp_num]].copy() # Store target piece for later (look up in index table)
# Track x/y for later collision detection w/ normal small building pieces
target_x = tp.x
target_y = tp.y
tp.x *= core_dist_range+core_dist_range_base
tp.y *= core_dist_range+core_dist_range_base
last_tp_x = tp.x
last_tp_y = tp.y
# Setup our target piece and add to final queue
tp.id = rndelem([catalog.building_a6,catalog.building_c4,catalog.building_c5,catalog.building_d1a,
catalog.building_d1b,catalog.building_a1,catalog.building_a2,catalog.building_a4,
catalog.building_c2,catalog.building_c6])
tp.color = color_major
final_queue += [tp]
else:
tp = None
stress_chance = [40,35,30,25,20,15,12,10,8,6][music.decimal_stress]
raw_queue = selector.rndset(raw_queue, mychance=stress_chance)
for p in raw_queue:
# only process and add pieces that aren't in same space as target piece
if p.x != target_x and p.y != target_y:
final_queue += [p]
p.id = rndelem([catalog.building_16x16x32,catalog.building_8x24x32,
catalog.building_8x8x32,catalog.building_16x8x32,catalog.building_24x24x32,
catalog.building_8x12x32])
p.color = color_alt
p.x *= core_dist_range+core_dist_range_base*(3+rnd(2))
p.y *= core_dist_range+core_dist_range_base*(3+rnd(2))
# Kludge for z-fighting
p.x += rndsym(1)
p.y += rndsym(1)
p.z += rndsym(1)
meta_queue += final_queue
'''
###### ## ######## ### ## ## ## ## ########
## ## ## ## ## ## ### ## ## ## ## ##
## ## ## ## ## #### ## ## ## ## ##
## ## ###### ## ## ## ## ## ## ## ########
## ## ## ######### ## #### ## ## ##
## ## ## ## ## ## ## ### ## ## ##
###### ######## ######## ## ## ## ## ####### ##
'''
# Finished with new sub-pattern:
new_sub_pattern = 0
#
# Compile all queue together:
#
pieces += meta_queue
# Pattern returns all pieces and rails generated:
return pieces
'''
## ## #### ###### ## ## ### ## #### ######## ######## ########
## ## ## ## ## ## ## ## ## ## ## ## ## ## ##
## ## ## ## ## ## ## ## ## ## ## ## ## ##
## ## ## ###### ## ## ## ## ## ## ## ###### ########
## ## ## ## ## ## ######### ## ## ## ## ## ##
## ## ## ## ## ## ## ## ## ## ## ## ## ## ##
### #### ###### ####### ## ## ######## #### ######## ######## ## ##
'''
def generate_visualizer():
# Analyze the stress of the song layer by layer and track the timestamps of
# when the stress changes for later use by the visualizer timeline.
# Calculate average beats per second
avg_beats_per_second = 60.0 / music.average_bpm
# Calculate any settings differences based on feel of music (low/mid/high)
average_stress_normalized = scale_clamp(music.average_stress, 15.0, 88.0, 0.0, 0.99999)
if average_stress_normalized > 0.58 or music.average_bpm >= 120.0: # High tempo/stress feel
use_stress_average = False
console_write("Visualizer Mode: HIGH TEMPO / STRESS", FOREGROUND_GREEN | FOREGROUND_INTENSITY )
elif average_stress_normalized > 0.3 or music.average_bpm >= 80.0: # Medium tempo/stress feel
use_stress_average = False
console_write("Visualizer Mode: MID TEMPO / STRESS", FOREGROUND_GREEN | FOREGROUND_INTENSITY )
else: # Low tempo/stress feel
use_stress_average = True
console_write("Visualizer Mode: LOW TEMPO / STRESS", FOREGROUND_GREEN | FOREGROUND_INTENSITY )
color_skybox_none = palette.Black
color_skybox_low = shift_hue(color_major, 180)
color_skybox_med = shift_hue(color_minor, 180)
color_skybox_high = shift_hue(color_alt, 180)
color_skybox_extreme = shift_hue(color_accent, 180)
# Tracks what patterns are picked at what timestamps according to energy levels of music
energy_pattern_changes =☻
# Tracks what patterns are picked at what timestamps according to stress levels of music
stress_pattern_changes =☻
# Variable initialization:
energy_pat = None # Stores the current pattern number that was picked
stress_pat = None # Stores the current pattern number that was picked
song_time = music.length_in_meters/level.hack_meters_per_second # Song length in seconds
total_layers = music.length_in_meters
for ilayer in range( total_layers*4 ):
layer = ilayer*0.25
# Have BeatBox update energy, stress, and so forth:
beatbox_for_layer(layer)
# Store previous patterns
old_energy_pat = energy_pat
old_stress_pat = stress_pat
# Calculate music data
energy_data = music.energy_scaled
if use_stress_average:
stress_data = music.stress_avg
else:
stress_data = music.stress
#Pick energy pattern based on energy_data
if energy_data < 0.2:
energy_pat = 0
elif energy_data < 0.4:
energy_pat = 1
elif energy_data < 0.6:
energy_pat = 2
elif energy_data < 0.8:
energy_pat = 3
elif energy_data < 0.9:
energy_pat = 4
else:
energy_pat = 5
# If a new pattern has been picked, mark our energy data with the timestamp and energy level
if old_energy_pat != energy_pat:
energy_pattern_changes += [[energy_pat, layer/level.hack_meters_per_second]]
#Pick stress pattern based on moving stress_data
if stress_data < 0.2:
stress_pat = 0
elif stress_data < 0.4:
stress_pat = 1
elif stress_data < 0.6:
stress_pat = 2
elif stress_data < 0.8:
stress_pat = 3
elif stress_data < 0.9:
stress_pat = 4
else:
stress_pat = 5
# If a new pattern has been picked, mark our stress data with the timestamp and stress level
if old_stress_pat != stress_pat:
stress_pattern_changes += [[stress_pat, layer/level.hack_meters_per_second]]
# Create our visualizer
visualizer = Visualizer(loop_all=False)
# Add a visual to our visualizer
visual = visualizer.add_visual()
# With camera following rotation
visual.camera.follow_rotation = True
# Need to override Unity default camera fov
visual.camera.add_fov_node(fov=60.0)
# Starting off skybox color comes from our first data point
first_skybox_color = [color_skybox_none, color_skybox_low, color_skybox_med, color_skybox_high, color_skybox_extreme, palette.White][energy_pattern_changes[0][0]]
visual.camera.add_color_skybox_node(color=first_skybox_color, play_count=1)
# Now build out the other skybox tweens according to the volume (energy) changes
for n in range(1, len(energy_pattern_changes)):
color_index = energy_pattern_changes[n][0]
tween_duration = energy_pattern_changes[n][1]-energy_pattern_changes[n-1][1]
skybox_color = [color_skybox_none, color_skybox_low, color_skybox_med, color_skybox_high, color_skybox_extreme, palette.White][color_index]
# With a skybox color
visual.camera.add_color_skybox_node(color=skybox_color, alpha=0.5, duration=tween_duration)
# Create our pieces to add to the visualizer
pieces =☻
# Start w/ base piece for queue
base_piece = Piece()
base_piece.id = rndelem([catalog.viz_cube, catalog.viz_tube, catalog.viz_cross, catalog.viz_prism, catalog.viz_pyramid,
catalog.viz_tetrahedron, catalog.viz_wallwindow, catalog.viz_tube, catalog.viz_cylinder])
base_piece.position = [0,0,225]
queue = [base_piece]
# Arc values:
radius = 96
segment_angle_delta = 6
segments = 360/segment_angle_delta
start_angle = rnd(360)
for p0 in queue[:]:
queue2 = p0.multiply(segments-1)
queue += queue2
queue2.append(p0)
# queue2 now has all pieces in this ring.
# Position the segments:
for n in range(len(queue2)):
p = queue2[n]
p.x = radius
# Compensate for end-cap overlap
ang_adj = 0
if n == 0:
ang_adj = -8
elif n == len(queue2) - 1:
ang_adj = 8
ang = n*segment_angle_delta + start_angle + ang_adj
p.position = vec_rotate(p.position, [0, 0, ang])
p.pan = ang
p.scale = rndelem([[4, 8, 16],[2, 4, 8],[8, 16, 32]])
p.color = color_major
if chance(25):
p.color = color_minor
# Throb ring city:
p.setup_throbber_color(color=[0,0,0], band=rndelem([0, 7]))
p.setup_throbber_scale(scale=[0, 0, 256], band=rndelem([0, 7]))
# First and final segments become buildings:
if len(queue2)>1:
# Both buildings:
for p in [queue2[0], queue2[-1]]:
p.scale = [8, 32, 128]
#
# Change the begin/end buildings:
#
# Skinny:
if chance(20):
p.scale[1]*=0.75
else:
p.scale[0]*=0.75
# Double building inside/outside:
if chance(10):
p3 = p.copy()
queue.append(p3)
new_radius = vec_length([p.x, p.y, 0])+rndelem([12, -12])
p3.position = vec_normalize([p.x, p.y, 0], new_radius )
p3.z = p.z
# Bright building:
if chance(20):
p.color = vec_scale(p.color, 2)
pieces += queue
visual.add_pieces(pieces)
visualizer.add_timeline(index=0, duration=music.length_in_meters/level.hack_meters_per_second)
return visualizer
'''
###### ######## ######## ### ######## ######## ###### #### ######## ## ##
## ## ## ## ## ## ## ## ## ## ## ## ## ## ##
## ## ## ## ## ## ## ## ## ## ## ####
## ######## ###### ## ## ## ###### ## ## ## ##
## ## ## ## ######### ## ## ## ## ## ##
## ## ## ## ## ## ## ## ## ## ## ## ## ##
###### ## ## ######## ## ## ## ######## ###### #### ## ##
'''
# Build the entire city:
def create_city():
global level
# Boilerplate for loading level info and instantiating the level:
level = start_level_nice()
# Movement and camera related vars:
level.hack_meters_per_second = 32.0*DEBUG_SPEED_MULTIPLIER
level.hack_move_speed = 50.0
level.hack_camera_max_tilt_angle = 12.5*4
level.camera_look_duration = 0.01
level.hack_dead_zone_screen_percent = 0.015
#
# Start creating the level:
#
# Generate the pattern:
pattern = Pattern()
pieces = pattern.generate()
# Every city needs at least one rail!
rail = Rail()
rails = [rail]
# Collect any addiontal railnodes the PCG may have generated
railnodes = selector.railnodes(pieces)
# Hardcode the position of the first node to be the beginning of the song
rails[0].add_node(position=[0,0,0],max_radius=256)
if len(railnodes)>0: # If PCG generated additional railnodes:
railnodes[-1].z = music.length_in_meters # Adjust the position of the last rail node to be the end of the song
# then iterate through all the additional railnodes adding them to the rail
for p in railnodes:
rails[0].add_node(position=p.position, max_radius=p.max_radius)
else: # Otherwise, create the second one at the end of the song
rails[0].add_node(position=[0,0,music.length_in_meters],max_radius=256)
# Now filter out the railnodes from the remainder of the pieces
pieces = selector.non_railnodes(pieces)
# Every city needs at least one visualizer
visualizer = generate_visualizer()
# Write the entire level to disk.
level.write(pieces, rails, visualizer)
# Execute this script as a standalone.
run_creator(create_city)
'''
# Here is a listing of available catalog names to try using if you like
#
# You can use them above when you assign the piece id (ex: p.id = catalog.cone)
# will set that piece to use the cone.
#
catalog.cone=1000
catalog.cross=1001
catalog.cylinder=1002
catalog.hedragon=1003
catalog.hexagon=1004
catalog.prism=1005
catalog.pyramid=1006
catalog.spike=1007
catalog.tetraduodron=1008
catalog.tetrahedron=1009
catalog.tube=1010
catalog.wallcornerthick=1011
catalog.wallcornerthin=1012
catalog.wallcrossthick=1013
catalog.wallcrossthin=1014
catalog.walldoorway=1015
catalog.walltthick=1016
catalog.walltthin=1017
catalog.wallwindow=1018
catalog.cubetubethin=1019
catalog.cylindertubethin=1020
catalog.halfpipe=1021
catalog.hexagontubethin=1022
catalog.newcube=1023
catalog.newcylinder=1025
catalog.newhexagon=1026
catalog.funnel=1027
catalog.funnelstriped=1028
catalog.helix=1029
catalog.rib=1030
catalog.tubec=1031
catalog.tubes=1032
catalog.helixslide=1033
catalog.helixtube=1034
catalog.spiral=1035
catalog.cube=1413
catalog.opposing_monoliths_for_spinning=1429
catalog.hoop1=1440
catalog.hoop1b=1441
catalog.hoop1c=1442
catalog.hoop1d=1443
catalog.hoop2=1444
catalog.hoop3=1445
catalog.hoop4=1446
catalog.piece1a=1447
catalog.piece1b=1448
catalog.piece2a=1449
catalog.piece2b=1450
catalog.piece3a=1451
catalog.piece3b=1452
catalog.piece4=1453
catalog.piece5=1454
catalog.piece6=1455
catalog.piece7a=1456
catalog.piece7b=1457
catalog.piece8a=1458
catalog.piece8b=1459
catalog.piece9a=1460
catalog.piece9b=1461
catalog.piece10a=1462
catalog.piece10b=1463
catalog.piece11=1464
catalog.piece12=1465
catalog.piece13a=1466
catalog.piece13b=1467
catalog.rock1=1500
catalog.rock2=1501
catalog.marchingcubes1=1502
catalog.marchingcubes2=1503
catalog.building_a6=1600
catalog.building_c4=1601
catalog.building_c5=1602
catalog.building_d1a=1603
catalog.building_d1b=1604
catalog.building_a1=1605
catalog.building_a2=1606
catalog.building_a4=1607
catalog.building_c2=1608
catalog.building_c6=1609
catalog.building_16x16x32=1700
catalog.building_16x24x32=1701
catalog.building_8x24x32=1702
catalog.building_8x8x32=1703
catalog.building_16x8x32=1704
catalog.building_24x24x32=1705
catalog.building_16x12x32=1706
catalog.building_8x12x32=1707
catalog.building_300m=1708
catalog.building_64x64x256=1709
catalog.gardenbox=1800
catalog.radiotower=1801
catalog.walkway=1802
catalog.lightpost=1803
catalog.loadingdock=1804
catalog.fan_2blades=1805
catalog.fan_3blades=1806
catalog.fan_4blades=1807
catalog.car=2000
catalog.helicopter=2001
catalog.motorcycle=2002
catalog.truck=2003
catalog.triangle_tower=3000
catalog.holo_circle_6x6_1=5000
catalog.holo_circle_6x6_2=5001
catalog.sign_6x18_docking=6000
catalog.sign_6x18_stripes=6001
catalog.sign_8x9_arrow=6002
catalog.sign_8x9_dejocorp=6003
catalog.sign_8x15_burger=6004
catalog.sign_8x15_hazard=6005
catalog.sign_8x15_water=6006
catalog.sign_9x3_apply=6007
catalog.sign_9x3_vacancy=6008
catalog.sign_9x11_02=6009
catalog.sign_9x11_floor=6010
catalog.sign_9x11_stripes=6011
catalog.sign_9x11_zapdot=6012
catalog.sign_13x5_fuel=6013
catalog.sign_13x5_titan=6014
catalog.sign_13x7_moon=6015
catalog.scorepickup=8000
catalog.signal1=9000
catalog.signal2=9001
catalog.signal3=9002
'''