actorai.js 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562
  1. /*
  2. * ===========================================================================
  3. *
  4. * Wolf3D Browser Version GPL Source Code
  5. * Copyright (C) 2012 id Software LLC, a ZeniMax Media company.
  6. *
  7. * This file is part of the Wolf3D Browser Version GPL Source Code ("Wolf3D Browser Source Code").
  8. *
  9. * Wolf3D Browser Source Code is free software: you can redistribute it and/or modify
  10. * it under the terms of the GNU General Public License as published by
  11. * the Free Software Foundation, either version 2 of the License, or
  12. * (at your option) any later version.
  13. *
  14. * Wolf3D Browser Source Code is distributed in the hope that it will be useful,
  15. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  16. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  17. * GNU General Public License for more details.
  18. *
  19. * You should have received a copy of the GNU General Public License version 2
  20. * along with Wolf3D Browser Source Code. If not, see <http://www.gnu.org/licenses/>.
  21. *
  22. * 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.
  23. *
  24. * ===========================================================================
  25. */
  26. /**
  27. * @namespace
  28. * @description Artificial intelligence
  29. */
  30. Wolf.ActorAI = (function() {
  31. var dsounds = [
  32. "sfx/025.wav",
  33. "sfx/026.wav",
  34. "sfx/086.wav",
  35. "sfx/088.wav",
  36. "sfx/105.wav",
  37. "sfx/107.wav",
  38. "sfx/109.wav"
  39. ];
  40. /**
  41. * @description Initiate death scream sound effect.
  42. * @memberOf Wolf.ActorAI
  43. * @param {object} self The enemy actor object.
  44. * @param {object} game The game object.
  45. */
  46. function deathScream(self, game) {
  47. var pos = game.player.position;
  48. switch (self.type) {
  49. case Wolf.en_mutant:
  50. Wolf.Sound.startSound(pos, self, 1, Wolf.CHAN_VOICE, "sfx/037.wav", 1, Wolf.ATTN_NORM, 0);
  51. break;
  52. case Wolf.en_guard:
  53. Wolf.Sound.startSound(pos, self, 1, Wolf.CHAN_VOICE, dsounds[Wolf.Random.rnd() % 6], 1, Wolf.ATTN_NORM, 0);
  54. break;
  55. case Wolf.en_officer:
  56. Wolf.Sound.startSound(pos, self, 1, Wolf.CHAN_VOICE, "sfx/074.wav", 1, Wolf.ATTN_NORM, 0);
  57. break;
  58. case Wolf.en_ss:
  59. Wolf.Sound.startSound(pos, self, 1, Wolf.CHAN_VOICE, "sfx/046.wav", 1, Wolf.ATTN_NORM, 0);
  60. break;
  61. case Wolf.en_dog:
  62. Wolf.Sound.startSound(pos, self, 1, Wolf.CHAN_VOICE, "sfx/035.wav", 1, Wolf.ATTN_NORM, 0);
  63. break;
  64. case Wolf.en_boss:
  65. Wolf.Sound.startSound(pos, self, 1, Wolf.CHAN_VOICE, "sfx/019.wav", 1, Wolf.ATTN_NORM, 0);
  66. break;
  67. case Wolf.en_schabbs:
  68. Wolf.Sound.startSound(pos, self, 1, Wolf.CHAN_VOICE, "sfx/061.wav", 1, Wolf.ATTN_NORM, 0);
  69. break;
  70. case Wolf.en_fake:
  71. Wolf.Sound.startSound(pos, self, 1, Wolf.CHAN_VOICE, "sfx/069.wav", 1, Wolf.ATTN_NORM, 0);
  72. break;
  73. case Wolf.en_mecha:
  74. Wolf.Sound.startSound(pos, self, 1, Wolf.CHAN_VOICE, "sfx/084.wav", 1, Wolf.ATTN_NORM, 0);
  75. break;
  76. case Wolf.en_hitler:
  77. Wolf.Sound.startSound(pos, self, 1, Wolf.CHAN_VOICE, "sfx/044.wav", 1, Wolf.ATTN_NORM, 0);
  78. break;
  79. case Wolf.en_gretel:
  80. Wolf.Sound.startSound(pos, self, 1, Wolf.CHAN_VOICE, "sfx/115.wav", 1, Wolf.ATTN_NORM, 0);
  81. break;
  82. case Wolf.en_gift:
  83. Wolf.Sound.startSound(pos, self, 1, Wolf.CHAN_VOICE, "sfx/091.wav", 1, Wolf.ATTN_NORM, 0);
  84. break;
  85. case Wolf.en_fat:
  86. Wolf.Sound.startSound(pos, self, 1, Wolf.CHAN_VOICE, "sfx/119.wav", 1, Wolf.ATTN_NORM, 0);
  87. break;
  88. }
  89. }
  90. /* Hitler */
  91. /**
  92. * @description Play Mecha sound.
  93. * @memberOf Wolf.ActorAI
  94. * @param {object} self The enemy actor object.
  95. * @param {object} game The game object.
  96. */
  97. function mechaSound(self, game) {
  98. if (game.level.state.areabyplayer[self.areanumber]) {
  99. Wolf.Sound.startSound(game.player.position, self, 1, Wolf.CHAN_VOICE, "sfx/080.wav", 1, Wolf.ATTN_NORM, 0);
  100. }
  101. }
  102. /**
  103. * @description Play Slurpie sound.
  104. * @memberOf Wolf.ActorAI
  105. * @param {object} self The enemy actor object.
  106. */
  107. function slurpie(self, game) {
  108. Wolf.Sound.startSound(game.player.position, self, 1, Wolf.CHAN_VOICE, "lsfx/061.wav", 1, Wolf.ATTN_NORM, 0);
  109. }
  110. /**
  111. * @description Spawn new actor, when Mecha Hitler is dead.
  112. * @memberOf Wolf.ActorAI
  113. * @param {object} self The enemy actor object.
  114. * @param {object} game The game object.
  115. */
  116. function hitlerMorph(self, game) {
  117. var hitpoints = [500, 700, 800, 900],
  118. level = game.level,
  119. hitler;
  120. hitler = Wolf.Actors.getNewActor(level);
  121. if(!hitler) {
  122. return;
  123. }
  124. hitler.x = self.x;
  125. hitler.y = self.y;
  126. hitler.distance = self.distance;
  127. hitler.tile.x = self.tile.x;
  128. hitler.tile.y = self.tile.y;
  129. hitler.angle = self.angle;
  130. hitler.dir = self.dir;
  131. hitler.health = hitpoints[game.skill];
  132. hitler.areanumber = self.areanumber;
  133. hitler.state = Wolf.st_chase1;
  134. hitler.type = Wolf.en_hitler;
  135. hitler.speed = Wolf.SPDPATROL * 5;
  136. hitler.ticcount = 0;
  137. hitler.flags = self.flags | Wolf.FL_SHOOTABLE;
  138. hitler.sprite = Wolf.Sprites.getNewSprite(level);
  139. }
  140. /* Angel of Death */
  141. var angel_temp = 0;
  142. /**
  143. * @description Play Angel of Death Breathing sound.
  144. * @memberOf Wolf.ActorAI
  145. * @param {object} self The enemy actor object.
  146. */
  147. function breathing(self) {
  148. Wolf.Sound.startSound(null, null, 1, Wolf.CHAN_VOICE, "lsfx/080.wav", 1, Wolf.ATTN_NORM, 0);
  149. }
  150. /**
  151. * @description Reset Angel of Death attack counter
  152. * @memberOf Wolf.ActorAI
  153. * @param {object} self The enemy actor object.
  154. */
  155. function startAttack(self) {
  156. angel_temp = 0;
  157. }
  158. /**
  159. * @description Angel of Death AI.
  160. * @memberOf Wolf.ActorAI
  161. * @param {object} self The enemy actor object.
  162. */
  163. function relaunch(self) {
  164. if (++angel_temp == 3) {
  165. Wolf.Actors.stateChange(self, Wolf.st_pain);
  166. return;
  167. }
  168. if (Wolf.Random.rnd() & 1) {
  169. Wolf.Actors.stateChange(self, Wolf.st_chase1);
  170. return;
  171. }
  172. }
  173. /**
  174. * @description Victory - start intermission.
  175. * @memberOf Wolf.ActorAI
  176. */
  177. function victory(game) {
  178. Wolf.Game.startIntermission(game);
  179. }
  180. /**
  181. * @description Entity is dormant state.
  182. * @memberOf Wolf.ActorAI
  183. * @param {object} self The enemy actor object.
  184. * @param {object} game The game object.
  185. */
  186. function dormant(self, game) {
  187. var level = game.level,
  188. player = game.player,
  189. deltax,
  190. deltay,
  191. xl, xh, yl, yh,
  192. x, y, n,
  193. moveok = false;
  194. deltax = self.x - player.position.x;
  195. deltay = self.y - player.position.y;
  196. if (deltax < -Wolf.MINACTORDIST || deltax > Wolf.MINACTORDIST) {
  197. moveok = true;
  198. } else if(deltay < -Wolf.MINACTORDIST || deltay > Wolf.MINACTORDIST) {
  199. moveok = true;
  200. }
  201. if (!moveok) {
  202. return;
  203. }
  204. // moveok:
  205. xl = (self.x - Wolf.MINDIST) >> Wolf.TILESHIFT;
  206. xh = (self.x + Wolf.MINDIST) >> Wolf.TILESHIFT;
  207. yl = (self.y - Wolf.MINDIST) >> Wolf.TILESHIFT;
  208. yh = (self.y + Wolf.MINDIST) >> Wolf.TILESHIFT;
  209. for(y = yl; y <= yh ; ++y ) {
  210. for(x = xl;x <= xh;++x) {
  211. if (level.tileMap[x][y] & Wolf.SOLID_TILE) {
  212. return;
  213. }
  214. for (n=0;n<level.state.numGuards;++n) {
  215. if (level.state.guards[n].state >= Wolf.st_die1) {
  216. continue;
  217. }
  218. if (level.state.guards[n].tile.x == x && level.state.guards[n].tile.y == y) {
  219. return; // another guard in path
  220. }
  221. }
  222. }
  223. }
  224. self.flags |= Wolf.FL_AMBUSH | Wolf.FL_SHOOTABLE;
  225. self.flags &= ~Wolf.FL_ATTACKMODE;
  226. self.dir = Wolf.Math.dir8_nodir;
  227. Wolf.Actors.stateChange(self, Wolf.st_path1);
  228. }
  229. /**
  230. * @description Death cam animation.
  231. * Tthe DeathCam feature isn't implimented, but we want to
  232. * give the animation time to play before declaring victory.
  233. * @memberOf Wolf.ActorAI
  234. * @param {object} self The enemy actor object.
  235. * @param {object} game The game object.
  236. */
  237. function startDeathCam(game, self) {
  238. self.playstate = Wolf.ex_complete;
  239. setTimeout(function() {
  240. Wolf.Game.startIntermission(game);
  241. }, 5000);
  242. }
  243. /**
  244. * @description Rockets emmit smoke.
  245. * @memberOf Wolf.ActorAI
  246. * @param {object} self The enemy actor object.
  247. * @param {object} level The level object.
  248. */
  249. function smoke(self, game) {
  250. var level = game.level,
  251. smokeEnt = Wolf.Actors.getNewActor(level);
  252. if (!smokeEnt) {
  253. return;
  254. }
  255. smokeEnt.x = self.x;
  256. smokeEnt.y = self.y;
  257. smokeEnt.tile.x = self.tile.x;
  258. smokeEnt.tile.y = self.tile.y;
  259. smokeEnt.state = Wolf.st_die1;
  260. smokeEnt.type = (self.type == Wolf.en_hrocket) ? Wolf.en_hsmoke : Wolf.en_smoke;
  261. smokeEnt.ticcount = 6;
  262. smokeEnt.flags = Wolf.FL_NEVERMARK;
  263. smokeEnt.sprite = Wolf.Sprites.getNewSprite(level);
  264. }
  265. /**
  266. * @description Puts an actor into attack mode and possibly reverses the direction if the player is behind it.
  267. * @memberOf Wolf.ActorAI
  268. * @param {object} self The enemy actor object.
  269. */
  270. function firstSighting(self, game) {
  271. switch (self.type) {
  272. case Wolf.en_guard:
  273. Wolf.Sound.startSound(game.player.position, self, 1, Wolf.CHAN_VOICE, "sfx/001.wav", 1, Wolf.ATTN_NORM, 0);
  274. self.speed *= 3; // go faster when chasing player
  275. break;
  276. case Wolf.en_officer:
  277. Wolf.Sound.startSound(game.player.position, self, 1, Wolf.CHAN_VOICE, "sfx/071.wav", 1, Wolf.ATTN_NORM, 0);
  278. self.speed *= 5; // go faster when chasing player
  279. break;
  280. case Wolf.en_mutant:
  281. self.speed *= 3; // go faster when chasing player
  282. break;
  283. case Wolf.en_ss:
  284. Wolf.Sound.startSound(game.player.position, self, 1, Wolf.CHAN_VOICE, "sfx/015.wav", 1, Wolf.ATTN_NORM, 0);
  285. self.speed *= 4; // go faster when chasing player
  286. break;
  287. case Wolf.en_dog:
  288. Wolf.Sound.startSound(game.player.position, self, 1, Wolf.CHAN_VOICE, "sfx/002.wav", 1, Wolf.ATTN_NORM, 0);
  289. self.speed *= 2; // go faster when chasing player
  290. break;
  291. case Wolf.en_boss:
  292. Wolf.Sound.startSound(game.player.position, self, 1, Wolf.CHAN_VOICE, "sfx/017.wav", 1, Wolf.ATTN_NORM, 0);
  293. self.speed = Wolf.SPDPATROL * 3; // go faster when chasing player
  294. break;
  295. case Wolf.en_gretel:
  296. Wolf.Sound.startSound(game.player.position, self, 1, Wolf.CHAN_VOICE, "sfx/112.wav", 1, Wolf.ATTN_NORM, 0);
  297. self.speed *= 3; // go faster when chasing player
  298. break;
  299. case Wolf.en_gift:
  300. Wolf.Sound.startSound(game.player.position, self, 1, Wolf.CHAN_VOICE, "sfx/096.wav", 1, Wolf.ATTN_NORM, 0);
  301. self.speed *= 3; // go faster when chasing player
  302. break;
  303. case Wolf.en_fat:
  304. Wolf.Sound.startSound(game.player.position, self, 1, Wolf.CHAN_VOICE, "sfx/102.wav", 1, Wolf.ATTN_NORM, 0);
  305. self.speed *= 3; // go faster when chasing player
  306. break;
  307. case Wolf.en_schabbs:
  308. Wolf.Sound.startSound(game.player.position, self, 1, Wolf.CHAN_VOICE, "sfx/065.wav", 1, Wolf.ATTN_NORM, 0);
  309. self.speed *= 3; // go faster when chasing player
  310. break;
  311. case Wolf.en_fake:
  312. Wolf.Sound.startSound(game.player.position, self, 1, Wolf.CHAN_VOICE, "sfx/054.wav", 1, Wolf.ATTN_NORM, 0);
  313. self.speed *= 3; // go faster when chasing player
  314. break;
  315. case Wolf.en_mecha:
  316. Wolf.Sound.startSound(game.player.position, self, 1, Wolf.CHAN_VOICE, "sfx/040.wav", 1, Wolf.ATTN_NORM, 0);
  317. self.speed *= 3; // go faster when chasing player
  318. break;
  319. case Wolf.en_hitler:
  320. Wolf.Sound.startSound(game.player.position, self, 1, Wolf.CHAN_VOICE, "sfx/040.wav", 1, Wolf.ATTN_NORM, 0);
  321. self.speed *= 5; // go faster when chasing player
  322. break;
  323. case Wolf.en_blinky:
  324. case Wolf.en_clyde:
  325. case Wolf.en_pinky:
  326. case Wolf.en_inky:
  327. self.speed *= 2; // go faster when chasing player
  328. break;
  329. default:
  330. return;
  331. }
  332. Wolf.Actors.stateChange(self, Wolf.st_chase1);
  333. if (self.waitfordoorx) {
  334. self.waitfordoorx = self.waitfordoory = 0; // ignore the door opening command
  335. }
  336. self.dir = Wolf.Math.dir8_nodir;
  337. self.flags |= Wolf.FL_ATTACKMODE | Wolf.FL_FIRSTATTACK;
  338. }
  339. /**
  340. * @description Called when the player succesfully hits an enemy.
  341. * Does damage points to enemy ob, either putting it into a stun frame or killing it.
  342. * @memberOf Wolf.ActorAI
  343. * @param {object} self The enemy actor object.
  344. * @param {object} game The game object.
  345. * @param {object} player The player object.
  346. * @param {number} damage The number of damage points.
  347. */
  348. function damageActor(self, game, player, damage) {
  349. player.madenoise = 1;
  350. // do double damage if shooting a non attack mode actor
  351. if (!(self.flags & Wolf.FL_ATTACKMODE)) {
  352. damage <<= 1;
  353. }
  354. self.health -= damage;
  355. if (self.health <= 0) {
  356. killActor(self, game, player);
  357. } else {
  358. if (!(self.flags & Wolf.FL_ATTACKMODE) ) {
  359. firstSighting(self, game); // put into combat mode
  360. }
  361. switch (self.type) { // dogs only have one hit point
  362. case Wolf.en_guard:
  363. case Wolf.en_officer:
  364. case Wolf.en_mutant:
  365. case Wolf.en_ss:
  366. if (self.health & 1) {
  367. Wolf.Actors.stateChange(self, Wolf.st_pain);
  368. } else {
  369. Wolf.Actors.stateChange(self, Wolf.st_pain1);
  370. }
  371. break;
  372. }
  373. }
  374. }
  375. /**
  376. * @description Actor has been killed, so give points and spawn powerups.
  377. * @memberOf Wolf.ActorAI
  378. * @param {object} self The enemy actor object.
  379. * @param {object} game The game object.
  380. * @param {object} player The player object.
  381. */
  382. function killActor(self, game, player) {
  383. var level = game.level,
  384. tilex = self.tile.x = self.x >> Wolf.TILESHIFT; // drop item on center,
  385. tiley = self.tile.y = self.y >> Wolf.TILESHIFT;
  386. switch (self.type) {
  387. case Wolf.en_guard:
  388. Wolf.Player.givePoints(player, 100);
  389. Wolf.Powerups.spawn(level, tilex, tiley, Wolf.pow_clip2);
  390. break;
  391. case Wolf.en_officer:
  392. Wolf.Player.givePoints(player, 400);
  393. Wolf.Powerups.spawn(level, tilex, tiley, Wolf.pow_clip2);
  394. break;
  395. case Wolf.en_mutant:
  396. Wolf.Player.givePoints(player, 700);
  397. Wolf.Powerups.spawn(level, tilex, tiley, Wolf.pow_clip2);
  398. break;
  399. case Wolf.en_ss:
  400. Wolf.Player.givePoints(player, 500);
  401. if (player.items & Wolf.ITEM_WEAPON_3) { // have a schmeiser?
  402. Wolf.Powerups.spawn(level, tilex, tiley, Wolf.pow_clip2);
  403. } else {
  404. Wolf.Powerups.spawn(level, tilex, tiley, Wolf.pow_machinegun);
  405. }
  406. break;
  407. case Wolf.en_dog:
  408. Wolf.Player.givePoints(player, 200);
  409. break;
  410. case Wolf.en_boss:
  411. Wolf.Player.givePoints(player, 5000);
  412. Wolf.Powerups.spawn(level, tilex, tiley, Wolf.pow_key1);
  413. break;
  414. case Wolf.en_gretel:
  415. Wolf.Player.givePoints(player, 5000);
  416. Wolf.Powerups.spawn(level, tilex, tiley, Wolf.pow_key1);
  417. break;
  418. case Wolf.en_gift:
  419. Wolf.Player.givePoints(player, 5000);
  420. startDeathCam(game, self);
  421. break;
  422. case Wolf.en_fat:
  423. Wolf.Player.givePoints(player, 5000);
  424. startDeathCam(game, self);
  425. break;
  426. case Wolf.en_schabbs:
  427. Wolf.Player.givePoints(player, 5000);
  428. deathScream(self, game);
  429. startDeathCam(game, self);
  430. break;
  431. case Wolf.en_fake:
  432. Wolf.Player.givePoints(player, 2000);
  433. break;
  434. case Wolf.en_mecha:
  435. Wolf.Player.givePoints(player, 5000);
  436. break;
  437. case Wolf.en_hitler:
  438. Wolf.Player.givePoints(player, 5000);
  439. deathScream(self, game);
  440. startDeathCam(game, self);
  441. break;
  442. }
  443. Wolf.Actors.stateChange(self, Wolf.st_die1);
  444. if (++level.state.killedMonsters == level.state.totalMonsters) {
  445. Wolf.Game.notify("You killed the last enemy!");
  446. }
  447. self.flags &= ~Wolf.FL_SHOOTABLE;
  448. self.flags |= Wolf.FL_NONMARK;
  449. }
  450. return {
  451. firstSighting : firstSighting,
  452. damageActor : damageActor,
  453. killActor : killActor,
  454. deathScream : deathScream,
  455. mechaSound : mechaSound,
  456. slurpie : slurpie,
  457. hitlerMorph : hitlerMorph,
  458. breathing : breathing,
  459. startAttack : startAttack,
  460. relaunch : relaunch,
  461. victory : victory,
  462. dormant : dormant,
  463. startDeathCam : startDeathCam,
  464. smoke : smoke
  465. };
  466. })();