Compare commits

...

130 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
Linus Probert c4811ed073 Fixes checksumtool output again 2019-03-21 10:01:04 +01:00
Linus Probert eceb241162 Removes useless text from checksumtool output 2019-03-21 08:53:31 +01:00
Linus Probert cea891f87d Move checksum configuration to CMakeLists.txt and config.h 2019-03-21 08:52:39 +01:00
Linus Probert 8e282304ad Fixes release notes 2019-03-20 20:29:59 +01:00
Linus Probert 53703f1761 Patch version raised to 1 2019-03-20 20:28:29 +01:00
Linus Probert ce182ad20f Updates checksums 2019-03-20 20:27:06 +01:00
Linus Probert ebfe2715e0 Fixes compile error and some graphical issues
- Keys and artifacts should render on to of other items
- Locked doors will impact surrounding wall layouts
2019-03-20 19:56:30 +01:00
Linus Probert 2ae6ada297 Completes locked room generation 2019-03-20 09:53:35 +01:00
Linus Probert 18987e2c2a Incomplete monster key fixes 2019-03-19 21:54:58 +01:00
Linus Probert 59a1e81c6e Implementation of keys. Completely untested 2019-03-18 11:39:26 +01:00
Linus Probert 1065216b67 Include all files in pack files 2019-03-16 10:32:58 +01:00
Linus Probert 68533f05b3 Remove memory leaks from map destruction 2019-03-15 16:00:20 +01:00
Linus Probert 38dc3deb2c Merge branch 'dev' of github.com:liquidityc/breakhack into dev 2019-03-14 19:15:52 +01:00
Linus Probert beda4f6c7e Reduces fairy frequency 2019-03-14 19:15:45 +01:00
Linus Probert e50d40fb1c Adds doors and greater chance of walls 2019-03-14 19:11:25 +01:00
Linus Probert 0983bd1bd2 Fixes a weird issue with missing code 2019-03-14 08:52:27 +01:00
Linus Probert 184b42ff5a Fixes door logic 2019-03-11 16:28:57 +01:00
Linus Probert e42e0243af Adds some door logic 2019-03-11 16:22:39 +01:00
Linus Probert 756afbae9d Separates walls from regular tiles 2019-03-11 16:21:15 +01:00
Linus Probert 40a20e936c Adapts fopen/fopen_s to WIN32/GCC compile 2019-03-11 08:02:31 +01:00
Linus Probert af930f6b23 Fixes win compiler warnings 2019-03-11 07:27:50 +01:00
Linus Probert c4fd16d925 Updated dll checksums for windows build 2019-03-11 07:21:43 +01:00
Linus Probert f840b28839 Adds mediocre crack protection
- Added a simple checksum calculation lib
- Validates the hardcoded "lib file" checksum against the calculated
    checksum.

By looking at instructions for the cracked version of BreakHack on
various sites that show up on google it seems that the common theme is
to replace the steamlib dll/so with a modified version. Now the Steam
version of the game will validate a hardcoded checksum against a live
validated version to see if everything seems ok before boot.

It's not a major hinderance but it should eliminate the more basic
sites modus operandi.

Honestly I don't really care if people steal the game. IT'S OPEN SOURCE
FFS!

And the above exclamation confirms why I did this. Also, I wanted to se
if it was possible. Game on Crackers! :D
2019-03-11 00:06:06 +01:00
Linus Probert 7fbeaa3907 Introduced base structure for doors in maps 2019-03-10 19:33:47 +01:00
Linus Probert 3e43c30bcb Adds funny casting to avoid pedantic ISO C warning 2019-03-10 07:30:12 +01:00
Linus Probert fd3a625249 Adds monster bloodlust
- Introduces the beginning of an event listener system
- Introduces fairies. When they are killed monsters in the room rage!
2019-03-10 00:15:47 +01:00
Linus Probert d749861477 Make jumbled rooms more likely 2019-03-09 08:55:59 +01:00
Linus Probert 7ab5ece83b Nicer wall decorations on interior walls 2019-03-08 16:58:25 +01:00
Linus Probert 8c9622d6bd Shopkeeper now has bodyguards next time you meet after kiling him
Fixed cl warnings in cmake and added some more walldecorations as well.
Wall decorations needs better logic so we get the right decoration on
the right tile.
2019-03-08 14:39:23 +01:00
Linus Probert af0c61684d Adds juice
- Dust puffs when tiles begin to fall
- Fixed shop prices. 100 gold is a lot of money and it impacts your
    score when buying.
2019-03-08 13:02:56 +01:00
Linus Probert 46fbdc1b3c Fixed jumble layout logic 2019-03-08 12:39:21 +01:00
Linus Probert b40c54a88d Layouts can now be jumbled with each other
This takes quadrants from 4 random layouts and creates a new layout
based on those quadrants.

This is not done for shop layouts.
2019-03-08 12:00:08 +01:00
Linus Probert 8f20f36db7 Lighting on wall layouts and aggro shopkeepers 2019-03-08 10:17:06 +01:00
Linus Probert 3ba33d8852 Adds light tiles to layout files and a shopkeeper
Next step is to add game logic for shopkeeper murderers and
ability to take items after the shopkeeper is dead.
2019-03-07 20:06:04 +01:00
Linus Probert 447c1a773c Monster shopkeeper logic 2019-03-07 15:31:09 +01:00
Linus Probert 7e1dffb45e Shops implemented
Only need to add the shopkeeper now
2019-03-07 11:09:14 +01:00
Linus Probert 6009aa17f1 Merge branch 'dev' into shops
Conflicts:
	src/artifact.c
2019-03-06 22:30:17 +01:00
Linus Probert 9b4a7b6cfc Removes crazy old bug that never presented itself before
This isn't an issue with the current release because destruction of
objects and artifact objects is identical. Only became an issue now that
the artifact struct and destruction is chaning.
2019-03-06 22:28:13 +01:00
Linus Probert 63812fbe05 More wall layouts and cordinated walls 2019-03-06 18:21:27 +01:00
Linus Probert c6a022e2db Moved text sprite logic to sprite_util 2019-03-04 20:32:09 +01:00
Linus Probert 06ca6c3e1e Not finnished yet, commit 2019-03-04 18:06:46 +01:00
Linus Probert bab5fd7980 Merge branch 'dev' into shops 2019-03-04 16:06:58 +01:00
Linus Probert 68f44090ed Add amt sprite to all items where value != 0 2019-03-04 16:06:13 +01:00
Linus Probert 959fae1730 Add value sprite to items.
Completely untested
2019-03-04 15:00:35 +01:00
Linus Probert ca41520c06 Wall layouts added to regular build 2019-03-04 13:13:58 +01:00
Linus Probert 88f4703d00 Some more fence layouts 2019-03-04 13:05:23 +01:00
Linus Probert 486c1bb641 Merge branch 'dev' into shops 2019-03-04 12:52:20 +01:00
Linus Probert 206f95f3b6 Fences and walls in layout files 2019-03-04 10:09:55 +01:00
Linus Probert 1c4e600dd4 Wall layout data 2019-03-04 07:35:26 +01:00
Linus Probert 57a16888b8 Merge branch 'dev' into shops 2019-03-01 12:44:49 +01:00
Linus Probert 13ca11c70e Begins refactoring of layout parser to accomodate more layout details 2019-03-01 12:44:12 +01:00
Linus Probert 33efba87b5 Merge branch 'dev' into shops 2019-03-01 08:58:33 +01:00
Linus Probert 5d61a91139 Split pit layout logic into separate file
The plan is to extend this further to incorporate walls and stuff.
This can later be used for the shop layout.
2019-03-01 08:57:15 +01:00
Linus Probert 6542e4f1d0 Moves item update to its own function 2019-02-28 21:53:30 +01:00
Linus Probert 7b47c6a6bf Completes buyable items 2019-02-28 20:37:19 +01:00
Linus Probert 16d9035941 Merge branch 'dev' into shops 2019-02-28 19:41:30 +01:00
Linus Probert 4054c37caa Ignore vscode_build dir 2019-02-28 19:41:07 +01:00
Linus Probert ad227f1bed A start to "priced" items 2019-02-28 13:49:15 +01:00
Linus Probert 37ba189e54 Suppress cppcheck issues 2019-02-28 13:31:56 +01:00
Linus Probert 645a02c99e Ignore .vscode folder 2019-02-27 10:40:43 +01:00
109 changed files with 4310 additions and 685 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']

6
.gitignore vendored
View File

@ -1,3 +1,5 @@
/.vscode/
/vscode_build/
/CMakeSettings.json /CMakeSettings.json
/_build/ /_build/
/*.dll /*.dll
@ -13,3 +15,7 @@
/steam_appid.txt /steam_appid.txt
/*.so /*.so
/breakhack*.run /breakhack*.run
compile_commands.json
*.o
breakhack

2
.vimrc
View File

@ -5,5 +5,5 @@ nnoremap <F4> :ter ++close env LD_LIBRARY_PATH=$LD_LIBRARY_PATH:./ ./_build/debu
packadd termdebug packadd termdebug
let g:termdebug_wide = 1 let g:termdebug_wide = 1
let g:syntastic_c_include_dirs = [ '_build/debug', '/usr/include/SDL2', 'steamworks_c_wrapper/src', 'physfs-3.0/src', 'bh_random/src' ] let g:syntastic_c_include_dirs = [ '_build/debug', '/usr/include/SDL2', 'steamworks_c_wrapper/src', 'physfs-3.0/src', 'bh_random/src', 'checksum/src' ]
let g:syntastic_cpp_include_dirs = [ 'steamworks_c_wrapper/sdk/public/steam', 'bh_random/src' ] let g:syntastic_cpp_include_dirs = [ 'steamworks_c_wrapper/sdk/public/steam', 'bh_random/src' ]

View File

@ -5,11 +5,15 @@ SET(CMAKE_COLOR_MAKEFILE ON)
project(breakhack C) project(breakhack C)
set(breakhack_GAME_TITLE "BreakHack") set(breakhack_GAME_TITLE "BreakHack")
set(breakhack_MAJOR_VERSION 2) set(breakhack_MAJOR_VERSION 4)
set(breakhack_MINOR_VERSION 1) set(breakhack_MINOR_VERSION 0)
set(breakhack_PATCH_VERSION 4) set(breakhack_PATCH_VERSION 3)
set(breakhack_RELEASE_TYPE "") set(breakhack_RELEASE_TYPE "")
# Checksums
set(breakhack_STEAMAPI_DLL_CHECKSUM 0x18dba28)
set(breakhack_STEAMAPI_SO_CHECKSUM 0x1f5786b)
include(FindLua) include(FindLua)
include(FindPhysFS) include(FindPhysFS)
include(build/cmake/FindSDL2.cmake) include(build/cmake/FindSDL2.cmake)
@ -40,6 +44,7 @@ if (STEAM)
add_subdirectory(steamworks_c_wrapper) add_subdirectory(steamworks_c_wrapper)
endif() endif()
add_subdirectory(bh_random) add_subdirectory(bh_random)
add_subdirectory(checksum)
if ("${CMAKE_C_COMPILER_ID}" STREQUAL "Clang") if ("${CMAKE_C_COMPILER_ID}" STREQUAL "Clang")
set(CLANG 1) set(CLANG 1)
@ -47,6 +52,10 @@ elseif ("${CMAKE_C_COMPILER_ID}" STREQUAL "GNU")
set(GCC 1) set(GCC 1)
endif() endif()
if ("${CMAKE_GENERATOR}" STREQUAL "Ninja")
set(NINJA 1)
endif()
if ("${CMAKE_SYSTEM_NAME}" STREQUAL "Darwin") if ("${CMAKE_SYSTEM_NAME}" STREQUAL "Darwin")
set(OSX 1) set(OSX 1)
endif() endif()
@ -68,15 +77,17 @@ endif()
IF ( MSVC ) IF ( MSVC )
MESSAGE ( STATUS "Setting MSVC MT switches") MESSAGE ( STATUS "Setting MSVC MT switches")
SET ( string (REPLACE
CMAKE_C_FLAGS_DEBUG "/MDd"
"${CMAKE_C_FLAGS_DEBUG} /MTd" "/MTd"
CACHE STRING "MSVC MT flags " FORCE CMAKE_C_FLAGS_DEBUG
${CMAKE_C_FLAGS_DEBUG}
) )
SET ( string (REPLACE
CMAKE_C_FLAGS_RELEASE "/MDd"
"${CMAKE_C_FLAGS_RELEASE} /MT" "/MTd"
CACHE STRING "MSVC MT flags " FORCE CMAKE_C_FLAGS_RELEASE
${CMAKE_C_FLAGS_RELEASE}
) )
ELSEIF ( WIN32 ) ELSEIF ( WIN32 )
SET ( SET (
@ -128,6 +139,7 @@ include_directories(
${LUA_INCLUDE_DIR} ${LUA_INCLUDE_DIR}
sqlite3 sqlite3
bh_random/src bh_random/src
checksum/src
) )
if (CMOCKA_FOUND) if (CMOCKA_FOUND)
@ -153,17 +165,6 @@ set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -DDEBUG")
if (STEAM) if (STEAM)
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -DSTEAM_BUILD") set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -DSTEAM_BUILD")
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -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 set(STEAM_SOURCES
src/steam/steamworks_api_wrapper src/steam/steamworks_api_wrapper
) )
@ -171,59 +172,74 @@ else ()
set(STEAM_SOURCES "") set(STEAM_SOURCES "")
endif () 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: # PROGRAMS:
add_executable(breakhack add_executable(breakhack
src/main src/main.c
src/texture src/texture.c
src/screenresolution src/screenresolution.c
src/sprite src/sprite.c
src/util src/sprite_util.c
src/player src/util.c
src/map src/event.c
src/map_lua src/player.c
src/camera src/save.c
src/timer src/map.c
src/roommatrix src/map_lua.c
src/position src/camera.c
src/monster src/timer.c
src/stats src/roommatrix.c
src/actiontext src/position.c
src/random src/monster.c
src/time src/stats.c
src/linkedlist src/actiontext.c
src/hashtable src/random.c
src/gui src/time.c
src/item src/linkedlist.c
src/item_builder src/hashtable.c
src/pointer src/gui.c
src/gui_button src/item.c
src/particle_engine src/item_builder.c
src/menu src/pointer.c
src/collisions src/gui_button.c
src/keyboard src/particle_engine.c
src/input src/particle_emitter.c
src/mixer src/menu.c
src/io_util src/collisions.c
src/physfsrwops src/keyboard.c
src/skillbar src/input.c
src/texturecache src/mixer.c
src/skill src/io_util.c
src/projectile src/physfsrwops.c
src/vector2d src/skillbar.c
src/map_room_modifiers src/texturecache.c
sqlite3/sqlite3 src/skill.c
src/db src/projectile.c
src/settings src/vector2d.c
src/actiontextbuilder src/map_room_modifiers.c
src/animation sqlite3/sqlite3.c
src/trap src/db.c
src/artifact src/settings.c
src/screen src/actiontextbuilder.c
src/hiscore src/animation.c
src/object src/trap.c
src/gui_util src/artifact.c
src/tooltip src/screen.c
src/gamecontroller src/hiscore.c
src/object.c
src/gui_util.c
src/tooltip.c
src/gamecontroller.c
src/effect_util.c
${STEAM_SOURCES} ${STEAM_SOURCES}
) )
@ -245,6 +261,7 @@ target_link_libraries(breakhack
${LUA_LIBRARIES} ${LUA_LIBRARIES}
${PHYSFS_LIBRARY} ${PHYSFS_LIBRARY}
bh_random bh_random
checksum
) )
if (STEAM) if (STEAM)
@ -254,11 +271,11 @@ if (STEAM)
endif () endif ()
if (MSVC) 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 COMPILE_DEFINITIONS_DEBUG "_CONSOLE")
set_target_properties(breakhack PROPERTIES LINK_FLAGS_RELWITHDEBINFO "/SUBSYSTEM: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 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") set_target_properties(breakhack PROPERTIES LINK_FLAGS_MINSIZEREL "/SUBSYSTEM:WINDOWS")
endif (MSVC) endif (MSVC)
@ -266,19 +283,19 @@ endif (MSVC)
IF (CMOCKA_FOUND AND NOT OSX AND NOT CLANG) IF (CMOCKA_FOUND AND NOT OSX AND NOT CLANG)
find_package(Threads REQUIRED) find_package(Threads REQUIRED)
enable_testing() 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}) target_link_libraries(test_util ${CMOCKA_LIBRARY})
add_test(test_util test_util) 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}) target_link_libraries(test_linkedlist ${CMOCKA_LIBRARY})
add_test(test_linkedlist test_linkedlist) 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}) target_link_libraries(test_hashtable ${CMOCKA_LIBRARY})
add_test(test_hashtable test_hashtable) 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 target_link_libraries(test_input
${CMOCKA_LIBRARY} ${CMOCKA_LIBRARY}
${SDL2_LIBRARY} ${SDL2_LIBRARY}
@ -292,7 +309,7 @@ ENDIF ()
# LINT: # LINT:
if (CPPCHECK_FOUND) if (CPPCHECK_FOUND)
add_custom_target(lint add_custom_target(lint
COMMAND ${CPPCHECK_EXECUTABLE} --force --language=c --template=gcc --error-exitcode=1 --quiet --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} WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
COMMENT "Run cppcheck" COMMENT "Run cppcheck"
) )
@ -319,16 +336,13 @@ if (NOT DEBUG_BUILD)
"monstergen.lua" "monstergen.lua"
"trapgen.lua" "trapgen.lua"
"chestgen.lua" "chestgen.lua"
"layoutparser.lua"
"pitlayouts.dat" "pitlayouts.dat"
"walllayouts.dat"
"shoplayouts.dat"
"lockedroomlayouts.dat"
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/data WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/data
) )
if (WIN32 AND NOT MSVC)
add_definitions(-mwindows)
endif()
else()
if (WIN32)
add_definitions(-mconsole)
endif()
endif () endif ()
SET(CMAKE_INSTALL_SYSTEM_RUNTIME_COMPONENT "Release") SET(CMAKE_INSTALL_SYSTEM_RUNTIME_COMPONENT "Release")

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 .PHONY: release
clean: clean:
@make clean -sC _build/debug @make -sC _build/debug clean
@make clean -sC _build/release @make -sC _build/release clean
.PHONY: clean .PHONY: clean
test: test:
@make test -sC _build/debug @make -sC _build/debug test
.PHONY: test .PHONY: test
run: $(all) run: $(all)
@ -21,12 +21,30 @@ run: $(all)
playtest: $(all) playtest: $(all)
@LD_LIBRARY_PATH=$LD_LIBRARY_PATH:./ ./_build/release/breakhack @LD_LIBRARY_PATH=$LD_LIBRARY_PATH:./ ./_build/release/breakhack
.PHONY: run .PHONY: playtest
lint: lint:
@make lint -sC _build/debug @make -sC _build/debug lint
.PHONY: lint .PHONY: lint
package: package:
@make package -sC _build/release @make -sC _build/release package
.PHONY: 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://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) [![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) [![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 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). * [Linux](LINUX_BUILD_INSTRUCTIONS.md)
Optionally you can also install **cppcheck** and **physfs** * [Windows (Visual Studio)](MSVC_BUILD_INSTRUCTIONS.md)
Once that is done run the following:
```bash
mkdir _build
cd _build
cmake -DCMAKE_BUILD_TYPE=Debug .. # Build type flag is optional
make
```
Contribute 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.

Binary file not shown.

View File

@ -8,20 +8,22 @@ if (NOT CMAKE_BUILD_TYPE)
endif () endif ()
add_library(bh_random STATIC add_library(bh_random STATIC
src/bh_random src/bh_random.cpp
) )
IF ( MSVC ) IF ( MSVC )
MESSAGE ( STATUS "Setting MSVC MT switches") MESSAGE ( STATUS "Setting MSVC MT switches")
SET ( string (REPLACE
CMAKE_CXX_FLAGS_DEBUG "/MDd"
"${CMAKE_CXX_FLAGS_DEBUG} /MTd" "/MTd"
CACHE STRING "MSVC MT flags " FORCE CMAKE_C_FLAGS_DEBUG
${CMAKE_C_FLAGS_DEBUG}
) )
SET ( string (REPLACE
CMAKE_CXX_FLAGS_RELEASE "/MDd"
"${CMAKE_CXX_FLAGS_RELEASE} /MT" "/MTd"
CACHE STRING "MSVC MT flags " FORCE CMAKE_C_FLAGS_RELEASE
${CMAKE_C_FLAGS_RELEASE}
) )
endif () endif ()

View File

@ -64,6 +64,14 @@
# (To distribute this file outside of CMake, substitute the full # (To distribute this file outside of CMake, substitute the full
# License text for the above reference.) # 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 SET(SDL2_SEARCH_PATHS
~/Library/Frameworks ~/Library/Frameworks
/Library/Frameworks /Library/Frameworks
@ -87,7 +95,7 @@ FIND_LIBRARY(SDL2_LIBRARY_TEMP
NAMES SDL2 NAMES SDL2
HINTS HINTS
$ENV{SDL2DIR} $ENV{SDL2DIR}
PATH_SUFFIXES lib64 lib PATH_SUFFIXES lib64 lib lib/${SDL2_PROCESSOR_ARCH}
PATHS ${SDL2_SEARCH_PATHS} ${SDL2_INCLUDE_DIR}/../.. PATHS ${SDL2_SEARCH_PATHS} ${SDL2_INCLUDE_DIR}/../..
) )
@ -101,7 +109,7 @@ IF(NOT SDL2_BUILDING_LIBRARY)
NAMES SDL2main NAMES SDL2main
HINTS HINTS
$ENV{SDL2DIR} $ENV{SDL2DIR}
PATH_SUFFIXES lib64 lib PATH_SUFFIXES lib64 lib lib/${SDL2_PROCESSOR_ARCH}
PATHS ${SDL2_SEARCH_PATHS} PATHS ${SDL2_SEARCH_PATHS}
) )
ENDIF(NOT ${SDL2_INCLUDE_DIR} MATCHES ".framework") ENDIF(NOT ${SDL2_INCLUDE_DIR} MATCHES ".framework")

View File

@ -28,6 +28,14 @@
# See the License for more information. # 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) if(NOT SDL2_TTF_INCLUDE_DIR AND SDL2TTF_INCLUDE_DIR)
set(SDL2_TTF_INCLUDE_DIR ${SDL2TTF_INCLUDE_DIR} CACHE PATH "directory cache set(SDL2_TTF_INCLUDE_DIR ${SDL2TTF_INCLUDE_DIR} CACHE PATH "directory cache
entry initialized from old variable name") entry initialized from old variable name")
@ -48,7 +56,7 @@ find_library(SDL2_TTF_LIBRARY
HINTS HINTS
ENV SDLTTFDIR ENV SDLTTFDIR
ENV SDLDIR 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") if(SDL2_TTF_INCLUDE_DIR AND EXISTS "${SDL2_TTF_INCLUDE_DIR}/SDL_ttf.h")

View File

@ -0,0 +1,60 @@
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
2ae6ada Completes locked room generation
18987e2 Incomplete monster key fixes
59a1e81 Implementation of keys. Completely untested
1065216 Include all files in pack files
68533f0 Remove memory leaks from map destruction
38dc3de Merge branch 'dev' of github.com:liquidityc/breakhack into dev
beda4f6 Reduces fairy frequency
e50d40f Adds doors and greater chance of walls
0983bd1 Fixes a weird issue with missing code
184b42f Fixes door logic
e42e024 Adds some door logic
756afba Separates walls from regular tiles
40a20e9 Adapts fopen/fopen_s to WIN32/GCC compile
af930f6 Fixes win compiler warnings
c4fd16d Updated dll checksums for windows build
f840b28 Adds mediocre crack protection
7fbeaa3 Introduced base structure for doors in maps
3e43c30 Adds funny casting to avoid pedantic ISO C warning
fd3a625 Adds monster bloodlust
d749861 Make jumbled rooms more likely
7ab5ece Nicer wall decorations on interior walls
8c9622d Shopkeeper now has bodyguards next time you meet after kiling him
af0c616 Adds juice
46fbdc1 Fixed jumble layout logic
b40c54a Layouts can now be jumbled with each other
8f20f36 Lighting on wall layouts and aggro shopkeepers
3ba33d8 Adds light tiles to layout files and a shopkeeper
447c1a7 Monster shopkeeper logic
7e1dffb Shops implemented
6009aa1 Merge branch 'dev' into shops
9b4a7b6 Removes crazy old bug that never presented itself before
63812fb More wall layouts and cordinated walls
c6a022e Moved text sprite logic to sprite_util
06ca6c3 Not finnished yet, commit
bab5fd7 Merge branch 'dev' into shops
68f4409 Add amt sprite to all items where value != 0
959fae1 Add value sprite to items.
ca41520 Wall layouts added to regular build
88f4703 Some more fence layouts
486c1bb Merge branch 'dev' into shops
206f95f Fences and walls in layout files
1c4e600 Wall layout data
57a1688 Merge branch 'dev' into shops
13ca11c Begins refactoring of layout parser to accomodate more layout details
33efba8 Merge branch 'dev' into shops
5d61a91 Split pit layout logic into separate file
6542e4f Moves item update to its own function
7b47c6a Completes buyable items
16d9035 Merge branch 'dev' into shops
4054c37 Ignore vscode_build dir
ad227f1 A start to "priced" items
37ba189 Suppress cppcheck issues
645a02c Ignore .vscode folder

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 -

33
checksum/CMakeLists.txt Normal file
View File

@ -0,0 +1,33 @@
cmake_minimum_required(VERSION 3.1)
project(checksum C)
if (NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE Release)
endif ()
add_executable(checksumtool
src/checksum.c
)
target_compile_definitions(checksumtool PUBLIC EXECUTABLE=1)
add_library(checksum
src/checksum.c
)
IF ( MSVC )
MESSAGE ( STATUS "Setting MSVC MT switches")
string (REPLACE
"/MDd"
"/MTd"
CMAKE_C_FLAGS_DEBUG
${CMAKE_C_FLAGS_DEBUG}
)
string (REPLACE
"/MDd"
"/MTd"
CMAKE_C_FLAGS_RELEASE
${CMAKE_C_FLAGS_RELEASE}
)
endif ()

61
checksum/src/checksum.c Normal file
View File

@ -0,0 +1,61 @@
/*
** CHECKSUM.C - Compute the checksum of a file
**
** public somain demo by Bob Stout
*/
#include <stdlib.h>
#include "checksum.h"
unsigned
checksum(void *buffer, size_t len, unsigned int seed)
{
unsigned char *buf = (unsigned char *)buffer;
size_t i;
for (i = 0; i < len; ++i)
seed += (unsigned int)(*buf++);
return seed;
}
unsigned
checksum_fp(FILE *fp)
{
unsigned int seed = 0;
char buf[4096];
size_t len;
do {
len = fread(buf, sizeof(char), sizeof(buf), fp);
seed = checksum(buf, len, seed);
} while (len > 0);
return seed;
}
#ifdef EXECUTABLE
#include <stdio.h>
int main(int argc, char *argv[])
{
FILE *fp;
const char *file;
if (argc < 2) {
printf("You need to provide an input file\n");
printf("Example: %s <file>", argv[0]);
}
file = argv[1];
if (NULL == (fp = fopen(file, "rb")))
{
printf("Unable to open %s for reading\n", file);
return -1;
}
printf("%#x\n", checksum_fp(fp));
return 0;
}
#endif

27
checksum/src/checksum.h Normal file
View File

@ -0,0 +1,27 @@
/*
* 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 <stdio.h>
unsigned
checksum(void *buffer, size_t len, unsigned int seed);
unsigned
checksum_fp(FILE *fp);

406
data/layoutparser.lua Normal file
View File

@ -0,0 +1,406 @@
local random = map_random
local pits = {}
local walls = {}
local fences = {}
local lights = {}
local doors = {}
local walldecor = {}
local chest
local function readLayoutFile(file)
local layoutfile = read_file(file)
local cleanData = ""
for i=1, #layoutfile do
local c = layoutfile:sub(i+1, i+1)
if c ~= " " and c ~= "\n" and c ~= "\r" then
cleanData = cleanData .. c
end
end
local matrix = {}
for i=0, #cleanData-1 do
local c = cleanData:sub(i, i)
local col = i % 16
local row = (i - col)/16
local layout = 1 + (row - (row % 12))/12
local row = row % 12
if not matrix[layout] then matrix[layout] = {} end
if not matrix[layout][col] then matrix[layout][col] = {} end
matrix[layout][col][row] = c
end
return matrix;
end
local function has_value(list, char)
for _, value in ipairs(list) do
if value == char then return true end
end
return false
end
local function getTileStateFor(matrix, i, j, c)
local charList
if type(c) == "string" then
charList = { c }
else
charList = c
end
local above = has_value(charList, matrix[i][j-1])
local below = has_value(charList, matrix[i][j+1])
local left = has_value(charList, matrix[i-1][j])
local right = has_value(charList, matrix[i+1][j])
local above_left = has_value(charList, matrix[i-1][j-1])
local above_right = has_value(charList, matrix[i+1][j-1])
local below_left = has_value(charList, matrix[i-1][j+1])
local below_right = has_value(charList, matrix[i+1][j+1])
return above, below, left, right, above_left, above_right, below_left, below_right
end
local function getRandomWallDecorFrom(wallDecorations)
return wallDecorations[random(#wallDecorations)]
end
local function setBlockTile(room, matrix, i, j, tiles, char, decor)
local above, below, left, right, above_left, above_right, below_left, below_right = getTileStateFor(matrix, i, j, char);
room.decor[i][j] = nil
local tile = nil
local decorTile = nil
if above and below and left and right then
tile = tiles.cross
elseif not above and below and left and right then
tile = tiles.top_t
elseif not below and above and left and right then
tile = tiles.bottom_t
decorTile = getRandomWallDecorFrom(walldecor.bottom_t)
elseif not left and above and below and right then
tile = tiles.left_t
decorTile = getRandomWallDecorFrom(walldecor.left_t)
elseif not right and above and below and left then
tile = tiles.right_t
decorTile = getRandomWallDecorFrom(walldecor.right_t)
elseif not above and not left and right and below then
tile = tiles.topleft
decorTile = getRandomWallDecorFrom(walldecor.topleft)
elseif not above and not right and left and below then
tile = tiles.topright
decorTile = getRandomWallDecorFrom(walldecor.topright)
elseif not below and not left and above and right then
tile = tiles.bottomleft
decorTile = getRandomWallDecorFrom(walldecor.bottomleft)
elseif not below and not right and above and left then
tile = tiles.bottomright
decorTile = getRandomWallDecorFrom(walldecor.bottomright)
elseif not left and not right and below then
tile = tiles.left
decorTile = getRandomWallDecorFrom(walldecor.left)
elseif not above and not below and (left or right) then
tile = tiles.top
decorTile = getRandomWallDecorFrom(walldecor.top)
else
tile = tiles.single
decorTile = getRandomWallDecorFrom(walldecor.single)
end
room.walls[i][j] = tile
if random(8) == 1 and decor then
room.decor[i][j] = decorTile
end
end
local function setPitTile(room, matrix, i, j)
local above, below, left, right, above_left, above_right, below_left, below_right = getTileStateFor(matrix, i, j, "p");
room.decor[i][j] = nil
if not above_left and not above_right and left and right and above then
room.tiles[i][j] = pits.innermid
elseif not above_left and left and above then
room.tiles[i][j] = pits.innerleft
elseif not above_right and right and above then
room.tiles[i][j] = pits.innerright
elseif not left and not above and not right then
room.tiles[i][j] = pits.topcrevice
elseif not left and not right then
room.tiles[i][j] = pits.bottomcrevice
elseif not left and not above then
room.tiles[i][j] = pits.topleft
elseif not right and not above then
room.tiles[i][j] = pits.topright
elseif not left then
room.tiles[i][j] = pits.left
elseif not right then
room.tiles[i][j] = pits.right
elseif not above then
room.tiles[i][j] = pits.top
else
room.tiles[i][j] = pits.center
end
end
local function getDoor(matrix, i, j, topDoor, leftDoor)
local above, below, left, right, above_left, above_right, below_left, below_right = getTileStateFor(matrix, i, j, { "#", "\"", "/"});
if above and below then
return leftDoor
else
return topDoor
end
end
local module = {}
function module.load_textures(map, wall_xoffset, wall_yoffset)
local t_pit0 = add_texture(map, "Objects/Pit0.png")
local t_pit1 = add_texture(map, "Objects/Pit1.png")
local t_wall = add_texture(map, "Objects/Wall.png")
local t_fence = add_texture(map, "Objects/Fence.png")
local t_decor0 = add_texture(map, "Objects/Decor0.png")
local t_decor1 = add_texture(map, "Objects/Decor1.png")
local t_door0 = add_texture(map, "Objects/Door0.png")
local t_door1 = add_texture(map, "Objects/Door1.png")
local yo = (random(5) + random(3)) * (16 * 2)
pits = {
center = { t_pit0, t_pit1, 16, yo + 16, false, false, false, true },
top = { t_pit0, t_pit1, 16, yo, false, false, false, true },
left = { t_pit0, t_pit1, 0, yo + 16, false, false, false, true },
right = { t_pit0, t_pit1, 32, yo + 16, false, false, false, true },
topleft = { t_pit0, t_pit1, 0, yo, false, false, false, true },
topright = { t_pit0, t_pit1, 32, yo, false, false, false, true },
innerleft = { t_pit0, t_pit1, 80, yo, false, false, false, true },
innermid = { t_pit0, t_pit1, 96, yo, false, false, false, true },
innerright = { t_pit0, t_pit1, 112, yo, false, false, false, true },
topcrevice = { t_pit0, t_pit1, 64, yo, false, false, false, true },
bottomcrevice = { t_pit0, t_pit1, 64, yo + 16, false, false, false, true },
}
local xo = wall_xoffset
yo = wall_yoffset
walls = {
topleft = { t_wall, nil, xo + 0, yo, true },
top = { t_wall, nil, xo + 16, yo, true },
single = { t_wall, nil, xo + 16, yo + 16, true },
topright = { t_wall, nil, xo + 32, yo, true },
left = { t_wall, nil, xo + 0, yo + 16, true },
bottomleft = { t_wall, nil, xo + 0, yo + 32, true },
bottomright = { t_wall, nil, xo + 32, yo + 32, true },
center = { t_wall, nil, xo + 48, yo, true },
top_t = { t_wall, nil, xo + 64, yo, true },
left_t = { t_wall, nil, xo + 48, yo + 16, true },
cross = { t_wall, nil, xo + 64, yo + 16, true },
right_t = { t_wall, nil, xo + 80, yo + 16, true },
bottom_t = { t_wall, nil, xo + 64, yo + 32, true },
}
yo = 48 * random(3)
fences = {
topleft = { t_fence, nil, 0, yo, true },
top = { t_fence, nil, 16, yo, true },
single = { t_fence, nil, 0, yo + 16, true },
topright = { t_fence, nil, 32, yo, true },
left = { t_fence, nil, 0, yo + 16, true },
bottomleft = { t_fence, nil, 0, yo + 32, true },
bottomright = { t_fence, nil, 32, yo + 32, true },
center = { t_fence, nil, 48, yo, true },
top_t = { t_fence, nil, 64, yo, true },
left_t = { t_fence, nil, 48, yo + 16, true },
cross = { t_fence, nil, 64, yo + 16, true },
right_t = { t_fence, nil, 80, yo + 16, true },
bottom_t = { t_fence, nil, 64, yo + 32, true },
}
doors = {
door_top_nolock = { t_door0, t_door1, 0, 0, true },
door_left_nolock = { t_door0, t_door1, 16, 0, true },
door_top_silverlock = { t_door0, t_door1, 32, 0, true, false, false, false, 1 },
door_left_silverlock = { t_door0, t_door1, 48, 0, true, false, false, false, 1 },
door_top_goldlock = { t_door0, t_door1, 64, 0, true, false, false, false, 2 },
door_left_goldlock = { t_door0, t_door1, 80, 0, true, false, false, false, 2 },
gate_top_nolock = { t_door0, t_door1, 0, 32, true },
gate_left_nolock = { t_door0, t_door1, 16, 32, true },
gate_top_silverlock = { t_door0, t_door1, 32, 32, true, false, false, false, 1 },
gate_left_silverlock = { t_door0, t_door1, 48, 32, true, false, false, false, 1 },
gate_top_goldlock = { t_door0, t_door1, 64, 32, true, false, false, false, 2 },
gate_left_goldlock = { t_door0, t_door1, 80, 32, true, false, false, false, 2 },
}
lights = {
candle0 = { t_decor0, t_decor1, 3 * 16, 8 * 16, true, true },
candle1 = { t_decor0, t_decor1, 1 * 16, 8 * 16, true, true },
candle2 = { t_decor0, t_decor1, 5 * 16, 8 * 16, true, false },
}
walldecor = {
topleft = {
{ t_decor0, nil, 2 * 16, 2 * 16, false },
{ t_decor0, nil, 6 * 16, 2 * 16, false },
{ t_decor0, nil, 7 * 16, 2 * 16, false },
},
top = {
{ t_decor0, nil, 0 * 16, 2 * 16, false },
{ t_decor0, nil, 1 * 16, 2 * 16, false },
{ t_decor0, nil, 4 * 16, 2 * 16, false },
{ t_decor0, nil, 5 * 16, 2 * 16, false },
},
single = {
{ t_decor0, nil, 0 * 16, 2 * 16, false },
{ t_decor0, nil, 1 * 16, 2 * 16, false },
{ t_decor0, nil, 4 * 16, 2 * 16, false },
{ t_decor0, nil, 5 * 16, 2 * 16, false },
},
topright = {
{ t_decor0, nil, 3 * 16, 2 * 16, false },
},
left = {
{ t_decor0, nil, 2 * 16, 2 * 16, false },
{ t_decor0, nil, 3 * 16, 2 * 16, false },
{ t_decor0, nil, 6 * 16, 2 * 16, false },
{ t_decor0, nil, 7 * 16, 2 * 16, false },
},
bottomleft = {
{ t_decor0, nil, 0 * 16, 2 * 16, false },
{ t_decor0, nil, 1 * 16, 2 * 16, false },
{ t_decor0, nil, 2 * 16, 2 * 16, false },
{ t_decor0, nil, 4 * 16, 2 * 16, false },
{ t_decor0, nil, 5 * 16, 2 * 16, false },
{ t_decor0, nil, 6 * 16, 2 * 16, false },
{ t_decor0, nil, 7 * 16, 2 * 16, false },
},
bottomright = {
{ t_decor0, nil, 0 * 16, 2 * 16, false },
{ t_decor0, nil, 1 * 16, 2 * 16, false },
{ t_decor0, nil, 3 * 16, 2 * 16, false },
{ t_decor0, nil, 4 * 16, 2 * 16, false },
{ t_decor0, nil, 5 * 16, 2 * 16, false },
},
left_t = {
{ t_decor0, nil, 2 * 16, 2 * 16, false },
{ t_decor0, nil, 6 * 16, 2 * 16, false },
{ t_decor0, nil, 7 * 16, 2 * 16, false },
},
right_t = {
{ t_decor0, nil, 3 * 16, 2 * 16, false },
},
bottom_t = {
{ t_decor0, nil, 0 * 16, 2 * 16, false },
{ t_decor0, nil, 1 * 16, 2 * 16, false },
{ t_decor0, nil, 4 * 16, 2 * 16, false },
{ t_decor0, nil, 5 * 16, 2 * 16, false },
},
}
chest = { "Items/Chest0.png", "Items/Chest1.png", 16, 0}
end
function createJumbleLayout(matrix)
local room1 = matrix[random(#matrix)]
local room2 = matrix[random(#matrix)]
local room3 = matrix[random(#matrix)]
local room4 = matrix[random(#matrix)]
local room = {}
for i=0,15 do
for j=0,12 do
if not room[i] then room[i] = {} end
if not room[i][j] then room[i][j] = {} end
if i < 7 then
if j < 6 then
room[i][j] = room1[i][j]
else
room[i][j] = room2[i][j]
end
else
if j < 6 then
room[i][j] = room3[i][j]
else
room[i][j] = room4[i][j]
end
end
end
end
return room
end
function draw_layout_to_room(room, matrix, roomx, roomy)
local wallTypes = {"#", "\"", "/", "d", "g", "S", "G"}
for i=2,13 do
for j=2,10 do
if matrix[i][j] == "p" then
setPitTile(room, matrix, i, j);
elseif matrix[i][j] == "#" then
setBlockTile(room, matrix, i, j, walls, wallTypes, false)
elseif matrix[i][j] == "\"" then
setBlockTile(room, matrix, i, j, walls, wallTypes, false)
room.decor[i][j] = lights.candle1
elseif matrix[i][j] == "/" then
setBlockTile(room, matrix, i, j, walls, wallTypes, false)
if random(2) == 1 then
room.decor[i][j] = lights.candle1
else
room.decor[i][j] = lights.candle2
end
elseif matrix[i][j] == "f" then
setBlockTile(room, matrix, i, j, fences, "f", true)
elseif matrix[i][j] == "a" then
create_shop_artifact(map, (roomx*512) + i*32, (roomy * 384) + j*32)
elseif matrix[i][j] == "l" then
room.decor[i][j] = lights.candle0
elseif matrix[i][j] == "c" then
room.chests[i][j] = chest
elseif matrix[i][j] == "d" then
room.doors[i][j] = getDoor(matrix, i, j, doors.door_top_nolock, doors.door_left_nolock)
elseif matrix[i][j] == "S" then
room.doors[i][j] = getDoor(matrix, i, j, doors.door_top_silverlock, doors.door_left_silverlock)
elseif matrix[i][j] == "G" then
room.doors[i][j] = getDoor(matrix, i, j, doors.door_top_goldlock, doors.door_left_goldlock)
elseif matrix[i][j] == "g" then
room.doors[i][j] = getDoor(matrix, i, j, doors.gate_top_nolock, doors.gate_left_nolock)
end
end
end
end
function pickALayout(matrix)
-- Chose a random layout
if random(3) == 1 then
return matrix[random(#matrix)]
else
return createJumbleLayout(matrix)
end
end
function module.add_walls_to_room(room)
if random(2) == 1 then
return false
end
draw_layout_to_room(room, pickALayout(readLayoutFile("walllayouts.dat")))
return true
end
function module.add_pits_to_room(room)
if CURRENT_LEVEL < 2 or random(5) ~= 1 then
return false
end
draw_layout_to_room(room, pickALayout(readLayoutFile("pitlayouts.dat")))
return true
end
function module.add_shop_layout(room, roomx, roomy)
local matrix = readLayoutFile("shoplayouts.dat")
draw_layout_to_room(room, matrix[random(#matrix)], roomx, roomy)
return true
end
function module.add_locked_room_layout(room, roomx, roomy)
local matrix = readLayoutFile("lockedroomlayouts.dat")
draw_layout_to_room(room, matrix[random(#matrix)], roomx, roomy)
return true
end
return module

View File

@ -0,0 +1,90 @@
++++++++++++++++
++++++++++++++++
++"#####G####"++
++#-c--c--c--#++
++#----------#++
++#-c--c--c--#++
++G----------G++
++#-c--c--c--#++
++#----------#++
++"#####G####"++
++++++++++++++++
++++++++++++++++
++++++++++++++++
++++++++++++++++
++"#####S####"++
++#-c--c--c--#++
++#----------#++
++#-c--c--c--#++
++S----------S++
++#-c--c--c--#++
++#----------#++
++"#####S####"++
++++++++++++++++
++++++++++++++++
++++++++++++++++
++++++++++++++++
++/####-#####/++
++#--c#-#c---#++
++S---#-#----G++
++#####-######++
++------------++
++#####-######++
++S--c#-#c---G++
++/####-#####/++
++++++++++++++++
++++++++++++++++
++++++++++++++++
++++++++++++++++
++----#S#-----++
++----#c#-----++
++----#c#-----++
++----#c#-----++
++----###-----++
++------------++
++------------++
++------------++
++++++++++++++++
++++++++++++++++
++++++++++++++++
++++++++++++++++
++------------++
++------------++
++------------++
++---###S##---++
++---#cccc#---++
++---######---++
++------------++
++------------++
++++++++++++++++
++++++++++++++++
++++++++++++++++
++++++++++++++++
++----#G#-----++
++----#c#-----++
++----#c#-----++
++----#c#-----++
++----###-----++
++------------++
++------------++
++------------++
++++++++++++++++
++++++++++++++++
++++++++++++++++
++++++++++++++++
++------------++
++------------++
++------------++
++---###G##---++
++---#cccc#---++
++---######---++
++------------++
++------------++
++++++++++++++++
++++++++++++++++

View File

@ -14,6 +14,8 @@ local LEFT = 2
local RIGHT = 3 local RIGHT = 3
local DOWN = 4 local DOWN = 4
local lockedDoorsAdded = false
-- BEGIN FUNCTIONS -- BEGIN FUNCTIONS
local function matrix_coverage (matrix) local function matrix_coverage (matrix)
local cov = 0 local cov = 0
@ -49,9 +51,11 @@ local function generate_path ()
local direction = 0 local direction = 0
local lastDirection = 0 local lastDirection = 0
local coridoor_count = 0 local coridoor_count = 0
local shopLevel = CURRENT_LEVEL % 4 == 0
local bossLevel = CURRENT_LEVEL % 5 == 0 local bossLevel = CURRENT_LEVEL % 5 == 0
if QUICK_MODE then if QUICK_MODE then
bossLevel = CURRENT_LEVEL % 3 == 0 bossLevel = CURRENT_LEVEL % 3 == 0
shopLevel = CURRENT_LEVEL % 2 == 0
end end
local coverage = 8 + CURRENT_LEVEL local coverage = 8 + CURRENT_LEVEL
if bossLevel or CURRENT_LEVEL == 1 then if bossLevel or CURRENT_LEVEL == 1 then
@ -59,6 +63,11 @@ local function generate_path ()
end end
if ARCADE_MODE then if ARCADE_MODE then
coverage = 40 coverage = 40
shopLevel = true
end
if shopLevel then
coverage = coverage + 1
end end
-- Create the first room -- Create the first room
@ -114,17 +123,33 @@ local function generate_path ()
local roomCount = 0 local roomCount = 0
local bossAdded = false local bossAdded = false
local shopAdded = false
-- Build all the rooms -- Build all the rooms
for i=1,10 do for i=1,10 do
for j=1,10 do for j=1,10 do
room = map_matrix[i][j] room = map_matrix[i][j]
if room then if room then
if roomCount > 4 and shopLevel and not shopAdded and not room.goal then
room.type = "shop"
shopAdded = true
elseif random(8) == 1 and not room.goal then
room.type = "locked"
lockedDoorsAdded = true
end
roomCount = roomCount + 1 roomCount = roomCount + 1
room_builder.build_room(room) room_builder.build_room(room, i-1, j-1)
monster_gen.add_monsters_to_room(room, i-1, j-1) if room.type ~= "shop" then
trap_gen.add_traps_to_room(room, i-1, j-1) monster_gen.add_monsters_to_room(room, i-1, j-1)
chest_gen.add_chests_to_room(room, i-1, j-1) trap_gen.add_traps_to_room(room, i-1, j-1)
chest_gen.add_chests_to_room(room, i-1, j-1)
else
monster_gen.add_shopkeeper_to_room(room, i-1, j-1)
if PlayerData.shopOwnerKiller then
monster_gen.add_bodyguard_to_room(room, i-1, j-1)
monster_gen.add_bodyguard_to_room(room, i-1, j-1)
end
end
if roomCount > 3 and bossLevel and not bossAdded then if roomCount > 3 and bossLevel and not bossAdded then
bossAdded = true bossAdded = true
monster_gen.add_boss_to_room(room, i-1, j-1) monster_gen.add_boss_to_room(room, i-1, j-1)
@ -157,4 +182,8 @@ for i=1,10 do
end end
end end
end end
if lockedDoorsAdded then
add_keybearers(map)
end
-- END SCRIPT -- END SCRIPT

View File

@ -1,5 +1,6 @@
-- FUNCTIONS -- FUNCTIONS
local random = map_random local random = map_random
local layoutparser = require "layoutparser"
-- CONSTANTS -- CONSTANTS
local UP = 1 local UP = 1
@ -28,18 +29,10 @@ local wall = {
horizontal = nil horizontal = nil
} }
local pits = {
center = nil,
top = nil,
left = nil,
right = nil,
topleft = nil,
topright = nil,
}
local special = { level_exit = nil } local special = { level_exit = nil }
local floorDecor = { } local floorDecor = { }
local blockingFloorDecor = { }
local lightDecor = { } local lightDecor = { }
local function load_decor_textures() local function load_decor_textures()
@ -49,9 +42,9 @@ local function load_decor_textures()
-- Skulls -- Skulls
table.insert(floorDecor, { td0, td1, 0, 12 * 16, false, false }) table.insert(floorDecor, { td0, td1, 0, 12 * 16, false, false })
table.insert(floorDecor, { td0, td1, 32, 12 * 16, false, false }) table.insert(floorDecor, { td0, td1, 32, 12 * 16, false, false })
table.insert(floorDecor, { td0, td1, 64, 12 * 16, false, true })
table.insert(floorDecor, { td0, td1, 0, 13 * 16, false, false }) table.insert(floorDecor, { td0, td1, 0, 13 * 16, false, false })
table.insert(floorDecor, { td0, td1, 32, 13 * 16, false, false }) table.insert(floorDecor, { td0, td1, 32, 13 * 16, false, false })
table.insert(floorDecor, { td0, td1, 64, 12 * 16, false, true })
table.insert(floorDecor, { td0, td1, 64, 13 * 16, false, true }) table.insert(floorDecor, { td0, td1, 64, 13 * 16, false, true })
-- Bones -- Bones
@ -60,42 +53,6 @@ local function load_decor_textures()
table.insert(floorDecor, { td0, td1, 16, 13 * 16, false, false }) table.insert(floorDecor, { td0, td1, 16, 13 * 16, false, false })
table.insert(floorDecor, { td0, td1, 48, 13 * 16, false, false }) table.insert(floorDecor, { td0, td1, 48, 13 * 16, false, false })
-- Urns
--table.insert(floorDecor, { td0, td1, 0 * 16, 48, true, false })
--table.insert(floorDecor, { td0, td1, 1 * 16, 48, true, false })
--table.insert(floorDecor, { td0, td1, 2 * 16, 48, true, false })
--table.insert(floorDecor, { td0, td1, 3 * 16, 48, true, false })
--table.insert(floorDecor, { td0, td1, 4 * 16, 48, true, false })
--table.insert(floorDecor, { td0, td1, 5 * 16, 48, true, false })
--table.insert(floorDecor, { td0, td1, 6 * 16, 48, false, false })
--table.insert(floorDecor, { td0, td1, 7 * 16, 48, false, false })
-- Racks
--table.insert(floorDecor, { td0, td1, 0 * 16, 11 * 16, true, false })
--table.insert(floorDecor, { td0, td1, 1 * 16, 11 * 16, true, false })
--table.insert(floorDecor, { td0, td1, 2 * 16, 11 * 16, true, false })
--table.insert(floorDecor, { td0, td1, 3 * 16, 11 * 16, true, false })
--table.insert(floorDecor, { td0, td1, 4 * 16, 11 * 16, true, false })
--table.insert(floorDecor, { td0, td1, 5 * 16, 11 * 16, true, false })
--table.insert(floorDecor, { td0, td1, 6 * 16, 11 * 16, true, false })
-- Headstones
table.insert(floorDecor, { td0, td1, 0 * 16, 17 * 16, true, false })
table.insert(floorDecor, { td0, td1, 1 * 16, 17 * 16, true, false })
table.insert(floorDecor, { td0, td1, 2 * 16, 17 * 16, true, false })
table.insert(floorDecor, { td0, td1, 3 * 16, 17 * 16, true, false })
table.insert(floorDecor, { td0, td1, 4 * 16, 17 * 16, true, false })
table.insert(floorDecor, { td0, td1, 0 * 16, 18 * 16, true, false })
table.insert(floorDecor, { td0, td1, 1 * 16, 18 * 16, true, false })
table.insert(floorDecor, { td0, td1, 2 * 16, 18 * 16, true, false })
table.insert(floorDecor, { td0, td1, 3 * 16, 18 * 16, true, false })
table.insert(floorDecor, { td0, td1, 4 * 16, 18 * 16, true, false })
-- Altars
table.insert(floorDecor, { td0, td1, 0 * 16, 20 * 16, true, false })
table.insert(floorDecor, { td0, td1, 1 * 16, 20 * 16, true, false })
table.insert(floorDecor, { td0, td1, 2 * 16, 20 * 16, true, false })
-- Webs -- Webs
--table.insert(floorDecor, { td0, td1, 0 * 16, 19 * 16, false }) --table.insert(floorDecor, { td0, td1, 0 * 16, 19 * 16, false })
--table.insert(floorDecor, { td0, td1, 1 * 16, 19 * 16, false }) --table.insert(floorDecor, { td0, td1, 1 * 16, 19 * 16, false })
@ -103,9 +60,47 @@ local function load_decor_textures()
--table.insert(floorDecor, { td0, td1, 3 * 16, 19 * 16, false }) --table.insert(floorDecor, { td0, td1, 3 * 16, 19 * 16, false })
--table.insert(floorDecor, { td0, td1, 4 * 16, 19 * 16, false }) --table.insert(floorDecor, { td0, td1, 4 * 16, 19 * 16, false })
-- Urns
table.insert(floorDecor, { td0, td1, 6 * 16, 48, false, false })
table.insert(floorDecor, { td0, td1, 7 * 16, 48, false, false })
-- Urns
--table.insert(blockingFloorDecor, { td0, td1, 0 * 16, 48, true, false })
--table.insert(blockingFloorDecor, { td0, td1, 1 * 16, 48, true, false })
--table.insert(blockingFloorDecor, { td0, td1, 2 * 16, 48, true, false })
--table.insert(blockingFloorDecor, { td0, td1, 3 * 16, 48, true, false })
--table.insert(blockingFloorDecor, { td0, td1, 4 * 16, 48, true, false })
--table.insert(blockingFloorDecor, { td0, td1, 5 * 16, 48, true, false })
-- Racks
--table.insert(blockingFloorDecor, { td0, td1, 0 * 16, 11 * 16, true, false })
--table.insert(blockingFloorDecor, { td0, td1, 1 * 16, 11 * 16, true, false })
--table.insert(blockingFloorDecor, { td0, td1, 2 * 16, 11 * 16, true, false })
--table.insert(blockingFloorDecor, { td0, td1, 3 * 16, 11 * 16, true, false })
--table.insert(blockingFloorDecor, { td0, td1, 4 * 16, 11 * 16, true, false })
--table.insert(blockingFloorDecor, { td0, td1, 5 * 16, 11 * 16, true, false })
--table.insert(blockingFloorDecor, { td0, td1, 6 * 16, 11 * 16, true, false })
-- Headstones
table.insert(blockingFloorDecor, { td0, td1, 0 * 16, 17 * 16, true, false })
table.insert(blockingFloorDecor, { td0, td1, 1 * 16, 17 * 16, true, false })
table.insert(blockingFloorDecor, { td0, td1, 2 * 16, 17 * 16, true, false })
table.insert(blockingFloorDecor, { td0, td1, 3 * 16, 17 * 16, true, false })
table.insert(blockingFloorDecor, { td0, td1, 4 * 16, 17 * 16, true, false })
table.insert(blockingFloorDecor, { td0, td1, 0 * 16, 18 * 16, true, false })
table.insert(blockingFloorDecor, { td0, td1, 1 * 16, 18 * 16, true, false })
table.insert(blockingFloorDecor, { td0, td1, 2 * 16, 18 * 16, true, false })
table.insert(blockingFloorDecor, { td0, td1, 3 * 16, 18 * 16, true, false })
table.insert(blockingFloorDecor, { td0, td1, 4 * 16, 18 * 16, true, false })
-- Altars
table.insert(blockingFloorDecor, { td0, td1, 0 * 16, 20 * 16, true, false })
table.insert(blockingFloorDecor, { td0, td1, 1 * 16, 20 * 16, true, false })
table.insert(blockingFloorDecor, { td0, td1, 2 * 16, 20 * 16, true, false })
-- Statues -- Statues
--table.insert(floorDecor, { td0, td1, 3 * 16, 20 * 16, true }) --table.insert(blockingFloorDecor, { td0, td1, 3 * 16, 20 * 16, true })
--table.insert(floorDecor, { td0, td1, 4 * 16, 20 * 16, true }) --table.insert(blockingFloorDecor, { td0, td1, 4 * 16, 20 * 16, true })
lightDecor.candle0 = { td0, td1, 0, 8 * 16, false, true } lightDecor.candle0 = { td0, td1, 0, 8 * 16, false, true }
lightDecor.candle1 = { td0, td1, 16, 8 * 16, false, true } lightDecor.candle1 = { td0, td1, 16, 8 * 16, false, true }
@ -126,100 +121,71 @@ local function repack(data)
isCollider = data[5] or false, isCollider = data[5] or false,
isLightSource = data[6] or false, isLightSource = data[6] or false,
isLevelExit = data[7] or false, isLevelExit = data[7] or false,
isLethal = data[8] or false isLethal = data[8] or false,
lockType = data[9] or 0
} }
end end
local function add_random_decor_to_room(room) local function room_tile_available(room, rx, ry)
local decor_count = random(8) return not room.chests[rx][ry]
for i=1,decor_count do and not room.traps[rx][ry]
x = random(11) + 1 and not room.walls[rx][ry]
y = random(8) + 1 and not room.doors[rx][ry]
if not room.decor[x][y] then and not room.monsters[rx][ry]
room.decor[x][y] = floorDecor[random(#floorDecor)] and not room.decor[rx][ry]
end and (room.tiles[rx][ry]
end and not room.tiles[rx][ry][5]
and not room.tiles[rx][ry][7]
if random(2) == 1 then and not room.tiles[rx][ry][8])
room.decor[4][3] = lightDecor.candle2
end
if random(2) == 1 then
room.decor[11][3] = lightDecor.candle2
end
if random(2) == 1 then
room.decor[4][9] = lightDecor.candle2
end
if random(2) == 1 then
room.decor[11][9] = lightDecor.candle2
end
end end
local function add_pits_to_room(room)
if CURRENT_LEVEL < 2 or random(5) ~= 1 then local function add_random_decor_to_room(room, blockingDecor)
return false local decor_count = random(8)
if blockingDecor then
decor_count = random(4)
end end
for i=1,decor_count do
local pitdata = read_file("pitlayouts.dat") local success = false
while not success do
local cleanData = "" x = random(11) + 1
for i=1, #pitdata do y = random(8) + 1
local c = pitdata:sub(i+1, i+1) if room_tile_available(room, x, y) then
if c == "#" or c == "-" then room.decor[x][y] = floorDecor[random(#floorDecor)]
cleanData = cleanData .. c success = true
end
end end
end end
local matrix = {} if blockingDecor then
for i=0, #cleanData-1 do decor_count = random(4)
local c = cleanData:sub(i, i) for i=1,decor_count do
local col = i % 16 local success = false
local row = (i - col)/16 while not success do
local layout = 1 + (row - (row % 12))/12 x = random(11) + 1
local row = row % 12 y = random(8) + 1
if not matrix[layout] then matrix[layout] = {} end if room_tile_available(room, x, y) then
if not matrix[layout][col] then matrix[layout][col] = {} end room.decor[x][y] = blockingFloorDecor[random(#blockingFloorDecor)]
if c == "#" then success = true
matrix[layout][col][row] = true
else
matrix[layout][col][row] = false
end
end
-- Chose a random layout
matrix = matrix[random(#matrix)]
for i=2,13 do
for j=2,10 do
if matrix[i][j] then
room.decor[i][j] = nil
if not matrix[i-1][j-1] and not matrix[i+1][j-1] and matrix[i-1][j] and matrix[i+1][j] and matrix[i][j-1] then
room.tiles[i][j] = pits.innermid
elseif not matrix[i-1][j-1] and matrix[i-1][j] and matrix[i][j-1] then
room.tiles[i][j] = pits.innerleft
elseif not matrix[i+1][j-1] and matrix[i+1][j] and matrix[i][j-1] then
room.tiles[i][j] = pits.innerright
elseif not matrix[i-1][j] and not matrix[i][j-1] and not matrix[i+1][j] then
room.tiles[i][j] = pits.topcrevice
elseif not matrix[i-1][j] and not matrix[i+1][j] then
room.tiles[i][j] = pits.bottomcrevice
elseif not matrix[i-1][j] and not matrix[i][j-1] then
room.tiles[i][j] = pits.topleft
elseif not matrix[i+1][j] and not matrix[i][j-1] then
room.tiles[i][j] = pits.topright
elseif not matrix[i-1][j] then
room.tiles[i][j] = pits.left
elseif not matrix[i+1][j] then
room.tiles[i][j] = pits.right
elseif not matrix[i][j-1] then
room.tiles[i][j] = pits.top
else
room.tiles[i][j] = pits.center
end end
end end
end end
end end
return true if blockingDecor then
if random(2) == 1 and room_tile_available(room, 4, 3) then
room.decor[4][3] = lightDecor.candle2
end
if random(2) == 1 and room_tile_available(room, 11, 3) then
room.decor[11][3] = lightDecor.candle2
end
if random(2) == 1 and room_tile_available(room, 4, 9) then
room.decor[4][9] = lightDecor.candle2
end
if random(2) == 1 and room_tile_available(room, 11, 9) then
room.decor[11][9] = lightDecor.candle2
end
end
end end
local function add_tiles_to_room (room, singletile) local function add_tiles_to_room (room, singletile)
@ -245,6 +211,8 @@ local function add_tiles_to_room (room, singletile)
else else
room.tiles[i][j] = singletile and floor.single or floor.center room.tiles[i][j] = singletile and floor.single or floor.center
end end
else
room.tiles[i][j] = floor.single
end end
end end
end end
@ -254,17 +222,17 @@ local function add_walls_to_room (room)
for i=0,15 do for i=0,15 do
for j=0,11 do for j=0,11 do
if (i == 0 and j == 0) then if (i == 0 and j == 0) then
room.tiles[i][j] = wall.topleft room.walls[i][j] = wall.topleft
elseif (i == 15 and j == 0) then elseif (i == 15 and j == 0) then
room.tiles[i][j] = wall.topright room.walls[i][j] = wall.topright
elseif (i == 0 and j == 11) then elseif (i == 0 and j == 11) then
room.tiles[i][j] = wall.bottomleft room.walls[i][j] = wall.bottomleft
elseif (i == 15 and j == 11) then elseif (i == 15 and j == 11) then
room.tiles[i][j] = wall.bottomright room.walls[i][j] = wall.bottomright
elseif (i == 0 or i == 15) then elseif (i == 0 or i == 15) then
room.tiles[i][j] = wall.vertical room.walls[i][j] = wall.vertical
elseif (j == 0 or j == 11) then elseif (j == 0 or j == 11) then
room.tiles[i][j] = wall.horizontal room.walls[i][j] = wall.horizontal
end end
end end
end end
@ -272,10 +240,12 @@ end
local function build_vert_center_coridoor(room, offset) local function build_vert_center_coridoor(room, offset)
for i=0,4 do for i=0,4 do
room.tiles[6][offset+i] = wall.vertical room.walls[6][offset+i] = wall.vertical
room.tiles[7][offset+i] = floor.center room.tiles[7][offset+i] = floor.center
room.tiles[8][offset+i] = floor.center room.tiles[8][offset+i] = floor.center
room.tiles[9][offset+i] = wall.vertical room.walls[7][offset+i] = nil
room.walls[8][offset+i] = nil
room.walls[9][offset+i] = wall.vertical
end end
if random(2) == 1 then if random(2) == 1 then
room.decor[6][offset+2] = lightDecor.candle1 room.decor[6][offset+2] = lightDecor.candle1
@ -287,10 +257,12 @@ end
local function build_horiz_center_coridoor(room, offset) local function build_horiz_center_coridoor(room, offset)
for i=0,6 do for i=0,6 do
room.tiles[offset+i][4] = wall.horizontal room.walls[offset+i][4] = wall.horizontal
room.tiles[offset+i][5] = floor.center room.tiles[offset+i][5] = floor.center
room.tiles[offset+i][6] = floor.center room.tiles[offset+i][6] = floor.center
room.tiles[offset+i][7] = wall.horizontal room.walls[offset+i][5] = nil
room.walls[offset+i][6] = nil
room.walls[offset+i][7] = wall.horizontal
end end
if random(2) == 1 then if random(2) == 1 then
room.decor[offset+3][4] = lightDecor.candle1 room.decor[offset+3][4] = lightDecor.candle1
@ -303,32 +275,32 @@ end
local function build_center_corner_walls(room, exits) local function build_center_corner_walls(room, exits)
if exits.down then if exits.down then
if exits.left then if exits.left then
room.tiles[6][7] = wall.topright room.walls[6][7] = wall.topright
end end
if exits.right then if exits.right then
room.tiles[9][7] = wall.topleft room.walls[9][7] = wall.topleft
end end
else else
if not exits.left then if not exits.left then
room.tiles[6][7] = wall.bottomleft room.walls[6][7] = wall.bottomleft
end end
if not exits.right then if not exits.right then
room.tiles[9][7] = wall.bottomright room.walls[9][7] = wall.bottomright
end end
end end
if exits.up then if exits.up then
if exits.left then if exits.left then
room.tiles[6][4] = wall.bottomright room.walls[6][4] = wall.bottomright
end end
if exits.right then if exits.right then
room.tiles[9][4] = wall.bottomleft room.walls[9][4] = wall.bottomleft
end end
else else
if not exits.left then if not exits.left then
room.tiles[6][4] = wall.topleft room.walls[6][4] = wall.topleft
end end
if not exits.right then if not exits.right then
room.tiles[9][4] = wall.topright room.walls[9][4] = wall.topright
end end
end end
end end
@ -336,25 +308,33 @@ end
local function add_exits_to_room(room) local function add_exits_to_room(room)
for _,direction in ipairs(room.exits) do for _,direction in ipairs(room.exits) do
if direction == UP then if direction == UP then
room.tiles[6][0] = wall.bottomright room.walls[6][0] = wall.bottomright
room.tiles[7][0] = floor.singleleft room.tiles[7][0] = floor.singleleft
room.tiles[8][0] = floor.singleright room.tiles[8][0] = floor.singleright
room.tiles[9][0] = wall.bottomleft room.walls[7][0] = nil
room.walls[8][0] = nil
room.walls[9][0] = wall.bottomleft
elseif direction == LEFT then elseif direction == LEFT then
room.tiles[0][4] = wall.bottomright room.walls[0][4] = wall.bottomright
room.tiles[0][5] = floor.singletop room.tiles[0][5] = floor.singletop
room.tiles[0][6] = floor.singlebottom room.tiles[0][6] = floor.singlebottom
room.tiles[0][7] = wall.topright room.walls[0][5] = nil
room.walls[0][6] = nil
room.walls[0][7] = wall.topright
elseif direction == RIGHT then elseif direction == RIGHT then
room.tiles[15][4] = wall.bottomleft room.walls[15][4] = wall.bottomleft
room.tiles[15][5] = floor.singletop room.tiles[15][5] = floor.singletop
room.tiles[15][6] = floor.singlebottom room.tiles[15][6] = floor.singlebottom
room.tiles[15][7] = wall.topleft room.walls[15][5] = nil
room.walls[15][6] = nil
room.walls[15][7] = wall.topleft
elseif direction == DOWN then elseif direction == DOWN then
room.tiles[6][11] = wall.topright room.walls[6][11] = wall.topright
room.tiles[7][11] = floor.singleleft room.tiles[7][11] = floor.singleleft
room.tiles[8][11] = floor.singleright room.tiles[8][11] = floor.singleright
room.tiles[9][11] = wall.topleft room.walls[7][11] = nil
room.walls[8][11] = nil
room.walls[9][11] = wall.topleft
end end
end end
end end
@ -376,18 +356,19 @@ local function build_coridoor_room(room)
end end
-- Fill the center -- Fill the center
room.tiles[6][5] = wall.vertical
room.tiles[6][6] = wall.vertical
room.tiles[7][4] = wall.horizontal
room.tiles[7][5] = floor.center room.tiles[7][5] = floor.center
room.tiles[7][6] = floor.center room.tiles[7][6] = floor.center
room.tiles[7][7] = wall.horizontal
room.tiles[8][4] = wall.horizontal
room.tiles[8][5] = floor.center room.tiles[8][5] = floor.center
room.tiles[8][6] = floor.center room.tiles[8][6] = floor.center
room.tiles[8][7] = wall.horizontal
room.tiles[9][5] = wall.vertical room.walls[6][5] = wall.vertical
room.tiles[9][6] = wall.vertical room.walls[6][6] = wall.vertical
room.walls[7][4] = wall.horizontal
room.walls[7][7] = wall.horizontal
room.walls[8][4] = wall.horizontal
room.walls[8][7] = wall.horizontal
room.walls[9][5] = wall.vertical
room.walls[9][6] = wall.vertical
-- Build the coridoors -- Build the coridoors
if exits.down then build_vert_center_coridoor(room, 7) end if exits.down then build_vert_center_coridoor(room, 7) end
@ -398,19 +379,8 @@ local function build_coridoor_room(room)
build_center_corner_walls(room, exits) build_center_corner_walls(room, exits)
end end
local function room_tile_available(room, rx, ry)
return not room.chests[rx][ry]
and not room.traps[rx][ry]
and not room.monsters[rx][ry]
and not room.decor[rx][ry]
and (room.tiles[rx][ry]
and not room.tiles[rx][ry][5]
and not room.tiles[rx][ry][7]
and not room.tiles[rx][ry][8])
end
local function add_level_exit(room) local function add_level_exit(room)
success = false local success = false
while not success do while not success do
x = random(14) x = random(14)
y = random(10) y = random(10)
@ -421,14 +391,39 @@ local function add_level_exit(room)
end end
end end
local function build_normal_room(room) local function build_shop_room(room, roomx, roomy)
local crumbling = (CURRENT_LEVEL > 3 or QUICK_MODE) and random(8) == 1 add_tiles_to_room(room, false)
add_tiles_to_room(room, crumbling)
add_random_decor_to_room(room)
add_walls_to_room(room) add_walls_to_room(room)
add_exits_to_room(room) add_exits_to_room(room)
local pitsAdded = crumbling or add_pits_to_room(room) layoutparser.add_shop_layout(room, roomx, roomy)
return room
end
local function build_locked_room(room, roomx, roomy)
add_tiles_to_room(room, false)
add_walls_to_room(room)
add_exits_to_room(room)
layoutparser.add_locked_room_layout(room, roomx, roomy)
return room
end
local function build_normal_room(room)
local crumbling = (CURRENT_LEVEL > 3 or QUICK_MODE) and random(8) == 1
local pitsAdded = false;
local interiorWallsAdded = false
add_tiles_to_room(room, crumbling)
add_walls_to_room(room)
add_exits_to_room(room)
if not crumbling then
pitsAdded = layoutparser.add_pits_to_room(room)
end
if not pitsAdded and (not crumbling or CURRENT_LEVEL > 3) then
interiorWallsAdded = layoutparser.add_walls_to_room(room)
end
add_random_decor_to_room(room, not interiorWallsAdded)
if room.goal then if room.goal then
add_level_exit(room) add_level_exit(room)
@ -440,7 +435,7 @@ local function build_normal_room(room)
elseif (CURRENT_LEVEL > 3 or QUICK_MODE) and random(8) == 1 then elseif (CURRENT_LEVEL > 3 or QUICK_MODE) and random(8) == 1 then
room.modifier.type = "FIRE" room.modifier.type = "FIRE"
room.modifier.arg = "" room.modifier.arg = ""
elseif ((not pitsAdded and (CURRENT_LEVEL > 1 or QUICK_MODE)) or CURRENT_LEVEL > 3) and random(8) == 1 then elseif ((not pitsAdded and not crumbling and (CURRENT_LEVEL > 1 or QUICK_MODE)) or CURRENT_LEVEL > 3) and random(8) == 1 then
directions = { "LEFT", "RIGHT", "UP", "DOWN" } directions = { "LEFT", "RIGHT", "UP", "DOWN" }
room.modifier.type = "WINDY" room.modifier.type = "WINDY"
room.modifier.arg = directions[random(#directions)] room.modifier.arg = directions[random(#directions)]
@ -478,6 +473,8 @@ function module.create_empty_room()
path_dir = 0, path_dir = 0,
type = "room", type = "room",
tiles = {}, tiles = {},
walls = {},
doors = {},
decor = {}, decor = {},
modifier = { modifier = {
type = nil, type = nil,
@ -489,12 +486,16 @@ function module.create_empty_room()
} }
for i=0,15 do for i=0,15 do
room.tiles[i] = {} room.tiles[i] = {}
room.walls[i] = {}
room.doors[i] = {}
room.decor[i] = {} room.decor[i] = {}
room.monsters[i] = {} room.monsters[i] = {}
room.traps[i] = {} room.traps[i] = {}
room.chests[i] = {} room.chests[i] = {}
for j=0,11 do for j=0,11 do
room.tiles[i][j] = nil room.tiles[i][j] = nil
room.walls[i][j] = nil
room.doors[i][j] = nil
room.decor[i][j] = nil room.decor[i][j] = nil
room.monsters[i][j] = nil room.monsters[i][j] = nil
room.traps[i][j] = nil room.traps[i][j] = nil
@ -504,11 +505,16 @@ function module.create_empty_room()
return room return room
end end
function module.build_room(room) function module.build_room(room, roomx, roomy)
if room.type == "coridoor" then if room.type == "coridoor" then
build_coridoor_room(room) build_coridoor_room(room)
elseif room.type == "shop" then
build_shop_room(room, roomx, roomy)
elseif room.type == "locked" then
build_locked_room(room, roomx, roomy)
else else
build_normal_room(room) build_normal_room(room)
--build_locked_room(room, roomx, roomy)
end end
end end
@ -518,6 +524,12 @@ function module.load_room(map, room)
if room.tiles[i][j] then if room.tiles[i][j] then
add_tile(map, i, j, repack(room.tiles[i][j])) add_tile(map, i, j, repack(room.tiles[i][j]))
end end
if room.walls[i][j] then
add_wall(map, i, j, repack(room.walls[i][j]))
end
if room.doors[i][j] then
add_door(map, i, j, repack(room.doors[i][j]))
end
if room.decor[i][j] then if room.decor[i][j] then
add_decoration(map, i, j, repack(room.decor[i][j])) add_decoration(map, i, j, repack(room.decor[i][j]))
end end
@ -531,15 +543,16 @@ end
function module.load_textures(map) function module.load_textures(map)
t_floor = add_texture(map, "Objects/Floor.png") t_floor = add_texture(map, "Objects/Floor.png")
t_wall = add_texture(map, "Objects/Wall.png") t_wall = add_texture(map, "Objects/Wall.png")
t_pit0 = add_texture(map, "Objects/Pit0.png")
t_pit1 = add_texture(map, "Objects/Pit1.png")
local seed = get_random_seed(CURRENT_LEVEL); local seed = get_random_seed(CURRENT_LEVEL);
info("Map room random seed: " .. seed) info("Map room random seed: " .. seed)
map_randomseed(seed) map_randomseed(seed)
local xo = (random(3) - 1) * 112 local xo = (random(3) - 1) * 112
local yo = (random(8)) * 48 local yo = (random(8)) * 48
layoutparser.load_textures(map, xo, yo)
floor.center = { t_floor, -1, xo + 16, yo + 16, false } floor.center = { t_floor, -1, xo + 16, yo + 16, false }
floor.top = { t_floor, -1, xo + 16, yo + 0, false } floor.top = { t_floor, -1, xo + 16, yo + 0, false }
floor.bottom = { t_floor, -1, xo + 16, yo + 32, false } floor.bottom = { t_floor, -1, xo + 16, yo + 32, false }
@ -555,19 +568,6 @@ function module.load_textures(map)
floor.singleright = { t_floor, -1, xo + 96, yo + 16, false } floor.singleright = { t_floor, -1, xo + 96, yo + 16, false }
floor.single = { t_floor, -1, xo + 80, yo + 0, false } floor.single = { t_floor, -1, xo + 80, yo + 0, false }
local pit_yo = (random(5) + random(3)) * (16 * 2)
pits.topleft = { t_pit0, t_pit1, 0, pit_yo, false, false, false, true }
pits.top = { t_pit0, t_pit1, 16, pit_yo, false, false, false, true }
pits.topright = { t_pit0, t_pit1, 32, pit_yo, false, false, false, true }
pits.left = { t_pit0, t_pit1, 0, pit_yo + 16, false, false, false, true }
pits.center = { t_pit0, t_pit1, 16, pit_yo + 16, false, false, false, true }
pits.right = { t_pit0, t_pit1, 32, pit_yo + 16, false, false, false, true }
pits.innerleft = { t_pit0, t_pit1, 80, pit_yo, false, false, false, true }
pits.innermid = { t_pit0, t_pit1, 96, pit_yo, false, false, false, true }
pits.innerright = { t_pit0, t_pit1, 112, pit_yo, false, false, false, true }
pits.topcrevice = { t_pit0, t_pit1, 64, pit_yo, false, false, false, true }
pits.bottomcrevice = { t_pit0, t_pit1, 64, pit_yo + 16, false, false, false, true }
wall.topleft = { t_wall, -1, xo + 0, yo + 0, true } wall.topleft = { t_wall, -1, xo + 0, yo + 0, true }
wall.topright = { t_wall, -1, xo + 32, yo + 0, true } wall.topright = { t_wall, -1, xo + 32, yo + 0, true }
wall.bottomleft = { t_wall, -1, xo + 0, yo + 32, true } wall.bottomleft = { t_wall, -1, xo + 0, yo + 32, true }

View File

@ -9,7 +9,7 @@ room_builder.load_textures(map)
set_current_room(map, 0, 0) set_current_room(map, 0, 0)
local room = room_builder.create_empty_room() local room = room_builder.create_empty_room()
room_builder.build_room(room) room_builder.build_room(room, 0, 0)
room_builder.add_full_lighting(room) room_builder.add_full_lighting(room)
monster_gen.add_monsters_to_room(room, 0, 0) monster_gen.add_monsters_to_room(room, 0, 0)
trap_gen.add_traps_to_room(room, 0, 0) trap_gen.add_traps_to_room(room, 0, 0)

View File

@ -106,7 +106,14 @@ local stats = {
atk = 0, atk = 0,
def = 0, def = 0,
speed = 1 speed = 1
} },
shopkeeper = {
hp = 200,
dmg = 10,
atk = 2,
def = 0,
speed = 1
},
} }
local function concat(table1, table2) local function concat(table1, table2)
@ -162,6 +169,18 @@ for i=1,#misc do
misc[i] = concat({ texturePaths.misc0, texturePaths.misc1 }, misc[i]) misc[i] = concat({ texturePaths.misc0, texturePaths.misc1 }, misc[i])
end end
local fairies = {
{ stats.misc, 0, 48, "A Fairy", behaviour.pacifist },
{ stats.misc, 16, 48, "A Fairy", behaviour.pacifist },
{ stats.misc, 32, 48, "A Fairy", behaviour.pacifist },
{ stats.misc, 48, 48, "A Fairy", behaviour.pacifist },
{ stats.misc, 64, 48, "A Fairy", behaviour.pacifist },
{ stats.misc, 80, 48, "A Fairy", behaviour.pacifist },
}
for i=1,#fairies do
fairies[i] = concat({ texturePaths.humanoid0, texturePaths.humanoid1 }, fairies[i])
end
local reanimated = { local reanimated = {
{ stats.undead, 0, 32, "A Skeleton", behaviour.normal }, { stats.undead, 0, 32, "A Skeleton", behaviour.normal },
{ stats.undead, 48, 32, "A Black Skeleton", behaviour.normal }, { stats.undead, 48, 32, "A Black Skeleton", behaviour.normal },
@ -270,6 +289,31 @@ for i=1,#eastereggs do
eastereggs[i] = concat({ texturePaths.player0, texturePaths.player1 }, eastereggs[i]) eastereggs[i] = concat({ texturePaths.player0, texturePaths.player1 }, eastereggs[i])
end end
local shopkeeperBehaviour = behaviour.passive
if PlayerData.shopOwnerKiller then
shopkeeperBehaviour = behaviour.hostile
end
local shopkeeper = {
texturePaths.humanoid0,
texturePaths.humanoid1,
stats.shopkeeper,
16,
12*16,
"The Trader",
shopkeeperBehaviour
}
local bodyguard = {
texturePaths.humanoid0,
texturePaths.humanoid1,
stats.orc,
32,
12*16,
"A Bodyguard",
behaviour.hostile
}
-- Add Platino -- Add Platino
table.insert(eastereggs, { table.insert(eastereggs, {
texturePaths.reptile0, texturePaths.reptile0,
@ -327,6 +371,7 @@ if(CURRENT_LEVEL > 0) then
end end
local addSpecialInLevel = random(100) == 1 local addSpecialInLevel = random(100) == 1
local addFairyToLevel = random(3) == 1;
local function add_monster_to_tile(room, roomx, roomy, rx, ry, monster) local function add_monster_to_tile(room, roomx, roomy, rx, ry, monster)
local x = (roomx * 512) + rx * 32 local x = (roomx * 512) + rx * 32
@ -339,7 +384,8 @@ local function add_monster_to_tile(room, roomx, roomy, rx, ry, monster)
end end
function module.add_monsters_to_room(room, roomx, roomy) function module.add_monsters_to_room(room, roomx, roomy)
local addSpecial = addSpecialInLevel and random(5) == 1 local addSpecial = addSpecialInLevel and random(2) == 1
local addFairy = random(4) == 1
local count = random(3) local count = random(3)
if (CURRENT_LEVEL > 3) then if (CURRENT_LEVEL > 3) then
count = random(4) count = random(4)
@ -354,6 +400,9 @@ function module.add_monsters_to_room(room, roomx, roomy)
addSpecialInLevel = false addSpecialInLevel = false
addSpecial = false addSpecial = false
add_monster_to_tile(room, roomx, roomy, rx, ry, eastereggs[random(#eastereggs)]) add_monster_to_tile(room, roomx, roomy, rx, ry, eastereggs[random(#eastereggs)])
elseif addFairyToLevel and addFairy then
addFairyToLevel = false
add_monster_to_tile(room, roomx, roomy, rx, ry, fairies[random(#fairies)])
else else
add_monster_to_tile(room, roomx, roomy, rx, ry, enemies[random(#enemies)]) add_monster_to_tile(room, roomx, roomy, rx, ry, enemies[random(#enemies)])
end end
@ -384,6 +433,42 @@ function module.add_boss_to_room(room, roomx, roomy)
end end
end end
function module.add_shopkeeper_to_room(room, roomx, roomy)
local success = false
while not success do
local rx = random(13) + 1
local ry = random(9) + 1
if room_builder.is_tile_avilable(room, rx, ry) then
local x = (roomx * 512) + rx * 32
local y = (roomy * 384) + ry * 32
room.monsters[rx][ry] = {
x,
y,
shopkeeper
}
success = true
end
end
end
function module.add_bodyguard_to_room(room, roomx, roomy)
local success = false
while not success do
local rx = random(13) + 1
local ry = random(9) + 1
if room_builder.is_tile_avilable(room, rx, ry) then
local x = (roomx * 512) + rx * 32
local y = (roomy * 384) + ry * 32
room.monsters[rx][ry] = {
x,
y,
bodyguard
}
success = true
end
end
end
function module.load_monsters(map, monsters) function module.load_monsters(map, monsters)
for i=0,15 do for i=0,15 do
for j=0,11 do for j=0,11 do

View File

@ -1,13 +1,13 @@
---------------- ----------------
---------------- ----------------
--##--------##-- --pp--------pp--
--##--####--##-- --pp--pppp--pp--
-------##------- -------pp-------
-------##------- -------pp-------
-------##------- -------pp-------
-------##------- -------pp-------
--##--####--##-- --pp--pppp--pp--
--##--------##-- --pp--------pp--
---------------- ----------------
---------------- ----------------
@ -15,11 +15,11 @@
---------------- ----------------
---------------- ----------------
---------------- ----------------
-----##-##------ -----pp-pp------
-----##-##------ -----pp-pp------
---------------- ----------------
-----##-##------ -----pp-pp------
-----##-##------ -----pp-pp------
---------------- ----------------
---------------- ----------------
---------------- ----------------
@ -27,12 +27,12 @@
---------------- ----------------
---------------- ----------------
---------------- ----------------
--############-- --pppppppppppp--
---------------- ----------------
---------------- ----------------
---------------- ----------------
---------------- ----------------
--############-- --pppppppppppp--
---------------- ----------------
---------------- ----------------
---------------- ----------------
@ -40,194 +40,194 @@
---------------- ----------------
---------------- ----------------
---------------- ----------------
--############-- --pppppppppppp--
---------------- ----------------
--############-- --pppppppppppp--
--############-- --pppppppppppp--
---------------- ----------------
--############-- --pppppppppppp--
---------------- ----------------
---------------- ----------------
---------------- ----------------
---------------- ----------------
---------------- ----------------
--############-- --pppppppppppp--
--############-- --pppppppppppp--
---------------- ----------------
---------------- ----------------
---------------- ----------------
---------------- ----------------
--############-- --pppppppppppp--
--############-- --pppppppppppp--
---------------- ----------------
---------------- ----------------
---------------- ----------------
---------------- ----------------
-------#-------- -------p--------
------###------- ------ppp-------
-------#-------- -------p--------
------###------- ------ppp-------
------###------- ------ppp-------
-------#-------- -------p--------
------###------- ------ppp-------
-------#-------- -------p--------
---------------- ----------------
---------------- ----------------
---------------- ----------------
---------------- ----------------
--############-- --pppppppppppp--
--#----------#-- --p----------p--
--#----------#-- --p----------p--
---------------- ----------------
---------------- ----------------
--#----------#-- --p----------p--
--#----------#-- --p----------p--
--############-- --pppppppppppp--
---------------- ----------------
---------------- ----------------
---------------- ----------------
---------------- ----------------
--############-- --pppppppppppp--
--#----------#-- --p----------p--
--#-########-#-- --p-pppppppp-p--
----#----------- ----p-----------
-----------#---- -----------p----
--#-########-#-- --p-pppppppp-p--
--#----------#-- --p----------p--
--############-- --pppppppppppp--
---------------- ----------------
---------------- ----------------
---------------- ----------------
---------------- ----------------
--############-- --pppppppppppp--
--#----------#-- --p----------p--
--#-###-####-#-- --p-ppp-pppp-p--
----###---##---- ----ppp---pp----
----#####-##---- ----ppppp-pp----
--#-#####-##-#-- --p-ppppp-pp-p--
--#----------#-- --p----------p--
--############-- --pppppppppppp--
---------------- ----------------
---------------- ----------------
---------------- ----------------
---------------- ----------------
--##--------##-- --pp--------pp--
--##--------##-- --pp--------pp--
---------------- ----------------
-------##------- -------pp-------
-------##------- -------pp-------
--#----------#-- --p----------p--
--#----------#-- --p----------p--
--############-- --pppppppppppp--
---------------- ----------------
---------------- ----------------
---------------- ----------------
---------------- ----------------
--##--------##-- --pp--------pp--
--##--------##-- --pp--------pp--
---------------- ----------------
-------##------- -------pp-------
-------##------- -------pp-------
---------------- ----------------
--##--------##-- --pp--------pp--
--##--------##-- --pp--------pp--
---------------- ----------------
---------------- ----------------
---------------- ----------------
---------------- ----------------
--##--------##-- --pp--------pp--
--##--------##-- --pp--------pp--
---------------- ----------------
---------------- ----------------
---------------- ----------------
---------------- ----------------
--##--------##-- --pp--------pp--
--##--------##-- --pp--------pp--
---------------- ----------------
---------------- ----------------
---------------- ----------------
---------------- ----------------
--##---##---##-- --pp---pp---pp--
--##---##---##-- --pp---pp---pp--
---------------- ----------------
---------------- ----------------
---------------- ----------------
---------------- ----------------
--##---##---##-- --pp---pp---pp--
--##---##---##-- --pp---pp---pp--
---------------- ----------------
---------------- ----------------
---------------- ----------------
---------------- ----------------
--##---##---##-- --pp---pp---pp--
--##---##---##-- --pp---pp---pp--
---------------- ----------------
--##---##---##-- --pp---pp---pp--
--##---##---##-- --pp---pp---pp--
---------------- ----------------
--##---##---##-- --pp---pp---pp--
--##---##---##-- --pp---pp---pp--
---------------- ----------------
---------------- ----------------
---------------- ----------------
---------------- ----------------
---#----#----#-- ---p----p----p--
--##---##---##-- --pp---pp---pp--
---------------- ----------------
---#----#----#-- ---p----p----p--
--##---##---##-- --pp---pp---pp--
---------------- ----------------
---#----#----#-- ---p----p----p--
--##---##---##-- --pp---pp---pp--
---------------- ----------------
---------------- ----------------
---------------- ----------------
---------------- ----------------
--##---##---##-- --pp---pp---pp--
--#----------#-- --p----------p--
---------------- ----------------
--#----##----#-- --p----pp----p--
--#----##----#-- --p----pp----p--
---------------- ----------------
--#----------#-- --p----------p--
--##---##---##-- --pp---pp---pp--
---------------- ----------------
---------------- ----------------
---------------- ----------------
---------------- ----------------
--##---##---##-- --pp---pp---pp--
--#----------#-- --p----------p--
---------------- ----------------
--#----------#-- --p----------p--
--#----------#-- --p----------p--
---------------- ----------------
--#----------#-- --p----------p--
--##---##---##-- --pp---pp---pp--
---------------- ----------------
---------------- ----------------
---------------- ----------------
---------------- ----------------
--##---##---##-- --pp---pp---pp--
--#----------#-- --p----------p--
-----##--##----- -----pp--pp-----
--#--#----#--#-- --p--p----p--p--
--#--#----#--#-- --p--p----p--p--
-----##--##----- -----pp--pp-----
--#----------#-- --p----------p--
--##---##---##-- --pp---pp---pp--
---------------- ----------------
---------------- ----------------

64
data/shoplayouts.dat Normal file
View File

@ -0,0 +1,64 @@
++++++++++++++++
++++++++++++++++
++##ffffffff##++
++#"--------"#++
++f---a--a---f++
++-----------f++
++-----------f++
++f---a--a---f++
++#"--------"#++
++##ffffffff##++
++++++++++++++++
++++++++++++++++
++++++++++++++++
++++++++++++++++
++ffff----ffff++
++fppf-a--fppf++
++ffff--l-ffff++
++------a-----++
++------------++
++ffff-a--ffff++
++fppf-l--fppf++
++ffff--a-ffff++
++++++++++++++++
++++++++++++++++
++++++++++++++++
++++++++++++++++
++###########"++
++#----------#++
++#-####"###-#++
++--#aa--aa#-#++
++--#--------#++
++#-####"###-#++
++#----------#++
++"###########++
++++++++++++++++
++++++++++++++++
++++++++++++++++
++++++++++++++++
++--pppppppp--++
++--pffffffp--++
++--pfl--afp--++
++-------afp--++
++-------afp--++
++--pfl--afp--++
++--pffffffp--++
++--pppppppp--++
++++++++++++++++
++++++++++++++++
++++++++++++++++
++++++++++++++++
++pppppppppppp++
++ppffffffffpp++
++fffa----afff++
++------l-----++
++-----l------++
++fffa----afff++
++ppffffffffpp++
++pppppppppppp++
++++++++++++++++
++++++++++++++++

90
data/walllayouts.dat Normal file
View File

@ -0,0 +1,90 @@
++++++++++++++++
++++++++++++++++
++/#--------#/++
++##--------##++
++------------++
++------------++
++------------++
++------------++
++##--------##++
++/#--------#/++
++++++++++++++++
++++++++++++++++
++++++++++++++++
++++++++++++++++
++#--#-#-#-#-#++
++---c--------++
++/-#-#-#-#--/++
++-------c----++
++/--#-#-#-#-/++
++------------++
++--#-#-#-#---++
++#-########-#++
++++++++++++++++
++++++++++++++++
++++++++++++++++
++++++++++++++++
++############++
++#----------#++
++#-/#######-#++
++--#------#-#++
++--d--cc--d-#++
++#-/#######-#++
++#----------#++
++############++
++++++++++++++++
++++++++++++++++
++++++++++++++++
++++++++++++++++
++/#--------#/++
++##-ffffdf-##++
++---f--c-f---++
++---f----f---++
++---f-c--f---++
++---f----f---++
++##-fdffff-##++
++/#--------#/++
++++++++++++++++
++++++++++++++++
++++++++++++++++
++++++++++++++++
++/#ffffffff#/++
++#----------#++
++---######---++
++---d----#---++
++---#-cc-d---++
++---######---++
++#----------#++
++/#ffffffff#/++
++++++++++++++++
++++++++++++++++
++++++++++++++++
++++++++++++++++
++/##########/++
++d-----d----d++
++#c----#---c#++
++############++
++#----#-----#++
++#-c--d--c--#++
++d----#-----d++
++/##########/++
++++++++++++++++
++++++++++++++++
++++++++++++++++
++++++++++++++++
++/####-#####/++
++#--c#-#c---#++
++d---#-#----d++
++#####-######++
++------------++
++#####-######++
++d--c#-#c---d++
++/####-#####/++
++++++++++++++++
++++++++++++++++

View File

@ -9,7 +9,7 @@
# compile, using preprocessor checks for platform-specific bits instead of # compile, using preprocessor checks for platform-specific bits instead of
# testing in here. # testing in here.
cmake_minimum_required(VERSION 2.8.4) cmake_minimum_required(VERSION 3.6)
project(PhysicsFS) project(PhysicsFS)
set(PHYSFS_VERSION 3.0.1) 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) if (t->dead)
return; return;
if (!t->dead && !timer_started(t->timer)) if (!timer_started(t->timer))
timer_start(t->timer); timer_start(t->timer);
if (timer_get_ticks(t->timer) < 500) { if (timer_get_ticks(t->timer) < 500) {

View File

@ -32,7 +32,7 @@ actiontextbuilder_init(SDL_Renderer *renderer)
} }
void 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); assert (gRenderer != NULL);
Sprite *sprite = sprite_create(); Sprite *sprite = sprite_create();

View File

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

View File

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

View File

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

View File

@ -22,6 +22,7 @@
#include "particle_engine.h" #include "particle_engine.h"
#include "player.h" #include "player.h"
#include "random.h" #include "random.h"
#include "sprite_util.h"
static void static void
artifact_set_effect(Artifact *a, MagicalEffect effect) artifact_set_effect(Artifact *a, MagicalEffect effect)
@ -64,6 +65,21 @@ artifact_set_effect(Artifact *a, MagicalEffect effect)
a->info.name = "Shadow cloak"; a->info.name = "Shadow cloak";
a->info.desc = "You feel more in phase with the world"; a->info.desc = "You feel more in phase with the world";
break; 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: default:
break; break;
} }
@ -77,7 +93,10 @@ static int WarriorArtifacts[] = {
PUSH_BACK, // 4 PUSH_BACK, // 4
FEAR_INDUCING, // 5 FEAR_INDUCING, // 5
INCREASED_STUN, // 6 INCREASED_STUN, // 6
CHARGE_THROUGH // 7 DAGGER_BOUNCE, // 7
EXPLOSIVE_KILLS, // 8
VOLATILE_DAGGERS, // 9
CHARGE_THROUGH // 10
}; };
static int RogueArtifacts[] = { static int RogueArtifacts[] = {
@ -88,31 +107,109 @@ static int RogueArtifacts[] = {
PUSH_BACK, // 4 PUSH_BACK, // 4
FEAR_INDUCING, // 5 FEAR_INDUCING, // 5
INCREASED_STUN, // 6 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
add_level_sprite(Artifact *a)
{
Sprite *sprite = sprite_util_create_text_sprite("GUI/SDS_8x8.ttf",
8,
C_BLUE,
C_BLACK,
"%u",
a->level);
sprite->pos = a->sprite->pos;
sprite->offset = POS(32 - sprite->dim.width, 32 - sprite->dim.height);
a->levelSprite = sprite;
}
Artifact * Artifact *
artifact_create_random(Player *p, Uint8 level) artifact_create_random(Player *p, Uint8 level)
{ {
int option = -1; int option = get_random(9);
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 * artifactPool = NULL; int * artifactPool = NULL;
if (p->class == ROGUE) if (p->class == ROGUE)
artifactPool = RogueArtifacts; artifactPool = RogueArtifacts;
else if (p->class == MAGE)
artifactPool = MageArtifacts;
else if (p->class == PALADIN)
artifactPool = PaladinArtifacts;
else if (p->class == ENGINEER)
artifactPool = EngineerArtifacts;
else else
artifactPool = WarriorArtifacts; artifactPool = WarriorArtifacts;
Artifact *a = artifact_create(artifactPool[option]); Artifact *a = artifact_create(artifactPool[option]);
a->level = level; a->level = level;
if (level > 1)
add_level_sprite(a);
return a; return a;
} }
void
artifact_add_price(Artifact *a, unsigned int price)
{
Sprite *sprite = sprite_util_create_text_sprite("GUI/SDS_8x8.ttf",
8,
C_YELLOW,
C_BLACK,
"$%u",
price);
sprite->pos = a->sprite->pos;
a->price = price;
a->priceSprite = sprite;
}
Sprite * Sprite *
artifact_sprite_for(MagicalEffect effect) artifact_sprite_for(MagicalEffect effect)
{ {
@ -164,6 +261,26 @@ artifact_sprite_for(MagicalEffect effect)
sprite_set_texture(sprite, t, 0); sprite_set_texture(sprite, t, 0);
sprite->clip = CLIP16(1*16, 5*16); sprite->clip = CLIP16(1*16, 5*16);
break; 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: default:
break; break;
} }
@ -175,15 +292,18 @@ artifact_create(MagicalEffect effect)
{ {
Artifact *a = ec_malloc(sizeof(Artifact)); Artifact *a = ec_malloc(sizeof(Artifact));
a->sprite = artifact_sprite_for(effect); a->sprite = artifact_sprite_for(effect);
a->priceSprite = NULL;
a->levelSprite = NULL;
a->sprite->dim = GAME_DIMENSION; a->sprite->dim = GAME_DIMENSION;
a->collected = false; a->collected = false;
a->level = 1; a->level = 1;
a->price = 0;
artifact_set_effect(a, effect); artifact_set_effect(a, effect);
return a; return a;
} }
Artifact * Artifact *
artifact_copy(Artifact *a) artifact_copy(const Artifact *a)
{ {
Artifact *new = ec_malloc(sizeof(Artifact)); Artifact *new = ec_malloc(sizeof(Artifact));
*new = *a; *new = *a;
@ -197,12 +317,24 @@ artifact_render(Artifact *a, Camera *cam)
Position pos = a->sprite->pos; Position pos = a->sprite->pos;
pos.x += 4; pos.x += 4;
pos.y += 4; pos.y += 4;
particle_engine_sparkle(pos, (Dimension) { 24, 24 }, C_PURPLE, false); particle_engine_sparkle(pos, DIM(24, 24), C_PURPLE, false);
if (a->priceSprite) {
a->priceSprite->pos = a->sprite->pos;
sprite_render(a->priceSprite, cam);
}
if (a->levelSprite) {
a->levelSprite->pos = a->sprite->pos;
sprite_render(a->levelSprite, cam);
}
} }
void void
artifact_destroy(Artifact *a) artifact_destroy(Artifact *a)
{ {
sprite_destroy(a->sprite); sprite_destroy(a->sprite);
if (a->priceSprite)
sprite_destroy(a->priceSprite);
if (a->levelSprite)
sprite_destroy(a->levelSprite);
free(a); free(a);
} }

View File

@ -30,6 +30,10 @@ typedef enum MagicalEffect {
INCREASED_STUN, INCREASED_STUN,
CHARGE_THROUGH, CHARGE_THROUGH,
PHASE_IMPROVEMENT, PHASE_IMPROVEMENT,
SKILL_RADIUS,
DAGGER_BOUNCE,
EXPLOSIVE_KILLS,
VOLATILE_DAGGERS,
LAST_ARTIFACT_EFFECT // Sentinel LAST_ARTIFACT_EFFECT // Sentinel
} MagicalEffect; } MagicalEffect;
@ -40,10 +44,13 @@ typedef struct ArtifactInfo {
typedef struct Artifact { typedef struct Artifact {
Sprite *sprite; Sprite *sprite;
Sprite *priceSprite;
Sprite *levelSprite;
MagicalEffect effect; MagicalEffect effect;
ArtifactInfo info; ArtifactInfo info;
bool collected; bool collected;
int level; int level;
unsigned int price;
} Artifact; } Artifact;
Sprite * Sprite *
@ -52,11 +59,14 @@ artifact_sprite_for(MagicalEffect);
Artifact * Artifact *
artifact_create_random(Player*, Uint8 level); artifact_create_random(Player*, Uint8 level);
void
artifact_add_price(Artifact*, unsigned int price);
Artifact * Artifact *
artifact_create(MagicalEffect); artifact_create(MagicalEffect);
Artifact * Artifact *
artifact_copy(Artifact*); artifact_copy(const Artifact*);
void void
artifact_render(Artifact*, Camera*); 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

@ -27,4 +27,9 @@
#define PATCH_VERSION @breakhack_PATCH_VERSION@ #define PATCH_VERSION @breakhack_PATCH_VERSION@
#define RELEASE_TYPE "@breakhack_RELEASE_TYPE@" #define RELEASE_TYPE "@breakhack_RELEASE_TYPE@"
/* Checksums */
#define SO_LIBSTEAM_CHECKSUM @breakhack_STEAMAPI_SO_CHECKSUM@
#define DLL_LIBSTEAM_CHECKSUM @breakhack_STEAMAPI_DLL_CHECKSUM@
#endif // CONFIG_H_ #endif // CONFIG_H_

View File

@ -67,9 +67,11 @@
#define C_RED (SDL_Color) { 255, 0, 0, 255 } #define C_RED (SDL_Color) { 255, 0, 0, 255 }
#define C_GREEN (SDL_Color) { 0, 255, 0, 255 } #define C_GREEN (SDL_Color) { 0, 255, 0, 255 }
#define C_BLUE (SDL_Color) { 60, 134, 252, 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_YELLOW (SDL_Color) { 255, 255, 0, 255 }
#define C_BLACK (SDL_Color) { 0, 0, 0, 255 } #define C_BLACK (SDL_Color) { 0, 0, 0, 255 }
#define C_PURPLE (SDL_Color) { 137, 16, 229, 255 } #define C_PURPLE (SDL_Color) { 137, 16, 229, 255 }
#define C_GREY (SDL_Color) { 89, 89, 89, 255 }
// MSVC seems to have min/max defined. // MSVC seems to have min/max defined.
// Haven't looked into it further. // Haven't looked into it further.

25
src/doorlocktype.h Normal file
View File

@ -0,0 +1,25 @@
/*
* 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
typedef enum DoorLockType {
LOCK_NONE,
LOCK_SILVER,
LOCK_GOLD
} DoorLockType;

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);

48
src/event.c Normal file
View File

@ -0,0 +1,48 @@
/*
* 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 "event.h"
#include "util.h"
#include "linkedlist.h"
static LinkedList *callbacks = NULL;
void
event_register_listener(EventCallback cb)
{
// Cast a pointer to a pointer to avoid -wpedantic iso warning
linkedlist_append(&callbacks, *(void**)(&cb));
}
void
event_clear_listeners(void)
{
while (callbacks)
linkedlist_pop(&callbacks);
}
void
event_trigger(Event *event)
{
LinkedList *cbs = callbacks;
while (cbs) {
// Reverse the cast from the register (-pedantic ISO warning)
(*(EventCallback*)(&cbs->data))(event);
cbs = cbs->next;
}
}

47
src/event.h Normal file
View File

@ -0,0 +1,47 @@
/*
* 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 "player.h"
typedef enum EventType {
MONSTER_KILLED_EVENT
} EventType;
typedef struct MonsterKilledEvent {
EventType *type;
Player *player;
Monster *monster;
} MonsterKilledEvent;
typedef union Event {
EventType type;
MonsterKilledEvent monsterKilled;
} Event;
typedef void (*EventCallback)(Event*);
void
event_register_listener(EventCallback cb);
void
event_clear_listeners(void);
void
event_trigger(Event *event);

View File

@ -112,21 +112,21 @@ init_sprites(Gui *gui, Camera *cam)
linkedlist_append(&gui->sprites, create_xp_sprite( linkedlist_append(&gui->sprites, create_xp_sprite(
t, t,
(SDL_Rect) { 6 * 16, 0, 16, 16 }, (SDL_Rect) { 6 * 16, 0, 16, 16 },
(Position) { 16, POS_Y_XPBAR } POS(16, POS_Y_XPBAR)
)); ));
// Right end // Right end
linkedlist_append(&gui->sprites, create_xp_sprite( linkedlist_append(&gui->sprites, create_xp_sprite(
t, t,
(SDL_Rect) { 8 * 16, 0, 16, 16 }, (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) { for (i = 1; i < 7; ++i) {
linkedlist_append(&gui->sprites, create_xp_sprite( linkedlist_append(&gui->sprites, create_xp_sprite(
t, t,
(SDL_Rect) { 7 * 16, 0, 16, 16 }, (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( linkedlist_append(&gui->xp_bar, create_xp_sprite(
t, t,
(SDL_Rect) { 6 * 16, 4 * 16, 16, 16 }, (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; s->fixed = true;
sprite_set_texture(s, t, 0); sprite_set_texture(s, t, 0);
s->clip = CLIP16(0, 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); linkedlist_append(&gui->sprites, s);
t = texturecache_add("Items/Money.png"); t = texturecache_add("Items/Money.png");
@ -152,7 +152,7 @@ init_sprites(Gui *gui, Camera *cam)
s->fixed = true; s->fixed = true;
sprite_set_texture(s, t, 0); sprite_set_texture(s, t, 0);
s->clip = CLIP16(16, 16); 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); linkedlist_append(&gui->sprites, s);
t = texturecache_add("Items/ShortWep.png"); t = texturecache_add("Items/ShortWep.png");
@ -160,9 +160,25 @@ init_sprites(Gui *gui, Camera *cam)
s->fixed = true; s->fixed = true;
sprite_set_texture(s, t, 0); sprite_set_texture(s, t, 0);
s->clip = CLIP16(0, 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); 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, gui->statsFrame = gui_util_create_frame_sprite(RIGHT_GUI_WIDTH/16,
STATS_GUI_HEIGHT/16, STATS_GUI_HEIGHT/16,
cam); cam);
@ -173,6 +189,10 @@ init_sprites(Gui *gui, Camera *cam)
gui->miniMapFrame = gui_util_create_frame_sprite(RIGHT_GUI_WIDTH/16, gui->miniMapFrame = gui_util_create_frame_sprite(RIGHT_GUI_WIDTH/16,
MINIMAP_GUI_HEIGHT/16, MINIMAP_GUI_HEIGHT/16,
cam); 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* Gui*
@ -197,28 +217,33 @@ gui_create(Camera *cam)
texture_load_font(gui->event_message, "GUI/SDS_8x8.ttf", EVENT_MESSAGE_FONT_SIZE, 2); texture_load_font(gui->event_message, "GUI/SDS_8x8.ttf", EVENT_MESSAGE_FONT_SIZE, 2);
gui->event_message_timer = _timer_create(); gui->event_message_timer = _timer_create();
gui->labels[CURRENT_XP_LABEL] = create_label_sprite((Position) { 16, POS_Y_XPBAR + 18 }); gui->labels[CURRENT_XP_LABEL] = create_label_sprite(POS(16, POS_Y_XPBAR + 18));
gui->labels[LEVEL_LABEL] = create_label_sprite((Position) { 16, POS_Y_XPBAR + 18 + 14 }); gui->labels[LEVEL_LABEL] = create_label_sprite(POS(16, POS_Y_XPBAR + 18 + 14));
gui->labels[DUNGEON_LEVEL_LABEL] = gui->labels[DUNGEON_LEVEL_LABEL] =
create_label_sprite((Position) { create_label_sprite(POS(
16, 16,
POS_Y_XPBAR + 18 + (2*14) 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] = gui->labels[HEALTH_POTION_LABEL] =
create_label_sprite((Position) { create_label_sprite(POS(
32, 32,
POS_Y_COLLECTABLES + 5 POS_Y_COLLECTABLES + 5
}); ));
gui->labels[GOLD_LABEL] = gui->labels[GOLD_LABEL] =
create_label_sprite((Position) { create_label_sprite(POS(
32, 32,
POS_Y_COLLECTABLES + 16 + 5 POS_Y_COLLECTABLES + 16 + 5
}); ));
gui->labels[DAGGER_LABEL] = gui->labels[DAGGER_LABEL] =
create_label_sprite((Position) { create_label_sprite(POS(
32, 32,
POS_Y_COLLECTABLES + 32 + 5 POS_Y_COLLECTABLES + 32 + 5
}); ));
gui_malloc_log(); gui_malloc_log();
gui_malloc_eventmessages(); gui_malloc_eventmessages();
@ -251,7 +276,7 @@ set_max_health(Gui *gui, int max)
sprite->fixed = true; sprite->fixed = true;
sprite->animate = false; sprite->animate = false;
sprite->clip = (SDL_Rect) { 0, 16, 16, 16 }; 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, texture0, 0);
sprite_set_texture(sprite, texture1, 1); sprite_set_texture(sprite, texture1, 1);
linkedlist_append(&gui->health, sprite); 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; gui->labels[LEVEL_LABEL]->dim = gui->labels[LEVEL_LABEL]->textures[0]->dim;
last_level = data.level; 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 void
@ -410,6 +439,8 @@ gui_render_panel(Gui *gui, Camera *cam)
sprite_render(s, cam); sprite_render(s, cam);
item = item->next; item = item->next;
} }
sprite_render(gui->silverKey, cam);
sprite_render(gui->goldKey, cam);
item = gui->sprites; item = gui->sprites;
while (item != NULL) { while (item != NULL) {
Sprite *s = item->data; Sprite *s = item->data;
@ -622,5 +653,8 @@ gui_destroy(Gui *gui)
for (int i = 0; i < LABEL_COUNT; ++i) for (int i = 0; i < LABEL_COUNT; ++i)
sprite_destroy(gui->labels[i]); sprite_destroy(gui->labels[i]);
sprite_destroy(gui->silverKey);
sprite_destroy(gui->goldKey);
free(gui); free(gui);
} }

View File

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

View File

@ -97,7 +97,7 @@ hiscore_init(void)
static void static void
save_hiscore(double gold, int lvl, int dlvl) 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); sqlite3_stmt *stmt = db_prepare(db, query);
debug("Saving high score: %dg %dpl %dl", debug("Saving high score: %dg %dpl %dl",

View File

@ -21,31 +21,57 @@
#include "util.h" #include "util.h"
#include "mixer.h" #include "mixer.h"
#include "gui.h" #include "gui.h"
#include "actiontextbuilder.h"
Item * Item *
item_create(void) item_create(void)
{ {
Item *item = ec_malloc(sizeof(Item)); Item *item = ec_malloc(sizeof(Item));
item->sprite = NULL; item->sprite = NULL;
item->subsprites = NULL;
item->collected = false; item->collected = false;
item->openable = false; item->openable = false;
item->opened = false; item->opened = false;
m_strcpy(item->label, 50, ""); m_strcpy(item->label, 50, "");
item->price = 0.0;
item->value = 0.0; item->value = 0.0;
item->items = NULL; item->items = NULL;
item->effect = NULL; item->effect = NULL;
return item; return item;
} }
void
item_update(Item *item)
{
LinkedList *subsprites = item->subsprites;
while (subsprites != NULL) {
Sprite *sprite = subsprites->data;
sprite->pos = item->sprite->pos;
sprite->pos.x += 15 - sprite->dim.width / 2;
subsprites = subsprites->next;
}
}
void void
item_render(Item *item, Camera *cam) item_render(Item *item, Camera *cam)
{ {
sprite_render(item->sprite, cam); sprite_render(item->sprite, cam);
LinkedList *subsprites = item->subsprites;
while (subsprites != NULL) {
sprite_render(subsprites->data, cam);
subsprites = subsprites->next;
}
} }
void void
item_collected(Item *item, Player *player) item_collected(Item *item, Player *player)
{ {
if (item->price > player->gold){
gui_log("You don't have enough gold to buy %s", item->label);
return;
}
if (item->collected || item->opened) if (item->collected || item->opened)
return; return;
@ -60,6 +86,13 @@ item_collected(Item *item, Player *player)
gui_log("You find nothing inside"); gui_log("You find nothing inside");
} }
if (item->price) {
player->gold -= item->price;
char costLabel[10];
m_sprintf(costLabel, 10, "-$%.0f", item->price);
actiontextbuilder_create_text(costLabel, C_YELLOW, &player->sprite->pos);
}
if (item->effect != NULL) if (item->effect != NULL)
item->effect(item, player); item->effect(item, player);
@ -76,6 +109,9 @@ item_destroy(Item *item)
if (item->sprite) if (item->sprite)
sprite_destroy(item->sprite); sprite_destroy(item->sprite);
while (item->subsprites != NULL)
sprite_destroy(linkedlist_pop(&item->subsprites));
while (item->items != NULL) while (item->items != NULL)
item_destroy(linkedlist_pop(&item->items)); item_destroy(linkedlist_pop(&item->items));

View File

@ -29,10 +29,12 @@
typedef struct Item_t { typedef struct Item_t {
Sprite *sprite; Sprite *sprite;
LinkedList *subsprites;
bool collected; bool collected;
bool openable; bool openable;
bool opened; bool opened;
char label[50]; char label[50];
double price;
double value; double value;
LinkedList *items; LinkedList *items;
void (*effect)(struct Item_t *, Player *); void (*effect)(struct Item_t *, Player *);
@ -44,6 +46,9 @@ item_create(void);
void void
item_collected(Item*, Player*); item_collected(Item*, Player*);
void
item_update(Item*);
void void
item_render(Item*, Camera*); item_render(Item*, Camera*);

View File

@ -18,6 +18,7 @@
#include <SDL.h> #include <SDL.h>
#include <stdlib.h> #include <stdlib.h>
#include <assert.h>
#include "item_builder.h" #include "item_builder.h"
#include "texture.h" #include "texture.h"
@ -27,6 +28,8 @@
#include "random.h" #include "random.h"
#include "texturecache.h" #include "texturecache.h"
#include "sprite.h" #include "sprite.h"
#include "sprite_util.h"
#include "map.h"
static ItemBuilder *builder = NULL; static ItemBuilder *builder = NULL;
@ -108,29 +111,13 @@ pickup_gold(Item *item, Player *player)
gui_log("You pick up %s", &item->label); gui_log("You pick up %s", &item->label);
} }
static Item * Item *
create_treasure(int current_level) item_builder_build_treasure(Treasure type, double goldAmt)
{ {
double amt; double amt = goldAmt;
char label[50] = ""; char label[50] = "";
unsigned int highest_treasure;
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;
} else if (current_level > 3) {
highest_treasure = GOLD;
} else {
highest_treasure = SILVER;
}
value = get_random(highest_treasure);
SDL_Rect clip = CLIP16(0, 0); SDL_Rect clip = CLIP16(0, 0);
switch (value) { switch (type) {
case COPPER: case COPPER:
m_sprintf(label, 50, "%.0f copper", amt); m_sprintf(label, 50, "%.0f copper", amt);
amt /= 100; amt /= 100;
@ -165,6 +152,71 @@ create_treasure(int current_level)
return item; return item;
} }
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;
}
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;
}
Item *
item_builder_build_key(unsigned int type)
{
char label[20];
Item *item;
switch (type) {
case 1:
m_sprintf(label, 20, "a 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("Extras/Keys.png",
NULL,
CLIP16(16, 0),
&pickup_gold_key);
break;
default:
fatal("Bad keytype provided");
}
m_strcpy(item->label, 20, label);
return item;
}
static Item *
create_treasure(int current_level)
{
double amt;
unsigned int highest_treasure;
unsigned int value;
amt = (unsigned int) 1 + get_random(5*current_level) % 40;
if (current_level > 9) {
highest_treasure = PLATINUM;
} else if (current_level > 3) {
highest_treasure = GOLD;
} else {
highest_treasure = SILVER;
}
value = get_random(highest_treasure);
return item_builder_build_treasure((Treasure) value, amt);
}
Item * Item *
item_builder_build_item(ItemKey key, int level) item_builder_build_item(ItemKey key, int level)
{ {
@ -205,6 +257,18 @@ item_builder_build_item(ItemKey key, int level)
break; break;
} }
if (item->value != 1) {
Sprite *valueSprite = sprite_util_create_text_sprite("GUI/SDS_8x8.ttf",
8,
C_BLUE,
C_BLACK,
"%g",
item->value);
valueSprite->offset.x = item->sprite->dim.width - valueSprite->dim.width;
valueSprite->offset.y = item->sprite->dim.height - valueSprite->dim.height;
linkedlist_append(&item->subsprites, valueSprite);
}
return item; return item;
} }

View File

@ -53,6 +53,12 @@ item_builder_build_sack(void);
Item * Item *
item_builder_build_container(const char *path0, const char *path1, SDL_Rect clip); item_builder_build_container(const char *path0, const char *path1, SDL_Rect clip);
Item *
item_builder_build_treasure(Treasure type, double goldAmt);
Item *
item_builder_build_key(unsigned int type);
void void
item_builder_close(void); item_builder_close(void);

View File

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

View File

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

View File

@ -55,18 +55,31 @@
#include "tooltip.h" #include "tooltip.h"
#include "gamecontroller.h" #include "gamecontroller.h"
#include "time.h" #include "time.h"
#include "sprite_util.h"
#include "event.h"
#include "config.h"
#include "save.h"
#ifdef STEAM_BUILD #ifdef STEAM_BUILD
#include "checksum.h"
#include "steam/steamworks_api_wrapper.h" #include "steam/steamworks_api_wrapper.h"
#endif // STEAM_BUILD #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[] = { static char *artifacts_tooltip[] = {
"CONGRATULATIONS!", "", "CONGRATULATIONS!", "",
"", "",
" You just picked up your first artifact!", "", " You just picked up your first artifact!", "",
"", "",
" Your current artifacts and corresponding level are", "", " 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", "", " Artifacts have mystical effects that improve your offensive", "",
@ -90,8 +103,8 @@ static char *skills_tooltip[] = {
"", "",
"", "",
" SKILL INFO: SHIFT + <N>", "", " SKILL INFO: SHIFT + <N>", "",
" Where <N> is the number corresponding to the skill", "", " Where <N> is the number corresponding to", "",
" Eg. 1, 2, 3, 4, 5", "", " the skill, i.e. 1, 2, 3, 4, 5", "",
"", "",
" DISABLE TOOLTIPS: CTRL + D", "", " DISABLE TOOLTIPS: CTRL + D", "",
"", "",
@ -233,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); 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, gWindow = SDL_CreateWindow(title_buffer,
SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
(int)(SCREEN_WIDTH * renderScale), (int)(SCREEN_WIDTH * renderScale),
(int)(SCREEN_HEIGHT * renderScale), (int)(SCREEN_HEIGHT * renderScale),
SDL_WINDOW_SHOWN); SDL_WINDOW_SHOWN);
@ -268,7 +281,7 @@ bool initSDL(void)
debug("Disabling text input"); debug("Disabling text input");
SDL_StopTextInput(); SDL_StopTextInput();
} }
return true; return true;
} }
@ -308,6 +321,7 @@ initGame(void)
initViewports(0); initViewports(0);
input_init(&input); input_init(&input);
texturecache_init(gRenderer); texturecache_init(gRenderer);
sprite_util_init(gRenderer);
gCamera = camera_create(gRenderer); gCamera = camera_create(gRenderer);
gRoomMatrix = roommatrix_create(); gRoomMatrix = roommatrix_create();
gGui = gui_create(gCamera); gGui = gui_create(gCamera);
@ -400,6 +414,8 @@ on_character_select(const char *str)
playerClass = WARRIOR; playerClass = WARRIOR;
else if (strcmp(str, "rogue") == 0) else if (strcmp(str, "rogue") == 0)
playerClass = ROGUE; playerClass = ROGUE;
else if (strcmp(str, "mage") == 0)
playerClass = MAGE;
startGame(); startGame();
} }
@ -413,6 +429,56 @@ goToCharacterMenu(void *unused)
gGameState = CHARACTER_MENU; 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 static void
startRegularGame(void *unused) startRegularGame(void *unused)
{ {
@ -452,6 +518,7 @@ static void
goToMainMenu(void *unused) goToMainMenu(void *unused)
{ {
UNUSED(unused); UNUSED(unused);
save_load();
gui_clear_message_log(); gui_clear_message_log();
gGameState = MENU; gGameState = MENU;
menu_destroy(inGameMenu); menu_destroy(inGameMenu);
@ -467,37 +534,47 @@ static void
goToGameSelectMenu(void *unused) goToGameSelectMenu(void *unused)
{ {
UNUSED(unused); UNUSED(unused);
static TEXT_MENU_ITEM menuItems[] = { int item_count = 3;
{
"STANDARD GAME",
"Standard 20 level game, recommended for new players",
startRegularGame
},
#ifdef STEAM_BUILD #ifdef STEAM_BUILD
{ item_count += 1;
"WEEKLY CHALLENGE",
"Quick game with weekly leaderboards at breakhack.net",
startWeeklyGame
},
#endif #endif
{ if (save_exists()) {
"QUICK GAME", item_count += 1;
"Shorter 12 level game, with more action earlier in the game", }
startQuickGame TEXT_MENU_ITEM *menuItems = ec_malloc(item_count * sizeof(TEXT_MENU_ITEM));
}, int i = 0;
{ if (save_exists()) {
"ARCADE GAME", menuItems[i++] = (TEXT_MENU_ITEM) {
"One big level with lots of action", "CONTINUE",
startArcadeGame "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 menu_create_text_menu(&gameSelectMenu, menuItems, item_count, gRenderer);
int count = 4; free(menuItems);
#else
int count = 3;
#endif
menu_create_text_menu(&gameSelectMenu, &menuItems[0], count, gRenderer);
gGameState = GAME_SELECT; gGameState = GAME_SELECT;
} }
@ -634,16 +711,31 @@ resetGame(void)
mode = ARCADE; mode = ARCADE;
info("Building new map"); info("Building new map");
gMap = map_lua_generator_run(cLevel, mode, gRenderer); gMap = map_lua_generator_run(cLevel, mode, gPlayer, gRenderer);
gPlayer->sprite->pos = (Position) { player_reset_on_levelchange(gPlayer);
TILE_DIMENSION, TILE_DIMENSION };
map_set_current_room(gMap, &gPlayer->sprite->pos); map_set_current_room(gMap, &gPlayer->sprite->pos);
camera_follow_position(gCamera, &gPlayer->sprite->pos); camera_follow_position(gCamera, &gPlayer->sprite->pos);
repopulate_roommatrix(); repopulate_roommatrix();
} }
static void
on_event_callback(Event *event)
{
if (event->type == MONSTER_KILLED_EVENT) {
if (strcmp(event->monsterKilled.monster->label, "A Fairy") == 0) {
LinkedList *monsters = gMap->monsters;
while (monsters) {
Monster *monster = monsters->data;
monsters = monsters->next;
if (position_in_room(&monster->sprite->pos, &gMap->currentRoom))
monster_set_bloodlust(monster, true);
}
}
}
}
static bool static bool
init(void) init(void)
{ {
@ -666,7 +758,10 @@ init(void)
return false; return false;
} }
event_register_listener(on_event_callback);
settings_init(); settings_init();
save_init();
hiscore_init(); hiscore_init();
initMainMenu(); initMainMenu();
@ -827,7 +922,7 @@ end_game_details(void)
{ {
gui_log("You earned %.2f gold", gPlayer->gold); gui_log("You earned %.2f gold", gPlayer->gold);
gui_event_message("You earned %.2f gold", gPlayer->gold); gui_event_message("You earned %.2f gold", gPlayer->gold);
if (hiscore_get_top_gold() < gPlayer->gold) { if (hiscore_get_top_gold() < gPlayer->gold) {
gui_event_message("NEW HIGHSCORE"); gui_event_message("NEW HIGHSCORE");
gui_log("NEW HIGHSCORE"); gui_log("NEW HIGHSCORE");
@ -850,17 +945,17 @@ check_next_level(void)
return; return;
} }
if (tile->levelExit) { if (tile->levelExit) {
mixer_play_effect(NEXT_LEVEL);
++cLevel; ++cLevel;
if (cLevel > (quickGame ? 11 : 19)) { if (!weeklyGame) {
mixer_play_music(BOSS_MUSIC0); save_save(get_random_seed(),
} else if (cLevel % (quickGame ? 3 : 5) == 0) { cLevel,
gui_log("You sense something powerful in the vicinity"); quickGame,
mixer_play_music(BOSS_MUSIC0); arcadeGame,
} else { gPlayer);
mixer_play_music(GAME_MUSIC0 + get_random(2));
} }
mixer_play_effect(NEXT_LEVEL);
choose_music();
if (!gameCompleted()) { if (!gameCompleted()) {
resetGame(); resetGame();
} }
@ -1002,9 +1097,6 @@ render_game(void)
map_render_top_layer(gMap, gRoomMatrix, gCamera); 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); roommatrix_render_lightmap(gRoomMatrix, gCamera);
actiontextbuilder_render(gCamera); actiontextbuilder_render(gCamera);
gui_render_event_message(gGui, gCamera); gui_render_event_message(gGui, gCamera);
@ -1065,10 +1157,13 @@ register_scores(void)
steam_set_achievement(ROGUE_LIKE); steam_set_achievement(ROGUE_LIKE);
steam_register_rogue_score((int) gPlayer->gold, steam_register_rogue_score((int) gPlayer->gold,
(int32_t*) &details, 1); (int32_t*) &details, 1);
} } else if (gPlayer->class == WARRIOR) {
else if (gPlayer->class == WARRIOR) {
steam_register_warrior_score((int) gPlayer->gold, steam_register_warrior_score((int) gPlayer->gold,
(int32_t*) &details, 1); (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 #endif
@ -1092,6 +1187,7 @@ run_game(void)
camera_shake(VECTOR2D_RIGHT, 800); camera_shake(VECTOR2D_RIGHT, 800);
gui_log("The dungeon consumed you"); gui_log("The dungeon consumed you");
gui_event_message("You died!"); gui_event_message("You died!");
save_clear();
end_game_details(); end_game_details();
mixer_play_effect(SPLAT); mixer_play_effect(SPLAT);
gGameState = GAME_OVER; gGameState = GAME_OVER;
@ -1325,12 +1421,14 @@ void close(void)
texturecache_close(); texturecache_close();
settings_close(); settings_close();
hiscore_close(); hiscore_close();
save_close();
#ifdef STEAM_BUILD #ifdef STEAM_BUILD
steam_shutdown(); steam_shutdown();
#endif // STEAM_BUILD #endif // STEAM_BUILD
gamecontroller_close(); gamecontroller_close();
event_clear_listeners();
SDL_DestroyRenderer(gRenderer); SDL_DestroyRenderer(gRenderer);
SDL_DestroyWindow(gWindow); SDL_DestroyWindow(gWindow);
gWindow = NULL; gWindow = NULL;
@ -1339,12 +1437,46 @@ void close(void)
SDL_Quit(); SDL_Quit();
} }
#ifdef STEAM_BUILD
static void
validate_lib_checksum(void)
{
FILE *fp;
#ifdef WIN32
const char *file = "./steam_api.dll";
unsigned int expected = DLL_LIBSTEAM_CHECKSUM;
fopen_s(&fp, file, "rb");
#else // WIN32
const char *file = "./libsteam_api.so";
unsigned int expected = SO_LIBSTEAM_CHECKSUM;
fp = fopen(file, "rb");
#endif // WIN32
if (!fp) {
fatal("Unable to open %s for reading\n", file);
}
unsigned calculated = checksum_fp(fp);
fclose(fp);
if (calculated != expected) {
fatal("Checksum validation failiure: %#x != %#x", calculated, expected);
} else {
info("Checksum validated: %#x", calculated);
}
}
#endif // STEAM_BUILD
int main(int argc, char *argv[]) int main(int argc, char *argv[])
{ {
UNUSED(argc); UNUSED(argc);
#ifdef STEAM_BUILD
validate_lib_checksum();
#endif // STEAM_BUILD
PHYSFS_init(argv[0]); PHYSFS_init(argv[0]);
#ifndef DEBUG #if !defined(DEBUG) && !defined(__amigaos4__)
PHYSFS_mount("assets.pack", NULL, 0); PHYSFS_mount("assets.pack", NULL, 0);
PHYSFS_mount("data.pack", NULL, 0); PHYSFS_mount("data.pack", NULL, 0);
#else #else
@ -1360,7 +1492,7 @@ int main(int argc, char *argv[])
return 1; return 1;
if (settings_get()->fullscreen_enabled) { if (settings_get()->fullscreen_enabled) {
// Game starts in windowed mode so this will // Game starts in windowed mode so this will
// change to fullscreen // change to fullscreen
toggle_fullscreen(); toggle_fullscreen();
} }
@ -1370,3 +1502,4 @@ int main(int argc, char *argv[])
return 0; return 0;
} }

120
src/map.c
View File

@ -26,6 +26,7 @@
#include "particle_engine.h" #include "particle_engine.h"
#include "update_data.h" #include "update_data.h"
#include "trap.h" #include "trap.h"
#include "mixer.h"
static Room* static Room*
create_room(void) create_room(void)
@ -35,12 +36,15 @@ create_room(void)
room = ec_malloc(sizeof(Room)); room = ec_malloc(sizeof(Room));
room->modifier.type = RMOD_TYPE_NONE; room->modifier.type = RMOD_TYPE_NONE;
room->visited = false;
room->lockTypes = 0;
for (i=0; i < MAP_ROOM_WIDTH; ++i) { for (i=0; i < MAP_ROOM_WIDTH; ++i) {
for (j=0; j < MAP_ROOM_HEIGHT; ++j) { for (j=0; j < MAP_ROOM_HEIGHT; ++j) {
room->tiles[i][j] = NULL; room->tiles[i][j] = NULL;
room->walls[i][j] = NULL;
room->decorations[i][j] = NULL; room->decorations[i][j] = NULL;
room->traps[i][j] = NULL; room->traps[i][j] = NULL;
room->visited = false; room->doors[i][j] = NULL;
} }
} }
return room; return room;
@ -60,6 +64,7 @@ map_create(void)
map->currentRoom = (Position) { 0, 0 }; map->currentRoom = (Position) { 0, 0 };
map->monsterMoveTimer = _timer_create(); map->monsterMoveTimer = _timer_create();
map->level = 1; map->level = 1;
map->lockTypes = 0;
for (i=0; i < MAP_H_ROOM_COUNT; ++i) { for (i=0; i < MAP_H_ROOM_COUNT; ++i) {
for (j=0; j < MAP_V_ROOM_COUNT; ++j) { for (j=0; j < MAP_V_ROOM_COUNT; ++j) {
@ -81,6 +86,8 @@ map_create_tile(void)
tile->lethal = false; tile->lethal = false;
tile->lightsource = false; tile->lightsource = false;
tile->levelExit = false; tile->levelExit = false;
tile->door = false;
tile->lockType = LOCK_NONE;
return tile; return tile;
} }
@ -92,37 +99,9 @@ map_tile_destroy(MapTile *tile)
free(tile); free(tile);
} }
void static void
map_add_tile(Map *map, Position *tile_pos, MapTile *tile) switch_tile(Map *map, Position *tile_pos, MapTile *tile, MapTile **oldTile)
{ {
const Position *cr = &map->currentRoom;
Room *room = map->rooms[cr->x][cr->y];
MapTile **oldTile = &room->tiles[tile_pos->x][tile_pos->y];
// Set the tile sprites position to match tile pos
tile->sprite->pos = POS(tile_pos->x * TILE_DIMENSION + (map->currentRoom.x * GAME_VIEW_WIDTH),
tile_pos->y * TILE_DIMENSION + (map->currentRoom.y * GAME_VIEW_HEIGHT));
// If this is the level exit then clear the decoration if one exists
if (tile->levelExit && room->decorations[tile_pos->x][tile_pos->y]) {
MapTile **decoration = &room->decorations[tile_pos->x][tile_pos->y];
map_tile_destroy(*decoration);
*decoration = NULL;
}
// Clear possible tile
if (*oldTile != NULL) {
map_tile_destroy(*oldTile);
*oldTile = NULL;
}
*oldTile = tile;
}
void map_add_decoration(Map *map, Position *tile_pos, MapTile *tile)
{
const Position *cr = &map->currentRoom;
MapTile **oldTile = &map->rooms[cr->x][cr->y]->decorations[tile_pos->x][tile_pos->y];
// Set the decoration sprites position to match tile pos // Set the decoration sprites position to match tile pos
tile->sprite->pos = POS(tile_pos->x * TILE_DIMENSION + (map->currentRoom.x * GAME_VIEW_WIDTH), tile->sprite->pos = POS(tile_pos->x * TILE_DIMENSION + (map->currentRoom.x * GAME_VIEW_WIDTH),
tile_pos->y * TILE_DIMENSION + (map->currentRoom.y * GAME_VIEW_HEIGHT)); tile_pos->y * TILE_DIMENSION + (map->currentRoom.y * GAME_VIEW_HEIGHT));
@ -134,6 +113,50 @@ void map_add_decoration(Map *map, Position *tile_pos, MapTile *tile)
*oldTile = tile; *oldTile = tile;
} }
void
map_add_tile(Map *map, Position *tile_pos, MapTile *tile)
{
const Position *cr = &map->currentRoom;
Room *room = map->rooms[cr->x][cr->y];
switch_tile(map, tile_pos, tile, &room->tiles[tile_pos->x][tile_pos->y]);
// If this is the level exit then clear the decoration if one exists
if (tile->levelExit && room->decorations[tile_pos->x][tile_pos->y]) {
MapTile **decoration = &room->decorations[tile_pos->x][tile_pos->y];
map_tile_destroy(*decoration);
*decoration = NULL;
}
}
void
map_add_wall(Map *map, Position *tile_pos, MapTile *tile)
{
const Position *cr = &map->currentRoom;
Room *room = map->rooms[cr->x][cr->y];
switch_tile(map, tile_pos, tile, &room->walls[tile_pos->x][tile_pos->y]);
}
void map_add_decoration(Map *map, Position *tile_pos, MapTile *tile)
{
const Position *cr = &map->currentRoom;
Room *room = map->rooms[cr->x][cr->y];
switch_tile(map, tile_pos, tile, &room->decorations[tile_pos->x][tile_pos->y]);
}
void
map_add_door(Map *map, Position *tile_pos, MapTile *tile)
{
const Position *cr = &map->currentRoom;
Room *room = map->rooms[cr->x][cr->y];
switch_tile(map, tile_pos, tile, &room->doors[tile_pos->x][tile_pos->y]);
tile->door = true;
tile->sprite->texture_index = 0;
tile->sprite->animate = false;
map->lockTypes |= tile->lockType;
room->lockTypes |= tile->lockType;
}
void void
map_add_trap(Map *map, Position *pos, Trap *trap) map_add_trap(Map *map, Position *pos, Trap *trap)
{ {
@ -295,6 +318,12 @@ map_update(UpdateData *data)
sprite_update(room->tiles[i][j]->sprite, data); sprite_update(room->tiles[i][j]->sprite, data);
} }
} }
LinkedList *items = map->items;
while (items) {
item_update(items->data);
items = items->next;
}
} }
void map_render(Map *map, Camera *cam) void map_render(Map *map, Camera *cam)
@ -306,6 +335,8 @@ void map_render(Map *map, Camera *cam)
for (i=0; i < MAP_ROOM_WIDTH; ++i) { for (i=0; i < MAP_ROOM_WIDTH; ++i) {
for (j=0; j < MAP_ROOM_HEIGHT; ++j) { for (j=0; j < MAP_ROOM_HEIGHT; ++j) {
map_tile_render(room->tiles[i][j], cam); map_tile_render(room->tiles[i][j], cam);
map_tile_render(room->walls[i][j], cam);
map_tile_render(room->doors[i][j], cam);
map_tile_render(room->decorations[i][j], cam); map_tile_render(room->decorations[i][j], cam);
if (room->traps[i][j]) if (room->traps[i][j])
@ -398,6 +429,12 @@ void map_room_destroy(Room *room)
if (room->tiles[i][j]) { if (room->tiles[i][j]) {
map_tile_destroy(room->tiles[i][j]); map_tile_destroy(room->tiles[i][j]);
} }
if (room->doors[i][j]) {
map_tile_destroy(room->doors[i][j]);
}
if (room->walls[i][j]) {
map_tile_destroy(room->walls[i][j]);
}
if (room->decorations[i][j]) { if (room->decorations[i][j]) {
map_tile_destroy(room->decorations[i][j]); map_tile_destroy(room->decorations[i][j]);
} }
@ -412,10 +449,29 @@ void map_room_destroy(Room *room)
void void
map_trigger_tile_fall(MapTile *tile) map_trigger_tile_fall(MapTile *tile)
{ {
if (tile->sprite->state != SPRITE_STATE_FALLING && tile->sprite->state != SPRITE_STATE_PLUMMETED)
particle_engine_dust_puff(tile->sprite->pos, tile->sprite->dim);
tile->sprite->state = SPRITE_STATE_FALLING; tile->sprite->state = SPRITE_STATE_FALLING;
tile->lethal = true; tile->lethal = true;
} }
bool
map_open_door(MapTile *tile, Player *player)
{
if (tile->lockType == LOCK_NONE || tile->lockType & player->equipment.keys) {
// Open the door
if (tile->lockType != LOCK_NONE)
gui_log("You unlocked a door!");
mixer_play_effect(DOOR_OPEN);
tile->sprite->texture_index = 1;
tile->collider = false;
return true;
}
return false;
}
void void
map_destroy(Map *map) map_destroy(Map *map)
{ {
@ -439,7 +495,7 @@ map_destroy(Map *map)
artifact_destroy(linkedlist_pop(&map->artifacts)); artifact_destroy(linkedlist_pop(&map->artifacts));
while (map->objects != NULL) while (map->objects != NULL)
artifact_destroy(linkedlist_pop(&map->objects)); object_destroy(linkedlist_pop(&map->objects));
timer_destroy(map->monsterMoveTimer); timer_destroy(map->monsterMoveTimer);
free(map); free(map);

View File

@ -32,6 +32,7 @@
#include "player.h" #include "player.h"
#include "map_room_modifiers.h" #include "map_room_modifiers.h"
#include "object.h" #include "object.h"
#include "doorlocktype.h"
typedef struct UpdateData UpdateData; typedef struct UpdateData UpdateData;
typedef struct Trap Trap; typedef struct Trap Trap;
@ -42,14 +43,19 @@ typedef struct MapTile_t {
bool lethal; bool lethal;
bool lightsource; bool lightsource;
bool levelExit; bool levelExit;
bool door;
DoorLockType lockType;
} MapTile; } MapTile;
typedef struct Room_t { typedef struct Room_t {
MapTile* tiles[MAP_ROOM_WIDTH][MAP_ROOM_HEIGHT]; MapTile* tiles[MAP_ROOM_WIDTH][MAP_ROOM_HEIGHT];
MapTile* walls[MAP_ROOM_WIDTH][MAP_ROOM_HEIGHT];
MapTile* decorations[MAP_ROOM_WIDTH][MAP_ROOM_HEIGHT]; MapTile* decorations[MAP_ROOM_WIDTH][MAP_ROOM_HEIGHT];
MapTile* doors[MAP_ROOM_WIDTH][MAP_ROOM_HEIGHT];
Trap* traps[MAP_ROOM_WIDTH][MAP_ROOM_HEIGHT]; Trap* traps[MAP_ROOM_WIDTH][MAP_ROOM_HEIGHT];
RoomModifierData modifier; RoomModifierData modifier;
bool visited; bool visited;
unsigned int lockTypes;
} Room; } Room;
typedef struct Map_t { typedef struct Map_t {
@ -62,6 +68,7 @@ typedef struct Map_t {
Position currentRoom; Position currentRoom;
Timer *monsterMoveTimer; Timer *monsterMoveTimer;
int level; int level;
unsigned int lockTypes;
} Map; } Map;
Map* Map*
@ -76,9 +83,15 @@ map_add_texture(Map*, const char *path, SDL_Renderer*);
void void
map_add_tile(Map *map, Position *tile_pos, MapTile*); map_add_tile(Map *map, Position *tile_pos, MapTile*);
void
map_add_wall(Map *map, Position *tile_pos, MapTile*);
void void
map_add_decoration(Map *map, Position *tile_pos, MapTile*); map_add_decoration(Map *map, Position *tile_pos, MapTile*);
void
map_add_door(Map *map, Position *tile_pos, MapTile*);
void void
map_add_trap(Map*, Position*, Trap*); map_add_trap(Map*, Position*, Trap*);
@ -112,6 +125,9 @@ map_set_current_room(Map*, Position*);
void void
map_trigger_tile_fall(MapTile *tile); map_trigger_tile_fall(MapTile *tile);
bool
map_open_door(MapTile *tile, Player *player);
void void
map_destroy(Map*); map_destroy(Map*);

View File

@ -37,6 +37,7 @@
#include "item_builder.h" #include "item_builder.h"
#include "random.h" #include "random.h"
#include "bh_random.h" #include "bh_random.h"
#include "artifact.h"
static static
lua_State* load_lua_state(void) lua_State* load_lua_state(void)
@ -94,6 +95,18 @@ SDL_Renderer* luaL_checksdlrenderer(lua_State *L)
return renderer; return renderer;
} }
static Player*
luaL_checkplayer(lua_State *L)
{
Player *player;
lua_getglobal(L, "_game_player");
if (!lua_islightuserdata(L, -1))
fatal("in luaL_checkplayer(), pointer lost in lua script");
player = lua_touserdata(L, -1);
return player;
}
static int static int
l_map_set_current_room_modifier(lua_State *L) l_map_set_current_room_modifier(lua_State *L)
{ {
@ -169,6 +182,7 @@ extract_tile_data(lua_State *L,
int tile_x, tile_y; int tile_x, tile_y;
int t_index0, t_index1, tile_clip_x, tile_clip_y; int t_index0, t_index1, tile_clip_x, tile_clip_y;
bool collider, lightsource, levelExit, lethal; bool collider, lightsource, levelExit, lethal;
int lockType;
map = luaL_checkmap(L, 1); map = luaL_checkmap(L, 1);
tile_x = (int) luaL_checkinteger(L, 2); tile_x = (int) luaL_checkinteger(L, 2);
@ -187,18 +201,20 @@ extract_tile_data(lua_State *L,
lua_getfield(L, 4, "isLightSource"); lua_getfield(L, 4, "isLightSource");
lua_getfield(L, 4, "isLevelExit"); lua_getfield(L, 4, "isLevelExit");
lua_getfield(L, 4, "isLethal"); lua_getfield(L, 4, "isLethal");
lua_getfield(L, 4, "lockType");
t_index0 = (int) luaL_checkinteger(L, -8); t_index0 = (int) luaL_checkinteger(L, -9);
t_index1 = (int) luaL_checkinteger(L, -7); t_index1 = (int) luaL_checkinteger(L, -8);
tile_clip_x = (int) luaL_checkinteger(L, -6); tile_clip_x = (int) luaL_checkinteger(L, -7);
tile_clip_y = (int) luaL_checkinteger(L, -5); tile_clip_y = (int) luaL_checkinteger(L, -6);
collider = lua_toboolean(L, -4); collider = lua_toboolean(L, -5);
lightsource = lua_toboolean(L, -3); lightsource = lua_toboolean(L, -4);
levelExit = lua_toboolean(L, -2); levelExit = lua_toboolean(L, -3);
lethal = lua_toboolean(L, -1); lethal = lua_toboolean(L, -2);
lockType = (int) luaL_checkinteger(L, -1);
// Clear the stack // Clear the stack
lua_pop(L, 8); lua_pop(L, 9);
Position tilePos = (Position) { tile_x, tile_y }; Position tilePos = (Position) { tile_x, tile_y };
SDL_Rect clip = (SDL_Rect) { tile_clip_x, tile_clip_y, 16, 16 }; SDL_Rect clip = (SDL_Rect) { tile_clip_x, tile_clip_y, 16, 16 };
@ -214,6 +230,7 @@ extract_tile_data(lua_State *L,
tile->lightsource = lightsource; tile->lightsource = lightsource;
tile->levelExit = levelExit; tile->levelExit = levelExit;
tile->lethal = lethal; tile->lethal = lethal;
tile->lockType = lockType;
f_add_tile(map, &tilePos, tile); f_add_tile(map, &tilePos, tile);
} }
@ -258,6 +275,20 @@ l_add_tile(lua_State *L)
return 0; return 0;
} }
static int
l_add_wall(lua_State *L)
{
extract_tile_data(L, &map_add_wall);
return 0;
}
static int
l_add_door(lua_State *L)
{
extract_tile_data(L, &map_add_door);
return 0;
}
static int static int
l_add_decoration(lua_State *L) l_add_decoration(lua_State *L)
{ {
@ -359,6 +390,52 @@ l_add_chest(lua_State *L)
return 0; return 0;
} }
static bool
monster_is_in_room_with_locktype(Map *map, Monster *m, DoorLockType lockType)
{
Position roomPos = position_to_room_coords(&m->sprite->pos);
Room *room = map->rooms[roomPos.x][roomPos.y];
return room->lockTypes & lockType;
}
static void
add_keybearer_to_map(Map *map, int keyType)
{
for (size_t tries = 0; tries < 10; tries++) {
unsigned int max = linkedlist_size(map->monsters);
unsigned int index = bh_map_rand() % max;
Monster *m = linkedlist_get(&map->monsters, index);
if (!m)
continue;
if (m->items.keyType != LOCK_NONE)
continue;
if (!monster_is_in_room_with_locktype(map, m, keyType)) {
debug("Adding key %d to monster '%s' (%u)", keyType, m->label, index);
m->items.keyType = keyType;
return;
} else {
debug("Looking for another monster to be the keybearer");
}
}
error("Failed to find a suitable keybearer (%d)", keyType);
}
static int
l_add_key_to_random_monsters(lua_State *L)
{
Map *map = luaL_checkmap(L, 1);
if (map->lockTypes & LOCK_GOLD)
add_keybearer_to_map(map, LOCK_GOLD);
if (map->lockTypes & LOCK_SILVER)
add_keybearer_to_map(map, LOCK_SILVER);
return 0;
}
static int static int
l_add_monster(lua_State *L) l_add_monster(lua_State *L)
{ {
@ -404,6 +481,7 @@ l_add_monster(lua_State *L)
lua_pop(L, 8); lua_pop(L, 8);
// Make sure traders stay hostile if you've killed one
monster = monster_create(); monster = monster_create();
monster->sprite->clip = (SDL_Rect) { clip_x, clip_y, 16, 16 }; monster->sprite->clip = (SDL_Rect) { clip_x, clip_y, 16, 16 };
monster_update_pos(monster, (Position) { x, y }); monster_update_pos(monster, (Position) { x, y });
@ -492,8 +570,40 @@ l_get_random(lua_State *L)
return 1; return 1;
} }
static int
l_create_shop_artifact(lua_State *L)
{
Player *player = luaL_checkplayer(L);
if (player == NULL)
return 0;
Map *map = luaL_checkmap(L, 1);
int x = (int) luaL_checkinteger(L, 2);
int y = (int) luaL_checkinteger(L, 3);
Artifact *a = artifact_create_random(player, 2);
a->sprite->pos = POS(x, y);
artifact_add_price(a, 100);
linkedlist_append(&map->artifacts, a);
return 0;
}
static void
build_player_state_table(lua_State *L, Player *player)
{
lua_createtable(L, 0, 1);
lua_pushboolean(L, player ? player->stateData.shopOwnerKiller : false);
lua_setfield(L, -2, "shopOwnerKiller");
lua_pushnumber(L, player ? player->stats.lvl : 1);
lua_setfield(L, -2, "level");
lua_setglobal(L, "PlayerData");
}
static Map* static Map*
generate_map(unsigned int level, const char *file, GameMode gameMode, SDL_Renderer *renderer) generate_map(unsigned int level, const char *file, GameMode gameMode, Player *player, SDL_Renderer *renderer)
{ {
int status, result; int status, result;
@ -512,9 +622,17 @@ generate_map(unsigned int level, const char *file, GameMode gameMode, SDL_Render
} }
// Present stuff to lua // Present stuff to lua
build_player_state_table(L, player);
lua_pushlightuserdata(L, renderer); lua_pushlightuserdata(L, renderer);
lua_setglobal(L, "_sdl_renderer"); lua_setglobal(L, "_sdl_renderer");
lua_pushlightuserdata(L, player);
lua_setglobal(L, "_game_player");
lua_pushcfunction(L, l_create_shop_artifact);
lua_setglobal(L, "create_shop_artifact");
lua_pushcfunction(L, l_create_map); lua_pushcfunction(L, l_create_map);
lua_setglobal(L, "create_map"); lua_setglobal(L, "create_map");
@ -524,6 +642,12 @@ generate_map(unsigned int level, const char *file, GameMode gameMode, SDL_Render
lua_pushcfunction(L, l_add_tile); lua_pushcfunction(L, l_add_tile);
lua_setglobal(L, "add_tile"); lua_setglobal(L, "add_tile");
lua_pushcfunction(L, l_add_wall);
lua_setglobal(L, "add_wall");
lua_pushcfunction(L, l_add_door);
lua_setglobal(L, "add_door");
lua_pushcfunction(L, l_add_decoration); lua_pushcfunction(L, l_add_decoration);
lua_setglobal(L, "add_decoration"); lua_setglobal(L, "add_decoration");
@ -548,6 +672,9 @@ generate_map(unsigned int level, const char *file, GameMode gameMode, SDL_Render
lua_pushcfunction(L, l_add_monster); lua_pushcfunction(L, l_add_monster);
lua_setglobal(L, "add_monster"); lua_setglobal(L, "add_monster");
lua_pushcfunction(L, l_add_key_to_random_monsters);
lua_setglobal(L, "add_keybearers");
lua_pushcfunction(L, l_get_random_seed); lua_pushcfunction(L, l_get_random_seed);
lua_setglobal(L, "get_random_seed"); lua_setglobal(L, "get_random_seed");
@ -594,13 +721,13 @@ generate_map(unsigned int level, const char *file, GameMode gameMode, SDL_Render
Map* map_lua_generator_single_room__run(unsigned int level, SDL_Renderer *renderer) Map* map_lua_generator_single_room__run(unsigned int level, SDL_Renderer *renderer)
{ {
char file[] = "menumapgen.lua"; char file[] = "menumapgen.lua";
return generate_map(level, file, REGULAR, renderer); return generate_map(level, file, REGULAR, NULL, renderer);
} }
Map* map_lua_generator_run(unsigned int level, GameMode gameMode, SDL_Renderer *renderer) Map* map_lua_generator_run(unsigned int level, GameMode gameMode, Player *player, SDL_Renderer *renderer)
{ {
char file[] = "mapgen.lua"; char file[] = "mapgen.lua";
return generate_map(level, file, gameMode, renderer); return generate_map(level, file, gameMode, player, renderer);
} }

View File

@ -22,7 +22,7 @@
#include "map.h" #include "map.h"
#include "defines.h" #include "defines.h"
Map* map_lua_generator_run(unsigned int level, GameMode gameMode, SDL_Renderer *renderer); Map* map_lua_generator_run(unsigned int level, GameMode gameMode, Player *player, SDL_Renderer *renderer);
Map* map_lua_generator_single_room__run(unsigned int level, SDL_Renderer *renderer); Map* map_lua_generator_single_room__run(unsigned int level, SDL_Renderer *renderer);
#endif // MAP_LUA_H_ #endif // MAP_LUA_H_

View File

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

View File

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

View File

@ -88,6 +88,11 @@ load_effects(void)
effects[FADE_IN] = load_effect("Sounds/FX/fade_in.wav"); effects[FADE_IN] = load_effect("Sounds/FX/fade_in.wav");
effects[FADE_OUT] = load_effect("Sounds/FX/fade_out.wav"); effects[FADE_OUT] = load_effect("Sounds/FX/fade_out.wav");
effects[BURST] = load_effect("Sounds/FX/burst.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 void
@ -109,7 +114,7 @@ mixer_toggle_sound(void)
} }
bool bool
mixer_toggle_music(GameState *state) mixer_toggle_music(const GameState *state)
{ {
Settings *settings = settings_get(); Settings *settings = settings_get();
settings->music_enabled = !settings->music_enabled; settings->music_enabled = !settings->music_enabled;

View File

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

View File

@ -18,6 +18,7 @@
#include <stdlib.h> #include <stdlib.h>
#include <assert.h> #include <assert.h>
#include <string.h>
#include "monster.h" #include "monster.h"
#include "util.h" #include "util.h"
@ -127,15 +128,20 @@ damage_surroundings(Monster *m, RoomMatrix *rm)
for (Sint32 j = -1; j <= 1; ++j) { for (Sint32 j = -1; j <= 1; ++j) {
if (i == 0 && j == 0) if (i == 0 && j == 0)
continue; 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) { if (r->monster) {
int dmg = stats_fight(&m->stats, &r->monster->stats); CombatResult result = stats_fight(&m->stats, &r->monster->stats);
monster_hit(r->monster, dmg); monster_hit(r->monster, result.dmg, result.critical);
gui_log("%s takes %d damage from the explosion", r->monster->label, dmg); gui_log("%s takes %d damage from the explosion", r->monster->label, result.dmg);
} else if (r->player) { } else if (r->player) {
int dmg = stats_fight(&m->stats, &r->player->stats); CombatResult result = stats_fight(&m->stats, &r->player->stats);
player_hit(r->player, dmg); player_hit(r->player, result.dmg);
gui_log("You take %d damage from the explosion", dmg); gui_log("You take %d damage from the explosion", result.dmg);
} }
} }
} }
@ -208,6 +214,9 @@ handle_sentinel_behaviour(Monster *m, RoomMatrix *rm)
static void static void
monster_behaviour_check(Monster *m, RoomMatrix *rm) monster_behaviour_check(Monster *m, RoomMatrix *rm)
{ {
if (m->state.stepsSinceChange < m->state.forceCount)
return;
switch (m->behaviour) { switch (m->behaviour) {
case GUERILLA: case GUERILLA:
case SORCERER: case SORCERER:
@ -232,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*
monster_create(void) monster_create(void)
{ {
@ -264,7 +291,9 @@ monster_create(void)
m->stateIndicator.shownOnPlayerRoomEnter = false; m->stateIndicator.shownOnPlayerRoomEnter = false;
m->state.forceCount = 0; m->state.forceCount = 0;
m->boss = false; m->boss = false;
m->items.keyType = LOCK_NONE;
monster_set_behaviour(m, NORMAL); monster_set_behaviour(m, NORMAL);
create_emitters(m);
return m; return m;
} }
@ -288,14 +317,14 @@ has_collided(Monster *monster, RoomMatrix *matrix, Vector2d direction)
RoomSpace *space = roommatrix_get_space_for(matrix, &monster->sprite->pos); RoomSpace *space = roommatrix_get_space_for(matrix, &monster->sprite->pos);
if (space->player && monster->state.current == AGRESSIVE) { if (space->player && monster->state.current == AGRESSIVE) {
unsigned int dmg = stats_fight(&monster->stats, CombatResult result = stats_fight(&monster->stats,
&space->player->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", gui_log("%s hit you for %u damage",
monster->label, dmg); monster->label, result.dmg);
camera_shake(direction, 300); camera_shake(direction, 300);
} else { } else {
gui_log("%s missed you", monster->label); gui_log("%s missed you", monster->label);
@ -445,7 +474,7 @@ monster_coward_walk(Monster *m, RoomMatrix *rm)
} }
static void 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); Position currentTilePos = position_to_matrix_coords(&m->sprite->pos);
Player *player = rm->spaces[rm->playerRoomPos.x][rm->playerRoomPos.y].player; Player *player = rm->spaces[rm->playerRoomPos.x][rm->playerRoomPos.y].player;
@ -530,9 +559,23 @@ monster_perform_aoe_attack(Monster *m, RoomMatrix *rm)
return false; 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 bool
monster_move(Monster *m, RoomMatrix *rm, Map *map) monster_move(Monster *m, RoomMatrix *rm, Map *map)
{ {
apply_bleed_damage(roommatrix_get_player(rm), m);
Player *player = roommatrix_get_player(rm); Player *player = roommatrix_get_player(rm);
if (player && player->phase_count) if (player && player->phase_count)
return true; return true;
@ -583,6 +626,19 @@ monster_reset_steps(Monster *m)
m->steps = 0; 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 void
monster_update(Monster *m, UpdateData *data) monster_update(Monster *m, UpdateData *data)
{ {
@ -590,6 +646,7 @@ monster_update(Monster *m, UpdateData *data)
return; return;
sprite_update(m->sprite, data); sprite_update(m->sprite, data);
update_emitters(m);
if (m->sprite->state == SPRITE_STATE_PLUMMETED) { if (m->sprite->state == SPRITE_STATE_PLUMMETED) {
m->stats.hp = 0; m->stats.hp = 0;
@ -631,7 +688,7 @@ monster_update(Monster *m, UpdateData *data)
} }
void void
monster_hit(Monster *monster, unsigned int dmg) monster_hit(Monster *monster, unsigned int dmg, bool critical)
{ {
if (dmg > 0) { if (dmg > 0) {
Position p = monster->sprite->pos; Position p = monster->sprite->pos;
@ -649,6 +706,9 @@ monster_hit(Monster *monster, unsigned int dmg)
C_YELLOW, C_YELLOW,
&monster->sprite->pos); &monster->sprite->pos);
} }
if (critical)
monster_set_bleeding(monster);
monster_behaviour_check_post_hit(monster); monster_behaviour_check_post_hit(monster);
} }
@ -690,6 +750,24 @@ monster_drop_loot(Monster *monster, Map *map, Player *player)
linkedlist_append(&map->items, treasure); linkedlist_append(&map->items, treasure);
} }
if (monster->items.keyType != LOCK_NONE) {
Item *key = item_builder_build_key(monster->items.keyType);
key->sprite->pos = monsterTilePos;
linkedlist_append(&map->items, key);
}
if (strcmp(monster->label, "The Trader") == 0) {
Item *treasure = item_builder_build_treasure(PLATINUM, 10 * monster->stats.lvl);
treasure->sprite->pos = monsterTilePos;
linkedlist_append(&map->items, treasure);
}
if (strcmp(monster->label, "A Fairy") == 0) {
Item *treasure = item_builder_build_treasure(PLATINUM, 3 * monster->stats.lvl);
treasure->sprite->pos = monsterTilePos;
linkedlist_append(&map->items, treasure);
}
if (monster->stats.lvl > 2 && get_random(29) == 0) { if (monster->stats.lvl > 2 && get_random(29) == 0) {
Artifact *a = artifact_create_random(player, 1); Artifact *a = artifact_create_random(player, 1);
a->sprite->pos = monsterTilePos; a->sprite->pos = monsterTilePos;
@ -723,15 +801,15 @@ monster_drop_loot(Monster *monster, Map *map, Player *player)
gui_log("%s dropped something", monster->label); gui_log("%s dropped something", monster->label);
if (item_count == 1) { if (item_count == 1) {
linkedlist_append(&map->items, items[0]); linkedlist_push(&map->items, items[0]);
} else { } else {
Item *container = item_builder_build_sack(); Item *container = item_builder_build_sack();
container->sprite->pos = monsterTilePos; container->sprite->pos = monsterTilePos;
unsigned int i; unsigned int i;
for (i = 0; i < item_count; ++i) { for (i = 0; i < item_count; ++i) {
linkedlist_append(&container->items, items[i]); linkedlist_push(&container->items, items[i]);
} }
linkedlist_append(&map->items, container); linkedlist_push(&map->items, container);
} }
} }
@ -741,6 +819,8 @@ monster_render(Monster *m, Camera *cam)
if (m->stats.hp <= 0) if (m->stats.hp <= 0)
return; return;
particle_emitter_render(m->emitters.bloodlust);
particle_emitter_render(m->emitters.bleed);
sprite_render(m->sprite, cam); sprite_render(m->sprite, cam);
} }
@ -804,7 +884,7 @@ monster_push(Monster *m, Player *p, RoomMatrix *rm, Vector2d direction)
if (space->trap) { if (space->trap) {
int dmg = space->trap->damage * 3; int dmg = space->trap->damage * 3;
m->stats.hp -= dmg; 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); gui_log("%s takes %d damage from a trap", m->label, dmg);
} else if (space->damaging) { } else if (space->damaging) {
LinkedList *objects = space->objects; LinkedList *objects = space->objects;
@ -814,7 +894,7 @@ monster_push(Monster *m, Player *p, RoomMatrix *rm, Vector2d direction)
if (!o->damage) if (!o->damage)
return; return;
m->stats.hp -= o->damage * 3; 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)) { } else if (has_collided(m, rm, direction)) {
m->sprite->pos.x -= TILE_DIMENSION * (int) direction.x; m->sprite->pos.x -= TILE_DIMENSION * (int) direction.x;
@ -825,6 +905,42 @@ monster_push(Monster *m, Player *p, RoomMatrix *rm, Vector2d direction)
player_monster_kill_check(p, m); player_monster_kill_check(p, m);
} }
void
monster_set_bloodlust(Monster *m, bool bloodlust)
{
if (m->emitters.bloodlust->enabled == bloodlust || m->stats.hp <= 0) {
return;
}
m->emitters.bloodlust->enabled = bloodlust;
if (bloodlust) {
gui_log("%s rages with bloodlust", m->label);
monster_set_behaviour(m, HOSTILE);
m->stats.advantage = true;
m->stats.atk += 2;
m->stats.def += 2;
m->stats.dmg += 2;
m->stats.hp += 10;
m->stats.maxhp += 10;
} else {
gui_log("%s calms down from it's bloodlust", m->label);
monster_set_behaviour(m, NORMAL);
m->stats.advantage = false;
m->stats.atk -= 2;
m->stats.def -= 2;
m->stats.dmg -= 2;
m->stats.hp -= 10;
m->stats.maxhp -= 10;
}
}
void
monster_set_bleeding(Monster *m)
{
m->emitters.bleed->enabled = true;
gui_log("%s starts bleeding profusely", m->label);
}
void void
monster_destroy(Monster *m) monster_destroy(Monster *m)
{ {
@ -833,6 +949,10 @@ monster_destroy(Monster *m)
free(m->label); free(m->label);
if (m->lclabel) if (m->lclabel)
free(m->lclabel); free(m->lclabel);
particle_emitter_destroy(m->emitters.bloodlust);
particle_emitter_destroy(m->emitters.bleed);
sprite_destroy(m->stateIndicator.sprite); sprite_destroy(m->stateIndicator.sprite);
free(m); free(m);
} }

View File

@ -25,6 +25,8 @@
#include "actiontext.h" #include "actiontext.h"
#include "player.h" #include "player.h"
#include "linkedlist.h" #include "linkedlist.h"
#include "doorlocktype.h"
#include "particle_emitter.h"
struct UpdateData; struct UpdateData;
@ -63,6 +65,15 @@ typedef struct MonsterStateIndicator {
int displayCount; int displayCount;
} MonsterStateIndicator; } MonsterStateIndicator;
typedef struct MonsterItems {
enum DoorLockType keyType;
} MonsterItems;
typedef struct ParticleEmitters {
ParticleEmitter *bloodlust;
ParticleEmitter *bleed;
} ParticleEmitters;
typedef struct Monster { typedef struct Monster {
char *label; char *label;
char *lclabel; char *lclabel;
@ -71,6 +82,8 @@ typedef struct Monster {
State state; State state;
MonsterStateIndicator stateIndicator; MonsterStateIndicator stateIndicator;
MonsterBehaviour behaviour; MonsterBehaviour behaviour;
MonsterItems items;
ParticleEmitters emitters;
unsigned int steps; unsigned int steps;
bool boss; bool boss;
} Monster; } Monster;
@ -90,7 +103,7 @@ void
monster_render_top_layer(Monster*, RoomMatrix*, Camera*); monster_render_top_layer(Monster*, RoomMatrix*, Camera*);
void void
monster_hit(Monster*, unsigned int dmg); monster_hit(Monster*, unsigned int dmg, bool critical);
void void
monster_update_stats_for_level(Monster*, unsigned int level); monster_update_stats_for_level(Monster*, unsigned int level);
@ -113,6 +126,12 @@ monster_push(Monster *, Player *, RoomMatrix*, Vector2d dir);
void void
monster_reset_steps(Monster *m); monster_reset_steps(Monster *m);
void
monster_set_bloodlust(Monster*, bool bloodlust);
void
monster_set_bleeding(Monster*);
void void
monster_destroy(Monster*); 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(); engine->global_particles = linkedlist_create();
} }
void
particle_engine_bleed(Position pos, Dimension dim)
{
particle_engine_bloodspray(pos, dim, 10);
}
void void
particle_engine_bloodspray(Position pos, Dimension dim, unsigned int count) 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); 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 void
particle_engine_eldritch_explosion(Position pos, Dimension dim) particle_engine_eldritch_explosion(Position pos, Dimension dim)
{ {
@ -188,6 +201,38 @@ particle_engine_eldritch_explosion(Position pos, Dimension dim)
create_explosion(pos, dim, 1, C_GREEN); create_explosion(pos, dim, 1, C_GREEN);
} }
void
particle_engine_dust_puff(Position pos, Dimension dim)
{
check_engine();
for (unsigned int i = 0; i < 50; ++i) {
int x, y, xv, yv, w, h;
unsigned int lt;
Particle *p;
x = get_random(dim.width) + pos.x;
y = get_random(dim.height) + pos.y;
xv = get_random(200) - 100;
yv = get_random(200) - 100;
lt = get_random(10) + 10;
w = get_random(3);
h = get_random(3);
p = create_rect_particle();
p->particle.rect.pos = (Position) { x, y };
p->particle.rect.dim = (Dimension) { w, h };
p->velocity = (Vector2d) { (float) xv, (float) yv };
p->movetime = lt;
p->lifetime = lt;
p->color = C_GREY;
p->blend_mode = SDL_BLENDMODE_BLEND;
linkedlist_append(&engine->game_particles, p);
}
}
void void
particle_engine_speed_lines(Position pos, Dimension dim, bool horizontal) particle_engine_speed_lines(Position pos, Dimension dim, bool horizontal)
{ {
@ -222,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 void
particle_engine_sparkle(Position pos, Dimension dim, SDL_Color color, bool global) particle_engine_sparkle(Position pos, Dimension dim, SDL_Color color, bool global)
{ {

View File

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

View File

@ -36,13 +36,15 @@
#include "animation.h" #include "animation.h"
#include "trap.h" #include "trap.h"
#include "gamecontroller.h" #include "gamecontroller.h"
#include "event.h"
#include "effect_util.h"
#ifdef STEAM_BUILD #ifdef STEAM_BUILD
#include "steam/steamworks_api_wrapper.h" #include "steam/steamworks_api_wrapper.h"
#endif // STEAM_BUILD #endif // STEAM_BUILD
#define ENGINEER_STATS { 12, 12, 5, 7, 2, 2, 1, false, false } #define ENGINEER_STATS { 12, 12, 5, 7, 2, 1, 1, false, false }
#define MAGE_STATS { 12, 12, 5, 7, 1, 2, 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 PALADIN_STATS { 12, 12, 8, 9, 3, 1, 1, false, false }
#define ROGUE_STATS { 9, 9, 6, 9, 4, 2, 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 } #define WARRIOR_STATS { 12, 12, 8, 9, 3, 1, 1, false, false }
@ -132,16 +134,16 @@ on_monster_collision(Player *player,
RoomMatrix *matrix, RoomMatrix *matrix,
Vector2d direction) Vector2d direction)
{ {
unsigned int hit = stats_fight(&player->stats, CombatResult result = stats_fight(&player->stats,
&monster->stats); &monster->stats);
mixer_play_effect(SWING0 + get_random(2)); mixer_play_effect(SWING0 + get_random(2));
monster_hit(monster, hit); monster_hit(monster, result.dmg, result.critical);
animation_run(player->swordAnimation); animation_run(player->swordAnimation);
if (hit > 0) { if (result.dmg > 0) {
gui_log("You hit %s for %u damage", gui_log("You hit %s for %u damage",
monster->lclabel, hit); monster->lclabel, result.dmg);
player->stat_data.hits += 1; player->stat_data.hits += 1;
mixer_play_effect(SWORD_HIT); mixer_play_effect(SWORD_HIT);
} else { } else {
@ -149,6 +151,17 @@ on_monster_collision(Player *player,
player->stat_data.misses += 1; player->stat_data.misses += 1;
} }
player_monster_kill_check(player, monster); 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 (monster->stats.hp > 0) {
if (get_random(10) < player_has_artifact(player, PUSH_BACK)) { if (get_random(10) < player_has_artifact(player, PUSH_BACK)) {
@ -238,17 +251,22 @@ has_collided(Player *player, RoomMatrix *matrix, Vector2d direction)
gamecontroller_rumble(0.30f, 100); gamecontroller_rumble(0.30f, 100);
if (space->monster) { if (space->monster) {
on_monster_collision(player, space->monster, matrix, direction); on_monster_collision(player, space->monster, matrix, direction);
} else if (space->door && map_open_door(space->door, player)) {
// Door opened, pass
} else { } else {
mixer_play_effect(BONK); mixer_play_effect(BONK);
camera_shake(direction, 100); camera_shake(direction, 100);
gui_log("Ouch! There is something in the way"); if (space->door)
gui_log("Ouch! That door seems to be locked");
else
gui_log("Ouch! There is something in the way");
} }
return true; return true;
} }
else { else {
player_collect_items(player, space); player_collect_items(player, space);
player_pickup_artifacts(player, space); player_pickup_artifacts(player, space);
// If not phased or phase will end this turn, react to traps and pits // 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))) { if (!player->phase_count || (player->phase_count == 1 && player->stats.speed == (player->stat_data.steps + 1))) {
player_interact_objects(player, space); player_interact_objects(player, space);
@ -320,7 +338,7 @@ move(Player *player, RoomMatrix *matrix, Vector2d direction)
lastSpace->trap == NULL && lastSpace->trap == NULL &&
lastSpace->objects == NULL && lastSpace->objects == NULL &&
lastSpace->items == NULL lastSpace->items == NULL
) )
map_trigger_tile_fall(lastSpace->tile); map_trigger_tile_fall(lastSpace->tile);
} }
} }
@ -328,8 +346,13 @@ move(Player *player, RoomMatrix *matrix, Vector2d direction)
void void
player_sip_health(Player *player) 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; --player->potion_sips;
if (player->class == MAGE)
--player->potion_sips;
++player->stats.hp; ++player->stats.hp;
mixer_play_effect(BUBBLE0 + get_random(2)); mixer_play_effect(BUBBLE0 + get_random(2));
gui_log("You take a sip from a health potion"); gui_log("You take a sip from a health potion");
@ -498,8 +521,10 @@ player_create(class_t class, Camera *cam)
player->sprite = sprite_create(); player->sprite = sprite_create();
#ifdef DEBUG #ifdef DEBUG
player->daggers = 10; player->daggers = 10;
player->gold = 500;
#else #else
player->daggers = 0; player->daggers = 0;
player->gold = 0;
#endif #endif
player->stat_data.total_steps = 0; player->stat_data.total_steps = 0;
player->stat_data.steps = 0; player->stat_data.steps = 0;
@ -507,7 +532,6 @@ player_create(class_t class, Camera *cam)
player->stat_data.kills = 0; player->stat_data.kills = 0;
player->stat_data.misses = 0; player->stat_data.misses = 0;
player->xp = 0; player->xp = 0;
player->gold = 0;
player->potion_sips = 0; player->potion_sips = 0;
player->phase_count = 0; player->phase_count = 0;
player->class = class; player->class = class;
@ -516,6 +540,8 @@ player_create(class_t class, Camera *cam)
player->animationTimer = _timer_create(); player->animationTimer = _timer_create();
player->swordAnimation = animation_create(5); player->swordAnimation = animation_create(5);
player->equipment.hasArtifacts = false; player->equipment.hasArtifacts = false;
player->equipment.keys = 0;
player->stateData.shopOwnerKiller = false;
build_sword_animation(player, cam->renderer); build_sword_animation(player, cam->renderer);
@ -534,6 +560,9 @@ player_create(class_t class, Camera *cam)
case MAGE: case MAGE:
m_strcpy(asset, 100, "Commissions/Mage.png"); m_strcpy(asset, 100, "Commissions/Mage.png");
player->stats = (Stats) MAGE_STATS; 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; break;
case PALADIN: case PALADIN:
m_strcpy(asset, 100, "Commissions/Paladin.png"); m_strcpy(asset, 100, "Commissions/Paladin.png");
@ -545,7 +574,6 @@ player_create(class_t class, Camera *cam)
player->skills[0] = skill_create(BACKSTAB, cam); player->skills[0] = skill_create(BACKSTAB, cam);
player->skills[1] = skill_create(TRIP, cam); player->skills[1] = skill_create(TRIP, cam);
player->skills[2] = skill_create(PHASE, cam); player->skills[2] = skill_create(PHASE, cam);
player->skills[3] = skill_create(DAGGER_THROW, cam);
player->daggers = 10; player->daggers = 10;
break; break;
case WARRIOR: case WARRIOR:
@ -554,10 +582,10 @@ player_create(class_t class, Camera *cam)
player->skills[0] = skill_create(FLURRY, cam); player->skills[0] = skill_create(FLURRY, cam);
player->skills[1] = skill_create(BASH, cam); player->skills[1] = skill_create(BASH, cam);
player->skills[2] = skill_create(CHARGE, cam); player->skills[2] = skill_create(CHARGE, cam);
player->skills[3] = skill_create(DAGGER_THROW, cam);
break; break;
} }
player->skills[3] = skill_create(DAGGER_THROW, cam);
player->skills[4] = skill_create(SIP_HEALTH, cam); player->skills[4] = skill_create(SIP_HEALTH, cam);
sprite_load_texture(player->sprite, asset, 0, cam->renderer); sprite_load_texture(player->sprite, asset, 0, cam->renderer);
@ -568,7 +596,16 @@ player_create(class_t class, Camera *cam)
return player; return player;
} }
ExperienceData player_get_xp_data(Player *p) void
player_reset_on_levelchange(Player *player)
{
player->sprite->pos = (Position) {
TILE_DIMENSION, TILE_DIMENSION };
player->equipment.keys = 0;
}
ExperienceData
player_get_xp_data(Player *p)
{ {
ExperienceData data; ExperienceData data;
data.previousLevel = next_level_threshold(p->stats.lvl - 1); data.previousLevel = next_level_threshold(p->stats.lvl - 1);
@ -606,6 +643,15 @@ player_monster_kill_check(Player *player, Monster *monster)
else if (strcmp("Ti, the Mage", monster->label) == 0) else if (strcmp("Ti, the Mage", monster->label) == 0)
steam_set_achievement(BUGGCREATOR); steam_set_achievement(BUGGCREATOR);
#endif // STEAM_BUILD #endif // STEAM_BUILD
if (strcmp("The Trader", monster->label) == 0) {
player->stateData.shopOwnerKiller = true;
}
Event event;
event.type = MONSTER_KILLED_EVENT;
event.monsterKilled.monster = monster;
event.monsterKilled.player = player;
event_trigger(&event);
} }
} }
@ -750,9 +796,20 @@ player_has_artifact(Player *p, MagicalEffect effect)
void void
player_add_artifact(Player *p, Artifact *a) player_add_artifact(Player *p, Artifact *a)
{ {
if (a->price > p->gold) {
gui_log("You don't have enough gold to buy a %s",
a->info.name);
return;
}
if (a->collected) if (a->collected)
return; return;
if (a->price) {
gui_log("You pay %d gold for %s", a->price, a->info.name);
p->gold -= a->price;
}
mixer_play_effect(MAGIC_PICKUP); mixer_play_effect(MAGIC_PICKUP);
a->collected = true; a->collected = true;

View File

@ -60,8 +60,13 @@ typedef struct ArtifactData {
typedef struct PlayerEquipment { typedef struct PlayerEquipment {
ArtifactData artifacts[LAST_ARTIFACT_EFFECT]; ArtifactData artifacts[LAST_ARTIFACT_EFFECT];
bool hasArtifacts; bool hasArtifacts;
Uint32 keys;
} PlayerEquipment; } PlayerEquipment;
typedef struct PlayerStateData {
bool shopOwnerKiller;
} PlayerStateData;
typedef struct Player { typedef struct Player {
Sprite *sprite; Sprite *sprite;
Stats stats; Stats stats;
@ -78,11 +83,15 @@ typedef struct Player {
Timer *animationTimer; Timer *animationTimer;
Animation *swordAnimation; Animation *swordAnimation;
PlayerEquipment equipment; PlayerEquipment equipment;
PlayerStateData stateData;
} Player; } Player;
Player* Player*
player_create(class_t, Camera*); player_create(class_t, Camera*);
void
player_reset_on_levelchange(Player *player);
ExperienceData ExperienceData
player_get_xp_data(Player*); player_get_xp_data(Player*);

View File

@ -109,3 +109,9 @@ position_to_tile_pos(const Position *p)
p->y - (p->y % TILE_DIMENSION) p->y - (p->y % TILE_DIMENSION)
}; };
} }
Position
position_add(const Position *a, const Position *b)
{
return POS(a->x + b->x, a->y + b->y);
}

View File

@ -71,4 +71,10 @@ position_in_roommatrix(const Position*);
Position Position
position_to_tile_pos(const Position*); position_to_tile_pos(const Position*);
/*
* Add two positions
*/
Position
position_add(const Position*, const Position*);
#endif // POSITION_H_ #endif // POSITION_H_

View File

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

View File

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

View File

@ -47,6 +47,8 @@ roommatrix_reset(RoomMatrix *m)
space->player = NULL; space->player = NULL;
space->trap = NULL; space->trap = NULL;
space->tile = NULL; space->tile = NULL;
space->wall = NULL;
space->door = NULL;
space->decoration = NULL; space->decoration = NULL;
while (space->items != NULL) while (space->items != NULL)
linkedlist_pop(&space->items); linkedlist_pop(&space->items);
@ -126,6 +128,20 @@ void roommatrix_populate_from_map(RoomMatrix *rm, Map *m)
space->lethal = space->lethal =
r->tiles[i][j]->lethal; r->tiles[i][j]->lethal;
} }
if (r->walls[i][j]) {
space->wall = r->walls[i][j];
space->occupied =
r->walls[i][j]->collider;
space->lightsource =
r->walls[i][j]->lightsource;
}
if (r->doors[i][j]) {
space->door = r->doors[i][j];
space->occupied =
r->doors[i][j]->collider;
space->lightsource =
r->doors[i][j]->lightsource;
}
if (r->decorations[i][j]) { if (r->decorations[i][j]) {
space->decoration = r->decorations[i][j]; space->decoration = r->decorations[i][j];
space->occupied |= space->occupied |=

View File

@ -15,9 +15,7 @@
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#pragma once
#ifndef ROOMMATRIX_H_
#define ROOMMATRIX_H_
#include <stdbool.h> #include <stdbool.h>
#include "defines.h" #include "defines.h"
@ -45,6 +43,8 @@ typedef struct RoomSpace {
bool damaging; bool damaging;
int light; int light;
MapTile *tile; MapTile *tile;
MapTile *wall;
MapTile *door;
MapTile *decoration; MapTile *decoration;
Monster *monster; Monster *monster;
Player *player; Player *player;
@ -85,6 +85,9 @@ roommatrix_render_lightmap(RoomMatrix*, Camera*);
RoomSpace* RoomSpace*
roommatrix_get_space_for(RoomMatrix*, const Position *p); roommatrix_get_space_for(RoomMatrix*, const Position *p);
Player *
roommatrix_get_player(RoomMatrix*);
#ifdef DEBUG #ifdef DEBUG
void void
roommatrix_render_debug(RoomMatrix*, Camera*); roommatrix_render_debug(RoomMatrix*, Camera*);
@ -92,8 +95,3 @@ roommatrix_render_debug(RoomMatrix*, Camera*);
void void
roommatrix_destroy(RoomMatrix*); 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)); linkedlist_push(&screen->sprites, credit_txt("liquidityc.github.io", C_WHITE, x + columnOffset, y, renderer));
y += 20; y += 20;
linkedlist_push(&screen->sprites, credit_txt("@LiquidityC", C_WHITE, x + columnOffset, y, renderer)); 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)); linkedlist_push(&screen->sprites, credit_txt(" - Graphics -", C_BLUE, x, y, renderer));
y += 30; y += 30;
linkedlist_push(&screen->sprites, credit_txt("Tileset:", C_YELLOW, x, y, renderer)); 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)); sprite_destroy(linkedlist_pop(&screen->sprites));
free(screen); free(screen);
} }

View File

@ -36,6 +36,8 @@
#include "artifact.h" #include "artifact.h"
#include "trap.h" #include "trap.h"
#include "tooltip.h" #include "tooltip.h"
#include "actiontextbuilder.h"
#include "effect_util.h"
static char *flurry_tooltip[] = { static char *flurry_tooltip[] = {
"FLURRY", "", "FLURRY", "",
@ -55,6 +57,25 @@ static char *flurry_tooltip[] = {
NULL 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[] = { static char *bash_tooltip[] = {
"BASH", "", "BASH", "",
"", "",
@ -156,6 +177,43 @@ static char *charge_tooltip[] = {
NULL 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[] = { static char *dagger_tooltip[] = {
"THROW DAGGER", "", "THROW DAGGER", "",
"", "",
@ -266,6 +324,73 @@ check_skill_validity(Position *playerPos, Position *targetPos, SkillData *data)
return true; 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 static bool
skill_use_flurry(Skill *skill, SkillData *data) skill_use_flurry(Skill *skill, SkillData *data)
{ {
@ -284,12 +409,12 @@ skill_use_flurry(Skill *skill, SkillData *data)
unsigned int hitCount = 0; unsigned int hitCount = 0;
for (size_t i = 0; i < 3; ++i) { for (size_t i = 0; i < 3; ++i) {
unsigned int originalHp = monster->stats.hp; unsigned int originalHp = monster->stats.hp;
unsigned int dmg = stats_fight(&data->player->stats, &monster->stats); CombatResult result = stats_fight(&data->player->stats, &monster->stats);
if (dmg > 0 && originalHp > 0) { if (result.dmg > 0 && originalHp > 0) {
gui_log("You hit for %u damage", dmg); gui_log("You hit for %u damage", result.dmg);
hitCount++; hitCount++;
} }
monster_hit(monster, dmg); monster_hit(monster, result.dmg, result.critical);
} }
if (hitCount == 1) { if (hitCount == 1) {
mixer_play_effect(SWORD_HIT); mixer_play_effect(SWORD_HIT);
@ -298,9 +423,6 @@ skill_use_flurry(Skill *skill, SkillData *data)
} else if (hitCount == 3) { } else if (hitCount == 3) {
mixer_play_effect(TRIPPLE_SWORD_HIT); mixer_play_effect(TRIPPLE_SWORD_HIT);
} }
data->player->stat_data.hits += hitCount;
} else { } else {
gui_log("You swing at thin air with a flurry of strikes"); 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); mixer_play_effect(SWING0);
if (monster) { if (monster) {
gui_log("You bash %s with your shield", monster->lclabel); gui_log("You bash %s with your shield", monster->lclabel);
unsigned int dmg = stats_fight(&data->player->stats, &monster->stats); CombatResult result = stats_fight(&data->player->stats, &monster->stats);
if (dmg > 0) { if (result.dmg > 0) {
gui_log("You hit for %u damage", dmg); gui_log("You hit for %u damage", result.dmg);
if (monster->stats.hp > 0) { if (monster->stats.hp > 0) {
gui_log("%s seems dazed and confused", monster->label); gui_log("%s seems dazed and confused", monster->label);
monster_set_state(monster, STUNNED, monster_set_state(monster, STUNNED,
(Uint8) (3 + player_has_artifact(data->player, INCREASED_STUN))); (Uint8) (3 + player_has_artifact(data->player, INCREASED_STUN)));
} }
mixer_play_effect(SLAM); mixer_play_effect(SLAM);
data->player->stat_data.hits += 1;
} else { } else {
gui_log("You missed %s", monster->lclabel); gui_log("You missed %s", monster->lclabel);
} }
monster_hit(monster, dmg); monster_hit(monster, result.dmg, result.critical);
} else { } else {
gui_log("You bash your shield at nothing"); 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)); mixer_play_effect(SWING0 + get_random(2));
animation_run(data->player->swordAnimation); animation_run(data->player->swordAnimation);
if (space->monster) { if (space->monster) {
int dmg = stats_fight(&data->player->stats, &space->monster->stats); CombatResult result = stats_fight(&data->player->stats, &space->monster->stats);
if (dmg) if (result.dmg)
mixer_play_effect(SWORD_HIT); mixer_play_effect(SWORD_HIT);
gui_log("You trip %s causing it to fall away from you", space->monster->lclabel); 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); 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); Uint32 pushCount = 1 + player_has_artifact(data->player, PUSH_BACK);
for (Uint32 i = 0; i < pushCount; ++i) { for (Uint32 i = 0; i < pushCount; ++i) {
monster_push(space->monster, data->player, data->matrix, data->direction); 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); monster_push(m, data->player, data->matrix, reverseDirection);
m->stats.disadvantage = true; 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; m->stats.disadvantage = false;
monster_hit(m, dmg); monster_hit(m, result.dmg, result.critical);
player_monster_kill_check(data->player, m); player_monster_kill_check(data->player, m);
if (dmg) { if (result.dmg) {
mixer_play_effect(SWORD_HIT); 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 static bool
skill_sip_health_available(Player *player) 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 static bool
@ -644,13 +767,12 @@ skill_charge_check_path(SkillData *data,
Stats tmpStats = player->stats; Stats tmpStats = player->stats;
tmpStats.dmg *= steps > 0 ? steps : 1; tmpStats.dmg *= steps > 0 ? steps : 1;
mixer_play_effect(SWING0 + get_random(2)); mixer_play_effect(SWING0 + get_random(2));
unsigned int dmg = stats_fight(&tmpStats, &monster->stats); CombatResult result = stats_fight(&tmpStats, &monster->stats);
if (dmg > 0) { if (result.dmg > 0) {
gui_log("You charged %s for %u damage", monster->lclabel, dmg); gui_log("You charged %s for %u damage", monster->lclabel, result.dmg);
mixer_play_effect(SWORD_HIT); 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); player_monster_kill_check(data->player, monster);
} }
@ -752,6 +874,143 @@ create_charge(void)
return skill; 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*
skill_create(enum SkillType t, Camera *cam) skill_create(enum SkillType t, Camera *cam)
{ {
@ -761,6 +1020,10 @@ skill_create(enum SkillType t, Camera *cam)
skill = create_flurry(); skill = create_flurry();
skill->tooltip = tooltip_create(flurry_tooltip, cam); skill->tooltip = tooltip_create(flurry_tooltip, cam);
break; break;
case VAMPIRIC_BLOW:
skill = create_vampiric_blow();
skill->tooltip = tooltip_create(vampiric_blow_tooltip, cam);
break;
case SIP_HEALTH: case SIP_HEALTH:
skill = create_sip_health(); skill = create_sip_health();
skill->tooltip = tooltip_create(health_tooltip, cam); skill->tooltip = tooltip_create(health_tooltip, cam);
@ -769,6 +1032,14 @@ skill_create(enum SkillType t, Camera *cam)
skill = create_charge(); skill = create_charge();
skill->tooltip = tooltip_create(charge_tooltip, cam); skill->tooltip = tooltip_create(charge_tooltip, cam);
break; 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: case DAGGER_THROW:
skill = create_throw_dagger(); skill = create_throw_dagger();
skill->tooltip = tooltip_create(dagger_tooltip, cam); skill->tooltip = tooltip_create(dagger_tooltip, cam);

View File

@ -35,7 +35,10 @@ enum SkillType {
SIP_HEALTH, SIP_HEALTH,
BACKSTAB, BACKSTAB,
TRIP, TRIP,
PHASE PHASE,
BLINK,
ERUPT,
VAMPIRIC_BLOW
}; };
typedef struct SkillData_t { 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->dim = (Dimension) { 8, 8 };
s->fixed = true; s->fixed = true;
sprite_load_text_texture(s, "GUI/SDS_8x8.ttf", 0, 8, 0); 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); texture_load_from_text(s->textures[0], buffer, C_YELLOW, C_BLACK, renderer);
linkedlist_append(&bar->sprites, s); linkedlist_append(&bar->sprites, s);
} }

View File

@ -30,7 +30,8 @@ sprite_create_default(void)
s->textures[1] = NULL; s->textures[1] = NULL;
s->clip = (SDL_Rect) { 0, 0, 0, 0 }; s->clip = (SDL_Rect) { 0, 0, 0, 0 };
s->destroyTextures = false; s->destroyTextures = false;
s->pos = (Position) { 0, 0 }; s->pos = POS(0, 0);
s->offset = POS(0, 0);
s->dim = DEFAULT_DIMENSION; s->dim = DEFAULT_DIMENSION;
s->angle = 0; s->angle = 0;
s->rotationPoint = (SDL_Point) { 0, 0 }; s->rotationPoint = (SDL_Point) { 0, 0 };
@ -169,6 +170,8 @@ sprite_render(Sprite *s, Camera *cam)
else else
cameraPos = s->pos; cameraPos = s->pos;
cameraPos = position_add(&cameraPos, &s->offset);
SDL_Rect box = { SDL_Rect box = {
cameraPos.x, cameraPos.y, s->dim.width, s->dim.height cameraPos.x, cameraPos.y, s->dim.width, s->dim.height
}; };

View File

@ -41,6 +41,7 @@ typedef struct Sprite {
SDL_Rect clip; SDL_Rect clip;
bool destroyTextures; bool destroyTextures;
Position pos; Position pos;
Position offset;
Dimension dim; Dimension dim;
double angle; double angle;
SDL_Point rotationPoint; SDL_Point rotationPoint;

59
src/sprite_util.c Normal file
View File

@ -0,0 +1,59 @@
/*
* 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 "sprite_util.h"
#include "util.h"
static struct {
SDL_Renderer *renderer;
} utilData;
void
sprite_util_init(SDL_Renderer *renderer)
{
utilData.renderer = renderer;
}
Sprite *
sprite_util_create_text_sprite(const char *fontPath,
int size,
SDL_Color fg,
SDL_Color outline,
const char *fmt,
...)
{
va_list args;
Sprite *sprite = sprite_create();
sprite_load_text_texture(sprite, fontPath, 0, size, 1);
char priceLabel[10];
va_start(args, fmt);
m_vsprintf(priceLabel, 10, fmt, args);
va_end(args);
texture_load_from_text(sprite->textures[0],
priceLabel,
fg,
outline,
utilData.renderer);
sprite->dim = sprite->textures[0]->dim;
return sprite;
}

Some files were not shown because too many files have changed in this diff Show More