summaryrefslogtreecommitdiff
path: root/echidna_ai.c
diff options
context:
space:
mode:
authorSilvan Jegen <s.jegen@gmail.com>2018-09-22 20:01:40 +0200
committerSilvan Jegen <s.jegen@gmail.com>2018-09-22 20:05:53 +0200
commit5d5bc3e446ee50601f98ca30d4f21172d6e75e29 (patch)
treeabdbe68d23373c095e2d50b2cd8f15af40dfdb65 /echidna_ai.c
Initial commit
Diffstat (limited to 'echidna_ai.c')
-rw-r--r--echidna_ai.c245
1 files changed, 245 insertions, 0 deletions
diff --git a/echidna_ai.c b/echidna_ai.c
new file mode 100644
index 0000000..64759a4
--- /dev/null
+++ b/echidna_ai.c
@@ -0,0 +1,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
+ }
+ }
+ }
+}
+
+