-
-
Save cmann1/21f0e43a4c1111184b2102492f7f0f4a to your computer and use it in GitHub Desktop.
Dustmod script animation
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include 'utils.cpp'; | |
#include 'Model.cpp'; | |
#include 'TreeGen.cpp'; | |
class script | |
{ | |
script() | |
{ | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | |
} | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | |
// } | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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