-
Notifications
You must be signed in to change notification settings - Fork 1
/
game.js
150 lines (142 loc) · 4.82 KB
/
game.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
(function() {
//private methods
function gameLoop(game) {
game.fire("gameLoopBegin");
game.fire("gameLoopEnd");
}
jojo.Game = Class.create(jojo.event.EventPublisher, {
frameDelay: 30,
initialize: function($super, options) {
//basic validation: require a canvas element to be passed in
if (!options || !options.canvas) {
throw new Error("The MojoJojo.Game class requires a canvas element to be passed into the constructor");
}
//base class construction
$super(options);
//entity management objects
this.entityCollection = []; //flat array of all entities that can be used for sorting algorithms
this.entityCache = {}; //indexed associative array for fast retrieval
this.entityKeys = []; //array of keys used to index entities in the cache object
//setup options
this.frameDelay = options.frameDelay || this.frameDelay;
this.canvas = options.canvas; //the canvas element to use to draw the game scenes to and listen to events
this.config = options.config;
if (options.configPath) {
this.loadConfig(options.configPath);
}
//pause game on explicit errors
this.on("error", this.pauseGame.bind(this));
},
loadConfig: function(path) {
var me = this;
this.configPath = path;
var request = new Ajax.Request(path, {
method: 'get',
evalJSON: 'force',
onSuccess: function(args) {
me.config = args.responseJSON;
me.fire("configLoaded");
},
onFailure: function(args) {
me.fire("error", {
source: "loadConfig",
message: "Failure loading config file",
data: args
});
}
});
},
startGame: function(options) {
if (options) {
Object.extend(this, options);
}
var me = this;
if (!this.wired) { //only want to wire the Mojo listeners once per instance
this.ctx = this.canvas.getContext('2d');
Mojo.Event.listen(this.canvas, 'mousedown', this.handleTouch.bind(this));
Mojo.Event.listen(this.canvas, 'mouseup', this.handleMouseUp.bind(this));
Mojo.Event.listen(this.canvas, 'mousemove' , this.handleMouseMove.bind(this));
this.wired = true;
}
this.fire("beforeGameStart");
if (!this.preventGameStart) {
this.gameInterval = setInterval(function(){
try {
gameLoop(me);
}
catch (error) {
me.fire("error", {
source: "gameLoop",
message: "Failure in gameLoop: " + error.message
});
}
}, this.frameDelay);
this.gameRunning = true;
this.fire("gameStarted");
}
},
pauseGame: function() {
if (this.gameRunning && this.gameInterval) {
this.fire("beforeGamePause");
if (!this.preventGamePause) {
clearInterval(this.gameInterval);
delete this.gameInterval;
this.gameRunning = false;
this.fire("gamePaused");
}
}
},
handleTouch: function(event) {
this.fire("touch", {event: event});
//handle entity touch events
var touchedEntities = findTouchedEntities();//pseudo-code
if (touchedEntities) {
for (var i = 0, _e = touchedEntities; i < touchedEntities.length; i++) {
_e[i].fire("touch", {game: this, event: event});
}
}
},
handleMouseUp: function(event) {
this.fire("mouseUp", {event: event});
//handle entity click events
var clickedEntities = findClickedEntities(event);//pseudo-code
if (clickedEntities) {
for (var i = 0, _e = clickedEntities; i < clickedEntities.length; i++) {
_e[i].fire("click", {game: this, event: event});
}
}
},
handleMouseMove: function(event) {
this.fire("mouseMove", {event: event});
//handle entity "untouch" events (entities where the previous x/y/z was in touchzone but new x/y/z is not)
var untouchedEntities = findUntouchedEntities(event);//pseudo-code
if (untouchedEntities) {
for (var i = 0, _e = untouchedEntities; i < untouchedEntities.length; i++) {
_e[i].fire("untouch", {game: this, event: event});
}
}
//handle new entity touch events
var newTouchedEntities = findNewTouchedEntities(event);//pseudo-code
if (newTouchedEntities) {
for (var i = 0, _e = newTouchedEntities; i < newTouchedEntities.length; i++) {
_e[i].fire("touch", {game: this, event: event});
}
}
},
addEntity: function(entity) {
if (!entity.addedToCollection) {
this.fire("beforeAddEntity", {entity: entity});
this.entityCollection.push(entity);
//allow custom indexing function on entity classes (recommended)
var key = entity.getIndexKey ? entity.getIndexKey() : entity.id;
this.entityCache[key] = entity;
this.entityKeys.push(key);
entity.addedToCollection = true;
entity.game = this;
//allow entity classes to define logic upon being added to the scene (for example, for initial pathfinding calculations or other setup)
entity.fire("added");
this.fire("entityAdded", {entity: entity});
}
}
});
})();