make more like misty

This commit is contained in:
John Alanbrook 2024-04-01 17:58:29 -05:00
parent 3b9a932c85
commit 32333c32ad
14 changed files with 133 additions and 219 deletions

View file

@ -1,6 +1,6 @@
var actor = {}; var actor = {};
actor.spawn = function(script, config){ actor.spawn = function(script, config, callback){
if (typeof script !== 'string') return undefined; if (typeof script !== 'string') return undefined;
console.info(`spawning actor with script ${script}`); console.info(`spawning actor with script ${script}`);
var padawan = Object.create(actor); var padawan = Object.create(actor);

View file

@ -786,14 +786,6 @@ Object.defineProperty(String.prototype, 'fromlast', {
} }
}); });
Object.defineProperty(String.prototype, 'tolast', {
value: function(val) {
var idx = this.lastIndexOf(val);
if (idx === -1) return this.slice();
return this.slice(0,idx);
}
});
Object.defineProperty(String.prototype, 'tofirst', { Object.defineProperty(String.prototype, 'tofirst', {
value: function(val) { value: function(val) {
var idx = this.indexOf(val); var idx = this.indexOf(val);
@ -822,13 +814,6 @@ Object.defineProperty(String.prototype, 'base', {
value: function() { return this.fromlast('/'); } value: function() { return this.fromlast('/'); }
}); });
Object.defineProperty(String.prototype, 'dir', {
value: function() {
if (!this.includes('/')) return "";
return this.tolast('/');
}
});
Object.defineProperty(String.prototype, 'splice', { Object.defineProperty(String.prototype, 'splice', {
value: function(index, str) { value: function(index, str) {
return this.slice(0,index) + str + this.slice(index); return this.slice(0,index) + str + this.slice(index);

View file

@ -124,6 +124,7 @@ Object.mixin(os.sprite(true), {
this.del_anim?.(); this.del_anim?.();
}, },
set path(p) { set path(p) {
console.info(`setting path to ${p}`);
p = Resources.find_image(p); p = Resources.find_image(p);
if (!p) { if (!p) {
console.warn(`Could not find image ${p}.`); console.warn(`Could not find image ${p}.`);

View file

@ -1039,12 +1039,9 @@ editor.inputs.lm.released = function() {
} else { } else {
var box = bbox.frompoints([editor.sel_start, Mouse.worldpos()]); var box = bbox.frompoints([editor.sel_start, Mouse.worldpos()]);
var hits = physics.box_query(bbox.tocwh(box)); physics.box_query(bbox.tocwh(box), function(entity) {
var obj = editor.do_select(entity);
hits.forEach(function(x, i) { if (obj) selects.push(obj);
var obj = editor.do_select(x);
if (obj)
selects.push(obj);
}); });
} }

View file

@ -1,5 +1,58 @@
"use math"; "use math";
Object.defineProperty(String.prototype, 'tolast', {
value: function(val) {
var idx = this.lastIndexOf(val);
if (idx === -1) return this.slice();
return this.slice(0,idx);
}
});
Object.defineProperty(String.prototype, 'dir', {
value: function() {
if (!this.includes('/')) return "";
return this.tolast('/');
}
});
globalThis.Resources = {};
Resources.replpath = function(str, path)
{
if (!str) return str;
if (str[0] === "/")
return str.rm(0);
if (str[0] === "@")
return os.prefpath() + "/" + str.rm(0);
if (!path) return str;
var stem = path.dir();
while (stem) {
var tr = stem + "/" +str;
if (io.exists(tr)) return tr;
stem = stem.updir();
}
return str;
}
Resources.replstrs = function(path)
{
if (!path) return;
var script = io.slurp(path);
var regexp = /"[^"\s]*?\.[^"\s]+?"/g;
var stem = path.dir();
script = script.replace(regexp,function(str) {
var newstr = Resources.replpath(str.trimchr('"'), path);
return `"${newstr}"`;
});
return script;
}
globalThis.json = {}; globalThis.json = {};
json.encode = function(value, replacer, space, whitelist) json.encode = function(value, replacer, space, whitelist)
{ {
@ -32,7 +85,6 @@ json.doc = {
readout: "Encode an object fully, including function definitions." readout: "Encode an object fully, including function definitions."
}; };
globalThis.Resources = {};
Resources.scripts = ["jsoc", "jsc", "jso", "js"]; Resources.scripts = ["jsoc", "jsc", "jso", "js"];
Resources.images = ["png", "gif", "jpg", "jpeg"]; Resources.images = ["png", "gif", "jpg", "jpeg"];
Resources.sounds = ["wav", 'flac', 'mp3', "qoa"]; Resources.sounds = ["wav", 'flac', 'mp3', "qoa"];
@ -142,8 +194,8 @@ function use(file, env, script)
return; return;
} }
console.info(`slurping ${file}`); console.info(`slurping ${file}`);
script ??= io.slurp(file); script ??= Resources.replstrs(file);
script = `(function() { ${script}; })`; script = `(function() { var self = this; ${script}; })`;
var fn = os.eval(file,script); var fn = os.eval(file,script);
use.cache[file] = fn; use.cache[file] = fn;
var ret = fn.call(env); var ret = fn.call(env);

View file

@ -10,6 +10,8 @@ function obj_unique_name(name, obj) {
return n; return n;
} }
var urcache = {};
var gameobject_impl = { var gameobject_impl = {
get pos() { get pos() {
assert(this.master, `Entity ${this.toString()} has no master.`); assert(this.master, `Entity ${this.toString()} has no master.`);
@ -232,13 +234,6 @@ var gameobject = {
physics.sgscale(this, x) physics.sgscale(this, x)
}, },
phys_material() {
var mat = {};
mat.elasticity = this.elasticity;
mat.friction = this.friction;
return mat;
},
worldpos() { return this.pos; }, worldpos() { return this.pos; },
set_worldpos(x) { set_worldpos(x) {
var poses = this.objects.map(x => x.pos); var poses = this.objects.map(x => x.pos);
@ -247,10 +242,10 @@ var gameobject = {
}, },
screenpos() { return window.world2screen(this.worldpos()); }, screenpos() { return window.world2screen(this.worldpos()); },
worldangle() { return Math.rad2turn(this.angle); }, worldangle() { return this.angle; },
sworldangle(x) { this.angle = Math.turn2rad(x); }, sworldangle(x) { this.angle = x; },
get_ur() { return Object.access(ur, this.ur); }, get_ur() { return urcache[this.ur]; },
/* spawn an entity /* spawn an entity
text can be: text can be:
@ -258,34 +253,22 @@ var gameobject = {
an ur object an ur object
nothing nothing
*/ */
spawn(text) { spawn(text, config, callback) {
var ent = os.make_gameobject(); var ent = os.make_gameobject();
ent.setref(ent);
if (typeof text === 'object') ent.components ??= {};
text = text.name; ent.objects ??= {};
if (typeof text === 'undefined')
ent.ur = "empty";
else if (typeof text !== 'string') {
console.error(`Must pass in an ur type or a string to make an entity.`);
return;
} else {
if (Object.access(ur, text))
ent.ur = text;
else if (io.exists(text))
ent.ur = "script";
else {
console.warn(`Cannot make an entity from '${text}'. Not a valid ur.`);
return;
}
}
console.spam(`Creating entity of type ${ent.ur}`);
ent.warp_layer = [true];
ent.components = {};
ent.objects = {};
ent.timers = []; ent.timers = [];
if (typeof text === 'object') // assume it's an ur
{
config = text.data;
text = text.text;
}
if (text)
use(text, ent);
if (config)
Object.assign(ent, json.decode(io.slurp(config)));
ent.reparent(this); ent.reparent(this);
@ -296,18 +279,7 @@ var gameobject = {
urdiff: {}, urdiff: {},
}; };
ent.setref(ent); ent.ur = text + "+" + config;
Object.hide(ent, 'ur', 'components', 'objects', '_ed', 'timers', 'master');
if (ent.ur === 'empty') {
if (!ur.empty.proto) ur.empty.proto = json.decode(json.encode(ent));
return ent;
}
if (ent.ur === 'script')
use(text, ent);
else
apply_ur(ent.ur, ent);
for (var [prop, p] of Object.entries(ent)) { for (var [prop, p] of Object.entries(ent)) {
if (!p) continue; if (!p) continue;
@ -329,6 +301,10 @@ var gameobject = {
if (sim.playing()) if (sim.playing())
if (typeof ent.start === 'function') ent.start(); if (typeof ent.start === 'function') ent.start();
ent.objects ??= {};
ent.components ??= {};
Object.hide(ent, 'ur', 'components', 'objects', '_ed', 'timers', 'master');
var mur = ent.get_ur(); var mur = ent.get_ur();
if (mur && !mur.proto) if (mur && !mur.proto)
mur.proto = json.decode(json.encode(ent)); mur.proto = json.decode(json.encode(ent));
@ -784,10 +760,6 @@ game.loadurs = function() {
var topur = file2fqn(file); var topur = file2fqn(file);
topur.data = file; topur.data = file;
} }
ur.empty = {
name: "empty"
};
}; };
return { go_init } return { go_init }

View file

@ -7,7 +7,6 @@ var HIT = {
pos: "Position in world space of the contact.", pos: "Position in world space of the contact.",
depth: "Depth of the contact.", depth: "Depth of the contact.",
}; };
*/ */
var pq = physics.pos_query; var pq = physics.pos_query;
@ -16,10 +15,13 @@ physics.pos_query = function(pos,give) {
pq(pos,give); pq(pos,give);
} }
var bpq = physics.box_point_query;
physics.box_point_query = function(box,points) { physics.box_point_query = function(box,points) {
if (!box || !points) return []; if (!box || !points) return [];
return bpq(box.pos,box.wh,points,points.length) var bbox = bbox.fromcwh(box.pos,box.wh);
var inside = [];
for (var i in points)
if (bbox.pointin(bbox,points[i])) inside.push[i];
return inside;
} }
Object.assign(physics, { Object.assign(physics, {
@ -35,7 +37,7 @@ Object.assign(physics, {
physics.doc = {}; physics.doc = {};
physics.doc.pos_query = "Returns any object colliding with the given point."; physics.doc.pos_query = "Returns any object colliding with the given point.";
physics.doc.box_query = "Returns an array of body ids that collide with a given box."; physics.doc.box_query = "Calls a given function on every shape object in the given bbox.";
physics.doc.box_point_query = "Returns the subset of points from a given list that are inside a given box."; physics.doc.box_point_query = "Returns the subset of points from a given list that are inside a given box.";
physics.collision = { physics.collision = {

View file

@ -32,7 +32,6 @@ audio.music = function(file, fade) {
fade ??= 0; fade ??= 0;
if (!fade) { if (!fade) {
song = audio.play(file); song = audio.play(file);
console.info("STRAIGHT PLAY");
return; return;
} }

View file

@ -69,42 +69,6 @@ Resources.texture.dimensions = function(path) { texture.dimensions(path); }
Resources.gif = {}; Resources.gif = {};
Resources.gif.frames = function(path) { return render.gif_frames(path); } Resources.gif.frames = function(path) { return render.gif_frames(path); }
Resources.replpath = function(str, path)
{
if (!str) return str;
if (str[0] === "/")
return str.rm(0);
if (str[0] === "@")
return os.prefpath() + "/" + str.rm(0);
if (!path) return str;
var stem = path.dir();
while (stem) {
var tr = stem + "/" +str;
if (io.exists(tr)) return tr;
stem = stem.updir();
}
return str;
}
Resources.replstrs = function(path)
{
var script = io.slurp(path);
var regexp = /"[^"\s]*?\.[^"\s]+?"/g;
var stem = path.dir();
script = script.replace(regexp,function(str) {
var newstr = Resources.replpath(str.trimchr('"'), path);
return `"${newstr}"`;
});
return script;
}
/* /*
io path rules. Starts with, meaning: io path rules. Starts with, meaning:
"@": user path "@": user path

View file

@ -103,65 +103,6 @@ gameobject **clean_ids(gameobject **ids)
return ids; return ids;
} }
typedef struct querybox {
cpBB bb;
gameobject **ids;
} querybox;
void querylist(cpShape *shape, cpContactPointSet *points, querybox *qb) {
arrput(qb->ids, shape2go(shape));
}
void querylistbodies(cpBody *body, querybox *qb) {
if (cpBBContainsVect(qb->bb, cpBodyGetPosition(body)))
arrput(qb->ids,body2go(body));
}
/* Return all points from a list of points in the given boundingbox */
int *phys2d_query_box_points(HMM_Vec2 pos, HMM_Vec2 wh, HMM_Vec2 *points, int n) {
cpBB bbox;
bbox = cpBBExpand(bbox, cpvadd(pos.cp, cpvmult(wh.cp,0.5)));
bbox = cpBBExpand(bbox, cpvsub(pos.cp, cpvmult(wh.cp,0.5)));
int *hits = NULL;
for (int i = 0; i < n; i++)
if (cpBBContainsVect(bbox, points[i].cp))
arrpush(hits, i);
return hits;
}
/* Return all gameobjects within the given box */
gameobject **phys2d_query_box(HMM_Vec2 pos, HMM_Vec2 wh) {
cpShape *box = cpBoxShapeNew(NULL, wh.x, wh.y, 0.f);
cpTransform T = {0};
T.a = 1;
T.d = 1;
T.tx = pos.x;
T.ty = pos.y;
cpShapeUpdate(box, T);
cpBB bbox = cpShapeGetBB(box);
querybox qb;
qb.bb = bbox;
qb.ids = NULL;
cpSpaceShapeQuery(space, box, querylist, &qb);
cpSpaceEachBody(space, querylistbodies, &qb);
cpShapeFree(box);
return clean_ids(qb.ids);
}
gameobject **phys2d_query_shape(struct phys2d_shape *shape)
{
gameobject **ids = NULL;
cpSpaceShapeQuery(space, shape->shape, querylist, ids);
return clean_ids(ids);
}
int cpshape_enabled(cpShape *c) { int cpshape_enabled(cpShape *c) {
cpShapeFilter filter = cpShapeGetFilter(c); cpShapeFilter filter = cpShapeGetFilter(c);
if (filter.categories == ~CP_ALL_CATEGORIES && filter.mask == ~CP_ALL_CATEGORIES) if (filter.categories == ~CP_ALL_CATEGORIES && filter.mask == ~CP_ALL_CATEGORIES)

View file

@ -105,7 +105,6 @@ void phys2d_init();
void phys2d_update(float deltaT); void phys2d_update(float deltaT);
cpShape *phys2d_query_pos(cpVect pos); cpShape *phys2d_query_pos(cpVect pos);
void phys2d_query_ray(HMM_Vec2 start, HMM_Vec2 end, float radius, cpShapeFilter filter, JSValue cb); void phys2d_query_ray(HMM_Vec2 start, HMM_Vec2 end, float radius, cpShapeFilter filter, JSValue cb);
gameobject **phys2d_query_box(HMM_Vec2 pos, HMM_Vec2 wh);
struct shape_cb { struct shape_cb {
struct phys2d_shape *shape; struct phys2d_shape *shape;
@ -126,8 +125,6 @@ struct rgba shape_color_s(cpShape *shape);
void shape_gui(struct phys2d_shape *shape); void shape_gui(struct phys2d_shape *shape);
void phys2d_setup_handlers(gameobject *go); void phys2d_setup_handlers(gameobject *go);
gameobject **phys2d_query_shape(struct phys2d_shape *shape);
int *phys2d_query_box_points(HMM_Vec2 pos, HMM_Vec2 wh, HMM_Vec2 *points, int n);
int query_point(HMM_Vec2 pos); int query_point(HMM_Vec2 pos);
void flush_collide_cbs(); void flush_collide_cbs();

View file

@ -701,10 +701,7 @@ JSC_SCALL(os_make_texture,
YughInfo("Made texture with %s", str); YughInfo("Made texture with %s", str);
JS_SetPropertyStr(js, ret, "path", JS_DupValue(js,argv[0])); JS_SetPropertyStr(js, ret, "path", JS_DupValue(js,argv[0]));
) )
JSC_SCALL(os_system, system(str); )
JSC_SCALL(os_system,
system(str);
)
static const JSCFunctionListEntry js_os_funcs[] = { static const JSCFunctionListEntry js_os_funcs[] = {
MIST_FUNC_DEF(os,sprite,1), MIST_FUNC_DEF(os,sprite,1),
@ -1038,34 +1035,12 @@ JSC_CCALL(physics_sgscale,
JSC_CCALL(physics_set_cat_mask, set_cat_mask(js2number(argv[0]), js2bitmask(argv[1]))) JSC_CCALL(physics_set_cat_mask, set_cat_mask(js2number(argv[0]), js2bitmask(argv[1])))
JSC_CCALL(physics_box_query,
int *ids = phys2d_query_box(js2vec2(argv[0]), js2vec2(argv[1]));
JSValue ret = gos2ref(ids);
arrfree(ids);
return ret;
)
JSC_CCALL(physics_pos_query, JSC_CCALL(physics_pos_query,
gameobject *go = pos2gameobject(js2vec2(argv[0]), js2number(argv[1])); gameobject *go = pos2gameobject(js2vec2(argv[0]), js2number(argv[1]));
JSValue ret = go ? JS_DupValue(js,go->ref) : JS_UNDEFINED; JSValue ret = go ? JS_DupValue(js,go->ref) : JS_UNDEFINED;
return ret; return ret;
) )
JSC_CCALL(physics_box_point_query,
void *v = js2cpvec2arr(argv[2]);
int *intids = phys2d_query_box_points(js2vec2(argv[0]), js2vec2(argv[1]), v, js_arrlen(argv[2]));
JSValue ret = ints2js(intids);
arrfree(intids);
return ret;
)
JSC_CCALL(physics_query_shape,
int *ids = phys2d_query_shape(js2ptr(argv[0]));
JSValue ret = gos2ref(ids);
arrfree(ids);
return ret;
)
JSC_CCALL(physics_closest_point, JSC_CCALL(physics_closest_point,
void *v1 = js2cpvec2arr(argv[1]); void *v1 = js2cpvec2arr(argv[1]);
JSValue ret = number2js(point2segindex(js2vec2(argv[0]), v1, js2number(argv[2]))); JSValue ret = number2js(point2segindex(js2vec2(argv[0]), v1, js2number(argv[2])));
@ -1081,6 +1056,36 @@ JSC_CCALL(physics_collide_rm, phys2d_rm_go_handlers(js2gameobject(argv[0])))
JSC_CCALL(physics_collide_separate, js2gameobject(argv[1])->cbs.separate = JS_DupValue(js,argv[0])) JSC_CCALL(physics_collide_separate, js2gameobject(argv[1])->cbs.separate = JS_DupValue(js,argv[0]))
JSC_CCALL(physics_collide_shape, gameobject_add_shape_collider(js2gameobject(argv[1]), JS_DupValue(js,argv[0]), js2ptr(argv[2]))) JSC_CCALL(physics_collide_shape, gameobject_add_shape_collider(js2gameobject(argv[1]), JS_DupValue(js,argv[0]), js2ptr(argv[2])))
void bb_query_fn(cpShape *shape, JSValue *cb)
{
JSValue go = JS_DupValue(js,shape2go(shape)->ref);
script_call_sym(*cb, 1, &go);
JS_FreeValue(js, go);
}
JSC_CCALL(physics_box_query,
HMM_Vec2 pos = js2vec2(argv[0]);
HMM_Vec2 wh = js2vec2(argv[1]);
cpBB bbox;
bbox.l = pos.x-wh.x/2;
bbox.r = pos.x+wh.x/2;
bbox.t = pos.y+wh.y/2;
bbox.b = pos.y-wh.y/2;
cpSpaceBBQuery(space, bbox, allfilter, bb_query_fn, &argv[3]);
return ret;
)
static void shape_query_fn(cpShape *shape, cpContactPointSet *points, JSValue *cb)
{
JSValue go = JS_DupValue(js,shape2go(shape)->ref);
script_call_sym(*cb, 1, &go);
JS_FreeValue(js, go);
}
JSC_CCALL(physics_shape_query,
cpSpaceShapeQuery(space, ((struct phys2d_shape*)js2ptr(argv[0]))->shape, shape_query_fn, &argv[1]);
)
static void ray_query_fn(cpShape *shape, float t, cpVect n, float a, JSValue *cb) static void ray_query_fn(cpShape *shape, float t, cpVect n, float a, JSValue *cb)
{ {
JSValue argv[3] = { JSValue argv[3] = {
@ -1100,11 +1105,10 @@ JSC_CCALL(physics_ray_query,
static const JSCFunctionListEntry js_physics_funcs[] = { static const JSCFunctionListEntry js_physics_funcs[] = {
MIST_FUNC_DEF(physics, sgscale, 2), MIST_FUNC_DEF(physics, sgscale, 2),
MIST_FUNC_DEF(physics, set_cat_mask, 2), MIST_FUNC_DEF(physics, set_cat_mask, 2),
MIST_FUNC_DEF(physics, box_query, 2),
MIST_FUNC_DEF(physics, pos_query, 2), MIST_FUNC_DEF(physics, pos_query, 2),
MIST_FUNC_DEF(physics, ray_query, 2), MIST_FUNC_DEF(physics, ray_query, 2),
MIST_FUNC_DEF(physics, box_point_query, 3), MIST_FUNC_DEF(physics, box_query, 2),
MIST_FUNC_DEF(physics, query_shape, 1), MIST_FUNC_DEF(physics, shape_query, 1),
MIST_FUNC_DEF(physics, closest_point, 3), MIST_FUNC_DEF(physics, closest_point, 3),
MIST_FUNC_DEF(physics, make_damp, 0), MIST_FUNC_DEF(physics, make_damp, 0),
MIST_FUNC_DEF(physics, make_gravity, 0), MIST_FUNC_DEF(physics, make_gravity, 0),
@ -1206,9 +1210,11 @@ static const JSCFunctionListEntry js_window_funcs[] = {
}; };
JSC_GETSET_BODY(pos, Position, cvec2) JSC_GETSET_BODY(pos, Position, cvec2)
JSC_GETSET_BODY(angle, Angle, number) JSValue js_gameobject_set_angle (JSContext *js, JSValue this, JSValue val) { cpBodySetAngle(js2gameobject(this)->body, HMM_TurnToRad*js2number(val)); }
JSValue js_gameobject_get_angle (JSContext *js, JSValue this) { return number2js(HMM_RadToTurn*cpBodyGetAngle(js2gameobject(this)->body)); }
JSC_GETSET_BODY(velocity, Velocity, cvec2) JSC_GETSET_BODY(velocity, Velocity, cvec2)
JSC_GETSET_BODY(angularvelocity, AngularVelocity, number) JSValue js_gameobject_set_angularvelocity (JSContext *js, JSValue this, JSValue val) { cpBodySetAngularVelocity(js2gameobject(this)->body, HMM_TurnToRad*js2number(val)); }
JSValue js_gameobject_get_angularvelocity (JSContext *js, JSValue this) { return number2js(HMM_RadToTurn*cpBodyGetAngularVelocity(js2gameobject(this)->body)); }
JSC_GETSET_BODY(moi, Moment, number) JSC_GETSET_BODY(moi, Moment, number)
JSC_GETSET_BODY(torque, Torque, number) JSC_GETSET_BODY(torque, Torque, number)
JSC_CCALL(gameobject_impulse, cpBodyApplyImpulseAtWorldPoint(js2gameobject(this)->body, js2vec2(argv[0]).cp, cpBodyGetPosition(js2gameobject(this)->body))) JSC_CCALL(gameobject_impulse, cpBodyApplyImpulseAtWorldPoint(js2gameobject(this)->body, js2vec2(argv[0]).cp, cpBodyGetPosition(js2gameobject(this)->body)))

View file

@ -4,7 +4,6 @@
#include "quickjs/quickjs.h" #include "quickjs/quickjs.h"
#include "HandmadeMath.h" #include "HandmadeMath.h"
#define MIST_CFUNC_DEF(name, length, func1) { name, JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE | JS_PROP_ENUMERABLE, JS_DEF_CFUNC, 0, .u = { .func = { length, JS_CFUNC_generic, { .generic = func1 } } } } #define MIST_CFUNC_DEF(name, length, func1) { name, JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE | JS_PROP_ENUMERABLE, JS_DEF_CFUNC, 0, .u = { .func = { length, JS_CFUNC_generic, { .generic = func1 } } } }
#define MIST_FUNC_DEF(TYPE, FN, LEN) MIST_CFUNC_DEF(#FN, LEN, js_##TYPE##_##FN) #define MIST_FUNC_DEF(TYPE, FN, LEN) MIST_CFUNC_DEF(#FN, LEN, js_##TYPE##_##FN)

View file

@ -278,7 +278,6 @@ void sound_fillbuf(struct sound *s, soundbyte *buf, int n) {
if(end) { if(end) {
if (s->loop) if (s->loop)
s->frame = 0; s->frame = 0;
YughInfo("CALLING HOOK");
script_call_sym(s->hook,0,NULL); script_call_sym(s->hook,0,NULL);
} }
} }