-
Notifications
You must be signed in to change notification settings - Fork 0
/
GameObject.cs
452 lines (391 loc) · 18.9 KB
/
GameObject.cs
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
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
using System;
using System.Collections.Generic;
namespace ACE {
/// <summary>
/// This class represent a Game Object, a Game Object is an Object wich is drawn on the screen,
/// it can have collision, it can move, it can change appeareance and so on.
/// </summary>
public class GameObject:GameScript {
#region public variables
/// <summary>
/// The position of the GameObject on the X axis
/// </summary>
public int xPosition;
/// <summary>
/// The position of the GameObject on the X axis
/// </summary>
public int yPosition;
/// <summary>
/// This is the foregroundColor used to print this gameObject
/// </summary>
public ConsoleColor foreColor;
/// <summary>
/// This is the backgroundColor used to print this gameObject
/// </summary>
public ConsoleColor backColor;
/// <summary>
/// The position of the GameObject on the Z axis
/// (note: the Z axis is Only used for drawing's priority)
/// </summary>
public int drawLayer;
/// <summary>
/// ...C'mon, don't let me do this, you know what this variable is about
/// </summary>
public bool visible;
/// <summary>
/// If set to true the Object won't go into other Physical Object, if set to false this object will be able to pass through all the other objects
/// </summary>
public bool physicalObject;
/// <summary>
/// Returns the object that collided with this Object in the last movement, can be NULL
/// </summary>
public GameObject lastCollision { get { return hiddenLastCollision; } }
/// <summary>
/// NOT RECOMMENDED: the list of the timers used by this GameObject, you should use the default functions related to timers to create and delete them, but...
/// y'know, i don't really like closed codes in wich you can't fuck up everything, dont'cha agree?
/// </summary>
protected internal List<Timer> timers;
protected internal List<Timer> newTimers;
protected internal List<Timer> toDeleteTimers;
public int height { get => hiddenHeight; }
public int width { get => hiddenWidth; }
#endregion
#region protected variables
/// <summary>
/// NOT RECOMMENDED: this is the matrix that represent the aspect of the GameObject, you shouldn't change it directly unless you have to do something really particular
/// </summary>
protected char[,] repMatrix;
/// <summary>
/// NOT RECOMMENDED: this is old position on the X axis of the GameObject before the last movement, you shouldn't touch it unless you want to override the move function
/// </summary>
protected int oldX;
/// <summary>
/// NOT RECOMMENDED: this is old position on the Y axis of the GameObject before the last movement, you shouldn't touch it unless you want to override the move function
/// </summary>
protected int oldY;
/// <summary>
/// NOT RECOMMENDED: this is the calculated width of the GameObject, don't change it unless you know what you are doing
/// </summary>
protected int hiddenWidth;
/// <summary>
/// NOT RECOMMENDED: this is the calculated height of the GameObject, don't change it unless you know what you are doing
/// </summary>
protected int hiddenHeight;
/// <summary>
/// This contain the GameObject that collided with this one in the last movement (NULL if there was no collision)
/// </summary>
protected GameObject hiddenLastCollision;
#endregion
#region Constructors
#region Constructors that require the aspect of the gameObject
/// <summary>
/// Create a GameObject
/// </summary>
/// <param name="design">The string that represent the aspect of the GameObject</param>
/// <param name="x">The position of the GameObject on the X axis</param>
/// <param name="y">The position of the GameObject on the Y axis</param>
/// <param name="drawLayer">The priority for drawing this Object</param>
/// <param name="physicalObject">A bool that indicate if the Object is Physical or not</param>
public GameObject(string design, int x, int y, int drawLayer, bool physicalObject = true) {
Initialize(x, y);
setDesign(design);
this.drawLayer = drawLayer;
this.physicalObject = physicalObject;
}
/// <summary>
/// Create a GameObject
/// </summary>
/// <param name="design">A char matrix that represent the aspect of the Object</param>
/// <param name="x">The position of the GameObject on the X axis</param>
/// <param name="y">The position of the GameObject on the Y axis</param>
/// <param name="drawLayer">The priority for drawing this Object</param>
/// <param name="physicalObject">A bool that indicate if the Object is Physical or not</param>
public GameObject(char[,] design, int x, int y, int drawLayer, bool physicalObject = true) {
Initialize(x, y);
setDesign(design);
this.drawLayer = drawLayer;
this.physicalObject = physicalObject;
}
/// <summary>
/// Create a GameObject
/// </summary>
/// <param name="design">A char matrix that represent the aspect of the Object</param>
/// <param name="x">The position of the GameObject on the X axis</param>
/// <param name="y">The position of the GameObject on the Y axis</param>
/// <param name="physicalObject">A bool that indicate if the Object is Physical or not</param>
public GameObject(char[,] design, int x, int y, bool physicalObject = true) : this(design, x, y, 0, physicalObject) { }
/// <summary>
/// Create a GameObject
/// </summary>
/// <param name="design">The string that represent the aspect of the GameObject</param>
/// <param name="x">The position of the GameObject on the X axis</param>
/// <param name="y">The position of the GameObject on the Y axis</param>
/// <param name="physicalObject">A bool that indicate if the Object is Physical or not</param>
public GameObject(string design, int x, int y, bool physicalObject = true) : this(design, x, y, 0, physicalObject) { }
#endregion
#region Constructors that don't require the aspect of the gameObject
/// <summary>
/// Create a GameObject
/// </summary>
/// <param name="x">The position of the GameObject on the X axis</param>
/// <param name="y">The position of the GameObject on the Y axis</param>
/// <param name="drawLayer">The priority for drawing this Object</param>
/// <param name="physicalObject">A bool that indicate if the Object is Physical or not</param>
public GameObject(int x, int y, int drawLayer, bool physicalObject = true) {
Initialize(x, y);
this.drawLayer = drawLayer;
this.physicalObject = physicalObject;
}
/// <summary>
/// Create a GameObject
/// </summary>
/// <param name="x">The position of the GameObject on the X axis</param>
/// <param name="y">The position of the GameObject on the Y axis</param>
/// <param name="physicalObject">A bool that indicate if the Object is Physical or not</param>
public GameObject(int x, int y, bool physicalObject = true) : this(x, y, 0, physicalObject) { }
#endregion
private void Initialize(int x, int y) {
timers = new List<Timer>();
newTimers = new List<Timer>();
toDeleteTimers = new List<Timer>();
//imposto le impostazioni di default, verranno poi cambiate dai costruttori se specificate
visible = true;
physicalObject = true;
//aggiungo il gameObject appena creato alla lista dei gameObject
Engine.AddGameObject(this);
//imposto la sua posizione
xPosition = x;
yPosition = y;
//imposto il colore di default
foreColor = ConsoleColor.White;
backColor = ConsoleColor.Black;
hiddenLastCollision = null;
//avvio il metodo di inizializzazione del gameObject
Start();
}
#endregion
#region Method for drawing the GameObject
internal void clean() {
Console.ForegroundColor = ConsoleColor.White;
Console.BackgroundColor = ConsoleColor.Black;
for(int y = 0; y < repMatrix.GetLength(0); y++) {
for(int x = 0; x < repMatrix.GetLength(1); x++) {
Engine.Write(oldX + x, oldY + y, ' ');
}
}
}
internal void draw() {
Console.ForegroundColor = foreColor;
Console.BackgroundColor = backColor;
for(int y = 0; y < repMatrix.GetLength(0); y++) {
for(int x = 0; x < repMatrix.GetLength(1); x++) {
Engine.Write(xPosition + x, yPosition + y, repMatrix[y, x]);
}
}
oldX = xPosition;
oldY = yPosition;
}
#endregion
#region Method to use Engine timers
//struttura del timer = {ID, RATEO, CURRENT_MS}
/// <summary>
/// This method create a timer that you can check in the Update method
/// </summary>
/// <param name="timerID">the ID of the timer, different gameObject can have the same ID for different timer</param>
/// <param name="msPerTick">The number of milliseconds necessary for this timer to tick</param>
/// <param name="currentMS">The current number of milliseconds passed from the last tick</param>
public Timer createTimer(int timerID, int msPerTick, Action tickAction, int currentMS) {
return createTimer(timerID, msPerTick, tickAction, true, currentMS);
}
public Timer createTimer(int timerID, int msPerTick, Action tickAction, bool restartFlag = true, int currentMS = 0) {
if(msPerTick < 1) throw new Exception("You cannot create a timer that tick every less than one second");
//cerco il timer, se esiste cambio il tuo rateo di tick
bool timerExists = false;
foreach(Timer timer in timers) {
if(timer.ID == timerID) {
timerExists = true;
}
}
//se non esiste ne creo uno nuovo
if(timerExists) {
throw new Exception("A timer with the ID:" + timerID + " already exists");
} else {
Timer timer = new Timer(timerID, msPerTick, tickAction, restartFlag, currentMS);
newTimers.Add(timer);
return timer;
}
}
public Timer getTimer(int timerID) {
foreach(Timer timer in timers) {
if(timer.ID == timerID) {
return timer;
}
}
return null;
}
public bool removeTimer(int timerID) {
int index = -1;
for(int i = 0; i < timers.Count && index == -1; i++) {
if(timers[i].ID == timerID) {
index = i;
toDeleteTimers.Add(timers[i]);
}
}
if(index != -1) {
return true;
}
return false; ;
}
#endregion
#region Methods related to GameObject's appearance
#region Methods to alter GameObject's appearance
/// <summary>
/// This replace the design with a new one of the same size
/// </summary>
/// <param name="design">The new design as a char matrix</param>
/// <returns>true if the new design is compatible with the last one</returns>
public bool replaceDesign(char[,] design) {
if(hiddenHeight != design.GetLength(0) || hiddenWidth != design.GetLength(1)) return false;
repMatrix = design;
return true;
}
/// <summary>
/// This replace the design with a new one of the same size
/// </summary>
/// <param name="design">The new design as a string</param>
/// <returns>true if the new design is compatible with the last one</returns>
public bool replaceDesign(string design) {
int[] designDimensions = getDesignDimensions(Engine.Utility.StringToDesignArray(design));
if(hiddenHeight != designDimensions[0] || hiddenWidth != designDimensions[1]) return false;
setDesign(design);
return true;
}
#endregion
#region Methods to set or reset GameObject's appearance
/// <summary>
/// NOT RECOMMENDED: this method set a new design without checking if the size of the new design is the same.
/// It does set the new width and height, but using this method can still lead to unexpected behaviours
/// </summary>
/// <param name="design">The new design as a string</param>
protected void setDesign(string design) {
//divido la stringa per capire quanto dev'essere grande la matrice per disegnare l'oggetto;
string[] designArray = Engine.Utility.StringToDesignArray(design);
int[] designDimensions = getDesignDimensions(designArray);
//imposto le dimensioni del gameObject
hiddenHeight = designDimensions[0];
hiddenWidth = designDimensions[1];
//compongo la matrice riempendola con i caratteri della stringa
repMatrix = new char[hiddenHeight, hiddenWidth];
for(int y = 0; y < designArray.Length; y++) {
for(int x = 0; x < designArray[y].Length; x++) {
repMatrix[y, x] = designArray[y][x];
}
}
}
/// <summary>
/// NOT RECOMMENDED: this method set a new design without checking if the size of the new design is the same.
/// It does set the new width and height, but using this method can still lead to unexpected behaviours
/// </summary>
/// <param name="design">The new design as a char matrix</param>
protected void setDesign(char[,] design) {
repMatrix = design;
hiddenHeight = repMatrix.GetLength(0);
hiddenWidth = repMatrix.GetLength(1);
}
#endregion
#region Methods to get informations about the Game
/// <summary>
/// Get the dimensions of a string design
/// </summary>
/// <param name="design">The design that should be checked</param>
/// <returns></returns>
protected int[] getDesignDimensions(string[] design) {
int height = design.Length;
int width = design[0].Length;
foreach(string line in design) {
width = line.Length > width ? line.Length : width;
}
return new int[] { height, width };
}
#endregion
#endregion
#region Methods related to the movement of the GameObject
/// <summary>
/// This function moves the gameObject of a certain offset
/// </summary>
/// <param name="xOffset">The offset of the movement of the GameObject on the X axis</param>
/// <param name="yOffset">The offset of the movement of the GameObject on the Y axis</param>
/// <returns>This return true if the gameObject moved without collisions, false otherwise</returns>
public virtual bool move(int xOffset, int yOffset) {
//muovo il GameObject controllando che non esca dal Frame
xPosition += xOffset;
yPosition += yOffset;
if(xPosition > Engine.FRAME_WIDTH +1 -hiddenWidth || xPosition < 0 || yPosition > Engine.FRAME_HEIGHT +1 - hiddenHeight || yPosition < 0) {
xPosition -= xOffset;
yPosition -= yOffset;
hiddenLastCollision = null;
return false;
}
//controllo se ha sbattuto contro qualcosa
if((hiddenLastCollision = checkCollisions()) != null) {
if(physicalObject) {
xPosition -= xOffset;
yPosition -= yOffset;
}
OnCollisionEnter();
hiddenLastCollision.hiddenLastCollision = this;
hiddenLastCollision.OnCollisionEnter();
return false;
}
return true;
}
#endregion
#region Methods related to collisions
/// <summary>
/// Check if this object collides with some other objects.
/// </summary>
/// <returns>This method return ONLY the first gameObject found that collides, NOT ALL OF THEM.</returns>
protected GameObject checkCollisions() {
foreach(GameObject gameObject2 in Engine.findGameObjects()) {
if(this != gameObject2) {
if(this.collide(gameObject2)) {
return gameObject2;
}
}
}
return null;
}
/// <summary>
/// Check if this GameObject is colliding with another one, WITHOUT firing OnCollisionEnter()
/// </summary>
/// <param name="gameObject2"></param>
/// <returns></returns>
public bool collide(GameObject gameObject2) {
//distanze calcolate dai centri
float distX = Math.Abs((xPosition+ hiddenWidth/2f) - (gameObject2.xPosition + gameObject2.hiddenWidth/2f));
float distY = Math.Abs((yPosition + hiddenHeight/2f) - (gameObject2.yPosition + gameObject2.hiddenHeight/2f));
//metà della somma delle dimensioni
float widthSum = (hiddenWidth + gameObject2.hiddenWidth)/2f;
float heightSum = (hiddenHeight + gameObject2.hiddenHeight)/2f;
if(distX >= widthSum || distY >= heightSum) {
return false;
}
return true;
}
#endregion
#region Virtual Methods that should be overridden by the Game Programmer in his scripts
/// <summary>
/// This method is called every game cycle, here you should check your timers, input flags, process movements and so on.
/// </summary>
public virtual void Update() { }
/// <summary>
/// This method is called once after the Object is created and before the first Update.
/// </summary>
public virtual void Start() { }
/// <summary>
/// This method is called when this object is colliding with another Object, the collision data are in the lastCollision property
/// </summary>
public virtual void OnCollisionEnter() { }
#endregion
}
}