1: /* File: snake3d.h
2: Submitted as part of Project 4 for EECS 672, Spring 2007
3: by Douglas McClendon, KUID 0536810
4: */
5:
6: /*
7: * snake3d.h
8: *
9: * 3-D snake game
10: *
11: * Doug McClendon
12: */
13:
14: /*
15: * Defines and Constants
16: */
17:
18: // how many frame's fps number to store, for looking at the average
19: #define FPS_ARRAY_SIZE 256
20: #define FPS_INIT_VALUE 60
21:
22: // for code readability in functions which got passed snake_id
23: #define SNAKE (game.snake[snake_id])
24:
25: // HACK PACKAGING
26: #define SCORE_FILE /usr/local/share/games/snake3d/scores
27:
28: // hi score parameters
29: #define NUM_HI_SCORES 5
30: #define DEFAULT_CLASSIC_HI_SCORE 9
31: #define DEFAULT_QUEST_HI_SCORE 9
32:
33: // Animation parameters for cycling the hi score display between gametypes.
34: // Values are in seconds and must be integers (for now)
35: #define HI_SCORE_FADE_TIME 1
36: #define HI_SCORE_DISPLAY_TIME 10
37: // derived parameters
38: #define HI_SCORE_FADE_TIME_USEC (HI_SCORE_FADE_TIME * 1000000)
39: #define HI_SCORE_DISPLAY_TIME_USEC (HI_SCORE_DISPLAY_TIME * 1000000)
40: #define HI_SCORE_CYCLE (2 * HI_SCORE_DISPLAY_TIME + 4 * HI_SCORE_FADE_TIME)
41: #define HI_SCORE_CYCLE_USEC (HI_SCORE_CYCLE * 1000000)
42:
43: // this leaves open the idea of things like 10 snakes at a time
44: #define MAX_SNAKES 2
45:
46: // readable ascii values
47: #define ENTERKEY 13
48: #define ESCAPEKEY 27
49:
50: // default controls
51: #define P1_UPKEY 'w'
52: #define P1_DOWNKEY 's'
53: #define P1_LEFTKEY 'a'
54: #define P1_RIGHTKEY 'd'
55:
56: #define P2_UPKEY 'i'
57: #define P2_DOWNKEY 'k'
58: #define P2_LEFTKEY 'j'
59: #define P2_RIGHTKEY 'l'
60:
61: // back is increase z, right is increase x, up is increase y
62: #define TRANSLATE_CAMERA_FORWARD '!'
63: #define TRANSLATE_CAMERA_BACK '@'
64: #define TRANSLATE_CAMERA_LEFT '#'
65: #define TRANSLATE_CAMERA_RIGHT '$'
66: #define TRANSLATE_CAMERA_DOWN '%'
67: #define TRANSLATE_CAMERA_UP '^'
68:
69: #define DEBUG_DUMP_MATRIX_KEY '~'
70:
71: #define MAX_GAME_WIDTH 128
72: #define MAX_GAME_HEIGHT 128
73:
74: // These define the virtual
75: // range of the geometry I draw, i.e. all vertices lie within
76: // a rectangular solid defined by these values
77: //
78: // QUESTION(OpenGL): What range should I draw geometry within to get
79: // the most precision. I'm doing +/-100.0f. Should I do +/- the max
80: // float value??? Perhaps this is a question of the precision of IEEE
81: // floats
82: #define SCREEN_WIDTH 100.0f
83: #define SCREEN_HEIGHT 100.0f
84: #define SCREEN_DEPTH 100.0f
85:
86: /*
87: * Typedefs
88: */
89:
90: typedef enum {LEFT, RIGHT, UP, DOWN} Direction;
91:
92: typedef enum {CLASSIC, VS, QUEST} GameType;
93:
94: // QUESTION(C): Can I have the same values in two enumerated types without
95: // them conflicting? Or are enum's implemented as integers&#define's?
96: typedef enum {
97: MENU,
98: INSTRUCTIONS,
99: GAME_CLASSIC,
100: GAME_VS,
101: GAME_QUEST_LEVEL1,
102: GAME_QUEST_LEVEL2,
103: GAME_PAUSED,
104: GAME_OVER,
105: ENTER_INITIALS,
106: HI_SCORES,
107: OFF,
108: } GameState;
109:
110: // QUESTION(C): I use MAINMENUSTATES here as a way to find the integer number
111: // of elements in the enumerated type. Is there a better way?
112: typedef enum {
113: MENU_INSTRUCTIONS,
114: MENU_CLASSIC,
115: MENU_VS,
116: MENU_QUEST,
117: MENU_HI_SCORES,
118: MENU_QUIT,
119: MAINMENUSTATES,
120: } MainMenuState;
121:
122: typedef struct HiScoreEntry_tag{
123: int score;
124: char initials[3];
125: } HiScoreEntry;
126:
127: typedef struct GameHistory_tag{
128: HiScoreEntry classic_hiscores[NUM_HI_SCORES];
129: HiScoreEntry quest_hiscores[NUM_HI_SCORES];
130: } GameHistory;
131:
132: typedef struct StateDataClassic_tag{
133: int score;
134: } StateDataClassic;
135:
136: typedef struct StateDataQuest_tag{
137: int score;
138: } StateDataQuest;
139:
140: typedef struct StateDataEnter_Initials_tag{
141: GameType gametype;
142: char gametype_string[32];
143: int score, place;
144: HiScoreEntry *hiscores;
145:
146: int current_initial;
147: } StateDataEnter_Initials;
148:
149: typedef struct StateDataHi_Scores_tag{
150: struct timeval starttime;
151: } StateDataHi_Scores;
152:
153: typedef struct StateData_tag{
154: StateDataClassic classic;
155: StateDataQuest quest;
156: StateDataEnter_Initials enter_initials;
157: StateDataHi_Scores hi_scores;
158: } StateData;
159:
160: /*
161: * This defines a position on the "game board", typically
162: * of size 20 X 11. X increases left to right, starting with 0
163: * Y increases bottome to top, starting with 0
164: */
165: typedef struct position_tag{
166: int x;
167: int y;
168: } Position;
169:
170: typedef struct matrixnode{
171: // ID of the snake inhabiting this coordinate, 0 if uninhabited
172: int snake;
173: // rotational offset of inhabitant snake segment, in degrees if any
174: // 0<=sr<360
175: float snake_segment_angle_value;
176: // velocity, in revolutions per second
177: float snake_segment_angle_velocity;
178: // pointer to the next part of the snake (towards the head) that occupies
179: // this coordinate. Meaningless if snake==0, or a snake's head points to
180: // this node
181: Position next;
182: } MatrixNode;
183:
184: typedef struct snake{
185: // next_dir is used to check for valid input,
186: // i.e. so the player cannot reverse direction
187: Direction current, next_dir;
188: Position head;
189: Position tail;
190: // last_tail is used for the rendering of frames between
191: // gameticks, to tell what direction a snake segment is moving in
192: Position last_tail;
193: int belly;
194: int length;
195: int active;
196: // non-intuitive, a solution to rendering the snake
197: int tail_hidden;
198: } Snake;
199:
200: typedef struct GameOptionsClassic_tag{
201: int init_snake_length;
202: int init_snake_belly;
203: // the game matrix
204: int game_width;
205: int game_height;
206: int food_amount;
207: // the amount of time between motion in the game, in usec,
208: // must be less than 1,000,000
209: int timeslice;
210: } GameOptionsClassic;
211:
212: typedef struct GameOptionsVS_tag{
213: int init_snake_length;
214: int init_snake_belly;
215: int game_width;
216: int game_height;
217: int food_amount;
218: int timeslice;
219: } GameOptionsVS;
220:
221: typedef struct GameOptionsQuestLevel1_tag{
222: int length, food;
223: int width, height;
224: } GameOptionsQuestLevel1;
225:
226: typedef struct GameOptionsQuestLevel2_tag{
227: int length, food;
228: int width, height;
229: } GameOptionsQuestLevel2;
230:
231: typedef struct GameOptionsQuest_tag{
232: GameOptionsQuestLevel1 level1;
233: GameOptionsQuestLevel2 level2;
234: int timeslice;
235: } GameOptionsQuest;
236:
237: typedef struct GameOptions_tag{
238: GameOptionsClassic classic;
239: GameOptionsVS vs;
240: GameOptionsQuest quest;
241:
242: // the window size
243: int win_width;
244: int win_height;
245: int use_sound;
246: int fullscreen;
247: int show_fps;
248:
249: // HACK HACK HACK get rid of this
250: int num_snakes;
251:
252: GLubyte snake_segment_color[MAX_SNAKES][4];
253: } GameOptions;
254:
255: typedef struct Game_tag{
256: // current and previous states
257: GameState state;
258: GameState previous_state;
259:
260: // keep track of this so that when we return to the menu
261: // the same thing is highlighted as before
262: MainMenuState mainmenu;
263:
264: // Seperate some globals by which state they are used in. In the
265: // case of a multi-state gametype, there are members like GAME_CLASSIC
266: StateData statedata;
267:
268: GameOptions options;
269:
270: Position food;
271: Snake snake[MAX_SNAKES];
272: // The playing board
273: MatrixNode matrix[MAX_GAME_WIDTH][MAX_GAME_HEIGHT];
274: // The current gamespeed, i.e. the amount of time between motion in the
275: // game, in usec, must be less than 1,000,000
276: int timeslice;
277: int game_width, game_height;
278: int food_amount;
279:
280: /*
281: * Notes on gametime, and what happens during a pause
282: * --------------------------------------------------
283: *
284: * The concept of time is maintained with two game state variables,
285: * numticks, and time_of_last_gametick. TOLG is the exact moment at
286: * which the snake was last moved, or the game was "ticked". Numticks
287: * is incremented every tick. During pause however, we changed the sense
288: * of the variable TOLG into the amount of time between the time of the
289: * pause, and the time of the last game tick. Then when the game is
290: * unpuased, we use this value and the current time to compute a new value
291: * for TOLG which will maintain a consistency of what TOLG means. This
292: * results in smooth rendering.
293: */
294:
295: // time tracking
296: int numticks;
297: struct timeval time_of_last_gametick;
298:
299: //
300: // Notes on fps tracking
301: // ---------------------
302: // S3D's FPS tracking involves-
303: // -the number of frames rendered since beginning of a game (frames_rendered)
304: // -the exact time the last frame render occurred(time_of_last_render)
305: // -the framerate of _just_ the last frame (fps_last_frame)
306: // -the framerate average of the last FPS_ARRAY_SIZE frames, calculated
307: // using a rotating buffer of framerates(fps_history,
308: // fps_history_pointer, fps_history_total)
309: // -a pseudo "worst framerate of the last FPS_ARRAY_SIZE frames"
310: // (fps_recent_worst_value, fps_recent_worst_index)
311: int frames_rendered;
312: struct timeval time_of_last_render; // time of last frame render
313: int fps_last_frame; // current rendering rate, only for the last frame
314: int *fps_history;
315: int fps_history_pointer;
316: int fps_history_total;
317: int fps_recent_worst_value;
318: int fps_recent_worst_index;
319:
320: // various frequently used (by rendering) values calculated at the start of
321: // each game
322: float render_width;
323: float render_height;
324: float screen_unit_length;
325: } Game;
326:
327: typedef struct JoystickState_tag{
328: int j1_fd;
329: int j1_axis[2];
330: char j1_button[10];
331: int j1_button_buffer[10];
332:
333: int j2_fd;
334: int j2_axis[2];
335: char j2_button[10];
336: int j2_button_buffer[10];
337: } JoystickState;
338:
339: /*
340: * Macros
341: */
342:
343: // subtract one timeval from another, result in usec
344: #define TIMEDIFF(TIMEA, TIMEB) \
345: (1000000 * (TIMEA.tv_sec - TIMEB.tv_sec) + (TIMEA.tv_usec - TIMEB.tv_usec))
346: #define MAX(X, Y) ( (X < Y) ? Y : X )
347: #define MIN(X, Y) ( (X < Y) ? X : Y )
348:
349: /*
350: * Prototypes
351: */
352:
353: //
354: // joystick engine functions
355: //
356: void JoystickInit(void);
357: void JoystickFini(void);
358:
359: //
360: // registered callbacks
361: //
362: void reshape(int w, int h);
363:
364: // timer call, one for each game(playable) state
365: void timer_classic(int value);
366: void timer_vs(int value);
367: void timer_quest_level1(int value);
368: void timer_quest_level2(int value);
369:
370: // one key function for each game.state
371: void key_shared(unsigned char k, int x, int y);
372: void key_menu(unsigned char k, int x, int y);
373: void key_game_shared(unsigned char k, int x, int y);
374: void key_game_paused(unsigned char k, int x, int y);
375: void key_game_over(unsigned char k, int x, int y);
376: void key_enter_initials(unsigned char k, int x, int y);
377: void key_hi_scores(unsigned char k, int x, int y);
378:
379: // only one special and joy function which just translate the
380: // special key or joystick event into a normal key. It then
381: // calls key() which is a wrapper wich calls key_* according
382: // to current gamestate
383: void joy(void);
384: void special(int k, int x, int y);
385: void key(unsigned char k, int x, int y);
386:
387: // currently every state uses idle_shared which just checks for joy events,
388: // and translates them into key() calls
389: void idle_shared(void);
390:
391: // one draw function for each game.state and nothing placeholder
392: void draw_nothing(void);
393: void draw_menu(void);
394: void draw_game(void);
395: void draw_game_paused(void);
396: void draw_game_over(void);
397: void draw_enter_initials(void);
398: void draw_hi_scores(void);
399:
400: // drawing subroutines (called from draw_*)
401: void draw_arena(void);
402: void draw_snake(int snake_id, float partial_factor);
403: void draw_food(void);
404: void draw_text(void);
405: void draw_brick(float x, float y);
406:
407: // other game functions
408: void GraphicsInit(void);
409: void S3DInit(void);
410: void SetState(GameState new_state);
411: void SetTimePausing(void);
412: void SetTimeIfUnpausing(void);
413: void movesnake(int snake_id);
414: void movesnake_gameover(int snake_id);
415: void HideTail(int snake_id);
416: void ShrinkTail(int snake_id);
417: void UnhideTail(int snake_id);
418: int InsertNewHiScore(void);
419: void StartNewGameClassic(void);
420: void StartNewGameVS(void);
421: void StartNewGameQuest(void);
422: void StartQuestLevel1(void);
423: void StartQuestLevel2(void);
424: void InitLevel(void);
425: void InitSnake(int snake_id);
426:
427: // History functions
428: void ReadGameHistory(void);
429: void WriteGameHistory(void);
430:
431: // usage
432: void Usage(void);
433:
434: // debug functions
435: void debug_dump_matrix(void);
436:
437: