Skip to content

Instantly share code, notes, and snippets.

@cmann1
Created December 10, 2017 08:36
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save cmann1/21f0e43a4c1111184b2102492f7f0f4a to your computer and use it in GitHub Desktop.
Save cmann1/21f0e43a4c1111184b2102492f7f0f4a to your computer and use it in GitHub Desktop.
Dustmod script animation
#include 'Track.cpp'
class Animation
{
Model@ model;
string name = '';
int length = 0;
float fps = 30;
bool loop = true;
bool skip_last_frame = false;
array<Track@> tracks;
array<Track@> track_list;
int num_tracks = 0;
Animation(string name, Model@ model)
{
this.name = name;
@this.model = model;
tracks.resize(model.num_nodes);
}
Track@ addTrack(Node@ node)
{
return addTrack(node.id);
}
Track@ addTrack(int id)
{
if(id < 0 or id >= model.num_nodes)
{
return null;
}
Track@ track = Track(id);
track_list.insertLast(track);
num_tracks++;
return @tracks[id] = track;
}
}
class Bone : Node
{
string name;
float base_length;
float length;
int depth = 0;
Bone@ parent = null;
array<Bone@> children;
int num_children = 0;
array<Drawable@> drawables;
int num_drawables = 0;
Bone(string name, float length=100)
{
super(NodeType::Bone);
this.name = name;
this.length = base_length = length;
}
void reset()
{
Node::reset();
length = base_length;
}
void clear()
{
for(int i = 0; i < num_children; i++)
{
@children[i].parent = null;
children[i].depth = 0;
}
children.resize(num_children = 0);
drawables.resize(num_drawables = 0);
}
void addChild(Bone@ child)
{
if(child.parent is this) return;
if(@child.parent != null)
{
child.parent.removeChild(child);
}
@child.parent = this;
child.depth = depth + 1;
children.insertLast(child);
num_children++;
}
void removeChild(Bone@ child)
{
if(child.parent !is this) return;
@child.parent = null;
child.depth = 0;
children.removeAt(children.findByRef(child));
num_children--;
}
Drawable@ addDrawable(Drawable@ child)
{
drawables.insertLast(child);
num_drawables++;
return child;
}
Drawable@ removeDrawable(Drawable@ child)
{
const int index = drawables.findByRef(child);
if(index >= 0)
{
children.removeAt(index);
num_drawables--;
}
return child;
}
}
#include 'Sprite.cpp'
#include 'Text.cpp'
#include 'Rect.cpp'
#include 'Line.cpp'
enum DrawableType
{
None,
Sprite,
Text,
Rect,
Line
}
class Drawable : Node
{
DrawableType type;
int layer = 17;
int sub_layer = 19;
Drawable(DrawableType type=DrawableType::None)
{
super(NodeType::Drawable);
this.type = type;
}
}
class Line : Drawable
{
float x1;
float y1;
float x2;
float y2;
float width;
uint colour;
Line(float x1, float y1, float x2, float y2, float width, uint colour)
{
super(DrawableType::Line);
this.x1 = x1;
this.y1 = y1;
this.x2 = x2;
this.y2 = y2;
this.width = width;
this.colour = colour;
}
}
#include 'utils.cpp';
#include 'Model.cpp';
#include 'TreeGen.cpp';
class script
{
script()
{
}
}
const float EPSILON = 5.3E-5;
const float PI = 3.1415926535897932384626433832795;
const float PI2 = PI * 2;
const float HALF_PI = PI / 2;
const float DEG2RAD = 1.0 / 180.0 * PI;
const float RAD2DEG = 1.0 / PI * 180.0;
const float PIXEL2TILE = 1.0 / 48;
const float TILE2PIXEL = 48;
//const float NaN = float(0x7fc00000);
//const float Infinity = float(0x7f800000);
float dot(float x1, float y1, float x2, float y2)
{
return x1 * x2 + y1 * y2;
}
float frand()
{
return float(rand()) / float(0x3fffffff);
}
int rand_range(int min, int max)
{
return min + (rand() % (max - min + 1));
}
float rand_range(float min, float max)
{
return min + (max - min) * frand();
}
float magnitude(float x, float y)
{
return sqrt(x * x + y * y);
}
float length_sqr(float x, float y)
{
return x * x + y * y;
}
float dist_sqr(float x1, float y1, float x2, float y2)
{
float dx = x2 - x1;
float dy = y2 - y1;
return dx * dx + dy * dy;
}
float lerp(float a, float b, float x)
{
return a * (1.0 - x) + b * x;
}
void normalize(float x, float y, float &out out_x, float &out out_y)
{
const float len = sqrt(x * x + y * y);
out_x = len != 0 ? x / len : 0;
out_y = len != 0 ? y / len : 0;
}
void project(float ax, float ay, float bx, float by, float &out out_x, float &out out_y)
{
const float dp = dot(ax, ay, bx, by);
out_x = ( dp / (bx * bx + by * by) ) * bx;
out_y = ( dp / (bx * bx + by * by) ) * by;
}
void reflect(float x, float y, float normal_x, float normal_y, float &out out_x, float &out out_y)
{
float d = dot(x, y, normal_x, normal_y);
out_x = x - 2 * normal_x * d;
out_y = y - 2 * normal_y * d;
}
void rotate(float x, float y, float angle, float &out out_x, float &out out_y)
{
out_x = cos(angle) * x - sin(angle) * y;
out_y = sin(angle) * x + cos(angle) * y;
}
float sgn(float x)
{
return x < -1e-9 ? -1 : (x > 1e-9 ? 1 : 0);
}
void vec2_limit(float x, float y, float limit, float &out out_x, float &out out_y)
{
float length = x * x + y * y;
if(length > limit * limit && length > 0)
{
length = sqrt(length);
out_x = x / length * limit;
out_y = y / length * limit;
}
else
{
out_x = x;
out_y = y;
}
}
float map(float value, float from_min, float from_max, float to_min, float to_max)
{
value = (value - from_min) / (from_max - from_min);
return to_min + value * (to_max - to_min);
}
#include '../common/math.cpp'
#include 'Node.cpp'
#include 'Bone.cpp'
#include 'drawables/Drawable.cpp'
#include 'anim/Animation.cpp'
class Model : trigger_base
{
scene@ g;
script@ script;
scripttrigger@ self;
canvas@ c;
Bone root('root');
[text] string current_anim_name = '';
[text] bool is_playing = true;
[text] float playback_speed = 1;
[angle] float rotation = 0;
[text] float scale_x = 1;
[text] float scale_y = 1;
[text] bool show_bones = false;
[hidden] float current_time = 0;
[hidden] int current_frame = -2;
[hidden] int target_frame = 0;
[hidden] float fps_step = 0;
dictionary animations;
Animation@ current_anim = null;
array<Bone@> bones;
int num_bones = 0;
array<Node@> nodes;
int num_nodes = 0;
Model()
{
@g = get_scene();
@c = create_canvas(false, 12, 19);
}
void init(script@ script, scripttrigger@ self)
{
@this.script = @script;
@this.self = @self;
self.editor_handle_size(8);
Bone bone2('bone2');
Bone bone3('bone3');
Bone bone4('bone4');
Bone bone5('bone5');
Bone bone6('bone6');
root.addDrawable(Sprite('flag', 'cidle'));
root.addChild(@bone2);
root.addChild(@bone6);
Sprite@ spr = cast<Sprite>(bone2.addDrawable(Sprite('props1', 'backdrops_5')));
spr.y = 85;
bone2.rotation = 20;
bone3.rotation = 40;
bone4.rotation = 60;
bone5.rotation = 80;
bone5.rotation = 30;
bone2.addChild(@bone3);
bone3.addChild(@bone4);
bone4.addChild(@bone5);
bone3.rotation = 45;
bone3.addDrawable(Rect(0, 0, 100, 100, 0, 0xAAFF0000));
Drawable@ text = bone3.addDrawable(Text(10, 10, 'XZua-23489UHo8'));
array<Drawable@> lines;
for(float y = -30; y <= 30; y += 15)
{
lines.insertLast(bone2.addDrawable(Line(100, 0, 150 - abs(y) * 0.5, y, 2, 0xFF00FF00)));
}
build();
Animation@ anim = Animation('Default', this);
anim.fps = 30;
anim.length = 180;
add_animation(anim);
anim.loop = true;
anim.skip_last_frame = true;
Track@ track;
@track = anim.addTrack(root);
track.addKeyFrame(0, 0, 0, 1, 1, 0);
track.addKeyFrame(89, 0, 0, 1, 1, 180);
track.addKeyFrame(179, 0, 0, 1, 1, 0);
@track = anim.addTrack(bone3);
track.addKeyFrame(0, 0, 0, 1, 1, 45);
track.addKeyFrame(59, 0, 0, 1, 1, 90);
track.addKeyFrame(119, 0, 0, 1, 1, 45);
@track = anim.addTrack(bone2);
track.addKeyFrame(0, 0, 0, 1, 1, 20);
track.addKeyFrame(59, 0, 0, 1, 1, -90);
track.addKeyFrame(119, 0, 0, 1, 1, 20);
@track = anim.addTrack(text);
track.addKeyFrame(0, 10, 10, 1, 1, 0);
track.addKeyFrame(89, 10, 10, 0.5714285, 0.5714285, 0);
track.addKeyFrame(179, 10, 10, 1, 1, 0);
for(int i = 0; i < 5; i++)
{
float t = i / 4.0 - 0.5;
@track = anim.addTrack(lines[i]);
track.addKeyFrame(0, 0, 0, 1, 1, 0);
track.addKeyFrame(29, 20, t * 60, 1, 1, 0);
track.addKeyFrame(59, 0, 0, 1, 1, 0);
}
set_animation(current_anim_name);
// const double value = 1231324455346;
// ByteArray data("");
// data.writeDouble(value);
// data.position = 0;
// puts('value ' + formatFloat(value, '0', 8, 20));
// puts('and back ' + formatFloat(data.readDouble(), '0', 8, 20));
}
void build()
{
array<Bone@> bones_stack = {root};
int bones_stack_size = 1;
int bones_stack_index = 1;
bones.resize(0);
num_bones = 0;
nodes.resize(0);
num_nodes = 0;
while(bones_stack_index > 0)
{
Bone@ bone = bones_stack[--bones_stack_index];
if(bone is null)
{
bones.insertLast(null);
num_bones++;
continue;
}
bone.id = num_nodes;
bones.insertLast(bone);
num_bones++;
nodes.insertLast(bone);
num_nodes++;
if(bones_stack_size < bones_stack_index + bone.num_children * 2)
{
bones_stack.resize(bones_stack_size = bones_stack_index + bone.num_children * 2);
}
for(int i = 0, count = bone.num_drawables; i < count; i++)
{
Drawable@ drawable = bone.drawables[i];
drawable.id = num_nodes;
nodes.insertLast(drawable);
num_nodes++;
}
@bones_stack[bones_stack_index++] = null;
for(int i = bone.num_children - 1; i >= 0; i--)
{
Bone@ child = bone.children[i];
@bones_stack[bones_stack_index++] = child;
}
}
}
void add_animation(Animation@ animation)
{
@animations[animation.name] = animation;
}
void play()
{
}
void pause()
{
}
void goto_next_frame()
{
if(current_anim is null) return;
target_frame++;
if(target_frame >= current_anim.length - (current_anim.skip_last_frame and current_anim.loop ? 1 : 0))
{
target_frame = current_anim.loop ? 0 : current_anim.length - 1;
}
}
void goto_prev_frame()
{
if(current_anim is null) return;
target_frame--;
if(target_frame < 0)
{
target_frame = current_anim.loop ? current_anim.length - (current_anim.skip_last_frame ? 2 : 1) : 0;
}
}
void set_position(int frame)
{
if(current_anim is null or frame == current_frame) return;
if(current_anim.loop)
{
frame = frame % (current_anim.length - (current_anim.skip_last_frame ? 1 : 0));
}
else
{
if(frame < 0) frame = 0;
else if(frame >= current_anim.length) frame = int(current_anim.length) - 1;
}
target_frame = frame;
}
void set_animation(string name)
{
if(!animations.exists(name)) name = '_bind';
@current_anim = cast<Animation>(animations[name]);
if(@current_anim != null)
{
fps_step = current_anim.fps / 60;
current_frame = -2;
current_time = 0;
target_frame = 0;
}
}
void force_update()
{
if(current_frame == target_frame or current_anim is null) return;
const bool goto_prev = target_frame == current_frame - 1;
const bool goto_next = target_frame == current_frame + 1;
array<Track@>@ tracks = current_anim.track_list;
const int num_tracks = current_anim.num_tracks;
for(int i = 0; i < num_tracks; i++)
{
Track@ track = tracks[i];
const int size = track.num_keyframes;
if(size == 0) continue;
const array<int>@ frames = @track.frame_index;
int current = track.current;
int prev = track.prev;
int next = track.next;
if(goto_next)
{
if(next != -1)
{
if(frames[next] == target_frame)
{
current = next;
prev = current > 0 ? current - 1 : -1;
next = current < size - 1 ? current + 1 : -1;
}
else if(current != -1)
{
prev = current;
current = -1;
}
}
else if(current != -1)
{
prev = current;
current = -1;
}
}
else if(goto_prev)
{
if(prev != -1)
{
if(frames[prev] == target_frame)
{
current = prev;
prev = current > 0 ? current - 1 : -1;
next = current < size - 1 ? current + 1 : -1;
}
else if(current != -1)
{
next = current;
current = -1;
}
}
else if(current != -1)
{
next = current;
current = -1;
}
}
else
{
int lo = 0;
int hi = size - 1;
int index = -1;
while(lo < hi)
{
const int mid = (lo + hi) / 2;
if(frames[mid] == target_frame)
{
index = mid;
break;
}
else if(frames[mid] > target_frame)
{
hi = mid - 1;
}
else
{
lo = mid + 1;
}
}
if(lo > hi)
{
const int tmp = lo;
lo = hi;
hi = tmp;
}
if(frames[lo] == target_frame) index = lo;
if(index == -1)
{
if(frames[lo] > target_frame) lo = -1;
else if(hi >= size) hi = -1;
else if(lo == hi) hi++;
}
else{
lo = index > 0 ? index - 1 : -1;
hi = index < size - 1 ? index + 1 : -1;
}
current = index;
prev = lo;
next = hi >= size ? -1 : hi;
}
track.current = current;
track.prev = prev;
track.next = next;
Node@ node = nodes[track.node_id];
const int index = current != -1 ? current : (prev == -1 and next != -1 ? next : (prev != -1 and next == -1 ? prev : -1));
if(index != -1)
{
node.x = track.x[index];
node.y = track.y[index];
node.scale_x = track.scale_x[index];
node.scale_y = track.scale_y[index];
node.rotation = track.rotation[index];
}
else
{
const float t = (target_frame - track.frame_index[prev]) / float(track.frame_index[next] - track.frame_index[prev]);
node.x = track.x[prev] + (track.x[next] - track.x[prev]) * t;
node.y = track.y[prev] + (track.y[next] - track.y[prev]) * t;
node.scale_x = track.scale_x[prev] + (track.scale_x[next] - track.scale_x[prev]) * t;
node.scale_y = track.scale_y[prev] + (track.scale_y[next] - track.scale_y[prev]) * t;
node.rotation = track.rotation[prev] + (((track.rotation[next] - track.rotation[prev]) + 180) % 360 - 180) * t;
}
}
current_frame = target_frame;
if(!goto_prev and !goto_next) current_time = target_frame;
}
void step()
{
if(is_playing and @current_anim != null)
{
current_time += fps_step * playback_speed;
target_frame = int(floor(current_time));
if(target_frame != current_frame)
{
if(target_frame >= current_anim.length - (current_anim.skip_last_frame and current_anim.loop ? 1 : 0))
{
target_frame = current_anim.loop ? 0 : current_anim.length - 1;
}
else if(target_frame < 0)
{
target_frame = current_anim.loop ? current_anim.length - (current_anim.skip_last_frame ? 2 : 1) : 0;
}
}
}
if(current_frame != target_frame)
{
force_update();
}
}
void draw(float sub_frame)
{
c.reset();
c.translate(self.x(), self.y());
c.rotate(rotation, 0, 0);
c.scale(scale_x, scale_y);
int layer = c.layer();
int sub_layer = c.sub_layer();
array<Bone@>@ bones = @this.bones;
const int num_bones = this.num_bones;
for(int j = 0; j < num_bones; j++)
{
Bone@ bone = bones[j];
if(bone is null)
{
c.pop();
continue;
}
c.push();
c.translate(bone.x, bone.y);
if(bone.rotation != 0) c.rotate(bone.rotation, 0, 0);
if(bone.scale_x != 1 or bone.scale_y != 1) c.scale(bone.scale_x, bone.scale_y);
for(int i = 0, count = bone.num_drawables; i < count; i++)
{
Drawable@ drawable = bone.drawables[i];
if(layer != drawable.layer) c.layer(layer = drawable.layer);
if(sub_layer != drawable.sub_layer) c.sub_layer(sub_layer = drawable.sub_layer);
switch(drawable.type)
{
case DrawableType::Sprite:
{
Sprite@ spr = cast<Sprite>(drawable);
c.draw_sprite(spr.sprite, spr.sprite_name, spr.frame, spr.palette, spr.x, spr.y, spr.rotation, spr.scale_x, spr.scale_y, 0xFFFFFFFF);
} break;
case DrawableType::Text:
{
Text@ text = cast<Text>(drawable);
c.draw_text(text.txt, text.x, text.y, text.scale_x, text.scale_y, text.rotation);
} break;
case DrawableType::Rect:
{
Rect@ rect = cast<Rect>(drawable);
c.draw_rectangle(
rect.x + rect.x1 * rect.scale_x, rect.y + rect.y1 * rect.scale_y,
rect.x + rect.x2 * rect.scale_x, rect.y + rect.y2 * rect.scale_y,
rect.rotation, rect.colour);
} break;
case DrawableType::Line:
{
Line@ line = cast<Line>(drawable);
c.draw_line(
line.x + line.x1 * line.scale_x, line.y + line.y1 * line.scale_y,
line.x + line.x2 * line.scale_x, line.y + line.y2 * line.scale_y,
line.width, line.colour);
} break;
}
}
c.translate(bone.length, 0);
}
if(show_bones)
{
draw_bones();
}
}
protected void draw_bones()
{
c.reset();
c.layer(21);
c.sub_layer(21);
c.translate(self.x(), self.y());
c.rotate(rotation, 0, 0);
c.scale(scale_x, scale_y);
array<Bone@>@ bones = @this.bones;
const int num_bones = this.num_bones;
for(int j = 0; j < num_bones; j++)
{
Bone@ bone = bones[j];
if(bone is null)
{
c.pop();
continue;
}
c.push();
c.translate(bone.x, bone.y);
if(bone.rotation != 0) c.rotate(bone.rotation, 0, 0);
c.draw_line(0, 0, bone.length * bone.scale_x, 0, 6, 0xFF000000);
c.draw_line(0, 0, bone.length * bone.scale_x, 0, 3, 0xFFFFFFFF);
if(bone.scale_x != 1 or bone.scale_y != 1) c.scale(bone.scale_x, bone.scale_y);
c.translate(bone.length, 0);
}
}
void editor_draw(float sub_frame)
{
draw(sub_frame);
}
}
enum NodeType
{
Bone,
Drawable
}
class Node
{
int id = 0;
NodeType node_type;
float x = 0;
float y = 0;
float scale_x = 1;
float scale_y = 1;
float rotation = 0;
Node(NodeType node_type)
{
this.node_type = node_type;
}
void reset()
{
x = 0;
y = 0;
scale_x = 1;
scale_y = 1;
rotation = 0;
}
}
class Rect : Drawable
{
float x1;
float y1;
float x2;
float y2;
uint colour;
Rect(float x1, float y1, float x2, float y2, float rotation, uint colour)
{
super(DrawableType::Rect);
this.x1 = x1;
this.y1 = y1;
this.x2 = x2;
this.y2 = y2;
this.rotation = rotation;
this.colour = colour;
}
}
class Sprite : Drawable
{
sprites@ sprite;
string sprite_name;
uint32 frame = 0;
uint32 palette = 0;
Sprite(string sprite_set, string sprite_name, float x=0, float y=0, float scale_x=1, float scale_y=1, float rotation=0)
{
super(DrawableType::Sprite);
this.sprite_name = sprite_name;
@sprite = create_sprites();
sprite.add_sprite_set(sprite_set);
this.x = x;
this.y = y;
this.scale_x = scale_x;
this.scale_y = scale_y;
this.rotation = rotation;
}
void reset()
{
Drawable::reset();
frame = 0;
palette = 0;
}
}
class Text : Drawable
{
textfield@ txt;
Text(float x, float y, string text, string font='Caracteres', uint font_size=36, uint colour=0xFFFFFFFF, int align_h=-1, int align_v=-1)
{
super(DrawableType::Text);
this.x = x;
this.y = y;
@txt = create_textfield();
txt.text(text);
txt.set_font(font, font_size);
txt.colour(colour);
txt.align_horizontal(align_h);
txt.align_vertical (align_v);
}
}
class Track
{
int node_id;
array<int> frame_index;
array<float> x;
array<float> y;
array<float> scale_x;
array<float> scale_y;
array<float> rotation;
int num_keyframes = 0;
int current = -1;
int prev = -1;
int next = -1;
Track(int node_id)
{
this.node_id = node_id;
}
void addKeyFrame(int index, float x, float y, float scale_x, float scale_y, float rotation)
{
this.frame_index.insertLast(index);
this.x.insertLast(x);
this.y.insertLast(y);
this.scale_x.insertLast(scale_x);
this.scale_y.insertLast(scale_y);
this.rotation.insertLast(rotation);
num_keyframes++;
}
// void set_position(int frame)
// {
// int index = 0;
//
// while(index < num_keyframes)
// {
// if(frame_index[index] > frame) break;
// index++;
// }
//
// current_index = index;
// }
}
class TreeGen : Model
{
[text] uint seed = 0;
[text] int layer = 16;
[text] int sub_layer = 19;
[text] int leaf_sub_layer_min = 20;
[text] int leaf_sub_layer_max = 20;
[text] float start_width = 36;
[text] float start_height = 300;
[text] float width_factor = 0.75;
[text] float height_factor = 0.75;
[text] int branch_min = 2;
[text] int branch_max = 4;
[text] float branch_angle = 50;
[text] int max_depth = 4;
[text] float swing_min = 10;
[text] float swing_max = 20;
[text] float swing_factor = 1.2;
[text] int swing_length = 240;
[colour,alpha] uint colour = 0xFFFFFFFF;
[text] int leaf_min = 2;
[text] int leaf_max = 8;
[text] float leaf_scale_min = 0.6;
[text] float leaf_scale_max = 0.8;
[text] float leaf_x_min = 0.7;
[text] float leaf_x_max = 1;
[text] float leaf_angle = 20;
[text] float leaf_swing_angle = 120;
[text] string leaf_set = 'slimeboss';
[text] string leaf_sprite = 'sbcleanse';
[text] int leaf_frame = 0;
uint prev_seed = 0;
void init(script@ script, scripttrigger@ self)
{
Model::init(script, self);
generate();
}
protected void generate()
{
array<Sprite@> leaves;
root.clear();
root.rotation = -90;
srand(seed);
generate_branch(root, null, leaves, start_width, start_height, 0, 0);
build();
Animation@ anim = Animation('Default', this);
anim.fps = 30;
anim.length = swing_length;
add_animation(anim);
anim.loop = true;
anim.skip_last_frame = true;
Track@ track;
for(int i = 0; i < num_bones; i++)
{
Bone@ bone = bones[i];
if(bone is null) continue;
@track = anim.addTrack(bone);
const int steps = 30;
const float angle = (swing_min + frand() * (swing_max - swing_min)) * pow(swing_factor, bone.depth);
for(int j = 0; j < steps; j++)
{
const float t = j / (steps - 1.0);
track.addKeyFrame(int(t * (swing_length - 1)), 0, 0, 1, 1, bone.rotation + angle * sin(t * PI2));
}
}
for(int i = int(leaves.length()) - 1; i >= 0; i--)
{
Sprite@ leaf = leaves[i];
@track = anim.addTrack(leaf);
track.addKeyFrame(0, leaf.x, leaf.y, leaf.scale_x, leaf.scale_y, leaf.rotation);
track.addKeyFrame(int(swing_length * rand_range(0.35, 0.65)), leaf.x, leaf.y, leaf.scale_x, leaf.scale_y, leaf.rotation + rand_range(-leaf_swing_angle, leaf_swing_angle));
track.addKeyFrame(swing_length, leaf.x, leaf.y, leaf.scale_x, leaf.scale_y, leaf.rotation);
}
set_animation('Default');
}
protected Bone@ generate_branch(Bone@ branch, Bone@ parent, array<Sprite@>@ leaves, float width, float height, int branch_index, int current_depth)
{
if(branch is null) @branch = Bone('bone.' + current_depth + '.' + branch_index);
if(parent !is null) parent.addChild(branch);
branch.length = height;
const float scale_x = height / 200;
const float scale_y = width / 25;
Sprite@ spr = Sprite('props1', 'backdrops_5', 120 * scale_x, 82 * scale_y, scale_x, scale_y);
// branch.addDrawable(Rect(0, -width*0.5, height, width*0.5, 0, colour));
branch.addDrawable(spr);
spr.layer = layer;
spr.sub_layer = sub_layer;
if(current_depth > 0 and leaf_set != '' and leaf_sprite != '')
{
int leaf_count = rand_range(leaf_min, leaf_max);
for(int i = 0; i < leaf_count; i++)
{
const float flip = frand() > 0.5 ? 1 : -1;
const float leaf_scale = rand_range(leaf_scale_min, leaf_scale_max);
@spr = Sprite(leaf_set, leaf_sprite,
branch.length * rand_range(leaf_x_min, leaf_x_max), -18 * scale_y * flip,
leaf_scale, leaf_scale * flip);
branch.addDrawable(spr);
spr.layer = layer;
spr.sub_layer = rand_range(leaf_sub_layer_min, leaf_sub_layer_max);
spr.rotation = rand_range(-leaf_angle, leaf_angle);
spr.frame = leaf_frame;
leaves.insertLast(spr);
}
}
if(current_depth >= max_depth) return branch;
width *= rand_factor(width_factor, 0.35);
height *= rand_factor(height_factor, 0.5);
current_depth++;
int child_count = rand_range(branch_min, branch_max);
for(int i = 0; i < child_count; i++)
{
Bone@ child = generate_branch(null, branch, leaves, width, height, i, current_depth);
child.rotation = (i / max(1, child_count - 1.0) - 0.5) * rand_factor(branch_angle, 0.5);
}
return branch;
}
protected float rand_factor(float value, float amount)
{
return value * (1 + (frand() - 0.5) * amount);
}
void editor_step()
{
if(prev_seed != seed)
{
generate();
prev_seed = seed;
}
}
}
#include "math.cpp"
const float DT = 1.0 / 60;
const float SCREEN_LEFT = -800;
const float SCREEN_TOP = -450;
const float SCREEN_RIGHT = 800;
const float SCREEN_BOTTOM = 450;
const int VAR_TYPE_NONE = 0;
const int VAR_TYPE_BOOL = 1;
const int VAR_TYPE_INT8 = 2;
const int VAR_TYPE_INT16 = 3;
const int VAR_TYPE_INT32 = 4;
const int VAR_TYPE_INT64 = 5;
const int VAR_TYPE_FLOAT = 6;
const int VAR_TYPE_STRING = 7;
const int VAR_TYPE_ARRAY = 8;
const int VAR_TYPE_STRUCT = 9;
const int VAR_TYPE_VEC2 = 10;
const int LT_NORMAL = 0;
const int LT_NEXUS = 1;
const int LT_NEXUS_MP = 2;
const int LT_TUGOFWAR = 3;
const int LT_SURVIVAL = 4;
const int LT_RUSH = 5;
const int LT_DUSTMOD = 6;
string bin(uint64 x, uint max_bits=32)
{
string result = "";
while(max_bits-- > 0)
{
result = ((x & 1 == 1) ? "1" : "0") + result;
x >>= 1;
}
return result;
}
prop@ create_prop(uint set, uint group, uint index, int layer=19, int sub_layer=19, float x=0, float y=0, float rotation=0)
{
prop@ p = create_prop();
p.prop_set(set);
p.prop_group(group);
p.prop_index(index);
p.layer(layer);
p.sub_layer(sub_layer);
p.x(x);
p.y(y);
p.rotation(rotation);
return p;
}
void build_sprite(message@ msg, string name, int ox=0, int oy=0, string var_prefix="spr_")
{
msg.set_string(name, var_prefix + name);
if(ox != 0) msg.set_int(name + "|offsetx", ox);
if(oy != 0) msg.set_int(name + "|offsety", oy);
}
void build_sound(message@ msg, string name, float loop_seconds = 0, string var_prefix="snd_")
{
msg.set_string(name, var_prefix + name);
if(loop_seconds > 0) msg.set_int(name + "|loop", int(44100 * loop_seconds)); // 2 seconds in
}
void print_vars(entity@ e)
{
varstruct@ vars = e.vars();
puts(e.type_name() + "[" + vars.num_vars() + "]");
for(uint i = 0; i < vars.num_vars(); i++)
{
// string name = vars.var_name(i);
// varvalue@ value = vars.get_var(i);
puts( print_var_value(vars.var_name(i), vars.get_var(i), " ") );
}
}
string print_var_value(string name, varvalue@ value, string indent="", int array_max=25, bool print_type=true)
{
const int id = value.type_id();
string value_str = "";
if(id == VAR_TYPE_BOOL)
{
value_str = "" + value.get_bool();
}
else if(id == VAR_TYPE_INT8)
{
value_str = "" + value.get_int8();
}
else if(id == VAR_TYPE_INT16)
{
value_str = "" + value.get_int16();
}
else if(id == VAR_TYPE_INT32)
{
value_str = "" + value.get_int32();
}
else if(id == VAR_TYPE_INT64)
{
value_str = "" + value.get_int64();
}
else if(id == VAR_TYPE_FLOAT)
{
value_str = "" + value.get_float();
}
else if(id == VAR_TYPE_STRING)
{
value_str = "\"" + value.get_string() +"\"";
}
else if(id == VAR_TYPE_VEC2)
{
value_str = "<" + value.get_vec2_x() + ", " + value.get_vec2_y() + ">";
}
else if(id == VAR_TYPE_ARRAY)
{
vararray@ arr_value = value.get_array();
int size = arr_value.size();
value_str = var_type_string(arr_value.element_type_id()) + "[" + size + "]";
for(int i = 0; i < size; i++)
{
varvalue@ item = arr_value.at(i);
value_str += "\n" + print_var_value(i + "", item, indent + " ", array_max, false);
if(array_max > 0 and i > array_max and i + 1 < size)
{
value_str += "\n " + indent + "... " + (size - array_max) + " more";
break;
}
}
}
else if(id == VAR_TYPE_STRUCT)
{
// value_str = "<" + value.get_vec2_x() + ", " + value.get_vec2_y() + ">";
}
return indent + name + (print_type ? "[" + var_type_string(id) + "]" : "") + " = " + value_str;
}
string var_type_string(const int id)
{
if(id == VAR_TYPE_NONE)
return "None";
if(id == VAR_TYPE_BOOL)
return "Bool";
if(id == VAR_TYPE_INT8)
return "Int8";
if(id == VAR_TYPE_INT16)
return "Int16";
if(id == VAR_TYPE_INT32)
return "Int32";
if(id == VAR_TYPE_INT64)
return "Int64";
if(id == VAR_TYPE_FLOAT)
return "Float";
if(id == VAR_TYPE_STRING)
return "String";
if(id == VAR_TYPE_ARRAY)
return "Array";
if(id == VAR_TYPE_STRUCT)
return "Struct";
if(id == VAR_TYPE_VEC2)
return "Vec2";
return "Uknown";
}
string str(float x)
{
return formatFloat(x, "", 0, 3);
}
string vstr(float x, float y)
{
return "<" + str(x) + ", " + str(y) + "> ";
}
entity@ update_text_trigger(scene@ g, entity@ original_trigger, string new_text)
{
entity@ text_trigger = create_entity("text_trigger");
varstruct@ o_vars = original_trigger.vars();
varstruct@ n_vars = text_trigger.vars();
n_vars.get_var("text_string").set_string(new_text);
n_vars.get_var("width").set_int32(o_vars.get_var("width").get_int32() );
text_trigger.x(original_trigger.x());
text_trigger.y(original_trigger.y());
g.add_entity(text_trigger);
g.remove_entity(original_trigger);
return text_trigger;
}
void play_script_stream(scene@ g, string name, uint soundGroup, float x, float y, bool loop, float volume, bool positional)
{
audio@ a;
while(volume > 0)
{
@a = g.play_script_stream(name, soundGroup, x, y, loop, min(1, volume));
if(positional) a.positional(true);
volume--;
}
}
entity@ create_emitter(int id, float x, float y, int width, int height, int layer, int sub_layer)
{
entity@ emitter = create_entity("entity_emitter");
varstruct@ vars = emitter.vars();
emitter.layer(layer);
vars.get_var("emitter_id").set_int32(id);
vars.get_var("width").set_int32(width);
vars.get_var("height").set_int32(height);
vars.get_var("draw_depth_sub").set_int32(sub_layer);
vars.get_var("r_area").set_bool(true);
emitter.set_xy(x, y);
return emitter;
}
void puts(bool x) { puts(x + ""); }
void puts(int x) { puts(x + ""); }
void puts(uint x) { puts(x + ""); }
void puts(float x) { puts(x + ""); }
void puts(double x) { puts(x + ""); }
void puts(float x, float y) { puts(str(x) + ", " + str(y)); }
void puts(rectangle@ r)
{
puts(vstr(r.left(), r.top()) + "" + vstr(r.right(), r.bottom()));
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment