summaryrefslogtreecommitdiff
path: root/echidna_ai.c
blob: 64759a464e346f706cdae06b86e90d338eb297d8 (plain)
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

/* The echidna AI has three modes:
 * 
 * 	MODE_HUNT
 *    The echidna tries to close the space between itself and the player
 * 
 *  MODE_FOLLOW_WALL
 *    The echidna has hit a wall and is now following it
 * 
 *  MODE_CORNER
 *    This mode makes the monster move around a corner after it has been following a wall
 * 
 * See the developer documentation for more information about how this large algorithm works.
 */
 
// Give each of the modes a numerical value
#define MODE_HUNT 1
#define MODE_FOLLOW_WALL 2

// The echidna structure is declared in 'level.c' so it can be loaded


// is_square solid returns 1 if a square is not passable ( ie a wall or closed door )
// and 0 if it is ( ie floor, button )
int is_square_solid( struct level_struct *currentlevel, int xpos, int ypos )
{		
	
	switch ( currentlevel->squares[ xpos ][ ypos ] )
	{
		case T_WALL:         return 1;
		case T_BLUE_DOOR_UP: return 1; 
		case T_YELL_DOOR_UP: return 1;
		case T_RED_DOOR_UP:  return 1;
	
		// The default case occurs when none of the above are met
		default: return 0; // ie the square is walkable on
	}
}

// run_all_echidnaAI_onestep runs one 'step' of echidna AI for every echidna in a level
void run_all_echidnaAI_onestep( struct level_struct *currentlevel )
{
	// Ieterate through each and every echinda
	int ech_no; // Echidna number	 
	for ( ech_no = 0; ech_no < 10; ech_no++ )
	{
		struct echidna *current_echidna = &currentlevel->echidnas[ech_no];
		
		
		if ( current_echidna->this_echidna_exists == 1 )
		{
			
			// This echidna is alive and we can move it
			
			// How the echidna is going to move, similiar to the player code;
			signed int x = 0;
			signed int y = 0;
			
			
			/////////////////////////////////////////////////////////////////////////////////////////////// HUNT MODE
			if ( current_echidna->mode == MODE_HUNT )
			{
				// Find the orthoganol ( x and y, not true or diagonal ) distances between the echidna and player
				// NB abs() returns the absolute value of something, which fixes negative numbers by changing
				// them to be position
				int distx = abs( current_echidna->xpos - currentlevel->px );
				int disty = abs( current_echidna->ypos - currentlevel->py );
	
				if ( distx > disty ) 
				{
					
					// Now check whether the player is to the left or right of the echidna
					if ( currentlevel->px > current_echidna->xpos )
					{
						// Echidna is left of player
						x = 1;
					}
					else
					{
						// Echidna is right of player
						x = -1;
					}
				}
				else
				{
					// Is the player abov or below the echidna
					if ( currentlevel->py > current_echidna->ypos )
					{
						// Echidna is below player
						y = 1;
					}
					else
					{
						// Echidna is above player
						y = -1;
					}
				}
				
				// Now check if this is going to make the echidna try to walk into a wall
				// If it will, change mode to MODE_FOLLOW_WALL
				if ( is_square_solid( currentlevel, current_echidna->xpos + x, current_echidna->ypos + y ) == 1 )
				{
					current_echidna->mode = MODE_FOLLOW_WALL;
					
					// Make a decision about which way to move
					// This scheme ensures the echidna moves clockwise
					current_echidna->move_x = 0;
					current_echidna->move_y = 0;
					if      ( x ==  1 ) current_echidna->move_y =  1;
					else if ( x == -1 ) current_echidna->move_y = -1;
					else if ( y ==  1 ) current_echidna->move_x = -1;
					else if ( y == -1 ) current_echidna->move_x =  1;		
					
					// Make sure the echidna checks the wall its following
					// to see if it still exists
					current_echidna->check_x = x;
					current_echidna->check_y = y;
				}
			}
			
			///////////////////////////////////////////////////////////////////////////////////////////// FOLLOW WALL MODE
			if ( current_echidna->mode == MODE_FOLLOW_WALL )
			{
				
				// Check the wall we are following
				if ( is_square_solid( currentlevel, current_echidna->xpos + current_echidna->check_x, current_echidna->ypos + current_echidna->check_y ) == 1 )
				{
					// The wall is still there, so keep following it
					x = current_echidna->move_x;
					y = current_echidna->move_y;
					
					// Check if a new wall is in the way
					while ( is_square_solid( currentlevel, current_echidna->xpos + x, current_echidna->ypos + y ) == 1 )
					{
						// We need to follow another wall now.  Same clockwise logic as before
						current_echidna->check_x = x;
						current_echidna->check_y = y;
						if      ( x ==  1 ) { x = 0; y = 1; }
						else if ( x == -1 ) { x = 0; y =-1; }
						else if ( y ==  1 ) { x =-1; y = 0; }
						else if ( y == -1 ) { x = 1; y = 0; }
						current_echidna->move_x = x;
						current_echidna->move_y = y;
					}
				}
				else
				{
					// The wall has gone, so immediately go around the corner
					x = current_echidna->check_x;
					y = current_echidna->check_y;
					
					
					// And resume HUNT mode next time
					current_echidna->mode = MODE_HUNT;
				}
			}
			
			////////////////////////////////////////////// MOVE THE ECHIDNA
			
			// Move
			current_echidna->xpos = current_echidna->xpos + x;
			current_echidna->ypos = current_echidna->ypos + y;
			
			// Check if we have moved ontop of an Echidna, and if so then move back to where we started
			// ( it will appear as if we have not moved at all )
			
			int target_num;
			for ( target_num = 0; target_num < ech_no; target_num++ ) // Check all of the echidnas BEFORE the current one, so we can have right of way
			{
				struct echidna target_echidna = currentlevel->echidnas[target_num];
				if ( target_echidna.this_echidna_exists == 1 )
				{
					if ( target_echidna.xpos == current_echidna->xpos && target_echidna.ypos == current_echidna->ypos )
					{
						// We have a clash, so move back to the original square
						current_echidna->xpos = current_echidna->xpos - x;
						current_echidna->ypos = current_echidna->ypos - y;
					}
				}
			}
		}
	}
}



// Returns 1 if an echidna is in the same spot as the player
int check_echidna_proximity( struct level_struct *currentlevel )
{
	// Ieterate through eacha and every echinda
	int ech_no; // Echidna number	
	 

	for ( ech_no = 0; ech_no < 10; ech_no++ )
	{
		struct echidna currentechidna = currentlevel->echidnas[ech_no];
		
		// First make sure we are checking an echidna that ACTUALLY EXISTS
		// Until I wrote this code to check that, I was mysteriously dieing halfway
		// throught some levels.  Fun :)
		if ( currentechidna.this_echidna_exists == 1 )
		{
			if ( currentechidna.xpos == currentlevel->px && currentechidna.ypos == currentlevel->py ) // && means 'and'
			{
				// An echidna is atop a player
				return 1;
			}
		}
	}
	
	// No echidna is atop the player
	return 0;
}

// Echidnas are the onlt things that can toggler the red levers, and this loop checks to see if that need to happen
void check_echidna_on_switches( struct level_struct *currentlevel )
{
	// Ieterate through each and every echinda
	int ech_no; // Echidna number	
	 

	for ( ech_no = 0; ech_no < 10; ech_no++ )
	{
		struct echidna currentechidna = currentlevel->echidnas[ech_no];
		
		if ( currentechidna.this_echidna_exists == 1 )
		{
			// Get the square the echinda is standing on
			char square = currentlevel->squares[ currentechidna.xpos ][ currentechidna.ypos ] ;
			
			if ( square == T_RED_SWITCH_ON )
			{
				level_replace_tiles( currentlevel, T_RED_SWITCH_ON, T_RED_SWITCH_OFF ); // change ALL levers
				level_replace_tiles( currentlevel, T_RED_DOOR_UP, T_RED_DOOR_DOWN ); // open all red doors
			}
			else if ( square == T_RED_SWITCH_OFF )
			{
				level_replace_tiles( currentlevel, T_RED_SWITCH_OFF, T_RED_SWITCH_ON ); // change ALL levers
				level_replace_tiles( currentlevel, T_RED_DOOR_DOWN, T_RED_DOOR_UP ); // close all red doors
			}
		}
	}
}