Compare commits

...

70 Commits

Author SHA1 Message Date
George Sokianos c076098ad9 Fixed hiscore date and compiled with latest SDL 2023-07-04 20:52:03 +01:00
George Sokianos 5dd814e452 Prepare first release 2022-12-21 17:46:45 +00:00
George Sokianos 43c8bd610f Some more fixes and minor changes 2022-09-10 11:57:02 +01:00
George Sokianos 6a78b47ae6 Cleaned up the makefile.os4 2022-09-09 23:38:37 +01:00
George Sokianos 2b1fd35298 Added makefile for OS4 and some fixes to make texts show right 2022-08-31 22:39:46 +01:00
lprobert fc6bb50460
Fixes a bunch of lint errors 2022-08-24 16:00:13 +02:00
lprobert 2aba6c4338
Adds setup/teardown to Makefile 2022-08-24 15:26:02 +02:00
Linus Probert 39bc060a77
Adds maintenance badge 2021-10-05 09:28:01 +02:00
Linus Probert abef4ed9a6
Patches all CMakeLists.txt to new CMake version 2021-10-05 09:15:29 +02:00
Linus Probert 26c5448a30
Fixes linting and some lint warnings 2020-07-11 10:16:57 +02:00
Linus Probert 1a559741e2
Create FUNDING.yml 2020-05-27 21:25:12 +02:00
Linus Probert 38207e03d1 Patch version raised to 3 2020-03-04 21:51:31 +01:00
Linus Probert e636ec1e53 Don't delete saves from other architectures on death 2020-03-04 21:50:51 +01:00
417e7d3cc8 Patch version raised to 2 2020-03-04 17:06:57 +01:00
Linus Probert 4edeb17ddc Fixes cross save problems due to architecture differences 2020-03-04 17:03:02 +01:00
Linus Probert de770ccc9f Patch version raised to 1 2020-03-04 12:50:39 +01:00
Linus Probert 8a347790c1 Update major version 2020-03-04 12:50:20 +01:00
Linus Probert 5a9335576a Completes game saving and resuming 2020-03-03 23:50:11 +01:00
Linus Probert b385f6e73a Don't want to store compile commands like this 2020-03-03 13:57:44 +01:00
Linus Probert 4106a856fc Begin an idea for "continue game" functionality 2020-03-03 13:07:32 +01:00
Linus Probert 8221304df8 Fixes some odd indentations 2019-12-03 15:18:47 +01:00
Linus Probert 4ceb82f171 Fixes MAGICAL achievemnt 2019-06-10 11:37:13 +02:00
Linus Probert 9e5e1d0a1e Patch version raised to 2 2019-06-10 11:20:24 +02:00
Linus Probert 6d323ca500 Adds steam achievements and LB for the Mage class 2019-06-10 11:19:40 +02:00
Linus Probert 9aefed0d00 Adds an dev env setup script 2019-06-10 09:43:39 +02:00
Linus Probert f102e49daf Realeasenotes v3.1.1 2019-06-10 09:19:43 +02:00
Linus Probert 392edd843c Updates blink tooltip to include item pickups. 2019-06-07 07:45:36 +02:00
Boris Vanhoof 6584981d10 remove unnecessary pickup 2019-06-06 23:56:15 +02:00
Boris Vanhoof d2cf79710a Mage blink skill pickup items in the path #60 2019-06-06 23:56:15 +02:00
Boris Vanhoof 78e9413188 Fix : Missing with vampiric blow doesn't seem to trigger a 'Dodge' action text #59 2019-06-06 00:53:52 +02:00
Linus Probert af957eb662 Disables mouse square rendering for all classes 2019-05-25 13:18:25 +02:00
Linus Probert 1d988d7f2f Fixes working explosions and artifacts
Artifacts:
- Explosive daggers
- Explosive kills
2019-05-25 13:11:24 +02:00
Linus Probert 68f4e152d3 Fixes returning daggers and resets erupt attack 2019-05-20 15:37:25 +02:00
Linus Probert 7f232beb32 A beginning to the new exploding artifacts 2019-05-16 07:43:19 +02:00
Linus Probert 12aff9afad Fixes erupt so it has a base push of 1 2019-05-15 20:33:56 +02:00
Linus Probert 2dc25c9b53 Adds the DAGGER_MAGNET artifact
* Adds sprites for volotile and synamite as well
2019-05-15 20:32:48 +02:00
Linus Probert 5e67ca7ef7 Removes stun from backstab skill 2019-05-15 07:50:45 +02:00
Linus Probert cac3fea106 Altered the vampiric blow icon slightly 2019-05-15 07:46:27 +02:00
Linus Probert 9ed80acbee Removes erupt test code 2019-05-14 22:48:29 +02:00
Linus Probert 05cbce0abc Adds vampiric blow icon 2019-05-14 22:47:35 +02:00
Linus Probert 6c135c6b9c Adds gui display for held keys
Also makes the gold key actually golden
2019-05-14 22:27:13 +02:00
Linus Probert fb031e2af3 Bumps version 2019-05-14 20:36:00 +02:00
Linus Probert b861e6e452 Fixes SENTINEL monsters from ignoring forced fear 2019-05-14 16:30:02 +02:00
Linus Probert 046551ad6d Changes ERUPT to cause fear instead of bleeding 2019-05-14 16:10:28 +02:00
Linus Probert 8fb1bbcc1e Fixes bug with erupt pushing direction 2019-05-14 15:57:36 +02:00
Linus Probert 35164a9876 Introduces vampiric blow
Icon isn't done yet.
2019-05-14 15:20:45 +02:00
Linus Probert dd3e84d70d Makes critical hits cause bleeding 2019-05-14 10:26:28 +02:00
Linus Probert 8f21e8bfb2 Prevents skill radius from impacting push back range 2019-05-14 09:00:59 +02:00
Linus Probert 2b743160aa Updates erupt tooltip to mention push back 2019-05-14 08:56:39 +02:00
Linus Probert 7ddaab816e Make erupt always push back 1 tile without any artifacts 2019-05-14 08:38:42 +02:00
Linus Probert 43ca8f4560 Fixes #58 - Game crash with AOE attacks 2019-05-14 08:36:37 +02:00
Linus Probert ef5cd4e244 Adds PUSH_BACK and SKILL_RADIUS effects to erupt skill 2019-05-14 08:27:49 +02:00
Linus Probert b00e607dec Start with debug gold 2019-05-10 12:51:42 +02:00
Linus Probert 67e29d609d Added the SKILL_RADIUS artifact (not used yet)
Also drops the levelcap on artifact drops. I don't think it really
matters if you get an artifact before you have the skill. This should
make the shop a bit more useful.
2019-05-10 12:47:33 +02:00
Linus Probert 81e7df1920 Adds skill icons for the two mage skills 2019-05-09 21:06:06 +02:00
Linus Probert 18effa2416 Rogue backstab skill now triggers bleeding as well 2019-05-09 16:13:25 +02:00
Linus Probert c043ad09cf Implements the erupt skill
Also fixed monsters dying through bleeding logic
2019-05-09 16:02:33 +02:00
Linus Probert 223a3b00c1 Implements the blink skill
The icon still needs to be created but the skill is complete
2019-05-09 10:16:38 +02:00
Linus Probert 2ca78e2ec7 Adds monster bleed damage every turn 2019-05-08 12:59:15 +02:00
Linus Probert 0476d4d1bd Introduces particle emitters on monsters
Also adds the bleed emitter to monsters for later use with the mage.
2019-05-07 11:52:30 +02:00
Linus Probert 826cbe11ff A quick start to the Mage 2019-05-06 13:48:40 +02:00
Linus Probert fcc6527a93 Minor fix 2019-04-23 16:06:20 +02:00
Linus Probert b409b76eca Improved build instruction structure 2019-04-23 16:05:47 +02:00
Andriy Svyryd b0753901ec React to PR feedback 2019-04-23 15:55:20 +02:00
Andriy Svyryd 86e6f66b58 Adds Windows compilation instructions 2019-04-23 15:55:20 +02:00
Linus Probert c0a4a4eb93 Make ninja builds smoother 2019-04-06 12:27:42 +02:00
Linus Probert c917981bad Patch version raised to 2 2019-03-24 17:19:44 +01:00
Linus Probert c2b8fd40eb Adds key pickup SFX 2019-03-24 12:20:45 +01:00
Linus Probert 7a59d15be0 Don't add walls to crumbling rooms before lvl 4 2019-03-24 12:13:38 +01:00
Linus Probert c9fde8dde1 Updated releasenotes 2019-03-21 15:58:23 +01:00
78 changed files with 2156 additions and 299 deletions

12
.github/FUNDING.yml vendored Normal file
View File

@ -0,0 +1,12 @@
# These are supported funding model platforms
github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
patreon: # Replace with a single Patreon username
open_collective: # Replace with a single Open Collective username
ko_fi: LiquidityC
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
liberapay: # Replace with a single Liberapay username
issuehunt: # Replace with a single IssueHunt username
otechie: # Replace with a single Otechie username
custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']

4
.gitignore vendored
View File

@ -15,3 +15,7 @@
/steam_appid.txt
/*.so
/breakhack*.run
compile_commands.json
*.o
breakhack

View File

@ -5,9 +5,9 @@ SET(CMAKE_COLOR_MAKEFILE ON)
project(breakhack C)
set(breakhack_GAME_TITLE "BreakHack")
set(breakhack_MAJOR_VERSION 3)
set(breakhack_MAJOR_VERSION 4)
set(breakhack_MINOR_VERSION 0)
set(breakhack_PATCH_VERSION 1)
set(breakhack_PATCH_VERSION 3)
set(breakhack_RELEASE_TYPE "")
# Checksums
@ -52,6 +52,10 @@ elseif ("${CMAKE_C_COMPILER_ID}" STREQUAL "GNU")
set(GCC 1)
endif()
if ("${CMAKE_GENERATOR}" STREQUAL "Ninja")
set(NINJA 1)
endif()
if ("${CMAKE_SYSTEM_NAME}" STREQUAL "Darwin")
set(OSX 1)
endif()
@ -161,17 +165,6 @@ set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -DDEBUG")
if (STEAM)
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -DSTEAM_BUILD")
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -DSTEAM_BUILD")
endif ()
if (MINGW)
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -DMINGW")
endif (MINGW)
if (NOT MSVC)
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -D__FNAME__='\"$(subst ${CMAKE_SOURCE_DIR}/,,$(abspath $<))\"'")
else (NOT MSVC)
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -D__FNAME__=__FILE__")
endif (NOT MSVC)
if (STEAM)
set(STEAM_SOURCES
src/steam/steamworks_api_wrapper
)
@ -179,61 +172,74 @@ else ()
set(STEAM_SOURCES "")
endif ()
if (MINGW)
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -DMINGW")
endif (MINGW)
if (NOT MSVC)
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -D__FNAME__='\"$(subst ${CMAKE_SOURCE_DIR}/,,$(abspath $<))\"'")
else ()
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -D__FNAME__=__FILE__")
endif ()
# PROGRAMS:
add_executable(breakhack
src/main
src/texture
src/screenresolution
src/sprite
src/sprite_util
src/util
src/event
src/player
src/map
src/map_lua
src/camera
src/timer
src/roommatrix
src/position
src/monster
src/stats
src/actiontext
src/random
src/time
src/linkedlist
src/hashtable
src/gui
src/item
src/item_builder
src/pointer
src/gui_button
src/particle_engine
src/menu
src/collisions
src/keyboard
src/input
src/mixer
src/io_util
src/physfsrwops
src/skillbar
src/texturecache
src/skill
src/projectile
src/vector2d
src/map_room_modifiers
sqlite3/sqlite3
src/db
src/settings
src/actiontextbuilder
src/animation
src/trap
src/artifact
src/screen
src/hiscore
src/object
src/gui_util
src/tooltip
src/gamecontroller
src/main.c
src/texture.c
src/screenresolution.c
src/sprite.c
src/sprite_util.c
src/util.c
src/event.c
src/player.c
src/save.c
src/map.c
src/map_lua.c
src/camera.c
src/timer.c
src/roommatrix.c
src/position.c
src/monster.c
src/stats.c
src/actiontext.c
src/random.c
src/time.c
src/linkedlist.c
src/hashtable.c
src/gui.c
src/item.c
src/item_builder.c
src/pointer.c
src/gui_button.c
src/particle_engine.c
src/particle_emitter.c
src/menu.c
src/collisions.c
src/keyboard.c
src/input.c
src/mixer.c
src/io_util.c
src/physfsrwops.c
src/skillbar.c
src/texturecache.c
src/skill.c
src/projectile.c
src/vector2d.c
src/map_room_modifiers.c
sqlite3/sqlite3.c
src/db.c
src/settings.c
src/actiontextbuilder.c
src/animation.c
src/trap.c
src/artifact.c
src/screen.c
src/hiscore.c
src/object.c
src/gui_util.c
src/tooltip.c
src/gamecontroller.c
src/effect_util.c
${STEAM_SOURCES}
)
@ -265,11 +271,11 @@ if (STEAM)
endif ()
if (MSVC)
set_target_properties(breakhack PROPERTIES LINK_FLAGS_DEBUG "/SUBSYSTEM:CONSOLE")
set_target_properties(breakhack PROPERTIES LINK_FLAGS_DEBUG "/SUBSYSTEM:CONSOLE /NODEFAULTLIB:MSVCRTD")
set_target_properties(breakhack PROPERTIES COMPILE_DEFINITIONS_DEBUG "_CONSOLE")
set_target_properties(breakhack PROPERTIES LINK_FLAGS_RELWITHDEBINFO "/SUBSYSTEM:CONSOLE")
set_target_properties(breakhack PROPERTIES COMPILE_DEFINITIONS_RELWITHDEBINFO "_CONSOLE")
set_target_properties(breakhack PROPERTIES LINK_FLAGS_RELEASE "/SUBSYSTEM:WINDOWS")
set_target_properties(breakhack PROPERTIES LINK_FLAGS_RELEASE "/SUBSYSTEM:WINDOWS /NODEFAULTLIB:MSVCRTD")
set_target_properties(breakhack PROPERTIES LINK_FLAGS_MINSIZEREL "/SUBSYSTEM:WINDOWS")
endif (MSVC)
@ -277,19 +283,19 @@ endif (MSVC)
IF (CMOCKA_FOUND AND NOT OSX AND NOT CLANG)
find_package(Threads REQUIRED)
enable_testing()
add_executable(test_util test/test_util src/util)
add_executable(test_util test/test_util.c src/util.c)
target_link_libraries(test_util ${CMOCKA_LIBRARY})
add_test(test_util test_util)
add_executable(test_linkedlist test/test_linkedlist src/linkedlist src/util)
add_executable(test_linkedlist test/test_linkedlist.c src/linkedlist.c src/util.c)
target_link_libraries(test_linkedlist ${CMOCKA_LIBRARY})
add_test(test_linkedlist test_linkedlist)
add_executable(test_hashtable test/test_hashtable src/hashtable src/util)
add_executable(test_hashtable test/test_hashtable.c src/hashtable.c src/util.c)
target_link_libraries(test_hashtable ${CMOCKA_LIBRARY})
add_test(test_hashtable test_hashtable)
add_executable(test_input test/test_input src/input src/keyboard)
add_executable(test_input test/test_input.c src/input.c src/keyboard.c)
target_link_libraries(test_input
${CMOCKA_LIBRARY}
${SDL2_LIBRARY}
@ -303,7 +309,7 @@ ENDIF ()
# LINT:
if (CPPCHECK_FOUND)
add_custom_target(lint
COMMAND ${CPPCHECK_EXECUTABLE} --force --language=c --template=gcc --error-exitcode=1 --quiet --suppress=unreadVariable --suppress=missingInclude --enable=warning,style,performance,portability,information,missingInclude src/
COMMAND ${CPPCHECK_EXECUTABLE} --force --language=c --template=gcc --error-exitcode=1 --quiet --suppress=missingInclude --enable=warning,style,performance,portability,information,missingInclude src/
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
COMMENT "Run cppcheck"
)

View File

@ -0,0 +1,10 @@
Apart from basic compile tools (*GCC/Clang, Make*) you'll also need to install **sdl2, sdl2-image, sdl2-ttf, sdl2-mixer** (If on a debian based dist you need to install the *dev* packages).
Optionally you can also install **cppcheck** and **physfs**
Once that is done run the following:
```bash
mkdir _build
cd _build
cmake -DCMAKE_BUILD_TYPE=Debug .. # Build type flag is optional
make
```

View File

@ -0,0 +1,16 @@
Compile on Windows with Visual Studio
-------------------------------------
1. Install [Visual Studio Community 2019](https://visualstudio.microsoft.com/vs/community/) with the "Desktop development with C++" workload
2. Install [CMake](https://cmake.org/download/)
3. Install [NSIS](https://nsis.sourceforge.io/Download)
4. Download and unzip [SDL2-devel-2.x.x-VC.zip](https://www.libsdl.org/download-2.0.php), [SDL2_image-devel-2.x.x-VC.zip](https://www.libsdl.org/projects/SDL_image/), [SDL2_mixer-devel-2.x.x-VC.zip](https://www.libsdl.org/projects/SDL_mixer/), [SDL2_ttf-devel-2.x.x-VC.zip](https://www.libsdl.org/projects/SDL_ttf/)
5. Open Developer Command Prompt for VS 2019 and run the following, substituting paths as appropriate:
```batch
SET SDL2DIR=C:\repos\breakhackBuild\SDL2-2.0.9
SET SDL2MIXERDIR=C:\repos\breakhackBuild\SDL2_mixer-2.0.4
SET SDL2_IMAGE=C:\repos\breakhackBuild\SDL2_image-2.0.4
SET SDLTTFDIR=C:\repos\breakhackBuild\SDL2_ttf-2.0.15
cmake -S C:\repos\breakhack -B C:\repos\breakhackBuild
cmake --build C:\repos\breakhackBuild --target package --config Release
```

View File

@ -7,12 +7,12 @@ release:
.PHONY: release
clean:
@make clean -sC _build/debug
@make clean -sC _build/release
@make -sC _build/debug clean
@make -sC _build/release clean
.PHONY: clean
test:
@make test -sC _build/debug
@make -sC _build/debug test
.PHONY: test
run: $(all)
@ -21,12 +21,30 @@ run: $(all)
playtest: $(all)
@LD_LIBRARY_PATH=$LD_LIBRARY_PATH:./ ./_build/release/breakhack
.PHONY: run
.PHONY: playtest
lint:
@make lint -sC _build/debug
@make -sC _build/debug lint
.PHONY: lint
package:
@make package -sC _build/release
@make -sC _build/release package
.PHONY: package
setup:
@mkdir -p _build/release
@mkdir -p _build/debug
@cd _build/debug/ && \
cmake -DCMAKE_BUILD_TYPE=Debug -DCMAKE_EXPORT_COMPILE_COMMANDS=YES ../.. && \
cd -
@cd _build/debug/ && \
cmake -DCMAKE_BUILD_TYPE=Release ../.. && \
cd -
@ln -s _build/debug/compile_commands.json
@echo "Setup complete"
.PHONY: setup
teardown:
@rm -rf _build
@rm compile_commands.json
.PHONY: teardown

503
Makefile.os4 Normal file
View File

@ -0,0 +1,503 @@
#
# Project: breakhack
#
# Created on: 29-08-2022 21:00:37
# by George Sokianos
#
###################################################################
##
##//// Objects
##
###################################################################
breakhack_OBJ := \
src/actiontext.o src/actiontextbuilder.o src/animation.o \
src/artifact.o src/camera.o src/collisions.o \
src/db.o src/effect_util.o src/event.o \
src/gamecontroller.o src/gui_button.o src/gui.o \
src/gui_util.o src/hashtable.o src/hiscore.o \
src/input.o src/io_util.o src/item_builder.o \
src/item.o src/keyboard.o src/linkedlist.o \
src/main.o src/map.o src/map_lua.o \
src/map_room_modifiers.o src/menu.o src/mixer.o \
src/monster.o src/object.o src/particle_emitter.o \
src/particle_engine.o src/physfsrwops.o src/player.o \
src/pointer.o src/position.o src/projectile.o \
src/random.o src/roommatrix.o src/save.o \
src/screen.o src/screenresolution.o src/settings.o \
src/skill.o src/skillbar.o src/sprite.o \
src/sprite_util.o src/stats.o src/texture.o \
src/texturecache.o src/time.o src/timer.o \
src/tooltip.o src/trap.o src/util.o \
src/vector2d.o bh_random/src/bh_random.o
###################################################################
##
##//// Variables and Environment
##
###################################################################
CC := gcc:bin/gcc
INCPATH := -I. -I/sdk/local/newlib/include/SDL2 -Ibh_random/src \
-I/sdk/local/common/include/lua53
# -DDEBUG
CFLAGS := $(INCPATH) -Wall -Wwrite-strings -Wno-discarded-qualifiers
# \
# -g -gstabs
# -ggdb -gdwarf-2
LFLAGS := -lauto -lSDL2_image -lSDL2_ttf -lfreetype -ltiff -lwebp -lpng -ljpeg \
-llua53 -lsqlite3 -lz \
-lSDL2_mixer -lmikmod -lmodplug -lFLAC -logg \
-lSDL2 -lphysfs -lpthread -athread=native -lstdc++
# \
# -g -gstabs
# -ggdb -gdwarf-2
# -lunix
# -lenet -lcrypto -lSDL2_gl4es -lglu_gl4es -lgl4es -lopenal \
###################################################################
##
##//// General rules
##
###################################################################
.PHONY: all all-before all-after clean clean-custom realclean
all: all-before breakhack all-after
all-before:
# You can add rules here to execute before the project is built
all-after:
# You can add rules here to execute after the project is built
clean: clean-custom
@echo "Cleaning compiler objects..."
@rm -f $(breakhack_OBJ)
realclean:
@echo "Cleaning compiler objects and targets..."
@rm -f $(breakhack_OBJ) breakhack
###################################################################
##
##//// Targets
##
###################################################################
breakhack: $(breakhack_OBJ)
@echo "Linking breakhack"
@gcc:bin/gcc -o breakhack $(breakhack_OBJ) $(LFLAGS)
@echo "Removing stale debug target: breakhack"
@rm -f breakhack.debug
###################################################################
##
##//// Standard rules
##
###################################################################
# A default rule to make all the objects listed below
# because we are hiding compiler commands from the output
.c.o:
@echo "Compiling $<"
@$(CC) -c $< -o $*.o $(CFLAGS)
src/actiontext.o: src/actiontext.c src/actiontext.h \
src/position.h src/sprite.h src/texture.h \
src/camera.h src/roommatrix.h src/timer.h \
src/vector2d.h src/util.h src/update_data.h \
src/player.h src/stats.h src/skill.h \
src/linkedlist.h src/input.h src/artifact.h \
src/map.h src/defines.h src/monster.h \
src/map_room_modifiers.h src/object.h src/doorlocktype.h
src/actiontextbuilder.o: src/actiontextbuilder.c src/actiontextbuilder.h src/actiontext.h \
src/position.h src/sprite.h src/timer.h \
src/vector2d.h src/camera.h src/update_data.h \
src/player.h src/map.h src/roommatrix.h \
src/gui.h src/util.h
src/animation.o: src/animation.c src/animation.h src/timer.h \
src/camera.h src/position.h src/vector2d.h \
src/sprite.h src/texture.h \
src/dimension.h src/roommatrix.h src/defines.h \
src/map_room_modifiers.h src/input.h
src/artifact.o: src/artifact.c src/artifact.h src/sprite.h \
src/texture.h src/position.h src/camera.h \
src/roommatrix.h src/timer.h \
src/vector2d.h src/util.h src/texturecache.h \
src/dimension.h src/particle_engine.h src/player.h \
src/stats.h src/actiontext.h src/skill.h \
src/linkedlist.h src/input.h src/random.h \
src/camera.o: src/camera.c src/camera.h src/position.h \
src/timer.h src/vector2d.h src/map.h \
src/linkedlist.h src/sprite.h src/texture.h \
src/roommatrix.h src/defines.h src/config.h \
src/monster.h src/stats.h src/actiontext.h \
src/player.h src/doorlocktype.h src/particle_emitter.h \
src/skill.h src/input.h src/artifact.h \
src/map_room_modifiers.h src/object.h src/util.h
src/collisions.o: src/collisions.c src/collisions.h
src/db.o: src/db.c src/db.h
src/effect_util.o: src/effect_util.c src/gui.h src/linkedlist.h \
src/sprite.h src/texture.h src/position.h \
src/camera.h src/roommatrix.h src/timer.h \
src/vector2d.h src/player.h \
src/stats.h src/actiontext.h src/skill.h \
src/input.h src/artifact.h src/monster.h src/doorlocktype.h \
src/particle_emitter.h src/dimension.h src/defines.h \
src/config.h src/map_room_modifiers.h
src/event.o: src/event.c src/event.h src/player.h \
src/sprite.h src/stats.h \
src/actiontext.h src/camera.h src/skill.h \
src/linkedlist.h src/input.h src/artifact.h
src/gamecontroller.o: src/gamecontroller.c src/gamecontroller.h
src/gui_button.o: src/gui_button.c src/util.h src/gui_button.h \
src/pointer.h src/sprite.h src/camera.h \
src/input.h src/texture.h src/position.h \
src/roommatrix.h src/timer.h src/linkedlist.h \
src/vector2d.h
src/gui.o: src/gui.c src/gui.h src/linkedlist.h \
src/sprite.h src/texture.h src/position.h \
src/camera.h src/roommatrix.h src/timer.h \
src/vector2d.h src/player.h \
src/stats.h src/actiontext.h src/skill.h \
src/input.h src/artifact.h src/util.h \
src/map.h \
src/defines.h src/config.h src/monster.h \
src/doorlocktype.h src/particle_emitter.h src/map_room_modifiers.h \
src/object.h src/texturecache.h \
src/dimension.h src/gui_util.h
src/gui_util.o: src/gui_util.c src/texturecache.h src/texture.h \
src/dimension.h src/position.h \
src/camera.h src/gui_util.h src/sprite.h \
src/roommatrix.h src/timer.h
src/hashtable.o: src/hashtable.c src/hashtable.h src/defines.h \
src/config.h
src/hiscore.o: src/hiscore.c src/hiscore.h src/linkedlist.h \
src/player.h src/sprite.h \
src/stats.h src/actiontext.h src/camera.h \
src/skill.h src/input.h src/artifact.h \
src/db.h
src/input.o: src/input.c src/input.h
src/io_util.o: src/io_util.c src/physfsrwops.h src/io_util.h
src/item_builder.o: src/item_builder.c src/item_builder.h \
src/item.h src/sprite.h src/position.h \
src/player.h src/camera.h src/linkedlist.h \
src/texture.h src/dimension.h \
src/timer.h src/vector2d.h src/util.h \
src/gui.h src/roommatrix.h src/stats.h \
src/actiontext.h src/skill.h src/input.h \
src/artifact.h src/mixer.h src/gamestate.h \
src/random.h src/texturecache.h src/defines.h \
src/map_room_modifiers.h src/sprite_util.h src/map.h \
src/config.h src/monster.h src/doorlocktype.h \
src/particle_emitter.h
src/item.o: src/item.c src/item.h src/sprite.h \
src/texture.h src/position.h src/camera.h \
src/roommatrix.h src/timer.h src/player.h \
src/stats.h src/actiontext.h \
src/skill.h src/linkedlist.h src/input.h \
src/artifact.h src/vector2d.h src/util.h \
src/mixer.h src/gamestate.h src/gui.h \
src/actiontextbuilder.h src/update_data.h
src/keyboard.o: src/keyboard.c src/keyboard.h src/defines.h \
src/config.h
src/linkedlist.o: src/linkedlist.c src/linkedlist.h
src/main.o: src/main.c /sdk/local/newlib/include/SDL2/SDL_main.h \
src/linkedlist.h src/player.h src/sprite.h \
src/texture.h src/position.h src/camera.h \
src/roommatrix.h src/timer.h src/stats.h \
src/actiontext.h src/vector2d.h src/skill.h \
src/input.h src/artifact.h src/screenresolution.h \
src/dimension.h src/map.h src/defines.h \
src/config.h src/monster.h src/doorlocktype.h \
src/particle_emitter.h src/map_room_modifiers.h src/object.h \
src/map_lua.h src/gamestate.h src/gui.h \
src/util.h src/item_builder.h src/item.h \
src/pointer.h src/gui_button.h src/particle_engine.h \
src/menu.h src/keyboard.h \
src/mixer.h src/random.h src/skillbar.h \
src/texturecache.h src/update_data.h src/settings.h \
src/actiontextbuilder.h src/screen.h src/hiscore.h \
src/io_util.h src/tooltip.h src/gamecontroller.h \
src/time.h src/sprite_util.h src/event.h \
src/save.h src/steam/steamworks_api_wrapper.h src/defines.h
src/map.o: src/map.c src/map.h src/linkedlist.h \
src/sprite.h src/texture.h src/position.h \
src/camera.h src/roommatrix.h src/timer.h \
src/vector2d.h src/defines.h src/config.h \
src/monster.h src/stats.h src/actiontext.h \
src/player.h src/doorlocktype.h src/particle_emitter.h \
src/skill.h src/input.h src/artifact.h \
src/map_room_modifiers.h src/object.h src/map_lua.h \
src/util.h src/item.h src/item_builder.h \
src/gui.h src/particle_engine.h src/dimension.h \
src/update_data.h src/trap.h src/mixer.h
src/map_lua.o: src/map_lua.c src/map_lua.h src/map.h \
src/linkedlist.h src/sprite.h \
src/camera.h src/position.h src/timer.h \
src/defines.h src/monster.h src/player.h \
src/map_room_modifiers.h src/object.h src/doorlocktype.h \
src/config.h src/util.h src/stats.h \
src/io_util.h src/texturecache.h src/texture.h \
src/dimension.h src/trap.h src/roommatrix.h \
src/actiontext.h src/skill.h src/input.h \
src/artifact.h src/update_data.h src/gui.h \
src/vector2d.h src/item.h src/item_builder.h
src/map_room_modifiers.o: src/map_room_modifiers.c src/map_room_modifiers.h src/vector2d.h \
src/player.h src/sprite.h src/texture.h \
src/position.h src/camera.h src/roommatrix.h \
src/timer.h src/stats.h src/actiontext.h \
src/skill.h src/linkedlist.h src/input.h \
src/artifact.h src/defines.h
src/menu.o: src/menu.c src/menu.h src/camera.h \
src/position.h src/timer.h src/vector2d.h \
src/linkedlist.h src/texture.h \
src/dimension.h src/sprite.h src/roommatrix.h \
src/util.h src/defines.h src/map_room_modifiers.h \
src/input.h src/config.h src/gui_button.h \
src/pointer.h src/keyboard.h src/mixer.h \
src/gamestate.h src/collisions.h
src/mixer.o: src/mixer.c src/mixer.h src/gamestate.h \
src/util.h src/io_util.h src/settings.h
src/monster.o: src/monster.c src/monster.h src/sprite.h \
src/texture.h src/position.h src/camera.h \
src/roommatrix.h src/timer.h src/stats.h \
src/actiontext.h src/vector2d.h src/player.h \
src/skill.h src/linkedlist.h src/input.h \
src/artifact.h src/doorlocktype.h src/particle_emitter.h \
src/dimension.h src/util.h src/random.h \
src/gui.h src/item.h src/item_builder.h \
src/map.h src/defines.h src/config.h \
src/map_room_modifiers.h src/object.h src/particle_engine.h \
src/update_data.h src/actiontextbuilder.h src/texturecache.h \
src/trap.h src/mixer.h
src/object.o: src/object.c src/object.h src/camera.h \
src/position.h src/timer.h src/vector2d.h \
src/player.h src/sprite.h src/stats.h \
src/actiontext.h src/skill.h src/linkedlist.h \
src/input.h src/artifact.h src/texture.h \
src/roommatrix.h src/util.h src/mixer.h \
src/gamestate.h src/random.h src/texturecache.h
src/particle_emitter.o: src/particle_emitter.c src/particle_emitter.h src/timer.h \
src/position.h src/dimension.h
src/particle_engine.o: src/particle_engine.c src/particle_engine.h src/position.h \
src/dimension.h src/camera.h src/timer.h \
src/vector2d.h src/linkedlist.h src/util.h \
src/defines.h src/config.h
src/physfsrwops.o: src/physfsrwops.c src/physfsrwops.h
src/player.o: src/player.c src/player.h src/sprite.h \
src/texture.h src/position.h src/camera.h \
src/roommatrix.h src/timer.h src/stats.h \
src/actiontext.h src/vector2d.h src/skill.h \
src/linkedlist.h src/input.h src/artifact.h \
src/monster.h src/doorlocktype.h src/particle_emitter.h \
src/dimension.h src/util.h src/gui.h \
src/item.h src/particle_engine.h src/keyboard.h \
src/defines.h src/config.h src/mixer.h \
src/gamestate.h src/random.h src/projectile.h \
src/map_room_modifiers.h src/update_data.h src/map.h \
src/texturecache.h src/actiontextbuilder.h \
src/animation.h src/trap.h src/gamecontroller.h \
src/event.h src/effect_util.h src/steam/steamworks_api_wrapper.h \
src/defines.h
src/pointer.o: src/pointer.c src/pointer.h src/sprite.h \
src/texture.h src/position.h src/camera.h \
src/roommatrix.h src/timer.h src/vector2d.h \
src/input.h src/util.h src/particle_engine.h
src/position.o: src/position.c src/position.h src/defines.h
src/projectile.o: src/projectile.c src/projectile.h src/sprite.h \
src/texture.h src/position.h src/camera.h \
src/roommatrix.h src/timer.h \
src/vector2d.h src/defines.h src/map_room_modifiers.h \
src/input.h src/update_data.h src/player.h \
src/map.h src/gui.h src/config.h \
src/util.h src/texturecache.h src/dimension.h \
src/stats.h src/actiontext.h src/skill.h \
src/linkedlist.h src/artifact.h src/monster.h \
src/doorlocktype.h src/particle_emitter.h src/mixer.h \
src/gamestate.h src/item_builder.h src/item.h \
src/random.h src/object.h src/effect_util.h
src/random.o: src/random.c src/time.h src/random.h
src/roommatrix.o: src/roommatrix.c src/defines.h src/config.h \
src/roommatrix.h src/position.h src/camera.h \
src/timer.h src/vector2d.h \
src/map_room_modifiers.h src/input.h src/util.h \
src/map.h src/linkedlist.h src/sprite.h src/texture.h \
src/monster.h src/stats.h src/actiontext.h \
src/player.h src/doorlocktype.h src/particle_emitter.h \
src/skill.h src/artifact.h src/object.h \
src/item.h src/update_data.h src/gui.h
src/save.o: src/save.c src/save.h src/player.h \
src/sprite.h src/stats.h \
src/actiontext.h src/camera.h src/skill.h \
src/linkedlist.h src/input.h src/artifact.h \
src/db.h src/defines.h src/config.h
src/screen.o: src/screen.c src/screen.h src/texture.h \
src/dimension.h \
src/position.h src/camera.h src/linkedlist.h \
src/sprite.h src/roommatrix.h src/timer.h \
src/vector2d.h src/util.h src/hiscore.h \
src/player.h src/stats.h src/actiontext.h \
src/skill.h src/input.h
src/screenresolution.o: src/screenresolution.c src/defines.h src/config.h \
src/util.h src/screenresolution.h
src/settings.o: src/settings.c src/settings.h src/util.h \
src/defines.h src/config.h
src/skill.o: src/skill.c src/texturecache.h src/texture.h \
src/dimension.h src/position.h \
src/camera.h src/skill.h src/roommatrix.h \
src/defines.h src/map_room_modifiers.h src/input.h \
src/sprite.h src/timer.h src/vector2d.h \
src/util.h src/player.h src/stats.h \
src/actiontext.h src/linkedlist.h src/artifact.h \
src/config.h src/monster.h src/doorlocktype.h \
src/particle_emitter.h src/mixer.h src/gamestate.h \
src/gui.h src/random.h src/particle_engine.h \
src/projectile.h src/update_data.h src/map.h \
src/item.h src/animation.h src/trap.h \
src/tooltip.h src/actiontextbuilder.h
src/skillbar.o: src/skillbar.c src/defines.h src/config.h \
src/skillbar.h src/linkedlist.h src/camera.h \
src/position.h src/timer.h src/vector2d.h \
src/player.h src/sprite.h src/stats.h \
src/actiontext.h src/skill.h src/input.h \
src/artifact.h src/texture.h \
src/dimension.h src/util.h src/roommatrix.h \
src/map_room_modifiers.h src/keyboard.h src/texturecache.h \
src/particle_engine.h src/update_data.h src/map.h \
src/monster.h src/object.h src/doorlocktype.h
src/sprite.o: src/sprite.c src/sprite.h src/texture.h \
src/dimension.h \
src/position.h src/camera.h src/timer.h \
src/vector2d.h src/roommatrix.h src/defines.h \
src/map_room_modifiers.h src/input.h src/util.h \
src/update_data.h src/player.h src/stats.h \
src/actiontext.h src/skill.h src/linkedlist.h \
src/artifact.h src/map.h src/monster.h \
src/object.h src/doorlocktype.h
src/sprite_util.o: src/sprite_util.c src/sprite_util.h src/sprite.h \
src/texture.h src/position.h src/camera.h \
src/roommatrix.h src/timer.h
src/stats.o: src/stats.c src/gui.h \
src/linkedlist.h src/sprite.h src/texture.h \
src/position.h src/camera.h src/roommatrix.h \
src/timer.h src/vector2d.h src/player.h \
src/stats.h src/actiontext.h src/skill.h \
src/input.h src/artifact.h src/random.h \
src/util.h src/defines.h
src/texture.o: src/texture.c src/texture.h \
src/dimension.h src/position.h \
src/camera.h src/timer.h src/vector2d.h \
src/util.h
src/texturecache.o: src/texturecache.c src/texturecache.h \
src/texture.h src/dimension.h \
src/position.h src/camera.h src/hashtable.h \
src/defines.h src/config.h src/util.h
src/time.o: src/time.c src/time.h
src/timer.o: src/timer.c src/util.h
src/tooltip.o: src/tooltip.c src/tooltip.h src/camera.h \
src/position.h src/timer.h \
src/vector2d.h src/sprite.h src/texture.h \
src/roommatrix.h src/gui_util.h src/defines.h \
src/config.h src/gui.h src/linkedlist.h \
src/player.h src/stats.h src/actiontext.h \
src/skill.h src/input.h src/artifact.h \
src/texturecache.h
src/trap.o: src/trap.c src/trap.h \
src/sprite.h src/texture.h src/position.h \
src/camera.h src/roommatrix.h src/timer.h \
src/player.h src/stats.h src/actiontext.h \
src/skill.h src/linkedlist.h src/input.h \
src/artifact.h src/update_data.h src/map.h \
src/gui.h src/vector2d.h src/util.h \
src/random.h
src/util.o: src/util.c src/time.h src/defines.h \
src/config.h
src/vector2d.o: src/vector2d.c
bh_random/src/bh_random.o:
@g++ -c bh_random/src/bh_random.cpp -o bh_random/src/bh_random.o $(CFLAGS)
# prepare an archive for the program
release:
@echo "Creating release files..."
@mkdir -p release/breakhack
@cp -r release_files/* release/breakhack/
@cp -r data release/breakhack/
@cp -r assets release/breakhack/
@cp breakhack release/breakhack/
@strip release/breakhack/breakhack
@cp README.md release/breakhack/
@cp README_Amiga.md release/breakhack/
@cp LICENSE.txt release/breakhack/
@cp CREDITS.md release/breakhack/
@echo "Creating release archive..."
@lha -aeqr3 a breakhack-OS4.lha release/
@echo "Clean release files..."
@delete release ALL QUIET FORCE

View File

@ -1,3 +1,4 @@
[![maintenance](https://img.shields.io/badge/maintenance-passively--maintained-orange)](https://img.shields.io/badge/maintenance-passively--maintained-orange)
[![Build Status](https://travis-ci.org/Oliveshark/breakhack.svg?branch=master)](https://travis-ci.org/Oliveshark/breakhack)
[![Build status](https://ci.appveyor.com/api/projects/status/2a18npdntkmlx3dc?svg=true)](https://ci.appveyor.com/project/LiquidityC/breakhack)
[![Codacy Badge](https://api.codacy.com/project/badge/Grade/fc02d56fa7194e61b2c7d260fd2e4186)](https://www.codacy.com/app/LiquidityC/breakhack?utm_source=github.com&amp;utm_medium=referral&amp;utm_content=Oliveshark/breakhack&amp;utm_campaign=Badge_Grade)
@ -46,16 +47,8 @@ BreakHack is released under two different licenses.
Compile
-------
Apart from basic compile tools (*GCC/Clang, Make*) you'll also need to install **sdl2, sdl2-image, sdl2-ttf, sdl2-mixer** (If on a debian based dist you need to install the *dev* packages).
Optionally you can also install **cppcheck** and **physfs**
Once that is done run the following:
```bash
mkdir _build
cd _build
cmake -DCMAKE_BUILD_TYPE=Debug .. # Build type flag is optional
make
```
* [Linux](LINUX_BUILD_INSTRUCTIONS.md)
* [Windows (Visual Studio)](MSVC_BUILD_INSTRUCTIONS.md)
Contribute
----------

48
README_Amiga.md Normal file
View File

@ -0,0 +1,48 @@
# breakhack for AmigaOS 4.1 FE
breakhack is a small roguelike game for having some good time, while
you wait for your amiga to compile or render a screen. It is developed
by Linus Probert and you can find it for various platforms on Steam.
## Installation
Extract the archive wherever you want and run the *breakhack* binary.
## I would like to thank
- Linus Probert for creating this awesome game and open sourcing it
- Capehill for his tireless work on SDL port for AmigaOS 4.1 FE
- Roman "kas1e" Kargin and Nouvel "HunoPPC" Hugues for their help
with libphysfs
## Known issues
There are some issues with the Fullscreen graphics, depending which
Renderer driver is used. I might fix them in the future.
## Support
If you enjoy what I am doing and would like to keep me up during the night,
please consider to buy me a coffee at:
https://ko-fi.com/walkero
## Known issues
You can find the known issues at
https://git.walkero.gr/walkero/breakhack/issues
# Changelog
## [4.0.3r2] - 2023-07-04
### Changed
- Compiled with latest SDL 2.0.28 that has a better support for 16bit
screens. Now it works under Qemu. Tested with Software rendering
### Fixed
- Fixed the hiscore date. Now it is saved on UTC +8 years which is
the Amiga epoch diff against the unix one. This doesn affect the
old hi-scores though
## [4.0.3r1] - 2022-12-21
### Added
- First release for AmigaOS 4

BIN
README_Amiga.md.info Normal file

Binary file not shown.

BIN
assets/Extras/Artifacts.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 548 B

BIN
assets/Extras/Keys.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 321 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 KiB

After

Width:  |  Height:  |  Size: 4.3 KiB

BIN
assets/Sounds/FX/blast.wav Normal file

Binary file not shown.

BIN
assets/Sounds/FX/blink.wav Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -8,7 +8,7 @@ if (NOT CMAKE_BUILD_TYPE)
endif ()
add_library(bh_random STATIC
src/bh_random
src/bh_random.cpp
)
IF ( MSVC )

View File

@ -64,6 +64,14 @@
# (To distribute this file outside of CMake, substitute the full
# License text for the above reference.)
if(CMAKE_SIZEOF_VOID_P EQUAL 8)
set(SDL2_ARCH_64 TRUE)
set(SDL2_PROCESSOR_ARCH "x64")
else()
set(SDL2_ARCH_64 FALSE)
set(SDL2_PROCESSOR_ARCH "x86")
endif(CMAKE_SIZEOF_VOID_P EQUAL 8)
SET(SDL2_SEARCH_PATHS
~/Library/Frameworks
/Library/Frameworks
@ -87,7 +95,7 @@ FIND_LIBRARY(SDL2_LIBRARY_TEMP
NAMES SDL2
HINTS
$ENV{SDL2DIR}
PATH_SUFFIXES lib64 lib
PATH_SUFFIXES lib64 lib lib/${SDL2_PROCESSOR_ARCH}
PATHS ${SDL2_SEARCH_PATHS} ${SDL2_INCLUDE_DIR}/../..
)
@ -101,7 +109,7 @@ IF(NOT SDL2_BUILDING_LIBRARY)
NAMES SDL2main
HINTS
$ENV{SDL2DIR}
PATH_SUFFIXES lib64 lib
PATH_SUFFIXES lib64 lib lib/${SDL2_PROCESSOR_ARCH}
PATHS ${SDL2_SEARCH_PATHS}
)
ENDIF(NOT ${SDL2_INCLUDE_DIR} MATCHES ".framework")

View File

@ -28,6 +28,14 @@
# See the License for more information.
#=============================================================================
if(CMAKE_SIZEOF_VOID_P EQUAL 8)
set(SDL2_ARCH_64 TRUE)
set(SDL2_PROCESSOR_ARCH "x64")
else()
set(SDL2_ARCH_64 FALSE)
set(SDL2_PROCESSOR_ARCH "x86")
endif(CMAKE_SIZEOF_VOID_P EQUAL 8)
if(NOT SDL2_TTF_INCLUDE_DIR AND SDL2TTF_INCLUDE_DIR)
set(SDL2_TTF_INCLUDE_DIR ${SDL2TTF_INCLUDE_DIR} CACHE PATH "directory cache
entry initialized from old variable name")
@ -48,7 +56,7 @@ find_library(SDL2_TTF_LIBRARY
HINTS
ENV SDLTTFDIR
ENV SDLDIR
PATH_SUFFIXES lib
PATH_SUFFIXES lib lib/${SDL2_PROCESSOR_ARCH}
)
if(SDL2_TTF_INCLUDE_DIR AND EXISTS "${SDL2_TTF_INCLUDE_DIR}/SDL_ttf.h")

View File

@ -1,3 +1,7 @@
c4811ed Fixes checksumtool output again
eceb241 Removes useless text from checksumtool output
cea891f Move checksum configuration to CMakeLists.txt and config.h
8e28230 Fixes release notes
53703f1 Patch version raised to 1
ce182ad Updates checksums
ebfe271 Fixes compile error and some graphical issues

View File

@ -0,0 +1,6 @@
c2b8fd4 Adds key pickup SFX
7a59d15 Don't add walls to crumbling rooms before lvl 4
c9fde8d Updated releasenotes
c4811ed Fixes checksumtool output again
eceb241 Removes useless text from checksumtool output
cea891f Move checksum configuration to CMakeLists.txt and config.h

View File

@ -0,0 +1,40 @@
392edd8 Updates blink tooltip to include item pickups.
6584981 remove unnecessary pickup
d2cf797 Mage blink skill pickup items in the path #60
78e9413 Fix : Missing with vampiric blow doesn't seem to trigger a 'Dodge' action text #59
af957eb Disables mouse square rendering for all classes
1d988d7 Fixes working explosions and artifacts
68f4e15 Fixes returning daggers and resets erupt attack
7f232be A beginning to the new exploding artifacts
12aff9a Fixes erupt so it has a base push of 1
2dc25c9 Adds the DAGGER_MAGNET artifact
5e67ca7 Removes stun from backstab skill
cac3fea Altered the vampiric blow icon slightly
9ed80ac Removes erupt test code
05cbce0 Adds vampiric blow icon
6c135c6 Adds gui display for held keys
fb031e2 Bumps version
b861e6e Fixes SENTINEL monsters from ignoring forced fear
046551a Changes ERUPT to cause fear instead of bleeding
8fb1bbc Fixes bug with erupt pushing direction
35164a9 Introduces vampiric blow
dd3e84d Makes critical hits cause bleeding
8f21e8b Prevents skill radius from impacting push back range
2b74316 Updates erupt tooltip to mention push back
7ddaab8 Make erupt always push back 1 tile without any artifacts
43ca8f4 Fixes #58 - Game crash with AOE attacks
ef5cd4e Adds PUSH_BACK and SKILL_RADIUS effects to erupt skill
b00e607 Start with debug gold
67e29d6 Added the SKILL_RADIUS artifact (not used yet)
81e7df1 Adds skill icons for the two mage skills
18effa2 Rogue backstab skill now triggers bleeding as well
c043ad0 Implements the erupt skill
223a3b0 Implements the blink skill
2ca78e2 Adds monster bleed damage every turn
0476d4d Introduces particle emitters on monsters
826cbe1 A quick start to the Mage
fcc6527 Minor fix
b409b76 Improved build instruction structure
b075390 React to PR feedback
86e6f66 Adds Windows compilation instructions
c0a4a4e Make ninja builds smoother

View File

@ -0,0 +1,2 @@
6d323ca Adds steam achievements and LB for the Mage class
9aefed0 Adds an dev env setup script

View File

View File

@ -0,0 +1 @@
4edeb17 Fixes cross save problems due to architecture differences

View File

@ -0,0 +1 @@
e636ec1 Don't delete saves from other architectures on death

View File

@ -0,0 +1,12 @@
#!/bin/sh
mkdir -p _build/debug
mkdir -p _build/release
cd _build/debug
cmake -DCMAKE_BUILD_TYPE=Debug ../..
cd -
cd _build/release
cmake -DCMAKE_BUILD_TYPE=Release ../..
cd -

View File

@ -7,12 +7,12 @@ endif ()
add_executable(checksumtool
src/checksum
src/checksum.c
)
target_compile_definitions(checksumtool PUBLIC EXECUTABLE=1)
add_library(checksum
src/checksum
src/checksum.c
)
IF ( MSVC )

View File

@ -419,7 +419,7 @@ local function build_normal_room(room)
if not crumbling then
pitsAdded = layoutparser.add_pits_to_room(room)
end
if not pitsAdded then
if not pitsAdded and (not crumbling or CURRENT_LEVEL > 3) then
interiorWallsAdded = layoutparser.add_walls_to_room(room)
end

View File

@ -9,7 +9,7 @@
# compile, using preprocessor checks for platform-specific bits instead of
# testing in here.
cmake_minimum_required(VERSION 2.8.4)
cmake_minimum_required(VERSION 3.6)
project(PhysicsFS)
set(PHYSFS_VERSION 3.0.1)

Binary file not shown.

View File

@ -47,7 +47,7 @@ actiontext_render(ActionText *t, Camera *cam)
if (t->dead)
return;
if (!t->dead && !timer_started(t->timer))
if (!timer_started(t->timer))
timer_start(t->timer);
if (timer_get_ticks(t->timer) < 500) {

View File

@ -32,7 +32,7 @@ actiontextbuilder_init(SDL_Renderer *renderer)
}
void
actiontextbuilder_create_text(const char *msg, SDL_Color color, Position *p)
actiontextbuilder_create_text(const char *msg, SDL_Color color, const Position *p)
{
assert (gRenderer != NULL);
Sprite *sprite = sprite_create();

View File

@ -34,7 +34,7 @@ void
actiontextbuilder_render(Camera*);
void
actiontextbuilder_create_text(const char *msg, SDL_Color, Position*);
actiontextbuilder_create_text(const char *msg, SDL_Color, const Position*);
void
actiontextbuilder_close(void);

View File

@ -86,7 +86,7 @@ animation_render(Animation *animation, Camera *camera)
}
void
animation_set_frames(Animation *animation, AnimationClip clips[])
animation_set_frames(Animation *animation, const AnimationClip clips[])
{
for (size_t i = 0; i < animation->clipCount; i++) {
animation->clips[i] = clips[i];

View File

@ -53,7 +53,7 @@ void
animation_load_texture(Animation *, const char *path, SDL_Renderer*);
void
animation_set_frames(Animation*, AnimationClip clips[]);
animation_set_frames(Animation*, const AnimationClip clips[]);
void
animation_run(Animation*);

View File

@ -65,6 +65,21 @@ artifact_set_effect(Artifact *a, MagicalEffect effect)
a->info.name = "Shadow cloak";
a->info.desc = "You feel more in phase with the world";
break;
case SKILL_RADIUS:
a->info.name = "Magic wand";
a->info.desc = "Your magic has greater reach";
break;
case DAGGER_BOUNCE:
a->info.name = "Magnet";
a->info.desc = "You are attractive to daggers";
break;
case EXPLOSIVE_KILLS:
a->info.name = "Stick of dynamite";
a->info.desc = "You are an explosive slayer";
break;
case VOLATILE_DAGGERS:
a->info.name = "Glowing dagger";
a->info.desc = "Your daggers are volatile";
default:
break;
}
@ -78,7 +93,10 @@ static int WarriorArtifacts[] = {
PUSH_BACK, // 4
FEAR_INDUCING, // 5
INCREASED_STUN, // 6
CHARGE_THROUGH // 7
DAGGER_BOUNCE, // 7
EXPLOSIVE_KILLS, // 8
VOLATILE_DAGGERS, // 9
CHARGE_THROUGH // 10
};
static int RogueArtifacts[] = {
@ -89,7 +107,54 @@ static int RogueArtifacts[] = {
PUSH_BACK, // 4
FEAR_INDUCING, // 5
INCREASED_STUN, // 6
PHASE_IMPROVEMENT // 7
DAGGER_BOUNCE, // 7
EXPLOSIVE_KILLS, // 8
VOLATILE_DAGGERS, // 9
PHASE_IMPROVEMENT // 10
};
static int MageArtifacts[] = {
IMPROVED_HEARING, // 0
TRAP_AVOIDANCE, // 1
PIERCING_DAGGERS, // 2
DAGGER_RECOVERY, // 3
PUSH_BACK, // 4
FEAR_INDUCING, // 5
INCREASED_STUN, // 6
DAGGER_BOUNCE, // 7
EXPLOSIVE_KILLS, // 8
VOLATILE_DAGGERS, // 9
SKILL_RADIUS // 10
};
/* Not in play yet */
static int PaladinArtifacts[] = {
IMPROVED_HEARING, // 0
TRAP_AVOIDANCE, // 1
PIERCING_DAGGERS, // 2
DAGGER_RECOVERY, // 3
PUSH_BACK, // 4
FEAR_INDUCING, // 5
INCREASED_STUN, // 6
DAGGER_BOUNCE, // 7
EXPLOSIVE_KILLS, // 8
VOLATILE_DAGGERS, // 9
SKILL_RADIUS // 10
};
/* Not in play yet */
static int EngineerArtifacts[] = {
IMPROVED_HEARING, // 0
TRAP_AVOIDANCE, // 1
PIERCING_DAGGERS, // 2
DAGGER_RECOVERY, // 3
PUSH_BACK, // 4
FEAR_INDUCING, // 5
INCREASED_STUN, // 6
DAGGER_BOUNCE, // 7
EXPLOSIVE_KILLS, // 8
VOLATILE_DAGGERS, // 9
PHASE_IMPROVEMENT // 10
};
static void
@ -109,17 +174,17 @@ add_level_sprite(Artifact *a)
Artifact *
artifact_create_random(Player *p, Uint8 level)
{
int option = -1;
if (p->stats.lvl >= 4)
option = get_random(7);
else if (p->stats.lvl >= 3)
option = get_random(6);
else
option = get_random(5);
int option = get_random(9);
int * artifactPool = NULL;
if (p->class == ROGUE)
artifactPool = RogueArtifacts;
else if (p->class == MAGE)
artifactPool = MageArtifacts;
else if (p->class == PALADIN)
artifactPool = PaladinArtifacts;
else if (p->class == ENGINEER)
artifactPool = EngineerArtifacts;
else
artifactPool = WarriorArtifacts;
@ -196,6 +261,26 @@ artifact_sprite_for(MagicalEffect effect)
sprite_set_texture(sprite, t, 0);
sprite->clip = CLIP16(1*16, 5*16);
break;
case SKILL_RADIUS:
t = texturecache_add("Items/Wand.png");
sprite_set_texture(sprite, t, 0);
sprite->clip = CLIP16(2*16, 0);
break;
case DAGGER_BOUNCE:
t = texturecache_add("Extras/Artifacts.png");
sprite_set_texture(sprite, t, 0);
sprite->clip = CLIP16(0, 0);
break;
case EXPLOSIVE_KILLS:
t = texturecache_add("Extras/Artifacts.png");
sprite_set_texture(sprite, t, 0);
sprite->clip = CLIP16(32, 0);
break;
case VOLATILE_DAGGERS:
t = texturecache_add("Extras/Artifacts.png");
sprite_set_texture(sprite, t, 0);
sprite->clip = CLIP16(16, 0);
break;
default:
break;
}
@ -218,7 +303,7 @@ artifact_create(MagicalEffect effect)
}
Artifact *
artifact_copy(Artifact *a)
artifact_copy(const Artifact *a)
{
Artifact *new = ec_malloc(sizeof(Artifact));
*new = *a;

View File

@ -30,6 +30,10 @@ typedef enum MagicalEffect {
INCREASED_STUN,
CHARGE_THROUGH,
PHASE_IMPROVEMENT,
SKILL_RADIUS,
DAGGER_BOUNCE,
EXPLOSIVE_KILLS,
VOLATILE_DAGGERS,
LAST_ARTIFACT_EFFECT // Sentinel
} MagicalEffect;
@ -62,7 +66,7 @@ Artifact *
artifact_create(MagicalEffect);
Artifact *
artifact_copy(Artifact*);
artifact_copy(const Artifact*);
void
artifact_render(Artifact*, Camera*);

36
src/config.h Normal file
View File

@ -0,0 +1,36 @@
/*
* BreakHack - A dungeone crawler RPG
* Copyright (C) 2018 Linus Probert <linus.probert@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef CONFIG_H_
#define CONFIG_H_
// #cmakedefine _WIN32 ${WIN32}
#define GAME_TITLE "BreakHack"
#define MAJOR_VERSION 4
#define MINOR_VERSION 0
#define PATCH_VERSION 3
#define RELEASE_TYPE ""
/* Checksums */
#define SO_LIBSTEAM_CHECKSUM 0x1f5786b
#define DLL_LIBSTEAM_CHECKSUM 0x18dba28
#endif // CONFIG_H_

View File

@ -67,6 +67,7 @@
#define C_RED (SDL_Color) { 255, 0, 0, 255 }
#define C_GREEN (SDL_Color) { 0, 255, 0, 255 }
#define C_BLUE (SDL_Color) { 60, 134, 252, 255 }
#define C_LIGHTBLUE (SDL_Color) { 143, 178, 234, 255 }
#define C_YELLOW (SDL_Color) { 255, 255, 0, 255 }
#define C_BLACK (SDL_Color) { 0, 0, 0, 255 }
#define C_PURPLE (SDL_Color) { 137, 16, 229, 255 }

63
src/effect_util.c Normal file
View File

@ -0,0 +1,63 @@
/*
* BreakHack - A dungeone crawler RPG
* Copyright (C) 2018 Linus Probert <linus.probert@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "gui.h"
#include "monster.h"
#include "roommatrix.h"
#include "effect_util.h"
void
effect_damage_surroundings(Position *pos,
RoomMatrix *rm,
Player *player,
Stats *attackerStats,
unsigned int radius,
unsigned int pushRadius,
bool damagePlayer)
{
Position roomPos = position_to_matrix_coords(pos);
for (Sint32 i = -radius; i <= (Sint32) radius; ++i) {
for (Sint32 j = -radius; j <= (Sint32) radius; ++j) {
if (i == 0 && j == 0)
continue;
Position matrixPos = POS(roomPos.x + i, roomPos.y + j);
if (!position_in_roommatrix(&matrixPos))
continue;
RoomSpace *r = &rm->spaces[matrixPos.x][matrixPos.y];
if (r->monster) {
CombatResult result = stats_fight(attackerStats, &r->monster->stats);
monster_hit(r->monster, result.dmg, result.critical);
gui_log("%s takes %d damage from the explosion", r->monster->label, result.dmg);
Vector2d dir = vector2d_to_direction(&VEC2D((float) i, (float) j));
for (unsigned int k = 0; k < pushRadius; ++k) {
monster_push(r->monster,
player,
rm,
dir);
}
} else if (r->player && damagePlayer) {
CombatResult result = stats_fight(attackerStats, &r->player->stats);
player_hit(r->player, result.dmg);
gui_log("You take %d damage from the explosion", result.dmg);
}
}
}
}

31
src/effect_util.h Normal file
View File

@ -0,0 +1,31 @@
/*
* BreakHack - A dungeone crawler RPG
* Copyright (C) 2018 Linus Probert <linus.probert@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdlib.h>
#include "roommatrix.h"
#include "position.h"
#include "stats.h"
void
effect_damage_surroundings(Position *pos,
RoomMatrix *rm,
Player *player,
Stats *attackerStats,
unsigned int radius,
unsigned int pushRadius,
bool damagePlayer);

View File

@ -112,21 +112,21 @@ init_sprites(Gui *gui, Camera *cam)
linkedlist_append(&gui->sprites, create_xp_sprite(
t,
(SDL_Rect) { 6 * 16, 0, 16, 16 },
(Position) { 16, POS_Y_XPBAR }
POS(16, POS_Y_XPBAR)
));
// Right end
linkedlist_append(&gui->sprites, create_xp_sprite(
t,
(SDL_Rect) { 8 * 16, 0, 16, 16 },
(Position) { 16 + (16 * 7), POS_Y_XPBAR }
POS(16 + (16 * 7), POS_Y_XPBAR)
));
for (i = 1; i < 7; ++i) {
linkedlist_append(&gui->sprites, create_xp_sprite(
t,
(SDL_Rect) { 7 * 16, 0, 16, 16 },
(Position) { 16 + (i * 16), POS_Y_XPBAR }
POS(16 + (i * 16), POS_Y_XPBAR)
));
}
@ -134,7 +134,7 @@ init_sprites(Gui *gui, Camera *cam)
linkedlist_append(&gui->xp_bar, create_xp_sprite(
t,
(SDL_Rect) { 6 * 16, 4 * 16, 16, 16 },
(Position) { 16 + (i * 16), POS_Y_XPBAR }
POS(16 + (i * 16), POS_Y_XPBAR)
));
}
@ -144,7 +144,7 @@ init_sprites(Gui *gui, Camera *cam)
s->fixed = true;
sprite_set_texture(s, t, 0);
s->clip = CLIP16(0, 0);
s->pos = (Position) { 16, POS_Y_COLLECTABLES };
s->pos = POS(16, POS_Y_COLLECTABLES);
linkedlist_append(&gui->sprites, s);
t = texturecache_add("Items/Money.png");
@ -152,7 +152,7 @@ init_sprites(Gui *gui, Camera *cam)
s->fixed = true;
sprite_set_texture(s, t, 0);
s->clip = CLIP16(16, 16);
s->pos = (Position) { 16, POS_Y_COLLECTABLES + 16 };
s->pos = POS(16, POS_Y_COLLECTABLES + 16);
linkedlist_append(&gui->sprites, s);
t = texturecache_add("Items/ShortWep.png");
@ -160,9 +160,25 @@ init_sprites(Gui *gui, Camera *cam)
s->fixed = true;
sprite_set_texture(s, t, 0);
s->clip = CLIP16(0, 0);
s->pos = (Position) { 16, POS_Y_COLLECTABLES + 32 };
s->pos = POS(16, POS_Y_COLLECTABLES + 32);
linkedlist_append(&gui->sprites, s);
t = texturecache_add("Extras/Keys.png");
s = sprite_create();
s->fixed = true;
sprite_set_texture(s, t, 0);
s->clip = CLIP16(0, 0);
s->pos = POS(58, POS_Y_XPBAR + 15 + (3*14));
gui->silverKey = s;
t = texturecache_add("Extras/Keys.png");
s = sprite_create();
s->fixed = true;
sprite_set_texture(s, t, 0);
s->clip = CLIP16(16, 0);
s->pos = POS(74, POS_Y_XPBAR + 15 + (3*14));
gui->goldKey = s;
gui->statsFrame = gui_util_create_frame_sprite(RIGHT_GUI_WIDTH/16,
STATS_GUI_HEIGHT/16,
cam);
@ -173,6 +189,10 @@ init_sprites(Gui *gui, Camera *cam)
gui->miniMapFrame = gui_util_create_frame_sprite(RIGHT_GUI_WIDTH/16,
MINIMAP_GUI_HEIGHT/16,
cam);
texture_load_from_text(gui->labels[KEY_LABEL]->textures[0], "Keys:", C_WHITE, C_BLACK, cam->renderer);
gui->labels[KEY_LABEL]->dim = gui->labels[KEY_LABEL]->textures[0]->dim;
}
Gui*
@ -197,28 +217,33 @@ gui_create(Camera *cam)
texture_load_font(gui->event_message, "GUI/SDS_8x8.ttf", EVENT_MESSAGE_FONT_SIZE, 2);
gui->event_message_timer = _timer_create();
gui->labels[CURRENT_XP_LABEL] = create_label_sprite((Position) { 16, POS_Y_XPBAR + 18 });
gui->labels[LEVEL_LABEL] = create_label_sprite((Position) { 16, POS_Y_XPBAR + 18 + 14 });
gui->labels[CURRENT_XP_LABEL] = create_label_sprite(POS(16, POS_Y_XPBAR + 18));
gui->labels[LEVEL_LABEL] = create_label_sprite(POS(16, POS_Y_XPBAR + 18 + 14));
gui->labels[DUNGEON_LEVEL_LABEL] =
create_label_sprite((Position) {
create_label_sprite(POS(
16,
POS_Y_XPBAR + 18 + (2*14)
});
));
gui->labels[KEY_LABEL] =
create_label_sprite(POS(
16,
POS_Y_XPBAR + 18 + (3*14)
));
gui->labels[HEALTH_POTION_LABEL] =
create_label_sprite((Position) {
create_label_sprite(POS(
32,
POS_Y_COLLECTABLES + 5
});
));
gui->labels[GOLD_LABEL] =
create_label_sprite((Position) {
create_label_sprite(POS(
32,
POS_Y_COLLECTABLES + 16 + 5
});
));
gui->labels[DAGGER_LABEL] =
create_label_sprite((Position) {
create_label_sprite(POS(
32,
POS_Y_COLLECTABLES + 32 + 5
});
));
gui_malloc_log();
gui_malloc_eventmessages();
@ -251,7 +276,7 @@ set_max_health(Gui *gui, int max)
sprite->fixed = true;
sprite->animate = false;
sprite->clip = (SDL_Rect) { 0, 16, 16, 16 };
sprite->pos = (Position) { 16 + (i%8)*16, 16 + ((i-(i%8))/8)*16 };
sprite->pos = POS(16 + (i%8)*16, 16 + ((i-(i%8))/8)*16);
sprite_set_texture(sprite, texture0, 0);
sprite_set_texture(sprite, texture1, 1);
linkedlist_append(&gui->health, sprite);
@ -392,6 +417,10 @@ gui_update_player_stats(Gui *gui, Player *player, Map *map, SDL_Renderer *render
gui->labels[LEVEL_LABEL]->dim = gui->labels[LEVEL_LABEL]->textures[0]->dim;
last_level = data.level;
}
gui->silverKey->hidden = !(player->equipment.keys & LOCK_SILVER);
gui->goldKey->hidden = !(player->equipment.keys & LOCK_GOLD);
gui->goldKey->pos.x = gui->silverKey->hidden ? 58 : 74;
}
void
@ -410,6 +439,8 @@ gui_render_panel(Gui *gui, Camera *cam)
sprite_render(s, cam);
item = item->next;
}
sprite_render(gui->silverKey, cam);
sprite_render(gui->goldKey, cam);
item = gui->sprites;
while (item != NULL) {
Sprite *s = item->data;
@ -622,5 +653,8 @@ gui_destroy(Gui *gui)
for (int i = 0; i < LABEL_COUNT; ++i)
sprite_destroy(gui->labels[i]);
sprite_destroy(gui->silverKey);
sprite_destroy(gui->goldKey);
free(gui);
}

View File

@ -37,6 +37,7 @@ typedef enum Label_e {
CURRENT_XP_LABEL,
GOLD_LABEL,
DUNGEON_LEVEL_LABEL,
KEY_LABEL,
HEALTH_POTION_LABEL,
DAGGER_LABEL,
LABEL_COUNT
@ -51,6 +52,8 @@ typedef struct Gui {
Sprite *miniMapFrame;
Sprite *labels[LABEL_COUNT];
Sprite *activeTooltip;
Sprite *goldKey;
Sprite *silverKey;
Texture *log_lines[LOG_LINES_COUNT];
Texture *event_message;
Timer *event_message_timer;

View File

@ -97,7 +97,7 @@ hiscore_init(void)
static void
save_hiscore(double gold, int lvl, int dlvl)
{
const char *query = "INSERT INTO hiscore(gold, playerLevel, dungeonLevel) values (?, ?, ?)";
const char *query = "INSERT INTO hiscore(time, gold, playerLevel, dungeonLevel) values (datetime('now','+8 years'), ?, ?, ?)";
sqlite3_stmt *stmt = db_prepare(db, query);
debug("Saving high score: %dg %dpl %dl",

View File

@ -156,6 +156,7 @@ static void
pickup_silver_key(Item *item, Player *player)
{
gui_log("You pickup %s", item->label);
mixer_play_effect(KEY_PICKUP);
player->equipment.keys |= LOCK_SILVER;
}
@ -163,6 +164,7 @@ static void
pickup_gold_key(Item *item, Player *player)
{
gui_log("You pickup %s", item->label);
mixer_play_effect(KEY_PICKUP);
player->equipment.keys |= LOCK_GOLD;
}
@ -170,17 +172,21 @@ Item *
item_builder_build_key(unsigned int type)
{
char label[20];
SDL_Rect clip = CLIP16(0, 0);
Item *item;
switch (type) {
case 1:
m_sprintf(label, 20, "a silver key");
item = create_item("Items/Key.png", NULL, clip, &pickup_silver_key);
item = create_item("Extras/Keys.png",
NULL,
CLIP16(0, 0),
&pickup_silver_key);
break;
case 2:
m_sprintf(label, 20, "a gold key");
item = create_item("Items/Key.png", NULL, clip, &pickup_gold_key);
clip.x = 16;
item = create_item("Extras/Keys.png",
NULL,
CLIP16(16, 0),
&pickup_gold_key);
break;
default:
fatal("Bad keytype provided");
@ -198,7 +204,6 @@ create_treasure(int current_level)
unsigned int value;
amt = (unsigned int) 1 + get_random(5*current_level) % 40;
amt = amt == 0 ? 1 : amt;
if (current_level > 9) {
highest_treasure = PLATINUM;

View File

@ -142,3 +142,4 @@ unsigned int linkedlist_size(LinkedList *head)
}
return 1 + linkedlist_size(head->next);
}

View File

@ -44,3 +44,4 @@ void linkedlist_destroy(LinkedList **head);
unsigned int linkedlist_size(LinkedList *head);
#endif // LINKEDLIST_H_

View File

@ -58,19 +58,28 @@
#include "sprite_util.h"
#include "event.h"
#include "config.h"
#include "save.h"
#ifdef STEAM_BUILD
#include "checksum.h"
#include "steam/steamworks_api_wrapper.h"
#endif // STEAM_BUILD
#if defined(__amigaos4__)
#define USED __attribute__((used))
#define VSTRING "BreakHack 4.0.3r2 (04.07.2023)"
#define VERSTAG "\0$VER: " VSTRING
static const char *stack USED = "$STACK:102400";
static const char *version USED = VERSTAG;
#endif
static char *artifacts_tooltip[] = {
"CONGRATULATIONS!", "",
"",
" You just picked up your first artifact!", "",
"",
" Your current artifacts and corresponding level are", "",
" listed next to your skills." "",
" listed next to your skills.", "",
"",
"",
" Artifacts have mystical effects that improve your offensive", "",
@ -94,8 +103,8 @@ static char *skills_tooltip[] = {
"",
"",
" SKILL INFO: SHIFT + <N>", "",
" Where <N> is the number corresponding to the skill", "",
" Eg. 1, 2, 3, 4, 5", "",
" Where <N> is the number corresponding to", "",
" the skill, i.e. 1, 2, 3, 4, 5", "",
"",
" DISABLE TOOLTIPS: CTRL + D", "",
"",
@ -237,7 +246,7 @@ bool initSDL(void)
m_sprintf(title_buffer, 100, "%s %d.%d.%d %s", GAME_TITLE, MAJOR_VERSION, MINOR_VERSION, PATCH_VERSION, RELEASE_TYPE);
gWindow = SDL_CreateWindow(title_buffer,
SDL_WINDOWPOS_UNDEFINED,
SDL_WINDOWPOS_UNDEFINED,
SDL_WINDOWPOS_UNDEFINED,
(int)(SCREEN_WIDTH * renderScale),
(int)(SCREEN_HEIGHT * renderScale),
SDL_WINDOW_SHOWN);
@ -272,7 +281,7 @@ bool initSDL(void)
debug("Disabling text input");
SDL_StopTextInput();
}
return true;
}
@ -405,6 +414,8 @@ on_character_select(const char *str)
playerClass = WARRIOR;
else if (strcmp(str, "rogue") == 0)
playerClass = ROGUE;
else if (strcmp(str, "mage") == 0)
playerClass = MAGE;
startGame();
}
@ -418,6 +429,56 @@ goToCharacterMenu(void *unused)
gGameState = CHARACTER_MENU;
}
static void
choose_music(void)
{
if (cLevel > (unsigned int) (quickGame ? 11 : 19)) {
mixer_play_music(BOSS_MUSIC0);
} else if (cLevel % (quickGame ? 3 : 5) == 0) {
gui_log("You sense something powerful in the vicinity");
mixer_play_music(BOSS_MUSIC0);
} else {
mixer_play_music(GAME_MUSIC0 + get_random(2));
}
}
static void
continueGame(void *unused)
{
(void) unused;
const Save *save = save_get();
quickGame = save->quickGame;
arcadeGame = save->arcadeGame;
playerClass = save->player_class;
cLevel = save->map_level;
set_random_seed(save->seed);
debug("Loading seed: %d", save->seed);
debug("Loading map level: %d", save->map_level);
gGameState = PLAYING;
if (gPlayer)
player_destroy(gPlayer);
gPlayer = player_create(playerClass, gCamera);
// Load player from save
gPlayer->daggers = save->player_daggers;
gPlayer->xp = save->player_xp;
gPlayer->stateData = save->player_state;
gPlayer->stats = save->player_stats;
gPlayer->stat_data = save->player_player_stats;
gPlayer->potion_sips = save->player_potion_sips;
gPlayer->equipment = save->player_equipment;
gPlayer->gold = save->player_gold;
choose_music();
resetGame();
skillbar_reset(gSkillBar);
gui_clear_message_log();
gui_log("The Dungeon Crawl continues!");
gui_event_message("Welcome back!");
}
static void
startRegularGame(void *unused)
{
@ -457,6 +518,7 @@ static void
goToMainMenu(void *unused)
{
UNUSED(unused);
save_load();
gui_clear_message_log();
gGameState = MENU;
menu_destroy(inGameMenu);
@ -472,37 +534,47 @@ static void
goToGameSelectMenu(void *unused)
{
UNUSED(unused);
static TEXT_MENU_ITEM menuItems[] = {
{
"STANDARD GAME",
"Standard 20 level game, recommended for new players",
startRegularGame
},
int item_count = 3;
#ifdef STEAM_BUILD
{
"WEEKLY CHALLENGE",
"Quick game with weekly leaderboards at breakhack.net",
startWeeklyGame
},
item_count += 1;
#endif
{
"QUICK GAME",
"Shorter 12 level game, with more action earlier in the game",
startQuickGame
},
{
"ARCADE GAME",
"One big level with lots of action",
startArcadeGame
}
if (save_exists()) {
item_count += 1;
}
TEXT_MENU_ITEM *menuItems = ec_malloc(item_count * sizeof(TEXT_MENU_ITEM));
int i = 0;
if (save_exists()) {
menuItems[i++] = (TEXT_MENU_ITEM) {
"CONTINUE",
"Continue your last session",
continueGame,
};
}
menuItems[i++] = (TEXT_MENU_ITEM) {
"STANDARD GAME",
"Standard 20 level game, recommended for new players",
startRegularGame
};
#ifdef STEAM_BUILD
menuItems[i++] = (TEXT_MENU_ITEM) {
"WEEKLY CHALLENGE",
"Quick game with weekly leaderboards at breakhack.net",
startWeeklyGame
};
#endif
menuItems[i++] = (TEXT_MENU_ITEM) {
"QUICK GAME",
"Shorter 12 level game, with more action earlier in the game",
startQuickGame
};
menuItems[i++] = (TEXT_MENU_ITEM) {
"ARCADE GAME",
"One big level with lots of action",
startArcadeGame
};
#ifdef STEAM_BUILD
int count = 4;
#else
int count = 3;
#endif
menu_create_text_menu(&gameSelectMenu, &menuItems[0], count, gRenderer);
menu_create_text_menu(&gameSelectMenu, menuItems, item_count, gRenderer);
free(menuItems);
gGameState = GAME_SELECT;
}
@ -689,6 +761,7 @@ init(void)
event_register_listener(on_event_callback);
settings_init();
save_init();
hiscore_init();
initMainMenu();
@ -849,7 +922,7 @@ end_game_details(void)
{
gui_log("You earned %.2f gold", gPlayer->gold);
gui_event_message("You earned %.2f gold", gPlayer->gold);
if (hiscore_get_top_gold() < gPlayer->gold) {
gui_event_message("NEW HIGHSCORE");
gui_log("NEW HIGHSCORE");
@ -872,17 +945,17 @@ check_next_level(void)
return;
}
if (tile->levelExit) {
mixer_play_effect(NEXT_LEVEL);
++cLevel;
if (cLevel > (unsigned int) (quickGame ? 11 : 19)) {
mixer_play_music(BOSS_MUSIC0);
} else if (cLevel % (quickGame ? 3 : 5) == 0) {
gui_log("You sense something powerful in the vicinity");
mixer_play_music(BOSS_MUSIC0);
} else {
mixer_play_music(GAME_MUSIC0 + get_random(2));
if (!weeklyGame) {
save_save(get_random_seed(),
cLevel,
quickGame,
arcadeGame,
gPlayer);
}
mixer_play_effect(NEXT_LEVEL);
choose_music();
if (!gameCompleted()) {
resetGame();
}
@ -1024,9 +1097,6 @@ render_game(void)
map_render_top_layer(gMap, gRoomMatrix, gCamera);
if (gPlayer->class == MAGE || gPlayer->class == PALADIN)
roommatrix_render_mouse_square(gRoomMatrix, gCamera);
roommatrix_render_lightmap(gRoomMatrix, gCamera);
actiontextbuilder_render(gCamera);
gui_render_event_message(gGui, gCamera);
@ -1087,10 +1157,13 @@ register_scores(void)
steam_set_achievement(ROGUE_LIKE);
steam_register_rogue_score((int) gPlayer->gold,
(int32_t*) &details, 1);
}
else if (gPlayer->class == WARRIOR) {
} else if (gPlayer->class == WARRIOR) {
steam_register_warrior_score((int) gPlayer->gold,
(int32_t*) &details, 1);
} else if (gPlayer->class == MAGE) {
steam_set_achievement(MAGICAL);
steam_register_mage_score((int) gPlayer->gold,
(int32_t*) &details, 1);
}
}
#endif
@ -1114,6 +1187,7 @@ run_game(void)
camera_shake(VECTOR2D_RIGHT, 800);
gui_log("The dungeon consumed you");
gui_event_message("You died!");
save_clear();
end_game_details();
mixer_play_effect(SPLAT);
gGameState = GAME_OVER;
@ -1347,6 +1421,7 @@ void close(void)
texturecache_close();
settings_close();
hiscore_close();
save_close();
#ifdef STEAM_BUILD
steam_shutdown();
@ -1381,7 +1456,7 @@ validate_lib_checksum(void)
fatal("Unable to open %s for reading\n", file);
}
unsigned calculated = checksum_fp(fp);
unsigned calculated = checksum_fp(fp);
fclose(fp);
if (calculated != expected) {
@ -1401,7 +1476,7 @@ int main(int argc, char *argv[])
#endif // STEAM_BUILD
PHYSFS_init(argv[0]);
#ifndef DEBUG
#if !defined(DEBUG) && !defined(__amigaos4__)
PHYSFS_mount("assets.pack", NULL, 0);
PHYSFS_mount("data.pack", NULL, 0);
#else
@ -1417,7 +1492,7 @@ int main(int argc, char *argv[])
return 1;
if (settings_get()->fullscreen_enabled) {
// Game starts in windowed mode so this will
// Game starts in windowed mode so this will
// change to fullscreen
toggle_fullscreen();
}
@ -1427,3 +1502,4 @@ int main(int argc, char *argv[])
return 0;
}

View File

@ -106,22 +106,25 @@ menu_create_character_selector(void (*onCharacterSelect)(const char *), Camera *
{
const char *spriteSheets[] = {
"Commissions/Warrior.png",
"Commissions/Rogue.png"
"Commissions/Rogue.png",
"Commissions/Mage.png"
};
static char *callbackData[] = {
static const char *callbackData[] = {
"warrior",
"rogue"
"rogue",
"mage"
};
static char *descriptions[] = {
"Play as the warrior",
"Play as the rogue",
"Play as the mage"
};
Menu *menu = menu_create();
int xoffset = 224;
for (size_t i = 0; i < 2; ++i) {
int xoffset = 168;
for (size_t i = 0; i < 3; ++i) {
Sprite *s1 = sprite_create();
sprite_set_texture(s1, texturecache_add(spriteSheets[i]), 0);
s1->clip = CLIP16(0, 48);
@ -139,7 +142,7 @@ menu_create_character_selector(void (*onCharacterSelect)(const char *), Camera *
menu_item_add(menu, s1, s2, (void (*)(void *)) onCharacterSelect);
MenuItem *item = linkedlist_get(&menu->items, (Uint32) i);
item->button->usrdata = callbackData[i];
xoffset += 224;
xoffset += 168;
linkedlist_append(&menu->descriptions, descriptions[i]);
}

View File

@ -26,8 +26,8 @@
#include "sprite.h"
typedef struct TEXT_MENU_ITEM {
char label[20];
char description[100];
char *label;
char *description;
void (*callback)(void*);
} TEXT_MENU_ITEM;

View File

@ -89,6 +89,10 @@ load_effects(void)
effects[FADE_OUT] = load_effect("Sounds/FX/fade_out.wav");
effects[BURST] = load_effect("Sounds/FX/burst.wav");
effects[DOOR_OPEN] = load_effect("Sounds/FX/door_open.wav");
effects[KEY_PICKUP] = load_effect("Sounds/FX/key_pickup.wav");
effects[BLINK_EFFECT] = load_effect("Sounds/FX/blink.wav");
effects[BLAST_EFFECT] = load_effect("Sounds/FX/blast.wav");
effects[EXPLOSION_EFFECT] = load_effect("Sounds/FX/explosion.wav");
}
void
@ -110,7 +114,7 @@ mixer_toggle_sound(void)
}
bool
mixer_toggle_music(GameState *state)
mixer_toggle_music(const GameState *state)
{
Settings *settings = settings_get();
settings->music_enabled = !settings->music_enabled;

View File

@ -65,6 +65,10 @@ typedef enum Fx_t {
FADE_OUT,
BURST,
DOOR_OPEN,
KEY_PICKUP,
BLINK_EFFECT,
BLAST_EFFECT,
EXPLOSION_EFFECT,
LAST_EFFECT
} Fx;
@ -75,7 +79,7 @@ bool
mixer_toggle_sound(void);
bool
mixer_toggle_music(GameState*);
mixer_toggle_music(const GameState*);
void
mixer_play_effect(Fx fx);

View File

@ -128,15 +128,20 @@ damage_surroundings(Monster *m, RoomMatrix *rm)
for (Sint32 j = -1; j <= 1; ++j) {
if (i == 0 && j == 0)
continue;
RoomSpace *r = &rm->spaces[roomPos.x + i][roomPos.y + j];
Position matrixPos = POS(roomPos.x + i, roomPos.y + j);
if (!position_in_roommatrix(&matrixPos))
continue;
RoomSpace *r = &rm->spaces[matrixPos.x][matrixPos.y];
if (r->monster) {
int dmg = stats_fight(&m->stats, &r->monster->stats);
monster_hit(r->monster, dmg);
gui_log("%s takes %d damage from the explosion", r->monster->label, dmg);
CombatResult result = stats_fight(&m->stats, &r->monster->stats);
monster_hit(r->monster, result.dmg, result.critical);
gui_log("%s takes %d damage from the explosion", r->monster->label, result.dmg);
} else if (r->player) {
int dmg = stats_fight(&m->stats, &r->player->stats);
player_hit(r->player, dmg);
gui_log("You take %d damage from the explosion", dmg);
CombatResult result = stats_fight(&m->stats, &r->player->stats);
player_hit(r->player, result.dmg);
gui_log("You take %d damage from the explosion", result.dmg);
}
}
}
@ -209,6 +214,9 @@ handle_sentinel_behaviour(Monster *m, RoomMatrix *rm)
static void
monster_behaviour_check(Monster *m, RoomMatrix *rm)
{
if (m->state.stepsSinceChange < m->state.forceCount)
return;
switch (m->behaviour) {
case GUERILLA:
case SORCERER:
@ -233,6 +241,24 @@ monster_behaviour_check(Monster *m, RoomMatrix *rm)
}
}
static void
create_emitters(Monster *m)
{
// Bloodlust
ParticleEmitter *emitter = particle_emitter_create();
emitter->timestep = 0;
emitter->enabled = false;
emitter->particle_func = particle_engine_bloodlust;
m->emitters.bloodlust = emitter;
emitter = particle_emitter_create();
emitter->timestep = 1000;
emitter->enabled = false;
emitter->particle_func = particle_engine_bleed;
m->emitters.bleed = emitter;
}
Monster*
monster_create(void)
{
@ -265,9 +291,9 @@ monster_create(void)
m->stateIndicator.shownOnPlayerRoomEnter = false;
m->state.forceCount = 0;
m->boss = false;
m->bloodlust = false;
m->items.keyType = LOCK_NONE;
monster_set_behaviour(m, NORMAL);
create_emitters(m);
return m;
}
@ -291,14 +317,14 @@ has_collided(Monster *monster, RoomMatrix *matrix, Vector2d direction)
RoomSpace *space = roommatrix_get_space_for(matrix, &monster->sprite->pos);
if (space->player && monster->state.current == AGRESSIVE) {
unsigned int dmg = stats_fight(&monster->stats,
&space->player->stats);
CombatResult result = stats_fight(&monster->stats,
&space->player->stats);
player_hit(space->player, dmg);
player_hit(space->player, result.dmg);
if (dmg > 0) {
if (result.dmg > 0) {
gui_log("%s hit you for %u damage",
monster->label, dmg);
monster->label, result.dmg);
camera_shake(direction, 300);
} else {
gui_log("%s missed you", monster->label);
@ -448,7 +474,7 @@ monster_coward_walk(Monster *m, RoomMatrix *rm)
}
static void
on_monster_move(Monster *m, Position *origPos, Map *map, RoomMatrix *rm)
on_monster_move(Monster *m, const Position *origPos, Map *map, RoomMatrix *rm)
{
Position currentTilePos = position_to_matrix_coords(&m->sprite->pos);
Player *player = rm->spaces[rm->playerRoomPos.x][rm->playerRoomPos.y].player;
@ -533,9 +559,23 @@ monster_perform_aoe_attack(Monster *m, RoomMatrix *rm)
return false;
}
static void
apply_bleed_damage(Player *p, Monster *m)
{
if (!m->emitters.bleed->enabled)
return;
uint32_t dmg = m->stats.lvl * 2;
monster_hit(m, dmg, false);
m->stats.hp -= dmg;
player_monster_kill_check(p, m);
}
bool
monster_move(Monster *m, RoomMatrix *rm, Map *map)
{
apply_bleed_damage(roommatrix_get_player(rm), m);
Player *player = roommatrix_get_player(rm);
if (player && player->phase_count)
return true;
@ -586,6 +626,19 @@ monster_reset_steps(Monster *m)
m->steps = 0;
}
static void
update_emitters(Monster *m)
{
Position pos = m->sprite->pos;
pos.x += 6;
pos.y += 6;
particle_emitter_update(m->emitters.bloodlust, pos, DIM(20, 20));
pos.x += 5;
pos.y += 5;
particle_emitter_update(m->emitters.bleed, pos, DIM(10, 10));
}
void
monster_update(Monster *m, UpdateData *data)
{
@ -593,6 +646,7 @@ monster_update(Monster *m, UpdateData *data)
return;
sprite_update(m->sprite, data);
update_emitters(m);
if (m->sprite->state == SPRITE_STATE_PLUMMETED) {
m->stats.hp = 0;
@ -634,7 +688,7 @@ monster_update(Monster *m, UpdateData *data)
}
void
monster_hit(Monster *monster, unsigned int dmg)
monster_hit(Monster *monster, unsigned int dmg, bool critical)
{
if (dmg > 0) {
Position p = monster->sprite->pos;
@ -652,6 +706,9 @@ monster_hit(Monster *monster, unsigned int dmg)
C_YELLOW,
&monster->sprite->pos);
}
if (critical)
monster_set_bleeding(monster);
monster_behaviour_check_post_hit(monster);
}
@ -762,13 +819,8 @@ monster_render(Monster *m, Camera *cam)
if (m->stats.hp <= 0)
return;
if (m->bloodlust) {
Position pos = m->sprite->pos;
pos.x += 6;
pos.y += 6;
particle_engine_sparkle(pos, DIM(20, 20), C_RED, false);
}
particle_emitter_render(m->emitters.bloodlust);
particle_emitter_render(m->emitters.bleed);
sprite_render(m->sprite, cam);
}
@ -832,7 +884,7 @@ monster_push(Monster *m, Player *p, RoomMatrix *rm, Vector2d direction)
if (space->trap) {
int dmg = space->trap->damage * 3;
m->stats.hp -= dmg;
monster_hit(m, dmg);
monster_hit(m, dmg, false);
gui_log("%s takes %d damage from a trap", m->label, dmg);
} else if (space->damaging) {
LinkedList *objects = space->objects;
@ -842,7 +894,7 @@ monster_push(Monster *m, Player *p, RoomMatrix *rm, Vector2d direction)
if (!o->damage)
return;
m->stats.hp -= o->damage * 3;
monster_hit(m, o->damage * 3);
monster_hit(m, o->damage * 3, false);
}
} else if (has_collided(m, rm, direction)) {
m->sprite->pos.x -= TILE_DIMENSION * (int) direction.x;
@ -856,11 +908,11 @@ monster_push(Monster *m, Player *p, RoomMatrix *rm, Vector2d direction)
void
monster_set_bloodlust(Monster *m, bool bloodlust)
{
if (m->bloodlust == bloodlust || m->stats.hp <= 0) {
if (m->emitters.bloodlust->enabled == bloodlust || m->stats.hp <= 0) {
return;
}
m->bloodlust = bloodlust;
m->emitters.bloodlust->enabled = bloodlust;
if (bloodlust) {
gui_log("%s rages with bloodlust", m->label);
monster_set_behaviour(m, HOSTILE);
@ -882,6 +934,13 @@ monster_set_bloodlust(Monster *m, bool bloodlust)
}
}
void
monster_set_bleeding(Monster *m)
{
m->emitters.bleed->enabled = true;
gui_log("%s starts bleeding profusely", m->label);
}
void
monster_destroy(Monster *m)
{
@ -890,6 +949,10 @@ monster_destroy(Monster *m)
free(m->label);
if (m->lclabel)
free(m->lclabel);
particle_emitter_destroy(m->emitters.bloodlust);
particle_emitter_destroy(m->emitters.bleed);
sprite_destroy(m->stateIndicator.sprite);
free(m);
}

View File

@ -26,6 +26,7 @@
#include "player.h"
#include "linkedlist.h"
#include "doorlocktype.h"
#include "particle_emitter.h"
struct UpdateData;
@ -68,6 +69,11 @@ typedef struct MonsterItems {
enum DoorLockType keyType;
} MonsterItems;
typedef struct ParticleEmitters {
ParticleEmitter *bloodlust;
ParticleEmitter *bleed;
} ParticleEmitters;
typedef struct Monster {
char *label;
char *lclabel;
@ -77,9 +83,9 @@ typedef struct Monster {
MonsterStateIndicator stateIndicator;
MonsterBehaviour behaviour;
MonsterItems items;
ParticleEmitters emitters;
unsigned int steps;
bool boss;
bool bloodlust;
} Monster;
Monster* monster_create(void);
@ -97,7 +103,7 @@ void
monster_render_top_layer(Monster*, RoomMatrix*, Camera*);
void
monster_hit(Monster*, unsigned int dmg);
monster_hit(Monster*, unsigned int dmg, bool critical);
void
monster_update_stats_for_level(Monster*, unsigned int level);
@ -123,6 +129,9 @@ monster_reset_steps(Monster *m);
void
monster_set_bloodlust(Monster*, bool bloodlust);
void
monster_set_bleeding(Monster*);
void
monster_destroy(Monster*);

55
src/particle_emitter.c Normal file
View File

@ -0,0 +1,55 @@
#include "particle_emitter.h"
#include "util.h"
#include <stdlib.h>
ParticleEmitter *
particle_emitter_create(void)
{
ParticleEmitter *emitter = ec_malloc(sizeof(ParticleEmitter));
emitter->pos = POS(0, 0);
emitter->dim = DIM(32, 32);
emitter->particle_func = NULL;
emitter->timer = _timer_create();
emitter->timestep = 1;
emitter->enabled = false;
return emitter;
}
void
particle_emitter_render(ParticleEmitter *emitter)
{
if (!emitter->enabled)
return;
if (!timer_started(emitter->timer)) {
timer_start(emitter->timer);
return;
}
if (timer_get_ticks(emitter->timer) < emitter->timestep)
return;
timer_stop(emitter->timer);
timer_start(emitter->timer);
if (emitter->particle_func)
emitter->particle_func(emitter->pos, emitter->dim);
else
error("Particle emitter missing particle_func");
}
void
particle_emitter_update(ParticleEmitter *emitter, Position pos, Dimension dim)
{
emitter->pos = pos;
emitter->dim = dim;
}
void
particle_emitter_destroy(ParticleEmitter *emitter)
{
timer_destroy(emitter->timer);
emitter->timer = NULL;
free(emitter);
}

44
src/particle_emitter.h Normal file
View File

@ -0,0 +1,44 @@
/*
* BreakHack - A dungeone crawler RPG
* Copyright (C) 2018 Linus Probert <linus.probert@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "timer.h"
#include "position.h"
#include "dimension.h"
typedef struct ParticleEmitter {
unsigned int timestep;
Timer *timer;
Position pos;
Dimension dim;
void (*particle_func)(Position, Dimension);
bool enabled;
} ParticleEmitter;
ParticleEmitter *
particle_emitter_create(void);
void
particle_emitter_render(ParticleEmitter*);
void
particle_emitter_update(ParticleEmitter*, Position, Dimension);
void
particle_emitter_destroy(ParticleEmitter*);

View File

@ -100,6 +100,12 @@ particle_engine_init(void)
engine->global_particles = linkedlist_create();
}
void
particle_engine_bleed(Position pos, Dimension dim)
{
particle_engine_bloodspray(pos, dim, 10);
}
void
particle_engine_bloodspray(Position pos, Dimension dim, unsigned int count)
{
@ -181,6 +187,13 @@ particle_engine_fire_explosion(Position pos, Dimension dim)
create_explosion(pos, dim, 3, C_YELLOW, C_YELLOW, C_RED);
}
void
particle_engine_blink(Position pos, Dimension dim)
{
check_engine();
create_explosion(pos, dim, 3, (SDL_Color) { 0, 0, 255, 255 }, C_BLUE, C_LIGHTBLUE);
}
void
particle_engine_eldritch_explosion(Position pos, Dimension dim)
{
@ -254,6 +267,12 @@ particle_engine_speed_lines(Position pos, Dimension dim, bool horizontal)
}
}
void
particle_engine_bloodlust(Position pos, Dimension dim)
{
particle_engine_sparkle(pos, dim, C_RED, false);
}
void
particle_engine_sparkle(Position pos, Dimension dim, SDL_Color color, bool global)
{

View File

@ -28,12 +28,18 @@
void
particle_engine_init(void);
void
particle_engine_bleed(Position pos, Dimension dim);
void
particle_engine_bloodspray(Position, Dimension, unsigned int count);
void
particle_engine_fire_explosion(Position, Dimension);
void
particle_engine_blink(Position, Dimension);
void
particle_engine_eldritch_explosion(Position, Dimension);
@ -43,6 +49,9 @@ particle_engine_dust_puff(Position, Dimension);
void
particle_engine_speed_lines(Position, Dimension, bool horizontal);
void
particle_engine_bloodlust(Position, Dimension);
void
particle_engine_sparkle(Position, Dimension, SDL_Color, bool global);

View File

@ -37,13 +37,14 @@
#include "trap.h"
#include "gamecontroller.h"
#include "event.h"
#include "effect_util.h"
#ifdef STEAM_BUILD
#include "steam/steamworks_api_wrapper.h"
#endif // STEAM_BUILD
#define ENGINEER_STATS { 12, 12, 5, 7, 2, 2, 1, false, false }
#define MAGE_STATS { 12, 12, 5, 7, 1, 2, 1, false, false }
#define ENGINEER_STATS { 12, 12, 5, 7, 2, 1, 1, false, false }
#define MAGE_STATS { 12, 12, 5, 7, 1, 1, 1, false, false }
#define PALADIN_STATS { 12, 12, 8, 9, 3, 1, 1, false, false }
#define ROGUE_STATS { 9, 9, 6, 9, 4, 2, 1, false, false }
#define WARRIOR_STATS { 12, 12, 8, 9, 3, 1, 1, false, false }
@ -133,16 +134,16 @@ on_monster_collision(Player *player,
RoomMatrix *matrix,
Vector2d direction)
{
unsigned int hit = stats_fight(&player->stats,
&monster->stats);
CombatResult result = stats_fight(&player->stats,
&monster->stats);
mixer_play_effect(SWING0 + get_random(2));
monster_hit(monster, hit);
monster_hit(monster, result.dmg, result.critical);
animation_run(player->swordAnimation);
if (hit > 0) {
if (result.dmg > 0) {
gui_log("You hit %s for %u damage",
monster->lclabel, hit);
monster->lclabel, result.dmg);
player->stat_data.hits += 1;
mixer_play_effect(SWORD_HIT);
} else {
@ -150,6 +151,17 @@ on_monster_collision(Player *player,
player->stat_data.misses += 1;
}
player_monster_kill_check(player, monster);
if (monster->stats.hp <= 0 && (player_has_artifact(player, EXPLOSIVE_KILLS))) {
mixer_play_effect(EXPLOSION_EFFECT);
particle_engine_fire_explosion(monster->sprite->pos, DIM(32, 32));
effect_damage_surroundings(&monster->sprite->pos,
matrix,
player,
&player->stats,
player_has_artifact(player, EXPLOSIVE_KILLS),
0,
false);
}
if (monster->stats.hp > 0) {
if (get_random(10) < player_has_artifact(player, PUSH_BACK)) {
@ -254,7 +266,7 @@ has_collided(Player *player, RoomMatrix *matrix, Vector2d direction)
else {
player_collect_items(player, space);
player_pickup_artifacts(player, space);
// If not phased or phase will end this turn, react to traps and pits
if (!player->phase_count || (player->phase_count == 1 && player->stats.speed == (player->stat_data.steps + 1))) {
player_interact_objects(player, space);
@ -326,7 +338,7 @@ move(Player *player, RoomMatrix *matrix, Vector2d direction)
lastSpace->trap == NULL &&
lastSpace->objects == NULL &&
lastSpace->items == NULL
)
)
map_trigger_tile_fall(lastSpace->tile);
}
}
@ -334,8 +346,13 @@ move(Player *player, RoomMatrix *matrix, Vector2d direction)
void
player_sip_health(Player *player)
{
if (player->potion_sips > 0) {
bool hasSips = player->class == MAGE ?
player->potion_sips > 1 : player->potion_sips > 0;
if (hasSips) {
--player->potion_sips;
if (player->class == MAGE)
--player->potion_sips;
++player->stats.hp;
mixer_play_effect(BUBBLE0 + get_random(2));
gui_log("You take a sip from a health potion");
@ -504,8 +521,10 @@ player_create(class_t class, Camera *cam)
player->sprite = sprite_create();
#ifdef DEBUG
player->daggers = 10;
player->gold = 500;
#else
player->daggers = 0;
player->gold = 0;
#endif
player->stat_data.total_steps = 0;
player->stat_data.steps = 0;
@ -513,7 +532,6 @@ player_create(class_t class, Camera *cam)
player->stat_data.kills = 0;
player->stat_data.misses = 0;
player->xp = 0;
player->gold = 0;
player->potion_sips = 0;
player->phase_count = 0;
player->class = class;
@ -542,6 +560,9 @@ player_create(class_t class, Camera *cam)
case MAGE:
m_strcpy(asset, 100, "Commissions/Mage.png");
player->stats = (Stats) MAGE_STATS;
player->skills[0] = skill_create(VAMPIRIC_BLOW, cam);
player->skills[1] = skill_create(ERUPT, cam);
player->skills[2] = skill_create(BLINK, cam);
break;
case PALADIN:
m_strcpy(asset, 100, "Commissions/Paladin.png");
@ -553,7 +574,6 @@ player_create(class_t class, Camera *cam)
player->skills[0] = skill_create(BACKSTAB, cam);
player->skills[1] = skill_create(TRIP, cam);
player->skills[2] = skill_create(PHASE, cam);
player->skills[3] = skill_create(DAGGER_THROW, cam);
player->daggers = 10;
break;
case WARRIOR:
@ -562,10 +582,10 @@ player_create(class_t class, Camera *cam)
player->skills[0] = skill_create(FLURRY, cam);
player->skills[1] = skill_create(BASH, cam);
player->skills[2] = skill_create(CHARGE, cam);
player->skills[3] = skill_create(DAGGER_THROW, cam);
break;
}
player->skills[3] = skill_create(DAGGER_THROW, cam);
player->skills[4] = skill_create(SIP_HEALTH, cam);
sprite_load_texture(player->sprite, asset, 0, cam->renderer);
@ -584,7 +604,8 @@ player_reset_on_levelchange(Player *player)
player->equipment.keys = 0;
}
ExperienceData player_get_xp_data(Player *p)
ExperienceData
player_get_xp_data(Player *p)
{
ExperienceData data;
data.previousLevel = next_level_threshold(p->stats.lvl - 1);

View File

@ -28,6 +28,8 @@
#include "item_builder.h"
#include "random.h"
#include "update_data.h"
#include "effect_util.h"
#include "particle_engine.h"
static void
onDaggerRender(Sprite *s)
@ -49,6 +51,7 @@ projectile_dagger_create(void)
p->sprite->dim = (Dimension) { 32, 32 };
p->sprite->rotationPoint = (SDL_Point) { 16, 16 };
p->collisionCount = 0;
p->bounceCount = 0;
memset(&p->processedSpaces,
false,
sizeof(p->processedSpaces[0][0]) * MAP_ROOM_WIDTH * MAP_ROOM_HEIGHT);
@ -86,6 +89,30 @@ get_projectile_pos_for(Projectile *p)
return projectilePos;
}
static void
clear_processed_spaces(Projectile *p)
{
memset(&p->processedSpaces,
false,
sizeof(p->processedSpaces[0][0]) * MAP_ROOM_WIDTH * MAP_ROOM_HEIGHT);
}
static void
perform_dagger_explosion(Player *player, RoomMatrix *rm, Position *collisionPos)
{
if (player_has_artifact(player, VOLATILE_DAGGERS)) {
mixer_play_effect(EXPLOSION_EFFECT);
particle_engine_fire_explosion(*collisionPos, DIM(32, 32));
effect_damage_surroundings(collisionPos,
rm,
player,
&player->stats,
player_has_artifact(player, VOLATILE_DAGGERS),
0,
false);
}
}
void
projectile_update(Projectile *p, UpdateData *data)
{
@ -119,17 +146,23 @@ projectile_update(Projectile *p, UpdateData *data)
if (space->monster) {
Stats tmpStats = data->player->stats;
tmpStats.dmg *= 2;
Uint32 dmg = stats_fight(&tmpStats, &space->monster->stats);
if (dmg > 0) {
gui_log("Your dagger pierced %s for %u damage", space->monster->lclabel, dmg);
CombatResult result = stats_fight(&tmpStats, &space->monster->stats);
if (result.dmg > 0) {
gui_log("Your dagger pierced %s for %u damage", space->monster->lclabel, result.dmg);
data->player->stat_data.hits += 1;
perform_dagger_explosion(data->player, data->matrix, &collisionPos);
} else {
gui_log("%s dodged your dagger", space->monster->label);
}
monster_hit(space->monster, dmg);
monster_hit(space->monster, result.dmg, result.critical);
player_monster_kill_check(data->player, space->monster);
alive = player_has_artifact(data->player, PIERCING_DAGGERS) > p->collisionCount;
projectilePos = space->monster->sprite->pos;
} else {
p->bounceCount += 1;
vector2d_reverse(&p->velocity);
clear_processed_spaces(p);
alive = p->bounceCount <= player_has_artifact(data->player, DAGGER_BOUNCE);
}
mixer_play_effect(SWORD_HIT);

View File

@ -36,6 +36,7 @@ typedef struct Projectile {
Timer *lifetime;
bool alive;
Uint32 collisionCount;
Uint32 bounceCount;
bool processedSpaces[MAP_ROOM_WIDTH][MAP_ROOM_HEIGHT];
void (*onRender)(struct Projectile*);
} Projectile;

View File

@ -15,9 +15,7 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef ROOMMATRIX_H_
#define ROOMMATRIX_H_
#pragma once
#include <stdbool.h>
#include "defines.h"
@ -87,6 +85,9 @@ roommatrix_render_lightmap(RoomMatrix*, Camera*);
RoomSpace*
roommatrix_get_space_for(RoomMatrix*, const Position *p);
Player *
roommatrix_get_player(RoomMatrix*);
#ifdef DEBUG
void
roommatrix_render_debug(RoomMatrix*, Camera*);
@ -94,8 +95,3 @@ roommatrix_render_debug(RoomMatrix*, Camera*);
void
roommatrix_destroy(RoomMatrix*);
Player *
roommatrix_get_player(RoomMatrix*);
#endif // ROOMMATRIX_H_

166
src/save.c Normal file
View File

@ -0,0 +1,166 @@
/*
* BreakHack - A dungeone crawler RPG
* Copyright (C) 2018 Linus Probert <linus.probert@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include "save.h"
#include "sqlite3.h"
#include "db.h"
#include "defines.h"
#include "util.h"
static sqlite3 *db = NULL;
static bool loaded = false;
static Save save;
static
DbQuery MIGRATE_COMMAND = {
"CREATE TABLE IF NOT EXISTS saves("
"major_version INTEGER, "
"minor_version INTEGER, "
"patch_version INTEGER, "
"arch INTEGER, "
"save BLOB)",
NULL, NULL
};
static
DbQuery CLEAR_SAVE = {
"DELETE FROM saves "
#ifdef _WIN32
"WHERE arch = 1",
#else // _WIN32
"WHERE arch = 2",
#endif // _WIN32
NULL,
NULL };
static void
create_table(void)
{
db_execute(db, &MIGRATE_COMMAND);
}
void
save_load(void)
{
debug("Loading save");
const char *query =
"SELECT save FROM saves "
"WHERE major_version = ? "
"AND minor_version = ? "
"AND patch_version = ? "
#ifdef _WIN32
"AND arch = 1 "
#else // _WIN32
"AND arch = 2 "
#endif // _WIN32
"LIMIT 1";
sqlite3_stmt *stmt = db_prepare(db, query);
sqlite3_bind_int(stmt, 1, MAJOR_VERSION);
sqlite3_bind_int(stmt, 2, MINOR_VERSION);
sqlite3_bind_int(stmt, 3, PATCH_VERSION);
if (SQLITE_ROW == sqlite3_step(stmt)) {
int size = sqlite3_column_bytes(stmt, 0);
debug("Reading save bytes: %d", size);
memcpy(&save, sqlite3_column_blob(stmt, 0), size);
loaded = true;
} else {
loaded = false;
}
sqlite3_finalize(stmt);
}
void
save_init(void)
{
if (!db_open(DB_FILE, &db)) {
db_close(&db);
fatal("Exiting");
}
create_table();
save_load();
}
const Save *
save_get(void)
{
return &save;
}
bool
save_exists(void)
{
return loaded;
}
void
save_save(unsigned int seed,
unsigned int map_level,
bool quickGame,
bool arcadeGame,
Player *player)
{
debug("Saving game, Seed: %d, Map level: %d", seed, map_level);
save_clear();
save.seed = seed;
save.map_level = map_level;
save.quickGame = quickGame;
save.arcadeGame = arcadeGame;
save.player_stats = player->stats;
save.player_daggers = player->daggers;
save.player_gold = player->gold;
save.player_xp = player->xp;
save.player_potion_sips = player->potion_sips;
save.player_player_stats = player->stat_data;
save.player_state = player->stateData;
save.player_class = player->class;
save.player_equipment = player->equipment;
const char *query =
"INSERT INTO saves"
"(major_version, minor_version, patch_version, arch, save) "
"VALUES(?, ?, ?, ?, ?)";
sqlite3_stmt *stmt = db_prepare(db, query);
sqlite3_bind_int(stmt, 1, MAJOR_VERSION);
sqlite3_bind_int(stmt, 2, MINOR_VERSION);
sqlite3_bind_int(stmt, 3, PATCH_VERSION);
#ifdef _WIN32
sqlite3_bind_int(stmt, 4, 1);
#else // _WIN32
sqlite3_bind_int(stmt, 4, 2);
#endif // _WIN32
sqlite3_bind_blob(stmt, 5, &save, sizeof(Save), SQLITE_STATIC);
sqlite3_step(stmt);
sqlite3_finalize(stmt);
}
void
save_clear(void)
{
db_execute(db, &CLEAR_SAVE);
}
void
save_close(void)
{
db_close(&db);
}

67
src/save.h Normal file
View File

@ -0,0 +1,67 @@
/*
* BreakHack - A dungeone crawler RPG
* Copyright (C) 2018 Linus Probert <linus.probert@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef SAVE_H_
#define SAVE_H_
#include <stdbool.h>
#include "player.h"
#include "artifact.h"
typedef struct Save {
int seed;
bool quickGame;
bool arcadeGame;
unsigned int map_level;
unsigned int player_daggers;
unsigned int player_xp;
unsigned int player_potion_sips;
unsigned int potion_sips;
Stats player_stats;
PlayerStatData player_player_stats;
double player_gold;
PlayerStateData player_state;
class_t player_class;
PlayerEquipment player_equipment;
} Save;
void
save_init(void);
void
save_load(void);
const Save *
save_get(void);
bool
save_exists(void);
void
save_save(unsigned int seed,
unsigned int map_level,
bool quickGame,
bool arcadeGame,
Player *player);
void
save_clear(void);
void
save_close(void);
#endif // SAVE_H_

View File

@ -69,8 +69,13 @@ screen_create_credits(SDL_Renderer *renderer)
linkedlist_push(&screen->sprites, credit_txt("liquidityc.github.io", C_WHITE, x + columnOffset, y, renderer));
y += 20;
linkedlist_push(&screen->sprites, credit_txt("@LiquidityC", C_WHITE, x + columnOffset, y, renderer));
y += 20;
linkedlist_push(&screen->sprites, credit_txt("AmigaOS 4", C_YELLOW, x, y, renderer));
y += 20;
linkedlist_push(&screen->sprites, credit_txt("port:", C_YELLOW, x, y, renderer));
linkedlist_push(&screen->sprites, credit_txt("George Sokianos", C_WHITE, x + columnOffset, y, renderer));
y += 60;
y += 40;
linkedlist_push(&screen->sprites, credit_txt(" - Graphics -", C_BLUE, x, y, renderer));
y += 30;
linkedlist_push(&screen->sprites, credit_txt("Tileset:", C_YELLOW, x, y, renderer));
@ -209,3 +214,4 @@ screen_destroy(Screen *screen)
sprite_destroy(linkedlist_pop(&screen->sprites));
free(screen);
}

View File

@ -36,6 +36,8 @@
#include "artifact.h"
#include "trap.h"
#include "tooltip.h"
#include "actiontextbuilder.h"
#include "effect_util.h"
static char *flurry_tooltip[] = {
"FLURRY", "",
@ -55,6 +57,25 @@ static char *flurry_tooltip[] = {
NULL
};
static char *vampiric_blow_tooltip[] = {
"VAMPIRIC BLOW", "",
"",
" Hits an adjecant enemy with a vampiric blow.", "",
" Upon hitting you will siphon life from the target", "",
" and cause the target to bleed.", "",
"",
"COOLDOWN:", "",
" 5 turns", "",
"",
"USAGE:", "",
" activate the skill (press ", "1", ")", "",
" followed by a direction (left, right, up or down)", "",
"",
"",
"Press ", "ESC", " to close", "", "",
NULL
};
static char *bash_tooltip[] = {
"BASH", "",
"",
@ -156,6 +177,43 @@ static char *charge_tooltip[] = {
NULL
};
static char *blink_tooltip[] = {
"BLINK", "",
"",
" You blink in a chosen direction into the first obstructing", "",
" object picking up items in your path. Monsters will not", "",
" obstruct your blink.", "",
"",
"COOLDOWN:", "",
" 4 turns", "",
"",
"USAGE:",
" activate the skill (press ", "3", ")", "",
" followed by a direction (left, right, up or down)", "",
"",
"",
"Press ", "ESC", " to close", "",
NULL
};
static char *erupt_tooltip[] = {
"ERUPT", "",
"",
" You erupt in a magical explosion damaging monsters", "",
" around you pushing them back and causing fear", "",
" for 3 turns.", "",
"",
"COOLDOWN:", "",
" 3 turns", "",
"",
"USAGE:",
" Erupt (press ", "2", ")", "",
"",
"",
"Press ", "ESC", " to close", "",
NULL
};
static char *dagger_tooltip[] = {
"THROW DAGGER", "",
"",
@ -266,6 +324,73 @@ check_skill_validity(Position *playerPos, Position *targetPos, SkillData *data)
return true;
}
static bool
vampiric_blow_skill(Skill *skill, SkillData *data)
{
UNUSED (skill);
Position playerPos, targetPos;
Player *player = data->player;
if (!check_skill_validity(&playerPos, &targetPos, data)) {
return false;
}
animation_run(player->swordAnimation);
Monster *monster = data->matrix->spaces[targetPos.x][targetPos.y].monster;
mixer_play_effect(SWING0);
if (monster) {
gui_log("You attack %s with a vampiric blow", monster->lclabel);
player->stats.advantage = true;
CombatResult result = stats_fight(&player->stats, &monster->stats);
player->stats.advantage = false;
monster_hit(monster, result.dmg, result.critical);
if (result.dmg) {
mixer_play_effect(SWORD_HIT);
monster_set_bleeding(monster);
unsigned int gain = player->stats.lvl * 3;
gain = min(gain, (unsigned int) player->stats.maxhp - player->stats.hp);
if (gain > 0) {
gui_log("You gain %u health", gain);
char msg[4];
m_sprintf(msg, 4, "+%u", gain);
actiontextbuilder_create_text(msg,
C_GREEN,
&player->sprite->pos);
player->stats.hp += gain;
player->stats.hp = min(player->stats.maxhp,
player->stats.hp);
}
} else {
gui_log("You missed %s", monster->lclabel);
}
} else {
gui_log("You swing at thin air with a vampiric blow");
}
player_monster_kill_check(data->player, monster);
return true;
}
static Skill *
create_vampiric_blow(void)
{
Texture *t = texturecache_add("Extras/Skills.png");
Sprite *s = sprite_create();
sprite_set_texture(s, t, 0);
s->dim = GAME_DIMENSION;
s->clip = CLIP32(0, 64);
s->fixed = true;
Skill *skill = create_default("Vampiric blow", s);
skill->levelcap = 2;
skill->use = vampiric_blow_skill;
skill->resetTime = 5;
return skill;
}
static bool
skill_use_flurry(Skill *skill, SkillData *data)
{
@ -284,12 +409,12 @@ skill_use_flurry(Skill *skill, SkillData *data)
unsigned int hitCount = 0;
for (size_t i = 0; i < 3; ++i) {
unsigned int originalHp = monster->stats.hp;
unsigned int dmg = stats_fight(&data->player->stats, &monster->stats);
if (dmg > 0 && originalHp > 0) {
gui_log("You hit for %u damage", dmg);
CombatResult result = stats_fight(&data->player->stats, &monster->stats);
if (result.dmg > 0 && originalHp > 0) {
gui_log("You hit for %u damage", result.dmg);
hitCount++;
}
monster_hit(monster, dmg);
monster_hit(monster, result.dmg, result.critical);
}
if (hitCount == 1) {
mixer_play_effect(SWORD_HIT);
@ -298,9 +423,6 @@ skill_use_flurry(Skill *skill, SkillData *data)
} else if (hitCount == 3) {
mixer_play_effect(TRIPPLE_SWORD_HIT);
}
data->player->stat_data.hits += hitCount;
} else {
gui_log("You swing at thin air with a flurry of strikes");
}
@ -401,20 +523,19 @@ skill_bash(Skill *skill, SkillData *data)
mixer_play_effect(SWING0);
if (monster) {
gui_log("You bash %s with your shield", monster->lclabel);
unsigned int dmg = stats_fight(&data->player->stats, &monster->stats);
if (dmg > 0) {
gui_log("You hit for %u damage", dmg);
CombatResult result = stats_fight(&data->player->stats, &monster->stats);
if (result.dmg > 0) {
gui_log("You hit for %u damage", result.dmg);
if (monster->stats.hp > 0) {
gui_log("%s seems dazed and confused", monster->label);
monster_set_state(monster, STUNNED,
(Uint8) (3 + player_has_artifact(data->player, INCREASED_STUN)));
}
mixer_play_effect(SLAM);
data->player->stat_data.hits += 1;
} else {
gui_log("You missed %s", monster->lclabel);
}
monster_hit(monster, dmg);
monster_hit(monster, result.dmg, result.critical);
} else {
gui_log("You bash your shield at nothing");
}
@ -456,13 +577,13 @@ skill_trip(Skill *skill, SkillData *data)
mixer_play_effect(SWING0 + get_random(2));
animation_run(data->player->swordAnimation);
if (space->monster) {
int dmg = stats_fight(&data->player->stats, &space->monster->stats);
if (dmg)
CombatResult result = stats_fight(&data->player->stats, &space->monster->stats);
if (result.dmg)
mixer_play_effect(SWORD_HIT);
gui_log("You trip %s causing it to fall away from you", space->monster->lclabel);
monster_hit(space->monster, dmg);
monster_hit(space->monster, result.dmg, result.critical);
player_monster_kill_check(data->player, space->monster);
if (dmg && space->monster->stats.hp > 0) {
if (result.dmg && space->monster->stats.hp > 0) {
Uint32 pushCount = 1 + player_has_artifact(data->player, PUSH_BACK);
for (Uint32 i = 0; i < pushCount; ++i) {
monster_push(space->monster, data->player, data->matrix, data->direction);
@ -529,13 +650,13 @@ skill_backstab(Skill *skill, SkillData *data)
monster_push(m, data->player, data->matrix, reverseDirection);
m->stats.disadvantage = true;
int dmg = stats_fight(&data->player->stats, &m->stats);
CombatResult result = stats_fight(&data->player->stats, &m->stats);
m->stats.disadvantage = false;
monster_hit(m, dmg);
monster_hit(m, result.dmg, result.critical);
player_monster_kill_check(data->player, m);
if (dmg) {
if (result.dmg) {
mixer_play_effect(SWORD_HIT);
monster_set_state(m, STUNNED, (Uint8)(2 + player_has_artifact(data->player, INCREASED_STUN)));
monster_set_bleeding(m);
}
}
@ -595,7 +716,9 @@ create_phase(void)
static bool
skill_sip_health_available(Player *player)
{
return player->potion_sips > 0 && player->stats.hp != player->stats.maxhp;
bool hasSips = player->class == MAGE ?
player->potion_sips > 1 : player->potion_sips > 0;
return hasSips > 0 && player->stats.hp != player->stats.maxhp;
}
static bool
@ -644,13 +767,12 @@ skill_charge_check_path(SkillData *data,
Stats tmpStats = player->stats;
tmpStats.dmg *= steps > 0 ? steps : 1;
mixer_play_effect(SWING0 + get_random(2));
unsigned int dmg = stats_fight(&tmpStats, &monster->stats);
if (dmg > 0) {
gui_log("You charged %s for %u damage", monster->lclabel, dmg);
CombatResult result = stats_fight(&tmpStats, &monster->stats);
if (result.dmg > 0) {
gui_log("You charged %s for %u damage", monster->lclabel, result.dmg);
mixer_play_effect(SWORD_HIT);
data->player->stat_data.hits += 1;
}
monster_hit(monster, dmg);
monster_hit(monster, result.dmg, result.critical);
player_monster_kill_check(data->player, monster);
}
@ -752,6 +874,143 @@ create_charge(void)
return skill;
}
static bool
skill_blink(Skill *skill, SkillData *data)
{
UNUSED(skill);
Player *player = data->player;
RoomMatrix *matrix = data->matrix;
Position playerStartPos = position_to_matrix_coords(&player->sprite->pos);
Position destination = playerStartPos;
// Find collider
destination.x += (int) data->direction.x;
destination.y += (int) data->direction.y;
Position lastAvailableDest = playerStartPos;
while (position_in_roommatrix(&destination))
{
RoomSpace *space = &matrix->spaces[destination.x][destination.y];
if (space->occupied) {
break;
}
if (!space->monster) {
lastAvailableDest = destination;
}
perform_pickups_for_space(space, player);
destination.x += (int) data->direction.x;
destination.y += (int) data->direction.y;
}
destination = lastAvailableDest;
// Move player
Position playerOriginPos = player->sprite->pos;
Sint32 xdiff = destination.x - playerStartPos.x;
Sint32 ydiff = destination.y - playerStartPos.y;
player->sprite->pos.x += xdiff * TILE_DIMENSION;
player->sprite->pos.y += ydiff * TILE_DIMENSION;
Position playerDestinationPos = player->sprite->pos;
player_turn(data->player, &data->direction);
particle_engine_blink(playerOriginPos, DIM(32, 32));
particle_engine_blink(playerDestinationPos, DIM(32, 32));
mixer_play_effect(BLINK_EFFECT);
Position lastTilePos = position_to_matrix_coords(&playerDestinationPos);
RoomSpace *destSpace = &matrix->spaces[lastTilePos.x][lastTilePos.y];
handle_space_effects(destSpace, player);
return true;
}
static Skill *
create_blink(void)
{
Texture *t = texturecache_add("Extras/Skills.png");
Sprite *s = sprite_create();
sprite_set_texture(s, t, 0);
s->dim = GAME_DIMENSION;
s->clip = CLIP32(64, 64);
s->fixed = true;
Skill *skill = create_default("Blink", s);
skill->levelcap = 3;
skill->use = skill_blink;
skill->resetTime = 4;
return skill;
}
static bool
skill_erupt(Skill *skill, SkillData *data)
{
UNUSED(skill);
Player *player = data->player;
RoomMatrix *rm = data->matrix;
gui_log("You erupt in a magical explosion");
particle_engine_eldritch_explosion(player->sprite->pos, DIM(32, 32));
mixer_play_effect(BLAST_EFFECT);
Position playerMPos = position_to_matrix_coords(&player->sprite->pos);
int range = player_has_artifact(player, SKILL_RADIUS);
for (Sint32 i = -1 - range; i <= 1 + range; ++i) {
for (Sint32 j = -1 - range; j <= 1 + range; ++j) {
if (i == 0 && j == 0)
continue;
Position matrixPos = POS(playerMPos.x + i, playerMPos.y + j);
if (!position_in_roommatrix(&matrixPos))
continue;
RoomSpace *r = &rm->spaces[matrixPos.x][matrixPos.y];
if (r->monster) {
player->stats.advantage = true;
CombatResult result = stats_fight(&player->stats, &r->monster->stats);
player->stats.advantage = false;
monster_hit(r->monster, result.dmg, result.critical);
gui_log("%s takes %d damage from the explosion", r->monster->label, result.dmg);
monster_set_state(r->monster, SCARED, 3);
int lvl = 1 + player_has_artifact(player, PUSH_BACK);
Vector2d dir = vector2d_to_direction(&VEC2D((float) i, (float) j));
for (int k = 0; k < lvl; ++k) {
if (r->monster->stats.hp > 0)
monster_push(r->monster,
player,
rm,
dir);
}
}
}
}
return true;
}
static Skill *
create_erupt(void)
{
Texture *t = texturecache_add("Extras/Skills.png");
Sprite *s = sprite_create();
sprite_set_texture(s, t, 0);
s->dim = GAME_DIMENSION;
s->clip = CLIP32(32, 64);
s->fixed = true;
Skill *skill = create_default("Erupt", s);
skill->levelcap = 3;
skill->use = skill_erupt;
skill->instantUse = true;
skill->resetTime = 3;
return skill;
}
Skill*
skill_create(enum SkillType t, Camera *cam)
{
@ -761,6 +1020,10 @@ skill_create(enum SkillType t, Camera *cam)
skill = create_flurry();
skill->tooltip = tooltip_create(flurry_tooltip, cam);
break;
case VAMPIRIC_BLOW:
skill = create_vampiric_blow();
skill->tooltip = tooltip_create(vampiric_blow_tooltip, cam);
break;
case SIP_HEALTH:
skill = create_sip_health();
skill->tooltip = tooltip_create(health_tooltip, cam);
@ -769,6 +1032,14 @@ skill_create(enum SkillType t, Camera *cam)
skill = create_charge();
skill->tooltip = tooltip_create(charge_tooltip, cam);
break;
case BLINK:
skill = create_blink();
skill->tooltip = tooltip_create(blink_tooltip, cam);
break;
case ERUPT:
skill = create_erupt();
skill->tooltip = tooltip_create(erupt_tooltip, cam);
break;
case DAGGER_THROW:
skill = create_throw_dagger();
skill->tooltip = tooltip_create(dagger_tooltip, cam);

View File

@ -35,7 +35,10 @@ enum SkillType {
SIP_HEALTH,
BACKSTAB,
TRIP,
PHASE
PHASE,
BLINK,
ERUPT,
VAMPIRIC_BLOW
};
typedef struct SkillData_t {

View File

@ -64,7 +64,7 @@ load_texture(SkillBar *bar, const char *path, SDL_Renderer *renderer)
s->dim = (Dimension) { 8, 8 };
s->fixed = true;
sprite_load_text_texture(s, "GUI/SDS_8x8.ttf", 0, 8, 0);
m_sprintf(buffer, 4, "%u", i + 1 < 10 ? i + 1 : 0);
m_sprintf(buffer, 4, "%u", i + 1);
texture_load_from_text(s->textures[0], buffer, C_YELLOW, C_BLACK, renderer);
linkedlist_append(&bar->sprites, s);
}

View File

@ -61,31 +61,30 @@ get_defence_roll(Stats *defender)
return roll + defender->def;
}
unsigned int
CombatResult
stats_fight(Stats *attacker, Stats *defender)
{
bool critical = false;
CombatResult result = { 0, false };
int atkRoll = get_attack_roll(attacker);
int defRoll = get_defence_roll(defender);
if (atkRoll - attacker->atk == 20)
critical = get_attack_roll(attacker) > defRoll;
result.critical = get_attack_roll(attacker) > defRoll;
int dmgRoll = 0;
if (atkRoll >= defRoll) {
if (attacker->dmg > 0)
dmgRoll = get_random(attacker->dmg - 1) + 1;
result.dmg = get_random(attacker->dmg - 1) + 1;
else
dmgRoll = 1;
result.dmg = 1;
if (critical) {
dmgRoll = dmgRoll * 2;
if (result.critical) {
result.dmg = result.dmg * 2;
gui_log("CRITICAL HIT!");
}
defender->hp -= dmgRoll;
defender->hp -= result.dmg;
}
return dmgRoll;
return result;
}

View File

@ -31,7 +31,12 @@ typedef struct Stats_t {
bool disadvantage;
} Stats;
unsigned int
typedef struct CombatResult {
unsigned int dmg;
bool critical;
} CombatResult;
CombatResult
stats_fight(Stats *attacker, Stats *defender);
#endif // STATS_H_

View File

@ -12,6 +12,7 @@ static const char *LB_HIGHSCORE = "Highscore";
static const char *LB_QUICKPLAY_HIGHSCORE = "Quickplay Highscore";
static const char *LB_ARCADE_HIGHSCORE = "Arcade Highscore";
static const char *LB_ROGUE_HIGHSCORE = "Rogue Highscore";
static const char *LB_MAGE_HIGHSCORE = "Mage Highscore";
static const char *LB_WARRIOR_HIGHSCORE = "Warrior Highscore";
static const char *LB_KILLS = "Most Kills";
static char *lb_weekly = NULL;
@ -22,9 +23,10 @@ static Achievement g_Achievements[] = {
_ACH_ID(LIGHTS_ON, "Omnidirectional light"),
_ACH_ID(BACK_TO_WORK, "Back to work"),
_ACH_ID(DRAGON_SLAYER, "Platinum dragon slayer"),
_ACH_ID(ROGUE_LIKE, "Rogue-like")
_ACH_ID(ROGUE_LIKE, "Rogue-like"),
_ACH_ID(MAGICAL, "Magical")
};
static Uint8 numAchievements = 6;
static Uint8 numAchievements = 7;
static bool m_Initiated = false;
static bool m_bStatsReceived = false;
@ -35,6 +37,7 @@ static Sint64 m_hArcadeHighscoreLeaderboard = 0;
static Sint64 m_hKillsLeaderboard = 0;
static Sint64 m_hRogueHighscore = 0;
static Sint64 m_hWarriorHighscore = 0;
static Sint64 m_hMageHighscore = 0;
static Sint64 m_hWeeklyHighscore = 0;
static Timer *requestDataTimer = NULL;
@ -70,6 +73,8 @@ leaderboard_received(Sint64 hLeaderboard, const char *name)
m_hKillsLeaderboard = hLeaderboard;
else if (strcmp(LB_ROGUE_HIGHSCORE, name) == 0)
m_hRogueHighscore = hLeaderboard;
else if (strcmp(LB_MAGE_HIGHSCORE, name) == 0)
m_hMageHighscore = hLeaderboard;
else if (strcmp(LB_WARRIOR_HIGHSCORE, name) == 0)
m_hWarriorHighscore = hLeaderboard;
else if (strcmp(LB_QUICKPLAY_HIGHSCORE, name) == 0)
@ -124,6 +129,8 @@ request_data_queue_run(void)
c_SteamUserStats_FindLeaderboard(LB_KILLS);
else if (!m_hRogueHighscore)
c_SteamUserStats_FindLeaderboard(LB_ROGUE_HIGHSCORE);
else if (!m_hMageHighscore)
c_SteamUserStats_FindLeaderboard(LB_MAGE_HIGHSCORE);
else if (!m_hWarriorHighscore)
c_SteamUserStats_FindLeaderboard(LB_WARRIOR_HIGHSCORE);
else if (!m_hWeeklyHighscore)
@ -183,6 +190,13 @@ void steam_register_arcade_score(Sint32 nScore, const int32_t * details, int32_t
c_SteamUserStats_UploadLeaderboardScore(m_hArcadeHighscoreLeaderboard, nScore, details, nDetails);
}
void steam_register_mage_score(Sint32 nScore, const int32_t * details, int32_t nDetails)
{
if (!m_hMageHighscore)
return;
c_SteamUserStats_UploadLeaderboardScore(m_hMageHighscore, nScore, details, nDetails);
}
void steam_register_warrior_score(Sint32 nScore, const int32_t * details, int32_t nDetails)
{
if (!m_hWarriorHighscore)

View File

@ -13,7 +13,8 @@ typedef enum EAchievement
BUGGFIXER = 7,
BUGGCREATOR = 8,
ROGUE_LIKE = 9,
ARCADE_HACK = 10
MAGICAL = 10,
ARCADE_HACK = 11
} EAchievement;
@ -45,6 +46,8 @@ void steam_register_weekly_score(Sint32 nScore, const int32_t *details, int32_t
void steam_register_arcade_score(Sint32 nScore, const int32_t *details, int32_t nDetails);
void steam_register_mage_score(Sint32 nScore, const int32_t *details, int32_t nDetails);
void steam_register_warrior_score(Sint32 nScore, const int32_t *details, int32_t nDetails);
void steam_register_rogue_score(Sint32 nScore, const int32_t *details, int32_t nDetails);

View File

@ -160,13 +160,11 @@ texture_load_from_text(Texture *t,
{
SDL_Surface *bg_surface = NULL;
SDL_Surface *fg_surface = NULL;
fg_surface = TTF_RenderText_Blended(t->font, text, c);
if (t->outlineFont) {
bg_surface = TTF_RenderText_Blended(t->outlineFont, text, oc);
fg_surface = TTF_RenderText_Blended(t->font, text, c);
}
else {
fg_surface = TTF_RenderText_Solid(t->font, text, c);
}
SDL_Surface *surface = fg_surface;
if (bg_surface) {
int outline = TTF_GetFontOutline(t->outlineFont);
@ -287,3 +285,4 @@ void texture_destroy(Texture *texture)
TTF_CloseFont(texture->outlineFont);
free(texture);
}

View File

@ -51,8 +51,6 @@ texturecache_add(const char *path)
texture_load_from_file(tc->texture, path, renderer);
ht_set(textures, path, tc);
debug("Cached texture: %s", path);
} else {
debug("Retrieved cached texture: %s", path);
}
return tc->texture;

View File

@ -12,3 +12,28 @@ vector2d_is_opposite(Vector2d v1, Vector2d v2)
return ((v1.x > 0 && v2.x < 0) ^ (v1.y > 0 && v2.y < 0))
|| ((v1.x < 0 && v2.x > 0) ^ (v1.y < 0 && v2.y > 0));
}
void
vector2d_reverse(Vector2d *vec)
{
vec->x *= -1;
vec->y *= -1;
}
Vector2d
vector2d_to_direction(const Vector2d *vec)
{
Vector2d new = VEC2D(vec->x, vec->y);
if (new.x > 0)
new.x = 1;
else if (new.x < 0)
new.x = -1;
if (new.y > 0)
new.y = 1;
else if (new.y < 0)
new.y = -1;
return new;
}

View File

@ -27,6 +27,8 @@
#define VECTOR2D_UP (Vector2d) { 0, -1 }
#define VECTOR2D_DOWN (Vector2d) { 0, 1 }
#define VEC2D(x, y) (Vector2d) { x, y }
typedef struct Vector2d_t {
float x;
float y;
@ -38,4 +40,10 @@ vector2d_equals(Vector2d, Vector2d);
bool
vector2d_is_opposite(Vector2d, Vector2d);
Vector2d
vector2d_to_direction(const Vector2d*);
void
vector2d_reverse(Vector2d*);
#endif // VECTOR2D_H_