123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206 |
- /*
- * ===========================================================================
- *
- * 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 Enemy AI
- */
- Wolf.AI = (function() {
- Wolf.setConsts({
- RUNSPEED : 6000,
- MINSIGHT : 0x18000
- });
- function checkSight(self, game) {
- var level = game.level,
- player = game.player,
- deltax, deltay;
- // don't bother tracing a line if the area isn't connected to the player's
- if (!(self.flags & Wolf.FL_AMBUSH)) {
- if (!level.state.areabyplayer[self.areanumber]) {
- return false;
- }
- }
- // if the player is real close, sight is automatic
- deltax = player.position.x - self.x;
- deltay = player.position.y - self.y;
- if (Math.abs(deltax) < Wolf.MINSIGHT && Math.abs(deltay) < Wolf.MINSIGHT) {
- return true;
- }
- // see if they are looking in the right direction
- switch (self.dir) {
- case Wolf.Math.dir8_north:
- if (deltay < 0) {
- return false;
- }
- break;
- case Wolf.Math.dir8_east:
- if (deltax < 0) {
- return false;
- }
- break;
- case Wolf.Math.dir8_south:
- if (deltay > 0) {
- return false;
- }
- break;
- case Wolf.Math.dir8_west:
- if (deltax > 0) {
- return false;
- }
- break;
- default:
- break;
- }
- // trace a line to check for blocking tiles (corners)
- return Wolf.Level.checkLine(self.x, self.y, player.position.x, player.position.y, level);
- }
- /**
- * @description Entity is going to move in a new direction.
- * Called, when actor finished previous moving & located in
- * the 'center' of the tile. Entity will try walking in direction.
- * @private
- * @returns {boolean} true if direction is OK, otherwise false.
- */
- function changeDir(self, new_dir, level) {
- var oldx,
- oldy,
- newx,
- newy, // all it tiles
- n,
- moveok = false;
-
- oldx = Wolf.POS2TILE(self.x);
- oldy = Wolf.POS2TILE(self.y);
- //assert( new_dir >= 0 && new_dir <= 8 );
- newx = oldx + Wolf.Math.dx8dir[new_dir];
- newy = oldy + Wolf.Math.dy8dir[new_dir];
- if (new_dir & 0x01) { // same as %2 (diagonal dir)
- if (level.tileMap[newx][oldy] & Wolf.SOLID_TILE ||
- level.tileMap[oldx][newy] & Wolf.SOLID_TILE ||
- level.tileMap[newx][newy] & Wolf.SOLID_TILE) {
- return false;
- }
- 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 == newx && level.state.guards[n].tile.y == newy) {
- return false; // another guard in path
- }
- if (level.state.guards[n].tile.x == oldx && level.state.guards[n].tile.y == newy) {
- return false; // another guard in path
- }
- if (level.state.guards[n].tile.x == newx && level.state.guards[n].tile.y == oldy) {
- return false; // another guard in path
- }
- }
- } else { // linear dir (E, N, W, S)
- if (level.tileMap[newx][newy] & Wolf.SOLID_TILE) {
- return false;
- }
- if (level.tileMap[newx][newy] & Wolf.DOOR_TILE) {
- if (self.type == Wolf.en_fake || self.type == Wolf.en_dog) { // they can't open doors
- if (level.state.doorMap[newx][newy].action != Wolf.dr_open) { // path is blocked by a closed opened door
- return false;
- }
- } else {
- self.waitfordoorx = newx;
- self.waitfordoory = newy;
- moveok = true;
- }
- }
- if (!moveok) {
- 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 == newx && level.state.guards[n].tile.y == newy) {
- return false; // another guard in path
- }
- }
- }
- }
- //moveok:
- self.tile.x = newx;
- self.tile.y = newy;
- level.tileMap[oldx][oldy] &= ~Wolf.ACTOR_TILE; // update map status
- level.tileMap[newx][newy] |= Wolf.ACTOR_TILE;
- if (level.areas[newx][newy] > 0) {
- // ambush tiles don't have valid area numbers (-3), so don't change the area if walking over them
- self.areanumber = level.areas[newx][newy];
- // assert( self.areanumber >= 0 && self.areanumber < NUMAREAS );
- }
- self.distance = Wolf.TILEGLOBAL;
- self.dir = new_dir;
- return true;
- }
- /**
- * @description Entity is going to turn on a way point.
- * @private
- */
- function path(self, game) {
- var level = game.level;
- if (level.tileMap[self.x >> Wolf.TILESHIFT][self.y >> Wolf.TILESHIFT] & Wolf.WAYPOINT_TILE) {
-
- var tileinfo = level.tileMap[self.x >> Wolf.TILESHIFT][self.y >> Wolf.TILESHIFT];
-
- if (tileinfo & Wolf.TILE_IS_E_TURN) {
- self.dir = Wolf.Math.dir8_east;
- } else if (tileinfo & Wolf.TILE_IS_NE_TURN) {
- self.dir = Wolf.Math.dir8_northeast;
- } else if (tileinfo & Wolf.TILE_IS_N_TURN) {
- self.dir = Wolf.Math.dir8_north;
- } else if (tileinfo & Wolf.TILE_IS_NW_TURN) {
- self.dir = Wolf.Math.dir8_northwest;
- } else if (tileinfo & Wolf.TILE_IS_W_TURN) {
- self.dir = Wolf.Math.dir8_west;
- } else if (tileinfo & Wolf.TILE_IS_SW_TURN) {
- self.dir = Wolf.Math.dir8_southwest;
- } else if (tileinfo & Wolf.TILE_IS_S_TURN) {
- self.dir = Wolf.Math.dir8_south;
- } else if (tileinfo & Wolf.TILE_IS_SE_TURN) {
- self.dir = Wolf.Math.dir8_southeast;
- }
- }
- if (!changeDir(self, self.dir, level)) {
- self.dir = Wolf.Math.dir8_nodir;
- }
- }
- /**
- * @description Called by entities that ARE NOT chasing the player.
- * @private
- */
- function findTarget(self, game, tics) {
- var level = game.level,
- player = game.player;
- if (self.temp2) { // count down reaction time
- self.temp2 -= tics;
- if (self.temp2 > 0) {
- return false;
- }
- self.temp2 = 0; // time to react
- } else {
-
- // check if we can/want to see/hear player
- if (player.flags & Wolf.FL_NOTARGET) {
- return false; // notarget cheat
- }
- // assert( self.areanumber >= 0 && self.areanumber < NUMAREAS );
- if (!(self.flags & Wolf.FL_AMBUSH) && ! level.state.areabyplayer[self.areanumber]) {
- return false;
- }
- if (!checkSight(self, game)) { // Player is visible - normal behavior
- if (self.flags & Wolf.FL_AMBUSH || !player.madenoise) {
- return false;
- }
- }
- self.flags &= ~Wolf.FL_AMBUSH;
-
-
- // if we are here we see/hear player!!!
- switch (self.type) {
- case Wolf.en_guard:
- self.temp2 = 1 + Wolf.Random.rnd() / 4;
- break;
- case Wolf.en_officer:
- self.temp2 = 2;
- break;
- case Wolf.en_mutant:
- self.temp2 = 1 + Wolf.Random.rnd() / 6;
- break;
- case Wolf.en_ss:
- self.temp2 = 1 + Wolf.Random.rnd() / 6;
- break;
- case Wolf.en_dog:
- self.temp2 = 1 + Wolf.Random.rnd() / 8;
- break;
- case Wolf.en_boss:
- case Wolf.en_schabbs:
- case Wolf.en_fake:
- case Wolf.en_mecha:
- case Wolf.en_hitler:
- case Wolf.en_gretel:
- case Wolf.en_gift:
- case Wolf.en_fat:
- case Wolf.en_spectre:
- case Wolf.en_angel:
- case Wolf.en_trans:
- case Wolf.en_uber:
- case Wolf.en_will:
- case Wolf.en_death:
- self.temp2 = 1;
- break;
- }
- return false; // we are amazed & waiting to understand what to do!
- }
- Wolf.ActorAI.firstSighting(self, game);
- return true;
- }
- /**
- * @description As dodge(), but doesn't try to dodge.
- * @private
- */
- function chase(self, game) {
- var level = game.level,
- player = game.player,
- deltax,
- deltay,
- d = [],
- tdir, olddir, turnaround;
- if (game.player.playstate == Wolf.ex_victory) {
- return;
- }
-
- olddir = self.dir;
- turnaround = Wolf.Math.opposite8[olddir];
- d[0] = d[1] = Wolf.Math.dir8_nodir;
- deltax = Wolf.POS2TILE(player.position.x) - Wolf.POS2TILE(self.x);
- deltay = Wolf.POS2TILE(player.position.y) - Wolf.POS2TILE(self.y);
- if (deltax > 0) {
- d[0] = Wolf.Math.dir8_east;
- } else if (deltax < 0) {
- d[0] = Wolf.Math.dir8_west;
- }
-
- if (deltay > 0) {
- d[1] = Wolf.Math.dir8_north;
- } else if (deltay < 0) {
- d[1] = Wolf.Math.dir8_south;
- }
- if (Math.abs(deltay) > Math.abs(deltax)) {
- tdir = d[0];
- d[0] = d[1];
- d[1] = tdir;
- } // swap d[0] & d[1]
- if (d[0] == turnaround) {
- d[0] = Wolf.Math.dir8_nodir;
- }
- if (d[1] == turnaround) {
- d[1] = Wolf.Math.dir8_nodir;
- }
- if (d[0] != Wolf.Math.dir8_nodir) {
- if (changeDir(self, d[0], level)) {
- return;
- }
- }
- if (d[1] != Wolf.Math.dir8_nodir) {
- if (changeDir(self, d[1], level)) {
- return;
- }
- }
- // there is no direct path to the player, so pick another direction
- if (olddir != Wolf.Math.dir8_nodir) {
- if (changeDir(self, olddir, level)) {
- return;
- }
- }
- if (Wolf.Random.rnd() > 128) { // randomly determine direction of search
- for (tdir = Wolf.Math.dir8_east; tdir <= Wolf.Math.dir8_south; tdir += 2) { // * Revision
- if (tdir != turnaround) {
- if (changeDir(self, tdir, level)) {
- return;
- }
- }
- }
- } else {
- for (tdir = Wolf.Math.dir8_south; tdir >= Wolf.Math.dir8_east; tdir -= 2) { // * Revision (JDC fix for unsigned enums)
- if (tdir != turnaround) {
- if (changeDir(self, tdir, level)) {
- return;
- }
- }
- }
- }
- if (turnaround != Wolf.Math.dir8_nodir) {
- if (changeDir(self, turnaround, level)) {
- return;
- }
- }
- self.dir = Wolf.Math.dir8_nodir; // can't move
- }
- /**
- * @description Run Away from player.
- * @private
- */
- function retreat(self, game) {
- var level = game.level,
- player = game.player,
- deltax,
- deltay,
- d = [],
- tdir;
- deltax = Wolf.POS2TILE(player.position.x) - Wolf.POS2TILE(self.x);
- deltay = Wolf.POS2TILE(player.position.y) - Wolf.POS2TILE(self.y);
- d[0] = deltax < 0 ? Wolf.Math.dir8_east : Wolf.Math.dir8_west;
- d[1] = deltay < 0 ? Wolf.Math.dir8_north : Wolf.Math.dir8_south;
- if (Math.abs(deltay) > Math.abs(deltax)) {
- tdir = d[0];
- d[0] = d[1];
- d[1] = tdir;
- } // swap d[0] & d[1]
- if (changeDir(self, d[0], level)) {
- return;
- }
- if (changeDir(self, d[1], level)) {
- return;
- }
- // there is no direct path to the player, so pick another direction
- if (Wolf.Random.rnd() > 128) { // randomly determine direction of search
- for(tdir = Wolf.Math.dir8_east; tdir <= Wolf.Math.dir8_south; tdir += 2 ) { // * Revision
- if (changeDir(self, tdir, level)) {
- return;
- }
- }
- } else {
- for (tdir = Wolf.Math.dir8_south; tdir >= Wolf.Math.dir8_east; tdir -= 2) { // * Revision (JDC fix for unsigned enums)
- if (changeDir(self, tdir, level)) {
- return;
- }
- }
- }
- self.dir = Wolf.Math.dir8_nodir; // can't move
- }
-
- /**
- * @description Attempts to choose and initiate a movement for entity
- * that sends it towards the player while dodging.
- * @private
- */
- function dodge(self, game) {
- var level = game.level,
- player = game.player,
- deltax,
- deltay,
- i,
-
- dirtry = [],
- turnaround,
- tdir;
-
- if (game.player.playstate == Wolf.ex_victory) {
- return;
- }
-
- if (self.flags & Wolf.FL_FIRSTATTACK) {
- // turning around is only ok the very first time after noticing the player
- turnaround = Wolf.Math.dir8_nodir;
- self.flags &= ~Wolf.FL_FIRSTATTACK;
- } else {
- turnaround = Wolf.Math.opposite8[self.dir];
- }
-
- deltax = Wolf.POS2TILE(player.position.x) - Wolf.POS2TILE(self.x);
- deltay = Wolf.POS2TILE(player.position.y) - Wolf.POS2TILE(self.y);
- //
- // arange 5 direction choices in order of preference
- // the four cardinal directions plus the diagonal straight towards
- // the player
- //
- if (deltax > 0) {
- dirtry[1] = Wolf.Math.dir8_east;
- dirtry[3] = Wolf.Math.dir8_west;
- } else {
- dirtry[1] = Wolf.Math.dir8_west;
- dirtry[3] = Wolf.Math.dir8_east;
- }
- if( deltay > 0 ) {
- dirtry[2] = Wolf.Math.dir8_north;
- dirtry[4] = Wolf.Math.dir8_south;
- } else {
- dirtry[2] = Wolf.Math.dir8_south;
- dirtry[4] = Wolf.Math.dir8_north;
- }
- // randomize a bit for dodging
- if (Math.abs(deltax) > Math.abs(deltay)) {
- tdir = dirtry[1]; dirtry[1]=dirtry[2]; dirtry[2]=tdir; // => swap dirtry[1] & dirtry[2]
- tdir = dirtry[3]; dirtry[3]=dirtry[4]; dirtry[4]=tdir; // => swap dirtry[3] & dirtry[4]
- }
- if (Wolf.Random.rnd() < 128) {
- tdir = dirtry[1]; dirtry[1]=dirtry[2]; dirtry[2]=tdir;
- tdir = dirtry[3]; dirtry[3]=dirtry[4]; dirtry[4]=tdir;
- }
- dirtry[0] = Wolf.Math.diagonal[dirtry[1]][dirtry[2]];
- // try the directions util one works
- for (i=0; i < 5; ++i) {
- if (dirtry[i] == Wolf.Math.dir8_nodir || dirtry[i] == turnaround) {
- continue;
- }
- if (changeDir(self, dirtry[i], level)) {
- return;
- }
- }
- // turn around only as a last resort
- if (turnaround != Wolf.Math.dir8_nodir) {
- if (changeDir(self, turnaround, level)) {
- return;
- }
- }
-
-
- self.dir = Wolf.Math.dir8_nodir;
- }
-
- /**
- * @memberOf Wolf.AI
- */
- function T_Stand(self, game, tics) {
- findTarget(self, game, tics);
- }
-
- /**
- * @memberOf Wolf.AI
- */
- function T_Path(self, game, tics) {
- var level = game.level;
- if (findTarget(self, game, tics)) {
- return;
- }
- if (!self.speed) {
- return; // if patroling with a speed of 0
- }
- if (self.dir == Wolf.Math.dir8_nodir) {
- path(self, game);
- if (self.dir == Wolf.Math.dir8_nodir) {
- return; // all movement is blocked
- }
- }
- T_Advance(self, game, path, tics);
- }
- /**
- * @description Try to damage the player.
- * @memberOf Wolf.AI
- */
- function T_Shoot(self, game, tics) {
- var level = game.level,
- player = game.player,
- dx, dy, dist,
- hitchance,
- damage;
- if (!level.state.areabyplayer[self.areanumber]) {
- return;
- }
-
- if (!Wolf.Level.checkLine(self.x, self.y, player.position.x, player.position.y, level)) {
- return; // player is behind a wall
- }
- dx = Math.abs(Wolf.POS2TILE(self.x ) - Wolf.POS2TILE(player.position.x));
- dy = Math.abs(Wolf.POS2TILE(self.y ) - Wolf.POS2TILE(player.position.y));
- dist = Math.max(dx, dy);
- if (self.type == Wolf.en_ss || self.type == Wolf.en_boss )
- {
- dist = dist * 2 / 3; // ss are better shots
- }
- if (player.speed >= Wolf.RUNSPEED) {
- hitchance = 160;
- } else {
- hitchance = 256;
- }
- // if guard is visible by player
- // player can see to dodge
- // (if CheckLine both player & enemy see each other)
- // So left only check if guard is in player's fov: FIXME: not fixed fov!
- var trans = Wolf.Math.transformPoint(self.x, self.y, player.position.x, player.position.y);
- if (Wolf.Angle.diff(trans, Wolf.FINE2DEG(player.angle)) < (Math.PI/3)) {
- hitchance -= dist * 16;
- } else {
- hitchance -= dist * 8;
- }
- // see if the shot was a hit
- if (Wolf.Random.rnd() < hitchance) {
- if (dist < 2) {
- damage = Wolf.Random.rnd() >> 2;
- } else if (dist < 4) {
- damage = Wolf.Random.rnd() >> 3;
- } else {
- damage = Wolf.Random.rnd() >> 4;
- }
- Wolf.Player.damage(player, self, damage);
- }
- switch (self.type) {
- case Wolf.en_ss:
- Wolf.Sound.startSound(player.position, self, 1, Wolf.CHAN_WEAPON, "sfx/024.wav", 1, Wolf.ATTN_NORM, 0);
- break;
- case Wolf.en_gift:
- case Wolf.en_fat:
- case Wolf.en_mecha:
- case Wolf.en_hitler:
- case Wolf.en_boss:
- Wolf.Sound.startSound(player.position, self, 1, Wolf.CHAN_WEAPON, "sfx/022.wav", 1, Wolf.ATTN_NORM, 0);
- break;
- default:
- Wolf.Sound.startSound(player.position, self, 1, Wolf.CHAN_WEAPON, "sfx/049.wav", 1, Wolf.ATTN_NORM, 0);
- break;
- }
- }
-
- /**
- * @description
- * @memberOf Wolf.AI
- */
- function T_Chase(self, game, tics) {
- var level = game.level,
- player = game.player,
- dx, dy,
- dist,
- chance,
- shouldDodge = false;
-
- // if (gamestate.victoryflag) return;
- if (Wolf.Level.checkLine(self.x, self.y, player.position.x, player.position.y, level)) { // got a shot at player?
- dx = Math.abs(Wolf.POS2TILE(self.x) - Wolf.POS2TILE(player.position.x));
- dy = Math.abs(Wolf.POS2TILE(self.y) - Wolf.POS2TILE(player.position.y));
- dist = Math.max(dx, dy);
- if (!dist || (dist == 1 && self.distance < 16)) {
- chance = 300;
- } else {
- chance = (tics << 4) / dist; // 100/dist;
- }
- if (Wolf.Random.rnd() < chance) {
- // go into attack frame
- Wolf.Actors.stateChange(self, Wolf.st_shoot1);
- return;
- }
- shouldDodge = true;
- }
-
- if (self.dir == Wolf.Math.dir8_nodir) {
- if (shouldDodge) {
- dodge(self, game);
- } else {
- chase(self, game);
- }
- if (self.dir == Wolf.Math.dir8_nodir) {
- return; // object is blocked in
- }
- self.angle = Wolf.Math.dir8angle[self.dir];
- }
- T_Advance(self, game, shouldDodge ? dodge : chase, tics);
- }
-
- /**
- * @description
- * @memberOf Wolf.AI
- */
- function T_DogChase(self, game, tics) {
- var level = game.level,
- player = game.player,
- dx, dy;
- if (self.dir == Wolf.Math.dir8_nodir) {
- dodge(self, game);
- self.angle = Wolf.Math.dir8angle[ self.dir ];
- if (self.dir == Wolf.Math.dir8_nodir) {
- return; // object is blocked in
- }
- }
- //
- // check for bite range
- //
- dx = Math.abs(player.position.x - self.x) - Wolf.TILEGLOBAL / 2;
- if (dx <= Wolf.MINACTORDIST) {
- dy = Math.abs(player.position.y - self.y) - Wolf.TILEGLOBAL / 2;
- if (dy <= Wolf.MINACTORDIST) {
- Wolf.Actors.stateChange(self, Wolf.st_shoot1);
- return; // bite player!
- }
- }
- T_Advance(self, game, dodge, tics);
- }
-
- /**
- * @description Try to damage the player.
- * @memberOf Wolf.AI
- */
- function T_BossChase(self, game, tics) {
- var level = game.level,
- player = game.player,
- dx, dy, dist,
- think,
- shouldDodge = false;
- dx = Math.abs(self.tile.x - Wolf.POS2TILE(player.position.x));
- dy = Math.abs(self.tile.y - Wolf.POS2TILE(player.position.y));
- dist = Math.max(dx, dy);
- if (Wolf.Level.checkLine(self.x, self.y, player.position.x, player.position.y, level)) {
- // got a shot at player?
- if (Wolf.Random.rnd() < tics << 3) {
- // go into attack frame
- Wolf.Actors.stateChange(self, Wolf.st_shoot1);
- return;
- }
- shouldDodge = true;
- }
- if( self.dir == Wolf.Math.dir8_nodir ) {
- if (shouldDodge) {
- dodge(self, game);
- } else {
- chase(self, game);
- }
- if( self.dir == Wolf.Math.dir8_nodir ) {
- // object is blocked in
- return;
- }
- }
- think = dist < 4 ? retreat : (shouldDodge ? dodge : chase);
- T_Advance(self, game, think, tics);
- }
-
-
- /**
- * @description
- * @memberOf Wolf.AI
- */
- function T_Fake(self, game, tics) {
- var level = game.level,
- player = game.player;
-
- if (Wolf.Level.checkLine(self.x, self.y, player.position.x, player.position.y, level)) {
- if (Wolf.Random.rnd() < tics << 1) {
- // go into attack frame
- Wolf.Actors.stateChange(self, Wolf.st_shoot1);
- return;
- }
- }
-
- if (self.dir == Wolf.Math.dir8_nodir) {
- dodge(self, game);
- if (self.dir == Wolf.Math.dir8_nodir ) {
- // object is blocked in
- return;
- }
- }
- T_Advance(self, game, dodge, tics);
- }
-
- /**
- * @description
- * @private
- */
- function T_Advance(self, game, think, tics) {
- var level = game.level,
- move, door;
- if (!think) {
- Wolf.log("Warning: Advance without <think> proc\n");
- return;
- }
- move = self.speed * tics;
- while (move > 0) {
- // waiting for a door to open
- if (self.waitfordoorx) {
- door = level.state.doorMap[self.waitfordoorx][self.waitfordoory];
- Wolf.Doors.open(door);
- if (door.action != Wolf.dr_open) {
- return; // not opened yet...
- }
- self.waitfordoorx = self.waitfordoory = 0; // go ahead, the door is now open
- }
- if (move < self.distance ) {
- T_Move(self, game, move);
- break;
- }
- // fix position to account for round off during moving
- self.x = Wolf.TILE2POS(self.tile.x);
- self.y = Wolf.TILE2POS(self.tile.y);
- move -= self.distance;
- // think: Where to go now?
- think(self, game, tics);
-
- self.angle = Wolf.Math.dir8angle[self.dir];
- if (self.dir == Wolf.Math.dir8_nodir) {
- return; // all movement is blocked
- }
- }
- }
-
- /**
- * @description Moves object for distance in global units, in self.dir direction.
- * @memberOf Wolf.AI
- */
- function T_Move(self, game, dist) {
- var level = game.level,
- player = game.player;
-
- if (self.dir == Wolf.Math.dir8_nodir || !dist) {
- return;
- }
- self.x += dist * Wolf.Math.dx8dir[self.dir];
- self.y += dist * Wolf.Math.dy8dir[self.dir];
- // check to make sure it's not on top of player
- if (Math.abs(self.x - player.position.x) <= Wolf.MINACTORDIST) {
- if (Math.abs(self.y - player.position.y) <= Wolf.MINACTORDIST) {
- var t = self.type;
- if (t == Wolf.en_blinky || t == Wolf.en_clyde || t == Wolf.en_pinky || t == Wolf.en_inky || t == Wolf.en_spectre) {
- Wolf.Player.damage(player, self, 2); // ghosts hurt player!
- }
- //
- // back up
- //
- self.x -= dist * Wolf.Math.dx8dir[self.dir];
- self.y -= dist * Wolf.Math.dy8dir[self.dir];
- return;
- }
- }
- self.distance -= dist;
- if (self.distance < 0) {
- self.distance = 0;
- }
- }
-
- /**
- * @description
- * @memberOf Wolf.AI
- */
- function T_Ghosts(self, game, tics) {
- var level = game.level,
- player = game.player;
- if (self.dir == Wolf.Math.dir8_nodir) {
- chase(self, game);
- if (self.dir == Wolf.Math.dir8_nodir ) {
- return; // object is blocked in
- }
- self.angle = Wolf.Math.dir8angle[self.dir];
- }
- T_Advance(self, game, chase, tics);
- }
-
- /**
- * @description
- * @memberOf Wolf.AI
- */
- function T_Bite(self, game, tics) {
- var level = game.level,
- player = game.player,
- dx, dy;
- Wolf.Sound.startSound(player.position, self, 1, Wolf.CHAN_VOICE, "sfx/002.wav", 1, Wolf.ATTN_NORM, 0);
- dx = Math.abs(player.position.x - self.x) - Wolf.TILEGLOBAL;
- if (dx <= Wolf.MINACTORDIST) {
- dy = Math.abs(player.position.y - self.y) - Wolf.TILEGLOBAL;
- if (dy <= Wolf.MINACTORDIST) {
- if (Wolf.Random.rnd() < 180) {
- Wolf.Player.damage(player, self, Wolf.Random.rnd() >> 4);
- return;
- }
- }
- }
- }
- /**
- * @description
- * @memberOf Wolf.AI
- */
- function T_UShoot(self, game, tics) {
- var level = game.level,
- player = game.player,
- dx, dy,
- dist;
- T_Shoot(self, game, tics);
- dx = Math.abs(self.tile.x - Wolf.POS2TILE(player.position.x));
- dy = Math.abs(self.tile.y - Wolf.POS2TILE(player.position.y));
- dist = Math.max(dx, dy);
- if (dist <= 1) {
- Wolf.Player.damage(player, self, 10);
- }
- }
-
-
- /**
- * @description
- * @memberOf Wolf.AI
- */
- function T_Launch(self, game, tics) {
- var level = game.level,
- player = game.player,
- proj, iangle;
- iangle = Wolf.Math.transformPoint(self.x, self.y, player.position.x, player.position.y) + Math.PI;
- if (iangle > 2 * Math.PI) {
- iangle -= 2 * Math.PI;
- }
- if (self.type == Wolf.en_death) {
- // death knight launches 2 rockets with 4 degree shift each.
- T_Shoot(self, game, tics);
- if (self.state == Wolf.st_shoot2) {
- iangle = Wolf.Math.normalizeAngle(iangle - Wolf.DEG2RAD(4));
- } else {
- iangle = Wolf.Math.normalizeAngle(iangle + Wolf.DEG2RAD(4));
- }
- }
- proj = Wolf.Actors.getNewActor(level);
- if (proj == null) {
- return;
- }
- proj.x = self.x;
- proj.y = self.y;
- proj.tile.x = self.tile.x;
- proj.tile.y = self.tile.y;
- proj.state = Wolf.st_stand;
- proj.ticcount = 1;
- proj.dir = Wolf.Math.dir8_nodir;
- proj.angle = Wolf.RAD2FINE(iangle)>>0;
- proj.speed = 0x2000;
- proj.flags = Wolf.FL_NONMARK; // FL_NEVERMARK;
- proj.sprite = Wolf.Sprites.getNewSprite(level);
-
- switch(self.type) {
- case Wolf.en_death:
- proj.type = Wolf.en_hrocket;
- Wolf.Sound.startSound(player.position, self, 1, Wolf.CHAN_WEAPON, "lsfx/078.wav", 1, Wolf.ATTN_NORM, 0);
- break;
- case Wolf.en_angel:
- proj.type = Wolf.en_spark;
- proj.state = Wolf.st_path1;
- Wolf.Sound.startSound(player.position, self, 1, Wolf.CHAN_WEAPON, "lsfx/069.wav", 1, Wolf.ATTN_NORM, 0);
- break;
- case Wolf.en_fake:
- proj.type = Wolf.en_fire;
- proj.state = Wolf.st_path1;
- proj.flags = Wolf.FL_NEVERMARK;
- proj.speed = 0x1200;
- Wolf.Sound.startSound(player.position, self, 1, Wolf.CHAN_WEAPON, "lsfx/069.wav", 1, Wolf.ATTN_NORM, 0);
- break;
- case Wolf.en_schabbs:
- proj.type = Wolf.en_needle;
- proj.state = Wolf.st_path1;
- Wolf.Sound.startSound(player.position, self, 1, Wolf.CHAN_WEAPON, "lsfx/008.wav", 1, Wolf.ATTN_NORM, 0);
- break;
- default:
- proj.type = Wolf.en_rocket;
- Wolf.Sound.startSound(player.position, self, 1, Wolf.CHAN_WEAPON, "lsfx/085.wav", 1, Wolf.ATTN_NORM, 0);
- }
-
- }
-
-
- /**
- * @description Called when projectile is airborne.
- * @private
- * @param {object} self The projectile actor object.
- * @param {object} level The level object.
- * @returns {boolean} True if move ok, otherwise false.
- */
- function projectileTryMove(self, level) {
- var PROJSIZE = 0x2000,
- xl, yl, xh, yh, x, y;
- xl = (self.x - PROJSIZE) >> Wolf.TILESHIFT;
- yl = (self.y - PROJSIZE) >> Wolf.TILESHIFT;
- xh = (self.x + PROJSIZE) >> Wolf.TILESHIFT;
- yh = (self.y + PROJSIZE) >> Wolf.TILESHIFT;
- // Checking for solid walls:
- for (y = yl; y <= yh; ++y) {
- for (x = xl; x <= xh; ++x) {
- // FIXME: decide what to do with statics & Doors!
- if (level.tileMap[x][y] & (Wolf.WALL_TILE | Wolf.BLOCK_TILE)) {
- return false;
- }
- if (level.tileMap[x][y] & Wolf.DOOR_TILE) {
- if (Wolf.Doors.opened(level.state.doorMap[x][y]) != Wolf.DOOR_FULLOPEN) {
- return false;
- }
- }
- }
- }
- // FIXME: Projectile will fly through objects (even guards & columns) - must fix to create rocket launcher!
- return true;
- }
- /**
- * @description Called when projectile is airborne.
- * @memberOf Wolf.AI
- * @param {object} self The enemy actor object.
- * @param {object} level The level object.
- * @param {object} player The player object.
- * @param {number} tics The number of tics.
- * @returns {boolean} True if move ok, otherwise false.
- */
- function T_Projectile(self, game, tics) {
- var level = game.level,
- player = game.player,
- PROJECTILESIZE = 0xC000,
- deltax, deltay,
- speed, damage;
-
- speed = self.speed * tics;
- deltax = (speed * Wolf.Math.CosTable[self.angle])>>0;
- deltay = (speed * Wolf.Math.SinTable[self.angle])>>0;
- if (deltax > Wolf.TILEGLOBAL) {
- deltax = Wolf.TILEGLOBAL;
- }
- if (deltax < -Wolf.TILEGLOBAL) {
- deltax = -Wolf.TILEGLOBAL; // my
- }
- if (deltay > Wolf.TILEGLOBAL) {
- deltay = Wolf.TILEGLOBAL;
- }
- if (deltay < -Wolf.TILEGLOBAL) {
- deltay = -Wolf.TILEGLOBAL; // my
- }
- self.x += deltax;
- self.y += deltay;
- deltax = Math.abs(self.x - player.position.x);
- deltay = Math.abs(self.y - player.position.y);
- if (!projectileTryMove(self, level)) {
- if (self.type == Wolf.en_rocket || self.type == Wolf.en_hrocket ) {
- // rocket ran into obstacle, draw explosion!
- Wolf.Sound.startSound(player.position, self, 1, Wolf.CHAN_WEAPON, "lsfx/086.wav", 1, Wolf.ATTN_NORM, 0);
- Wolf.Actors.stateChange(self, Wolf.st_die1);
- } else {
- Wolf.Actors.stateChange(self, Wolf.st_remove); // mark for removal
- }
- return;
- }
- if (deltax < PROJECTILESIZE && deltay < PROJECTILESIZE) {
- // hit the player
- switch (self.type) {
- case Wolf.en_needle:
- damage = (Wolf.Random.rnd() >> 3) + 20;
- break;
- case Wolf.en_rocket:
- case Wolf.en_hrocket:
- case Wolf.en_spark:
- damage = (Wolf.Random.rnd()>>3) + 30;
- break;
- case Wolf.en_fire:
- damage = (Wolf.Random.rnd() >> 3);
- break;
- default:
- damage = 0;
- break;
- }
- Wolf.Player.damage(player, self, damage);
- Wolf.Actors.stateChange(self, Wolf.st_remove); // mark for removal
- return;
- }
- self.tile.x = self.x >> Wolf.TILESHIFT;
- self.tile.y = self.y >> Wolf.TILESHIFT;
- }
-
- /**
- * @description
- * @memberOf Wolf.AI
- */
- function T_BJRun(self, game, tics) {
- var move = Wolf.BJRUNSPEED * tics;
- T_Move(self, game, move);
- if (!self.distance) {
- self.distance = Wolf.TILEGLOBAL;
- if (!(--self.temp2)) {
- Wolf.Actors.stateChange(self, Wolf.st_shoot1);
- self.speed = Wolf.BJJUMPSPEED;
- return;
- }
- }
- }
-
- /**
- * @description
- * @memberOf Wolf.AI
- */
- function T_BJJump(self, game, tics) {
- //var move = Wolf.BJRUNSPEED * tics;
- //T_Move(self, game, move);
- }
-
- /**
- * @description
- * @memberOf Wolf.AI
- */
- function T_BJYell(self, game, tics) {
- Wolf.Sound.startSound(null, null, 0, Wolf.CHAN_VOICE, "sfx/082.wav", 1, Wolf.ATTN_NORM, 0);
- }
-
- /**
- * @description
- * @memberOf Wolf.AI
- */
- function T_BJDone(self, game, tics) {
- Wolf.Player.playstate = Wolf.ex_victory; // exit castle tile
- //Wolf.Player.playstate = Wolf.ex_complete;
- Wolf.Game.endEpisode(game);
- }
-
- return {
- T_Stand : T_Stand,
- T_Path : T_Path,
- T_Ghosts : T_Ghosts,
- T_Bite : T_Bite,
- T_Shoot : T_Shoot,
- T_UShoot : T_UShoot,
- T_Launch : T_Launch,
- T_Chase : T_Chase,
- T_DogChase : T_DogChase,
- T_BossChase : T_BossChase,
- T_Fake : T_Fake,
- T_Projectile : T_Projectile,
- T_BJRun : T_BJRun,
- T_BJJump : T_BJJump,
- T_BJYell : T_BJYell,
- T_BJDone : T_BJDone
- };
-
- })();
|