diff --git a/.gitignore b/.gitignore
index 6586752..d390d10 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,4 +1,5 @@
-*.nds
-*.exe
-mongoose.conf
+*.nds
+*.exe
+*.sdat
+mongoose.conf
Code/Engine/col.lua
\ No newline at end of file
diff --git a/audio.html b/audio.html
index 9474d8b..3cb1432 100644
--- a/audio.html
+++ b/audio.html
@@ -1,80 +1,80 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-Current SSEQ: 0
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Current SSEQ: 0
+
\ No newline at end of file
diff --git a/audioSFX.html b/audioSFX.html
index 288d7f1..446fb6a 100644
--- a/audioSFX.html
+++ b/audioSFX.html
@@ -1,81 +1,81 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-Current SSEQ: 0
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Current SSEQ: 0
+
\ No newline at end of file
diff --git a/code/engine/cameras/cameraIngame.js b/code/engine/cameras/cameraIngame.js
index b28562c..85d3f94 100644
--- a/code/engine/cameras/cameraIngame.js
+++ b/code/engine/cameras/cameraIngame.js
@@ -20,35 +20,48 @@ window.cameraIngame = function(kart) {
var lookAtOffset = [0, 16, 0]
var camNormal = [0, 1, 0];
+ var forwardNormal = null;
var camAngle = 0;
var boostOff = 0;
+ function tweenVec3(from, to) {
+ from[0] += (to[0]-from[0])*0.075;
+ from[1] += (to[1]-from[1])*0.075;
+ from[2] += (to[2]-from[2])*0.075;
+ }
+
function getView(scene) {
+ var loop = kart.physBasis != null && kart.physBasis.loop;
var basis = buildBasis();
+ tweenVec3(camOffset, loop ? [0, 12, -57] : [0, 32, -48]);
var camPos = vec3.transformMat4([], camOffset, basis);
var lookAtPos = vec3.transformMat4([], lookAtOffset, basis);
vec3.scale(camPos, camPos, 1/1024);
vec3.scale(lookAtPos, lookAtPos, 1/1024);
- var mat = mat4.lookAt(mat4.create(), camPos, lookAtPos, [0, 1, 0]);
+ var mat = mat4.lookAt(mat4.create(), camPos, lookAtPos, (kart.physBasis) ? camNormal : [0, 1, 0]);
var kpos = vec3.clone(kart.pos);
if (kart.drifting && !kart.driftLanded && kart.ylock>0) kpos[1] -= kart.ylock;
mat4.translate(mat, mat, vec3.scale([], kpos, -1/1024));
//interpolate visual normal roughly to target
- camNormal[0] += (kart.kartNormal[0]-camNormal[0])*0.075;
- camNormal[1] += (kart.kartNormal[1]-camNormal[1])*0.075;
- camNormal[2] += (kart.kartNormal[2]-camNormal[2])*0.075;
+ tweenVec3(camNormal, kart.kartNormal);
vec3.normalize(camNormal, camNormal);
- if (kart.physBasis != null) {
+ if (loop) {
var kartA = kart.physicalDir+kart.driftOff/2;
var forward = [Math.sin(kartA), 0, -Math.cos(kartA)];
vec3.transformMat4(forward, forward, kart.physBasis.mat);
camAngle += dirDiff(Math.atan2(forward[0], -forward[2]), camAngle)*0.075;
+ if (forwardNormal == null) {
+ forwardNormal = [Math.sin(camAngle), 0, -Math.cos(camAngle)];
+ } else {
+ tweenVec3(forwardNormal, forward);
+ }
} else {
camAngle += dirDiff(kart.physicalDir+kart.driftOff/2, camAngle)*0.075;
+ forwardNormal = null;
}
camAngle = fixDir(camAngle);
@@ -67,8 +80,8 @@ window.cameraIngame = function(kart) {
function buildBasis() {
//order y, x, z
var kart = thisObj.kart;
- var forward = [Math.sin(camAngle), 0, -Math.cos(camAngle)];
- var side = [Math.cos(camAngle), 0, Math.sin(camAngle)];
+ var forward = (forwardNormal != null) ? forwardNormal : [Math.sin(camAngle), 0, -Math.cos(camAngle)];
+ var side = vec3.cross([], forward, camNormal);
/*
if (kart.physBasis != null) {
vec3.transformMat4(forward, forward, kart.physBasis.mat);
diff --git a/code/engine/ingameRes.js b/code/engine/ingameRes.js
index 05de045..205a937 100644
--- a/code/engine/ingameRes.js
+++ b/code/engine/ingameRes.js
@@ -1,108 +1,109 @@
-//
-// ingameRes.js
-//--------------------
-// Provides access to general ingame resources.
-// by RHY3756547
-//
-
-window.IngameRes = function(rom) {
- var r = this;
- this.kartPhys = new kartphysicalparam(rom.getFile("/data/KartModelMenu/kartphysicalparam.bin"));
- this.kartOff = new kartoffsetdata(rom.getFile("/data/KartModelMenu/kartoffsetdata.bin"));
- this.MapObj = new narc(lz77.decompress(rom.getFile("/data/Main/MapObj.carc"))); //contains generic map obj, look in here when mapobj res is missing from course. (itembox etc)
- this.MainRace = new narc(lz77.decompress(rom.getFile("/data/MainRace.carc"))); //contains item models.
- this.MainEffect = new narc(lz77.decompress(rom.getFile("/data/MainEffect.carc"))); //contains particles.
- this.Main2D = new narc(lz77.decompress(rom.getFile("/data/Main2D.carc")));
-
- this.KartModelSub = new narc(lz77.decompress(rom.getFile("/data/KartModelSub.carc"))); //contains characters + animations
-
- this.Race = new narc(lz77.decompress(rom.getFile("/data/Scene/Race.carc"))); //contains lakitu, count, various graphics
- this.RaceLoc = new narc(lz77.decompress(rom.getFile("/data/Scene/Race_us.carc"))); //contains lakitu lap signs, START, YOU WIN etc. some of these will be replaced by hi res graphics by default.
- this.RaceEffect = new spa(r.MainEffect.getFile("RaceEffect.spa"));
-
- this.MainFont = new nftr(r.Main2D.getFile("marioFont.NFTR"));
- //debugger;
-
- this.getChar = getChar;
- this.getKart = getKart;
-
- var itemNames = [
- "banana", "bomb", "gesso" /*squid*/, "kinoko" /*mushroom*/, "kinoko_p" /*queen shroom*/, "koura_g" /*green shell*/, "koura_r" /*red shell*/, "star", "teresa" /*boo*/, "thunder",
- "koura_w" /*blue shell item rep*/, "f_box", "killer" /*bullet bill*/
- ]
-
- //order
- //donkey, toad, bowser?, luigi, mario, peach, wario, yoshi, daisy, waluigi, dry bones (karon), robo, heyho
- var toSoundOff = [
- 4, 0, 1, 2, 5, 6, 7, 3, 10, 8, 9, 11, 12
- ];
-
- var charNames = [
- "mario", "donkey", "kinopio", "koopa", "peach", "wario", "yoshi", "luigi", "karon", "daisy", "waluigi", "robo", "heyho"
- ];
-
- var charAbbrv = [
- "MR", "DK", "KO", "KP", "PC", "WR", "YS", "LG", "KA", "DS", "WL", "RB", "HH"
- ];
-
- var tireName = ["kart_tire_L", "kart_tire_M", "kart_tire_S"];
-
- var characters = [];
- var karts = [];
-
- loadItems();
- loadTires();
-
- function loadItems() { //loads physical representations of items
- var t = {}
- for (var i=0; i 0 && r.other[0] != null) {
res.mdl[0].loadTexAnim(r.other[0]);
}
- if (r.other.length > 1 && r.other[1] != null)
+ if (r.other.length > 1 && r.other[1] != null) {
anim = new nitroAnimator(r.mdl[0].bmd, r.other[1]);
+ }
+ if (r.other.length > 2 && r.other[2] != null) {
+ res.mdl[0].loadTexPAnim(r.other[2]);
+ }
}
}
diff --git a/code/entities/item.js b/code/entities/item.js
new file mode 100644
index 0000000..36c7508
--- /dev/null
+++ b/code/entities/item.js
@@ -0,0 +1,292 @@
+//
+// shell.js
+//--------------------
+// Entity type for any item. Specific item types in `/item` folder
+// Has a default collision handler, but can pass control to the specific item code.
+// by RHY3756547
+//
+// includes: gl-matrix.js (glMatrix 2.0)
+// /formats/kcl.js
+//
+var itemTypes = {
+ //physics, holdable
+ '$koura_g': GreenShellC,
+ '$koura_r': RedShellC,
+ '$banana': BananaC,
+ '$bomb': BombC,
+ '$f_box': FakeBoxC,
+
+ //groups
+ '$koura_group': ShellGroupC,
+ '$banana_group': BananaGroupC,
+
+ //one use items
+ '$kinoko': MushroomC,
+ '$kinoko_group': MushroomGroupC,
+ '$kinoko_p': QueenMushroomC,
+ '$star': StarC,
+ '$thunder': ThunderC,
+ '$gesso': BlooperC,
+ '$teresa': BooC,
+ '$killer': KillerC,
+ '$koura_w': BlueShellC
+}
+
+window.Item = function(scene, owner, type) {
+ var t = this;
+ var minimumMove = 0.01;
+
+ this.id = 0;
+
+ 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.minBounceVel = 0.5;
+ this.airResist = 0.95;
+ this.enablePhysics = true;
+ this.floorBounce = 0.5;
+ this.held = true;
+ this.type = type;
+ this.owner = owner;
+
+ this.angle = owner.angle;
+ this.speed = 10;
+ this.yvel = 0;
+
+ this.colRadius = 4;
+ this.holdDist = 16;
+ this.safeKart = owner;
+
+ var deadTimerLength = 20;
+ var throwVelocity = 16;
+ var throwAngle = (Math.PI / 3) * 2;
+ this.deadTimer = 0; //animates death. goes to 20, then deletes for real. dead objects can't run update or otherwise
+
+ //a controller makes this item what it is...
+ // canBeHeld: boolean
+ // canBeDropped: boolean | 'func'
+ // isDestructive: boolean
+ // update?: (scene: CourseScene) => void
+ // draw?: (mvMatrix, pMatrix) => void // OVERRIDES NORMAL DRAW FUNCTION!
+ // release?: (direction: number) => boolean //direction is 1 for forward, -1 for back. returns if the item has more uses
+ // collide?: (item: Item | Kart)
+ // collideKart?: (item: Kart)
+ var subtypeInd = type.indexOf('-');
+ if (subtypeInd == -1) subtypeInd = type.length;
+ this.controller = new itemTypes["$"+type.substr(0, subtypeInd)](this, scene, type.substr(subtypeInd + 1));
+
+ //functions
+ this.update = update;
+ this.draw = draw;
+
+ this.updateHold = updateHold;
+ this.release = release;
+ this.canBeHeld = canBeHeld;
+ this.canBeDropped = canBeDropped;
+ this.isDestructive = isDestructive;
+ this.isSolid = isSolid;
+ this.finalize = finalize;
+ this.collide = collide;
+
+ function updateHold(kart) {
+ //move the object behind the kart (physical direction without drift off)
+ //assuming this will only be called for something that can be held
+ var dir = -kart.driftOff;
+
+ //offset the kart's drift offset (on direction)
+ var pos = [Math.sin(dir)*t.holdDist, 0, -Math.cos(dir)*t.holdDist];
+
+ //make relative to the kart's position
+ vec3.transformMat4(pos, pos, kart.mat);
+
+ vec3.sub(t.vel, pos, t.pos); //set the object's velocity to try move it to the hold location. (gravity is disabled)
+ t.enablePhysics = true;
+ }
+
+ function release(forward) {
+ //release the item, either forward or back
+ if (t.canBeHeld()) t.updateHold(owner);
+ if (t.controller.release) t.controller.release(forward);
+ else {
+ //default drop and throw. just here for template purposes
+ if (forward >= 0) {
+ var dir = owner.physicalDir;
+ vec3.zero(t.vel);
+ } else {
+ vec3.zero(t.vel);
+ }
+ }
+ this.held = false;
+ }
+
+ function canBeHeld() {
+ return t.controller.canBeHeld || false;
+ }
+
+ function canBeDropped() {
+ return t.controller.canBeDropped || true;
+ }
+
+ function isDestructive() {
+ return t.controller.isDestructive || false;
+ }
+
+ function isSolid() {
+ return t.controller.isSolid || true;
+ }
+
+ function finalize() {
+ //kill instantly
+ t.deadTimer = deadTimerLength;
+ scene.items.removeItem(t);
+ }
+
+ function collide(item) {
+ if (t.controller.collide) {
+ t.controller.collide(item);
+ return;
+ }
+
+ if (item.type) {
+ //has a type, definitely an item
+ if (item.isDestructive() || t.isDestructive()) {
+ //mutual destruction. other side will deal with how they handle the collision
+ t.deadTimer++;
+ } else if (item.isSolid() && t.isSolid()) {
+ //bounce off other items that are not destructive
+ //set our velocity to move away (not too intensely)
+ //(only apply if our id is before, to avoid double adding the velocity)
+ if (t.id < item.id) {
+ var diff = vec3.sub([], t.pos, item.pos);
+ vec3.scale(diff, diff, 0.5);
+ vec3.add(t.vel, t.vel, diff);
+ vec3.sub(item.vel, item.vel, diff);
+ t.enablePhysics = true;
+ item.enablePhysics = true;
+ }
+ }
+ } else {
+ //is a kart. usually this is where objects differ
+ if (t.controller.collideKart) {
+ t.controller.collideKart(item);
+ }
+ }
+ }
+
+ function update(scene) {
+ if (t.controller.update) t.controller.update(scene);
+
+ if (t.deadTimer > 0) {
+ t.deadTimer++;
+ if (t.deadTimer >= 20) t.finalize();
+ return;
+ }
+
+ //search for player collisions, collisions with other items
+ for (var i=0; i 0.01) {
+ var result = lsc.raycast(posSeg, velSeg, scene.kcl, 0.05, ignoreList);
+ if (result != null) {
+ if (t.controller.colResponse) t.controller.colResponse(posSeg, velSeg, result, ignoreList)
+ else 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;
+ }
+ }
+
+ function draw(mvMatrix, pMatrix) {
+ if (t.deadTimer > 0) nitroRender.setColMult([1, 1, 1, 1-(t.deadTimer/deadTimerLength)]); //fade out
+ if (t.controller.draw) {
+ t.controller.draw(mvMatrix, pMatrix);
+ } else {
+ 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[type].draw(mat, pMatrix);
+ }
+ if (t.deadTimer > 0) nitroRender.setColMult([1, 1, 1, 1]);
+ }
+
+ 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 adjustPos = true;
+
+ if (MKDS_COLTYPE.GROUP_WALL.indexOf(colType) != -1) { //wall
+ //normally, item collision with a wall cause a perfect reflection of the velocity.
+ var proj = vec3.dot(t.vel, n) * 2;
+ vec3.sub(t.vel, t.vel, vec3.scale(vec3.create(), n, proj));
+
+ } else if (MKDS_COLTYPE.GROUP_ROAD.indexOf(colType) != -1) {
+ //sliding plane
+ var proj = vec3.dot(t.vel, n) * (1 + t.floorBounce);
+ vec3.sub(t.vel, t.vel, vec3.scale(vec3.create(), n, proj));
+
+ if (t.floorBounce == 0 || Math.abs(proj) < t.minBounceVel) t.enablePhysics = false;
+ } else {
+ adjustPos = false;
+ ignoreList.push(plane);
+ }
+
+ if (adjustPos) { //move back from plane slightly
+ vec3.add(pos, pos, vec3.scale(vec3.create(), n, minimumMove));
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/code/entities/itembox.js b/code/entities/itembox.js
index a85c5bd..2a8f0f4 100644
--- a/code/entities/itembox.js
+++ b/code/entities/itembox.js
@@ -48,6 +48,7 @@ window.ItemBox = function(obji, scene) {
scene.particles.push(new NitroEmitter(scene, ok, 47));
t.mode = 1;
t.time = 0;
+ ok.items.getItem(null); //todo: specific item from some
break;
}
}
diff --git a/code/entities/items/bananas.js b/code/entities/items/bananas.js
new file mode 100644
index 0000000..3fccf8d
--- /dev/null
+++ b/code/entities/items/bananas.js
@@ -0,0 +1,54 @@
+window.BananaC = function(item, scene, type) {
+ this.canBeHeld = true;
+ this.canBeDropped = true;
+ this.isDestructive = false;
+ item.minBounceVel = 0;
+
+ this.collideKart = collideKart;
+
+ function collideKart(kart) {
+ item.deadTimerLength = 20;
+ kart.damage(MKDSCONST.DAMAGE_SPIN);
+ }
+}
+
+window.BananaGroupC = function(item, scene, type) {
+ this.canBeHeld = false;
+ this.canBeDropped = 'func';
+ this.rotationPeriod = 45;
+
+ item.colRadius = -Infinity;
+ item.enablePhysics = false;
+
+ this.draw = draw;
+
+ function draw(mvMatrix, pMatrix) {
+ //the group itself is invisible - the bananas draw individually
+ }
+}
+
+window.FakeBoxC = function(item, scene, type) {
+ this.canBeHeld = true;
+ this.canBeDropped = true;
+ this.isDestructive = false;
+ this.isSolid = true;
+ var model = scene.gameRes.fakeBox;
+
+ this.draw = draw;
+
+ function draw(view, pMatrix) {
+ mat4.translate(mat, view, t.pos);
+ mat4.translate(mat, view, [0, 16, 0]);
+
+ /* adjust to make it rest on a corner
+ 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));
+ */
+
+ mat4.scale(mat, mat, vec3.scale([], t.scale, 16));
+ model.draw(mat, pMatrix, animMat);
+ }
+}
+
+window.BombC = null;
diff --git a/code/entities/items/powerups.js b/code/entities/items/powerups.js
new file mode 100644
index 0000000..60fba18
--- /dev/null
+++ b/code/entities/items/powerups.js
@@ -0,0 +1,10 @@
+//boost, 3x boost, queen boost, star, ghost
+
+window.MushroomC = null;
+window.MushroomGroupC = null;
+window.QueenMushroomC = null;
+window.StarC = null;
+window.ThunderC = null;
+window.BlooperC = null;
+window.BooC = null;
+window.KillerC = null;
\ No newline at end of file
diff --git a/code/entities/items/shells.js b/code/entities/items/shells.js
new file mode 100644
index 0000000..58b64c9
--- /dev/null
+++ b/code/entities/items/shells.js
@@ -0,0 +1,25 @@
+window.GreenShellC = function(item, scene) {
+ this.canBeHeld = true;
+ this.canBeDropped = true;
+ this.isDestructive = true;
+}
+
+window.RedShellC = function(item, scene) {
+ this.canBeHeld = true;
+ this.canBeDropped = true;
+ this.isDestructive = true;
+}
+
+window.ShellGroupC = function(item, scene, type) {
+ this.canBeHeld = false;
+ this.canBeDropped = 'func';
+ this.rotationPeriod = 45;
+
+ this.draw = draw;
+
+ function draw(mvMatrix, pMatrix) {
+ //the group itself is invisible - the shells draw individually
+ }
+}
+
+window.BlueShellC = null;
\ No newline at end of file
diff --git a/code/entities/kart.js b/code/entities/kart.js
index 5c08965..3438d56 100644
--- a/code/entities/kart.js
+++ b/code/entities/kart.js
@@ -27,6 +27,9 @@ window.Kart = function(pos, angle, speed, kartN, charN, controller, scene) {
this.active = true;
this.preboost = true;
+ //supplimentary controllers
+ this.items = new KartItems(this, scene);
+
this.soundProps = {};
this.pos = pos;
this.angle = angle;
@@ -311,6 +314,7 @@ window.Kart = function(pos, angle, speed, kartN, charN, controller, scene) {
kartAnim = (kartAnim+1)%8;
var input = k.controller.fetchInput();
k.lastInput = input;
+ k.items.update(input);
if (input.turn > 0.3) {
if (k.driveAnimF < 28) k.driveAnimF++;
@@ -386,7 +390,7 @@ window.Kart = function(pos, angle, speed, kartN, charN, controller, scene) {
} 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];
- if (c.id2 != 0) {
+ if (c.id1 != -1 && c.id2 != -1) {
var c2 = scene.nkm.sections["KTPC"].entries[c.id2];
c = c2;
@@ -401,10 +405,18 @@ window.Kart = function(pos, angle, speed, kartN, charN, controller, scene) {
k.angle = k.physicalDir;
k.cannon = null;
} else {
-
var mat = mat4.create();
mat4.rotateY(mat, mat, c.angle[1]*(Math.PI/180));
- mat4.rotateX(mat, mat, c.angle[0]*(-Math.PI/180));
+ if (true) {
+ //vertical angle from position? airship fortress is impossible otherwise
+ //var c2 = scene.nkm.sections["KTPC"].entries[c.id2];
+ var diff = vec3.sub([], c.pos, k.pos);
+ var dAdj = Math.sqrt(diff[0]*diff[0] + diff[2]*diff[2]);
+ var dHyp = Math.sqrt(diff[0]*diff[0] + diff[1]*diff[1] + diff[2]*diff[2]);
+ mat4.rotateX(mat, mat, ((diff[1] > 0) ? -1 : 1) * Math.acos(dAdj/dHyp));
+ } else {
+ mat4.rotateX(mat, mat, c.angle[0]*(-Math.PI/180));
+ }
var forward = [0, 0, 1];
var up = [0, 1, 0];
@@ -415,10 +427,15 @@ window.Kart = function(pos, angle, speed, kartN, charN, controller, scene) {
k.physicalDir = (180-c.angle[1])*(Math.PI/180);
k.angle = k.physicalDir;
k.kartTargetNormal = vec3.transformMat4(up, up, mat);
+ k.airTime = 0;
var planeConst = -vec3.dot(c.pos, forward);
var cannonDist = vec3.dot(k.pos, forward) + planeConst;
- if (cannonDist > 0) k.cannon = null;
+ if (cannonDist > 0) {
+ k.cannon = null; //leaving cannon state
+ k.speed = params.topSpeed;
+ k.vel = vec3.scale([], vec3.transformMat4(forward, forward, mat), k.speed);
+ }
}
} else { //default kart mode
@@ -611,6 +628,7 @@ window.Kart = function(pos, angle, speed, kartN, charN, controller, scene) {
k.driftLanded = false;
k.driftMode = 0;
k.ylock = 0;
+ onGround = false;
var boing = nitroAudio.playSound(207, {transpose: -4}, 0, k);
boing.gainN.gain.value = 2;
@@ -655,11 +673,18 @@ window.Kart = function(pos, angle, speed, kartN, charN, controller, scene) {
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.angle += dirDiff(k.physicalDir, k.angle)*effect.handling;
+ k.angle += dirDiff(k.physicalDir, k.angle)*effect.handling; //applying this twice appears to be identical to the original
+ k.angle = fixDir(k.angle);
+ //reduce our forward speed by how much of our velocity is not going forwards
+ var factor = Math.sin(k.physicalDir)*Math.sin(k.angle) + Math.cos(k.physicalDir)*Math.cos(k.angle);
+ k.speed *= 1 - ((1-factor) * (1 - k.params.decel));
+ //var reducedSpeed = k.vel[0]*Math.sin(k.angle) + k.vel[2]*(-Math.cos(k.angle));
+ //reducedSpeed = ((reducedSpeed < 0) ? -1 : 1) * Math.sqrt(Math.abs(reducedSpeed));
k.vel[1] += k.gravity[1];
k.vel = [Math.sin(k.angle)*k.speed, k.vel[1], -Math.cos(k.angle)*k.speed]
+ //k.speed = reducedSpeed;
if (k.kartColTimer > 0) {
vec3.add(k.vel, k.vel, vec3.scale([], k.kartColVel, k.kartColTimer/10))
@@ -738,6 +763,24 @@ window.Kart = function(pos, angle, speed, kartN, charN, controller, scene) {
positionChanged(lastPos, k.pos);
}
+ function triggerCannon(id) {
+ if (k.cannon != null) return;
+ k.cannon = id;
+ var c = scene.nkm.sections["KTPC"].entries[k.cannon];
+ if (c.id1 != -1 && c.id2 != -1) {
+ nitroAudio.playSound(345, {volume: 2.5}, 0, k);
+ } else {
+ nitroAudio.playSound(347, {volume: 2.5}, 0, k);
+ if (k.local) {
+ if (c.id2 == 0) {
+ nitroAudio.playSound(380, {volume: 2}, 0, null); //airship fortress
+ } else {
+ nitroAudio.playSound(456, {volume: 2}, 0, null); //waluigi
+ }
+ }
+ }
+ }
+
function playCharacterSound(sound, volume) {
//0 - hit
//1 - hit spin
@@ -856,7 +899,7 @@ window.Kart = function(pos, angle, speed, kartN, charN, controller, scene) {
if (k != ok) {
var dist = vec3.dist(k.pos, ok.pos);
if (dist < 16) {
-
+ nitroAudio.playSound(208, { volume: 2 }, 0, k);
kartBounce(ok);
ok.kartBounce(k);
}
@@ -879,6 +922,8 @@ window.Kart = function(pos, angle, speed, kartN, charN, controller, scene) {
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;
+ //play this kart's horn
+ nitroAudio.playSound(192 + charRes.sndOff/14, { volume: 2 }, 0, k);
}
function fixDir(dir) {
@@ -896,7 +941,7 @@ window.Kart = function(pos, angle, speed, kartN, charN, controller, scene) {
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);
+ var transpose = (mode == 0)?0:(22*turn*Math.min(1.3, k.speed/params.topSpeed));
sounds.transpose += (transpose-sounds.transpose)/15;
if (mode != soundMode) {
@@ -965,6 +1010,7 @@ window.Kart = function(pos, angle, speed, kartN, charN, controller, scene) {
k.physBasis = {
mat: m4,
inv: mat4.invert([], m4),
+ normal: normal,
time: 15,
loop: false
};
@@ -1099,7 +1145,7 @@ window.Kart = function(pos, angle, speed, kartN, charN, controller, scene) {
stuckTo = dat.object;
} else if (colType == MKDS_COLTYPE.CANNON) {
//cannon!!
- k.cannon = colBE;
+ triggerCannon(colBE);
} else {
adjustPos = false;
ignoreList.push(plane);
diff --git a/code/entities/kartItems.js b/code/entities/kartItems.js
new file mode 100644
index 0000000..0d1f69e
--- /dev/null
+++ b/code/entities/kartItems.js
@@ -0,0 +1,77 @@
+//item state for a kart. not an entity, just supplemental to one.
+
+window.KartItems = function(kart, scene) {
+ var t = this;
+ t.heldItem = null; //of type Item
+ t.currentItem = null; //string name for item
+ t.specificItem = null;
+ t.empty = true;
+ t.cycleTime = 0;
+ t.totalTime = 230;
+ var maxItemTime = 230;
+ var minItemTime = 80;
+ var carouselSfx = null;
+ var lastItemState = false;
+ var holdAppearDelay = 15;
+
+ var hurtExplodeDelay = 105 //turn right slightly, huge double backflip, small bounces.
+ var hurtFlipDelay = 80; //turn right slightly, bounce twice, forward flip
+ var hurtSpinDelay = 40; //counter clockwise spin
+
+ t.getItem = getItem;
+ t.update = update;
+
+ var specialItems = ["star"];
+
+ function sfx(id) {
+ if (kart.local) {
+ return nitroAudio.playSound(id, {volume: 2}, 0, null);
+ }
+ return null;
+ }
+
+ function getItem(specific) {
+ if (!t.empty) return false;
+ else {
+ //begin carousel
+ t.cycleTime = 0;
+ t.totalTime = (specific) ? 60 : maxItemTime;
+ if (specific) t.specificItem = specific;
+ t.empty = false;
+
+ carouselSfx = sfx(62);
+ }
+ }
+
+ function update(input) {
+ var pressed = (input.item && !lastItemState);
+ if (!t.empty) {
+ if (t.currentItem == null) {
+ //carousel
+ t.cycleTime++;
+ if (t.cycleTime >= t.totalTime) {
+ if (carouselSfx != null) nitroAudio.kill(carouselSfx);
+
+ //decide on an item
+ var item = "koura_g";
+ sfx((specialItems.indexOf(item) == -1) ? 63 : 64);
+ t.currentItem = item;
+ } else {
+ //if item button is pressed, we speed up the carousel
+ if (pressed) {
+ t.totalTime = Math.max(minItemTime, t.totalTime - 20);
+ }
+ }
+ } else {
+ if (pressed) {
+ //fire?
+ t.currentItem = null;
+ t.empty = true;
+ kart.playCharacterSound(7);
+ }
+ }
+
+ }
+ lastItemState = input.item;
+ }
+}
\ No newline at end of file
diff --git a/code/formats/nsbtp.js b/code/formats/nsbtp.js
index 902e4c1..9aeddc7 100644
--- a/code/formats/nsbtp.js
+++ b/code/formats/nsbtp.js
@@ -36,6 +36,9 @@ window.nsbtp = function(input) {
}
this.load = load;
+ var texTotal;
+ var palTotal;
+
function load(input) {
var view = new DataView(input);
@@ -56,7 +59,6 @@ window.nsbtp = function(input) {
if (stamp != "PAT0") throw "NSBTP invalid. Expected PAT0, found "+stamp;
animData = nitro.read3dInfo(view, mainOff+8, animInfoHandler);
- debugger;
mainObj.animData = animData;
}
@@ -92,15 +94,15 @@ window.nsbtp = function(input) {
//8 bytes here? looks like texinfo
var duration = view.getUint16(offset, true);
- var tframes = view.getUint8(offset+2);
- var pframes = view.getUint8(offset+3);
+ texTotal = view.getUint8(offset+2);
+ palTotal = view.getUint8(offset+3);
var unknown = view.getUint16(offset+4, true);
var unknown2 = view.getUint16(offset+6, true);
//...then another nitro
var data = nitro.read3dInfo(view, offset+8, matInfoHandler);
- return {data: data, nextoff: data.nextoff, tframes:tframes, pframes:pframes, unknown:unknown, unknown2:unknown2, duration:duration};
+ return {data: data, nextoff: data.nextoff, texTotal:texTotal, palTotal:palTotal, unknown:unknown, unknown2:unknown2, duration:duration};
}
function matInfoHandler(view, offset, base) {
@@ -141,13 +143,24 @@ window.nsbtp = function(input) {
offset += 4;
}
+ obj.texNames = [];
//read 16char tex names
- for (var i=0; i= anim.frames[i].time) {
+ tex[pmat] = cacheTex(btx == null ? bmd.tex : btx, anim.frames[i].tex, anim.frames[i].mat, model.materials.objectData[pmat]);
+ }
+ }
+ }
+ }
+
+ if (nitroRender.last.tex != tex[pmat]) {
+ gl.bindTexture(gl.TEXTURE_2D, tex[pmat]); //load up material texture
+ nitroRender.last.tex = tex[pmat];
}
- var material = model.materials.objectData[poly.mat];
+ var material = model.materials.objectData[pmat];
nitroRender.setAlpha(material.alpha)
if (texAnim != null) {
//generate and send texture matrix from data
- var matname = model.materials.names[poly.mat]; //attach tex anim to mat with same name
+ var matname = model.materials.names[pmat]; //attach tex anim to mat with same name
var anims = texAnim.animData.objectData[modelind].data;
var animNum = anims.names.indexOf(matname);
@@ -666,7 +717,7 @@ function loadWhiteTex(btx) { //examines the materials in the loaded model and ge
drawModelBuffer(modelBuffers[modelind][polyind], gl, shader);
}
-function generateMatrixStack(model, targ) { //this generates a matrix stack with the default bones. use nitroAnimator to pass custom matrix stacks using nsbca animations.
+ function generateMatrixStack(model, targ) { //this generates a matrix stack with the default bones. use nitroAnimator to pass custom matrix stacks using nsbca animations.
var matrices = [];
var objs = model.objects.objectData;
@@ -733,10 +784,11 @@ function loadTex(img, gl, clampx, clampy) { //general purpose function for loadi
gl.bindTexture(gl.TEXTURE_2D, texture);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, img);
- gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
if (clampx) gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
if (clampy) gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
+ gl.generateMipmap(gl.TEXTURE_2D);
texture.width = img.width;
texture.height = img.height;
diff --git a/code/ui/race3DUI.js b/code/ui/race3DUI.js
index 09aadd0..f8d5ed5 100644
--- a/code/ui/race3DUI.js
+++ b/code/ui/race3DUI.js
@@ -36,11 +36,23 @@ window.Race3DUI = function(scene, type, animStart) {
],
"start": [ //offset 86 up
-128/1024, 128/1024, -(192+66)/1024, -66/1024
- ]
+ ],
+ "goal": [ //why are these all so different?
+ -128/1024, 128/1024, -(512 + 64)/1024, -(512 - 128)/1024
+ ],
+
+ //animations seem completely broken for these two (quickly files off screen after start)
+ //right now the vertical range of the viewport is large to try figure out where the hell it's going?
+ "win": [
+ -128/1024, 128/1024, -(1024)/1024, 1024/1024
+ ],
+ "lose": [
+ -128/1024, 128/1024, -(1024)/1024, 1024/1024
+ ],
}
var param = params[type];
- if (param == null) param = params["count"]
+ if (param == null) param = params["count"];
mat4.ortho(proj, param[0], param[1], param[2], param[3], -0.001, 10);
buildOrtho(nitroRender.getViewWidth(), nitroRender.getViewHeight());
@@ -54,10 +66,13 @@ window.Race3DUI = function(scene, type, animStart) {
bmd = new nsbmd(bmd);
var bca = new nsbca(scene.gameRes.Race.getFile(type+".nsbca"));
+ var btp = scene.gameRes.Race.getFile(type+".nsbtp");
+ if (btp != null) btp = new nsbtp(btp);
anim = new nitroAnimator(bmd, bca);
length = anim.getLength(0);
if (type == "count") length *= 3;
model = new nitroModel(bmd);
+ model.loadTexPAnim(btp)
}
function buildOrtho(width, height) {
@@ -72,11 +87,14 @@ window.Race3DUI = function(scene, type, animStart) {
var width = nitroRender.getViewWidth();
if (width != lastWidth) buildOrtho(width, nitroRender.getViewHeight());
mat4.translate(mat, view, t.pos);
+ nitroRender.pauseShadowMode();
model.draw(mat, proj, animMat);
+ nitroRender.unpauseShadowMode();
}
function update() {
if (anim != null) {
+ model.setFrame(animFrame);
animMat = anim.setFrame(0, 0, Math.max(0, animFrame++));
}
if (animFrame > length) {
diff --git a/index.html b/index.html
index abecd07..d112d77 100644
--- a/index.html
+++ b/index.html
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file