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