...and add it all back.

This commit is contained in:
RHY3756547 committed 2017-09-08 17:22:15 +01:00
1 parent a16ba9a907
commit 3ae7c01030
119 files changed
+41373

No files matched your search

+196
View File
@@ -0,0 +1,196 @@
//
// bowserPlatforms.js
//--------------------
// Provides platforms for Bowser's Castle
// by RHY3756547
//
// includes:
// render stuff idk
//
window.ObjRotaryRoom = function(obji, scene) {
var obji = obji;
var res = [];
var t = this;
t.collidable = true;
t.colMode = 0;
t.colRad = 512;
t.getCollision = getCollision;
t.moveWith = moveWith;
t.pos = vec3.clone(obji.pos);
//t.angle = vec3.clone(obji.angle);
t.scale = vec3.clone(obji.scale);
t.requireRes = requireRes;
t.provideRes = provideRes;
t.update = update;
t.draw = draw;
t.speed = (obji.setting1&0xFFFF)/8192;
t.angle = 0;
var dirVel = 0;
function update(scene) {
dirVel = t.speed;
t.angle += dirVel;
}
function draw(view, pMatrix) {
var mat = mat4.translate(mat4.create(), view, t.pos);
mat4.scale(mat, mat, vec3.scale([], t.scale, 16));
mat4.rotateY(mat, mat, t.angle);
res.mdl[0].draw(mat, pMatrix);
}
function requireRes() { //scene asks what resources to load
return {mdl:[{nsbmd:"rotary_room.nsbmd"}]};
}
function provideRes(r) {
res = r; //...and gives them to us. :)
}
function getCollision() {
var obj = {};
var inf = res.mdl[0].getCollisionModel(0, 0);
obj.tris = inf.dat;
var mat = mat4.translate([], mat4.create(), t.pos);
mat4.scale(mat, mat, vec3.mul([], [16*inf.scale, 16*inf.scale, 16*inf.scale], t.scale));
mat4.rotateY(mat, mat, t.angle);
obj.mat = mat;
return obj;
}
function moveWith(obj) { //used for collidable objects that move.
var p = vec3.sub([], obj.pos, t.pos);
vec3.transformMat4(p, p, mat4.rotateY([], mat4.create(), dirVel));
vec3.add(obj.pos, t.pos, p);
obj.physicalDir -= dirVel;
}
}
window.ObjRoutePlatform = function(obji, scene) {
var obji = obji;
var res = [];
var genCol;
var t = this;
t.collidable = true;
t.colMode = 0;
t.colRad = 512;
t.getCollision = getCollision;
t.moveWith = moveWith;
t.pos = vec3.clone(obji.pos);
//t.angle = vec3.clone(obji.angle);
t.scale = vec3.clone(obji.scale);
t.requireRes = requireRes;
t.provideRes = provideRes;
t.update = update;
t.draw = draw;
generateCol();
t.statDur = (obji.setting1&0xFFFF);
t.route = scene.paths[obji.routeID];
t.routeSpeed = 1/6;
t.routePos = 0;
t.nextNode = t.route[t.routePos];
t.prevPos = t.pos;
t.elapsedTime = 0;
t.mode = 0;
var movVel;
//t.speed = (obji.setting1&0xFFFF)/8192;
function update(scene) {
if (t.mode == 0) {
t.elapsedTime += t.routeSpeed;
movVel = vec3.sub([], t.nextNode.pos, t.prevPos);
//vec3.normalize(movVel, movVel);
vec3.scale(movVel, movVel, t.routeSpeed/t.nextNode.duration);
vec3.add(t.pos, t.pos, movVel);
if (t.elapsedTime >= t.nextNode.duration) {
t.elapsedTime = 0;
t.prevPos = t.nextNode.pos;
t.routePos = (t.routePos+1)%t.route.length;
t.nextNode = t.route[t.routePos];
t.mode = 1;
}
} else {
t.elapsedTime += 1;
movVel = [0, 0, 0];
if (t.elapsedTime > t.statDur) {
t.mode = 0;
t.elapsedTime = 0;
}
}
}
function draw(view, pMatrix) {
var mat = mat4.translate(mat4.create(), view, t.pos);
mat4.scale(mat, mat, vec3.scale([], t.scale, 16));
res.mdl[0].draw(mat, pMatrix);
}
function requireRes() { //scene asks what resources to load
return {mdl:[{nsbmd:"koopa_block.nsbmd"}]}; //25x, 11y
}
function provideRes(r) {
res = r; //...and gives them to us. :)
}
function generateCol() {
genCol = {dat: [
{
Vertex1: [25, 0, 11],
Vertex2: [25, 0, -11],
Vertex3: [-25, 0, -11],
Normal: [0, 1, 0]
},
{
Vertex1: [-25, 0, -11],
Vertex2: [-25, 0, 11],
Vertex3: [25, 0, 11],
Normal: [0, 1, 0]
},
], scale: 1};
}
function getCollision() {
var obj = {};
var inf = genCol;//res.mdl[0].getCollisionModel(0, 0);
obj.tris = inf.dat;
var mat = mat4.translate([], mat4.create(), t.pos);
mat4.scale(mat, mat, vec3.mul([], [16*inf.scale, 16*inf.scale, 16*inf.scale], t.scale));
obj.mat = mat;
return obj;
}
function moveWith(obj) { //used for collidable objects that move.
/*var p = vec3.sub([], obj.pos, t.pos);
vec3.transformMat4(p, p, mat4.rotateY([], mat4.create(), dirVel));
vec3.add(obj.pos, t.pos, p);
obj.physicalDir -= dirVel;*/
vec3.add(obj.pos, obj.pos, movVel);
}
}
+273
View File
@@ -0,0 +1,273 @@
//
// decorations.js
//--------------------
// Provides decoration objects.
// by RHY3756547
//
// includes:
// render stuff idk
//
window.ObjDecor = function(obji, scene) {
var forceBill;
var obji = obji;
var res = [];
var t = this;
t.pos = vec3.clone(obji.pos);
t.angle = vec3.clone(obji.angle);
t.scale = vec3.clone(obji.scale);
t.requireRes = requireRes;
t.provideRes = provideRes;
t.update = update;
t.draw = draw;
var mat = mat4.create();
var frame = 0;
var anim = null;
var animFrame = 0;
var animMat = null;
function draw(view, pMatrix) {
mat4.translate(mat, view, t.pos);
if (t.angle[2] != 0) mat4.rotateZ(mat, mat, t.angle[2]*(Math.PI/180));
if (t.angle[1] != 0) mat4.rotateY(mat, mat, t.angle[1]*(Math.PI/180));
if (t.angle[0] != 0) mat4.rotateX(mat, mat, t.angle[0]*(Math.PI/180));
if (anim != null) {
animMat = anim.setFrame(0, 0, animFrame++);
}
mat4.scale(mat, mat, vec3.scale([], t.scale, 16));
res.mdl[0].draw(mat, pMatrix, animMat);
}
function update() {
}
function requireRes() { //scene asks what resources to load
forceBill = true;
switch (obji.ID) {
case 0x012D:
return {mdl:[{nsbmd:"BeachTree1.nsbmd"}]}; //non solid
case 0x012E:
return {mdl:[{nsbmd:"BeachTree1.nsbmd"}]};
case 0x012F:
return {mdl:[{nsbmd:"earthen_pipe1.nsbmd"}]}; //why is there an earthen pipe 2
case 0x0130:
return {mdl:[{nsbmd:"opa_tree1.nsbmd"}]};
case 0x0131:
return {mdl:[{nsbmd:"OlgPipe1.nsbmd"}]};
case 0x0132:
return {mdl:[{nsbmd:"OlgMush1.nsbmd"}]};
case 0x0133:
return {mdl:[{nsbmd:"of6yoshi1.nsbmd"}]};
case 0x0134:
return {mdl:[{nsbmd:"cow.nsbmd"}], other:[null, null, "cow.nsbtp"]}; //has animation, cow.nsbtp
case 0x0135:
forceBill = false;
return {mdl:[{nsbmd:"NsKiller1.nsbmd"}, {nsbmd:"NsKiller2.nsbmd"}, {nsbmd:"NsKiller2_s.nsbmd"}]}; //probably animates
case 0x0138:
return {mdl:[{nsbmd:"GardenTree1.nsbmd"}]};
case 0x0139:
return {mdl:[{nsbmd:"kamome.nsbmd"}], other:[null, null, "kamone.nsbtp"]}; //animates using nsbtp, and uses route to move
case 0x013A:
return {mdl:[{nsbmd:"CrossTree1.nsbmd"}]};
//0x013C is big cheep cheep
case 0x013C:
forceBill = false;
return {mdl:[{nsbmd:"bakubaku.nsbmd"}]};
//0x013D is spooky ghost
case 0x013D:
forceBill = false;
return {mdl:[{nsbmd:"teresa.nsbmd"}], other:[null, null, "teresa.nsbtp"]};
case 0x013E:
return {mdl:[{nsbmd:"Bank_Tree1.nsbmd"}]};
case 0x013F:
return {mdl:[{nsbmd:"GardenTree1.nsbmd"}]}; //non solid
case 0x0140:
return {mdl:[{nsbmd:"chandelier.nsbmd"}], other:[null, "chandelier.nsbca"]};
case 0x0142:
return {mdl:[{nsbmd:"MarioTree3.nsbmd"}]};
case 0x0145:
return {mdl:[{nsbmd:"TownTree1.nsbmd"}]};
case 0x0146:
//solid
return {mdl:[{nsbmd:"Snow_Tree1.nsbmd"}]};
case 0x0148:
return {mdl:[{nsbmd:"DeTree1.nsbmd"}]};
case 0x0149:
return {mdl:[{nsbmd:"BankEgg1.nsbmd"}]};
case 0x014B:
return {mdl:[{nsbmd:"KinoHouse1.nsbmd"}]};
case 0x014C:
return {mdl:[{nsbmd:"KinoHouse2.nsbmd"}]};
case 0x014D:
return {mdl:[{nsbmd:"KinoMount1.nsbmd"}]};
case 0x014E:
return {mdl:[{nsbmd:"KinoMount2.nsbmd"}]};
case 0x014F:
return {mdl:[{nsbmd:"olaTree1c.nsbmd"}]};
case 0x0150:
return {mdl:[{nsbmd:"osaTree1c.nsbmd"}]};
case 0x0151:
forceBill = false;
return {mdl:[{nsbmd:"picture1.nsbmd"}], other:[null, "picture1.nsbca"]};
case 0x0152:
forceBill = false;
return {mdl:[{nsbmd:"picture2.nsbmd"}], other:[null, "picture2.nsbca"]};
case 0x0153:
return {mdl:[{nsbmd:"om6Tree1.nsbmd"}]};
//0x0154 is rainbow road rotating star
case 0x0154:
forceBill = false;
return {mdl:[{nsbmd:"RainStar.nsbmd"}], other:["RainStar.nsbta"]};
case 0x0155:
return {mdl:[{nsbmd:"Of6Tree1.nsbmd"}]};
case 0x0156:
return {mdl:[{nsbmd:"Of6Tree1.nsbmd"}]};
case 0x0157:
return {mdl:[{nsbmd:"TownMonte.nsbmd"}], other:[null, null, "TownMonte.nsbtp"]}; //pianta!
//debug pianta bridge
case 0x00CC:
forceBill = false;
return {mdl:[{nsbmd:"bridge.nsbmd"}], other:[null, "bridge.nsbca"]};
//debug puddle
case 0x000D:
forceBill = false;
return {mdl:[{nsbmd:"puddle.nsbmd"}]};
//debug airship
case 0x0158:
forceBill = false;
return {mdl:[{nsbmd:"airship.nsbmd"}]};
//need version for 3d objects?
//DEBUG ENEMIES - remove here when implemented.
case 0x0191: //goomba
return {mdl:[{nsbmd:"kuribo.nsbmd"}], other:[null, null, "kuribo.nsbtp"]}; //has nsbtp, route
case 0x0192: //rock
forceBill = false;
return {mdl:[{nsbmd:"rock.nsbmd"}, {nsbmd:"rock_shadow.nsbmd"}]}; //has route
case 0x0193: //thwomp
forceBill = false;
return {mdl:[{nsbmd:"dossun.nsbmd"}, {nsbmd:"dossun_shadow.nsbmd"}]}; //has route
case 0x0196: //chain chomp
forceBill = false;
return {mdl:[{nsbmd:"wanwan.nsbmd"}, {nsbmd:"wanwan_chain.nsbmd"}, {nsbmd:"wanwan_kui.nsbmd"}, {nsbmd:"rock_shadow.nsbmd"}]};
case 0x0198: //bowser castle GBA fireball
return {mdl:[{nsbmd:"mkd_ef_bubble.nsbmd"}]};
case 0x0199: //peach gardens monty
forceBill = false;
return {mdl:[{nsbmd:"choropu.nsbmd"}], other:[null, null, "choropu.nsbtp"]}; //has nsbtp
case 0x019B: //cheep cheep (bouncing)
return {mdl:[{nsbmd:"pukupuku.nsbmd"}], other:[null, null, "pukupuku.nsbtp"]}; //has nsbtp
case 0x019D: //snowman
return {mdl:[{nsbmd:"sman_top.nsbmd"}, {nsbmd:"sman_bottom.nsbmd"}]};
case 0x019E: //trunk with bats
forceBill = false;
return {mdl:[{nsbmd:"kanoke_64.nsbmd"}, {nsbmd:"basabasa.nsbmd"}], other:[null, "kanoke_64.nsbca"]}; //basa has nsbtp
case 0x01A0: //bat
return {mdl:[{nsbmd:"basabasa.nsbmd"}], other:[null, null, "basabasa.nsbtp"]}; //has nsbtp
case 0x01A1: //as fortress cannon
forceBill = false;
return {mdl:[{nsbmd:"NsCannon1.nsbmd"}]};
case 0x01A3: //mansion moving tree
forceBill = false;
return {mdl:[{nsbmd:"move_tree.nsbmd"}], other:[null, "move_tree.nsbca"]}; //has route
case 0x01A4: //flame
forceBill = false;
return {mdl:[{nsbmd:"mkd_ef_burner.nsbmd"}], other:["mkd_ef_burner.nsbta", null]};
case 0x01A5: //chain chomp no base
forceBill = false;
return {mdl:[{nsbmd:"wanwan.nsbmd"}, {nsbmd:"wanwan_chain.nsbmd"}, {nsbmd:"rock_shadow.nsbmd"}]};
case 0x01A6: //plant
return {mdl:[{nsbmd:"ob_pakkun_sf.nsbmd"}], other:[null, null, "ob_pakkun_sf.nsbtp"]}; //has nsbtp
case 0x01A7: //monty airship
forceBill = false;
return {mdl:[{nsbmd:"poo.nsbmd"}, {nsbmd:"cover.nsbmd"}, {nsbmd:"hole.nsbmd"}], other:[null, null, "poo.nsbtp"]}; //poo has nsbtp
case 0x01A8: //bound
forceBill = false;
return {mdl:[{nsbmd:"bound.nsbmd"}], other:[null, null, "bound.nsbtp"]}; //has nsbtp
case 0x01A9: //flipper
forceBill = false;
return {mdl:[{nsbmd:"flipper.nsbmd"}], other:["flipper.nsbta", null, "flipper.nsbtp"]}; //has nsbtp
case 0x01AA: //3d fire plant
forceBill = false;
//note... what exactly is PakkunZHead...
return {mdl:[{nsbmd:"PakkunMouth.nsbmd"}, {nsbmd:"PakkunBody.nsbmd"}, {nsbmd:"FireBall.nsbmd"}], other:[null, "PakkunMouth.nsbca"]};
case 0x01AC: //crab
forceBill = false;
return {mdl:[{nsbmd:"crab.nsbmd"}, {nsbmd:"crab_hand.nsbmd"}], other:[null, null, "crab.nsbtp"]}; //both have nsbtp
case 0x01AD: //desert hills sun
forceBill = false;
return {mdl:[{nsbmd:"sun.nsbmd"}, {nsbmd:"sun_LOD.nsbmd"}]/*, other:[null, "sun.nsbca"]*/}; //exesun animation does not load
case 0x01B0: //routed iron ball
return {mdl:[{nsbmd:"IronBall.nsbmd"}]};
case 0x01B1: //routed choco mountain rock
forceBill = false;
return {mdl:[{nsbmd:"rock2.nsbmd"}]};
case 0x01B2: //sanbo... whatever that is (pokey?) routed
return {mdl:[{nsbmd:"sanbo_h.nsbmd"}, {nsbmd:"sanbo_b.nsbmd"}]};
case 0x01B3: //iron ball
return {mdl:[{nsbmd:"IronBall.nsbmd"}]};
case 0x01B4: //cream
forceBill = false;
return {mdl:[{nsbmd:"cream.nsbmd"}, {nsbmd:"cream_effect.nsbmd"}]};
case 0x01B5: //berry
forceBill = false;
return {mdl:[{nsbmd:"berry.nsbmd"}, {nsbmd:"cream_effect.nsbmd"}]};
}
}
function provideRes(r) {
res = r; //...and gives them to us. :)
if (forceBill) {
t.angle[1] = 0;
var bmd = r.mdl[0].bmd;
bmd.hasBillboards = true;
var models = bmd.modelData.objectData;
for (var i=0; i<models.length; i++) {
var objs = models[i].objects.objectData;
for (var j=0; j<objs.length; j++) {
objs[i].billboardMode = 2;
}
}
}
if (r.other != null) {
if (r.other.length > 0 && r.other[0] != null) {
res.mdl[0].loadTexAnim(r.other[0]);
}
if (r.other.length > 1 && r.other[1] != null)
anim = new nitroAnimator(r.mdl[0].bmd, r.other[1]);
}
}
}
+120
View File
@@ -0,0 +1,120 @@
//
// itembox.js
//--------------------
// Drives and animates itembox entity.
// by RHY3756547
//
window.ItemBox = function(obji, scene) {
var obji = obji;
var res = [];
var t = this;
var anim = 0;
var animFrame = 0;
var animMat;
var frames = 0;
t.soundProps = {};
t.pos = vec3.clone(obji.pos);
//t.angle = vec3.clone(obji.angle);
t.scale = vec3.clone(obji.scale);
t.sndUpdate = sndUpdate;
t.requireRes = requireRes;
t.provideRes = provideRes;
t.update = update;
t.draw = draw;
t.mode = 0;
t.time = 0;
var test = 0;
function update(scene) {
switch (t.mode) {
case 0: //alive
for (var i=0; i<scene.karts.length; i++) {
var ok = scene.karts[i];
var dist = vec3.dist(vec3.add([], t.pos, [0,1,0]), ok.pos);
if (dist < 24) {
var breakSound = nitroAudio.playSound(212, {}, 0, t);
breakSound.gainN.gain.value = 4;
for (var j=0; j<10; j++) {
scene.particles.push(new ItemShard(scene, ok, res.mdl[2]));
}
t.mode = 1;
t.time = 0;
break;
}
}
break;
case 1: //dead
if (t.time++ > 30) {
t.mode = 2;
t.time = 0;
}
break;
case 2: //respawning
if (t.time++ > 30) {
t.mode = 0;
t.time = 0;
}
break;
}
animMat = anim.setFrame(0, 0, animFrame);
animFrame = (animFrame+1)%frames;
}
function draw(view, pMatrix, gl) {
if (t.mode == 0 || t.mode == 2) {
if (t.mode == 2) nitroRender.setColMult([1, 1, 1, t.time/30]);
var mat = mat4.translate(mat4.create(), view, t.pos);
mat4.scale(mat, mat, vec3.scale([], t.scale, 16));
//res.mdl[2].draw(mat, pMatrix);
mat4.translate(mat, mat, [0, 1, 0])
gl.enable(gl.CULL_FACE); //box part
//gl.depthMask(false);
res.mdl[0].drawPoly(mat, pMatrix, 0, 1, animMat);
//gl.depthMask(true);
gl.disable(gl.CULL_FACE);
//question mark part
gl.depthRange(0, 0.99); //hack to push question mark forward in z buffer, causes a few issues with far away boxes though
res.mdl[0].drawPoly(mat, pMatrix, 0, 0, animMat);
gl.depthRange(0, 1);
if (t.mode == 2) nitroRender.setColMult([1, 1, 1, 1]);
}
}
function sndUpdate(view) {
t.soundProps.pos = vec3.transformMat4([], t.pos, view);
if (t.soundProps.lastPos != null) t.soundProps.vel = vec3.sub([], t.soundProps.pos, t.soundProps.lastPos);
else t.soundProps.vel = [0, 0, 0];
t.soundProps.lastPos = t.soundProps.pos;
t.soundProps.refDistance = 192/1024;
t.soundProps.rolloffFactor = 1;
}
function requireRes() { //scene asks what resources to load
return {mdl:[{nsbmd:"itembox.nsbmd"}, {nsbmd:"obj_shadow.nsbmd"}, {nsbmd:"itembox_hahen.nsbmd"}], other:["itembox.nsbca"]};
}
function provideRes(r) {
res = r; //...and gives them to us. :)
anim = new nitroAnimator(r.mdl[0].bmd, r.other[0]);
frames = r.other[0].animData.objectData[0].frames;
animFrame = Math.floor(Math.random()*frames);
animMat = anim.setFrame(0, 0, animFrame);
}
}
+890
View File
@@ -0,0 +1,890 @@
//
// kart.js
//--------------------
// Entity type for karts.
// by RHY3756547
//
// includes: gl-matrix.js (glMatrix 2.0)
// /formats/kcl.js
//
window.Kart = function(pos, angle, speed, kartN, charN, controller, scene) {
var k = this;
var minimumMove = 0.05;
var MAXSPEED = 24;
var BOOSTTIME = 90;
var kartSoundBase = 170;
var COLBOUNCE_TIME = 20;
var COLBOUNCE_STRENGTH = 1;
var params = scene.gameRes.kartPhys.karts[kartN];
var offsets = scene.gameRes.kartOff.karts[kartN];
this.local = controller.local;
this.active = true;
this.preboost = true;
this.soundProps = {};
this.pos = pos;
this.angle = angle;
this.vel = vec3.create();
this.weight = params.weight;
this.params = params;
this.kartBounce = kartBounce;
this.speed = speed;
this.drifting = false;
this.driftMode = 0; //1 left, 2 right, 0 undecided
this.driftLanded = false; //if we haven't landed then apply a constant turn.
//powerslide info: to advance to the next mode you need to hold the same button for 10 or more frames. Mode 0 starts facing drift direction, 1 is other way, 2 is returning (mini spark), 3 is other way, 4 is returning (turbo spark)
this.driftPSTick = 0;
this.driftPSMode = 0;
this.kartTargetNormal = [0, 1, 0];
this.kartNormal = [0, 1, 0];
this.airTime = 0;
this.controller = controller;
this.driftOff = 0;
this.physicalDir = angle;
this.mat = mat4.create();
this.basis = mat4.create();
this.ylock = 0;
this.cannon = null;
this.gravity = [0, -0.17, 0]; //100% confirmed by me messing around with the gravity value in mkds. for sticky surface and loop should modify to face plane until in air
this.update = update;
this.sndUpdate = sndUpdate;
this.draw = draw;
this.drawKart = drawKart;
this.drawWheels = drawWheels;
this.drawChar = drawChar;
this.trackAttach = null; //a normal for the kart to attach to (loop)
this.boostMT = 0;
this.boostNorm = 0;
this.kartColVel = vec3.create();
this.kartColTimer = 0;
var charRes = scene.gameRes.getChar(charN);
var kartRes = scene.gameRes.getKart(kartN);
var kartPolys = [];
var kObj = kartRes.bmd.modelData.objectData[0];
for (var i=0; i<kObj.polys.objectData.length; i++) {
if (kObj.materials.names[kObj.polys.objectData[i].mat] != "kart_tire\0\0\0\0\0\0\0") kartPolys.push(i)
}
var tireRes = scene.gameRes.tireRes;
this.anim = new nitroAnimator(charRes.model.bmd, charRes.driveA);
this.charRes = charRes;
this.animMode = "drive";
this.driveAnimF = 14; //29 frames in total, 14 is mid
this.animFrame = 0; //only used for non drive anim
this.animMat = null;
this.lastInput = null;
//race statistics
this.lapNumber = 0;
this.passedKTP2 = false;
this.checkPointNumber = 0;
var nkm = scene.nkm;
var startLine = nkm.sections["KTPS"].entries[0];
var passLine = nkm.sections["KTP2"].entries[0];
var checkpoints = nkm.sections["CPOI"].entries;
var respawns = nkm.sections["CPOI"].entries;
var futureChecks = [1];
var hitGroundAnim = [ //length 13, on y axis
1.070,
1.130,
1.170,
1.190,
1.2,
1.190,
1.170,
1.130,
1.070,
1,
0.950,
0.920,
0.950,
]
var charGroundAnim = [ //length 13, on y axis
1,
1,
1,
1,
1,
1.080,
1.140,
1.180,
1.140,
1.060,
0.970,
0.960,
0.980,
]
var lastCollided = -1;
var lastBE = -1;
var lastColSounds = {};
var ylvel = 0;
var wheelTurn = 0;
var onGround;
var kartAnim = 0;
var groundAnim = -1;
var stuckTo = null;
var updateMat = true;
var drawMat = {
kart: mat4.create(),
wheels: [mat4.create(), mat4.create(), mat4.create(), mat4.create()],
character: mat4.create()
}
controller.setKart(k);
var soundMode = -1;
var sounds = { //sounds that can be simultaneous
kart: null,
drift: null,
lastTerrain: -1,
lastBE: -1,
drive: null,
boost: null,
powerslide: null,
boostSoundTrig: true, //true if a new boost sound can be played
transpose: 0
}
updateKartSound(0, {turn:0});
function recalcMat(view) {
var mat = mat4.mul([], view, k.mat);
var xscale = 1+Math.cos((kartAnim/4)*Math.PI)*0.015;
var yscale = 1+Math.cos(((kartAnim+4)/4)*Math.PI)*0.015;
if (groundAnim != -1) yscale *= hitGroundAnim[groundAnim];
mat4.translate(mat, mat, [0, -params.colRadius, 0]); //main part
var kmat = mat4.scale(drawMat.kart, mat, [16*xscale, 16*yscale, 16]);
//wheels
for (var i=0; i<4; i++) {
var scale = 16*((i<2)?offsets.frontTireSize:1);
var wmat = mat4.translate(drawMat.wheels[i], mat, [0, 0, 0]);
if (groundAnim != -1) mat4.scale(wmat, wmat, [1, hitGroundAnim[groundAnim], 1]);
mat4.translate(wmat, wmat, offsets.wheels[i]);
mat4.scale(wmat, wmat, [scale, scale, scale]);
if (i<2) mat4.rotateY(wmat, wmat, ((k.driveAnimF-14)/14)*Math.PI/6);
mat4.rotateX(wmat, wmat, wheelTurn);
}
var scale = 16;
var pos = vec3.clone(offsets.chars[charN]);
if (groundAnim != -1) pos[1] *= charGroundAnim[groundAnim];
var cmat = mat4.translate(drawMat.character, mat, vec3.scale([], pos, 16))
mat4.scale(cmat, cmat, [scale, scale, scale]);
if (k.animMode == "drive") k.animMat = k.anim.setFrame(0, 0, k.driveAnimF);
else k.animMat = k.anim.setFrame(0, 0, k.animFrame++);
updateMat = false;
}
function drawChar(view, pMatrix) {
charRes.model.draw(drawMat.character, pMatrix, k.animMat);
}
function drawKart(view, pMatrix, gl) {
if (updateMat) recalcMat(view);
//if we're in simple shadows mode, draw the kart's stencil shadow.
if (false) {
//gl.enable(gl.CULL_FACE); //culling is fun!
gl.clear(gl.STENCIL_BUFFER_BIT);
//gl.cullFace(gl.FRONT);
gl.colorMask(false, false, false, false);
gl.depthMask(false);
gl.enable(gl.STENCIL_TEST);
gl.stencilMask(0xFF);
gl.stencilFunc(gl.ALWAYS, 1, 0xFF);
gl.stencilOp(gl.KEEP, gl.INCR, gl.KEEP);
kartRes.shadVol.draw(drawMat.kart, pMatrix, simpleMatStack);
gl.colorMask(true, true, true, true)
//gl.cullFace(gl.BACK);
gl.stencilFunc(gl.LESS , 0, 0xFF);
gl.stencilOp(gl.KEEP, gl.KEEP, gl.KEEP);
kartRes.shadVol.draw(drawMat.kart, pMatrix, simpleMatStack);
gl.disable(gl.STENCIL_TEST);
//gl.disable(gl.CULL_FACE);
gl.depthMask(true);
}
for (var i=0; i<kartPolys.length; i++) {
kartRes.drawPoly(drawMat.kart, pMatrix, 0, kartPolys[i], simpleMatStack);
}
}
function drawWheels(view, pMatrix) {
if (updateMat) recalcMat(view);
var wheelMod = tireRes[offsets.name];
for (var i=0; i<4; i++) {
wheelMod.draw(drawMat.wheels[i], pMatrix, simpleMatStack);
}
}
function draw(view, pMatrix) {
drawWheels(view, pMatrix);
drawKart(view, pMatrix);
drawChar(view, pMatrix);
}
function update(scene) {
var lastPos = vec3.clone(k.pos);
updateMat = true;
if (groundAnim != -1) {
if (++groundAnim >= hitGroundAnim.length) groundAnim = -1;
}
onGround = (k.airTime < 5);
kartAnim = (kartAnim+1)%8;
var input = k.controller.fetchInput();
k.lastInput = input;
if (input.turn > 0.3) {
if (k.driveAnimF < 28) k.driveAnimF++;
} else if (input.turn < -0.3) {
if (k.driveAnimF > 0) k.driveAnimF--;
} else {
if (k.driveAnimF > 14) k.driveAnimF--;
else if (k.driveAnimF < 14) k.driveAnimF++;
}
//update sounds
var newSoundMode = soundMode;
if (input.accel) {
if (soundMode == 0 || soundMode == 6) newSoundMode = 2;
if (soundMode == 4) newSoundMode = 3;
} else {
if (soundMode != 0) {
if (soundMode == 2 || soundMode == 3) newSoundMode = 4;
if (newSoundMode == 4 && k.speed < 0.5) newSoundMode = 0;
}
}
if (k.boostMT+k.boostNorm > 0) {
if (k.boostNorm == BOOSTTIME || k.boostMT == params.miniTurbo) {
if (sounds.boostSoundTrig) {
if (sounds.boost != null) nitroAudio.instaKill(sounds.boost);
sounds.boost = nitroAudio.playSound(160, {}, 0, k);
sounds.boost.gainN.gain.value = 2;
sounds.boostSoundTrig = false;
}
} else {
sounds.boostSoundTrig = true;
}
} else if (sounds.boost != null) {
nitroAudio.kill(sounds.boost);
sounds.boost = null;
}
if (onGround && k.speed > 0.5) {
if (lastCollided != sounds.lastTerrain || lastBE != sounds.lastBE || sounds.drive == null) {
if (sounds.drive != null) nitroAudio.kill(sounds.drive);
if (lastColSounds.drive != null) {
sounds.drive = nitroAudio.playSound(lastColSounds.drive, {}, 0, k);
sounds.drive.gainN.gain.value = 2;
}
}
if (k.drifting && k.driftLanded) {
if (lastCollided != sounds.lastTerrain || lastBE != sounds.lastBE || sounds.drift == null) {
if (sounds.drift != null) nitroAudio.kill(sounds.drift);
if (lastColSounds.drift != null) {
sounds.drift = nitroAudio.playSound(lastColSounds.drift, {}, 0, k);
}
}
} else if (sounds.drift != null) { nitroAudio.kill(sounds.drift); sounds.drift = null; }
sounds.lastTerrain = lastCollided;
sounds.lastBE = lastBE;
} else {
if (sounds.drift != null) { nitroAudio.kill(sounds.drift); sounds.drift = null; }
if (sounds.drive != null) { nitroAudio.kill(sounds.drive); sounds.drive = null; }
}
//end sound update
if (k.preboost) {
} else if (k.cannon != null) { //when cannon is active, we fly forward at max move speed until we get to the cannon point.
var c = scene.nkm.sections["KTPC"].entries[k.cannon];
var mat = mat4.create();
mat4.rotateY(mat, mat, c.angle[1]*(Math.PI/180));
mat4.rotateX(mat, mat, c.angle[0]*(-Math.PI/180));
var forward = [0, 0, 1];
var up = [0, 1, 0];
k.vel = vec3.scale([], vec3.transformMat4(forward, forward, mat), MAXSPEED);
k.speed = MAXSPEED;
vec3.add(k.pos, k.pos, k.vel);
k.physicalDir = (180-c.angle[1])*(Math.PI/180);
k.angle = k.physicalDir;
k.kartTargetNormal = vec3.transformMat4(up, up, mat);
var planeConst = -vec3.dot(c.pos, forward);
var cannonDist = vec3.dot(k.pos, forward) + planeConst;
if (cannonDist > 0) k.cannon = null;
} else { //default kart mode
var groundEffect = 0;
if (lastCollided != -1) {
groundEffect = MKDS_COLTYPE.PHYS_MAP[lastCollided];
if (groundEffect == null) groundEffect = 0;
}
var effect = params.colParam[groundEffect];
var top = params.topSpeed*effect.topSpeedMul; //if you let go of accel, drift ends anyway, so always accel in drift.
var boosting = (k.boostNorm + k.boostMT)>0;
if (boosting) {
var top2
if (k.boostNorm>0){
top2 = params.topSpeed*1.3;
k.boostNorm--;
} else {
top2 = params.topSpeed*((effect.topSpeedMul >= 1)?1.3:effect.topSpeedMul);
}
if (k.boostMT>0) {
k.boostMT--;
}
if (k.speed <= top2) {
k.speed += 1;
if (k.speed > top2) k.speed = top2;
} else {
k.speed *= 0.95;
}
}
//kart controls
if (k.drifting) {
if ((onGround) && !(input.accel && input.drift && (k.speed > 2 || !k.driftLanded))) {
//end drift, execute miniturbo
k.drifting = false;
if (sounds.powerslide != null) {
nitroAudio.instaKill(sounds.powerslide);
sounds.powerslide = null;
}
if (k.driftPSMode == 3) {
k.boostMT = params.miniTurbo;
}
k.driftPSMode = 0;
k.driftPSTick = 0;
}
if (k.driftMode == 0) {
if (input.turn > 0.30) {
k.driftMode = 2;
} else if (input.turn < -0.30) {
k.driftMode = 1;
}
} else {
if (k.driftLanded) {
var change = (((k.driftMode-1.5)*Math.PI/1.5)-k.driftOff)*0.05;
k.driftOff += change;
k.physicalDir -= change;
}
//if we're above the initial y position, add a constant turn with a period of 180 frames.
if (!k.driftLanded && k.ylock>=0) {
k.physicalDir += (Math.PI*2/180)*(k.driftMode*2-3);
}
}
if (onGround) {
if (!k.driftLanded) {
if (k.driftMode == 0) k.drifting = false;
else {
k.driftPSMode = 0;
k.driftPSTick = 0;
k.driftLanded = true;
}
}
if (k.drifting) {
if (!boosting) {
if (k.speed <= top) {
k.speed += (k.speed/top > params.driftAccelSwitch)?params.driftAccel2:params.driftAccel1;
if (k.speed > top) k.speed = top;
} else {
k.speed *= 0.95;
}
}
var turn = ((k.driftMode == 1)?(input.turn-1):(input.turn+1))/2;
k.physicalDir += params.driftTurnRate*turn+((k.driftMode == 1)?-1:1)*(50/32768)*Math.PI; //what is this mystery number i hear you ask? well my friend, this is the turn rate for outward drift.
//miniturbo code
if (input.turn != 0) {
var inward = ((input.turn>0) == k.driftMode-1); //if we're turning
switch (k.driftPSMode) {
case 0: //dpad away from direction for 10 frames
if (!inward) k.driftPSTick++;
else if (k.driftPSTick > 9) {
k.driftPSMode++;
k.driftPSTick = 1;
//play blue spark sound
var blue = nitroAudio.playSound(210, {}, 0, k);
blue.gainN.gain.value = 2;
} else k.driftPSTick = 0;
break;
case 1: //dpad toward direction for 10 frames
if (inward) k.driftPSTick++;
else if (k.driftPSTick > 9) {
k.driftPSMode++;
k.driftPSTick = 1;
} else k.driftPSTick = 0;
break;
case 2: //dpad away from direction for 10 frames
if (!inward) k.driftPSTick++;
else if (k.driftPSTick > 9) {
k.driftPSMode++;
k.driftPSTick = 1;
//play red sparks sound, full MT!
sounds.powerslide = nitroAudio.playSound(209, {}, 0, k);
sounds.powerslide.gainN.gain.value = 2;
} else k.driftPSTick = 0;
break;
case 3: //turbo charged
break;
}
}
}
}
}
if (!k.drifting) {
if (onGround) {
var effect = params.colParam[groundEffect];
if (!boosting) {
if (input.accel) {
if (k.speed <= top) {
k.speed += (k.speed/top > params.accelSwitch)?params.accel2:params.accel1;
if (k.speed > top) k.speed = top;
} else {
k.speed *= 0.95;
}
} else {
k.speed *= params.decel;
}
}
if ((input.accel && k.speed >= 0) || (k.speed > 0.1)) {
k.physicalDir += params.turnRate*input.turn;
} else if ( k.speed < -0.1) {
k.physicalDir -= params.turnRate*input.turn;
}
if (input.drift) {
ylvel = 1.25;
k.vel[1] += 1.25;
k.airTime = 4;
k.drifting = true;
k.driftLanded = false;
k.driftMode = 0;
k.ylock = 0;
var boing = nitroAudio.playSound(207, {transpose: -4}, 0, k);
boing.gainN.gain.value = 2;
}
} else {
if (input.drift) {
ylvel = 0;
k.drifting = true;
k.driftLanded = false;
k.driftMode = 0;
k.ylock = -0.001;
}
}
}
k.physicalDir = fixDir(k.physicalDir);
if (k.driftOff != 0 && (!k.drifting || !k.driftLanded)) {
if (k.driftOff > 0) {
k.physicalDir += params.driftOffRestore;
k.driftOff -= params.driftOffRestore;
if (k.driftOff < 0) k.driftOff = 0;
} else {
k.physicalDir -= params.driftOffRestore;
k.driftOff += params.driftOffRestore;
if (k.driftOff > 0) k.driftOff = 0;
}
}
checkKartCollision(scene);
if (!onGround) {
this.kartTargetNormal = [0, 1, 0];
vec3.add(k.vel, k.vel, k.gravity)
if (k.ylock >= 0) {
ylvel += k.gravity[1];
k.ylock += ylvel;
}
if (k.kartColTimer == COLBOUNCE_TIME) {
vec3.add(k.vel, k.vel, k.kartColVel);
}
} else {
k.angle += dirDiff(k.physicalDir, k.angle)*effect.handling/2;
k.angle = fixDir(k.physicalDir);
k.vel[1] += k.gravity[1];
k.vel = [Math.sin(k.angle)*k.speed, k.vel[1], -Math.cos(k.angle)*k.speed]
if (k.kartColTimer > 0) {
vec3.add(k.vel, k.vel, vec3.scale([], k.kartColVel, k.kartColTimer/10))
}
}
if (k.kartColTimer > 0) k.kartColTimer--;
wheelTurn += k.speed/16;
wheelTurn = fixDir(wheelTurn);
k.airTime++;
//end kart controls
//move kart on moving platforms (no collision, will be corrected by next step)
if (stuckTo != null) {
if (stuckTo.moveWith != null) stuckTo.moveWith(k);
stuckTo = null;
}
//move kart.
var steps = 0;
var remainingT = 1;
var velSeg = vec3.clone(k.vel);
var posSeg = vec3.clone(k.pos);
var ignoreList = [];
while (steps++ < 10 && remainingT > 0.01) {
var result = lsc.sweepEllipse(posSeg, velSeg, scene, [params.colRadius, params.colRadius, params.colRadius], ignoreList);
if (result != null) {
colResponse(posSeg, velSeg, result, ignoreList)
remainingT -= result.t;
if (remainingT > 0.01) {
velSeg = vec3.scale(vec3.create(), k.vel, remainingT);
}
} else {
vec3.add(posSeg, posSeg, velSeg);
remainingT = 0;
}
}
k.pos = posSeg;
}
//interpolate visual normal roughly to target
var rate = onGround?0.15:0.025;
k.kartNormal[0] += (k.kartTargetNormal[0]-k.kartNormal[0])*rate;
k.kartNormal[1] += (k.kartTargetNormal[1]-k.kartNormal[1])*rate;
k.kartNormal[2] += (k.kartTargetNormal[2]-k.kartNormal[2])*rate;
vec3.normalize(k.kartNormal, k.kartNormal);
k.basis = buildBasis();
var mat = mat4.create();
mat4.translate(mat, mat, k.pos);
k.mat = mat4.mul(mat, mat, k.basis);
if (input.item) {
scene.items.addItem(0, scene.karts.indexOf(k), {})
}
updateKartSound(newSoundMode, input);
positionChanged(lastPos, k.pos);
}
function genFutureChecks() {
//all future points that
var chosen = {}
var current = checkpoints[k.checkPointNumber];
var expectedSection = current.nextSection;
futureChecks = [];
for (var i=k.checkPointNumber+1; i<checkpoints.length; i++) {
var check = checkpoints[i];
if (expectedSection != -1 && check.currentSection != expectedSection) continue;
if (chosen[check.currentSection] != true) {
futureChecks.push(i);
chosen[check.currentSection] = true;
}
}
}
function positionChanged(oldPos, pos) {
//crossed into new checkpoint?
for (var i=0; i<futureChecks.length; i++) {
var check = checkpoints[futureChecks[i]];
var distOld = vec2.sub([], [check.x1, check.z1], [oldPos[0], oldPos[2]]);
var dist = vec2.sub([], [check.x1, check.z1], [pos[0], pos[2]]);
var dot = vec2.dot(dist, [check.sinus, check.cosinus]);
var dotOld = vec2.dot(distOld, [check.sinus, check.cosinus]);
var lineCheck = vec2.sub([], [check.x1, check.z1], [check.x2, check.z2]);
var lineDot = vec2.dot(dist, lineCheck);
if (lineDot > 0 && lineDot < vec2.sqrLen(lineCheck) && dot < 0 && dotOld >= 0) {
k.checkPointNumber = futureChecks[i];
genFutureChecks();
break;
}
}
if (!k.passedKTP2 && forwardCrossedKTP(passLine, oldPos, pos)) k.passedKTP2 = true;
if (k.passedKTP2 && futureChecks.length == 0) {
//we can finish the lap
if (forwardCrossedKTP(startLine, oldPos, pos)) {
k.lapNumber++;
k.checkPointNumber = 0;
k.passedKTP2 = 0;
scene.lapAdvance(k);
}
}
}
function forwardCrossedKTP(ktp, oldPos, pos) {
var distOld = vec2.sub([], [ktp.pos[0], ktp.pos[2]], [oldPos[0], oldPos[2]]);
var dist = vec2.sub([], [ktp.pos[0], ktp.pos[2]], [pos[0], pos[2]]);
var ang = (ktp.angle[1]/180)*Math.PI;
var sinus = Math.sin(ang);
var cosinus = Math.cos(ang);
var dot = vec2.dot(dist, [sinus, cosinus]);
var dotOld = vec2.dot(distOld, [sinus, cosinus]);
return (dot < 0 && dotOld >= 0);
}
function checkKartCollision(scene) { //check collision with other karts. Really simple.
for (var i=0; i<scene.karts.length; i++) {
var ok = scene.karts[i];
if (!ok.active) continue;
if (k != ok) {
var dist = vec3.dist(k.pos, ok.pos);
if (dist < 16) {
kartBounce(ok);
ok.kartBounce(k);
}
}
}
}
function kartBounce(ok) {
k.kartColTimer = COLBOUNCE_TIME;
var weightMul = COLBOUNCE_STRENGTH*(1+(ok.weight-k.weight))*((ok.boostNorm>0 || ok.boostMT>0)?2:1)*((k.boostNorm>0 || k.boostMT>0)?0.5:1);
//as well as side bounce also add velocity difference if other vel > mine.
vec3.sub(k.kartColVel, k.pos, ok.pos);
k.kartColVel[1] = 0;
vec3.normalize(k.kartColVel, k.kartColVel);
vec3.scale(k.kartColVel, k.kartColVel, weightMul);
if (vec3.length(k.vel) < vec3.length(ok.vel)) vec3.add(k.kartColVel, k.kartColVel, vec3.sub([], ok.vel, k.vel));
k.kartColVel[1] = 0;
}
function fixDir(dir) {
return posMod(dir, Math.PI*2);
}
function dirDiff(dir1, dir2) {
var d = fixDir(dir1-dir2);
return (d>Math.PI)?(-2*Math.PI+d):d;
}
function posMod(i, n) {
return (i % n + n) % n;
}
function updateKartSound(mode, input) {
var turn = (onGround && !k.drifting)?(1-Math.abs(input.turn)/11):1;
var transpose = (mode == 0)?0:(22*turn*k.speed/params.topSpeed);
sounds.transpose += (transpose-sounds.transpose)/15;
if (mode != soundMode) {
soundMode = mode;
if (sounds.kart != null) nitroAudio.instaKill(sounds.kart);
sounds.kart = nitroAudio.playSound(kartSoundBase+soundMode, {transpose:sounds.transpose, volume:1}, 0, k);
//if (mode == 3) sounds.kart.gainN.gain.value = 0.5;
} else {
sounds.kart.seq.setTranspose(sounds.transpose);
}
}
function buildBasis() {
//order y, x, z
var dir = k.physicalDir+k.driftOff+(Math.sin((COLBOUNCE_TIME-k.kartColTimer)/3)*(Math.PI/6)*(k.kartColTimer/COLBOUNCE_TIME));
var basis = gramShmidt(k.kartNormal, [Math.cos(dir), 0, Math.sin(dir)], [Math.sin(dir), 0, -Math.cos(dir)]);
var temp = basis[0];
basis[0] = basis[1];
basis[1] = temp; //todo: cleanup
return [
basis[0][0], basis[0][1], basis[0][2], 0,
basis[1][0], basis[1][1], basis[1][2], 0,
basis[2][0], basis[2][1], basis[2][2], 0,
0, 0, 0, 1
]
}
function sndUpdate(view) {
k.soundProps.pos = vec3.transformMat4([], k.pos, view);
if (k.soundProps.lastPos != null) k.soundProps.vel = vec3.sub([], k.soundProps.pos, k.soundProps.lastPos);
else k.soundProps.vel = [0, 0, 0];
k.soundProps.lastPos = k.soundProps.pos;
k.soundProps.refDistance = 192/1024;
k.soundProps.rolloffFactor = 1;
var calcVol = (k.soundProps.refDistance / (k.soundProps.refDistance + k.soundProps.rolloffFactor * (Math.sqrt(vec3.dot(k.soundProps.pos, k.soundProps.pos)) - k.soundProps.refDistance)));
}
function gramShmidt(v1, v2, v3) {
var u1 = v1;
var u2 = vec3.sub([0, 0, 0], v2, project(u1, v2));
var u3 = vec3.sub([0, 0, 0], vec3.sub([0, 0, 0], v3, project(u1, v3)), project(u2, v3));
return [vec3.normalize(u1, u1), vec3.normalize(u2, u2), vec3.normalize(u3, u3)]
}
function colSound(collision, effect) {
if (MKDS_COLTYPE.SOUNDMAP[collision] == null) return {};
return MKDS_COLTYPE.SOUNDMAP[collision][effect] || {};
}
function project(u, v) {
return vec3.scale([], u, (vec3.dot(u, v)/vec3.dot(u, u)))
}
function colResponse(pos, pvel, dat, ignoreList) {
var plane = dat.plane;
var colType = (plane.CollisionType>>8)&31;
var colBE = (plane.CollisionType>>5)&7;
lastCollided = colType;
lastBE = colBE;
lastColSounds = colSound(lastCollided, colBE);
var n = vec3.normalize([], dat.normal);
var gravS = Math.sqrt(vec3.dot(k.gravity, k.gravity));
var angle = Math.acos(vec3.dot(vec3.scale(vec3.create(), k.gravity, -1/gravS), n));
var adjustPos = true;
if (MKDS_COLTYPE.GROUP_WALL.indexOf(colType) != -1) { //wall
//sliding plane, except normal is transformed to be entirely on the xz plane (cannot ride on top of wall, treated as vertical)
var xz = Math.sqrt(n[0]*n[0]+n[2]*n[2])
var adjN = [n[0]/xz, 0, n[2]/xz]
var proj = vec3.dot(k.vel, adjN);
if (proj < -1) {
if (lastColSounds.hit != null) nitroAudio.playSound(lastColSounds.hit, {volume:1}, 0, k)
}
vec3.sub(k.vel, k.vel, vec3.scale(vec3.create(), adjN, proj));
//convert back to angle + speed to keep change to kart vel
var v = k.vel;
k.speed = Math.sqrt(v[0]*v[0]+v[2]*v[2]);
k.angle = Math.atan2(v[0], -v[2]);
} else if (MKDS_COLTYPE.GROUP_ROAD.indexOf(colType) != -1) {
//sliding plane
if (MKDS_COLTYPE.GROUP_BOOST.indexOf(colType) != -1) {
k.boostNorm = BOOSTTIME;
}
if (k.vel[1] > 0) k.vel[1] = 0;
var proj = vec3.dot(k.vel, n);
if (proj < -4 && k.vel[1] < -2) { proj -= 1.5; }
vec3.sub(k.vel, k.vel, vec3.scale(vec3.create(), n, proj));
k.kartTargetNormal = dat.pNormal;
if (!onGround) {
console.log("ground: "+colType+", "+colBE);
groundAnim = 0;
if (lastColSounds.land != null) nitroAudio.playSound(lastColSounds.land, {volume:1}, 0, k)
}
k.airTime = 0;
stuckTo = dat.object;
} else if (colType == MKDS_COLTYPE.CANNON) {
//cannon!!
k.cannon = colBE;
} else {
adjustPos = false;
ignoreList.push(plane);
}
//vec3.add(pos, pos, vec3.scale(vec3.create(), n, minimumMove)); //move away from plane slightly
if (adjustPos) { //move back from plane slightly
//vec3.add(pos, pos, vec3.scale(vec3.create(), n, minimumMove));
vec3.add(pos, pos, vec3.scale(vec3.create(), pvel, dat.t));
vec3.add(pos, vec3.scale([], n, params.colRadius+minimumMove), dat.colPoint);
/*if (dat.embedded) {
} else {
var velMag = Math.sqrt(vec3.dot(pvel, pvel));
if (velMag*dat.t > minimumMove) {
vec3.add(pos, pos, vec3.scale(vec3.create(), pvel, dat.t-(minimumMove/velMag)));
} else {
//do not move, too close
}
}*/
} else {
vec3.add(pos, pos, vec3.scale(vec3.create(), pvel, dat.t));
}
}
}
+119
View File
@@ -0,0 +1,119 @@
//
// objDatabase.js
//--------------------
// Links object IDs to specific entity types. Must be initialized after all js files are loaded!
// by RHY3756547
//
// includes:
// entities/*
//
window.objDatabase = new (function(){
this.init = function() {
this.idToType = [];
var t = this.idToType;
t[0x0001] = ObjWater;
t[0x0003] = ObjWater;
t[0x0006] = ObjWater;
t[0x0008] = ObjSoundMaker;
t[0x0009] = ObjWater;
t[0x000C] = ObjWater;
t[0x0065] = ItemBox;
t[0x00CA] = ObjRoutePlatform;
t[0x00CB] = ObjGear;
t[0x00CE] = ObjGear; //test_cylinder, tick tock clock end
t[0x00D0] = ObjRotaryRoom;
t[0x00D1] = ObjGear; //rotary bridge
t[0x012D] = ObjDecor;
t[0x012E] = ObjDecor;
t[0x012F] = ObjDecor;
t[0x0130] = ObjDecor;
t[0x0131] = ObjDecor;
t[0x0132] = ObjDecor;
t[0x0133] = ObjDecor;
t[0x0134] = ObjDecor;
t[0x0135] = ObjDecor;
t[0x0138] = ObjDecor;
t[0x0139] = ObjDecor;
t[0x013C] = ObjDecor; //DEBUG: cheep cheep (routed)
t[0x013D] = ObjDecor; //DEBUG: ghost
t[0x013A] = ObjDecor; //figure 8 tree
t[0x013C] = ObjDecor;
t[0x013F] = ObjDecor;
t[0x0140] = ObjDecor;
t[0x0142] = ObjDecor; //more trees
t[0x0145] = ObjDecor;
t[0x0146] = ObjDecor;
t[0x0148] = ObjDecor;
t[0x0149] = ObjDecor; //yoshi falls egg
t[0x014B] = ObjDecor;
t[0x014C] = ObjDecor;
t[0x014D] = ObjDecor;
t[0x014E] = ObjDecor;
t[0x014F] = ObjDecor;
t[0x0150] = ObjDecor;
t[0x0151] = ObjDecor;
t[0x0152] = ObjDecor;
t[0x0153] = ObjDecor;
t[0x0154] = ObjDecor; //rainbow star
t[0x0155] = ObjDecor;
t[0x0156] = ObjDecor;
t[0x0157] = ObjDecor;
t[0x019C] = ObjTruck;
t[0x019A] = ObjCar;
t[0x0195] = ObjBus;
t[0x00CC] = ObjDecor; //DEBUG: pianta bridge
t[0x000D] = ObjDecor; //DEBUG: puddle
t[0x0158] = ObjDecor; //DEBUG: airship (routed)
//DEBUG ENEMIES AS DECOR: switch as implemented:
t[0x0191] = ObjDecor;
t[0x0192] = ObjDecor;
t[0x0193] = ObjDecor;
t[0x0196] = ObjDecor;
t[0x0198] = ObjDecor;
t[0x0199] = ObjDecor;
//truck
t[0x019B] = ObjDecor;
t[0x019D] = ObjDecor;
t[0x019E] = ObjDecor;
t[0x01A0] = ObjDecor;
t[0x01A1] = ObjDecor;
t[0x01A3] = ObjDecor;
t[0x01A4] = ObjDecor;
t[0x01A5] = ObjDecor;
t[0x01A6] = ObjDecor;
t[0x01A7] = ObjDecor;
t[0x01A8] = ObjDecor;
t[0x01A9] = ObjDecor;
t[0x01AA] = ObjDecor;
t[0x01AC] = ObjDecor;
t[0x01AD] = ObjDecor;
//rotating fireballs
t[0x01B0] = ObjDecor;
t[0x01B1] = ObjDecor;
t[0x01B2] = ObjDecor;
t[0x01B3] = ObjDecor;
t[0x01B4] = ObjDecor;
t[0x01B5] = ObjDecor;
}
})();
+161
View File
@@ -0,0 +1,161 @@
//
// rotatingGear.js
//--------------------
// Provides rotating gear objects for tick tock clock
// by RHY3756547
//
// includes:
// render stuff idk
//
window.ObjGear = function(obji, scene) {
var obji = obji;
var res = [];
var t = this;
t.collidable = true;
t.colMode = 0;
t.colRad = 512;
t.getCollision = getCollision;
t.moveWith = moveWith;
t.pos = vec3.clone(obji.pos);
//t.angle = vec3.clone(obji.angle);
t.scale = vec3.clone(obji.scale);
t.requireRes = requireRes;
t.provideRes = provideRes;
t.update = update;
t.draw = draw;
t.speed = (obji.setting1&0xFFFF)/8192;
t.duration = obji.setting1>>16;
t.rampDur = obji.setting2&0xFFFF;
t.statDur = obji.setting2>>16;
t.wB1 = obji.setting3&0xFFFF; //ONE of these flips direction, the other makes the gear use the black model. Not sure which is which, but for tick tock clock there is no need to get this right.
t.wB2 = obji.setting3>>16;
t.time = 0;
t.mode = 0; //0=rampup, 1=normal, 2=rampdown, 3=stationary
t.angle = 0;
t.dir = (t.wB1 == 0)
var dirVel = 0;
var prevMat;
var curMat;
setMat();
prevMat = curMat;
function setMat() {
prevMat = curMat;
var mat = mat4.create();
mat4.translate(mat, mat, t.pos);
mat4.scale(mat, mat, vec3.scale([], t.scale, 16));
mat4.rotateY(mat, mat, obji.angle[1]*(Math.PI/180));
mat4.rotateX(mat, mat, obji.angle[0]*(Math.PI/180));
mat4.rotateY(mat, mat, t.angle);
curMat = mat;
}
function update(scene) {
t.time++;
switch (t.mode) {
case 0:
dirVel = t.speed*(t.time/t.rampDur)*((t.dir)?-1:1);
if (t.time > t.rampDur) {
t.time = 0; t.mode = 1;
}
break;
case 1:
dirVel = t.speed*((t.dir)?-1:1);
if (t.time > t.duration) {
t.time = 0; t.mode = 2;
}
break;
case 2:
dirVel = t.speed*(1-t.time/t.rampDur)*((t.dir)?-1:1);
if (t.time > t.rampDur) {
t.time = 0; t.mode = 3; t.dir = !t.dir;
}
break;
case 3:
dirVel = 0;
if (t.time > t.statDur) {
t.time = 0; t.mode = 0;
}
break;
}
t.angle += dirVel;
setMat();
}
function draw(view, pMatrix) {
var mat = mat4.translate(mat4.create(), view, t.pos);
mat4.scale(mat, mat, vec3.scale([], t.scale, 16));
mat4.rotateY(mat, mat, obji.angle[1]*(Math.PI/180));
mat4.rotateX(mat, mat, obji.angle[0]*(Math.PI/180));
mat4.rotateY(mat, mat, t.angle);
res.mdl[t.wB1].draw(mat, pMatrix);
}
function requireRes() { //scene asks what resources to load
switch (obji.ID) {
case 0x00CB:
return {mdl:[{nsbmd:"gear_white.nsbmd"}, {nsbmd:"gear_black.nsbmd"}]};
case 0x00CE:
return {mdl:[{nsbmd:"test_cylinder.nsbmd"}]};
case 0x00D1:
t.colRad = 4096;
return {mdl:[{nsbmd:"rotary_bridge.nsbmd"}]};
}
}
function provideRes(r) {
res = r; //...and gives them to us. :)
}
function getCollision() {
var obj = {};
var inf = res.mdl[0].getCollisionModel(0, 0);
obj.tris = inf.dat;
var mat = mat4.translate([], mat4.create(), t.pos);
mat4.scale(mat, mat, vec3.mul([], [16*inf.scale, 16*inf.scale, 16*inf.scale], t.scale));
mat4.rotateY(mat, mat, obji.angle[1]*(Math.PI/180));
mat4.rotateX(mat, mat, obji.angle[0]*(Math.PI/180));
mat4.rotateY(mat, mat, t.angle);
obj.mat = mat;
return obj;
}
function moveWith(obj) { //used for collidable objects that move.
//the most general way to move something with an object is to multiply its position by the inverse mv matrix of that object, and then the new mv matrix.
vec3.transformMat4(obj.pos, obj.pos, mat4.invert([], prevMat))
vec3.transformMat4(obj.pos, obj.pos, curMat)
/*var p = vec3.sub([], obj.pos, t.pos);
if (obji.ID == 0x00D1) { //todo: maybe something more general
vec3.transformMat4(p, p, mat4.rotateX([], mat4.create(), dirVel));
vec3.add(obj.pos, t.pos, p);
} else {
vec3.transformMat4(p, p, mat4.rotateY([], mat4.create(), dirVel));
vec3.add(obj.pos, t.pos, p);
obj.physicalDir -= dirVel;
}*/
}
}
+118
View File
@@ -0,0 +1,118 @@
//
// shell.js
//--------------------
// Entity type for shells. (green)
// by RHY3756547
//
// includes: gl-matrix.js (glMatrix 2.0)
// /formats/kcl.js
//
window.GreenShell = function(scene, owner, time, itemID, cliID, params) {
var t = this;
var minimumMove = 0.01;
this.pos = vec3.transformMat4([], [0, (-owner.params.colRadius)+1, 16], owner.mat);
this.vel = vec3.create();
this.gravity = [0, -0.17, 0]; //100% confirmed by me messing around with the gravity value in mkds
this.angle = owner.angle;
this.speed = 10;
this.yvel = 0;
this.update = update;
this.draw = draw;
function update(scene) {
t.vel = [Math.sin(t.angle)*t.speed, t.yvel, -Math.cos(t.angle)*t.speed]
vec3.add(t.vel, t.vel, t.gravity);
//simple point move.
var steps = 0;
var remainingT = 1;
var velSeg = vec3.clone(t.vel);
var posSeg = vec3.clone(t.pos);
var ignoreList = [];
while (steps++ < 10 && remainingT > 0.01) {
var result = lsc.raycast(posSeg, velSeg, scene.kcl, 0.05, ignoreList);
if (result != null) {
colResponse(posSeg, velSeg, result, ignoreList)
remainingT -= result.t;
if (remainingT > 0.01) {
velSeg = vec3.scale(vec3.create(), t.vel, remainingT);
}
} else {
vec3.add(posSeg, posSeg, velSeg);
remainingT = 0;
}
}
t.pos = posSeg;
t.yvel = t.vel[1];
}
function draw(mvMatrix, pMatrix) {
var mat = mat4.translate(mat4.create(), mvMatrix, vec3.add(vec3.create(), t.pos, [0, 3, 0]));
spritify(mat);
mat4.scale(mat, mat, [16, 16, 16]);
scene.gameRes.items.koura_g.draw(mat, pMatrix);
}
var spritify = function(mat, scale) {
var scale = (scale == null)?Math.sqrt(mat[0]*mat[0]+mat[1]*mat[1]+mat[2]*mat[2]):scale;
mat[0]=scale; mat[1]=0; mat[2]=0;
mat[4]=0; mat[5]=scale; mat[6]=0;
mat[8]=0; mat[9]=0; mat[10]=scale;
}
function colResponse(pos, pvel, dat, ignoreList) {
var plane = dat.plane;
var colType = (plane.CollisionType>>8)&31;
vec3.add(pos, pos, vec3.scale(vec3.create(), pvel, dat.t));
var n = dat.normal;
vec3.normalize(n, n);
var gravS = Math.sqrt(vec3.dot(t.gravity, t.gravity));
var angle = Math.acos(vec3.dot(vec3.scale(vec3.create(), t.gravity, -1/gravS), n));
var adjustPos = true
if (MKDS_COLTYPE.GROUP_WALL.indexOf(colType) != -1) { //wall
//shell reflection code - slide y vel across plane, bounce on xz
vec3.add(t.vel, vec3.scale(vec3.create(), n, -2*(vec3.dot(t.vel, n)/vec3.dot(n,n))), t.vel);
t.vel[1] = 0;
var v = t.vel;
t.angle = Math.atan2(v[0], -v[2]);
} else if (MKDS_COLTYPE.GROUP_ROAD.indexOf(colType) != -1) {
//sliding plane
var proj = vec3.dot(t.vel, n);
vec3.sub(t.vel, t.vel, vec3.scale(vec3.create(), n, proj));
} else {
adjustPos = false;
ignoreList.push(plane);
}
var rVelMag = Math.sqrt(vec3.dot(t.vel, t.vel));
vec3.scale(t.vel, t.vel, t.speed/rVelMag); //force speed to shell speed for green shells.
//vec3.add(pos, pos, vec3.scale(vec3.create(), n, minimumMove)); //move away from plane slightly
if (adjustPos) { //move back from plane slightly
vec3.add(pos, pos, vec3.scale(vec3.create(), n, minimumMove));
/*
var velMag = Math.sqrt(vec3.dot(pvel, pvel));
if (velMag*dat.t > minimumMove) {
vec3.sub(pos, pos, vec3.scale(vec3.create(), pvel, minimumMove/velMag)); //move back slightly after moving
} else {
vec3.sub(pos, pos, vec3.scale(vec3.create(), pvel, dat.t)); //if we're already too close undo the movement.
}
*/
}
}
}
+77
View File
@@ -0,0 +1,77 @@
//
// soundMaker.js
//--------------------
// Provides env sound object, such as crowd for figure 8
// by RHY3756547
//
//0008
window.ObjSoundMaker = function(obji, scene) {
var obji = obji;
var t = this;
t.pos = vec3.clone(obji.pos);
t.soundProps = {};
t.sndUpdate = sndUpdate;
t.requireRes = requireRes;
t.provideRes = provideRes;
t.update = update;
t.draw = draw;
var mat = mat4.create();
var frame = 0;
var sound = null;
var sN = 0;
var threshold = 0.2;
var gain = 1;
switch (obji.ID) {
case 0x0008:
sN = 259;
gain = 2;
threshold = 0.2;
break;
}
function draw(view, pMatrix) {
}
function update() {
}
function sndUpdate(view) {
t.soundProps.pos = vec3.transformMat4([], t.pos, view);
t.soundProps.pos = [0, 0, Math.sqrt(vec3.dot(t.soundProps.pos, t.soundProps.pos))]
//if (t.soundProps.lastPos != null) t.soundProps.vel = vec3.sub([], t.soundProps.pos, t.soundProps.lastPos);
//else t.soundProps.vel = [0, 0, 0];
//t.soundProps.lastPos = t.soundProps.pos;
t.soundProps.refDistance = 1024/1024;
//t.soundProps.rolloffFactor = 1;
var calcVol = (t.soundProps.refDistance / (t.soundProps.refDistance + t.soundProps.rolloffFactor * (t.soundProps.pos[2] - t.soundProps.refDistance)));
if (calcVol<threshold) {
if (sound != null) {
nitroAudio.instaKill(sound);
sound = null;
}
} else {
if (sound == null) {
sound = nitroAudio.playSound(sN, {}, 0, t);
sound.gainN.gain.value = gain;
}
}
}
function requireRes() {
return {mdl: []};
}
function provideRes(r) { }
}
+94
View File
@@ -0,0 +1,94 @@
//
// trafficCar.js
//--------------------
// Provides multiple types of traffic.
// by RHY3756547
//
// includes:
// render stuff idk
//
window.ObjTruck = function(obji, scene) {
var obji = obji;
var res = [];
var t = this;
t.pos = vec3.clone(obji.pos);
//t.angle = vec3.clone(obji.angle);
t.scale = vec3.clone(obji.scale);
t.requireRes = requireRes;
t.provideRes = provideRes;
t.update = update;
t.draw = draw;
t.route = scene.paths[obji.routeID];
t.routeSpeed = (obji.setting1>>16)/100;
t.routePos = (obji.setting1&0xFFFF)%t.route.length;
t.nextNode = t.route[t.routePos];
t.prevPos = t.pos;
t.elapsedTime = 0;
var facingNormal = [0, 1, 0];
var curNormal = [0, 1, 0];
var floorNormal = [0, 1, 0];
function update(scene) {
//simple behaviour, just follow the path! piece of cake.
t.elapsedTime += t.routeSpeed;
t.pos = vec3.lerp([], t.prevPos, t.nextNode.pos, t.elapsedTime/t.nextNode.duration);
if (t.elapsedTime >= t.nextNode.duration) {
t.elapsedTime = 0;
t.prevPos = t.nextNode.pos;
t.routePos = (t.routePos+1)%t.route.length;
t.nextNode = t.route[t.routePos];
}
facingNormal = vec3.sub([], t.prevPos, t.nextNode.pos)
vec3.normalize(facingNormal, facingNormal);
var rate = 0.025
curNormal[0] += (facingNormal[0]-curNormal[0])*rate;
curNormal[1] += (facingNormal[1]-curNormal[1])*rate;
curNormal[2] += (facingNormal[2]-curNormal[2])*rate;
vec3.normalize(curNormal, curNormal);
var spos = vec3.clone(t.pos);
spos[1] += 32;
var result = lsc.raycast(spos, [0, -100, 0], scene.kcl, 0.05, []);
if (result != null) {
floorNormal = result.normal;
} else {
floorNormal = [0,1,0];
}
}
function draw(view, pMatrix) {
var mat = mat4.translate(mat4.create(), view, t.pos);
mat4.scale(mat, mat, vec3.scale([], t.scale, 16));
mat4.mul(mat, mat, mat4.invert([], mat4.lookAt([], [0, 0, 0], curNormal, floorNormal)));
res.mdl[0].draw(mat, pMatrix);
}
function requireRes() { //scene asks what resources to load
switch (obji.ID) {
case 0x019A:
return {mdl:[{nsbmd:"car_a.nsbmd"}]}; //one model, car
case 0x019C:
return {mdl:[{nsbmd:"truck_a.nsbmd"}]}; //one model, truck
case 0x0195:
return {mdl:[{nsbmd:"bus_a.nsbmd"}]}; //one model, bus
}
}
function provideRes(r) {
res = r; //...and gives them to us. :)
}
}
window.ObjCar = ObjTruck;
window.ObjBus = ObjTruck;
+86
View File
@@ -0,0 +1,86 @@
//
// water.js
//--------------------
// Provides multiple types of traffic.
// by RHY3756547
//
// includes:
// render stuff idk
//
window.ObjWater = function(obji, scene) {
var obji = obji;
var res = [];
var t = this;
t.pos = vec3.clone(obji.pos);
//t.angle = vec3.clone(obji.angle);
t.scale = vec3.clone(obji.scale);
t.requireRes = requireRes;
t.provideRes = provideRes;
t.update = update;
t.draw = draw;
var frame = 0;
function draw(view, pMatrix) {
if (nitroRender.flagShadow) return;
var waterM = mat4.create();
gl.enable(gl.STENCIL_TEST);
gl.stencilMask(0xFF);
gl.stencilFunc(gl.ALWAYS, 1, 0xFF);
gl.stencilOp(gl.KEEP, gl.KEEP, gl.REPLACE); //when depth test passes for water lower layer, pixel is already drawn, do not cover it with the white overlay (set stencil bit)
var height = (t.pos[1])+6.144+Math.sin(frame/150)*12.288 //0.106
mat4.translate(waterM, view, [Math.sin(frame/180)*96, height-3.072, Math.cos(frame/146)*96])
nitroRender.setAlpha(0x0A/31);
res.mdl[0].drawPoly(mat4.scale([], waterM, [16, 16, 16]), pMatrix, 0, 0); //water
if (res.mdl[1] != null) {
mat4.translate(waterM, view, [-Math.sin((frame+30)/180)*96, height-3, Math.cos((frame+100)/146)*96])
nitroRender.setAlpha(0x02/31);
res.mdl[1].draw(mat4.scale([], waterM, [16, 16, 16]), pMatrix); //water white detail part. stencil should do nothing here, since it's in the same position as the above.
}
gl.stencilFunc(gl.EQUAL, 0, 0xFF);
gl.stencilOp(gl.KEEP, gl.KEEP, gl.KEEP);
if (!obji.ID == 9) {
mat4.translate(waterM, view, [0, height, 0])
nitroRender.setAlpha(0x10/31);
res.mdl[0].drawPoly(mat4.scale([], waterM, [16, 16, 16]), pMatrix, 0, 1); //white shore wash part, water is stencil masked out
}
gl.disable(gl.STENCIL_TEST);
nitroRender.setAlpha(1);
}
function update() {
frame = (frame+1)%197100; //it's a big number but yolo... we have the technology...
}
function requireRes() { //scene asks what resources to load
switch (obji.ID) {
case 0x0001:
return {mdl:[{nsbmd:"beach_waterC.nsbmd"}, {nsbmd:"beach_waterA.nsbmd"}]};
case 0x0003:
return {mdl:[{nsbmd:"town_waterC.nsbmd"}, {nsbmd:"town_waterA.nsbmd"}]};
case 0x0006:
return {mdl:[{nsbmd:"yoshi_waterC.nsbmd"}]};
case 0x0009:
return {mdl:[{nsbmd:"hyudoro_waterC.nsbmd"}, {nsbmd:"hyudoro_waterA.nsbmd"}]};
case 0x000C:
return {mdl:[{nsbmd:"mini_stage3_waterC.nsbmd"}, {nsbmd:"mini_stage3_waterA.nsbmd"}]};
}
}
function provideRes(r) {
res = r; //...and gives them to us. :)
}
}