Advertisement
nordlaender

game.ts

Jan 1st, 2013
315
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 14.12 KB | None | 0 0
  1. /// <reference path="../d.ts/raphael-2.1.d.ts"/>
  2. /// <reference path="../d.ts/socket.io.client.d.ts"/>
  3.  
  4. //these are the global variables, that we will need
  5. //the drawing area
  6. var paper:RaphaelPaper;
  7. //the lists of gameObjects of the player and his enemy;
  8. var localGameObjects: GameObject[];
  9. var remoteGameObjects: GameObject[];
  10. //the input class. It's got a keyMap with boolean values for up, down, left and right
  11. var input: Input;
  12. var div: HTMLElement;
  13.  
  14. var socket: Socket;
  15.  
  16. //At first I wanted to keep this a simple boolean toggle, but maybe we want to add more gamestates
  17. var gameState = { active: false };
  18. //this is a little javascript peculiarity. We are displaying all content on an html page, therefore you have to remember that you're not alone
  19. //this script is included in the html head and thus starts executing very early. Actually too early.
  20. //It is common practice to wait till everything has loaded and start then. That's what window.onload is for:
  21. //window.onload is a member of the global window object. It's a callback that will be called once everything has loaded
  22. //but be careful. Javascript is one monster of a language and there are always special cases. window.onload will definitely be
  23. //called sooner or later but unfortunately sometimes its sooner rather than later. That's why the popular jquery framework has its own
  24. // "window.onload" function which is called "$".
  25. //well, long story short: This is the entry point of our application. Don't place any code outside, except global variables and class definitions
  26. window.onload = () => {
  27. //first thing's first, let's get these globals up and running
  28. div = document.getElementById("raphael");
  29. paper = Raphael(div, 500, 500);
  30. localGameObjects = new GameObject[];
  31. remoteGameObjects = new GameObject[];
  32. input = new Input();
  33.  
  34. //establish the connection to the server and print all incoming data
  35. socket = io.connect("localhost");
  36. socket.on("news", (data: any) => { alert(data) });
  37. socket.on("SpawnFactory", (data) => Network.RPCSpawnFactory(data));
  38. socket.on("SpawnRobot",(data)=>Network.RPCSpawnRobot(data));
  39. socket.on("MoveRobot", (data) => Network.RPCMoveRobot(data));
  40.  
  41. //time
  42. var prev = Date.now();
  43. var elapsedTime = Date.now() - prev;
  44.  
  45. //and a little placeholder, till we have some more features implemented
  46. //var robot =new Robot( paper.rect(10, 10, 100, 100));
  47. //robot.elem.attr("fill", "green");
  48. //this is just for testing
  49. //robot.tag = "enemy"
  50. //localGameObjects.push(robot);
  51.  
  52. //draw a grid
  53. GridManager.drawGrid(paper);
  54. GridManager.initGrid();
  55.  
  56. //set up the mouse input
  57. div.onmousedown = GridManager.spawnFactory;
  58.  
  59. //now the game loop. we all know the drill: update, draw, update, draw, update, ...
  60. //in this case we don't need to account for drawing, all elements created by the RaphaelPaper are drawn automatically
  61. //but we need to update continuously
  62. //at first I used a while loop but that turned out to be the wrong way.
  63. //window.setInterval is used to set a callback, its to parameters are the function that is to be called and the time to elapse
  64. //between individual calls. Once again one of my beloved lambdas.
  65. //window.setInterval registers a function to be called infinitely and returns a number that serves as an ID of the interval
  66. //I'm not storing this number, we don't want to delete the interval
  67. //as opposed to window.setInterval there's a second function window.setTimeout. Very similar: two parameters - one function and one delay
  68. window.setInterval(() => {
  69. //calculate the delta Time
  70. elapsedTime = Date.now() - prev;
  71. prev = Date.now();
  72. //update each object in our global GameObject list. Lambda again - I love it
  73. localGameObjects.forEach((object) => object.update(elapsedTime));
  74. }, 50);
  75. }
  76. // an interface
  77. interface GameObject{
  78. update(deltaTime:number);
  79. destroy();
  80. //lo and behold: Typescript allows the declaration of members in an interface
  81. tag:string;
  82. getPos():Vector2;
  83. }
  84.  
  85. //the input class, pretty straightforward
  86. //four members that store the input, at first I wanted to make them static but then, hm I still don't know, I just didn't feel like static
  87. class Input {
  88. up=false;
  89. down=false;
  90. left=false;
  91. right=false;
  92.  
  93. //brace yourself for two lambdas
  94. constructor() {
  95. //here comes number 1
  96. window.onkeydown = (ev: KeyboardEvent) => {
  97. switch (String.fromCharCode(ev.which)) {
  98. case "W":
  99. this.up = true;
  100. break;
  101. case "A":
  102. this.left = true;
  103. break;
  104. case "S":
  105. this.down = true;
  106. break;
  107. case "D":
  108. this.right = true;
  109. break;
  110. }
  111. }
  112. //and here number 2
  113. window.onkeyup = (ev: KeyboardEvent) => {
  114. switch (String.fromCharCode(ev.which)) {
  115. case "W":
  116. this.up = false;
  117. break;
  118. case "A":
  119. this.left = false;
  120. break;
  121. case "S":
  122. this.down = false;
  123. break;
  124. case "D":
  125. this.right = false;
  126. break;
  127. }
  128. }
  129. }
  130. }
  131.  
  132. //this is probably useless, but I wanted a vector2 type.
  133. class Vector2 { public x: number; public y: number;
  134. constructor(x: number, y: number) { this.x = x; this.y = y; }
  135. public add(other: Vector2) { this.x += other.x; this.y += other.y; return this; }
  136. public sub(other: Vector2) { this.x -= other.x; this.y -= other.y; return this; }
  137. public mul(other: number) { this.x *= other; this.y *= other; return this; }
  138. public div(other: number) { this.x *= 1 / other; this.y *= 1 / other; }
  139.  
  140. public length() { return Math.sqrt(Math.pow(this.x, 2) + Math.pow(this.y, 2)); }
  141.  
  142. public dirTo(other: Vector2) { var dir = new Vector2(0, 0); dir.x = other.x - this.x; dir.y = other.y - this.y; return dir; }
  143. public static normalize(other: Vector2) { return other.div(other.length()); }
  144. public normalize() { this.div(this.length()); return this; }
  145.  
  146. public static Distance(a: Vector2, b: Vector2) { var x = a.x - b.x; var y = a.y - b.y; return Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2));}
  147.  
  148. public toString() { return "" + this.x + "," + this.y; }
  149. }
  150.  
  151.  
  152. //as of now this is empty. The idea is to register the static functions as callbacks to events on the global socket variable
  153. //I'm thinking like this:
  154. //socket.on("spawnRobot",(data:any)=>Network.RPCSpawnRobot(data));
  155. //then inside the function, a new GameObject is created and placed in the grid
  156. //it's important to add it to the list remoteGameObjects, to prevent local calls to update.
  157. //this architecture should work with a turn based game as well as with a real time game
  158. //my idea is to encapsule
  159. class Network {
  160. //this will heavily resemble Gridmanager.spawnFactory. But it gives the new Factory a different color, adds it to remoteGameObjects
  161. //AND MOST IMPORTANTLY: works with the cell coordinates
  162. //data.x is the x coordinate in the grid
  163. //data.y is the y coordinate in the grid
  164. public static RPCSpawnFactory(data:any) {
  165. alert("spawnfactory");
  166. var pos = new Vector2(data.x, data.y);
  167.  
  168. var center = GridManager.getCellCenter(pos);
  169.  
  170. var factory = new Factory(paper.rect(center.x - 10, center.y - 10, 20, 20));
  171. factory.elem.attr("fill", "black");
  172. remoteGameObjects.push(factory);
  173. GridManager.setCell(pos, factory);
  174. }
  175. public static RPCSpawnRobot(data: any) {
  176. alert("spawnrobot");
  177. var pos = new Vector2(data.x, data.y);
  178.  
  179. var robot = new Robot(paper.circle(pos.x,pos.y, 5));
  180. robot.elem.attr("fill", "red");
  181. remoteGameObjects.push(robot);
  182. }
  183. public static RPCMoveRobot(data: any) {
  184. }
  185. }
  186.  
  187. class GridManager {
  188. public static gridwidth: number = 50;
  189. public static gridheight: number = 50;
  190.  
  191. public static height: number = 9;
  192. public static widht: number = 9;
  193.  
  194. public static grid: GameObject[][] = new GameObject[][];
  195.  
  196. public static initGrid() {
  197. for (var i = 0; i < GridManager.height + 1; i++) {
  198. grid.push(new GameObject[]);
  199. for (var j = 0; j < GridManager.widht + 1; j++) {
  200. grid[i].push(null);
  201. }
  202. }
  203. }
  204.  
  205. public static drawGrid(paper: RaphaelPaper) {
  206. for (var i = 0; i < GridManager.height + 1; i++) {
  207. for (var j = 0; j < GridManager.widht + 1; j++) {
  208. paper.path("M" + i * gridwidth + "," + 0 + "L" + i * gridwidth + "," + j * gridheight);
  209. paper.path("M" + 0 + "," + j * gridheight + "L" + i * gridwidth + "," + j * gridheight);
  210. }
  211. }
  212. }
  213.  
  214. public static getCellCenter(pos: Vector2): Vector2 {
  215. var xcoord = pos.x * gridwidth;
  216. xcoord += gridwidth / 2;
  217. var ycoord = pos.y * gridheight;
  218. ycoord += gridheight / 2;
  219. return new Vector2(xcoord, ycoord);
  220. }
  221.  
  222. public static getCellIndizes(pos: Vector2):Vector2 {
  223. var x = Math.floor(pos.x / gridwidth);
  224. var y = Math.floor(pos.y / gridheight);
  225. return new Vector2(x, y);
  226. }
  227.  
  228. public static cellEmpty(pos: Vector2):bool {
  229. return grid[pos.x][pos.y]==null;
  230. }
  231.  
  232. public static setCell(pos: Vector2, object:GameObject) {
  233. grid[pos.x][pos.y] = object;
  234. }
  235.  
  236. //create a new factory
  237. public static spawnFactory(ev:MouseEvent) {
  238.  
  239. var mouse_x = ev.pageX - div.offsetLeft;
  240. var mouse_y = ev.pageY - div.offsetTop;
  241.  
  242. var pos = new Vector2(mouse_x, mouse_y);
  243. var pos = getCellIndizes(pos);
  244. if (!GridManager.cellEmpty(pos)){ alert("full"); return;};
  245.  
  246. var center = getCellCenter(pos);
  247.  
  248. var factory = new Factory(paper.rect(center.x - 10, center.y - 10, 20, 20));
  249. factory.elem.attr("fill", "green");
  250. localGameObjects.push(factory);
  251. setCell(pos, factory);
  252.  
  253. socket.emit("SpawnFactory", {x:pos.x,y:pos.y});
  254. }
  255. }
  256.  
  257. //a container for RaphaelElements,
  258. //wraps the calls to attr nicely, setting a position by this.elem.attr("x",new_x); definitely felt wrong
  259. class Elem {
  260. public elem: RaphaelElement;
  261. public pos: Vector2;
  262.  
  263. constructor(element: RaphaelElement) {
  264. this.elem = element;
  265. this.pos = new Vector2(element.attr("x"), element.attr("y"))
  266. }
  267.  
  268. public setPos(position: Vector2){
  269. this.pos = position;
  270. this.elem.attr("x", this.pos.x);
  271. this.elem.attr("y", this.pos.y);
  272. }
  273.  
  274. public updatePos() {
  275. this.pos.x = this.elem.attr("x");
  276. this.pos.y = this.elem.attr("y");
  277. }
  278.  
  279. public getPos() { return this.pos; }
  280. }
  281.  
  282. //here comes the factory, spitting out a new robot every delay seconds
  283. class Factory implements GameObject extends Elem {
  284. tag: string = "factory";
  285.  
  286. //variables for handling the delay between spawning robots
  287. private current_time: number;
  288. public delay: number;
  289.  
  290. //keep track of the number of factories
  291. //the ratio of factories/mines will determine the speed of robot production
  292. public static count = 0;
  293.  
  294. constructor(element: RaphaelElement) {
  295. super(element);
  296. this.current_time = 0;
  297. this.delay = 2;
  298. Factory.count += 1;
  299. this.spawnRobot();
  300. }
  301.  
  302. //check if it's time to create a new robot
  303. update(deltaTime: number) {
  304. /*this.current_time += deltaTime/1000;
  305. if (this.current_time > this.delay*(Factory.count+1)/(Mine.count+1)) {
  306. this.current_time = 0;
  307. this.spawnRobot();
  308. }*/
  309. }
  310.  
  311. destroy() { Factory.count -= 1; }
  312. //create a new robot, don't forget to add it to the gameObjects list
  313. spawnRobot() {
  314. var robot = new Robot(paper.circle(this.elem.attr("x"),this.elem.attr("y"), 5));
  315. robot.elem.attr("fill", "blue");
  316. localGameObjects.push(robot);
  317.  
  318. socket.emit("SpawnRobot", { x: this.elem.attr("x"), y: this.elem.attr("y") });
  319. }
  320. }
  321.  
  322. class Mine implements GameObject extends Elem {
  323. tag = "mine";
  324.  
  325. public static count: number = 0;
  326.  
  327. constructor (element: RaphaelElement) {
  328. super(element);
  329. Mine.count += 1;
  330. }
  331.  
  332. destroy() { Mine.count -= 1; }
  333.  
  334. update(deltaTime: number) {
  335. //nothing to do here
  336. }
  337.  
  338. }
  339.  
  340. //this will be the fighting robot meant to destroy enemy factories
  341. class Robot implements GameObject extends Elem{
  342. tag: string = "robot";
  343. constructor(element: RaphaelElement) {
  344. super(element);
  345. }
  346.  
  347. destroy() { }
  348.  
  349. //now the robot is moved by keyboard input but soon it will check the gameObjects array and search for the closest enemy,
  350. //in order to attack it
  351. public update(deltaTime: number) {
  352. /*var dir: Vector2 = new Vector2(0,0);
  353. if (input.down) dir.y += 1;
  354. if (input.up) dir.y -= 1;
  355. if (input.left) dir.x -= 1;
  356. if (input.right) dir.x += 1;
  357.  
  358. //still using the old calls to attr instead of setPos
  359. this.elem.attr("x", this.elem.attr("x") + dir.x * deltaTime/50);
  360. this.elem.attr("y", this.elem.attr("y") + dir.y*deltaTime/50);*/
  361. /*var target:GameObject = null;
  362. var distance: number = null;
  363. remoteGameObjects.forEach((gameObject) =>
  364. {
  365. if (target == null) { target = gameObject; distance = Vector2.Distance(this.getPos(), target.getPos()); }
  366. else {
  367. if (Vector2.Distance(this.getPos(), gameObject.getPos()) < distance) { target = gameObject; distance = Vector2.Distance(this.getPos(), gameObject.getPos()); }
  368. }
  369. });
  370. if (target != null) {
  371. var dir=this.getPos().dirTo(target.getPos()).normalize();
  372. alert(this.pos.toString());
  373. this.setPos(this.getPos().add(dir));
  374. }*/
  375. }
  376. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement