game.js 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466
  1. /*
  2. game coordinate system:
  3. +y
  4. |
  5. -x --+-- +x
  6. |
  7. -y
  8. */
  9. var Game = function(options) {
  10. var defaults = {
  11. maxEntities: 4*1024,
  12. curEntities: 0,
  13. nextIndex: 0,
  14. mapCenter: pt(128,128),
  15. runGame: true,
  16. gameState: 'loading',
  17. maxStep: .01,
  18. secondAcc: 0,
  19. gameTime: 0,
  20. input: {},
  21. inputCache: {
  22. pressed: {},
  23. down: {},
  24. clicked: null,
  25. },
  26. hudData: {
  27. cash: {
  28. pos: pt(35, 30),
  29. font: '24px Sans Serif',
  30. color: 'yellow',
  31. }
  32. },
  33. map: null,
  34. coll: null,
  35. entities: [],
  36. components: {
  37. },
  38. // quick lookup of entities of certain types
  39. classes: {
  40. players: [],
  41. npcs: [],
  42. activeRender: [],
  43. mapRender: [],
  44. stands: [],
  45. },
  46. systems: {
  47. },
  48. disabledSystems: {
  49. },
  50. aiPaths: new PathSet(),
  51. mapcounter: 0,
  52. player: null, // points to an entity
  53. };
  54. var e = $.extend({}, defaults, options);
  55. for(x in e) this[x] = e[x];
  56. // shorthand
  57. this.c = this.components;
  58. this.init();
  59. }
  60. Game.prototype.init = function() {
  61. // a little splint here. there's probably a fancier js way to do this. meh.
  62. var that = this;
  63. this.animFrame = function() {
  64. that.loop();
  65. };
  66. var d = new Date();
  67. this.lastframe = d.getTime();
  68. // input handlers
  69. $(document).keydown(function(e) {
  70. if(!that.runGame) return;
  71. that.inputCache.down[e.which] = 1;
  72. that.inputCache.down[e.which] |= 0;
  73. e.preventDefault();
  74. });
  75. $(document).keyup(function(e) {
  76. if(!that.runGame) return;
  77. that.inputCache.down[e.which] = 0;
  78. that.inputCache.pressed[e.which] = (that.inputCache.pressed[e.which]>>>0) + 1;
  79. e.preventDefault();
  80. });
  81. // need to fix this
  82. $(document).mousedown(function(e) {
  83. if(!that.runGame) return;
  84. /* that.inputCache.down[e.which] = 1;
  85. that.inputCache.down[e.which] |= 0;
  86. */ e.preventDefault();
  87. });
  88. $(document).mouseup(function(e) {
  89. if(!that.runGame) return;
  90. that.inputCache.clicked = that.map.documentToMap(e.pageX, e.pageY);
  91. e.preventDefault();
  92. });
  93. // need double clicks
  94. // need dragging
  95. // need hover
  96. // canvas for actors
  97. this.actorCanvas = $('#entity-canvas')[0];
  98. this.actorCtx = this.actorCanvas.getContext("2d");
  99. this.actorCtx.setTransform(1, 0, 0, 1, 0, 0);
  100. // canvas for HUD
  101. this.hudCanvas = $('#hud-canvas')[0];
  102. this.hudCtx = this.hudCanvas.getContext("2d");
  103. this.hudCtx.setTransform(1, 0, 0, 1, 0, 0);
  104. // set up the map
  105. this.map = new Map({
  106. canvasSelector: '#tex-canvas',
  107. showDebugLabels: true,
  108. });
  109. this.grid = new GridSP();
  110. this.systems = Systems;
  111. // add some content...
  112. /*
  113. var b = this.aibtree = new BehaviorTree();
  114. b.addRoot({type: 'priority');
  115. */
  116. console.log('game init completed');
  117. // eh...
  118. //this.loop();
  119. };
  120. Game.prototype.loop = function() {
  121. //console.log('looping');
  122. this.updateInput();
  123. // inludes timers
  124. this.updateGame();
  125. this.render();
  126. if(this.runGame) window.requestAnimFrame(this.animFrame);
  127. };
  128. Game.prototype.loadLevel = function() {
  129. // generate the map
  130. MapGen_1(this.map, this);
  131. this.spawn(Entities.susie);
  132. var stand = this.spawn(Entities.stand);
  133. this.grid.addObj(stand);
  134. this.spawnCustomer(pt(129,126));
  135. this.spawnCustomer(pt(119,126));
  136. this.spawn(Entities.baron);
  137. };
  138. Game.prototype.render = function() {
  139. // texture canvas
  140. this.map.render(this.mapCenter);
  141. // entity canvas
  142. this.actorCtx.clearRect(0, 0, this.actorCanvas.width, this.actorCanvas.height);
  143. this.systems.drawActors(
  144. this.actorCtx,
  145. { width: this.actorCanvas.width, height: this.actorCanvas.height },
  146. this.mapCenter
  147. );
  148. this.systems.hud.money();
  149. };
  150. Game.prototype.updateInput = function() {
  151. var curinput = {
  152. down: this.inputCache.down,
  153. pressed: this.inputCache.pressed,
  154. clicked: this.inputCache.clicked,
  155. };
  156. this.inputCache.pressed = {};
  157. this.inputCache.clicked = null;
  158. this.input = curinput;
  159. };
  160. // broken....
  161. Game.prototype.updateGame = function(te) {
  162. // timeless stuff
  163. this.systems.userControl();
  164. // update the time
  165. var d = new Date();
  166. var now = d.getTime();
  167. // time elapsed since last frame, in seconds
  168. var te = (now - this.lastframe) / 1000.0;
  169. this.lastframe = now; // put this at the end
  170. this.secondAcc += te;
  171. var steps = Math.floor(te / this.maxStep);
  172. var leftover = te - (steps * this.maxStep);
  173. // nice little physics steps
  174. for(var i = 0; i < steps; i++) {
  175. this.gameTime += this.maxStep;
  176. this.step(this.maxStep);
  177. }
  178. this.gameTime += leftover;
  179. this.step(leftover);
  180. // once per frame
  181. this.frameStep(te);
  182. // ~once per second
  183. if(this.secondAcc > 1) {
  184. this.secondStep(this.secondAcc);
  185. this.secondAcc = 0;
  186. };
  187. return te;
  188. }
  189. // this is for things that need reasonable integration like physics
  190. Game.prototype.step = function(te) {
  191. //this.systems.move(te);
  192. this.systems.goTo();
  193. this.systems.acceleration(te);
  194. this.systems.velocity(te);
  195. this.systems.urge(te);
  196. this.systems.checkCollisions();
  197. // the odometer must be called immediately before movement finalization
  198. this.systems.odometer();
  199. this.systems.finalizeMove();
  200. };
  201. // this is for things that need per-frame updates, but can handle large steps like HUD
  202. Game.prototype.frameStep = function(te) {
  203. this.systems.mapFollows();
  204. this.systems.lookAt();
  205. };
  206. // called ~ once every second. useful for game logic, etc
  207. Game.prototype.secondStep = function(te) {
  208. this.systems.ai.thirst();
  209. this.systems.ai.thirsty_LocateStand();
  210. this.systems.ai.buyDrink();
  211. this.systems.ai.followPaths();
  212. };
  213. Game.prototype.addEntity = function() {
  214. var i = this.nextIndex++;
  215. return i;
  216. };
  217. Game.prototype.firstEntityWith = function(comp) {
  218. var c = this.components[comp];
  219. if(!c) return null;
  220. for(var eid in c) return eid;
  221. };
  222. Game.prototype.getAllComponents = function(eid) {
  223. var l = [];
  224. for(var cn in this.components) { if(this.components.hasOwnProperty(cn)) {
  225. var cmp = this.components[cn][eid]
  226. if(cmp) {
  227. l.push({type: cn, data: cmp});
  228. }
  229. }}
  230. return l;
  231. };
  232. // checks existence first
  233. Game.prototype.addComponent = function(eid, name, data) {
  234. if(!this.components[name])
  235. this.components[name] = {};
  236. this.components[name][eid] = data;
  237. };
  238. // doesn't check existence
  239. Game.prototype.setComp = function(eid, name, data) {
  240. this.components[name][eid] = data;
  241. };
  242. Game.prototype.getComp = function(eid, name) {
  243. return this.components[name] ? this.components[name][eid] : null;
  244. };
  245. Game.prototype.removeComp = function(eid, name) {
  246. delete this.components[name][eid];
  247. };
  248. Game.prototype.spawn = function(entity) {
  249. var eid = this.addEntity();
  250. for(var prop in entity) { if(entity.hasOwnProperty(prop)) {
  251. var data = entity[prop];
  252. // fuck references. real languages use pointers.
  253. if(typeof data == 'object')
  254. data = $.extend({}, data);
  255. this.addComponent(eid, prop, data);
  256. }}
  257. return eid;
  258. }
  259. // first arg is system name. rest of args are passed through
  260. Game.prototype.trySystem = function() {
  261. var name = arguments.unshift();
  262. if(this.disabledSystems) return null;
  263. return this.systems[name].apply(this, arguments);
  264. };
  265. function runSystem(allComps, reqComps, cb){
  266. var len = reqComps.length;
  267. for(var eid in allComps[reqComps[0]]) {
  268. var go = true;
  269. var e = {};
  270. for(var i = 0; i < len; i++) {
  271. var cn = reqComps[i];
  272. if(!allComps[cn] || !allComps[cn].hasOwnProperty(eid)) {
  273. go = false;
  274. break;
  275. }
  276. e[cn] = allComps[cn][eid]
  277. }
  278. if(go) cb(e, eid);
  279. }
  280. }
  281. Game.prototype.spawnCustomer = function(pos) {
  282. var eid = this.spawn(Entities.customer1);
  283. this.setComp(eid, 'position', ptc(pos));
  284. this.addComponent(eid, 'followPaths', 1);
  285. return eid;
  286. }