mirror of
https://github.com/id-Software/wolf3d-browser.git
synced 2024-11-09 23:11:43 +00:00
e0b653888d
This is the code for the browser version of Wolfenstein 3D which is playable on http://www.wolfenstein.com/game_NA.php
814 lines
26 KiB
JavaScript
814 lines
26 KiB
JavaScript
/*
|
|
* ===========================================================================
|
|
*
|
|
* 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
|
|
};
|
|
|
|
})();
|