/*
* ===========================================================================
*
* 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 .
*
* 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= 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
};
})();