Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- /// <reference path="../d.ts/raphael-2.1.d.ts"/>
- /// <reference path="../d.ts/socket.io.client.d.ts"/>
- //these are the global variables, that we will need
- //the drawing area
- var paper:RaphaelPaper;
- //the lists of gameObjects of the player and his enemy;
- var localGameObjects: GameObject[];
- var remoteGameObjects: GameObject[];
- //the input class. It's got a keyMap with boolean values for up, down, left and right
- var input: Input;
- var div: HTMLElement;
- var socket: Socket;
- //At first I wanted to keep this a simple boolean toggle, but maybe we want to add more gamestates
- var gameState = { active: false };
- //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
- //this script is included in the html head and thus starts executing very early. Actually too early.
- //It is common practice to wait till everything has loaded and start then. That's what window.onload is for:
- //window.onload is a member of the global window object. It's a callback that will be called once everything has loaded
- //but be careful. Javascript is one monster of a language and there are always special cases. window.onload will definitely be
- //called sooner or later but unfortunately sometimes its sooner rather than later. That's why the popular jquery framework has its own
- // "window.onload" function which is called "$".
- //well, long story short: This is the entry point of our application. Don't place any code outside, except global variables and class definitions
- window.onload = () => {
- //first thing's first, let's get these globals up and running
- div = document.getElementById("raphael");
- paper = Raphael(div, 500, 500);
- localGameObjects = new GameObject[];
- remoteGameObjects = new GameObject[];
- input = new Input();
- //establish the connection to the server and print all incoming data
- socket = io.connect("localhost");
- socket.on("news", (data: any) => { alert(data) });
- socket.on("SpawnFactory", (data) => Network.RPCSpawnFactory(data));
- socket.on("SpawnRobot",(data)=>Network.RPCSpawnRobot(data));
- socket.on("MoveRobot", (data) => Network.RPCMoveRobot(data));
- //time
- var prev = Date.now();
- var elapsedTime = Date.now() - prev;
- //and a little placeholder, till we have some more features implemented
- //var robot =new Robot( paper.rect(10, 10, 100, 100));
- //robot.elem.attr("fill", "green");
- //this is just for testing
- //robot.tag = "enemy"
- //localGameObjects.push(robot);
- //draw a grid
- GridManager.drawGrid(paper);
- GridManager.initGrid();
- //set up the mouse input
- div.onmousedown = GridManager.spawnFactory;
- //now the game loop. we all know the drill: update, draw, update, draw, update, ...
- //in this case we don't need to account for drawing, all elements created by the RaphaelPaper are drawn automatically
- //but we need to update continuously
- //at first I used a while loop but that turned out to be the wrong way.
- //window.setInterval is used to set a callback, its to parameters are the function that is to be called and the time to elapse
- //between individual calls. Once again one of my beloved lambdas.
- //window.setInterval registers a function to be called infinitely and returns a number that serves as an ID of the interval
- //I'm not storing this number, we don't want to delete the interval
- //as opposed to window.setInterval there's a second function window.setTimeout. Very similar: two parameters - one function and one delay
- window.setInterval(() => {
- //calculate the delta Time
- elapsedTime = Date.now() - prev;
- prev = Date.now();
- //update each object in our global GameObject list. Lambda again - I love it
- localGameObjects.forEach((object) => object.update(elapsedTime));
- }, 50);
- }
- // an interface
- interface GameObject{
- update(deltaTime:number);
- destroy();
- //lo and behold: Typescript allows the declaration of members in an interface
- tag:string;
- getPos():Vector2;
- }
- //the input class, pretty straightforward
- //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
- class Input {
- up=false;
- down=false;
- left=false;
- right=false;
- //brace yourself for two lambdas
- constructor() {
- //here comes number 1
- window.onkeydown = (ev: KeyboardEvent) => {
- switch (String.fromCharCode(ev.which)) {
- case "W":
- this.up = true;
- break;
- case "A":
- this.left = true;
- break;
- case "S":
- this.down = true;
- break;
- case "D":
- this.right = true;
- break;
- }
- }
- //and here number 2
- window.onkeyup = (ev: KeyboardEvent) => {
- switch (String.fromCharCode(ev.which)) {
- case "W":
- this.up = false;
- break;
- case "A":
- this.left = false;
- break;
- case "S":
- this.down = false;
- break;
- case "D":
- this.right = false;
- break;
- }
- }
- }
- }
- //this is probably useless, but I wanted a vector2 type.
- class Vector2 { public x: number; public y: number;
- constructor(x: number, y: number) { this.x = x; this.y = y; }
- public add(other: Vector2) { this.x += other.x; this.y += other.y; return this; }
- public sub(other: Vector2) { this.x -= other.x; this.y -= other.y; return this; }
- public mul(other: number) { this.x *= other; this.y *= other; return this; }
- public div(other: number) { this.x *= 1 / other; this.y *= 1 / other; }
- public length() { return Math.sqrt(Math.pow(this.x, 2) + Math.pow(this.y, 2)); }
- public dirTo(other: Vector2) { var dir = new Vector2(0, 0); dir.x = other.x - this.x; dir.y = other.y - this.y; return dir; }
- public static normalize(other: Vector2) { return other.div(other.length()); }
- public normalize() { this.div(this.length()); return this; }
- 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));}
- public toString() { return "" + this.x + "," + this.y; }
- }
- //as of now this is empty. The idea is to register the static functions as callbacks to events on the global socket variable
- //I'm thinking like this:
- //socket.on("spawnRobot",(data:any)=>Network.RPCSpawnRobot(data));
- //then inside the function, a new GameObject is created and placed in the grid
- //it's important to add it to the list remoteGameObjects, to prevent local calls to update.
- //this architecture should work with a turn based game as well as with a real time game
- //my idea is to encapsule
- class Network {
- //this will heavily resemble Gridmanager.spawnFactory. But it gives the new Factory a different color, adds it to remoteGameObjects
- //AND MOST IMPORTANTLY: works with the cell coordinates
- //data.x is the x coordinate in the grid
- //data.y is the y coordinate in the grid
- public static RPCSpawnFactory(data:any) {
- alert("spawnfactory");
- var pos = new Vector2(data.x, data.y);
- var center = GridManager.getCellCenter(pos);
- var factory = new Factory(paper.rect(center.x - 10, center.y - 10, 20, 20));
- factory.elem.attr("fill", "black");
- remoteGameObjects.push(factory);
- GridManager.setCell(pos, factory);
- }
- public static RPCSpawnRobot(data: any) {
- alert("spawnrobot");
- var pos = new Vector2(data.x, data.y);
- var robot = new Robot(paper.circle(pos.x,pos.y, 5));
- robot.elem.attr("fill", "red");
- remoteGameObjects.push(robot);
- }
- public static RPCMoveRobot(data: any) {
- }
- }
- class GridManager {
- public static gridwidth: number = 50;
- public static gridheight: number = 50;
- public static height: number = 9;
- public static widht: number = 9;
- public static grid: GameObject[][] = new GameObject[][];
- public static initGrid() {
- for (var i = 0; i < GridManager.height + 1; i++) {
- grid.push(new GameObject[]);
- for (var j = 0; j < GridManager.widht + 1; j++) {
- grid[i].push(null);
- }
- }
- }
- public static drawGrid(paper: RaphaelPaper) {
- for (var i = 0; i < GridManager.height + 1; i++) {
- for (var j = 0; j < GridManager.widht + 1; j++) {
- paper.path("M" + i * gridwidth + "," + 0 + "L" + i * gridwidth + "," + j * gridheight);
- paper.path("M" + 0 + "," + j * gridheight + "L" + i * gridwidth + "," + j * gridheight);
- }
- }
- }
- public static getCellCenter(pos: Vector2): Vector2 {
- var xcoord = pos.x * gridwidth;
- xcoord += gridwidth / 2;
- var ycoord = pos.y * gridheight;
- ycoord += gridheight / 2;
- return new Vector2(xcoord, ycoord);
- }
- public static getCellIndizes(pos: Vector2):Vector2 {
- var x = Math.floor(pos.x / gridwidth);
- var y = Math.floor(pos.y / gridheight);
- return new Vector2(x, y);
- }
- public static cellEmpty(pos: Vector2):bool {
- return grid[pos.x][pos.y]==null;
- }
- public static setCell(pos: Vector2, object:GameObject) {
- grid[pos.x][pos.y] = object;
- }
- //create a new factory
- public static spawnFactory(ev:MouseEvent) {
- var mouse_x = ev.pageX - div.offsetLeft;
- var mouse_y = ev.pageY - div.offsetTop;
- var pos = new Vector2(mouse_x, mouse_y);
- var pos = getCellIndizes(pos);
- if (!GridManager.cellEmpty(pos)){ alert("full"); return;};
- var center = getCellCenter(pos);
- var factory = new Factory(paper.rect(center.x - 10, center.y - 10, 20, 20));
- factory.elem.attr("fill", "green");
- localGameObjects.push(factory);
- setCell(pos, factory);
- socket.emit("SpawnFactory", {x:pos.x,y:pos.y});
- }
- }
- //a container for RaphaelElements,
- //wraps the calls to attr nicely, setting a position by this.elem.attr("x",new_x); definitely felt wrong
- class Elem {
- public elem: RaphaelElement;
- public pos: Vector2;
- constructor(element: RaphaelElement) {
- this.elem = element;
- this.pos = new Vector2(element.attr("x"), element.attr("y"))
- }
- public setPos(position: Vector2){
- this.pos = position;
- this.elem.attr("x", this.pos.x);
- this.elem.attr("y", this.pos.y);
- }
- public updatePos() {
- this.pos.x = this.elem.attr("x");
- this.pos.y = this.elem.attr("y");
- }
- public getPos() { return this.pos; }
- }
- //here comes the factory, spitting out a new robot every delay seconds
- class Factory implements GameObject extends Elem {
- tag: string = "factory";
- //variables for handling the delay between spawning robots
- private current_time: number;
- public delay: number;
- //keep track of the number of factories
- //the ratio of factories/mines will determine the speed of robot production
- public static count = 0;
- constructor(element: RaphaelElement) {
- super(element);
- this.current_time = 0;
- this.delay = 2;
- Factory.count += 1;
- this.spawnRobot();
- }
- //check if it's time to create a new robot
- update(deltaTime: number) {
- /*this.current_time += deltaTime/1000;
- if (this.current_time > this.delay*(Factory.count+1)/(Mine.count+1)) {
- this.current_time = 0;
- this.spawnRobot();
- }*/
- }
- destroy() { Factory.count -= 1; }
- //create a new robot, don't forget to add it to the gameObjects list
- spawnRobot() {
- var robot = new Robot(paper.circle(this.elem.attr("x"),this.elem.attr("y"), 5));
- robot.elem.attr("fill", "blue");
- localGameObjects.push(robot);
- socket.emit("SpawnRobot", { x: this.elem.attr("x"), y: this.elem.attr("y") });
- }
- }
- class Mine implements GameObject extends Elem {
- tag = "mine";
- public static count: number = 0;
- constructor (element: RaphaelElement) {
- super(element);
- Mine.count += 1;
- }
- destroy() { Mine.count -= 1; }
- update(deltaTime: number) {
- //nothing to do here
- }
- }
- //this will be the fighting robot meant to destroy enemy factories
- class Robot implements GameObject extends Elem{
- tag: string = "robot";
- constructor(element: RaphaelElement) {
- super(element);
- }
- destroy() { }
- //now the robot is moved by keyboard input but soon it will check the gameObjects array and search for the closest enemy,
- //in order to attack it
- public update(deltaTime: number) {
- /*var dir: Vector2 = new Vector2(0,0);
- if (input.down) dir.y += 1;
- if (input.up) dir.y -= 1;
- if (input.left) dir.x -= 1;
- if (input.right) dir.x += 1;
- //still using the old calls to attr instead of setPos
- this.elem.attr("x", this.elem.attr("x") + dir.x * deltaTime/50);
- this.elem.attr("y", this.elem.attr("y") + dir.y*deltaTime/50);*/
- /*var target:GameObject = null;
- var distance: number = null;
- remoteGameObjects.forEach((gameObject) =>
- {
- if (target == null) { target = gameObject; distance = Vector2.Distance(this.getPos(), target.getPos()); }
- else {
- if (Vector2.Distance(this.getPos(), gameObject.getPos()) < distance) { target = gameObject; distance = Vector2.Distance(this.getPos(), gameObject.getPos()); }
- }
- });
- if (target != null) {
- var dir=this.getPos().dirTo(target.getPos()).normalize();
- alert(this.pos.toString());
- this.setPos(this.getPos().add(dir));
- }*/
- }
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement