Skip to content

Commit a80035b

Browse files
committed
Completed smarter pathfinding for agressive monsters
Monsters acting agressive will now path to the player using the A* algorithm. Re-did how the project source files are added to the executable.
1 parent d08b770 commit a80035b

File tree

10 files changed

+400
-123
lines changed

10 files changed

+400
-123
lines changed

CMakeLists.txt

Lines changed: 26 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -199,69 +199,6 @@ else ()
199199
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -D__FNAME__=__FILE__")
200200
endif ()
201201
202-
# PROGRAMS:
203-
add_executable(breakhack
204-
src/main.c
205-
src/texture.c
206-
src/screenresolution.c
207-
src/sprite.c
208-
src/sprite_util.c
209-
src/util.c
210-
src/event.c
211-
src/player.c
212-
src/save.c
213-
src/map.c
214-
src/map_lua.c
215-
src/camera.c
216-
src/timer.c
217-
src/roommatrix.c
218-
src/position.c
219-
src/monster.c
220-
src/stats.c
221-
src/actiontext.c
222-
src/random.c
223-
src/time.c
224-
src/linkedlist.c
225-
src/hashtable.c
226-
src/gui.c
227-
src/item.c
228-
src/item_builder.c
229-
src/pointer.c
230-
src/gui_button.c
231-
src/particle_engine.c
232-
src/particle_emitter.c
233-
src/menu.c
234-
src/collisions.c
235-
src/keyboard.c
236-
src/input.c
237-
src/mixer.c
238-
src/io_util.c
239-
src/physfsrwops.c
240-
src/skillbar.c
241-
src/texturecache.c
242-
src/skill.c
243-
src/projectile.c
244-
src/vector2d.c
245-
src/map_room_modifiers.c
246-
lib/sqlite3/sqlite3.c
247-
src/db.c
248-
src/settings.c
249-
src/actiontextbuilder.c
250-
src/animation.c
251-
src/trap.c
252-
src/artifact.c
253-
src/screen.c
254-
src/hiscore.c
255-
src/object.c
256-
src/gui_util.c
257-
src/tooltip.c
258-
src/tooltip_manager.c
259-
src/gamecontroller.c
260-
src/effect_util.c
261-
${STEAM_SOURCES}
262-
)
263-
set_property(TARGET breakhack PROPERTY C_STANDARD 99)
264-
265202
set(INCLUDE_DIRS
266203
${PROJECT_BINARY_DIR}
267204
${SDL_INCLUDE_DIR}
@@ -274,11 +211,15 @@ set(INCLUDE_DIRS
274211
lib/bh_random/src
275212
lib/checksum/src
276213
)
277-
278-
target_include_directories(breakhack PRIVATE ${INCLUDE_DIRS})
214+
# PROGRAMS:
215+
add_executable(${PROJECT_NAME})
216+
add_subdirectory(src)
217+
add_subdirectory(lib/sqlite3)
218+
set_property(TARGET ${PROJECT_NAME} PROPERTY C_STANDARD 99)
219+
target_include_directories(${PROJECT_NAME} PRIVATE ${INCLUDE_DIRS})
279220
280221
if (NOT MSVC)
281-
set_target_properties(breakhack PROPERTIES COMPILE_FLAGS
222+
set_target_properties(${PROJECT_NAME} PROPERTIES COMPILE_FLAGS
282223
-std=gnu11 -pedantic -Wall -Wextra -Wshadow
283224
-Wpointer-arith -Wcast-qual -Wstrict-prototypes
284225
-Wmissing-prototypes -Wconversion -Wno-sign-conversion
@@ -297,7 +238,7 @@ endif ()
297238
# Sqlite has some warnings that I we don't need to see
298239
set_source_files_properties(lib/sqlite3/sqlite3.c COMPILE_FLAGS -w)
299240
300-
target_link_libraries(breakhack
241+
target_link_libraries(${PROJECT_NAME}
301242
${CMAKE_DL_LIBS} # Sqlite needs DL libs
302243
${SDL_LIBRARY}
303244
${SDL_IMAGE_LIBRARY}
@@ -310,19 +251,19 @@ target_link_libraries(breakhack
310251
)
311252
312253
if (STEAM)
313-
target_link_libraries(breakhack
254+
target_link_libraries(${PROJECT_NAME}
314255
${STEAMWORKS_LIBRARY}
315256
steamworks_c_wrapper
316257
)
317258
endif ()
318259
319260
if (MSVC)
320-
set_target_properties(breakhack PROPERTIES LINK_FLAGS_DEBUG "/SUBSYSTEM:CONSOLE /NODEFAULTLIB:MSVCRTD")
321-
set_target_properties(breakhack PROPERTIES COMPILE_DEFINITIONS_DEBUG "_CONSOLE")
322-
set_target_properties(breakhack PROPERTIES LINK_FLAGS_RELWITHDEBINFO "/SUBSYSTEM:CONSOLE")
323-
set_target_properties(breakhack PROPERTIES COMPILE_DEFINITIONS_RELWITHDEBINFO "_CONSOLE")
324-
set_target_properties(breakhack PROPERTIES LINK_FLAGS_RELEASE "/SUBSYSTEM:WINDOWS /NODEFAULTLIB:MSVCRTD")
325-
set_target_properties(breakhack PROPERTIES LINK_FLAGS_MINSIZEREL "/SUBSYSTEM:WINDOWS")
261+
set_target_properties(${PROJECT_NAME} PROPERTIES LINK_FLAGS_DEBUG "/SUBSYSTEM:CONSOLE /NODEFAULTLIB:MSVCRTD")
262+
set_target_properties(${PROJECT_NAME} PROPERTIES COMPILE_DEFINITIONS_DEBUG "_CONSOLE")
263+
set_target_properties(${PROJECT_NAME} PROPERTIES LINK_FLAGS_RELWITHDEBINFO "/SUBSYSTEM:CONSOLE")
264+
set_target_properties(${PROJECT_NAME} PROPERTIES COMPILE_DEFINITIONS_RELWITHDEBINFO "_CONSOLE")
265+
set_target_properties(${PROJECT_NAME} PROPERTIES LINK_FLAGS_RELEASE "/SUBSYSTEM:WINDOWS /NODEFAULTLIB:MSVCRTD")
266+
set_target_properties(${PROJECT_NAME} PROPERTIES LINK_FLAGS_MINSIZEREL "/SUBSYSTEM:WINDOWS")
326267
endif (MSVC)
327268
328269
# TESTS:
@@ -344,6 +285,11 @@ IF (CMOCKA_FOUND AND NOT OSX AND NOT CLANG)
344285
target_link_libraries(test_hashtable ${CMOCKA_LIBRARY})
345286
add_test(test_hashtable test_hashtable)
346287
288+
add_executable(test_pos_heap test/test_pos_heap.c src/pos_heap.c src/util.c)
289+
target_include_directories(test_pos_heap PRIVATE ${INCLUDE_DIRS})
290+
target_link_libraries(test_pos_heap ${CMOCKA_LIBRARY})
291+
add_test(test_pos_heap test_pos_heap)
292+
347293
add_executable(test_input test/test_input.c src/input.c src/keyboard.c)
348294
target_include_directories(test_input PRIVATE ${INCLUDE_DIRS})
349295
target_link_libraries(test_input
@@ -413,7 +359,7 @@ endif ()
413359
include(InstallRequiredSystemLibraries)
414360
415361
if (NOT STEAM)
416-
INSTALL(TARGETS breakhack
362+
INSTALL(TARGETS ${PROJECT_NAME}
417363
COMPONENT Release
418364
RUNTIME DESTINATION bin/
419365
)
@@ -422,7 +368,7 @@ if (NOT STEAM)
422368
DESTINATION share/breakhack/
423369
)
424370
else ()
425-
INSTALL(TARGETS breakhack
371+
INSTALL(TARGETS ${PROJECT_NAME}
426372
COMPONENT Release
427373
RUNTIME DESTINATION .
428374
)
@@ -432,7 +378,7 @@ else ()
432378
)
433379
endif ()
434380
435-
set(CPACK_INSTALL_CMAKE_PROJECTS "${PROJECT_BINARY_DIR};breakhack;Release;.")
381+
set(CPACK_INSTALL_CMAKE_PROJECTS "${PROJECT_BINARY_DIR};${PROJECT_NAME};Release;.")
436382
set(CPACK_PACKAGE_NAME "BreakHack")
437383
set(CPACK_PACKAGE_VENDOR "OliveShark")
438384
set(CPACK_PACKAGE_DESCRIPTION_FILE "${CMAKE_CURRENT_SOURCE_DIR}/README.md")
@@ -444,9 +390,9 @@ set(CPACK_PACKAGE_VERSION_PATCH ${breakhack_PATCH_VERSION})
444390
set(CPACK_PACKAGE_INSTALL_DIRECTORY "BreakHack")
445391
set(CPACK_PACKAGE_CHECKSUM "MD5")
446392
if (WIN32)
447-
set(CPACK_PACKAGE_FILE_NAME "breakhack-${CPACK_PACKAGE_VERSION_MAJOR}.${CPACK_PACKAGE_VERSION_MINOR}.${CPACK_PACKAGE_VERSION_PATCH}-win32")
393+
set(CPACK_PACKAGE_FILE_NAME "${PROJECT_NAME}-${CPACK_PACKAGE_VERSION_MAJOR}.${CPACK_PACKAGE_VERSION_MINOR}.${CPACK_PACKAGE_VERSION_PATCH}-win32")
448394
else ()
449-
set(CPACK_PACKAGE_FILE_NAME "breakhack-${CPACK_PACKAGE_VERSION_MAJOR}.${CPACK_PACKAGE_VERSION_MINOR}.${CPACK_PACKAGE_VERSION_PATCH}")
395+
set(CPACK_PACKAGE_FILE_NAME "${PROJECT_NAME}-${CPACK_PACKAGE_VERSION_MAJOR}.${CPACK_PACKAGE_VERSION_MINOR}.${CPACK_PACKAGE_VERSION_PATCH}")
450396
endif ()
451397
if (STEAM)
452398
set(CPACK_INCLUDE_TOPLEVEL_DIRECTORY OFF)
@@ -455,7 +401,7 @@ endif ()
455401
456402
if(UNIX)
457403
set(CPACK_GENERATOR TGZ ZIP)
458-
set(CPACK_STRIP_FILES breakhack)
404+
set(CPACK_STRIP_FILES ${PROJECT_NAME})
459405
set(CPACK_SOURCE_STRIP_FILES "")
460406
elseif(WIN32)
461407
set(CPACK_NSIS_ENABLE_UNINSTALL_BEFORE_INSTALL ON)

lib/sqlite3/CMakeLists.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
file(GLOB_RECURSE SOURCE_FILES CONFIGURE_DEPENDS *.c)
2+
3+
target_sources(${PROJECT_NAME} PRIVATE ${SOURCE_FILES})

src/CMakeLists.txt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
file(GLOB SOURCE_FILES CONFIGURE_DEPENDS *.c)
2+
3+
if (STEAM)
4+
file(GLOB SOURCE_FILES CONFIGURE_DEPENDS steam/*.c)
5+
endif ()
6+
7+
target_sources(${PROJECT_NAME} PRIVATE ${SOURCE_FILES})

src/defines.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ typedef int64_t Sint64;
9292
typedef uint64_t Uint64;
9393

9494
typedef enum Direction_t {
95-
UP, DOWN, LEFT, RIGHT
95+
UP, DOWN, LEFT, RIGHT, INVALID
9696
} Direction;
9797

9898
typedef enum GameMode {

src/monster.c

Lines changed: 96 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
#include "trap.h"
3838
#include "object.h"
3939
#include "mixer.h"
40+
#include "pos_heap.h"
4041

4142
static void
4243
monster_set_sprite_clip_for_current_state(Monster *m)
@@ -371,67 +372,117 @@ monster_drunk_walk(Monster *m, RoomMatrix *rm)
371372
}
372373
}
373374

375+
/**
376+
* \brief Get the first move in the provided path
377+
*/
378+
static Direction
379+
find_first_in_path(const Position *from, const Position *dest, const Position *start)
380+
{
381+
Position current = *dest;
382+
Position next = *dest;
383+
while (!position_equals(&next, start)) {
384+
current = next;
385+
next = from[current.x + current.y * MAP_ROOM_WIDTH];
386+
}
387+
388+
Vector2d dir = { current.x - next.x, current.y - next.y };
389+
if (dir.x > 0)
390+
return RIGHT;
391+
else if (dir.x < 0)
392+
return LEFT;
393+
else if (dir.y > 0)
394+
return DOWN;
395+
else
396+
return UP;
397+
}
398+
399+
/* Manhattan distance */
400+
#define MDIST(p1, p2) abs(p1.x - p2.x) + abs(p1.y - p2.y)
401+
402+
/**
403+
* \brief A* path finding algorithm
404+
*
405+
* This is a basic A* algorithm implementation usinga heap for the open_set.
406+
* The pathing will avoid dangerous, lethal and blocked tiles but not tiles
407+
* with monsters on so there can still be some stacking there.
408+
*/
374409
static Direction
375-
get_optimal_move_towards(Monster *m, RoomMatrix *rm, const Position *dest)
410+
get_optimal_move_towards(Monster *m, RoomMatrix *rm, const Position dest)
376411
{
412+
Direction ret_val = INVALID;
377413
int x_dist, y_dist;
378-
Position mPos;
414+
const Position start = position_to_matrix_coords(&m->sprite->pos);
415+
const Vector2d directions[] = {
416+
VECTOR2D_UP,
417+
VECTOR2D_DOWN,
418+
VECTOR2D_LEFT,
419+
VECTOR2D_RIGHT,
420+
};
379421

380-
mPos = position_to_matrix_coords(&m->sprite->pos);
422+
PHeap open_set;
381423

382-
unsigned int currentScore = 100;
383-
unsigned int chosenDirection = UP;
384-
385-
for (unsigned int i = 0; i < 4; ++i) {
386-
Position next = mPos;
387-
unsigned int nextScore = 0;
388-
389-
switch (i) {
390-
case UP:
391-
next.y -= 1;
392-
break;
393-
case DOWN:
394-
next.y += 1;
395-
break;
396-
case LEFT:
397-
next.x -= 1;
398-
break;
399-
case RIGHT:
400-
next.x += 1;
401-
break;
402-
}
424+
uint16_t gScore[MAP_ROOM_WIDTH * MAP_ROOM_HEIGHT];
425+
uint16_t fScore[MAP_ROOM_WIDTH * MAP_ROOM_HEIGHT];
403426

404-
if (position_equals(&next, dest)) {
405-
chosenDirection = (Direction) i;
406-
break;
407-
}
427+
memset(gScore, 0xFF, sizeof(gScore));
428+
memset(fScore, 0xFF, sizeof(fScore));
429+
430+
Position from[MAP_ROOM_WIDTH * MAP_ROOM_HEIGHT];
431+
bool visited[MAP_ROOM_WIDTH * MAP_ROOM_HEIGHT] = { false };
432+
const size_t WIDTH = MAP_ROOM_WIDTH;
408433

409-
if (!position_in_roommatrix(&next))
410-
continue;
434+
/* Pre-allocate all the space we'll need and add start node */
435+
pheap_init(&open_set, MAP_ROOM_WIDTH * MAP_ROOM_HEIGHT);
436+
pheap_insert(&open_set, start, MDIST(start, dest));
437+
visited[start.x + start.y * WIDTH] = true;
411438

412-
x_dist = abs(next.x - dest->x);
413-
y_dist = abs(next.y - dest->y);
439+
gScore[start.x + start.y * WIDTH] = 0;
440+
fScore[start.x + start.y * WIDTH] = MDIST(start, dest);
414441

415-
if (rm->spaces[next.x][next.y].occupied
416-
|| rm->spaces[next.x][next.y].lethal
417-
|| rm->spaces[next.x][next.y].trap) {
418-
nextScore += 50;
442+
while (open_set.size > 0) {
443+
Position current = pheap_pop(&open_set);
444+
if (position_equals(&current, &dest)) {
445+
ret_val = find_first_in_path(from, &dest, &start);
446+
goto out;
419447
}
420448

421-
nextScore += x_dist > y_dist ? x_dist : y_dist;
422-
if (nextScore < currentScore) {
423-
currentScore = nextScore;
424-
chosenDirection = (Direction) i;
449+
for (size_t i = 0; i < 4; i++) {
450+
Position next = {
451+
current.x + directions[i].x,
452+
current.y + directions[i].y,
453+
};
454+
455+
if (!position_equals(&next, &dest)) {
456+
if (!position_in_roommatrix(&next)
457+
|| rm->spaces[next.x][next.y].occupied
458+
|| rm->spaces[next.x][next.y].lethal
459+
|| rm->spaces[next.x][next.y].trap) {
460+
continue;
461+
}
462+
}
463+
464+
uint32_t temp_score = 1 + gScore[current.x + current.y * WIDTH];
465+
if (temp_score < gScore[next.x + next.y * WIDTH]) {
466+
from[next.x + next.y * WIDTH] = current;
467+
gScore[next.x + next.y * WIDTH] = temp_score;
468+
fScore[next.x + next.y * WIDTH] = temp_score + MDIST(next, dest);
469+
if (!visited[next.x + next.y * WIDTH]) {
470+
visited[next.x + next.y * WIDTH] = true;
471+
pheap_insert(&open_set, next, fScore[next.x + next.y * WIDTH]);
472+
}
473+
}
425474
}
426475
}
427476

428-
return chosenDirection;
477+
out:
478+
pheap_destroy(&open_set);
479+
return ret_val;
429480
}
430481

431482
static void
432483
monster_agressive_walk(Monster *m, RoomMatrix *rm)
433484
{
434-
unsigned int chosenDirection = get_optimal_move_towards(m, rm, &rm->playerRoomPos);
485+
unsigned int chosenDirection = get_optimal_move_towards(m, rm, rm->playerRoomPos);
435486

436487
switch (chosenDirection) {
437488
case UP:
@@ -446,6 +497,9 @@ monster_agressive_walk(Monster *m, RoomMatrix *rm)
446497
case RIGHT:
447498
move(m, rm, VECTOR2D_RIGHT);
448499
break;
500+
case INVALID:
501+
default:
502+
break;
449503
}
450504
}
451505

0 commit comments

Comments
 (0)