actors.js 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519
  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 Actors
  29. */
  30. Wolf.Actors = (function() {
  31. Wolf.setConsts({
  32. SPDPATROL : 512,
  33. SPDDOG : 1500,
  34. FL_SHOOTABLE : 1,
  35. FL_BONUS : 2,
  36. FL_NEVERMARK : 4,
  37. FL_VISABLE : 8,
  38. FL_ATTACKMODE : 16,
  39. FL_FIRSTATTACK : 32,
  40. FL_AMBUSH : 64,
  41. FL_NONMARK : 128,
  42. MAX_GUARDS : 255,
  43. NUMENEMIES : 31,
  44. NUMSTATES : 34,
  45. MINACTORDIST : 0x10000
  46. });
  47. Wolf.setConsts({
  48. en_guard : 0,
  49. en_officer : 1,
  50. en_ss : 2,
  51. en_dog : 3,
  52. en_boss : 4,
  53. en_schabbs : 5,
  54. en_fake : 6,
  55. en_mecha : 7,
  56. en_hitler : 8,
  57. en_mutant : 9,
  58. en_blinky : 10,
  59. en_clyde : 11,
  60. en_pinky : 12,
  61. en_inky : 13,
  62. en_gretel : 14,
  63. en_gift : 15,
  64. en_fat : 16,
  65. // --- Projectiles
  66. en_needle : 17,
  67. en_fire : 18,
  68. en_rocket : 19,
  69. en_smoke : 20,
  70. en_bj : 21,
  71. // --- Spear of destiny!
  72. en_spark : 22,
  73. en_hrocket : 23,
  74. en_hsmoke : 24,
  75. en_spectre : 25,
  76. en_angel : 26,
  77. en_trans : 27,
  78. en_uber : 28,
  79. en_will : 29,
  80. en_death : 30
  81. });
  82. Wolf.setConsts({
  83. st_stand : 0,
  84. st_path1 : 1,
  85. st_path1s : 2,
  86. st_path2 : 3,
  87. st_path3 : 4,
  88. st_path3s : 5,
  89. st_path4 : 6,
  90. st_pain : 7,
  91. st_pain1 : 8,
  92. st_shoot1 : 9,
  93. st_shoot2 : 10,
  94. st_shoot3 : 11,
  95. st_shoot4 : 12,
  96. st_shoot5 : 13,
  97. st_shoot6 : 14,
  98. st_shoot7 : 15,
  99. st_shoot8 : 16,
  100. st_shoot9 : 17,
  101. st_chase1 : 18,
  102. st_chase1s : 19,
  103. st_chase2 : 20,
  104. st_chase3 : 21,
  105. st_chase3s : 22,
  106. st_chase4 : 23,
  107. st_die1 : 24,
  108. st_die2 : 25,
  109. st_die3 : 26,
  110. st_die4 : 27,
  111. st_die5 : 28,
  112. st_die6 : 29,
  113. st_die7 : 30,
  114. st_die8 : 31,
  115. st_die9 : 32,
  116. st_dead : 33,
  117. st_remove : 34
  118. });
  119. var add8dir = [4, 5, 6, 7, 0, 1, 2, 3, 0],
  120. r_add8dir = [4, 7, 6, 5, 0, 1, 2, 3, 0];
  121. /**
  122. * @description Create new actor.
  123. * @memberOf Wolf.Actors
  124. * @param {object} level The level object.
  125. * @returns {object} The new actor object.
  126. */
  127. function getNewActor(level) {
  128. if (level.state.numGuards > Wolf.MAX_GUARDS) {
  129. return null;
  130. }
  131. var actor = {
  132. x : 0,
  133. y : 0,
  134. angle : 0,
  135. type : 0,
  136. health : 0,
  137. max_health : 0,
  138. speed : 0,
  139. ticcount : 0,
  140. temp2 : 0,
  141. distance : 0,
  142. tile : {
  143. x : 0,
  144. y : 0
  145. },
  146. areanumber : 0,
  147. waitfordoorx : 0,
  148. waitfordoory : 0, // waiting on this door if non 0
  149. flags : 0, // FL_SHOOTABLE, etc
  150. state : 0,
  151. dir : 0,
  152. sprite : 0
  153. };
  154. level.state.guards[level.state.numGuards++] = actor;
  155. return actor;
  156. }
  157. /**
  158. * @description Process a single actor.
  159. * @private
  160. * @param {object} ent The actor object.
  161. * @param {object} level The level object.
  162. * @param {object} player The player object.
  163. * @param {number} tics The number of tics.
  164. * @returns {boolean} False if actor should be removed, otherwise true.
  165. */
  166. function doGuard(ent, game, tics) { // FIXME: revise!
  167. var think;
  168. //assert( ent->tilex >= 0 && ent->tilex < 64 );
  169. //assert( ent->tiley >= 0 && ent->tiley < 64 );
  170. //assert( ent->dir >= 0 && ent->dir <= 8 );
  171. // ticcounts fire discrete actions separate from think functions
  172. if (ent.ticcount) {
  173. ent.ticcount -= tics;
  174. while (ent.ticcount <= 0) {
  175. //assert( ent->type >= 0 && ent->type < NUMENEMIES );
  176. //assert( ent->state >= 0 && ent->state < NUMSTATES );
  177. think = Wolf.objstate[ent.type][ent.state].action; // end of state action
  178. if (think) {
  179. think(ent, game, tics);
  180. if (ent.state == Wolf.st_remove) {
  181. return false;
  182. }
  183. }
  184. ent.state = Wolf.objstate[ent.type][ent.state].next_state;
  185. if (ent.state == Wolf.st_remove) {
  186. return false;
  187. }
  188. if (!Wolf.objstate[ent.type][ent.state].timeout) {
  189. ent.ticcount = 0;
  190. break;
  191. }
  192. ent.ticcount += Wolf.objstate[ent.type][ent.state].timeout;
  193. }
  194. }
  195. //
  196. // think
  197. //
  198. //assert( ent->type >= 0 && ent->type < NUMENEMIES );
  199. //assert( ent->state >= 0 && ent->state < NUMSTATES );
  200. think = Wolf.objstate[ent.type][ent.state].think;
  201. if (think) {
  202. think(ent, game, tics);
  203. if (ent.state == Wolf.st_remove) {
  204. return false;
  205. }
  206. }
  207. return true;
  208. }
  209. /**
  210. * @description Changes guard's state to that defined in newState.
  211. * @memberOf Wolf.Actors
  212. * @param {object} ent The actor object.
  213. * @param {number} newState The new state.
  214. */
  215. function stateChange(ent, newState) {
  216. ent.state = newState;
  217. // assert( ent->type >= 0 && ent->type < NUMENEMIES );
  218. if (newState == Wolf.st_remove) {
  219. ent.ticcount = 0;
  220. } else {
  221. // assert( ent->state >= 0 && ent->state < NUMSTATES );
  222. ent.ticcount = Wolf.objstate[ent.type][ent.state].timeout; //0;
  223. }
  224. }
  225. /**
  226. * @description Process all the enemy actors.
  227. * @memberOf Wolf.Actors
  228. * @param {object} level The level object.
  229. * @param {object} player The player object.
  230. * @param {number} tics The number of tics.
  231. */
  232. function process(game, tics) {
  233. var level = game.level,
  234. player = game.player,
  235. n, tex, guard,
  236. liveGuards = [];
  237. for (n = 0 ; n < level.state.numGuards ; ++n ) {
  238. guard = level.state.guards[n];
  239. if (!doGuard(guard, game, tics)) {
  240. // remove guard from the game forever!
  241. // remove(game, guards[n--]);
  242. Wolf.Sprites.remove(level, guard.sprite);
  243. level.state.guards[n] = null;
  244. continue;
  245. }
  246. Wolf.Sprites.setPos(level, guard.sprite, guard.x, guard.y, guard.angle);
  247. tex = Wolf.objstate[guard.type][guard.state].texture;
  248. if (Wolf.objstate[guard.type][guard.state].rotate) {
  249. if (guard.type == Wolf.en_rocket || guard.type == Wolf.en_hrocket) {
  250. tex += r_add8dir[Wolf.Math.get8dir( Wolf.Angle.distCW(Wolf.FINE2RAD(player.angle), Wolf.FINE2RAD(guard.angle)))];
  251. } else {
  252. tex += add8dir[Wolf.Math.get8dir( Wolf.Angle.distCW(Wolf.FINE2RAD(player.angle), Wolf.FINE2RAD(guard.angle)))];
  253. }
  254. }
  255. Wolf.Sprites.setTex(level, guard.sprite, 0, tex);
  256. }
  257. for (n = 0 ; n < level.state.numGuards ; ++n ) {
  258. if (level.state.guards[n]) {
  259. liveGuards.push(level.state.guards[n]);
  260. }
  261. }
  262. level.state.guards = liveGuards;
  263. level.state.numGuards = liveGuards.length;
  264. }
  265. /**
  266. * @description Reset and clear the enemy actors in the level.
  267. * @memberOf Wolf.Actors
  268. * @param {object} level The level object.
  269. */
  270. function resetGuards(level) {
  271. level.state.guards = [];
  272. level.state.numGuards = 0;
  273. //New = NULL;
  274. }
  275. /**
  276. * @description Spawn a new enemy actor at the given position.
  277. * @memberOf Wolf.Actors
  278. * @param {object} level The level object.
  279. * @param {number} skill The difficulty level.
  280. * @param {number} which The actor type.
  281. * @param {number} x The x position.
  282. * @param {number} y The y position.
  283. * @param {number} dir The direction.
  284. * @returns {object} The new actor object or null if actor creation failed.
  285. */
  286. function spawn(level, skill, which, x, y, dir) {
  287. var ent = getNewActor(level);
  288. if (!ent) {
  289. return null;
  290. }
  291. ent.x = Wolf.TILE2POS(x);
  292. ent.y = Wolf.TILE2POS(y);
  293. ent.tile.x = x;
  294. ent.tile.y = y;
  295. // assert( dir >= 0 && dir <= 4 );
  296. ent.angle = Wolf.Math.dir4angle[dir];
  297. ent.dir = Wolf.Math.dir4to8[dir];
  298. ent.areanumber = level.areas[x][y];
  299. if (ent.areanumber < 0) {
  300. // ambush marker tiles are listed as -3 area
  301. ent.areanumber = 0;
  302. }
  303. // assert( ent->areanumber >= 0 && ent->areanumber < NUMAREAS );
  304. ent.type = which;
  305. ent.health = Wolf.starthitpoints[skill][which];
  306. ent.sprite = Wolf.Sprites.getNewSprite(level);
  307. return ent;
  308. }
  309. /**
  310. * @description Spawn a dead guard.
  311. * @memberOf Wolf.Actors
  312. * @param {object} level The level object.
  313. * @param {number} skill The difficulty level.
  314. * @param {number} which The actor type.
  315. * @param {number} x The x position.
  316. * @param {number} y The y position.
  317. */
  318. function spawnDeadGuard(level, skill, which, x, y) {
  319. var self = spawn(level, skill, which, x, y, Wolf.Math.dir4_nodir);
  320. if (!self) {
  321. return;
  322. }
  323. self.state = Wolf.st_dead;
  324. self.speed = 0;
  325. self.health = 0;
  326. self.ticcount = Wolf.objstate[which][Wolf.st_dead].timeout ? Wolf.Random.rnd() % Wolf.objstate[which][Wolf.st_dead].timeout + 1 : 0;
  327. }
  328. /**
  329. * @description Spawn a patrolling guard.
  330. * @memberOf Wolf.Actors
  331. * @param {object} level The level object.
  332. * @param {number} skill The difficulty level.
  333. * @param {number} which The actor type.
  334. * @param {number} x The x position.
  335. * @param {number} y The y position.
  336. */
  337. function spawnPatrol(level, skill, which, x, y, dir) {
  338. var self = spawn(level, skill, which, x, y, dir);
  339. if (!self) {
  340. return;
  341. }
  342. self.state = Wolf.st_path1;
  343. self.speed = (which == Wolf.en_dog) ? Wolf.SPDDOG : Wolf.SPDPATROL;
  344. self.distance = Wolf.TILEGLOBAL;
  345. self.ticcount = Wolf.objstate[which][Wolf.st_path1].timeout ? Wolf.Random.rnd() % Wolf.objstate[which][Wolf.st_path1].timeout + 1 : 0;
  346. self.flags |= Wolf.FL_SHOOTABLE;
  347. level.state.totalMonsters++;
  348. }
  349. /**
  350. * @description Spawn a standing guard.
  351. * @memberOf Wolf.Actors
  352. * @param {object} level The level object.
  353. * @param {number} skill The difficulty level.
  354. * @param {number} which The actor type.
  355. * @param {number} x The x position.
  356. * @param {number} y The y position.
  357. */
  358. function spawnStand(level, skill, which, x, y, dir) {
  359. var self = spawn(level, skill, which, x, y, dir);
  360. if (!self) {
  361. return;
  362. }
  363. self.state = Wolf.st_stand;
  364. self.speed = Wolf.SPDPATROL;
  365. self.ticcount = Wolf.objstate[which][Wolf.st_stand].timeout ? Wolf.Random.rnd() % Wolf.objstate[which][Wolf.st_stand].timeout + 1 : 0;
  366. self.flags |= Wolf.FL_SHOOTABLE;
  367. if (level.tileMap[x][y] & Wolf.AMBUSH_TILE) {
  368. self.flags |= Wolf.FL_AMBUSH;
  369. }
  370. level.state.totalMonsters++;
  371. }
  372. function spawnBoss(level, skill, which, x, y) {
  373. var self,
  374. face;
  375. switch (which) {
  376. case Wolf.en_boss:
  377. case Wolf.en_schabbs:
  378. case Wolf.en_fat:
  379. case Wolf.en_hitler:
  380. face = Wolf.Math.dir4_south;
  381. break;
  382. case Wolf.en_fake:
  383. case Wolf.en_gretel:
  384. case Wolf.en_gift:
  385. face = Wolf.Math.dir4_north;
  386. break;
  387. case Wolf.en_trans:
  388. case Wolf.en_uber:
  389. case Wolf.en_will:
  390. case Wolf.en_death:
  391. case Wolf.en_angel:
  392. case Wolf.en_spectre:
  393. face = Wolf.Math.dir4_nodir;
  394. break;
  395. default:
  396. face = Wolf.Math.dir4_nodir;
  397. break;
  398. }
  399. self = spawn(level, skill, which, x, y, face);
  400. if (!self) {
  401. return;
  402. }
  403. self.state = which == Wolf.en_spectre ? Wolf.st_path1 : Wolf.st_stand;
  404. self.speed = Wolf.SPDPATROL;
  405. self.health = Wolf.starthitpoints[skill][which];
  406. self.ticcount = Wolf.objstate[which][Wolf.st_stand].timeout ? Wolf.Random.rnd() % Wolf.objstate[which][Wolf.st_stand].timeout + 1 : 0;
  407. self.flags |= Wolf.FL_SHOOTABLE | Wolf.FL_AMBUSH;
  408. level.state.totalMonsters++;
  409. }
  410. function spawnGhosts(level, skill, which, x, y) {
  411. var self = spawn(level, skill, which, x, y, Wolf.Math.dir4_nodir);
  412. if (!self) {
  413. return;
  414. }
  415. self.state = Wolf.st_chase1;
  416. self.speed = Wolf.SPDPATROL * 3;
  417. self.health = Wolf.starthitpoints[skill][which];
  418. self.ticcount = Wolf.objstate[which][Wolf.st_chase1].timeout ? Wolf.Random.rnd() % Wolf.objstate[which][Wolf.st_chase1].timeout + 1: 0;
  419. self.flags |= Wolf.FL_AMBUSH;
  420. level.state.totalMonsters++;
  421. }
  422. function spawnBJVictory(player, level, skill) {
  423. var x = Wolf.POS2TILE(player.position.x),
  424. y = Wolf.POS2TILE(player.position.y),
  425. bj = spawn(level, skill, Wolf.en_bj, x, y + 1, Wolf.Math.dir4_north);
  426. if (!bj) {
  427. return;
  428. }
  429. bj.x = player.position.x;
  430. bj.y = player.position.y;
  431. bj.state = Wolf.st_path1;
  432. bj.speed = Wolf.BJRUNSPEED;
  433. bj.flags = Wolf.FL_NONMARK; // FL_NEVERMARK;
  434. bj.temp2 = 6;
  435. bj.ticcount = 1;
  436. }
  437. return {
  438. process : process,
  439. resetGuards : resetGuards,
  440. getNewActor : getNewActor,
  441. spawn : spawn,
  442. spawnDeadGuard : spawnDeadGuard,
  443. spawnPatrol : spawnPatrol,
  444. spawnStand : spawnStand,
  445. spawnBoss : spawnBoss,
  446. spawnGhosts : spawnGhosts,
  447. spawnBJVictory : spawnBJVictory,
  448. stateChange : stateChange
  449. };
  450. })();