Wolfenstein 3D browser version

This is the code for the browser version of Wolfenstein 3D which is
playable on http://www.wolfenstein.com/game_NA.php
This commit is contained in:
Brian Harris
2013-05-16 11:34:31 -05:00
commit e0b653888d
32 changed files with 14155 additions and 0 deletions

562
js/actorai.js Normal file
View File

@@ -0,0 +1,562 @@
/*
* ===========================================================================
*
* Wolf3D Browser Version GPL Source Code
* Copyright (C) 2012 id Software LLC, a ZeniMax Media company.
*
* This file is part of the Wolf3D Browser Version GPL Source Code ("Wolf3D Browser Source Code").
*
* Wolf3D Browser Source Code is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* Wolf3D Browser Source Code is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License version 2
* along with Wolf3D Browser Source Code. If not, see <http://www.gnu.org/licenses/>.
*
* If you have questions concerning this license, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
*
* ===========================================================================
*/
/**
* @namespace
* @description Artificial intelligence
*/
Wolf.ActorAI = (function() {
var dsounds = [
"sfx/025.wav",
"sfx/026.wav",
"sfx/086.wav",
"sfx/088.wav",
"sfx/105.wav",
"sfx/107.wav",
"sfx/109.wav"
];
/**
* @description Initiate death scream sound effect.
* @memberOf Wolf.ActorAI
* @param {object} self The enemy actor object.
* @param {object} game The game object.
*/
function deathScream(self, game) {
var pos = game.player.position;
switch (self.type) {
case Wolf.en_mutant:
Wolf.Sound.startSound(pos, self, 1, Wolf.CHAN_VOICE, "sfx/037.wav", 1, Wolf.ATTN_NORM, 0);
break;
case Wolf.en_guard:
Wolf.Sound.startSound(pos, self, 1, Wolf.CHAN_VOICE, dsounds[Wolf.Random.rnd() % 6], 1, Wolf.ATTN_NORM, 0);
break;
case Wolf.en_officer:
Wolf.Sound.startSound(pos, self, 1, Wolf.CHAN_VOICE, "sfx/074.wav", 1, Wolf.ATTN_NORM, 0);
break;
case Wolf.en_ss:
Wolf.Sound.startSound(pos, self, 1, Wolf.CHAN_VOICE, "sfx/046.wav", 1, Wolf.ATTN_NORM, 0);
break;
case Wolf.en_dog:
Wolf.Sound.startSound(pos, self, 1, Wolf.CHAN_VOICE, "sfx/035.wav", 1, Wolf.ATTN_NORM, 0);
break;
case Wolf.en_boss:
Wolf.Sound.startSound(pos, self, 1, Wolf.CHAN_VOICE, "sfx/019.wav", 1, Wolf.ATTN_NORM, 0);
break;
case Wolf.en_schabbs:
Wolf.Sound.startSound(pos, self, 1, Wolf.CHAN_VOICE, "sfx/061.wav", 1, Wolf.ATTN_NORM, 0);
break;
case Wolf.en_fake:
Wolf.Sound.startSound(pos, self, 1, Wolf.CHAN_VOICE, "sfx/069.wav", 1, Wolf.ATTN_NORM, 0);
break;
case Wolf.en_mecha:
Wolf.Sound.startSound(pos, self, 1, Wolf.CHAN_VOICE, "sfx/084.wav", 1, Wolf.ATTN_NORM, 0);
break;
case Wolf.en_hitler:
Wolf.Sound.startSound(pos, self, 1, Wolf.CHAN_VOICE, "sfx/044.wav", 1, Wolf.ATTN_NORM, 0);
break;
case Wolf.en_gretel:
Wolf.Sound.startSound(pos, self, 1, Wolf.CHAN_VOICE, "sfx/115.wav", 1, Wolf.ATTN_NORM, 0);
break;
case Wolf.en_gift:
Wolf.Sound.startSound(pos, self, 1, Wolf.CHAN_VOICE, "sfx/091.wav", 1, Wolf.ATTN_NORM, 0);
break;
case Wolf.en_fat:
Wolf.Sound.startSound(pos, self, 1, Wolf.CHAN_VOICE, "sfx/119.wav", 1, Wolf.ATTN_NORM, 0);
break;
}
}
/* Hitler */
/**
* @description Play Mecha sound.
* @memberOf Wolf.ActorAI
* @param {object} self The enemy actor object.
* @param {object} game The game object.
*/
function mechaSound(self, game) {
if (game.level.state.areabyplayer[self.areanumber]) {
Wolf.Sound.startSound(game.player.position, self, 1, Wolf.CHAN_VOICE, "sfx/080.wav", 1, Wolf.ATTN_NORM, 0);
}
}
/**
* @description Play Slurpie sound.
* @memberOf Wolf.ActorAI
* @param {object} self The enemy actor object.
*/
function slurpie(self, game) {
Wolf.Sound.startSound(game.player.position, self, 1, Wolf.CHAN_VOICE, "lsfx/061.wav", 1, Wolf.ATTN_NORM, 0);
}
/**
* @description Spawn new actor, when Mecha Hitler is dead.
* @memberOf Wolf.ActorAI
* @param {object} self The enemy actor object.
* @param {object} game The game object.
*/
function hitlerMorph(self, game) {
var hitpoints = [500, 700, 800, 900],
level = game.level,
hitler;
hitler = Wolf.Actors.getNewActor(level);
if(!hitler) {
return;
}
hitler.x = self.x;
hitler.y = self.y;
hitler.distance = self.distance;
hitler.tile.x = self.tile.x;
hitler.tile.y = self.tile.y;
hitler.angle = self.angle;
hitler.dir = self.dir;
hitler.health = hitpoints[game.skill];
hitler.areanumber = self.areanumber;
hitler.state = Wolf.st_chase1;
hitler.type = Wolf.en_hitler;
hitler.speed = Wolf.SPDPATROL * 5;
hitler.ticcount = 0;
hitler.flags = self.flags | Wolf.FL_SHOOTABLE;
hitler.sprite = Wolf.Sprites.getNewSprite(level);
}
/* Angel of Death */
var angel_temp = 0;
/**
* @description Play Angel of Death Breathing sound.
* @memberOf Wolf.ActorAI
* @param {object} self The enemy actor object.
*/
function breathing(self) {
Wolf.Sound.startSound(null, null, 1, Wolf.CHAN_VOICE, "lsfx/080.wav", 1, Wolf.ATTN_NORM, 0);
}
/**
* @description Reset Angel of Death attack counter
* @memberOf Wolf.ActorAI
* @param {object} self The enemy actor object.
*/
function startAttack(self) {
angel_temp = 0;
}
/**
* @description Angel of Death AI.
* @memberOf Wolf.ActorAI
* @param {object} self The enemy actor object.
*/
function relaunch(self) {
if (++angel_temp == 3) {
Wolf.Actors.stateChange(self, Wolf.st_pain);
return;
}
if (Wolf.Random.rnd() & 1) {
Wolf.Actors.stateChange(self, Wolf.st_chase1);
return;
}
}
/**
* @description Victory - start intermission.
* @memberOf Wolf.ActorAI
*/
function victory(game) {
Wolf.Game.startIntermission(game);
}
/**
* @description Entity is dormant state.
* @memberOf Wolf.ActorAI
* @param {object} self The enemy actor object.
* @param {object} game The game object.
*/
function dormant(self, game) {
var level = game.level,
player = game.player,
deltax,
deltay,
xl, xh, yl, yh,
x, y, n,
moveok = false;
deltax = self.x - player.position.x;
deltay = self.y - player.position.y;
if (deltax < -Wolf.MINACTORDIST || deltax > Wolf.MINACTORDIST) {
moveok = true;
} else if(deltay < -Wolf.MINACTORDIST || deltay > Wolf.MINACTORDIST) {
moveok = true;
}
if (!moveok) {
return;
}
// moveok:
xl = (self.x - Wolf.MINDIST) >> Wolf.TILESHIFT;
xh = (self.x + Wolf.MINDIST) >> Wolf.TILESHIFT;
yl = (self.y - Wolf.MINDIST) >> Wolf.TILESHIFT;
yh = (self.y + Wolf.MINDIST) >> Wolf.TILESHIFT;
for(y = yl; y <= yh ; ++y ) {
for(x = xl;x <= xh;++x) {
if (level.tileMap[x][y] & Wolf.SOLID_TILE) {
return;
}
for (n=0;n<level.state.numGuards;++n) {
if (level.state.guards[n].state >= Wolf.st_die1) {
continue;
}
if (level.state.guards[n].tile.x == x && level.state.guards[n].tile.y == y) {
return; // another guard in path
}
}
}
}
self.flags |= Wolf.FL_AMBUSH | Wolf.FL_SHOOTABLE;
self.flags &= ~Wolf.FL_ATTACKMODE;
self.dir = Wolf.Math.dir8_nodir;
Wolf.Actors.stateChange(self, Wolf.st_path1);
}
/**
* @description Death cam animation.
* Tthe DeathCam feature isn't implimented, but we want to
* give the animation time to play before declaring victory.
* @memberOf Wolf.ActorAI
* @param {object} self The enemy actor object.
* @param {object} game The game object.
*/
function startDeathCam(game, self) {
self.playstate = Wolf.ex_complete;
setTimeout(function() {
Wolf.Game.startIntermission(game);
}, 5000);
}
/**
* @description Rockets emmit smoke.
* @memberOf Wolf.ActorAI
* @param {object} self The enemy actor object.
* @param {object} level The level object.
*/
function smoke(self, game) {
var level = game.level,
smokeEnt = Wolf.Actors.getNewActor(level);
if (!smokeEnt) {
return;
}
smokeEnt.x = self.x;
smokeEnt.y = self.y;
smokeEnt.tile.x = self.tile.x;
smokeEnt.tile.y = self.tile.y;
smokeEnt.state = Wolf.st_die1;
smokeEnt.type = (self.type == Wolf.en_hrocket) ? Wolf.en_hsmoke : Wolf.en_smoke;
smokeEnt.ticcount = 6;
smokeEnt.flags = Wolf.FL_NEVERMARK;
smokeEnt.sprite = Wolf.Sprites.getNewSprite(level);
}
/**
* @description Puts an actor into attack mode and possibly reverses the direction if the player is behind it.
* @memberOf Wolf.ActorAI
* @param {object} self The enemy actor object.
*/
function firstSighting(self, game) {
switch (self.type) {
case Wolf.en_guard:
Wolf.Sound.startSound(game.player.position, self, 1, Wolf.CHAN_VOICE, "sfx/001.wav", 1, Wolf.ATTN_NORM, 0);
self.speed *= 3; // go faster when chasing player
break;
case Wolf.en_officer:
Wolf.Sound.startSound(game.player.position, self, 1, Wolf.CHAN_VOICE, "sfx/071.wav", 1, Wolf.ATTN_NORM, 0);
self.speed *= 5; // go faster when chasing player
break;
case Wolf.en_mutant:
self.speed *= 3; // go faster when chasing player
break;
case Wolf.en_ss:
Wolf.Sound.startSound(game.player.position, self, 1, Wolf.CHAN_VOICE, "sfx/015.wav", 1, Wolf.ATTN_NORM, 0);
self.speed *= 4; // go faster when chasing player
break;
case Wolf.en_dog:
Wolf.Sound.startSound(game.player.position, self, 1, Wolf.CHAN_VOICE, "sfx/002.wav", 1, Wolf.ATTN_NORM, 0);
self.speed *= 2; // go faster when chasing player
break;
case Wolf.en_boss:
Wolf.Sound.startSound(game.player.position, self, 1, Wolf.CHAN_VOICE, "sfx/017.wav", 1, Wolf.ATTN_NORM, 0);
self.speed = Wolf.SPDPATROL * 3; // go faster when chasing player
break;
case Wolf.en_gretel:
Wolf.Sound.startSound(game.player.position, self, 1, Wolf.CHAN_VOICE, "sfx/112.wav", 1, Wolf.ATTN_NORM, 0);
self.speed *= 3; // go faster when chasing player
break;
case Wolf.en_gift:
Wolf.Sound.startSound(game.player.position, self, 1, Wolf.CHAN_VOICE, "sfx/096.wav", 1, Wolf.ATTN_NORM, 0);
self.speed *= 3; // go faster when chasing player
break;
case Wolf.en_fat:
Wolf.Sound.startSound(game.player.position, self, 1, Wolf.CHAN_VOICE, "sfx/102.wav", 1, Wolf.ATTN_NORM, 0);
self.speed *= 3; // go faster when chasing player
break;
case Wolf.en_schabbs:
Wolf.Sound.startSound(game.player.position, self, 1, Wolf.CHAN_VOICE, "sfx/065.wav", 1, Wolf.ATTN_NORM, 0);
self.speed *= 3; // go faster when chasing player
break;
case Wolf.en_fake:
Wolf.Sound.startSound(game.player.position, self, 1, Wolf.CHAN_VOICE, "sfx/054.wav", 1, Wolf.ATTN_NORM, 0);
self.speed *= 3; // go faster when chasing player
break;
case Wolf.en_mecha:
Wolf.Sound.startSound(game.player.position, self, 1, Wolf.CHAN_VOICE, "sfx/040.wav", 1, Wolf.ATTN_NORM, 0);
self.speed *= 3; // go faster when chasing player
break;
case Wolf.en_hitler:
Wolf.Sound.startSound(game.player.position, self, 1, Wolf.CHAN_VOICE, "sfx/040.wav", 1, Wolf.ATTN_NORM, 0);
self.speed *= 5; // go faster when chasing player
break;
case Wolf.en_blinky:
case Wolf.en_clyde:
case Wolf.en_pinky:
case Wolf.en_inky:
self.speed *= 2; // go faster when chasing player
break;
default:
return;
}
Wolf.Actors.stateChange(self, Wolf.st_chase1);
if (self.waitfordoorx) {
self.waitfordoorx = self.waitfordoory = 0; // ignore the door opening command
}
self.dir = Wolf.Math.dir8_nodir;
self.flags |= Wolf.FL_ATTACKMODE | Wolf.FL_FIRSTATTACK;
}
/**
* @description Called when the player succesfully hits an enemy.
* Does damage points to enemy ob, either putting it into a stun frame or killing it.
* @memberOf Wolf.ActorAI
* @param {object} self The enemy actor object.
* @param {object} game The game object.
* @param {object} player The player object.
* @param {number} damage The number of damage points.
*/
function damageActor(self, game, player, damage) {
player.madenoise = 1;
// do double damage if shooting a non attack mode actor
if (!(self.flags & Wolf.FL_ATTACKMODE)) {
damage <<= 1;
}
self.health -= damage;
if (self.health <= 0) {
killActor(self, game, player);
} else {
if (!(self.flags & Wolf.FL_ATTACKMODE) ) {
firstSighting(self, game); // put into combat mode
}
switch (self.type) { // dogs only have one hit point
case Wolf.en_guard:
case Wolf.en_officer:
case Wolf.en_mutant:
case Wolf.en_ss:
if (self.health & 1) {
Wolf.Actors.stateChange(self, Wolf.st_pain);
} else {
Wolf.Actors.stateChange(self, Wolf.st_pain1);
}
break;
}
}
}
/**
* @description Actor has been killed, so give points and spawn powerups.
* @memberOf Wolf.ActorAI
* @param {object} self The enemy actor object.
* @param {object} game The game object.
* @param {object} player The player object.
*/
function killActor(self, game, player) {
var level = game.level,
tilex = self.tile.x = self.x >> Wolf.TILESHIFT; // drop item on center,
tiley = self.tile.y = self.y >> Wolf.TILESHIFT;
switch (self.type) {
case Wolf.en_guard:
Wolf.Player.givePoints(player, 100);
Wolf.Powerups.spawn(level, tilex, tiley, Wolf.pow_clip2);
break;
case Wolf.en_officer:
Wolf.Player.givePoints(player, 400);
Wolf.Powerups.spawn(level, tilex, tiley, Wolf.pow_clip2);
break;
case Wolf.en_mutant:
Wolf.Player.givePoints(player, 700);
Wolf.Powerups.spawn(level, tilex, tiley, Wolf.pow_clip2);
break;
case Wolf.en_ss:
Wolf.Player.givePoints(player, 500);
if (player.items & Wolf.ITEM_WEAPON_3) { // have a schmeiser?
Wolf.Powerups.spawn(level, tilex, tiley, Wolf.pow_clip2);
} else {
Wolf.Powerups.spawn(level, tilex, tiley, Wolf.pow_machinegun);
}
break;
case Wolf.en_dog:
Wolf.Player.givePoints(player, 200);
break;
case Wolf.en_boss:
Wolf.Player.givePoints(player, 5000);
Wolf.Powerups.spawn(level, tilex, tiley, Wolf.pow_key1);
break;
case Wolf.en_gretel:
Wolf.Player.givePoints(player, 5000);
Wolf.Powerups.spawn(level, tilex, tiley, Wolf.pow_key1);
break;
case Wolf.en_gift:
Wolf.Player.givePoints(player, 5000);
startDeathCam(game, self);
break;
case Wolf.en_fat:
Wolf.Player.givePoints(player, 5000);
startDeathCam(game, self);
break;
case Wolf.en_schabbs:
Wolf.Player.givePoints(player, 5000);
deathScream(self, game);
startDeathCam(game, self);
break;
case Wolf.en_fake:
Wolf.Player.givePoints(player, 2000);
break;
case Wolf.en_mecha:
Wolf.Player.givePoints(player, 5000);
break;
case Wolf.en_hitler:
Wolf.Player.givePoints(player, 5000);
deathScream(self, game);
startDeathCam(game, self);
break;
}
Wolf.Actors.stateChange(self, Wolf.st_die1);
if (++level.state.killedMonsters == level.state.totalMonsters) {
Wolf.Game.notify("You killed the last enemy!");
}
self.flags &= ~Wolf.FL_SHOOTABLE;
self.flags |= Wolf.FL_NONMARK;
}
return {
firstSighting : firstSighting,
damageActor : damageActor,
killActor : killActor,
deathScream : deathScream,
mechaSound : mechaSound,
slurpie : slurpie,
hitlerMorph : hitlerMorph,
breathing : breathing,
startAttack : startAttack,
relaunch : relaunch,
victory : victory,
dormant : dormant,
startDeathCam : startDeathCam,
smoke : smoke
};
})();

518
js/actors.js Normal file
View File

@@ -0,0 +1,518 @@
/*
* ===========================================================================
*
* Wolf3D Browser Version GPL Source Code
* Copyright (C) 2012 id Software LLC, a ZeniMax Media company.
*
* This file is part of the Wolf3D Browser Version GPL Source Code ("Wolf3D Browser Source Code").
*
* Wolf3D Browser Source Code is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* Wolf3D Browser Source Code is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License version 2
* along with Wolf3D Browser Source Code. If not, see <http://www.gnu.org/licenses/>.
*
* If you have questions concerning this license, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
*
* ===========================================================================
*/
/**
* @namespace
* @description Actors
*/
Wolf.Actors = (function() {
Wolf.setConsts({
SPDPATROL : 512,
SPDDOG : 1500,
FL_SHOOTABLE : 1,
FL_BONUS : 2,
FL_NEVERMARK : 4,
FL_VISABLE : 8,
FL_ATTACKMODE : 16,
FL_FIRSTATTACK : 32,
FL_AMBUSH : 64,
FL_NONMARK : 128,
MAX_GUARDS : 255,
NUMENEMIES : 31,
NUMSTATES : 34,
MINACTORDIST : 0x10000
});
Wolf.setConsts({
en_guard : 0,
en_officer : 1,
en_ss : 2,
en_dog : 3,
en_boss : 4,
en_schabbs : 5,
en_fake : 6,
en_mecha : 7,
en_hitler : 8,
en_mutant : 9,
en_blinky : 10,
en_clyde : 11,
en_pinky : 12,
en_inky : 13,
en_gretel : 14,
en_gift : 15,
en_fat : 16,
// --- Projectiles
en_needle : 17,
en_fire : 18,
en_rocket : 19,
en_smoke : 20,
en_bj : 21,
// --- Spear of destiny!
en_spark : 22,
en_hrocket : 23,
en_hsmoke : 24,
en_spectre : 25,
en_angel : 26,
en_trans : 27,
en_uber : 28,
en_will : 29,
en_death : 30
});
Wolf.setConsts({
st_stand : 0,
st_path1 : 1,
st_path1s : 2,
st_path2 : 3,
st_path3 : 4,
st_path3s : 5,
st_path4 : 6,
st_pain : 7,
st_pain1 : 8,
st_shoot1 : 9,
st_shoot2 : 10,
st_shoot3 : 11,
st_shoot4 : 12,
st_shoot5 : 13,
st_shoot6 : 14,
st_shoot7 : 15,
st_shoot8 : 16,
st_shoot9 : 17,
st_chase1 : 18,
st_chase1s : 19,
st_chase2 : 20,
st_chase3 : 21,
st_chase3s : 22,
st_chase4 : 23,
st_die1 : 24,
st_die2 : 25,
st_die3 : 26,
st_die4 : 27,
st_die5 : 28,
st_die6 : 29,
st_die7 : 30,
st_die8 : 31,
st_die9 : 32,
st_dead : 33,
st_remove : 34
});
var add8dir = [4, 5, 6, 7, 0, 1, 2, 3, 0],
r_add8dir = [4, 7, 6, 5, 0, 1, 2, 3, 0];
/**
* @description Create new actor.
* @memberOf Wolf.Actors
* @param {object} level The level object.
* @returns {object} The new actor object.
*/
function getNewActor(level) {
if (level.state.numGuards > Wolf.MAX_GUARDS) {
return null;
}
var actor = {
x : 0,
y : 0,
angle : 0,
type : 0,
health : 0,
max_health : 0,
speed : 0,
ticcount : 0,
temp2 : 0,
distance : 0,
tile : {
x : 0,
y : 0
},
areanumber : 0,
waitfordoorx : 0,
waitfordoory : 0, // waiting on this door if non 0
flags : 0, // FL_SHOOTABLE, etc
state : 0,
dir : 0,
sprite : 0
};
level.state.guards[level.state.numGuards++] = actor;
return actor;
}
/**
* @description Process a single actor.
* @private
* @param {object} ent The actor object.
* @param {object} level The level object.
* @param {object} player The player object.
* @param {number} tics The number of tics.
* @returns {boolean} False if actor should be removed, otherwise true.
*/
function doGuard(ent, game, tics) { // FIXME: revise!
var think;
//assert( ent->tilex >= 0 && ent->tilex < 64 );
//assert( ent->tiley >= 0 && ent->tiley < 64 );
//assert( ent->dir >= 0 && ent->dir <= 8 );
// ticcounts fire discrete actions separate from think functions
if (ent.ticcount) {
ent.ticcount -= tics;
while (ent.ticcount <= 0) {
//assert( ent->type >= 0 && ent->type < NUMENEMIES );
//assert( ent->state >= 0 && ent->state < NUMSTATES );
think = Wolf.objstate[ent.type][ent.state].action; // end of state action
if (think) {
think(ent, game, tics);
if (ent.state == Wolf.st_remove) {
return false;
}
}
ent.state = Wolf.objstate[ent.type][ent.state].next_state;
if (ent.state == Wolf.st_remove) {
return false;
}
if (!Wolf.objstate[ent.type][ent.state].timeout) {
ent.ticcount = 0;
break;
}
ent.ticcount += Wolf.objstate[ent.type][ent.state].timeout;
}
}
//
// think
//
//assert( ent->type >= 0 && ent->type < NUMENEMIES );
//assert( ent->state >= 0 && ent->state < NUMSTATES );
think = Wolf.objstate[ent.type][ent.state].think;
if (think) {
think(ent, game, tics);
if (ent.state == Wolf.st_remove) {
return false;
}
}
return true;
}
/**
* @description Changes guard's state to that defined in newState.
* @memberOf Wolf.Actors
* @param {object} ent The actor object.
* @param {number} newState The new state.
*/
function stateChange(ent, newState) {
ent.state = newState;
// assert( ent->type >= 0 && ent->type < NUMENEMIES );
if (newState == Wolf.st_remove) {
ent.ticcount = 0;
} else {
// assert( ent->state >= 0 && ent->state < NUMSTATES );
ent.ticcount = Wolf.objstate[ent.type][ent.state].timeout; //0;
}
}
/**
* @description Process all the enemy actors.
* @memberOf Wolf.Actors
* @param {object} level The level object.
* @param {object} player The player object.
* @param {number} tics The number of tics.
*/
function process(game, tics) {
var level = game.level,
player = game.player,
n, tex, guard,
liveGuards = [];
for (n = 0 ; n < level.state.numGuards ; ++n ) {
guard = level.state.guards[n];
if (!doGuard(guard, game, tics)) {
// remove guard from the game forever!
// remove(game, guards[n--]);
Wolf.Sprites.remove(level, guard.sprite);
level.state.guards[n] = null;
continue;
}
Wolf.Sprites.setPos(level, guard.sprite, guard.x, guard.y, guard.angle);
tex = Wolf.objstate[guard.type][guard.state].texture;
if (Wolf.objstate[guard.type][guard.state].rotate) {
if (guard.type == Wolf.en_rocket || guard.type == Wolf.en_hrocket) {
tex += r_add8dir[Wolf.Math.get8dir( Wolf.Angle.distCW(Wolf.FINE2RAD(player.angle), Wolf.FINE2RAD(guard.angle)))];
} else {
tex += add8dir[Wolf.Math.get8dir( Wolf.Angle.distCW(Wolf.FINE2RAD(player.angle), Wolf.FINE2RAD(guard.angle)))];
}
}
Wolf.Sprites.setTex(level, guard.sprite, 0, tex);
}
for (n = 0 ; n < level.state.numGuards ; ++n ) {
if (level.state.guards[n]) {
liveGuards.push(level.state.guards[n]);
}
}
level.state.guards = liveGuards;
level.state.numGuards = liveGuards.length;
}
/**
* @description Reset and clear the enemy actors in the level.
* @memberOf Wolf.Actors
* @param {object} level The level object.
*/
function resetGuards(level) {
level.state.guards = [];
level.state.numGuards = 0;
//New = NULL;
}
/**
* @description Spawn a new enemy actor at the given position.
* @memberOf Wolf.Actors
* @param {object} level The level object.
* @param {number} skill The difficulty level.
* @param {number} which The actor type.
* @param {number} x The x position.
* @param {number} y The y position.
* @param {number} dir The direction.
* @returns {object} The new actor object or null if actor creation failed.
*/
function spawn(level, skill, which, x, y, dir) {
var ent = getNewActor(level);
if (!ent) {
return null;
}
ent.x = Wolf.TILE2POS(x);
ent.y = Wolf.TILE2POS(y);
ent.tile.x = x;
ent.tile.y = y;
// assert( dir >= 0 && dir <= 4 );
ent.angle = Wolf.Math.dir4angle[dir];
ent.dir = Wolf.Math.dir4to8[dir];
ent.areanumber = level.areas[x][y];
if (ent.areanumber < 0) {
// ambush marker tiles are listed as -3 area
ent.areanumber = 0;
}
// assert( ent->areanumber >= 0 && ent->areanumber < NUMAREAS );
ent.type = which;
ent.health = Wolf.starthitpoints[skill][which];
ent.sprite = Wolf.Sprites.getNewSprite(level);
return ent;
}
/**
* @description Spawn a dead guard.
* @memberOf Wolf.Actors
* @param {object} level The level object.
* @param {number} skill The difficulty level.
* @param {number} which The actor type.
* @param {number} x The x position.
* @param {number} y The y position.
*/
function spawnDeadGuard(level, skill, which, x, y) {
var self = spawn(level, skill, which, x, y, Wolf.Math.dir4_nodir);
if (!self) {
return;
}
self.state = Wolf.st_dead;
self.speed = 0;
self.health = 0;
self.ticcount = Wolf.objstate[which][Wolf.st_dead].timeout ? Wolf.Random.rnd() % Wolf.objstate[which][Wolf.st_dead].timeout + 1 : 0;
}
/**
* @description Spawn a patrolling guard.
* @memberOf Wolf.Actors
* @param {object} level The level object.
* @param {number} skill The difficulty level.
* @param {number} which The actor type.
* @param {number} x The x position.
* @param {number} y The y position.
*/
function spawnPatrol(level, skill, which, x, y, dir) {
var self = spawn(level, skill, which, x, y, dir);
if (!self) {
return;
}
self.state = Wolf.st_path1;
self.speed = (which == Wolf.en_dog) ? Wolf.SPDDOG : Wolf.SPDPATROL;
self.distance = Wolf.TILEGLOBAL;
self.ticcount = Wolf.objstate[which][Wolf.st_path1].timeout ? Wolf.Random.rnd() % Wolf.objstate[which][Wolf.st_path1].timeout + 1 : 0;
self.flags |= Wolf.FL_SHOOTABLE;
level.state.totalMonsters++;
}
/**
* @description Spawn a standing guard.
* @memberOf Wolf.Actors
* @param {object} level The level object.
* @param {number} skill The difficulty level.
* @param {number} which The actor type.
* @param {number} x The x position.
* @param {number} y The y position.
*/
function spawnStand(level, skill, which, x, y, dir) {
var self = spawn(level, skill, which, x, y, dir);
if (!self) {
return;
}
self.state = Wolf.st_stand;
self.speed = Wolf.SPDPATROL;
self.ticcount = Wolf.objstate[which][Wolf.st_stand].timeout ? Wolf.Random.rnd() % Wolf.objstate[which][Wolf.st_stand].timeout + 1 : 0;
self.flags |= Wolf.FL_SHOOTABLE;
if (level.tileMap[x][y] & Wolf.AMBUSH_TILE) {
self.flags |= Wolf.FL_AMBUSH;
}
level.state.totalMonsters++;
}
function spawnBoss(level, skill, which, x, y) {
var self,
face;
switch (which) {
case Wolf.en_boss:
case Wolf.en_schabbs:
case Wolf.en_fat:
case Wolf.en_hitler:
face = Wolf.Math.dir4_south;
break;
case Wolf.en_fake:
case Wolf.en_gretel:
case Wolf.en_gift:
face = Wolf.Math.dir4_north;
break;
case Wolf.en_trans:
case Wolf.en_uber:
case Wolf.en_will:
case Wolf.en_death:
case Wolf.en_angel:
case Wolf.en_spectre:
face = Wolf.Math.dir4_nodir;
break;
default:
face = Wolf.Math.dir4_nodir;
break;
}
self = spawn(level, skill, which, x, y, face);
if (!self) {
return;
}
self.state = which == Wolf.en_spectre ? Wolf.st_path1 : Wolf.st_stand;
self.speed = Wolf.SPDPATROL;
self.health = Wolf.starthitpoints[skill][which];
self.ticcount = Wolf.objstate[which][Wolf.st_stand].timeout ? Wolf.Random.rnd() % Wolf.objstate[which][Wolf.st_stand].timeout + 1 : 0;
self.flags |= Wolf.FL_SHOOTABLE | Wolf.FL_AMBUSH;
level.state.totalMonsters++;
}
function spawnGhosts(level, skill, which, x, y) {
var self = spawn(level, skill, which, x, y, Wolf.Math.dir4_nodir);
if (!self) {
return;
}
self.state = Wolf.st_chase1;
self.speed = Wolf.SPDPATROL * 3;
self.health = Wolf.starthitpoints[skill][which];
self.ticcount = Wolf.objstate[which][Wolf.st_chase1].timeout ? Wolf.Random.rnd() % Wolf.objstate[which][Wolf.st_chase1].timeout + 1: 0;
self.flags |= Wolf.FL_AMBUSH;
level.state.totalMonsters++;
}
function spawnBJVictory(player, level, skill) {
var x = Wolf.POS2TILE(player.position.x),
y = Wolf.POS2TILE(player.position.y),
bj = spawn(level, skill, Wolf.en_bj, x, y + 1, Wolf.Math.dir4_north);
if (!bj) {
return;
}
bj.x = player.position.x;
bj.y = player.position.y;
bj.state = Wolf.st_path1;
bj.speed = Wolf.BJRUNSPEED;
bj.flags = Wolf.FL_NONMARK; // FL_NEVERMARK;
bj.temp2 = 6;
bj.ticcount = 1;
}
return {
process : process,
resetGuards : resetGuards,
getNewActor : getNewActor,
spawn : spawn,
spawnDeadGuard : spawnDeadGuard,
spawnPatrol : spawnPatrol,
spawnStand : spawnStand,
spawnBoss : spawnBoss,
spawnGhosts : spawnGhosts,
spawnBJVictory : spawnBJVictory,
stateChange : stateChange
};
})();

1631
js/actstat.js Normal file

File diff suppressed because it is too large Load Diff

1206
js/ai.js Normal file

File diff suppressed because it is too large Load Diff

143
js/angle.js Normal file
View File

@@ -0,0 +1,143 @@
/*
* ===========================================================================
*
* Wolf3D Browser Version GPL Source Code
* Copyright (C) 2012 id Software LLC, a ZeniMax Media company.
*
* This file is part of the Wolf3D Browser Version GPL Source Code ("Wolf3D Browser Source Code").
*
* Wolf3D Browser Source Code is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* Wolf3D Browser Source Code is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License version 2
* along with Wolf3D Browser Source Code. If not, see <http://www.gnu.org/licenses/>.
*
* If you have questions concerning this license, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
*
* ===========================================================================
*/
/**
* @namespace
* @description Angle math
*/
Wolf.Angle = (function() {
Wolf.setConsts({
DEG2RAD : function(a) { return a * 0.01745329251994329576; }, // a * M_PI / 180.0f
RAD2DEG : function(a) { return a / 0.01745329251994329576; }, // a * 180.0f / M_PI
ANGLE2SHORT : function(x) { return ((x * 65536 / 360)>>0) & 65535; },
SHORT2ANGLE : function(x) { return x * 360.0 / 65536; }
});
/**
* @description Finds the difference between two angles.
* @memberOf Wolf.Angle
* @param {number} angle1 Angle in radians.
* @param {number} angle2 Angle in radians.
* @returns {number} The absolute difference between two angles, this will always be between 0 and 180 degrees.
*/
function diff(angle1, angle2) {
var d;
if (angle1 > angle2) {
d = angle1 - angle2;
} else {
d = angle2 - angle1;
}
if (d > Math.PI) {
return 2 * Math.PI - d;
} else {
return d;
}
}
/**
* @description Clockwise distance between two angles.
* @memberOf Wolf.Angle
* @param {number} angle1 Angle in radians.
* @param {number} angle2 Angle in radians.
* @returns {number} The clockwise distance from angle2 to angle1, this may be greater than 180 degrees.
*/
function distCW(angle1, angle2) {
if (angle1 > angle2) {
return angle1 - angle2;
} else {
return angle1 + 2 * Math.PI - angle2;
}
}
/**
* @description Linear interpolate between angle from and to by fraction frac.
* @memberOf Wolf.Angle
* @param {number} from Angle in radians.
* @param {number} to Angle in radians.
* @param {number} frac Fraction.
* @returns {number}
*/
function interpolate(from, to, frac) {
var d = diff(from, to) * frac;
if (distCW(to, from) >= Math.PI) {
return from - diff;
} else {
return from + diff;
}
}
/**
* @description Normalize angle.
* @memberOf Wolf.Angle
* @param {number} angle
* @returns {number}
*/
function normalize(angle) {
while (angle < 0) {
angle += (2 * Math.PI);
}
while (angle >= (2 * Math.PI)) {
angle -= (2 * Math.PI);
}
return angle;
}
/**
* @description Linear interpolate allowing for the Modulo 360 problem.
* @memberOf Wolf.Angle
* @param {number} from Angle in radians.
* @param {number} to Angle in radians.
* @param {number} frac fraction.
* @returns {number}
*/
function lerp(from, to, frac) {
if (to - from > 180) {
to -= 360;
}
if (to - from < -180) {
to += 360;
}
return from + frac * (to - from);
}
return {
diff : diff,
distCW : distCW,
normalize : normalize,
interpolate : interpolate,
lerp : lerp
}
})();

149
js/areas.js Normal file
View File

@@ -0,0 +1,149 @@
/*
* ===========================================================================
*
* Wolf3D Browser Version GPL Source Code
* Copyright (C) 2012 id Software LLC, a ZeniMax Media company.
*
* This file is part of the Wolf3D Browser Version GPL Source Code ("Wolf3D Browser Source Code").
*
* Wolf3D Browser Source Code is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* Wolf3D Browser Source Code is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License version 2
* along with Wolf3D Browser Source Code. If not, see <http://www.gnu.org/licenses/>.
*
* If you have questions concerning this license, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
*
* ===========================================================================
*/
/**
* @namespace
* @description Area management
*/
Wolf.Areas = (function() {
/*
Notes:
Open doors connect two areas, so sounds will travel between them and sight
will be checked when the player is in a connected area.
Areaconnect is incremented/decremented by each door. If >0 they connect.
Every time a door opens or closes the areabyplayer matrix gets recalculated.
An area is true if it connects with the player's current spor.
*/
/**
* @description Initialize areas
* @memberOf Wolf.Areas
* @param {object} levelState The level state object
* @param {number} areanumber Initial area
*/
function init(level, areanumber) {
level.state.areaconnect = [];
level.state.areabyplayer = [];
for (var i=0;i<Wolf.NUMAREAS;i++) {
level.state.areaconnect[i] = [];
for (var j=0;j<Wolf.NUMAREAS;j++) {
level.state.areaconnect[i][j] = 0;
}
level.state.areabyplayer[i] = false;
}
level.state.areabyplayer[areanumber] = true;
}
/**
* @private
* @description Scans outward from playerarea, marking all connected areas.
* @param {object} level The level object
* @param {number} areanumber Area
*/
function recursiveConnect(level, areanumber) {
for (var i = 0;i < Wolf.NUMAREAS; ++i) {
if (level.state.areaconnect[areanumber][i] && !level.state.areabyplayer[i]) {
level.state.areabyplayer[i] = true;
recursiveConnect(level, i);
}
}
}
/**
* @description Connect area.
* @memberOf Wolf.Areas
* @param {object} level The level object
* @param {number} areanumber New area
*/
function connect(level, areanumber) {
var i, c = 0;
if (areanumber >= Wolf.NUMAREAS) {
throw new Error("areanumber >= Wolf.NUMAREAS");
}
level.state.areabyplayer = [];
level.state.areabyplayer[areanumber] = true;
recursiveConnect(level, areanumber);
for (i = 0; i < Wolf.NUMAREAS; i++) {
if (level.state.areabyplayer[i]) {
c++;
}
}
}
/**
* @description Join ares
* @memberOf Wolf.Areas
* @param {object} level The level object
* @param {number} area1 Area 1
* @param {number} area2 Area 2
*/
function join(level, area1, area2) {
if (area1 < 0 || area1 >= Wolf.NUMAREAS) {
throw new Error("area1 < 0 || area1 >= Wolf.NUMAREAS");
}
if (area2 < 0 || area2 >= Wolf.NUMAREAS) {
throw new Error("area2 < 0 || area2 >= Wolf.NUMAREAS");
}
level.state.areaconnect[area1][area2]++;
level.state.areaconnect[area2][area1]++;
}
/**
* @description Disconnect ares
* @memberOf Wolf.Areas
* @param {object} level The level object
* @param {number} area1 Area 1
* @param {number} area2 Area 2
*/
function disconnect(level, area1, area2) {
if (area1 < 0 || area1 >= Wolf.NUMAREAS) {
throw new Error("area1 < 0 || area1 >= Wolf.NUMAREAS");
}
if (area2 < 0 || area2 >= Wolf.NUMAREAS) {
throw new Error("area2 < 0 || area2 >= Wolf.NUMAREAS");
}
level.state.areaconnect[area1][area2]--;
level.state.areaconnect[area2][area1]--;
}
return {
init : init,
connect : connect,
join : join,
disconnect : disconnect
};
})();

422
js/doors.js Normal file
View File

@@ -0,0 +1,422 @@
/*
* ===========================================================================
*
* Wolf3D Browser Version GPL Source Code
* Copyright (C) 2012 id Software LLC, a ZeniMax Media company.
*
* This file is part of the Wolf3D Browser Version GPL Source Code ("Wolf3D Browser Source Code").
*
* Wolf3D Browser Source Code is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* Wolf3D Browser Source Code is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License version 2
* along with Wolf3D Browser Source Code. If not, see <http://www.gnu.org/licenses/>.
*
* If you have questions concerning this license, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
*
* ===========================================================================
*/
/**
* @namespace
* @description Door management
*/
Wolf.Doors = (function() {
Wolf.setConsts({
CLOSEWALL : Wolf.MINDIST, // Space between wall & player
MAXDOORS : 64, // max number of sliding doors
MAX_DOORS : 256, // jseidelin: doesn't look like this is used?
DOOR_TIMEOUT : 300,
DOOR_MINOPEN : 50,
DOOR_FULLOPEN : 63,
DOOR_VERT : 255,
DOOR_HORIZ : 254,
DOOR_E_VERT : 253,
DOOR_E_HORIZ : 252,
DOOR_G_VERT : 251,
DOOR_G_HORIZ : 250,
DOOR_S_VERT : 249,
DOOR_S_HORIZ : 248,
FIRST_DOOR : 248,
LAST_LOCK : 251,
TEX_DOOR : 98,
// TEX_DOOR : 126,
dr_closing : -1,
dr_closed : 0,
dr_opening : 1,
dr_open : 2
});
Wolf.setConsts({
// texture IDs used by cache routines
TEX_DDOOR : (0 + Wolf.TEX_DOOR), // Simple Door
TEX_PLATE : (2 + Wolf.TEX_DOOR), // Door Plate
TEX_DELEV : (4 + Wolf.TEX_DOOR), // Elevator Door
TEX_DLOCK : (6 + Wolf.TEX_DOOR) // Locked Door
});
/**
* @description Reset doors in the level
* @memberOf Wolf.Doors
* @param {object} level The level object.
*/
function reset(level) {
level.state.numDoors = 0;
for (var x=0;x<64;x++) {
level.state.doorMap[x] = [];
for (var y=0;y<64;y++) {
level.state.doorMap[x][y] = 0;
}
}
}
/**
* @description Spawn a door at the specified position.
* @memberOf Wolf.Doors
* @param {object} level The level object.
* @param {number} x The x coordinate.
* @param {number} y The y coordinate.
* @param {number} type The door type.
* @returns {number} The index of the new door.
*/
function spawn(level, x, y, type) {
if (level.state.numDoors >= Wolf.MAXDOORS) {
throw new Error("Too many Doors on level!");
}
var door = level.state.doorMap[x][y] = {
type : -1,
vertical : 0,
texture : -1,
ticcount : 0
};
switch(type) {
case 0x5A:
door.type = Wolf.DOOR_VERT;
door.vertical = true;
door.texture = Wolf.TEX_DDOOR + 1;
break;
case 0x5B:
door.type = Wolf.DOOR_HORIZ;
door.vertical = false;
door.texture = Wolf.TEX_DDOOR;
break;
case 0x5C:
door.type = Wolf.DOOR_G_VERT;
door.vertical = true;
door.texture = Wolf.TEX_DLOCK;
break;
case 0x5D:
door.type = Wolf.DOOR_G_HORIZ;
door.vertical = false;
door.texture = Wolf.TEX_DLOCK;
break;
case 0x5E:
door.type = Wolf.DOOR_S_VERT;
door.vertical = true;
door.texture = Wolf.TEX_DLOCK + 1;
break;
case 0x5F:
door.type = Wolf.DOOR_S_HORIZ;
door.vertical = false;
door.texture = Wolf.TEX_DLOCK + 1;
break;
case 0x64:
door.type = Wolf.DOOR_E_VERT;
door.vertical = true;
door.texture = Wolf.TEX_DELEV + 1;
break;
case 0x65:
door.type = Wolf.DOOR_E_HORIZ;
door.vertical = false;
door.texture = Wolf.TEX_DELEV;
break;
default:
throw new Error("Unknown door type: " + type);
}
door.tile = {
x : x,
y : y
};
door.action = Wolf.dr_closed;
level.state.doors[level.state.numDoors] = door;
level.state.numDoors++;
return level.state.numDoors - 1;
}
/**
* @description Check to see if a door is open. If there are no doors in tile assume a closed door!
* @memberOf Wolf.Doors
* @param {object} doors The door object.
* @returns {number} DOOR_FULLOPEN if door is opened,
0 if door is closed,
>0 <DOOR_FULLOPEN if partially opened.
*/
function opened(door) {
return door.action == Wolf.dr_open ? Wolf.DOOR_FULLOPEN : door.ticcount;
}
/**
* @description Process door actions.
* @memberOf Wolf.Doors
* @param {object} level The level object
* @param {object} player The player object
* @param {number} tics Tics since last
*/
function process(level, player, tics) {
if (player.playstate == Wolf.ex_victory) {
return;
}
for (var n=0;n<level.state.numDoors;++n) {
var door = level.state.doors[n],
doorPos = {
x : Wolf.TILE2POS(door.tile.x),
y : Wolf.TILE2POS(door.tile.y)
};
switch (door.action) {
case Wolf.dr_closed: // this door is closed!
continue;
case Wolf.dr_opening:
if (door.ticcount >= Wolf.DOOR_FULLOPEN) { // door fully opened!
door.action = Wolf.dr_open;
door.ticcount = 0;
} else { // opening!
if (door.ticcount == 0) {
// door is just starting to open, so connect the areas
Wolf.Areas.join(level, door.area1, door.area2);
Wolf.Areas.connect(level, player.areanumber);
if (level.state.areabyplayer[door.area1]) { // Door Opening sound!
Wolf.Sound.startSound(player.position, doorPos, 1, Wolf.CHAN_AUTO, "sfx/010.wav", 1, Wolf.ATTN_STATIC, 0);
}
}
door.ticcount += tics;
if (door.ticcount > Wolf.DOOR_FULLOPEN) {
door.ticcount = Wolf.DOOR_FULLOPEN;
}
}
break;
case Wolf.dr_closing:
if (door.ticcount <= 0) { // door fully closed! disconnect areas!
Wolf.Areas.disconnect(level, door.area1, door.area2);
Wolf.Areas.connect(level, player.areanumber);
door.ticcount = 0;
door.action = Wolf.dr_closed;
} else { // closing!
if (door.ticcount == Wolf.DOOR_FULLOPEN) {
if (level.state.areabyplayer[door.area1]) { // Door Closing sound!
Wolf.Sound.startSound(player.position, doorPos, 1, Wolf.CHAN_AUTO, "sfx/007.wav", 1, Wolf.ATTN_STATIC, 0);
}
}
door.ticcount -= tics;
if (door.ticcount < 0) {
door.ticcount = 0;
}
}
break;
case Wolf.dr_open:
if (door.ticcount > Wolf.DOOR_MINOPEN) {
// If player or something is in door do not close it!
if (!canCloseDoor(level, player, door.tile.x, door.tile.y, door.vertical)) {
door.ticcount = Wolf.DOOR_MINOPEN; // do not close door immediately!
}
}
if (door.ticcount >= Wolf.DOOR_TIMEOUT) {
// Door timeout, time to close it!
door.action = Wolf.dr_closing;
door.ticcount = Wolf.DOOR_FULLOPEN;
} else {
// Increase timeout!
door.ticcount += tics;
}
break;
} // End switch lvldoors->Doors[ n ].action
} // End for n = 0 ; n < lvldoors->numDoors ; ++n
}
/**
* @description Set the areas doors in a level
* @memberOf Wolf.Doors
* @param {object} level The level object.
* @param {array} areas The areas map.
*/
function setAreas(level) {
var n, x, y,
door;
for (n=0; n<level.state.numDoors ; ++n){
door = level.state.doors[n];
x = door.tile.x;
y = door.tile.y;
if (door.vertical) {
door.area1 = level.areas[x + 1][y] >= 0 ? level.areas[x + 1][y] : 0;
door.area2 = level.areas[x - 1][y] >= 0 ? level.areas[x - 1][y] : 0;
} else {
door.area1 = level.areas[x][y + 1] >= 0 ? level.areas[x][y + 1] : 0;
door.area2 = level.areas[x][y - 1] >= 0 ? level.areas[x][y - 1] : 0;
}
}
}
/**
* @description Open a door
* @memberOf Wolf.Doors
* @param {object} doors The door object.
*/
function open(door) {
if (door.action == Wolf.dr_open) {
door.ticcount = 0; // reset opened time
} else {
door.action = Wolf.dr_opening; // start opening it
}
}
/**
* @description Change the state of a door
* @private
* @param {object} level The level object.
* @param {object} player The player object.
* @param {object} doors The door object.
*/
function changeDoorState(level, player, door) {
if (door.action < Wolf.dr_opening ) {
open(door);
} else if (door.action == Wolf.dr_open && canCloseDoor(level, player, door.tile.x, door.tile.y, door.vertical)) {
// !@# for the iphone with automatic using, don't allow any door close actions
// Door->action = dr_closing;
// Door->ticcount = DOOR_FULLOPEN;
}
}
function canCloseDoor(level, player, x, y, vert ) {
var n,
tileX = Wolf.POS2TILE(player.position.x),
tileY = Wolf.POS2TILE(player.position.y),
guard;
if (tileX == x && tileY == y ) {
return false;
}
if (vert) {
if (tileY == y) {
if (Wolf.POS2TILE(player.position.x + Wolf.CLOSEWALL) == x) {
return false;
}
if (Wolf.POS2TILE(player.position.x - Wolf.CLOSEWALL) == x) {
return false;
}
}
for (n = 0; n<level.state.numGuards;++n) {
guard = level.state.guards[n];
if (guard.tile.x == x && guard.tile.y == y ) {
return false; // guard in door
}
if (guard.tile.x == x - 1 && guard.tile.y == y && Wolf.POS2TILE(guard.x + Wolf.CLOSEWALL) == x) {
return false; // guard in door
}
if (guard.tile.x == x + 1 && guard.tile.y == y && Wolf.POS2TILE(guard.x - Wolf.CLOSEWALL) == x) {
return false; // guard in door
}
}
} else {
if (tileX == x) {
if (Wolf.POS2TILE(player.position.y + Wolf.CLOSEWALL) == y) {
return false;
}
if (Wolf.POS2TILE(player.position.y - Wolf.CLOSEWALL) == y) {
return false;
}
}
for (n = 0; n<level.state.numGuards;++n) {
var guard = level.state.guards[n];
if (guard.tile.x == x && guard.tile.y == y ) {
return false; // guard in door
}
if (guard.tile.x == x && guard.tile.y == y - 1 && Wolf.POS2TILE(guard.y + Wolf.CLOSEWALL) == y) {
return false; // guard in door
}
if (guard.tile.x == x && guard.tile.y == y + 1 && Wolf.POS2TILE(guard.y - Wolf.CLOSEWALL) == y) {
return false; // guard in door
}
}
}
return true;
}
/**
* @description Try to use a door with keys that the player has.
* @memberOf Wolf.Doors
* @param {object} level The level object
* @param {object} player The player object
* @param {object} door The door object
* @returns {boolean} Always returns true.
*/
function tryUse(level, player, door ) {
switch (door.type) {
case Wolf.DOOR_VERT:
case Wolf.DOOR_HORIZ:
case Wolf.DOOR_E_VERT:
case Wolf.DOOR_E_HORIZ:
changeDoorState(level, player, door); // does not require key!
break;
case Wolf.DOOR_G_VERT:
case Wolf.DOOR_G_HORIZ:
if (player.items & Wolf.ITEM_KEY_1) {
changeDoorState(level, player, door);
} else {
Wolf.Game.notify("You need a gold key");
}
break;
case Wolf.DOOR_S_VERT:
case Wolf.DOOR_S_HORIZ:
if (player.items & Wolf.ITEM_KEY_2) {
changeDoorState(level, player, door);
} else {
Wolf.Game.notify("You need a silver key");
}
break;
}
return true; // FIXME
}
return {
reset : reset,
spawn : spawn,
opened : opened,
open : open,
tryUse : tryUse,
process : process,
setAreas : setAreas
};
})();

80
js/episodes.js Normal file
View File

@@ -0,0 +1,80 @@
/*
* ===========================================================================
*
* Wolf3D Browser Version GPL Source Code
* Copyright (C) 2012 id Software LLC, a ZeniMax Media company.
*
* This file is part of the Wolf3D Browser Version GPL Source Code ("Wolf3D Browser Source Code").
*
* Wolf3D Browser Source Code is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* Wolf3D Browser Source Code is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License version 2
* along with Wolf3D Browser Source Code. If not, see <http://www.gnu.org/licenses/>.
*
* If you have questions concerning this license, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
*
* ===========================================================================
*/
/**
* @namespace
* @description Episode data
*/
Wolf.Episodes = [
{
name : "Escape from Wolfenstein",
enabled : true,
levels : [
{ file : "maps/w00.map", partime : 1.5 },
{ file : "maps/w01.map", partime : 2.0 },
{ file : "maps/w02.map", partime : 2.0 },
{ file : "maps/w03.map", partime : 3.5 },
{ file : "maps/w04.map", partime : 3.0 },
{ file : "maps/w05.map", partime : 3.0 },
{ file : "maps/w06.map", partime : 2.5 },
{ file : "maps/w07.map", partime : 2.5 },
{ file : "maps/w08.map", partime : 0.0 },
{ file : "maps/w09.map", partime : 0.0, secret : true }
]
},{
name : "Operation: Eisenfaust",
enabled : true,
levels : [
{ file : "maps/w10.map", partime : 1.5 },
{ file : "maps/w11.map", partime : 3.5 },
{ file : "maps/w12.map", partime : 3.0 },
{ file : "maps/w13.map", partime : 2.0 },
{ file : "maps/w14.map", partime : 4.0 },
{ file : "maps/w15.map", partime : 6.0 },
{ file : "maps/w16.map", partime : 1.0 },
{ file : "maps/w17.map", partime : 3.0 },
{ file : "maps/w18.map", partime : 0.0 },
{ file : "maps/w19.map", partime : 0.0, secret : true }
]
},{
name : "Die, Fuhrer, Die!",
enabled : true,
levels : [
{ file : "maps/w20.map", partime : 1.5 },
{ file : "maps/w21.map", partime : 1.5 },
{ file : "maps/w22.map", partime : 2.5 },
{ file : "maps/w23.map", partime : 2.5 },
{ file : "maps/w24.map", partime : 3.5 },
{ file : "maps/w25.map", partime : 2.5 },
{ file : "maps/w26.map", partime : 2.0 },
{ file : "maps/w27.map", partime : 6.0 },
{ file : "maps/w28.map", partime : 0.0 },
{ file : "maps/w29.map", partime : 0.0, secret : true }
]
}
];

217
js/file.js Normal file
View File

@@ -0,0 +1,217 @@
/*
* ===========================================================================
*
* Wolf3D Browser Version GPL Source Code
* Copyright (C) 2012 id Software LLC, a ZeniMax Media company.
*
* This file is part of the Wolf3D Browser Version GPL Source Code ("Wolf3D Browser Source Code").
*
* Wolf3D Browser Source Code is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* Wolf3D Browser Source Code is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License version 2
* along with Wolf3D Browser Source Code. If not, see <http://www.gnu.org/licenses/>.
*
* If you have questions concerning this license, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
*
* ===========================================================================
*/
/**
* @namespace
* @description Binary file reading
*/
Wolf.File = (function() {
/**
* @description Open a file from URL
* @memberOf Wolf.File
* @param {string} url The URL to open
* @param {function} callback Is called when file has been loaded. Second argument is file obj.
*/
function openURL(url, callback) {
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function() {
if (xhr.readyState == 4) {
if (xhr.status >= 200 && xhr.status < 300 || xhr.status == 0) {
callback(null, {
data : xhr.responseText,
size : xhr.responseText.length,
position : 0
});
} else {
callback(new Error("Server returned HTTP status: " + xhr.status));
}
}
}
xhr.open("GET", url, true);
xhr.overrideMimeType('text/plain; charset=x-user-defined');
xhr.send(null);
}
function atob(str) {
str = str.replace(/=+$/, "");
var b64chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",
a, b, c, b1, b2, b3, b4,
chr = String.fromCharCode,
out = [];
for (var i=0,len=str.length;i<len;) {
b1 = b64chars.indexOf(str.charAt(i++));
b2 = b64chars.indexOf(str.charAt(i++));
b3 = b64chars.indexOf(str.charAt(i++));
b4 = b64chars.indexOf(str.charAt(i++));
a = ((b1 & 0x3F) << 2) | ((b2 >> 4) & 0x3);
b = ((b2 & 0xF) << 4) | ((b3 >> 2) & 0xF);
c = ((b3 & 0x3) << 6) | (b4 & 0x3F);
out.push(chr(a), chr(b), chr(c));
}
return out.join("");
}
/**
* @description Open a file from base64 filetable
* @memberOf Wolf.File
* @param {string} filename The name of the file to open
* @param {object} files The filetable
* @param {function} callback Is called when file has been loaded. Second argument is file obj.
*/
function open(filename, files, callback) {
var b64data = files[filename];
if (b64data) {
var data = atob(b64data);
callback(null, {
data : data,
size : data.length,
position : 0
});
} else {
callback(new Error("File not found: " + filename));
}
}
/**
* @description Read an unsigned 8-bit integer from a file and advance the file position.
* @memberOf Wolf.File
* @param {object} f The file
* @returns {number}
*/
function readUInt8(f) {
var b = f.data.charCodeAt(f.position) & 0xFF
f.position++;
return b;
}
/**
* @description Read a signed 8-bit integer from a file and advance the file position.
* @memberOf Wolf.File
* @param {object} f The file
* @returns {number}
*/
function readInt8(f) {
var v = readUInt8(f);
return v > 127 ? v - 256 : v;
}
/**
* @description Read an unsigned 16-bit integer from a file and advance the file position.
* @memberOf Wolf.File
* @param {object} f The file
* @returns {number}
*/
function readUInt16(f) {
var v = readUInt8(f) + (readUInt8(f) << 8);
return (v < 0) ? v + 0x10000 : v;
}
/**
* @description Read a signed 16-bit integer from a file and advance the file position.
* @memberOf Wolf.File
* @param {object} f The file
* @returns {number}
*/
function readInt16(f) {
var v = readUInt16(f);
return (v > 0x7fff) ? v - 0x10000 : v;
}
/**
* @description Read an unsigned 32-bit integer from a file and advance the file position.
* @memberOf Wolf.File
* @param {object} f The file
* @returns {number}
*/
function readUInt32(f) {
var b0 = readUInt8(f),
b1 = readUInt8(f),
b2 = readUInt8(f),
b3 = readUInt8(f),
v = ((((b3 << 8) + b2) << 8) + b1 << 8) + b0;
return (v < 0) ? v + 0x100000000 : v;
}
/**
* @description Read a signed 32-bit int from a file and advance the file position.
* @memberOf Wolf.File
* @param {object} f The file
* @returns {number}
*/
function readInt32(f) {
var v = readUInt32(f);
return (v > 0x7fffffff) ? v - 0x100000000 : v;
}
/**
* @description Read a string from a file and advance the file position.
* @memberOf Wolf.File
* @param {object} f The file
* @param {number} length The length of the string
* @returns {string}
*/
function readString(f, length) {
var str = f.data.substr(f.position, length);
f.position += length;
return str;
}
/**
* @description Read an array of bytes a file and advance the file position.
* @memberOf Wolf.File
* @param {object} f The file
* @param {number} num The number of bytes to read
* @returns {array}
*/
function readBytes(f, num) {
var b = [];
for (var i=0;i<num;i++) {
b[i] = f.data.charCodeAt(f.position+i) & 0xFF;
}
f.position += num;
return b;
}
return {
open : open,
readInt8 : readInt8,
readUInt8 : readUInt8,
readInt16 : readInt16,
readUInt16 : readUInt16,
readInt32 : readInt32,
readUInt32 : readUInt32,
readBytes : readBytes,
readString : readString
};
})();

1351
js/game.js Normal file

File diff suppressed because it is too large Load Diff

359
js/input.js Normal file
View File

@@ -0,0 +1,359 @@
/*
* ===========================================================================
*
* Wolf3D Browser Version GPL Source Code
* Copyright (C) 2012 id Software LLC, a ZeniMax Media company.
*
* This file is part of the Wolf3D Browser Version GPL Source Code ("Wolf3D Browser Source Code").
*
* Wolf3D Browser Source Code is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* Wolf3D Browser Source Code is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License version 2
* along with Wolf3D Browser Source Code. If not, see <http://www.gnu.org/licenses/>.
*
* If you have questions concerning this license, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
*
* ===========================================================================
*/
/**
* @namespace
* @description Functions for capturing keyboard/mouse input
*/
Wolf.Input = (function() {
var keys,
lmbDown = false,
rmbDown = false,
bindings = [],
hasFocus = false,
mouseX = -1, mouseY = -1,
mouseMoveX = 0, mouseMoveY = 0;
function init() {
var game = $("#game"),
main = $("#main"),
renderer = $("#game .renderer");
if (!keys) {
keys = [];
$(document)
.on("keydown", function(e) {
e.preventDefault();
if (!Wolf.Game.isPlaying()) {
return;
}
keys[e.keyCode] = true;
if (bindings[e.keyCode]) {
for (var i=0,n=bindings[e.keyCode].length;i<n;i++) {
bindings[e.keyCode][i](e);
}
}
})
.on("keyup", function(e) {
e.preventDefault();
if (!Wolf.Game.isPlaying()) {
return;
}
keys[e.keyCode] = false;
})
.on("keypress", function(e) {
e.preventDefault();
})
.on("contextmenu", function(e) {
e.preventDefault();
})
.on("onrightclick", function(e) {
e.preventDefault();
})
.on("mousedown", function(e) {
window.focus();
e.preventDefault();
})
.on("mouseup", function(e) {
e.preventDefault();
});
$("#game")
.on("mousedown", function(e) {
if (hasFocus) {
if (e.which == 1) {
lmbDown = true;
} else if (e.which == 3) {
rmbDown = true;
}
} else {
window.focus();
}
e.preventDefault();
})
.on("mouseup", function(e) {
if (hasFocus) {
if (e.which == 1) {
lmbDown = false;
} else if (e.which == 3) {
rmbDown = false;
}
}
e.preventDefault();
})
.on("mousemove", function(e) {
if (!hasFocus) {
return;
}
if (isPointerLocked()) {
if ("webkitMovementX" in e.originalEvent) {
mouseMoveX += e.originalEvent.webkitMovementX;
mouseMoveY += e.originalEvent.webkitMovementY;
} else if ("mozMovementX" in e.originalEvent) {
mouseMoveX += e.originalEvent.mozMovementX;
mouseMoveY += e.originalEvent.mozMovementY;
} else if ("movementX" in e.originalEvent) {
mouseMoveX += e.originalEvent.movementX;
mouseMoveY += e.originalEvent.movementY;
}
} else {
if (Wolf.Game.isFullscreen()) {
mouseX = e.pageX / window.innerWidth;
mouseY = e.pageY / window.innerHeight;
} else {
var offset = main.offset();
mouseX = (e.pageX - offset.left) / main.width();
mouseY = (e.pageY - offset.top) / main.height();
}
}
e.preventDefault();
});
// reset keys and mouse if window/tab loses focus
$(window).on("blur", function(e) {
hasFocus = false;
reset();
});
$(window).on("focus", function(e) {
hasFocus = true;
});
}
}
function reset() {
resetMouse();
keys = [];
}
function resetMouse() {
lmbDown = false;
rmbDown = false;
mouseX = mouseY = 0.5;
}
function bindKey(k, handler) {
var keyCode = Wolf.Keys[k];
if (!bindings[keyCode]) {
bindings[keyCode] = [];
}
bindings[keyCode].push(handler);
}
/**
* @memberOf Wolf.Input
* @description Check if one of the specified keys is pressed.
* @param {array} keys Array of key names.
* @returns {boolean} True if a key is pressed, otherwise false.
*/
function checkKeys(ckeys) {
for (var i=0;i<ckeys.length;i++) {
var k = ckeys[i];
if (!!keys[Wolf.Keys[k]]) {
return true;
}
}
return false;
}
/**
* @memberOf Wolf.Input
* @description Clear status for keys.
* @param {array} keys Array of key names.
*/
function clearKeys(ckeys) {
for (var i=0;i<ckeys.length;i++) {
var k = ckeys[i];
keys[Wolf.Keys[k]] = false;
}
return false;
}
function leftMouseDown() {
return lmbDown;
}
function rightMouseDown() {
return rmbDown;
}
function getMouseCoords() {
if (mouseX < 0 || mouseX > 1 || mouseY < 0 || mouseY > 1) {
return null;
} else {
return {
x : (mouseX - 0.5) * 2,
y : (mouseY - 0.5) * 2
};
}
}
function getMouseMovement() {
var x = mouseMoveX,
y = mouseMoveY;
mouseMoveX = 0;
mouseMoveY = 0;
return {
x : x / screen.width,
y : y / screen.height
};
}
function getPointer() {
var pointer = navigator.pointer ||
navigator.webkitPointer ||
navigator.mozPointer ||
navigator.msPointer ||
navigator.oPointer;
return pointer;
}
function isPointerLocked() {
var pointer = getPointer();
return pointer && pointer.isLocked && pointer.isLocked();
}
function lockPointer() {
var pointer = getPointer();
if (!pointer) {
return;
}
if (Wolf.Game.isFullscreen()) {
pointer.lock($("#game")[0],
function(e) {
Wolf.log("Pointer locked")
}, function(e) {
Wolf.log("Could not lock pointer: " + e);
}
);
}
}
function unlockPointer() {
var pointer = getPointer();
if (!pointer) {
return;
}
pointer.unlock($("#game")[0]);
}
return {
init : init,
reset : reset,
resetMouse : resetMouse,
checkKeys : checkKeys,
clearKeys : clearKeys,
bindKey : bindKey,
leftMouseDown : leftMouseDown,
rightMouseDown : rightMouseDown,
getMouseCoords : getMouseCoords,
getMouseMovement : getMouseMovement,
isPointerLocked : isPointerLocked,
lockPointer : lockPointer,
unlockPointer : unlockPointer
};
})();
Wolf.Keys = {
LEFT : 37,
UP : 38,
RIGHT : 39,
DOWN : 40,
ENTER : 13,
SPACE : 32,
SHIFT : 16,
CTRL : 17,
ALT : 18,
ESC : 27,
HOME : 36,
END : 35,
DEL : 46,
INS : 45,
PGUP : 33,
PGDN : 34,
SLASH : 111,
MINUS : 109,
PLUS : 107,
COMMA : 188,
PERIOD : 190,
1 : 49,
2 : 50,
3 : 51,
4 : 52,
5 : 53,
6 : 54,
7 : 55,
8 : 56,
9 : 57,
0 : 58,
A : 65,
B : 66,
C : 67,
D : 68,
E : 69,
F : 70,
G : 71,
H : 72,
I : 73,
J : 74,
K : 75,
L : 76,
M : 77,
N : 78,
O : 79,
P : 80,
Q : 81,
R : 82,
S : 83,
T : 84,
U : 85,
V : 86,
W : 87,
X : 88,
Y : 89,
Z : 90,
F1 : 112,
F2 : 113,
F3 : 114,
F4 : 115,
F5 : 116,
F6 : 117,
F7 : 118,
F8 : 119,
F9 : 120,
F10 : 121,
F11 : 122,
F12 : 123
};

1134
js/level.js Normal file

File diff suppressed because it is too large Load Diff

126
js/load.js Normal file
View File

@@ -0,0 +1,126 @@
/*
* ===========================================================================
*
* Wolf3D Browser Version GPL Source Code
* Copyright (C) 2012 id Software LLC, a ZeniMax Media company.
*
* This file is part of the Wolf3D Browser Version GPL Source Code ("Wolf3D Browser Source Code").
*
* Wolf3D Browser Source Code is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* Wolf3D Browser Source Code is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License version 2
* along with Wolf3D Browser Source Code. If not, see <http://www.gnu.org/licenses/>.
*
* If you have questions concerning this license, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
*
* ===========================================================================
*/
(function($) {
// these files are preloaded while the title screen is showing
var files = [
"js/requestanimframe.js",
"js/wolf.js",
"js/random.js",
"js/angle.js",
"js/math.js",
"js/input.js",
"js/sound.js",
"js/menu.js",
"js/file.js",
"js/game.js",
"js/player.js",
"js/sprites.js",
"js/powerups.js",
"js/ai.js",
"js/actorai.js",
"js/actors.js",
"js/actstat.js",
"js/weapon.js",
"js/doors.js",
"js/pushwall.js",
"js/areas.js",
"js/level.js",
"js/raycaster.js",
"js/renderer.js",
"js/episodes.js",
"js/maps.js",
"preload!art/menubg_main.png",
"preload!art/menuitems.png",
"preload!art/menuselector.png"
];
// these files are preloaded in the background after the menu is displayed.
// only non-essential files here
var files2 = [
"preload!art/menubg_episodes.png",
"preload!art/menuitems_episodes.png",
"preload!art/menubg_skill.png",
"preload!art/menubg_levels.png",
"preload!art/menuitems_levels.png",
"preload!art/skillfaces.png",
"preload!art/getpsyched.png",
"preload!art/menubg_control.png",
"preload!art/menulight.png",
"preload!art/menubg_customize.png",
"preload!art/control_keys.png",
"preload!art/confirm_newgame.png",
"preload!art/paused.png"
];
$(document).ready(function() {
var progress = $("<div>"),
n = 0;
progress.addClass("load-progress").appendTo("#title-screen");
$("#title-screen").show();
yepnope.addPrefix("preload", function(resource) {
resource.noexec = true;
resource.instead = function(input, callback) {
var image = new Image();
image.onload = callback;
image.onerror = callback;
image.src = input.substr(input.lastIndexOf("!")+1);
};
return resource;
});
Modernizr.load([
{
load : files,
callback : function(file) {
progress.width((++n / files.length) * 100 + "%");
},
complete : function() {
progress.remove();
$("#title-screen").fadeOut(1500, function() {
Wolf.Input.init();
Wolf.Game.init();
Wolf.Menu.show();
});
// preload non-essential art
Modernizr.load(files2);
}
}
]);
});
})(jQuery);

37
js/maps.sample.js Normal file
View File

@@ -0,0 +1,37 @@
/*
* ===========================================================================
*
* Wolf3D Browser Version GPL Source Code
* Copyright (C) 2012 id Software LLC, a ZeniMax Media company.
*
* This file is part of the Wolf3D Browser Version GPL Source Code ("Wolf3D Browser Source Code").
*
* Wolf3D Browser Source Code is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* Wolf3D Browser Source Code is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License version 2
* along with Wolf3D Browser Source Code. If not, see <http://www.gnu.org/licenses/>.
*
* If you have questions concerning this license, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
*
* ===========================================================================
*/
/**
* @description Encoded map file data (Example)
*
* This file is a table of base64 encoded map data.
* Note: No actual data is included in this release.
*/
Wolf.MapData = {
"maps/w00.map" : "... base64 encoded map file data here ...",
"maps/w01.map" : "... base64 encoded map file data here ..."
};

329
js/math.js Normal file
View File

@@ -0,0 +1,329 @@
/*
* ===========================================================================
*
* Wolf3D Browser Version GPL Source Code
* Copyright (C) 2012 id Software LLC, a ZeniMax Media company.
*
* This file is part of the Wolf3D Browser Version GPL Source Code ("Wolf3D Browser Source Code").
*
* Wolf3D Browser Source Code is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* Wolf3D Browser Source Code is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License version 2
* along with Wolf3D Browser Source Code. If not, see <http://www.gnu.org/licenses/>.
*
* If you have questions concerning this license, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
*
* ===========================================================================
*/
/**
* @namespace
* @description Math functions and lookup tables
*/
Wolf.Math = (function() {
// ------------------------- * LUTs * -------------------------
var SinTable = [], // [ ANG_360 + ANG_90 + 1 ],
CosTable = [], // SinTable + ANG_90,
TanTable = [], //[ ANG_360 + 1 ];
XnextTable = [], //[ ANG_360 + 1 ],
YnextTable = [], //[ ANG_360 + 1 ],
ColumnAngle = [], // [ 640 ]; // ViewAngle=PlayerAngle+ColumnAngle[curcolumn]; /in fines/
// Angle Direction Types & LUTs (Hard Coded! Please do not mess them)
q_first = 0, q_second = 1, q_third = 2, q_fourth = 3, // quadrant;
dir4_east = 0, dir4_north = 1, dir4_west = 2, dir4_south = 3, dir4_nodir = 4, // dir4type;
dir8_east = 0, dir8_northeast = 1, dir8_north = 2, dir8_northwest = 3, dir8_west = 4,
dir8_southwest = 5, dir8_south = 6, dir8_southeast = 7, dir8_nodir = 8, // dir8type;
dx4dir = [1, 0, -1, 0, 0], // dx & dy based on direction
dy4dir = [0, 1, 0, -1, 0],
dx8dir = [1, 1, 0, -1, -1, -1, 0, 1, 0], // dx & dy based on direction
dy8dir = [0, 1, 1, 1, 0, -1, -1, -1, 0],
opposite4 = [2, 3, 0, 1, 4],
opposite8 = [4, 5, 6, 7, 0, 1, 2, 3, 8],
dir4to8 = [0, 2, 4, 6, 8],
diagonal = [
/* east */ [dir8_nodir, dir8_nodir, dir8_northeast, dir8_nodir, dir8_nodir, dir8_nodir, dir8_southeast, dir8_nodir, dir8_nodir],
[dir8_nodir, dir8_nodir, dir8_nodir, dir8_nodir, dir8_nodir, dir8_nodir, dir8_nodir, dir8_nodir, dir8_nodir],
/* north */ [dir8_northeast, dir8_nodir, dir8_nodir, dir8_nodir, dir8_northwest, dir8_nodir, dir8_nodir, dir8_nodir, dir8_nodir],
[dir8_nodir, dir8_nodir, dir8_nodir, dir8_nodir, dir8_nodir, dir8_nodir, dir8_nodir, dir8_nodir, dir8_nodir],
/* west */ [dir8_nodir, dir8_nodir, dir8_northwest, dir8_nodir, dir8_nodir, dir8_nodir, dir8_southwest, dir8_nodir, dir8_nodir],
[dir8_nodir, dir8_nodir, dir8_nodir, dir8_nodir, dir8_nodir, dir8_nodir, dir8_nodir, dir8_nodir, dir8_nodir],
/* south */ [dir8_southeast, dir8_nodir, dir8_nodir, dir8_nodir, dir8_southwest, dir8_nodir, dir8_nodir, dir8_nodir, dir8_nodir],
[dir8_nodir, dir8_nodir, dir8_nodir, dir8_nodir, dir8_nodir, dir8_nodir, dir8_nodir, dir8_nodir, dir8_nodir],
[dir8_nodir, dir8_nodir, dir8_nodir, dir8_nodir, dir8_nodir, dir8_nodir, dir8_nodir, dir8_nodir, dir8_nodir]
],
// dir of delta tooks dx{-1|0|1}+1 & dy{-1|0|1}+1 and give direction
dir4d = [
[dir4_nodir, dir4_west , dir4_nodir],
[dir4_south, dir4_nodir, dir4_north],
[dir4_nodir, dir4_east , dir4_nodir]
],
dir8angle = [Wolf.ANG_0, Wolf.ANG_45, Wolf.ANG_90, Wolf.ANG_135, Wolf.ANG_180, Wolf.ANG_225, Wolf.ANG_270, Wolf.ANG_315, Wolf.ANG_0];
dir4angle = [Wolf.ANG_0, Wolf.ANG_90, Wolf.ANG_180, Wolf.ANG_270, Wolf.ANG_0];
/**
* @private
* @description Build LUTs, etc.
*/
function buildTables() {
var angle, tanfov2, tanval, value,
n;
for (n = 0; n <= Wolf.ANG_90 ; ++n) {
angle = Wolf.FINE2RAD(n);
value = Math.sin(angle);
SinTable[n] = SinTable[Wolf.ANG_180 - n] = SinTable[n + Wolf.ANG_360] = value;
SinTable[Wolf.ANG_180 + n] = SinTable[Wolf.ANG_360 - n] = -value;
}
for (n = 0; n <= SinTable.length - Wolf.ANG_90; ++n) {
CosTable[n] = SinTable[n + Wolf.ANG_90];
}
for (n = 0; n <= Wolf.ANG_360 ; ++n) {
angle = Wolf.FINE2RAD(n); //angle is in radians, n is in FINEs
if (n == Wolf.ANG_90 || n == Wolf.ANG_270) {
TanTable[n] = Math.tan(Wolf.FINE2RAD(n - 0.5)); // infinity
YnextTable[n] = (Wolf.FLOATTILE * Math.tan(Wolf.FINE2RAD(n - 0.5)))>>0; // infinity
} else {
TanTable[n] = Math.tan(angle);
YnextTable[n] = (Wolf.FLOATTILE * Math.tan(angle))>>0;
}
if(n == Wolf.ANG_0 || n == Wolf.ANG_360) {
XnextTable[n] = (Wolf.FLOATTILE / Math.tan(Wolf.FINE2RAD(n + 0.5)))>>0; // infinity
} else if (n == Wolf.ANG_180) {
XnextTable[n] = (Wolf.FLOATTILE / Math.tan(Wolf.FINE2RAD(n - 0.5)))>>0; // -infinity
} else if (n == Wolf.ANG_90 || n == Wolf.ANG_270) {
XnextTable[n] = 0;
} else {
XnextTable[n] = (Wolf.FLOATTILE / Math.tan(angle))>>0;
}
}
tanfov2 = (Math.tan(Wolf.DEG2RAD((calcFov(75, Wolf.XRES, Wolf.YRES) / 2.0)))) * (Wolf.XRES / Wolf.YRES);
for (n = 0; n < Wolf.XRES; ++n) {
tanval = tanfov2 * (-1.0 + 2.0 * n / (Wolf.XRES-1));
ColumnAngle[n] = Wolf.RAD2FINE(Math.atan(tanval)) >> 0;
}
Wolf.Random.init(1); // random number generators
return 1;
}
/**
* @description Calculate the field of view.
* @memberOf Wolf.Math
* @param {number} fovX Must be within 1 and 179 degrees.
* @param {number} width Width of viewing area.
* @param {number} height Height of viewing area.
* @returns {number} The field of view in degrees.
*/
function calcFov(fovX, width, height) {
if (fovX < 1 || fovX > 179) {
throw Error("Bad fov: " + fovX );
}
return Wolf.RAD2DEG(Math.atan(height / (width / Math.tan(fovX / 360 * Math.PI)))) * 2;
}
/**
* @description Clips angle to [0..360] bounds.
* @memberOf Wolf.Math
* @param {number} alpha Angle in degrees.
* @returns {number} Normalized angle.
*/
function normalizeAngle(alpha) {
if (alpha > Wolf.ANG_360) {
alpha %= Wolf.ANG_360;
}
if (alpha < Wolf.ANG_0) {
alpha = Wolf.ANG_360 - (-alpha) % Wolf.ANG_360;
}
return alpha;
}
/**
* @description Get quadrant.
* @memberOf Wolf.Math
* @param {number} angle Radian angle.
* @returns {number}
*/
function getQuadrant(angle) {
angle = Wolf.Angle.normalize(angle);
if (angle < Math.PI / 2) {
return q_first;
} else if (angle < Math.PI) {
return q_second;
} else if (angle < 3 * Math.PI / 2) {
return q_third;
} else {
return q_fourth;
}
}
/**
* @description Get 4 point direction.
* @memberOf Wolf.Math
* @param {number} angle Radian angle.
* @returns {number} Directional point.
*/
function get4dir(angle) {
angle = Wolf.Angle.normalize(angle + Math.PI / 4);
if (angle < Math.PI / 2) {
return dir4_east;
} else if( angle < Math.PI ) {
return dir4_north;
} else if( angle < 3 * Math.PI / 2 ) {
return dir4_west;
} else {
return dir4_south;
}
}
/**
* @description Get 8 point direction.
* @memberOf Wolf.Math
* @param {number} angle Radian angle.
* @returns {number} Directional point.
*/
function get8dir(angle) {
angle = Wolf.Angle.normalize(angle + Math.PI / 12);
if ( angle <= (Math.PI / 4)) {
return dir8_east;
} else if (angle < (Math.PI / 2)) {
return dir8_northeast;
} else if (angle <= (3 * Math.PI / 4)) {
return dir8_north;
} else if (angle < Math.PI) {
return dir8_northwest;
} else if (angle <= (5 * Math.PI / 4)) {
return dir8_west;
} else if (angle < (3 * Math.PI / 2)) {
return dir8_southwest;
} else if (angle <= (7 * Math.PI / 4)) {
return dir8_south;
} else {
return dir8_southeast;
}
}
/**
* @description calculates distance between a point (x, y) and a line.
* @memberOf Wolf.Math
* @param {number} x X coord of point
* @param {number} y Y coord of point
* @param {number} a Line angle in degrees
* @returns {number} Distance
*/
function point2LineDist(x, y, a) {
return Math.abs( (x * SinTable[a] - y * CosTable[a]) >> 0);
}
/**
* @description Calculates line length to the point nearest to (poin).
* @memberOf Wolf.Math
* @param {number} x X coord of point
* @param {number} y Y coord of point
* @param {number} a Line angle in degrees
* @returns {number} Distance
*/
function lineLen2Point( x, y, a) {
return (x * CosTable[a] + y * SinTable[a]) >> 0;
}
/*
point2 = {x,y}
/ |
/ |
/ |
/a______|----------> x
point1 = {x, y}
*/
/**
* @description Returns angle in radians
* @memberOf Wolf.Math
* @param {number} x X coord of point
* @param {number} y Y coord of point
* @param {number} a Line angle in degrees
* @returns {number} Distance
*/
function transformPoint(point1X, point1Y, point2X, point2Y) {
var angle = Math.atan2(point1Y - point2Y, point1X - point2X);
return Wolf.Angle.normalize(angle);
}
buildTables();
return {
calcFov : calcFov,
normalizeAngle : normalizeAngle,
getQuadrant : getQuadrant,
get4dir : get4dir,
get8dir : get8dir,
point2LineDist : point2LineDist,
lineLen2Point : lineLen2Point,
transformPoint : transformPoint,
SinTable : SinTable,
CosTable : CosTable,
TanTable : TanTable,
XnextTable : XnextTable,
YnextTable : YnextTable,
ColumnAngle : ColumnAngle,
dir4_east : dir4_east,
dir4_north : dir4_north,
dir4_west : dir4_west,
dir4_south : dir4_south,
dir4_nodir : dir4_nodir,
dir8_east : dir8_east,
dir8_northeast : dir8_northeast,
dir8_north : dir8_north,
dir8_northwest : dir8_northwest,
dir8_west : dir8_west,
dir8_southwest : dir8_southwest,
dir8_south : dir8_south,
dir8_southeast : dir8_southeast,
dir8_nodir : dir8_nodir,
dx4dir : dx4dir,
dy4dir : dy4dir,
dx8dir : dx8dir,
dy8dir : dy8dir,
dir4angle : dir4angle,
dir8angle : dir8angle,
dir4to8 : dir4to8,
opposite4 : opposite4,
opposite8 : opposite8,
diagonal : diagonal
};
})();

622
js/menu.js Normal file
View File

@@ -0,0 +1,622 @@
/*
* ===========================================================================
*
* Wolf3D Browser Version GPL Source Code
* Copyright (C) 2012 id Software LLC, a ZeniMax Media company.
*
* This file is part of the Wolf3D Browser Version GPL Source Code ("Wolf3D Browser Source Code").
*
* Wolf3D Browser Source Code is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* Wolf3D Browser Source Code is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License version 2
* along with Wolf3D Browser Source Code. If not, see <http://www.gnu.org/licenses/>.
*
* If you have questions concerning this license, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
*
* ===========================================================================
*/
/**
* @namespace
* @description Game menu management
*/
Wolf.Menu = (function() {
var setupDone = false,
menuInputActive = false,
activeIndex = 0,
activeMouseItem = null,
activeEpisode,
messageBlink,
activeMessage,
activeSkill;
var keySprites = {},
i,
keySpriteNames = [
"BLANK",
"QUESTION",
"SHIFT",
"SPACE",
"CTRL",
"LEFT",
"RIGHT",
"UP",
"DOWN",
"ENTER",
"DEL",
"PGUP",
"PGDN",
"INS",
"SLASH",
"HOME",
"COMMA",
"PERIOD",
"PLUS",
"MINUS",
"0",
"1",
"2",
"3",
"4",
"5",
"6",
"7",
"8",
"9",
"A",
"B",
"C",
"D",
"E",
"F",
"G",
"H",
"I",
"J",
"K",
"L",
"M",
"N",
"O",
"P",
"Q",
"R",
"S",
"T",
"U",
"V",
"W",
"X",
"Y",
"Z"
];
for (i=0;i<keySpriteNames.length;i++) {
if (keySpriteNames[i] !== "") {
keySprites[keySpriteNames[i]] = i;
}
}
function playSound(file) {
Wolf.Sound.startSound(null, null, 1, Wolf.CHAN_AUTO, file, 1, Wolf.ATTN_NORM, 0);
}
function setActiveItem(item) {
playSound("lsfx/005.wav");
$("#menu div.menu.active li").removeClass("active");
item.addClass("active");
if ($("#menu div.menu.active").hasClass("skill")) {
$("#menu div.menu.active div.face")
.removeClass()
.addClass("face " + item.data("skill"));
}
}
/**
* @description Bind events to menu items
* @private
*/
function setupEvents() {
$(document).on("keydown", function(e) {
if (!$("#menu").is(":visible")) {
return;
}
if (!menuInputActive) {
return;
}
var oldActive = activeIndex;
switch (e.keyCode) {
case 38:
activeIndex--;
activeMouseItem = null;
break;
case 40:
activeIndex++;
activeMouseItem = null;
break;
case 13:
if (activeMouseItem) {
activeMouseItem.trigger("click");
} else {
$("#menu div.menu.active li").eq(activeIndex).trigger("click");
}
break;
case 27: // ESC
var back = $("#menu div.menu.active").data("backmenu");
if (back) {
playSound("lsfx/039.wav");
show(back);
}
return;
}
if (oldActive != activeIndex) {
var items = $("#menu div.menu.active li:not(.hidden)");
if (activeIndex < 0) {
activeIndex += items.length;
}
activeIndex %= items.length;
setActiveItem(items.eq(activeIndex));
}
});
$("#menu li").mouseover(function() {
if (!menuInputActive) {
return;
}
activeMouseItem = $(this);
setActiveItem($(this));
});
$("#menu li").on("click", function(e) {
if (!menuInputActive) {
return;
}
playSound("lsfx/032.wav");
var $this = $(this),
sub = $this.data("submenu");
if (sub) {
show(sub);
e.stopPropagation();
}
if ($this.hasClass("sfxon")) {
$("div.light", $this).addClass("on");
$("#menu li.sfxoff div.light").removeClass("on");
Wolf.Sound.toggleSound(true);
}
if ($this.hasClass("sfxoff")) {
$("div.light", $this).addClass("on");
$("#menu li.sfxon div.light").removeClass("on");
Wolf.Sound.toggleSound(false);
}
if ($this.hasClass("musicon")) {
$("div.light", $this).addClass("on");
$("#menu li.musicoff div.light").removeClass("on");
Wolf.Sound.toggleMusic(true);
}
if ($this.hasClass("musicoff")) {
$("div.light", $this).addClass("on");
$("#menu li.musicon div.light").removeClass("on");
Wolf.Sound.toggleMusic(false);
}
if ($this.hasClass("mouseenabled")) {
var mouseOn = Wolf.Game.isMouseEnabled();
$("div.light", $this).toggleClass("on", !mouseOn);
Wolf.Game.enableMouse(!mouseOn);
}
if ($this.hasClass("customizekeys")) {
customizeKeys($this);
e.stopPropagation();
}
});
$("#menu div.menu.episodes li").on("click", function(e) {
if (!menuInputActive) {
return;
}
var episode = $(this).data("episode");
if (Wolf.Game.isPlaying()) {
showMessage("confirm-newgame", true, function(result) {
if (result) {
activeEpisode = episode;
show("skill");
} else {
show("main");
}
});
} else {
activeEpisode = episode;
show("skill");
}
});
$("#menu div.menu.skill li").on("click", function(e) {
if (!menuInputActive) {
return;
}
activeSkill = $(this).data("skill");
});
$("#menu div.menu.main li.resumegame").on("click", function(e) {
if (!menuInputActive) {
return;
}
if (Wolf.Game.isPlaying()) {
hide();
Wolf.Game.resume();
}
});
$("#menu div.menu.main li.readthis").on("click", function(e) {
if (!menuInputActive) {
return;
}
menuInputActive = false;
$("#menu").fadeOut(null, function() {
showText("help", 11, function() {
$("#menu").fadeIn();
});
});
e.stopPropagation();
});
$("#menu div.menu.levels li").on("click", function(e) {
if (!menuInputActive) {
return;
}
var level, gameState;
hide();
level = $(this).data("level");
gameState = Wolf.Game.startGame(Wolf[activeSkill]);
Wolf.Game.startLevel(gameState, activeEpisode, level);
});
}
function customizeKeys($this) {
menuInputActive = false;
var current = 0,
isBinding = false,
blinkInterval;
function selectKey(index) {
if (index < 0) index += 4;
index = index % 4;
var currentSprite = $("span.active", $this);
if (currentSprite[0]) {
setCustomizeKey(
currentSprite.data("action"),
currentSprite.data("keyIndex"),
false
);
}
var sprite = $("span.k" + (index+1), $this);
setCustomizeKey(
sprite.data("action"),
sprite.data("keyIndex"),
true
);
current = index;
}
function activateKey(index) {
isBinding = true;
var sprite = $("span.k" + (index+1), $this),
blink = false;
setCustomizeKey(
sprite.data("action"), "QUESTION", true
);
if (blinkInterval) {
clearInterval(blinkInterval);
}
blinkInterval = setInterval(function() {
setCustomizeKey(sprite.data("action"), (blink = !blink) ? "BLANK" : "QUESTION", true);
}, 500)
}
function bindKey(index, key) {
var sprite = $("span.k" + (index+1), $this);
setCustomizeKey(
sprite.data("action"),
key,
true
);
Wolf.Game.bindControl(sprite.data("action"), [key]);
}
function exitCustomize() {
$(document).off("keydown", keyHandler);
initCustomizeMenu();
menuInputActive = true;
}
function keyHandler(e) {
var i;
if (isBinding) {
// look for key in bindable key codes. TODO: LUT?
for (i=2;i<keySpriteNames.length;i++) {
if (Wolf.Keys[keySpriteNames[i]] == e.keyCode) {
bindKey(current, keySpriteNames[i]);
isBinding = false;
clearInterval(blinkInterval);
blinkInterval = 0;
break;
}
}
return;
}
switch (e.keyCode) {
case 39: // right
selectKey(current + 1);
break;
case 37: // left
selectKey(current - 1);
break;
case 13: // enter
activateKey(current);
break;
case 27: // ESC
case 38: // up
case 40: // down
exitCustomize()
break;
}
}
$(document).on("keydown", keyHandler);
selectKey(current);
}
function setCustomizeKey(action, keyIndex, active) {
var menu = $("#menu div.menu.customize"),
x = (active ? -256 : 0),
y = -keySprites[keyIndex] * 32;
$("span." + action, menu)
.css(
"backgroundPosition", x + "px " + y + "px"
)
.data("keyIndex", keyIndex)
.toggleClass("active", !!active);
}
function initCustomizeMenu() {
var controls = Wolf.Game.getControls(),
keys = ["run", "use", "attack", "strafe", "left", "right", "up", "down"],
i;
for (i=0;i<keys.length;i++) {
setCustomizeKey(keys[i], controls[keys[i]][0])
}
}
function showMessage(name, blink, onclose) {
var box,
blinkOn = false;
activeMessage = name;
menuInputActive = false;
if (messageBlink) {
clearInterval(messageBlink);
messageBlink = 0;
}
$("#menu .message." + name).show();
box = $("#menu .message." + name + " div.box");
box.removeClass("blink");
if (blink) {
setInterval(function() {
blinkOn = !blinkOn;
if (blinkOn) {
box.addClass("blink");
} else {
box.removeClass("blink");
}
}, 200);
}
function close(value) {
playSound("lsfx/039.wav");
$(document).off("keydown", keyHandler);
$("#menu .message." + name).hide();
if (messageBlink) {
clearInterval(messageBlink);
messageBlink = 0;
}
menuInputActive = true;
if (onclose) {
onclose(value)
}
}
function keyHandler(e) {
switch (e.keyCode) {
case 27: // ESC
case 78: // N
close(false);
break;
case 89: // Y
close(true);
break;
}
}
$(document).on("keydown", keyHandler);
}
/**
* @description Show the menu
* @memberOf Wolf.Menu
*/
function show(menuName) {
var musicOn, soundOn, mouseOn;
if (!setupDone) {
setupEvents();
setupDone = true;
}
Wolf.Sound.startMusic("music/WONDERIN.ogg");
menuName = menuName || "main";
if (menuName == "main") {
if (Wolf.Game.isPlaying()) {
$("#menu div.menu.main li.resumegame")
.removeClass("hidden")
.show();
} else {
$("#menu div.menu.main li.resumegame")
.addClass("hidden")
.hide();
}
}
if (menuName == "customize") {
initCustomizeMenu();
}
if (menuName == "episodes") {
$("#menu div.menu.episodes li")
.removeClass("hidden")
.show();
if (!Wolf.Episodes[0].enabled) {
$("#menu div.menu.episodes li.episode-0")
.addClass("hidden")
.hide();
}
if (!Wolf.Episodes[1].enabled) {
$("#menu div.menu.episodes li.episode-1")
.addClass("hidden")
.hide();
}
if (!Wolf.Episodes[2].enabled) {
$("#menu div.menu.episodes li.episode-2")
.addClass("hidden")
.hide();
}
}
if (menuName == "sound") {
musicOn = Wolf.Sound.isMusicEnabled();
soundOn = Wolf.Sound.isSoundEnabled();
$("#menu li.sfxoff div.light").toggleClass("on", !soundOn);
$("#menu li.sfxon div.light").toggleClass("on", soundOn);
$("#menu li.musicoff div.light").toggleClass("on", !musicOn);
$("#menu li.musicon div.light").toggleClass("on", musicOn);
}
if (menuName == "control") {
mouseOn = Wolf.Game.isMouseEnabled();
$("#menu li.mouseenabled div.light").toggleClass("on", mouseOn);
}
if ($("#menu").data("menu")) {
$("#menu").removeClass($("#menu").data("menu"));
}
$("#menu div.menu").removeClass("active").hide();
$("#menu li").removeClass("active");
$("#menu").data("menu", menuName).addClass(menuName).show();
$("#menu div.menu." + menuName).addClass("active").show();
$("#menu div.menu." + menuName + " ul li").first().addClass("active");
$("#menu").focus();
activeIndex = 0;
activeMouseItem = null;
menuInputActive = true;
}
/**
* @description Hide the menu
* @memberOf Wolf.Menu
*/
function hide() {
$("#menu").hide();
menuInputActive = false;
}
function showText(name, num, closeFunction) {
var screen = $("#text-screen"),
current = 0;
menuInputActive = false;
function show(moveIdx) {
current += moveIdx;
if (current < 0) {
current += num;
}
current = current % num;
screen.css({
"backgroundImage" : "url(art/text-screens/" + name + "-" + (current+1) + ".png)"
});
// preload the next in the background
var next = (current + 1) % num,
nextImg = new Image();
nextImg.src = "art/text-screens/" + name + "-" + (next+1) + ".png";
}
function close() {
$(document).off("keydown", keyHandler);
screen.fadeOut(null, closeFunction);
menuInputActive = true;
}
function keyHandler(e) {
switch (e.keyCode) {
case 39: // right
show(1);
break;
case 37: // left
show(-1);
break;
case 27: // ESC
close();
break;
}
}
show(0);
screen.fadeIn(null, function() {
$(document).on("keydown", keyHandler);
});
}
return {
show : show,
hide : hide,
showText : showText
};
})();

814
js/player.js Normal file
View File

@@ -0,0 +1,814 @@
/*
* ===========================================================================
*
* Wolf3D Browser Version GPL Source Code
* Copyright (C) 2012 id Software LLC, a ZeniMax Media company.
*
* This file is part of the Wolf3D Browser Version GPL Source Code ("Wolf3D Browser Source Code").
*
* Wolf3D Browser Source Code is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* Wolf3D Browser Source Code is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License version 2
* along with Wolf3D Browser Source Code. If not, see <http://www.gnu.org/licenses/>.
*
* If you have questions concerning this license, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
*
* ===========================================================================
*/
/**
* @namespace
* @description Player management
*/
Wolf.Player = (function() {
Wolf.setConsts({
PLAYERSIZE : Wolf.MINDIST, // player radius
STOPSPEED : 0x0D00,
FRICTION : 0.25,
MAXMOVE : (Wolf.MINDIST*2-1),
EXTRAPOINTS : 40000, // points for an extra life
ITEM_KEY_1 : 1,
ITEM_KEY_2 : 2,
ITEM_KEY_3 : 4,
ITEM_KEY_4 : 8,
ITEM_WEAPON_1 : 16,
ITEM_WEAPON_2 : 32,
ITEM_WEAPON_3 : 64,
ITEM_WEAPON_4 : 128,
ITEM_WEAPON_5 : 256,
ITEM_WEAPON_6 : 512,
ITEM_WEAPON_7 : 1024,
ITEM_WEAPON_8 : 2048,
ITEM_BACKPACK : (1<<12), // doubles carrying capacity
ITEM_AUGMENT : (1<<13), // adds 50 to maximum health
ITEM_UNIFORM : (1<<14), // allows you to pass guards
ITEM_AUTOMAP : (1<<15), // shows unknown map ares in other color (as in DooM)
ITEM_FREE : (1<<16), // - unused -
PL_FLAG_REUSE : 1, // use button pressed
PL_FLAG_ATTCK : 2, // attacking
// debug (cheat codes) flags
FL_GODMODE : (1<<4),
FL_NOTARGET : (1<<6),
WEAPON_KNIFE : 0,
WEAPON_PISTOL : 1,
WEAPON_AUTO : 2,
WEAPON_CHAIN : 3,
WEAPON_TYPES : 4,
KEY_GOLD : 0,
KEY_SILVER : 1,
KEY_FREE1 : 2,
KEY_FREE2 : 3,
KEY_TYPES : 4,
AMMO_BULLETS : 0,
AMMO_TYPES : 1,
ex_notingame : 0,
ex_playing : 1,
ex_dead : 2,
ex_secretlevel : 3,
ex_victory : 4,
ex_complete : 5,
// victory animation
BJRUNSPEED : 2048,
BJJUMPSPEED : 680
});
/*
struct atkinf
{
char tics, attack, frame; // attack is 1 for gun, 2 for knife
}
*/
var attackinfo = [ // 4 guns, 14 frames max for every gun!
[ {tics:6,attack:0,frame:1},{tics:6,attack:2,frame:2},{tics:6,attack:0,frame:3},{tics:6,attack:-1,frame:0}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {} ],
[ {tics:6,attack:0,frame:1},{tics:6,attack:1,frame:2},{tics:6,attack:0,frame:3},{tics:6,attack:-1,frame:0}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {} ],
[ {tics:6,attack:0,frame:1},{tics:6,attack:1,frame:2},{tics:6,attack:3,frame:3},{tics:6,attack:-1,frame:0}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {} ],
[ {tics:6,attack:0,frame:1},{tics:6,attack:1,frame:2},{tics:6,attack:4,frame:3},{tics:6,attack:-1,frame:0}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {} ]
];
/**
* @description Spawn the player
* @memberOf Wolf.Player
* @param {object} location The location to spawn the player {origin, angle}
* @param {object} level The level object
* @param {number} skill The difficulty level
* @param {object} [oldPlayer] A player object to copy score and weapons from
* @returns {object} The new player object
*/
function spawn(location, level, skill, oldPlayer) {
var x = location.x,
y = location.y,
angle = location.angle,
tileX = Wolf.POS2TILE(x),
tileY = Wolf.POS2TILE(y),
areanumber = level.areas[tileX][tileY];
// Player structure: Holds all info about player
var player = {
episode : -1,
level : -1,
health : 100,
frags : 0,
ammo : [
],
score : 0,
lives : 0,
startScore : 0,
nextExtra : 0,
items : 0, // (keys, weapon)
weapon : 0,
pendingWeapon : -1,
previousWeapon : -1,
position : {
x : x,
y : y
},
angle : angle,
tile : {
x : tileX,
y : tileY
},
mov : {
x : 0,
y : 0
},
speed : 0,
armor : 0, // there are 2 types. The better one is indicated by high bit set
cmd : { // movement / action command
forwardMove : 0,
sideMove : 0,
buttons : 0,
impulse : 0
},
attackFrame : 0, // attack info
attackCount : 0,
weaponFrame : 0,
madenoise : false,
lastAttacker : null,
faceFrame : 0,
faceCount : 0, // bj's face in the HUD // FIXME decide something!
faceGotGun : false,
faceOuch : false,
flags : 0,
areanumber : areanumber,
playstate : 0,
attackDirection : [0,0],
skill : skill
};
if (player.areanumber < 0) {
player.areanumber = 36;
}
Wolf.Areas.init(level, player.areanumber);
Wolf.Areas.connect(level, player.areanumber);
if (oldPlayer) {
copyPlayer(player, oldPlayer);
} else {
newGame(player);
}
return player;
}
/**
* @description Copy player variables from another player object
* @private
* @param {object} player The player object
* @param {object} copyPlayer The player object to copy from
*/
function copyPlayer(player, copyPlayer) {
player.health = copyPlayer.health;
player.ammo = copyPlayer.ammo;
player.score = copyPlayer.score;
player.startScore = copyPlayer.startScore;
player.lives = copyPlayer.lives;
player.previousWeapon = copyPlayer.previousWeapon;
player.weapon = copyPlayer.weapon;
player.pendingWeapon = copyPlayer.pendingWeapon;
player.items = (copyPlayer.items & Wolf.ITEM_WEAPON_1) |
(copyPlayer.items & Wolf.ITEM_WEAPON_2) |
(copyPlayer.items & Wolf.ITEM_WEAPON_3) |
(copyPlayer.items & Wolf.ITEM_WEAPON_4);
player.nextExtra = copyPlayer.nextExtra;
}
/**
* @description Set up player for the new game
* @memberOf Wolf.Player
* @param {object} player The player object
*/
function newGame(player) {
player.health = 100;
player.ammo[Wolf.AMMO_BULLETS] = 8;
player.score = 0;
player.startScore = 0;
player.lives = 3;
player.previousWeapon = Wolf.WEAPON_KNIFE; //gsh
player.weapon = player.pendingWeapon = Wolf.WEAPON_PISTOL;
player.items = Wolf.ITEM_WEAPON_1 | Wolf.ITEM_WEAPON_2;
player.nextExtra = Wolf.EXTRAPOINTS;
}
/**
* @description Try to move player
* @private
* @param {object} player The player object.
* @param {object} level The level object.
* @returns {boolean} Returns true if move was ok
*/
function tryMove(player, level) {
var xl, yl, xh, yh, x, y,
d, n;
xl = Wolf.POS2TILE(player.position.x - Wolf.PLAYERSIZE );
yl = Wolf.POS2TILE(player.position.y - Wolf.PLAYERSIZE );
xh = Wolf.POS2TILE(player.position.x + Wolf.PLAYERSIZE );
yh = Wolf.POS2TILE(player.position.y + Wolf.PLAYERSIZE );
// Cheching for solid walls:
for (y = yl; y <= yh; ++y) {
for (x = xl; x <= xh; ++x) {
if (level.tileMap[x][y] & Wolf.SOLID_TILE) {
return false;
}
if (level.tileMap[x][y] & Wolf.DOOR_TILE && Wolf.Doors.opened(level.state.doorMap[x][y]) != Wolf.DOOR_FULLOPEN) {
// iphone hack to allow player to move halfway into door tiles
// if the player bounds doesn't cross the middle of the tile, let the move continue
if (Math.abs(player.position.x - Wolf.TILE2POS(x)) <= 0x9000 && Math.abs(player.position.y - Wolf.TILE2POS(y)) <= 0x9000) {
return false;
}
}
}
}
// check for actors
for (n = 0; n < level.state.numGuards; ++n) {
if (level.state.guards[n].state >= Wolf.st_die1) {
continue;
}
if (!(level.state.guards[n].flags & Wolf.FL_SHOOTABLE)) {
continue;
}
d = player.position.x - level.state.guards[n].x;
if (d < -Wolf.MINACTORDIST || d > Wolf.MINACTORDIST) {
continue;
}
d = player.position.y - level.state.guards[n].y;
if (d < -Wolf.MINACTORDIST || d > Wolf.MINACTORDIST) {
continue;
}
return false;
}
return true;
}
/**
* @description Clips movement
* @private
* @param {object} self The player object.
* @param {number} xmove Movement in x direction
* @param {number} ymove Movement in y direction
* @param {object} level The level object.
*/
function clipMove(self, xmove, ymove, level) {
var basex, basey;
basex = self.position.x;
basey = self.position.y;
self.position.x += xmove;
self.position.y += ymove;
if (tryMove(self, level)) {
return; // we moved as we wanted
}
if (xmove) { // don't bother if we don't move x!
self.position.x = basex + xmove;
self.position.y = basey;
if (tryMove(self, level)) {
return; // May be we'll move only X direction?
}
}
if (ymove) { // don't bother if we don't move y!
self.position.x = basex;
self.position.y = basey + ymove;
if (tryMove(self, level)) {
return; // May be we'll move only Y direction?
}
}
// movement blocked; we must stay on one place... :(
self.position.x = basex;
self.position.y = basey;
}
/**
* @description Changes player's angle and position
* @memberOf Wolf.Player
* @param {object} game The game object.
* @param {object} self The player object.
* @param {object} level The level object.
* @param {object} tics Number of tics.
*/
function controlMovement(game, self, level, tics) {
var angle, speed;
// rotation
angle = self.angle;
self.mov.x = self.mov.y = 0; // clear accumulated movement
if (self.cmd.forwardMove ) {
speed = tics * self.cmd.forwardMove;
self.mov.x += (speed * Wolf.Math.CosTable[angle])>>0;
self.mov.y += (speed * Wolf.Math.SinTable[angle])>>0;
}
if (self.cmd.sideMove) {
speed = tics * self.cmd.sideMove;
self.mov.x += (speed * Wolf.Math.SinTable[angle])>>0;
self.mov.y -= (speed * Wolf.Math.CosTable[angle])>>0;
}
if (!self.mov.x && !self.mov.y) {
return;
}
self.speed = self.mov.x + self.mov.y;
// bound movement
if (self.mov.x > Wolf.MAXMOVE) {
self.mov.x = Wolf.MAXMOVE;
} else if (self.mov.x < -Wolf.MAXMOVE) {
self.mov.x = -Wolf.MAXMOVE;
}
if (self.mov.y > Wolf.MAXMOVE) {
self.mov.y = Wolf.MAXMOVE;
} else if (self.mov.y < -Wolf.MAXMOVE) {
self.mov.y = -Wolf.MAXMOVE;
}
// move player and clip movement to walls (check for no-clip mode here)
clipMove(self, self.mov.x, self.mov.y, level);
self.tile.x = Wolf.POS2TILE(self.position.x);
self.tile.y = Wolf.POS2TILE(self.position.y);
// Powerup_PickUp( self.tilex, self.tiley );
// pick up items easier -- any tile you touch, instead of
// just the midpoint tile
var x, y, tileX, tileY;
for (x = -1 ;x <= 1; x+= 2) {
tilex = Wolf.POS2TILE(self.position.x + x * Wolf.PLAYERSIZE);
for (y = -1; y <= 1; y+= 2) {
tiley = Wolf.POS2TILE(self.position.y + y * Wolf.PLAYERSIZE);
Wolf.Powerups.pickUp(level, self, tilex, tiley);
}
}
// Checking for area change, ambush tiles and doors will have negative values
if (level.areas[self.tile.x][self.tile.y] >= 0 && level.areas[self.tile.x][self.tile.y] != self.areanumber) {
self.areanumber = level.areas[self.tile.x][self.tile.y];
// assert( self.areanumber >= 0 && self.areanumber < Wolf.NUMAREAS );
Wolf.Areas.connect(level, self.areanumber);
}
if (level.tileMap[self.tile.x][self.tile.y] & Wolf.EXIT_TILE) {
//Wolf.Game.startIntermission(0);
Wolf.Game.victory(game);
}
}
/**
* @description Called if player pressed USE button
* @private
* @param {object} self The player object.
* @param {object} level The level object.
* @returns {boolean} True if the player used something.
*/
function use(self, game) {
var x, y, dir, newtex,
level = game.level;
dir = Wolf.Math.get4dir(Wolf.FINE2RAD(self.angle));
x = self.tile.x + Wolf.Math.dx4dir[dir];
y = self.tile.y + Wolf.Math.dy4dir[dir];
if (level.tileMap[x][y] & Wolf.DOOR_TILE) {
return Wolf.Doors.tryUse(level, self, level.state.doorMap[x][y]);
}
if (level.tileMap[x][y] & Wolf.SECRET_TILE) {
return Wolf.PushWall.push(level, x, y, dir);
}
if (level.tileMap[x][y] & Wolf.ELEVATOR_TILE) {
switch (dir) {
case Wolf.Math.dir4_east:
case Wolf.Math.dir4_west:
newtex = level.wallTexX[x][y] += 2;
break;
case Wolf.Math.dir4_north:
case Wolf.Math.dir4_south:
return false; // don't allow to press elevator rails
}
if (level.tileMap[self.tile.x][self.tile.y] & Wolf.SECRETLEVEL_TILE) {
self.playstate = Wolf.ex_secretlevel;
} else {
self.playstate = Wolf.ex_complete;
}
Wolf.Sound.startSound(null, null, 0, Wolf.CHAN_BODY, "lsfx/040.wav", 1, Wolf.ATTN_NORM, 0 );
Wolf.Game.startIntermission(game);
return true;
}
return false;
}
/**
* @description Attack
* @memberOf Wolf.Player
* @param {object} game The game object.
* @param {object} player The player object.
* @param {boolean} reAttack True if re-attack
* @param {number} tics The number of tics
*/
function attack(game, player, reAttack, tics) {
var cur,
level = game.level;
player.attackCount -= tics;
while (player.attackCount <= 0) {
cur = attackinfo[player.weapon][player.attackFrame];
switch (cur.attack) {
case -1:
player.flags &= ~Wolf.PL_FLAG_ATTCK;
if (!player.ammo[Wolf.AMMO_BULLETS]) {
player.weapon = Wolf.WEAPON_KNIFE;
} else if (player.weapon != player.pendingWeapon) {
player.weapon = player.pendingWeapon;
}
player.attackFrame = player.weaponFrame = 0;
return;
case 4:
if (!player.ammo[Wolf.AMMO_BULLETS]) {
break;
}
if (reAttack) {
player.attackFrame -= 2;
}
case 1:
if (!player.ammo[Wolf.AMMO_BULLETS]) { // can only happen with chain gun
player.attackFrame++;
break;
}
Wolf.Weapon.fireLead(game, player);
player.ammo[Wolf.AMMO_BULLETS]--;
break;
case 2:
Wolf.Weapon.fireHit(game, player);
break;
case 3:
if (player.ammo[Wolf.AMMO_BULLETS] && reAttack) {
player.attackFrame -= 2;
}
break;
}
player.attackCount += cur.tics;
player.attackFrame++;
player.weaponFrame = attackinfo[player.weapon][player.attackFrame].frame;
}
}
/**
* @description Award points to the player
* @memberOf Wolf.Player
* @param {object} player The player object.
* @param {number} points The number of points.
*/
function givePoints(player, points) {
player.score += points;
while (player.score >= player.nextExtra) {
player.nextExtra += Wolf.EXTRAPOINTS;
giveLife(player);
Wolf.log("Extra life!");
}
}
/*
-----------------------------------------------------------------------------
Returns: returns true if player needs this health.
Notes:
gives player some HP
max can be:
0 - natural player's health limit (100 or 150 with augment)
>0 - indicates the limit
-----------------------------------------------------------------------------
*/
function giveHealth(player, points, max) {
if (max == 0) {
max = (player.items & Wolf.ITEM_AUGMENT) ? 150 : 100;
}
if (player.health >= max) {
return false; // doesn't need this health
}
player.health += points;
if (player.health > max) {
player.health = max;
}
player.faceGotGun = false;
return true; // took it
}
function giveLife(player) {
if (player.lives < 9) {
player.lives++;
}
}
function giveKey(player, key) {
player.items |= Wolf.ITEM_KEY_1 << key;
}
function giveWeapon(player, weapon) {
var itemflag;
giveAmmo(player, Wolf.AMMO_BULLETS, 6); // give some ammo with a weapon
itemflag = Wolf.ITEM_WEAPON_1 << weapon;
if (player.items & itemflag) {
return; // player owns this weapon
} else {
player.items |= itemflag;
// don't switch if already using better weapon
if (player.weapon < weapon ) {
player.weapon = player.pendingWeapon = weapon;
}
}
}
function giveAmmo(player, type, ammo) {
var maxAmmo = 99;
if (player.items & Wolf.ITEM_BACKPACK) {
maxAmmo *= 2;
}
if (player.ammo[type] >= maxAmmo) {
return false; // don't need
}
if (!player.ammo[type] && !player.attackFrame) {
// knife was out
player.weapon = player.pendingWeapon;
}
player.ammo[type] += ammo;
if (player.ammo[type] > maxAmmo) {
player.ammo[type] = maxAmmo;
}
return true;
}
/**
* @description Award points to the player
* @memberOf Wolf.Player
* @param {object} player The player object.
* @param {object} attacker The attacker actor object.
* @param {number} points The number of damage points.
* @param {number} skill The difficulty level.
*/
function damage(player, attacker, points, skill) {
var dx, dy,
angle, playerAngle, deltaAngle;
if (player.playstate == Wolf.ex_dead || player.playstate == Wolf.ex_complete || self.playstate == Wolf.ex_victory) {
return;
}
player.lastAttacker = attacker;
if (skill == Wolf.gd_baby) {
points >>= 2;
}
// note the direction of the last hit for the directional blends
dx = attacker.x - player.position.x;
dy = attacker.y - player.position.y;
// probably won't ever have damage from self, but check anyway
if (dx != 0 || dy != 0) {
angle = Math.atan2(dy, dx);
playerAngle = player.angle * 360.0 / Wolf.ANG_360;
angle = angle * 180.0 / Math.PI;
if (angle < 0) {
angle = 360 + angle;
}
deltaAngle = angle - playerAngle;
if (deltaAngle > 180) {
deltaAngle = deltaAngle - 360;
}
if (deltaAngle < -180) {
deltaAngle = 360 + deltaAngle;
}
if (deltaAngle > 40) {
player.attackDirection[0] = 1;
} else if (deltaAngle < -40) {
player.attackDirection[1] = 1;
}
}
// do everything else but subtract health in god mode, to ease
// testing of damage feedback
if (!(player.flags & Wolf.FL_GODMODE) ) {
player.health -= points;
}
if (player.health <= 0) {
// dead
Wolf.Game.notify("You have died");
player.health = 0;
player.playstate = Wolf.ex_dead;
Wolf.Sound.startSound(null, null, 0, Wolf.CHAN_BODY, "lsfx/009.wav", 1, Wolf.ATTN_NORM, 0);
}
// red screen flash
Wolf.Game.startDamageFlash(points);
// stop the happy grin face if shot before it times out
player.faceGotGun = false;
// make BJ's eyes bulge on huge hits
if (points > 30 && player.health != 0) {
player.faceOuch = true;
player.faceCount = 0;
}
}
function victorySpin(game, player, tics) {
var desty;
if (player.angle > Wolf.ANG_270) {
player.angle -= tics * Wolf.ANG_1 * 3;
if (player.angle < Wolf.ANG_270) {
player.angle = Wolf.ANG_270;
}
} else if (player.angle < Wolf.ANG_270) {
player.angle += tics * Wolf.ANG_1 * 3;
if (player.angle > Wolf.ANG_270) {
player.angle = Wolf.ANG_270;
}
}
//desty = ((player.tile.y-5) << Wolf.TILESHIFT) - 0x3000;
desty = Wolf.TILE2POS(player.tile.y+7)
if (player.position.y < desty) {
player.position.y += tics * 3072;
if (player.position.y > desty) {
player.position.y = desty;
}
}
}
/**
* @description Process player actions
* @memberOf Wolf.Player
* @param {object} self The player object.
* @param {object} game The game object.
* @param {number} tics Tics since last processing.
*/
function process(game, self, tics) {
var level = game.level,
n;
if (self.playstate == Wolf.ex_victory) {
victorySpin(game, self, tics);
return;
}
self.attackDirection = [0,0];
self.madenoise = false;
controlMovement(game, self, level, tics);
if (self.flags & Wolf.PL_FLAG_ATTCK) {
attack(game, self, self.cmd.buttons & Wolf.BUTTON_ATTACK, tics);
} else {
if (self.cmd.buttons & Wolf.BUTTON_USE) {
if (!(self.flags & Wolf.PL_FLAG_REUSE) && use(self, game)) {
self.flags |= Wolf.PL_FLAG_REUSE;
}
} else {
self.flags &= ~Wolf.PL_FLAG_REUSE;
}
if (self.cmd.buttons & Wolf.BUTTON_ATTACK) {
self.flags |= Wolf.PL_FLAG_ATTCK;
self.attackFrame = 0;
self.attackCount = attackinfo[self.weapon][0].tics;
self.weaponFrame = attackinfo[self.weapon][0].frame;
}
}
// process impulses
switch (self.cmd.impulse) {
case 0:
break; // no impulse
case 1:
case 2:
case 3:
case 4:
changeWeapon(self, self.cmd.impulse - 1);
break;
case 10: // next weapon /like in Quake/ FIXME: weapprev, weapnext
self.pendingWeapon = self.weapon;
for (n = 0; n < 4; ++n) {
if (++self.weapon > Wolf.WEAPON_CHAIN) {
self.weapon = Wolf.WEAPON_KNIFE;
}
if (changeWeapon(self, self.weapon)) {
break;
}
}
self.weapon = self.pendingWeapon;
break;
default:
Wolf.log("Unknown Impulse: ", + self.cmd.impulse);
break;
}
}
return {
spawn : spawn,
newGame : newGame,
controlMovement : controlMovement,
process : process,
damage : damage,
givePoints : givePoints,
giveHealth : giveHealth,
giveAmmo : giveAmmo,
giveWeapon : giveWeapon,
giveLife : giveLife,
giveKey : giveKey
};
})();

309
js/powerups.js Normal file
View File

@@ -0,0 +1,309 @@
/*
* ===========================================================================
*
* Wolf3D Browser Version GPL Source Code
* Copyright (C) 2012 id Software LLC, a ZeniMax Media company.
*
* This file is part of the Wolf3D Browser Version GPL Source Code ("Wolf3D Browser Source Code").
*
* Wolf3D Browser Source Code is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* Wolf3D Browser Source Code is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License version 2
* along with Wolf3D Browser Source Code. If not, see <http://www.gnu.org/licenses/>.
*
* If you have questions concerning this license, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
*
* ===========================================================================
*/
Wolf.Powerups = (function() {
Wolf.setConsts({
pow_gibs : 0, // 1% if <=10%; SLURPIESND
pow_gibs2 : 1, // 1% if <=10%; SLURPIESND
pow_alpo : 2, // 4% if <100%; HEALTH1SND
pow_firstaid : 3, // 25% if <100%; HEALTH2SND
pow_key1 : 4, // gold key; GETKEYSND
pow_key2 : 5, // silver key; GETKEYSND
pow_key3 : 6, // not used
pow_key4 : 7, // not used
pow_cross : 8, // 100pts; BONUS1SND
pow_chalice : 9, // 500pts; BONUS2SND
pow_bible : 10, // 1000pts; BONUS3SND
pow_crown : 11, // 5000pts; BONUS4SND
pow_clip : 12, // 8bul if <99bul; GETAMMOSND
pow_clip2 : 13, // 4bul if <99bul; GETAMMOSND
pow_machinegun : 14, // machine gun; GETMACHINESND
pow_chaingun : 15, // gatling gun; GETGATLINGSND
pow_food : 16, // 10% if <100%; HEALTH1SND
pow_fullheal : 17, // 99%, 25bul; BONUS1UPSND
pow_25clip : 18, // 25bul if <99bul; GETAMMOBOXSND
pow_spear : 19, // spear of destiny!
pow_last : 20
// add new types <!only!> here (after last)
});
var texture = [
Wolf.SPR_STAT_34, // pow_gibs
Wolf.SPR_STAT_38, // pow_gibs2
Wolf.SPR_STAT_6, // pow_alpo
Wolf.SPR_STAT_25, // pow_firstaid
Wolf.SPR_STAT_20, // pow_key1
Wolf.SPR_STAT_21, // pow_key2
// not used
Wolf.SPR_STAT_20, // pow_key3
Wolf.SPR_STAT_20, // pow_key4
Wolf.SPR_STAT_29, // pow_cross
Wolf.SPR_STAT_30, // pow_chalice
Wolf.SPR_STAT_31, // pow_bible
Wolf.SPR_STAT_32, // pow_crown
Wolf.SPR_STAT_26, // pow_clip
Wolf.SPR_STAT_26, // pow_clip2
Wolf.SPR_STAT_27, // pow_machinegun
Wolf.SPR_STAT_28, // pow_chaingun
Wolf.SPR_STAT_24, // pow_food
Wolf.SPR_STAT_33, // pow_fullheal
// spear
Wolf.SPR_STAT_49, // pow_25clip
Wolf.SPR_STAT_51 // pow_spear
];
function remove(level, powerup) {
powerup.x = -1;
powerup.y = -1;
}
function addNew(level) {
/*
for (var i = 0;i < level.state.numPowerups; i++ ) {
if (level.state.powerups[i].x == -1 ) {
return level.state.powerups[i];
}
}
*/
/*
if (level.state.numPowerups == Wolf.MAX_POWERUPS ) {
return level.state.powerups[0];
}
*/
level.state.numPowerups++;
var newp = {
x : -1,
y : -1,
type : 0,
sprite : null
};
level.state.powerups[level.state.numPowerups-1] = newp;
return newp;
}
function reset(level) {
level.state.numPowerups = 0;
level.state.powerups = [];
}
// x,y are in TILES.
function spawn(level, x, y, type) {
var newp = addNew(level);
newp.sprite = Wolf.Sprites.getNewSprite(level);
newp.type = type;
Wolf.Sprites.setPos(level, newp.sprite, Wolf.TILE2POS(newp.x = x), Wolf.TILE2POS(newp.y = y), 0);
Wolf.Sprites.setTex(level, newp.sprite, -1, texture[type]);
level.tileMap[x][y] |= Wolf.POWERUP_TILE;
// good place to update total treasure count!
}
function give(level, player, type) {
var keynames = ["Gold", "Silver", "?", "?"];
switch (type) {
// Keys
case Wolf.pow_key1:
case Wolf.pow_key2:
case Wolf.pow_key3:
case Wolf.pow_key4:
type -= Wolf.pow_key1;
Wolf.Player.giveKey(player, type);
Wolf.Sound.startSound(null, null, 0, Wolf.CHAN_ITEM, "lsfx/012.wav", 1, Wolf.ATTN_NORM, 0);
Wolf.Game.notify(keynames[type] + " key");
break;
// Treasure
case Wolf.pow_cross:
Wolf.Player.givePoints(player, 100);
Wolf.Sound.startSound(null, null, 0, Wolf.CHAN_ITEM, "lsfx/035.wav", 1, Wolf.ATTN_NORM, 0);
if ( ++level.state.foundTreasure == level.state.totalTreasure ) {
Wolf.Game.notify("You found the last treasure!");
}
break;
case Wolf.pow_chalice:
Wolf.Player.givePoints(player, 500);
Wolf.Sound.startSound(null, null, 0, Wolf.CHAN_ITEM, "lsfx/036.wav", 1, Wolf.ATTN_NORM, 0);
if (++level.state.foundTreasure == level.state.totalTreasure) {
Wolf.Game.notify("You found the last treasure!");
}
break;
case Wolf.pow_bible:
Wolf.Player.givePoints(player, 1000);
Wolf.Sound.startSound(null, null, 0, Wolf.CHAN_ITEM, "lsfx/037.wav", 1, Wolf.ATTN_NORM, 0);
if (++level.state.foundTreasure == level.state.totalTreasure) {
Wolf.Game.notify("You found the last treasure!");
}
break;
case Wolf.pow_crown:
Wolf.Player.givePoints(player, 5000);
Wolf.Sound.startSound(null, null, 0, Wolf.CHAN_ITEM, "lsfx/045.wav", 1, Wolf.ATTN_NORM, 0);
if (++level.state.foundTreasure == level.state.totalTreasure) {
Wolf.Game.notify("You found the last treasure!");
}
break;
// Health
case Wolf.pow_gibs:
if (!Wolf.Player.giveHealth(player, 1, 11)) {
return false;
}
Wolf.Sound.startSound(null, null, 0, Wolf.CHAN_ITEM, "lsfx/061.wav", 1, Wolf.ATTN_NORM, 0);
break;
case Wolf.pow_alpo:
if (!Wolf.Player.giveHealth(player, 4, 0)) {
return false;
}
Wolf.Sound.startSound(null, null, 0, Wolf.CHAN_ITEM, "lsfx/033.wav", 1, Wolf.ATTN_NORM, 0);
break;
case Wolf.pow_food:
if (!Wolf.Player.giveHealth(player, 10, 0)) {
return false;
}
Wolf.Sound.startSound(null, null, 0, Wolf.CHAN_ITEM, "lsfx/033.wav", 1, Wolf.ATTN_NORM, 0);
break;
case Wolf.pow_firstaid:
if (!Wolf.Player.giveHealth(player, 25, 0)) {
return false;
}
Wolf.Sound.startSound(null, null, 0, Wolf.CHAN_ITEM, "lsfx/034.wav", 1, Wolf.ATTN_NORM, 0);
break;
// Weapon & Ammo
case Wolf.pow_clip:
if (!Wolf.Player.giveAmmo(player, Wolf.AMMO_BULLETS, 8)) {
return false;
}
Wolf.Sound.startSound(null, null, 0, Wolf.CHAN_ITEM, "lsfx/031.wav", 1, Wolf.ATTN_NORM, 0);
break;
case Wolf.pow_clip2:
if (!Wolf.Player.giveAmmo(player, Wolf.AMMO_BULLETS, 4)) {
return false;
}
Wolf.Sound.startSound(null, null, 0, Wolf.CHAN_ITEM, "lsfx/031.wav", 1, Wolf.ATTN_NORM, 0);
break;
case Wolf.pow_25clip:
if (!Wolf.Player.giveAmmo(player, Wolf.AMMO_BULLETS, 25)) {
return false;
}
Wolf.Sound.startSound(null, null, 0, Wolf.CHAN_ITEM, "lsfx/031.wav", 1, Wolf.ATTN_NORM, 0);
break;
case Wolf.pow_machinegun:
Wolf.Player.giveWeapon(player, Wolf.WEAPON_AUTO );
Wolf.Sound.startSound(null, null, 0, Wolf.CHAN_ITEM, "lsfx/030.wav", 1, Wolf.ATTN_NORM, 0);
Wolf.Game.notify("Machinegun");
break;
case Wolf.pow_chaingun:
Wolf.Player.giveWeapon(player, Wolf.WEAPON_CHAIN );
Wolf.Sound.startSound(null, null, 0, Wolf.CHAN_ITEM, "lsfx/038.wav", 1, Wolf.ATTN_NORM, 0);
Wolf.Game.notify("Chaingun");
player.faceCount = -100;
player.faceGotGun = true;
break;
// Artifacts
case Wolf.pow_fullheal:
// go to 150 health
Wolf.Player.giveHealth(player, 99, 99 );
Wolf.Player.giveAmmo(player, Wolf.AMMO_BULLETS, 25 );
Wolf.Player.giveLife(player);
if (++level.state.foundTreasure == level.state.totalTreasure) {
Wolf.Game.notify("You found the last treasure!");
} else {
Wolf.Game.notify("Full Heal");
}
Wolf.Sound.startSound(null, null, 0, Wolf.CHAN_ITEM, "lsfx/034.wav", 1, Wolf.ATTN_NORM, 0);
Wolf.log("Extra life!");
break;
default:
Wolf.log("Warning: Unknown item type: " + type);
break;
}
Wolf.Game.startBonusFlash();
return true;
}
// x,y are in TILES.
function pickUp(level, player, x, y) {
var i, pow,
p_left = false,
p_pick = false;
for (i=0; i<level.state.numPowerups; i++) {
pow = level.state.powerups[i];
if (pow.x == x && pow.y == y) {
// got a powerup here
if (give(level, player, pow.type)) { //FIXME script
// picked up this stuff, remove it!
p_pick = true;
Wolf.Sprites.remove(level, pow.sprite);
remove(level, pow);
} else {
// player do not need it, so may be next time!
p_left = true;
}
}
}
if(p_left) {
level.tileMap[x][y] |= Wolf.POWERUP_TILE;
} else {
level.tileMap[x][y] &= ~Wolf.POWERUP_TILE;
}
}
return {
spawn : spawn,
reset : reset,
pickUp : pickUp
};
})();

145
js/pushwall.js Normal file
View File

@@ -0,0 +1,145 @@
/*
* ===========================================================================
*
* Wolf3D Browser Version GPL Source Code
* Copyright (C) 2012 id Software LLC, a ZeniMax Media company.
*
* This file is part of the Wolf3D Browser Version GPL Source Code ("Wolf3D Browser Source Code").
*
* Wolf3D Browser Source Code is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* Wolf3D Browser Source Code is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License version 2
* along with Wolf3D Browser Source Code. If not, see <http://www.gnu.org/licenses/>.
*
* If you have questions concerning this license, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
*
* ===========================================================================
*/
/**
* @namespace
* @description Push wall management
*/
Wolf.PushWall = (function() {
var PWall = {};
reset();
function reset() {
PWall.active = false;
PWall.tilesMoved = 0;
PWall.pointsMoved = 0;
PWall.dir = 0;
PWall.x = 0;
PWall.y = 0;
PWall.dx = 0;
PWall.dy = 0;
PWall.texX = 0;
PWall.texY = 0;
}
function push(level, x, y, dir) {
var dx, dy;
if (PWall.active) {
return false; // another PWall is moving [only one at a time!]
}
dx = Wolf.Math.dx4dir[dir];
dy = Wolf.Math.dy4dir[dir];
if (level.tileMap[x + dx][y + dy] & (Wolf.SOLID_TILE | Wolf.DOOR_TILE)) {
// noway (smth is blocking)
return true;
}
// remove secret flag & make everything needed when pushwall used!
level.tileMap[x][y] &= (~Wolf.SECRET_TILE);
level.tileMap[x][y] &= (~Wolf.WALL_TILE);
level.tileMap[x][y] |= Wolf.PUSHWALL_TILE;
if (++level.state.foundSecrets == level.state.totalSecrets) {
Wolf.Game.notify("You found the last secret!");
} else {
Wolf.Game.notify("You found a secret!");
}
Wolf.Sound.startSound(null, null, 1, Wolf.CHAN_AUTO, "sfx/034.wav", 1, Wolf.ATTN_STATIC, 0);
// good way to avoid stuckness; [un]comment one more down!
// it makes a tile behind pushwall unpassable
level.tileMap[x + dx][y + dy] |= Wolf.PUSHWALL_TILE;
level.wallTexX[x + dx][y + dy] = level.wallTexX[x][y];
level.wallTexY[x + dx][y + dy] = level.wallTexY[x][y];
// write down PWall info
PWall.active = true;
PWall.tilesMoved = PWall.pointsMoved = 0;
PWall.dir = dir;
PWall.x = x;
PWall.y = y;
PWall.dx = dx;
PWall.dy = dy;
PWall.texX = level.wallTexX[x][y];
PWall.texY = level.wallTexY[x][y];
return true;
}
function process(level, tics) {
if (!PWall.active) {
return; // no active PWall to work with
}
PWall.pointsMoved += tics;
if (PWall.pointsMoved < 128) {
return;
}
PWall.pointsMoved -= 128;
PWall.tilesMoved++;
// Free tile
level.tileMap[PWall.x][PWall.y] &= (~Wolf.PUSHWALL_TILE);
// Occupy new tile
PWall.x += PWall.dx;
PWall.y += PWall.dy;
// Shall we move further?
if (level.tileMap[PWall.x + PWall.dx][PWall.y + PWall.dy] & (Wolf.SOLID_TILE | Wolf.DOOR_TILE | Wolf.ACTOR_TILE | Wolf.POWERUP_TILE) || PWall.tilesMoved == 3) {
level.tileMap[PWall.x][PWall.y] &= (~Wolf.PUSHWALL_TILE); // wall now
level.tileMap[PWall.x][PWall.y] |= Wolf.WALL_TILE; // wall now
level.wallTexX[PWall.x][PWall.y] = PWall.texX;
level.wallTexY[PWall.x][PWall.y] = PWall.texY;
PWall.active = false; // Free Push Wall
} else {
level.tileMap[PWall.x + PWall.dx][PWall.y + PWall.dy] |= Wolf.PUSHWALL_TILE;
// Not sure if this is right but it fixed an issue with the pushwall texture changing mid-slide.
level.wallTexX[PWall.x + PWall.dx][PWall.y + PWall.dy] = PWall.texX;
level.wallTexY[PWall.x + PWall.dx][PWall.y + PWall.dy] = PWall.texY;
}
}
function get() {
return PWall;
}
return {
reset : reset,
process : process,
push : push,
get : get
};
})();

74
js/random.js Normal file
View File

@@ -0,0 +1,74 @@
/*
* ===========================================================================
*
* Wolf3D Browser Version GPL Source Code
* Copyright (C) 2012 id Software LLC, a ZeniMax Media company.
*
* This file is part of the Wolf3D Browser Version GPL Source Code ("Wolf3D Browser Source Code").
*
* Wolf3D Browser Source Code is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* Wolf3D Browser Source Code is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License version 2
* along with Wolf3D Browser Source Code. If not, see <http://www.gnu.org/licenses/>.
*
* If you have questions concerning this license, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
*
* ===========================================================================
*/
Wolf.Random = (function() {
/* This is just John Carmack's table driven pseudo-random number generator */
var rndtable = [
0, 8, 109, 220, 222, 241, 149, 107, 75, 248, 254, 140, 16, 66,
74, 21, 211, 47, 80, 242, 154, 27, 205, 128, 161, 89, 77, 36,
95, 110, 85, 48, 212, 140, 211, 249, 22, 79, 200, 50, 28, 188,
52, 140, 202, 120, 68, 145, 62, 70, 184, 190, 91, 197, 152, 224,
149, 104, 25, 178, 252, 182, 202, 182, 141, 197, 4, 81, 181, 242,
145, 42, 39, 227, 156, 198, 225, 193, 219, 93, 122, 175, 249, 0,
175, 143, 70, 239, 46, 246, 163, 53, 163, 109, 168, 135, 2, 235,
25, 92, 20, 145, 138, 77, 69, 166, 78, 176, 173, 212, 166, 113,
94, 161, 41, 50, 239, 49, 111, 164, 70, 60, 2, 37, 171, 75,
136, 156, 11, 56, 42, 146, 138, 229, 73, 146, 77, 61, 98, 196,
135, 106, 63, 197, 195, 86, 96, 203, 113, 101, 170, 247, 181, 113,
80, 250, 108, 7, 255, 237, 129, 226, 79, 107, 112, 166, 103, 241,
24, 223, 239, 120, 198, 58, 60, 82, 128, 3, 184, 66, 143, 224,
145, 224, 81, 206, 163, 45, 63, 90, 168, 114, 59, 33, 159, 95,
28, 139, 123, 98, 125, 196, 15, 70, 194, 253, 54, 14, 109, 226,
71, 17, 161, 93, 186, 87, 244, 138, 20, 52, 123, 251, 26, 36,
17, 46, 52, 231, 232, 76, 31, 221, 84, 37, 216, 165, 212, 106,
197, 242, 98, 43, 39, 175, 254, 145, 190, 84, 118, 222, 187, 136,
120, 163, 236, 249
];
var rndindex = 0;
function init(randomize) {
if (randomize ) {
rndindex = (new Date).getTime() & 0xFF;
} else {
rndindex = 0;
}
}
function rnd() {
rndindex++;
rndindex &= 0xFF;
return rndtable[rndindex];
}
return {
init : init,
rnd : rnd
}
})();

302
js/raycaster.js Normal file
View File

@@ -0,0 +1,302 @@
/*
* ===========================================================================
*
* Wolf3D Browser Version GPL Source Code
* Copyright (C) 2012 id Software LLC, a ZeniMax Media company.
*
* This file is part of the Wolf3D Browser Version GPL Source Code ("Wolf3D Browser Source Code").
*
* Wolf3D Browser Source Code is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* Wolf3D Browser Source Code is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License version 2
* along with Wolf3D Browser Source Code. If not, see <http://www.gnu.org/licenses/>.
*
* If you have questions concerning this license, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
*
* ===========================================================================
*/
Wolf.setConsts({
UPPERZCOORD : 0.6,
LOWERZCOORD : -0.6,
// marks
TRACE_MARK_MAP : 1, // marks traced area in 'AM_AutoMap.vis' array
// obstacle levels
TRACE_SIGHT : 2, // player sight
TRACE_SIGHT_AI : 4, // enemy sight
TRACE_BULLET : 8, // bullet
TRACE_OBJECT : 16, // object
TRACE_HIT_VERT : 32, // vertical wall was hit
TRACE_HIT_DOOR : 64, // door was hit
TRACE_HIT_PWALL : 128 // pushwall was hit
});
Wolf.Raycaster = (function() {
var x_tile_step = [ 1, -1, -1, 1 ],
y_tile_step = [ 1, 1, -1, -1 ];
var TILESHIFT = Wolf.TILESHIFT,
TRACE_HIT_VERT = Wolf.TRACE_HIT_VERT,
TILEGLOBAL = Wolf.TILEGLOBAL,
WALL_TILE = Wolf.WALL_TILE,
DOOR_TILE = Wolf.DOOR_TILE,
TILE2POS = Wolf.TILE2POS,
POS2TILE = Wolf.POS2TILE,
FINE2RAD = Wolf.FINE2RAD,
TRACE_HIT_DOOR = Wolf.TRACE_HIT_DOOR,
PUSHWALL_TILE = Wolf.PUSHWALL_TILE,
TRACE_HIT_PWALL = Wolf.TRACE_HIT_PWALL,
DOOR_FULLOPEN = Wolf.DOOR_FULLOPEN,
XnextTable = Wolf.Math.XnextTable,
YnextTable = Wolf.Math.YnextTable,
getQuadrant = Wolf.Math.getQuadrant,
TanTable = Wolf.Math.TanTable;
function traceCheck(tileMap, doorMap, visibleTiles, x, y, frac, dfrac, vert, flip, tracePoint) {
var door;
if (tileMap[x][y] & WALL_TILE) {
if (vert) {
tracePoint.x = (x << TILESHIFT) + (flip ? TILEGLOBAL : 0);
tracePoint.y = (y << TILESHIFT) + frac;
tracePoint.flags |= TRACE_HIT_VERT;
} else {
tracePoint.x = (x << TILESHIFT) + frac;
tracePoint.y = (y << TILESHIFT) + (flip ? TILEGLOBAL : 0);
tracePoint.flags &= ~TRACE_HIT_VERT;
}
tracePoint.tileX = x;
tracePoint.tileY = y;
tracePoint.frac = frac / TILEGLOBAL;
return true; // wall, stop tracing
}
if (visibleTiles) {
visibleTiles[x][y] = true; // this tile is visible
}
if (tileMap[x][y] & DOOR_TILE && doorMap[x][y].action != Wolf.dr_open) {
door = doorMap[x][y];
frac += dfrac >> 1;
if (POS2TILE(frac)) {
return false;
}
if (vert) {
if (door.action != Wolf.dr_closed && (frac >> 10) > DOOR_FULLOPEN - Wolf.Doors.opened(door)) {
return false; // opened enough
}
tracePoint.x = TILE2POS(x);
tracePoint.y = (y << TILESHIFT) + frac;
tracePoint.flags |= TRACE_HIT_VERT;
tracePoint.frac = frac / TILEGLOBAL;
} else {
if (door.action != Wolf.dr_closed && (frac >> 10) < Wolf.Doors.opened(door)) {
return false; // opened enough
}
tracePoint.y = TILE2POS(y);
tracePoint.x = (x << TILESHIFT) + frac;
tracePoint.flags &= ~TRACE_HIT_VERT;
tracePoint.frac = 1 - frac / TILEGLOBAL;
}
tracePoint.flags |= TRACE_HIT_DOOR;
tracePoint.tileX = x;
tracePoint.tileY = y;
tracePoint.frac += Wolf.Doors.opened(door) / DOOR_FULLOPEN;
return true; // closed door, stop tracing
}
if (tileMap[x][y] & PUSHWALL_TILE) {
var pwall = Wolf.PushWall.get(),
offset = pwall.pointsMoved / 128;
frac += dfrac * offset;
if (POS2TILE(frac)) {
return false;
}
if (vert) {
tracePoint.x = (x << TILESHIFT) + (flip ? TILEGLOBAL : 0) + offset * TILEGLOBAL * (flip ? -1 : 1);
tracePoint.y = (y << TILESHIFT) + frac;
tracePoint.flags |= TRACE_HIT_VERT;
} else {
tracePoint.x = (x << TILESHIFT) + frac;
tracePoint.y = (y << TILESHIFT) + (flip ? TILEGLOBAL : 0) + offset * TILEGLOBAL * (flip ? -1 : 1);
tracePoint.flags &= ~TRACE_HIT_VERT;
}
tracePoint.flags |= TRACE_HIT_PWALL;
tracePoint.tileX = x;
tracePoint.tileY = y;
tracePoint.frac = frac / TILEGLOBAL;
return true;
}
return false; // no intersection, go on!
}
function trace(level, visibleTiles, tracePoint) {
var xtilestep, ytilestep,
xstep, ystep,
xtile, ytile,
xintercept, yintercept,
YmapPos, XmapPos,
tileMap = level.tileMap,
doorMap = level.state.doorMap,
q;
// Setup for ray casting
q = getQuadrant(FINE2RAD(tracePoint.angle));
xtilestep = x_tile_step[q];
ytilestep = y_tile_step[q];
xtile = POS2TILE(tracePoint.x) + xtilestep;
ytile = POS2TILE(tracePoint.y) + ytilestep;
xstep = ytilestep * XnextTable[tracePoint.angle];
ystep = xtilestep * YnextTable[tracePoint.angle];
xintercept = (((((ytilestep == -1 ? ytile+1 : ytile) << TILESHIFT) - tracePoint.y)
/ TanTable[tracePoint.angle])>>0) + tracePoint.x;
yintercept = (((((xtilestep == -1 ? xtile+1 : xtile) << TILESHIFT) - tracePoint.x)
* TanTable[tracePoint.angle])>>0) + tracePoint.y;
YmapPos = yintercept >> TILESHIFT; // toXray
XmapPos = xintercept >> TILESHIFT; // toYray
if (visibleTiles) {
// this tile is visible
visibleTiles[POS2TILE(tracePoint.x)][POS2TILE(tracePoint.y)] = true;
}
var traceCount = 0;
// Start of ray-casting
while (1) {
traceCount++;
// Vertical loop // an analogue for X-Ray
while (!(ytilestep == -1 && YmapPos <= ytile) && !(ytilestep == 1 && YmapPos >= ytile)) {
if (xtile < 0 || xtile >= 64 || YmapPos < 0 || YmapPos >= 64) {
tracePoint.oob = true;
return;
}
if (traceCheck(tileMap, doorMap, visibleTiles, xtile, YmapPos, yintercept % TILEGLOBAL, ystep, true, (xtilestep == -1), tracePoint)) {
if (xstep < 0) {
tracePoint.frac = 1 - tracePoint.frac;
}
return;
}
// prepare for next step
xtile += xtilestep;
yintercept += ystep;
YmapPos = yintercept >> TILESHIFT;
}
// Horizontal loop // an analogue for Y-Ray
while (!(xtilestep == -1 && XmapPos <= xtile) && !(xtilestep == 1 && XmapPos >= xtile)) {
if (ytile < 0 || ytile >= 64 || XmapPos < 0 || XmapPos >= 64) {
tracePoint.oob = true;
return;
}
if (traceCheck(tileMap, doorMap, visibleTiles, XmapPos, ytile, xintercept % TILEGLOBAL, xstep, false, (ytilestep == -1), tracePoint)) {
if (ystep > 0) {
tracePoint.frac = 1 - tracePoint.frac;
}
return;
}
// prepare for next step
ytile += ytilestep;
xintercept += xstep;
XmapPos = xintercept >> TILESHIFT;
}
if (traceCount > 1000) {
return;
}
} // end of while( 1 )
}
function traceRays(viewport, level) {
var n, i, j,
tileMap = level.tileMap,
tracePoint,
visibleTiles = [],
numRays = Wolf.XRES / Wolf.SLICE_WIDTH,
tracers = [];
for (i=0;i<64;i++) {
visibleTiles[i] = [];
for (j=0;j<64;j++) {
visibleTiles[i][j] = 0;
}
}
// Ray casting
for (n = 0 ; n < numRays ; ++n) {
tracePoint = {
x : viewport.x,
y : viewport.y,
angle : Wolf.Math.normalizeAngle(viewport.angle - Wolf.Math.ColumnAngle[n * Wolf.SLICE_WIDTH]),
flags : Wolf.TRACE_SIGHT | Wolf.TRACE_MARK_MAP,
oob : false
};
trace(level, visibleTiles, tracePoint);
tracers[n] = tracePoint;
// Ugly hack to get rid of "blank slice" glitch due to out-of-bounds raycasting.
// We simply re-use the previous slice if possible.
if (tracePoint.oob) {
if (n > 0 && !tracers[n-1].oob) {
tracers[n] = tracers[n-1];
}
}
}
return {
visibleTiles : visibleTiles,
tracers : tracers
};
}
return {
traceRays : traceRays,
trace : trace
};
})();

434
js/renderer.js Normal file
View File

@@ -0,0 +1,434 @@
/*
* ===========================================================================
*
* Wolf3D Browser Version GPL Source Code
* Copyright (C) 2012 id Software LLC, a ZeniMax Media company.
*
* This file is part of the Wolf3D Browser Version GPL Source Code ("Wolf3D Browser Source Code").
*
* Wolf3D Browser Source Code is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* Wolf3D Browser Source Code is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License version 2
* along with Wolf3D Browser Source Code. If not, see <http://www.gnu.org/licenses/>.
*
* If you have questions concerning this license, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
*
* ===========================================================================
*/
Wolf.setConsts({
FOV_RAD : 75 * Math.PI / 180,
ISCHROME : /chrome/.test(navigator.userAgent.toLowerCase()),
ISSAFARI : /safari/.test(navigator.userAgent.toLowerCase()),
ISFIREFOX : /firefox/.test(navigator.userAgent.toLowerCase()),
ISXP : /windows nt 5\./.test(navigator.userAgent.toLowerCase()),
ISWEBKIT : /webkit/.test(navigator.userAgent.toLowerCase())
});
Wolf.setConsts({
VIEW_DIST : (Wolf.XRES / 2) / Math.tan((Wolf.FOV_RAD / 2)),
TEXTURERESOLUTION : Wolf.ISCHROME ? 128 : 64
});
Wolf.Renderer = (function() {
var slices = [],
useBackgroundImage = Wolf.ISWEBKIT,
texturePath = "art/walls-shaded/" + Wolf.TEXTURERESOLUTION + "/",
spritePath = "art/sprites/" + Wolf.TEXTURERESOLUTION + "/",
sprites = [],
maxDistZ = 64 * 0x10000,
hasInit = false;
visibleSprites = [];
var TILESHIFT = Wolf.TILESHIFT,
TILEGLOBAL = Wolf.TILEGLOBAL,
TRACE_HIT_VERT = Wolf.TRACE_HIT_VERT,
TRACE_HIT_DOOR = Wolf.TRACE_HIT_DOOR,
WALL_TILE = Wolf.WALL_TILE,
DOOR_TILE = Wolf.DOOR_TILE,
TEX_PLATE = Wolf.TEX_PLATE,
TILE2POS = Wolf.TILE2POS,
POS2TILE = Wolf.POS2TILE,
VIEW_DIST = Wolf.VIEW_DIST,
SLICE_WIDTH = Wolf.SLICE_WIDTH,
WALL_TEXTURE_WIDTH = Wolf.WALL_TEXTURE_WIDTH,
FINE2RAD = Wolf.FINE2RAD,
XRES = Wolf.XRES,
YRES = Wolf.YRES,
MINDIST = Wolf.MINDIST,
cos = Math.cos,
sin = Math.sin,
tan = Math.tan,
atan2 = Math.atan2,
round = Math.round,
sqrt = Math.sqrt;
function init() {
var image, slice, x;
if (hasInit) {
return;
}
hasInit = true;
$("#game .renderer")
.width(Wolf.XRES + "px")
.height(Wolf.YRES + "px");
for (x=0; x<Wolf.XRES; x += Wolf.SLICE_WIDTH) {
slice = $("<div>");
slice.css({
position : "absolute",
width : Wolf.SLICE_WIDTH + "px",
height : Wolf.YRES + "px",
left : x + "px",
top : 0,
overflow : "hidden"
});
slice.appendTo("#game .renderer");
image = useBackgroundImage ? $("<div>") : $("<img>");
image.css({
position : "absolute",
display : "block",
top : 0,
height : 0,
width : Wolf.SLICE_WIDTH * Wolf.WALL_TEXTURE_WIDTH + "px",
backgroundSize : "100% 100%"
});
var sliceElement = slice[0];
sliceElement.texture = image[0];
sliceElement.appendChild(sliceElement.texture);
slices.push(sliceElement);
}
}
function reset() {
$("#game .renderer .sprite").remove();
sprites = [];
visibleSprites = [];
}
function processTrace(viewport, tracePoint) {
var x = tracePoint.x,
y = tracePoint.y,
vx = viewport.x,
vy = viewport.y,
dx = viewport.x - tracePoint.x,
dy = viewport.y - tracePoint.y,
dist = Math.sqrt(dx*dx + dy*dy),
frac,
h, w, offset;
// correct for fisheye
dist = dist * cos(FINE2RAD(tracePoint.angle - viewport.angle));
w = WALL_TEXTURE_WIDTH * SLICE_WIDTH;
h = (VIEW_DIST / dist * TILEGLOBAL) >> 0;
if (tracePoint.flags & TRACE_HIT_DOOR) {
if (tracePoint.flags & TRACE_HIT_VERT) {
if (x < vx) {
frac = tracePoint.frac;
} else {
frac = 1 - tracePoint.frac;
}
} else {
if (y < vy) {
frac = 1 - tracePoint.frac;
} else {
frac = tracePoint.frac;
}
}
} else {
frac = 1 - tracePoint.frac;
}
offset = frac * w;
if (offset > w - SLICE_WIDTH) {
offset = w - SLICE_WIDTH;
}
offset = round(offset / SLICE_WIDTH) * SLICE_WIDTH;
if (offset < 0) {
offset = 0;
}
return {
w : w,
h : h,
dist : dist,
vert : tracePoint.flags & TRACE_HIT_VERT,
offset : offset
};
}
function clear() {
var n, sprite;
for (n=0;n<visibleSprites.length;n++) {
sprite = visibleSprites[n].sprite;
if (sprite && sprite.div) {
sprite.div.style.display = "none";
}
}
}
function draw(viewport, level, tracers, visibleTiles) {
var n, tracePoint;
for (var n=0,len=tracers.length;n<len;++n) {
tracePoint = tracers[n];
if (!tracePoint.oob) {
if (tracePoint.flags & Wolf.TRACE_HIT_DOOR) {
drawDoor(n, viewport, tracePoint, level);
} else {
drawWall(n, viewport, tracePoint, level);
}
}
}
drawSprites(viewport, level, visibleTiles);
}
function updateSlice(n, textureSrc, proc) {
var slice = slices[n],
image = slice.texture,
sliceStyle = slice.style,
imgStyle = image.style,
top = (Wolf.YRES - proc.h) / 2,
left = -(proc.offset) >> 0,
height = proc.h,
z = (maxDistZ - proc.dist) >> 0,
itop;
if (Wolf.ISXP && Wolf.ISFIREFOX) {
itop = (proc.texture % 2) ? 0 : -height;
} else {
itop = -(proc.texture-1) * height;
textureSrc = "art/walls-shaded/64/walls.png";
}
if (image._src != textureSrc) {
image._src = textureSrc;
if (useBackgroundImage) {
imgStyle.backgroundImage = "url(" + textureSrc + ")";
} else {
image.src = textureSrc;
}
}
if (slice._zIndex != z) {
sliceStyle.zIndex = slice._zIndex = z;
}
if (image._height != height) {
sliceStyle.height = (image._height = height) + "px";
if (Wolf.ISXP && Wolf.ISFIREFOX) {
imgStyle.height = (height * 2) + "px";
} else {
imgStyle.height = (height * 120) + "px";
}
}
if (image._itop != itop) {
imgStyle.top = (image._itop = itop) + "px";
}
if (image._top != top) {
sliceStyle.top = (image._top = top) + "px";
}
if (image._left != left) {
imgStyle.left = (image._left = left) + "px";
}
}
function drawWall(n, viewport, tracePoint, level) {
var x = tracePoint.tileX,
y = tracePoint.tileY,
vx = POS2TILE(viewport.x),
vy = POS2TILE(viewport.y),
tileMap = level.tileMap,
proc = processTrace(viewport, tracePoint),
texture = proc.vert ? level.wallTexX[x][y] : level.wallTexY[x][y],
textureSrc;
// door sides
if (tracePoint.flags & TRACE_HIT_VERT) {
if (x >= vx && tileMap[x-1][y] & DOOR_TILE) {
texture = TEX_PLATE;
}
if (x < vx && tileMap[x+1][y] & DOOR_TILE) {
texture = TEX_PLATE;
}
} else {
if (y >= vy && tileMap[x][y-1] & DOOR_TILE) {
texture = TEX_PLATE;
}
if (y < vy && tileMap[x][y+1] & DOOR_TILE) {
texture = TEX_PLATE;
}
}
texture++;
proc.texture = texture;
if (texture % 2 == 0) {
texture--;
}
textureSrc = texturePath + "w_" + texture + ".png";
updateSlice(n, textureSrc, proc);
}
function drawDoor(n, viewport, tracePoint, level) {
var proc = processTrace(viewport, tracePoint),
texture, textureSrc;
//texture = Wolf.TEX_DDOOR + 1;
texture = level.state.doorMap[tracePoint.tileX][tracePoint.tileY].texture + 1;
proc.texture = texture;
if (texture % 2 == 0) {
texture -= 1;
}
textureSrc = texturePath + "w_" + texture + ".png";
updateSlice(n, textureSrc, proc);
}
function drawSprites(viewport, level, visibleTiles) {
var vis, n,
dist, dx, dy, angle,
z, width, size,
div, image,
divStyle, imgStyle;
// build visible sprites list
visibleSprites = Wolf.Sprites.createVisList(viewport, level, visibleTiles);
for (n = 0; n < visibleSprites.length; ++n ){
vis = visibleSprites[n];
dist = vis.dist;
if (dist < MINDIST / 2 ) {
//continue; // little hack to save speed & z-buffer
}
// make sure sprite is loaded
if (!vis.sprite.div) {
loadSprite(vis.sprite)
}
div = vis.sprite.div;
divStyle = div.style;
image = div.image;
imgStyle = image.style;
dx = vis.sprite.x - viewport.x;
dy = vis.sprite.y - viewport.y;
angle = atan2(dy, dx) - FINE2RAD(viewport.angle);
//dist = dist * Math.cos(angle);
size = (VIEW_DIST / dist * TILEGLOBAL) >> 0;
divStyle.display = "block";
divStyle.width = size + "px";
divStyle.height = size + "px";
divStyle.left = (XRES / 2 - size / 2 - tan(angle) * VIEW_DIST) + "px";
divStyle.top = (YRES / 2 - size / 2) + "px";
texture = Wolf.Sprites.getTexture(vis.sprite.tex[0]);
textureSrc = spritePath + texture.sheet;
if (image._src != textureSrc) {
image._src = textureSrc;
if (useBackgroundImage) {
imgStyle.backgroundImage = "url(" + textureSrc + ")";
} else {
image.src = textureSrc;
}
}
z = (maxDistZ - dist) >> 0;
width = texture.num * size;
left = -texture.idx * size;
if (div._zIndex != z) {
divStyle.zIndex = div._zIndex = z;
}
if (image._width != width) {
imgStyle.width = (image._width = width) + "px";
}
if (image._height != size) {
imgStyle.height = (image._height = size) + "px";
}
if (image._left != left) {
imgStyle.left = (image._left = left) + "px";
}
}
}
function unloadSprite(sprite) {
if (sprite.div) {
$(sprite.div).remove();
sprite.div = null;
}
}
function loadSprite(sprite) {
var div = document.createElement("div"),
image;
div.style.display = "none";
div.style.position = "absolute";
div.style.width = "128px";
div.style.height = "128px";
div.style.overflow = "hidden";
div.className = "sprite";
image = useBackgroundImage ? $("<div>") : $("<img>");
image.css({
position : "absolute",
display : "block",
top : 0,
height : "100%",
width : "100%",
backgroundSize : "100%",
backgroundRepeat : "no-repeat"
});
div.image = image[0];
div.appendChild(div.image);
sprite.div = div;
$("#game .renderer").append(div);
}
return {
init : init,
draw : draw,
clear : clear,
loadSprite : loadSprite,
unloadSprite : unloadSprite,
reset : reset
};
})();

56
js/requestanimframe.js Normal file
View File

@@ -0,0 +1,56 @@
/*
* ===========================================================================
*
* Wolf3D Browser Version GPL Source Code
* Copyright (C) 2012 id Software LLC, a ZeniMax Media company.
*
* This file is part of the Wolf3D Browser Version GPL Source Code ("Wolf3D Browser Source Code").
*
* Wolf3D Browser Source Code is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* Wolf3D Browser Source Code is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License version 2
* along with Wolf3D Browser Source Code. If not, see <http://www.gnu.org/licenses/>.
*
* If you have questions concerning this license, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
*
* ===========================================================================
*/
/* requestAnimationFrame polyfill */
(function() {
window.requestAnimationFrame = (function() {
return window.requestAnimationFrame
|| window.webkitRequestAnimationFrame
|| window.mozRequestAnimationFrame
|| window.oRequestAnimationFrame
|| window.msRequestAnimationFrame
|| function(callback, element) {
return window.setTimeout(
function() {
callback(Date.now());
}, 1000 / 60
);
};
})();
window.cancelRequestAnimationFrame = (function() {
return window.cancelRequestAnimationFrame
|| window.webkitCancelRequestAnimationFrame
|| window.mozCancelRequestAnimationFrame
|| window.oCancelRequestAnimationFrame
|| window.msCancelRequestAnimationFrame
|| window.clearTimeout;
})();
})();

208
js/sound.js Normal file
View File

@@ -0,0 +1,208 @@
/*
* ===========================================================================
*
* Wolf3D Browser Version GPL Source Code
* Copyright (C) 2012 id Software LLC, a ZeniMax Media company.
*
* This file is part of the Wolf3D Browser Version GPL Source Code ("Wolf3D Browser Source Code").
*
* Wolf3D Browser Source Code is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* Wolf3D Browser Source Code is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License version 2
* along with Wolf3D Browser Source Code. If not, see <http://www.gnu.org/licenses/>.
*
* If you have questions concerning this license, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
*
* ===========================================================================
*/
Wolf.Sound = (function() {
Wolf.setConsts({
// Sound channels
// Channel 0 never willingly overrides
// Other channels (1-7) always override a playing sound on that channel
CHAN_AUTO : 0,
CHAN_WEAPON : 1,
CHAN_VOICE : 2,
CHAN_ITEM : 3,
CHAN_BODY : 4,
// Modifier flags
CHAN_NO_PHS_ADD : 8, // Send to all clients, not just ones in PHS (ATTN 0 will also do this)
CHAN_RELIABLE : 16, // Send by reliable message, not datagram
// Sound attenuation values
ATTN_NONE : 0, // Full volume the entire level
ATTN_NORM : 1,
ATTN_IDLE : 2,
ATTN_STATIC : 3, // Diminish very rapidly with distance
MAX_PLAYSOUNDS : 128,
MAX_CHANNELS : 64,
MUSIC_VOLUME : 0.8,
MASTER_VOLUME : 0.6
});
var sounds = {},
audioElements = [],
currentMusic,
soundEnabled = true,
musicEnabled = true,
music,
ext,
exts = ["ogg", "mp3"];
function getFileName(file) {
if (!ext) {
// look for a probably
for (var i=0;i<exts.length;i++) {
if (Modernizr.audio[exts[i]] == "probably") {
ext = exts[i];
break;
}
}
// look for a maybe
if (!ext) {
for (var i=0;i<exts.length;i++) {
if (Modernizr.audio[exts[i]] == "maybe") {
ext = exts[i];
break;
}
}
}
}
return file.split(".")[0] + "." + ext
}
function createAudioElement() {
var audio = new Audio();
audioElements.push(audio);
return audio;
}
function startSound(posPlayer, posSound, entNum, entChannel, file, volume, attenuation, timeOfs) {
var audio, dx, dy, dist;
if (!sounds[file]) {
sounds[file] = [];
}
for (var i=0;i<sounds[file].length;i++) {
if (sounds[file][i].ended || sounds[file][i].paused) {
audio = sounds[file][i];
break;
}
}
if (!audio) {
audio = createAudioElement();
audio.src = getFileName(file);
sounds[file].push(audio);
}
if (posPlayer && posSound) {
dx = (posPlayer.x - posSound.x) / Wolf.TILEGLOBAL;
dy = (posPlayer.y - posSound.y) / Wolf.TILEGLOBAL;
dist = dx * dx + dy * dy;
volume *= 1 / (1 + dist / 50);
}
audio.volume = volume * Wolf.MASTER_VOLUME * (soundEnabled ? 1 : 0);
audio.play();
}
function startMusic(file) {
if (!music) {
music = createAudioElement();
music.loop = true;
}
var filename = getFileName(file);
if (currentMusic != filename) {
music.src = currentMusic = filename;
music.volume = Wolf.MUSIC_VOLUME * Wolf.MASTER_VOLUME * (musicEnabled ? 1 : 0);
music.play();
}
}
function stopAllSounds() {
for (var i=0;i<audioElements.length;i++) {
if (audioElements[i].currentTime > 0) {
audioElements[i].currentTime = 0;
audioElements[i].pause();
}
}
}
function init() {
}
function isMusicEnabled() {
return musicEnabled
}
function isSoundEnabled() {
return soundEnabled;
}
function toggleMusic(enable) {
if (typeof enable != "undefined") {
musicEnabled = enable;
} else {
musicEnabled = !musicEnabled;
}
if (music) {
music.volume = Wolf.MUSIC_VOLUME * Wolf.MASTER_VOLUME * (musicEnabled ? 1 : 0);
}
}
function pauseMusic(enable) {
if (music) {
if (enable) {
music.pause();
} else if (music.paused) {
music.play();
}
}
}
function toggleSound(enable) {
if (typeof enable != "undefined") {
soundEnabled = enable;
} else {
soundEnabled = !soundEnabled;
}
}
if (Modernizr.audio) {
return {
startSound : startSound,
startMusic : startMusic,
stopAllSounds : stopAllSounds,
isMusicEnabled : isMusicEnabled,
isSoundEnabled : isSoundEnabled,
toggleMusic : toggleMusic,
toggleSound : toggleSound,
pauseMusic : pauseMusic,
init : init
}
} else {
return {
startSound : Wolf.noop,
startMusic : Wolf.noop,
stopAllSounds : Wolf.noop,
isMusicEnabled : Wolf.noop,
isSoundEnabled : Wolf.noop,
toggleMusic : Wolf.noop,
toggleSound : Wolf.noop,
pauseMusic : Wolf.noop,
init : Wolf.noop
}
}
})();

1085
js/sprites.js Normal file

File diff suppressed because it is too large Load Diff

175
js/weapon.js Normal file
View File

@@ -0,0 +1,175 @@
/*
* ===========================================================================
*
* Wolf3D Browser Version GPL Source Code
* Copyright (C) 2012 id Software LLC, a ZeniMax Media company.
*
* This file is part of the Wolf3D Browser Version GPL Source Code ("Wolf3D Browser Source Code").
*
* Wolf3D Browser Source Code is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* Wolf3D Browser Source Code is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License version 2
* along with Wolf3D Browser Source Code. If not, see <http://www.gnu.org/licenses/>.
*
* If you have questions concerning this license, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
*
* ===========================================================================
*/
/**
* @namespace
* @description Weapons
*/
Wolf.Weapon = (function() {
function fireHit(game, player) {
var level = game.level,
closest,
dist,
d1,
n,
shotDist,
damage,
guard;
Wolf.Sound.startSound(null, null, 0, Wolf.CHAN_WEAPON, "lsfx/023.wav", 1, Wolf.ATTN_NORM, 0);
// actually fire
dist = 0x7fffffff;
closest = null;
for (n=0; n<level.state.numGuards; ++n) {
guard = level.state.guards[n];
if (guard.flags & Wolf.FL_SHOOTABLE) { // && Guards[n].flags&FL_VISABLE
shotDist = Wolf.Math.point2LineDist(guard.x - player.position.x, guard.y - player.position.y, player.angle);
if (shotDist > (2 * Wolf.TILEGLOBAL / 3)) {
continue; // miss
}
d1 = Wolf.Math.lineLen2Point(guard.x - player.position.x, guard.y - player.position.y, player.angle);
if (d1 < 0 || d1 > dist) {
continue;
}
if (!Wolf.Level.checkLine(guard.x, guard.y, player.position.x, player.position.y, level)) {
continue; // obscured
}
dist = d1;
closest = guard;
}
}
if (!closest || dist > Wolf.TILE2POS(1)) {
return; // missed if further than 1.5 tiles
}
damage = Wolf.Random.rnd() >> 4;
Wolf.ActorAI.damageActor(closest, game, player, damage); // hit something
}
function fireLead(game, player) {
var level = game.level,
closest,
damage,
dx, dy, dist,
d1, shotDist, n,
guard;
switch (player.weapon) {
case Wolf.WEAPON_PISTOL:
Wolf.Sound.startSound(null, null, 0, Wolf.CHAN_WEAPON, "sfx/012.wav", 1, Wolf.ATTN_NORM, 0);
break;
case Wolf.WEAPON_AUTO:
Wolf.Sound.startSound(null, null, 0, Wolf.CHAN_WEAPON, "sfx/011.wav", 1, Wolf.ATTN_NORM, 0);
break;
case Wolf.WEAPON_CHAIN:
Wolf.Sound.startSound(null, null, 0, Wolf.CHAN_WEAPON, "sfx/013.wav", 1, Wolf.ATTN_NORM, 0);
break;
}
player.madenoise = true;
dist = 0x7fffffff;
closest = null;
for (n=0;n < level.state.numGuards; ++n) {
guard = level.state.guards[n];
if (guard.flags & Wolf.FL_SHOOTABLE ) { // && Guards[n].flags&FL_VISABLE
shotDist = Wolf.Math.point2LineDist(guard.x - player.position.x, guard.y - player.position.y, player.angle);
if (shotDist > (2 * Wolf.TILEGLOBAL / 3)) {
continue; // miss
}
d1 = Wolf.Math.lineLen2Point(guard.x - player.position.x, guard.y - player.position.y, player.angle);
if (d1 < 0 || d1 > dist) {
continue;
}
if (!Wolf.Level.checkLine(guard.x, guard.y, player.position.x, player.position.y, level)) {
continue; // obscured
}
dist = d1;
closest = guard;
}
}
if (!closest) { // missed
var tracePoint = {
angle : Wolf.Math.normalizeAngle(player.angle - Wolf.DEG2FINE(2) + (Math.random() * 0x10000) % (Wolf.DEG2FINE(4))),
x : player.position.x,
y : player.position.y,
flags : Wolf.TRACE_BULLET
}
Wolf.Raycaster.trace(level, null, tracePoint);
if (tracePoint.flags & Wolf.TRACE_HIT_DOOR) {
Wolf.Sound.startSound(null, null, 0, Wolf.CHAN_AUTO, "lsfx/028.wav", 1, Wolf.ATTN_NORM, 0);
}
return;
}
// hit something
dx = Math.abs(closest.tile.x - player.tile.x);
dy = Math.abs(closest.tile.y - player.tile.y);
dist = Math.max(dx, dy);
if (dist < 2) {
damage = Wolf.Random.rnd() / 4;
} else if (dist < 4) {
damage = Wolf.Random.rnd() / 6;
} else {
if (Wolf.Random.rnd() / 12 < dist) {
return; // missed
}
damage = Wolf.Random.rnd() / 6;
}
Wolf.ActorAI.damageActor(closest, game, player, damage);
}
return {
fireHit : fireHit,
fireLead : fireLead
};
})();

117
js/wolf.js Normal file
View File

@@ -0,0 +1,117 @@
/*
* ===========================================================================
*
* Wolf3D Browser Version GPL Source Code
* Copyright (C) 2012 id Software LLC, a ZeniMax Media company.
*
* This file is part of the Wolf3D Browser Version GPL Source Code ("Wolf3D Browser Source Code").
*
* Wolf3D Browser Source Code is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* Wolf3D Browser Source Code is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License version 2
* along with Wolf3D Browser Source Code. If not, see <http://www.gnu.org/licenses/>.
*
* If you have questions concerning this license, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
*
* ===========================================================================
*/
/** @namespace */
var Wolf = {
XRES : 608,
YRES : 304,
SLICE_WIDTH : 3,
WALL_TEXTURE_WIDTH : 64,
NUM_WALL_TEXTURES : 55,
HUD_FACE_WIDTH : 48,
HUD_FACE_HEIGHT : 64,
HUD_WEAPON_WIDTH : 256,
NUMAREAS : 37, // number of areas
FIRSTAREA : 0x6B, // first area in map data (it is by the way a way to the secret floor!)
AMBUSHTILE : 0x6A, // def guard
AMBUSH : -2,
TILEGLOBAL : 0x10000,
HALFTILE : 0x8000,
TILESHIFT : 16,
MINDIST : 0x5800,
FLOATTILE : 65536.0,
TILE2POS : function(a) { return (((a)<<Wolf.TILESHIFT)+Wolf.HALFTILE); },
POS2TILE : function(a) { return ((a)>>Wolf.TILESHIFT); },
POS2TILEf : function(a) { return ((a)/Wolf.FLOATTILE); },
ASTEP : 0.0078125, // 1 FINE=x DEGREES
ASTEPRAD : 0.000136354, // 1 FINE=x RADIANS
ANG_1RAD : 7333.8598, // 1 RADIAN=x FINES
ANG_0 : 0, //(int)((float)0/ASTEP)
ANG_1 : 128, //(int)((float)1/ASTEP)
ANG_6 : 768, //(int)((float)6/ASTEP)
ANG_15 : 1920, //(int)((float)15/ASTEP)
ANG_22_5 : 2880, //(int)((float)22.5/ASTEP)
ANG_30 : 3840, //(int)((float)30/ASTEP)
ANG_45 : 5760, //(int)((float)45/ASTEP)
ANG_67_5 : 8640, //(int)((float)67.5/ASTEP)
ANG_90 : 11520, //(int)((float)90/ASTEP)
ANG_112_5 : 14400, //(int)((float)112.5/ASTEP)
ANG_135 : 17280, //(int)((float)135/ASTEP)
ANG_157_5 : 20160, //(int)((float)157.5/ASTEP)
ANG_180 : 23040, //(int)((float)180/ASTEP)
ANG_202_5 : 25920, //(int)((float)202.5/ASTEP)
ANG_225 : 28800, //(int)((float)225/ASTEP)
ANG_247_5 : 31680, //(int)((float)247.5/ASTEP)
ANG_270 : 34560, //(int)((float)270/ASTEP)
ANG_292_5 : 37440, //(int)((float)292.5/ASTEP)
ANG_315 : 40320, //(int)((float)225/ASTEP)
ANG_337_5 : 43200, //(int)((float)337.5/ASTEP)
ANG_360 : 46080, //(int)((float)360/ASTEP)
ANGLES : 360, // must be divisable by 4
DEATHROTATE : 2,
FINE2RAD : function(a) { return (a * Math.PI / Wolf.ANG_180); },
RAD2FINE : function(a) { return (a * Wolf.ANG_180 / Math.PI); },
FINE2DEG : function(a) { return (a / Wolf.ANG_1) >> 0; }, // !@# don't lose precision bits
FINE2DEGf : function(a) { return (a / Wolf.ANG_1); },
DEG2FINE : function(a) { return (a * Wolf.ANG_1); }
};
Wolf.setConsts = function(C) {
for (var a in C) {
if (C.hasOwnProperty(a) && !(a in Wolf)) {
Wolf[a] = C[a];
}
}
};
Wolf.noop = function() {};
Wolf.log = function(str) {
/*
if (typeof console != "undefined") {
var t = new Date(),
e = new Error(),
f = "";
if (typeof str == "object" && typeof e.stack == "string") {
// ugly hack to get some kind of reference to where the log call originated
var s = e.stack.split("\n")[2]+"",
m = s.match(/at (.*)$/);
f = m ? "\t[" + m[1] + "]" : "";
}
console.log(t.toLocaleTimeString() + ": " + str + f);
}
*/
};